2. 同步FIFO参数化设计
任务目标
设计一个参数化同步FIFO,支持数据位宽和存储深度通过参数配置,并验证不同参数配置下的读写功能正确性。本案例聚焦FIFO核心逻辑,不包含跨时钟域处理。
设计实现
采用参数化设计,通过 DATA_WIDTH 和 DEPTH 两个参数控制位宽与深度。使用二进制计数器生成读写地址,通过读写指针相等判断空/满状态。存储体使用寄存器数组实现,深度限制为2的幂次以便地址回绕。
module sync_fifo #(
parameter DATA_WIDTH = 8,
parameter DEPTH = 16
)(
input wire clk,
input wire rst_n,
input wire wr_en,
input wire [DATA_WIDTH-1:0] wr_data,
input wire rd_en,
output wire [DATA_WIDTH-1:0] rd_data,
output wire full,
output wire empty
);
// 地址位宽(深度需为2的幂)
localparam ADDR_WIDTH = $clog2(DEPTH);
// 存储体
reg [DATA_WIDTH-1:0] mem [0:DEPTH-1];
// 读写指针与计数器
reg [ADDR_WIDTH-1:0] wr_ptr;
reg [ADDR_WIDTH-1:0] rd_ptr;
reg [ADDR_WIDTH:0] count; // 多1位用于满判断
// 读写指针与计数更新
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
wr_ptr <= 0;
rd_ptr <= 0;
count <= 0;
end else begin
case ({wr_en && !full, rd_en && !empty})
2'b10: begin // 仅写
mem[wr_ptr] <= wr_data;
wr_ptr <= wr_ptr + 1'b1;
count <= count + 1'b1;
end
2'b01: begin // 仅读
rd_ptr <= rd_ptr + 1'b1;
count <= count - 1'b1;
end
2'b11: begin // 同时读写
mem[wr_ptr] <= wr_data;
wr_ptr <= wr_ptr + 1'b1;
rd_ptr <= rd_ptr + 1'b1;
end
default: ; // 无操作
endcase
end
end
// 状态输出
assign full = (count == DEPTH);
assign empty = (count == 0);
assign rd_data = mem[rd_ptr];
endmodule
测试平台与验证
编写参数化testbench,通过 defparam 或模块实例化参数分别验证 8×16 和 16×8 两种配置。测试流程:先连续写入DEPTH个数据直至满,再连续读出并比对数据,最后验证空标志。
module tb_sync_fifo;
// 配置1:8位宽 × 16深度
parameter W1 = 8, D1 = 16;
// 配置2:16位宽 × 8深度
parameter W2 = 16, D2 = 8;
reg clk, rst_n;
reg wr_en, rd_en;
reg [W1-1:0] wdata1;
wire [W1-1:0] rdata1;
wire full1, empty1;
reg [W2-1:0] wdata2;
wire [W2-1:0] rdata2;
wire full2, empty2;
integer i, err;
// 时钟
initial clk = 0;
always #5 clk = ~clk;
// 实例化两个配置
sync_fifo #(.DATA_WIDTH(W1), .DEPTH(D1)) u_fifo1 (
.clk(clk), .rst_n(rst_n),
.wr_en(wr_en), .wr_data(wdata1),
.rd_en(rd_en), .rd_data(rdata1),
.full(full1), .empty(empty1)
);
sync_fifo #(.DATA_WIDTH(W2), .DEPTH(D2)) u_fifo2 (
.clk(clk), .rst_n(rst_n),
.wr_en(wr_en), .wr_data(wdata2),
.rd_en(rd_en), .rd_data(rdata2),
.full(full2), .empty(empty2)
);
// 测试任务
task test_fifo;
input [31:0] depth;
integer i;
begin
err = 0;
// 复位
rst_n = 0;
wr_en = 0; rd_en = 0;
#20 rst_n = 1;
#10;
// 写满
for (i = 0; i < depth; i = i + 1) begin
@(posedge clk);
wr_en = 1;
wdata1 = i;
wdata2 = i * 3;
end
@(posedge clk);
wr_en = 0;
// 检查满标志
if (u_fifo1.full !== 1'b1 || u_fifo2.full !== 1'b1) begin
$display("FAIL: full flag not asserted");
err = err + 1;
end
// 读空并比对
for (i = 0; i < depth; i = i + 1) begin
@(posedge clk);
rd_en = 1;
#1;
if (rdata1 !== i || rdata2 !== i * 3) begin
$display("FAIL: data mismatch at %0d, got %0d/%0d", i, rdata1, rdata2);
err = err + 1;
end
end
@(posedge clk);
rd_en = 0;
// 检查空标志
if (u_fifo1.empty !== 1'b1 || u_fifo2.empty !== 1'b1) begin
$display("FAIL: empty flag not asserted");
err = err + 1;
end
if (err == 0)
$display("PASS: depth=%0d test passed", depth);
else
$display("FAIL: depth=%0d, errors=%0d", depth, err);
end
endtask
initial begin
// 测试配置1(深度16)
test_fifo(D1);
// 测试配置2(深度8)
test_fifo(D2);
#50 $finish;
end
endmodule
运行与判定标准
运行方式:在仿真工具(如Vivado XSim、ModelSim)中,将上述两个模块加入工程,以 tb_sync_fifo 为顶层运行仿真。
通过条件:仿真控制台输出两行 PASS 信息,分别对应深度16和深度8的测试,且无 FAIL 信息。若出现数据不匹配或标志位错误,需检查读写时序或指针逻辑。
关键点说明
- 读写同时使能时,
count保持不变,但数据仍正确写入与读出,这是FIFO的标准行为。 - 深度必须为2的幂,否则
$clog2会向上取整导致地址空间大于实际存储,造成逻辑错误。 - 空/满标志在读写使能的同一时钟沿更新,仿真中需在时钟沿后采样(如
#1)以获得稳定值。