Merge dev to main. #1
|
@ -0,0 +1,83 @@
|
||||||
|
|
||||||
|
// 同步FIFO模块,可参数化配置
|
||||||
|
module fifo_sync #(
|
||||||
|
parameter DATA_WIDTH = 8, // 数据位宽
|
||||||
|
parameter DEPTH = 16, // FIFO深度
|
||||||
|
parameter USE_RAM = 0, // 是否使用RAM存储(0: 寄存器数组, 1: RAM)
|
||||||
|
parameter OUT_REG = 1 // 输出是否为reg型(1: 输出寄存器, 0: 直通)
|
||||||
|
)(
|
||||||
|
input wire clk,
|
||||||
|
input wire rst_n,
|
||||||
|
input wire wr_en,
|
||||||
|
input wire [DATA_WIDTH-1:0] din,
|
||||||
|
input wire rd_en,
|
||||||
|
output wire [DATA_WIDTH-1:0] dout,
|
||||||
|
output wire empty,
|
||||||
|
output wire full,
|
||||||
|
output wire [$clog2(DEPTH):0] data_count
|
||||||
|
);
|
||||||
|
|
||||||
|
localparam ADDR_WIDTH = $clog2(DEPTH);
|
||||||
|
|
||||||
|
reg [ADDR_WIDTH:0] wr_ptr, rd_ptr;
|
||||||
|
reg [DATA_WIDTH-1:0] mem [0:DEPTH-1];
|
||||||
|
reg [DATA_WIDTH-1:0] dout_reg;
|
||||||
|
wire [DATA_WIDTH-1:0] dout_wire;
|
||||||
|
|
||||||
|
// 写操作
|
||||||
|
always @(posedge clk or negedge rst_n) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
wr_ptr <= 0;
|
||||||
|
end else if (wr_en && !full) begin
|
||||||
|
wr_ptr <= wr_ptr + 1'b1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// 读操作
|
||||||
|
always @(posedge clk or negedge rst_n) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
rd_ptr <= 0;
|
||||||
|
end else if (rd_en && !empty) begin
|
||||||
|
rd_ptr <= rd_ptr + 1'b1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// 数据计数
|
||||||
|
assign data_count = wr_ptr - rd_ptr;
|
||||||
|
assign empty = (wr_ptr == rd_ptr);
|
||||||
|
assign full = (data_count == DEPTH);
|
||||||
|
|
||||||
|
generate
|
||||||
|
if (USE_RAM) begin : USE_RAM_BLOCK
|
||||||
|
// RAM实现(可替换为实际RAM IP)
|
||||||
|
always @(posedge clk) begin
|
||||||
|
if (wr_en && !full)
|
||||||
|
mem[wr_ptr[ADDR_WIDTH-1:0]] <= din;
|
||||||
|
end
|
||||||
|
assign dout_wire = mem[rd_ptr[ADDR_WIDTH-1:0]];
|
||||||
|
end else begin : USE_REG_BLOCK
|
||||||
|
// 寄存器数组实现
|
||||||
|
always @(posedge clk) begin
|
||||||
|
if (wr_en && !full)
|
||||||
|
mem[wr_ptr[ADDR_WIDTH-1:0]] <= din;
|
||||||
|
end
|
||||||
|
assign dout_wire = mem[rd_ptr[ADDR_WIDTH-1:0]];
|
||||||
|
end
|
||||||
|
endgenerate
|
||||||
|
|
||||||
|
// 输出寄存器可选
|
||||||
|
generate
|
||||||
|
if (OUT_REG) begin : OUT_REG_BLOCK
|
||||||
|
always @(posedge clk or negedge rst_n) begin
|
||||||
|
if (!rst_n)
|
||||||
|
dout_reg <= {DATA_WIDTH{1'b0}};
|
||||||
|
else if (rd_en && !empty)
|
||||||
|
dout_reg <= dout_wire;
|
||||||
|
end
|
||||||
|
assign dout = dout_reg;
|
||||||
|
end else begin : OUT_WIRE_BLOCK
|
||||||
|
assign dout = dout_wire;
|
||||||
|
end
|
||||||
|
endgenerate
|
||||||
|
|
||||||
|
endmodule
|
|
@ -0,0 +1,108 @@
|
||||||
|
|
||||||
|
`timescale 1ns/1ps
|
||||||
|
|
||||||
|
module fifo_sync_tb;
|
||||||
|
parameter DATA_WIDTH = 8;
|
||||||
|
parameter DEPTH = 16;
|
||||||
|
parameter USE_RAM = 0;
|
||||||
|
parameter OUT_REG = 1;
|
||||||
|
|
||||||
|
reg clk;
|
||||||
|
reg rst_n;
|
||||||
|
reg wr_en;
|
||||||
|
reg [DATA_WIDTH-1:0] din;
|
||||||
|
reg rd_en;
|
||||||
|
wire [DATA_WIDTH-1:0] dout;
|
||||||
|
wire empty;
|
||||||
|
wire full;
|
||||||
|
wire [$clog2(DEPTH):0] data_count;
|
||||||
|
|
||||||
|
// 实例化DUT
|
||||||
|
fifo_sync #(
|
||||||
|
.DATA_WIDTH(DATA_WIDTH),
|
||||||
|
.DEPTH(DEPTH),
|
||||||
|
.USE_RAM(USE_RAM),
|
||||||
|
.OUT_REG(OUT_REG)
|
||||||
|
) dut (
|
||||||
|
.clk(clk),
|
||||||
|
.rst_n(rst_n),
|
||||||
|
.wr_en(wr_en),
|
||||||
|
.din(din),
|
||||||
|
.rd_en(rd_en),
|
||||||
|
.dout(dout),
|
||||||
|
.empty(empty),
|
||||||
|
.full(full),
|
||||||
|
.data_count(data_count)
|
||||||
|
);
|
||||||
|
|
||||||
|
// 时钟生成
|
||||||
|
initial clk = 0;
|
||||||
|
always #5 clk = ~clk;
|
||||||
|
|
||||||
|
// 复位
|
||||||
|
initial begin
|
||||||
|
rst_n = 0;
|
||||||
|
wr_en = 0;
|
||||||
|
rd_en = 0;
|
||||||
|
din = 0;
|
||||||
|
#20;
|
||||||
|
rst_n = 1;
|
||||||
|
end
|
||||||
|
|
||||||
|
// 主测试流程
|
||||||
|
initial begin
|
||||||
|
wait(rst_n == 1);
|
||||||
|
@(negedge clk);
|
||||||
|
|
||||||
|
// 写入DEPTH个数据
|
||||||
|
for (int i = 0; i < DEPTH; i++) begin
|
||||||
|
@(negedge clk);
|
||||||
|
wr_en = 1;
|
||||||
|
din = i;
|
||||||
|
rd_en = 0;
|
||||||
|
end
|
||||||
|
@(negedge clk);
|
||||||
|
wr_en = 0;
|
||||||
|
|
||||||
|
// 检查full信号
|
||||||
|
if (!full) $display("[TB][ERROR] FIFO未满!");
|
||||||
|
else $display("[TB][INFO] FIFO已满!");
|
||||||
|
|
||||||
|
// 读出DEPTH个数据
|
||||||
|
for (int i = 0; i < DEPTH; i++) begin
|
||||||
|
@(negedge clk);
|
||||||
|
wr_en = 0;
|
||||||
|
rd_en = 1;
|
||||||
|
end
|
||||||
|
@(negedge clk);
|
||||||
|
rd_en = 0;
|
||||||
|
|
||||||
|
// 检查empty信号
|
||||||
|
if (!empty) $display("[TB][ERROR] FIFO未空!");
|
||||||
|
else $display("[TB][INFO] FIFO已空!");
|
||||||
|
|
||||||
|
// 交错写读
|
||||||
|
for (int i = 0; i < 8; i++) begin
|
||||||
|
@(negedge clk);
|
||||||
|
wr_en = 1;
|
||||||
|
din = i + 100;
|
||||||
|
rd_en = 1;
|
||||||
|
end
|
||||||
|
@(negedge clk);
|
||||||
|
wr_en = 0;
|
||||||
|
rd_en = 0;
|
||||||
|
|
||||||
|
#20;
|
||||||
|
$display("[TB][INFO] 测试结束");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
|
||||||
|
// 监控输出
|
||||||
|
always @(posedge clk) begin
|
||||||
|
if (wr_en && !full)
|
||||||
|
$display("[TB][WRITE] @%0t: din=%0d, wr_ptr=%0d", $time, din, dut.wr_ptr);
|
||||||
|
if (rd_en && !empty)
|
||||||
|
$display("[TB][READ ] @%0t: dout=%0d, rd_ptr=%0d", $time, dout, dut.rd_ptr);
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
Loading…
Reference in New Issue