1. 同步FIFO基础设计

同步FIFO基础设计 学习卡片

任务目标

设计一个深度为16、数据宽度为8的同步FIFO,包含读指针、写指针、空标志(empty)与满标志(full),并通过Verilog仿真验证基本读写功能。

设计规格

  • FIFO深度:16(使用4位地址指针)
  • 数据宽度:8 bit
  • 同步时钟:所有信号在时钟上升沿变化
  • 写操作:当 wren 为高且 full 为低时,在时钟上升沿写入数据
  • 读操作:当 rden 为高且 empty 为低时,在时钟上升沿读出数据
  • 空标志:读指针追上写指针时置高
  • 满标志:写指针追上读指针时置高(使用额外的一位计数器区分空/满)

核心实现

module sync_fifo #(
    parameter DATA_WIDTH = 8,
    parameter DEPTH      = 16
)(
    input  wire                  clk,
    input  wire                  rst_n,
    input  wire                  wren,
    input  wire                  rden,
    input  wire [DATA_WIDTH-1:0] wdata,
    output reg  [DATA_WIDTH-1:0] rdata,
    output wire                  empty,
    output wire                  full
);

    // 地址宽度 = clog2(DEPTH)
    localparam ADDR_WIDTH = 4;

    reg [DATA_WIDTH-1:0] mem [0:DEPTH-1];
    reg [ADDR_WIDTH:0]   wptr;  // 含额外位,用于区分空/满
    reg [ADDR_WIDTH:0]   rptr;

    wire [ADDR_WIDTH-1:0] waddr = wptr[ADDR_WIDTH-1:0];
    wire [ADDR_WIDTH-1:0] raddr = rptr[ADDR_WIDTH-1:0];

    assign empty = (wptr == rptr);
    assign full  = (wptr[ADDR_WIDTH] != rptr[ADDR_WIDTH]) &&
                   (wptr[ADDR_WIDTH-1:0] == rptr[ADDR_WIDTH-1:0]);

    // 写指针
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            wptr <= 0;
        else if (wren && !full)
            wptr <= wptr + 1'b1;
    end

    // 读指针
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            rptr <= 0;
        else if (rden && !empty)
            rptr <= rptr + 1'b1;
    end

    // 写数据
    always @(posedge clk) begin
        if (wren && !full)
            mem[waddr] <= wdata;
    end

    // 读数据(组合逻辑输出)
    always @(*) begin
        rdata = mem[raddr];
    end

endmodule

仿真测试平台

module tb_sync_fifo;

    reg         clk;
    reg         rst_n;
    reg         wren;
    reg         rden;
    reg  [7:0]  wdata;
    wire [7:0]  rdata;
    wire        empty;
    wire        full;

    sync_fifo uut (
        .clk   (clk),
        .rst_n (rst_n),
        .wren  (wren),
        .rden  (rden),
        .wdata (wdata),
        .rdata (rdata),
        .empty (empty),
        .full  (full)
    );

    // 时钟:10ns周期
    always #5 clk = ~clk;

    // 测试任务:写入8个数据,再读出8个数据
    integer i;

    initial begin
        clk = 0;
        rst_n = 0;
        wren = 0;
        rden = 0;
        wdata = 0;

        // 复位
        #10 rst_n = 1;

        // 写入 0x00 ~ 0x07
        for (i = 0; i < 8; i = i + 1) begin
            @(posedge clk);
            wren  = 1;
            wdata = i;
        end
        @(posedge clk);
        wren = 0;

        // 读出 8 个数据
        for (i = 0; i < 8; i = i + 1) begin
            @(posedge clk);
            rden = 1;
        end
        @(posedge clk);
        rden = 0;

        #20 $finish;
    end

    // 监控输出
    initial begin
        $monitor("Time=%0t | wren=%b rden=%b wdata=%h rdata=%h empty=%b full=%b",
                 $time, wren, rden, wdata, rdata, empty, full);
    end

endmodule

运行与验证

  1. 仿真工具:使用任意支持Verilog-2001的仿真器(如Vivado XSim、ModelSim、Icarus Verilog)。
  2. 编译与运行(以Icarus Verilog为例):
    iverilog -o fifo_tb sync_fifo.v tb_sync_fifo.v
    vvp fifo_tb
  3. 通过条件:观察仿真波形或$monitor输出,确认:
    • 复位后 empty=1、full=0;
    • 连续写入8个数据后,empty变为0,full保持0;
    • 连续读出8个数据时,rdata依次输出0x00、0x01、…、0x07,与写入顺序一致;
    • 读出全部数据后,empty重新变为1。

常见问题

  • 满标志不拉高:检查写指针额外位(wptr[ADDR_WIDTH])是否在回绕时翻转。本设计使用额外位区分空/满,若省略该位则无法区分全空与全满。
  • 读数据延迟:本设计采用组合逻辑输出rdata,读使能后数据立即有效。若需同步输出,可在读时钟沿打一拍。