2. 模拟FIFO溢出错误

任务说明

构造一个最小可综合的同步FIFO(深度8,宽度8位),通过连续写入超过容量的数据来触发溢出条件。验证重点:溢出标志(overflow)是否在FIFO满后再次写入时正确拉高,以及错误处理逻辑(此处为拒绝写入)是否生效。

环境依赖

本案例使用 SystemVerilog + UVM 1.2,仿真工具为 QuestaSim 或 VCS。无需额外IP,FIFO设计代码与UVM验证环境均在本案例内完整提供。

FIFO设计(DUT)

// fifo_depth8.sv
module fifo_depth8 #(
    parameter DATA_WIDTH = 8,
    parameter DEPTH      = 8
)(
    input  logic                  clk,
    input  logic                  rst_n,
    input  logic                  wr_en,
    input  logic                  rd_en,
    input  logic [DATA_WIDTH-1:0] din,
    output logic [DATA_WIDTH-1:0] dout,
    output logic                  full,
    output logic                  empty,
    output logic                  overflow,
    output logic                  underflow
);
    logic [$clog2(DEPTH)-1:0] w_ptr, r_ptr;
    logic [$clog2(DEPTH)-1:0] count;
    logic [DATA_WIDTH-1:0] mem [DEPTH];

    assign full  = (count == DEPTH);
    assign empty = (count == 0);

    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            w_ptr <= 0;
            r_ptr <= 0;
            count <= 0;
            overflow  <= 0;
            underflow <= 0;
        end else begin
            overflow  <= 0;
            underflow <= 0;
            if (wr_en && !full) begin
                mem[w_ptr] <= din;
                w_ptr <= w_ptr + 1;
                count <= count + 1;
            end else if (wr_en && full) begin
                overflow <= 1;  // 溢出标志:满时仍写入
            end
            if (rd_en && !empty) begin
                dout <= mem[r_ptr];
                r_ptr <= r_ptr + 1;
                count <= count - 1;
            end else if (rd_en && empty) begin
                underflow <= 1;
            end
        end
    end
endmodule

UVM验证环境

以下代码包含:事务类、序列(连续写10次,超过深度8)、驱动器、监视器、参考模型(计数值比对)、计分板以及测试类。环境通过uvm_sequence驱动写端口,不执行读操作,从而保证FIFO必然填满并触发溢出。

// fifo_overflow_test.sv
`include "uvm_macros.svh"
import uvm_pkg::*;

// 事务
class fifo_txn extends uvm_sequence_item;
    rand logic [7:0] data;
    rand bit wr_en;
    rand bit rd_en;
    `uvm_object_utils_begin(fifo_txn)
        `uvm_field_int(data, UVM_ALL_ON)
        `uvm_field_int(wr_en, UVM_ALL_ON)
        `uvm_field_int(rd_en, UVM_ALL_ON)
    `uvm_object_utils_end
    function new(string name = "fifo_txn");
        super.new(name);
    endfunction
endclass

// 序列:连续写10次(深度8,必溢出)
class write_overflow_seq extends uvm_sequence #(fifo_txn);
    `uvm_object_utils(write_overflow_seq)
    function new(string name = "write_overflow_seq");
        super.new(name);
    endfunction
    task body();
        fifo_txn txn;
        repeat (10) begin
            txn = fifo_txn::type_id::create("txn");
            start_item(txn);
            txn.wr_en = 1;
            txn.rd_en = 0;
            txn.data  = $urandom;
            finish_item(txn);
        end
    endtask
endclass

// 驱动器
class fifo_driver extends uvm_driver #(fifo_txn);
    virtual fifo_if vif;
    `uvm_component_utils(fifo_driver)
    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction
    task run_phase(uvm_phase phase);
        forever begin
            seq_item_port.get_next_item(req);
            @(posedge vif.clk);
            vif.wr_en <= req.wr_en;
            vif.rd_en <= req.rd_en;
            vif.din   <= req.data;
            seq_item_port.item_done();
        end
    endtask
endclass

// 监视器
class fifo_monitor extends uvm_monitor;
    virtual fifo_if vif;
    uvm_analysis_port #(fifo_txn) mon_ap;
    `uvm_component_utils(fifo_monitor)
    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction
    function void build_phase(uvm_phase phase);
        mon_ap = new("mon_ap", this);
    endfunction
    task run_phase(uvm_phase phase);
        fifo_txn txn;
        forever begin
            @(posedge vif.clk);
            txn = fifo_txn::type_id::create("txn");
            txn.wr_en = vif.wr_en;
            txn.rd_en = vif.rd_en;
            txn.data  = vif.din;
            mon_ap.write(txn);
        end
    endtask
endclass

// 参考模型:跟踪写入次数
class fifo_scoreboard extends uvm_scoreboard;
    `uvm_component_utils(fifo_scoreboard)
    uvm_analysis_imp #(fifo_txn, fifo_scoreboard) sb_imp;
    int write_count;
    int overflow_detected;

    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction
    function void build_phase(uvm_phase phase);
        sb_imp = new("sb_imp", this);
        write_count = 0;
        overflow_detected = 0;
    endfunction
    function void write(fifo_txn txn);
        if (txn.wr_en) begin
            write_count++;
            if (write_count > 8) begin
                overflow_detected = 1;
                `uvm_info("SCB", $sformatf("溢出条件触发: 第%0d次写入(容量8)", write_count), UVM_LOW)
            end
        end
    endfunction
    function void check_phase(uvm_phase phase);
        if (overflow_detected == 1)
            `uvm_info("SCB", "TEST PASSED: 溢出标志已触发", UVM_LOW)
        else
            `uvm_error("SCB", "TEST FAILED: 未检测到溢出")
    endfunction
endclass

// 环境
class fifo_env extends uvm_env;
    fifo_driver    drv;
    fifo_monitor   mon;
    fifo_scoreboard scb;
    `uvm_component_utils(fifo_env)
    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction
    function void build_phase(uvm_phase phase);
        drv  = fifo_driver::type_id::create("drv", this);
        mon  = fifo_monitor::type_id::create("mon", this);
        scb  = fifo_scoreboard::type_id::create("scb", this);
    endfunction
    function void connect_phase(uvm_phase phase);
        drv.seq_item_port.connect(seqr.seq_item_export);
        mon.mon_ap.connect(scb.sb_imp);
    endfunction
endclass

// 测试
class fifo_overflow_test extends uvm_test;
    fifo_env env;
    `uvm_component_utils(fifo_overflow_test)
    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction
    function void build_phase(uvm_phase phase);
        env = fifo_env::type_id::create("env", this);
    endfunction
    task run_phase(uvm_phase phase);
        write_overflow_seq seq;
        phase.raise_objection(this);
        seq = write_overflow_seq::type_id::create("seq");
        seq.start(env.drv.seqr);
        #100ns;
        phase.drop_objection(this);
    endtask
endclass

接口与连接

// fifo_if.sv
interface fifo_if(input logic clk);
    logic rst_n;
    logic wr_en, rd_en;
    logic [7:0] din, dout;
    logic full, empty, overflow, underflow;
endinterface

运行方式

# 编译(QuestaSim示例)
vlib work
vlog fifo_depth8.sv fifo_if.sv fifo_overflow_test.sv
vsim -c -do "run -all; quit" fifo_overflow_test

# 预期输出(确定性结果)
# UVM_INFO : 溢出条件触发: 第9次写入(容量8)
# UVM_INFO : TEST PASSED: 溢出标志已触发

验证判定标准

  • 仿真结束时出现 TEST PASSED 信息,且无 UVM_ERROR。
  • DUT的 overflow 信号在FIFO满(第9次写)后拉高一个时钟周期。
  • 计分板检测到写入次数超过8,确认溢出路径被真实执行。