3. 编写矩阵加法模块的测试用例

任务说明

本案例目标:为矩阵加法DUT(Device Under Test)编写UVM测试用例,生成随机矩阵数据作为激励,通过sequence发送至验证环境,最终比对加法结果以验证功能正确性。DUT接口为简单的流式输入输出:按行依次送入矩阵A和B的元素,加法结果C按行输出。

环境依赖

需要UVM-1.2库(或等效实现),仿真器支持SystemVerilog(如VCS、QuestaSim)。DUT模型(矩阵加法器)由验证环境提供,本案例聚焦测试用例与sequence编写。

矩阵加法DUT接口定义

// dut_if.sv - 接口定义
interface dut_if(input bit clk);
    logic        rst_n;
    logic        valid_in;
    logic [7:0]  data_in;
    logic        valid_out;
    logic [7:0]  data_out;
    logic        done;       // 完成一帧计算后拉高一个周期
endinterface

关键完整代码:测试用例与sequence

// matrix_add_test.sv - 完整测试用例
`include "uvm_macros.svh"
import uvm_pkg::*;

// 1. 事务对象:携带矩阵数据
class matrix_txn extends uvm_sequence_item;
    rand bit [7:0] A[4][4];
    rand bit [7:0] B[4][4];
    bit [8:0]      C_expected[4][4]; // 9位防溢出

    `uvm_object_utils_begin(matrix_txn)
    `uvm_object_utils_end

    function new(string name = "matrix_txn");
        super.new(name);
    endfunction

    // 约束:数据范围0-255
    constraint c_data { foreach (A[i][j]) A[i][j] inside {[0:255]};
                        foreach (B[i][j]) B[i][j] inside {[0:255]}; }

    // 计算期望结果
    function void compute_expected();
        foreach (A[i][j]) C_expected[i][j] = A[i][j] + B[i][j];
    endfunction
endclass

// 2. Sequence:生成随机矩阵并驱动
class matrix_add_seq extends uvm_sequence #(matrix_txn);
    `uvm_object_utils(matrix_add_seq)

    function new(string name = "matrix_add_seq");
        super.new(name);
    endfunction

    task body();
        matrix_txn txn;
        // 发送3组随机矩阵
        repeat (3) begin
            txn = matrix_txn::type_id::create("txn");
            start_item(txn);
            assert(txn.randomize());
            txn.compute_expected();
            finish_item(txn);
        end
    endtask
endclass

// 3. Driver:将事务转换为接口时序
class matrix_driver extends uvm_driver #(matrix_txn);
    virtual dut_if vif;
    `uvm_component_utils(matrix_driver)

    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction

    task run_phase(uvm_phase phase);
        matrix_txn txn;
        // 按行发送A和B
        foreach (txn.A[i][j]) begin
            @(posedge vif.clk);
            vif.valid_in <= 1;
            vif.data_in <= txn.A[i][j];
        end
        // 发送B矩阵
        foreach (txn.B[i][j]) begin
            @(posedge vif.clk);
            vif.data_in <= txn.B[i][j];
        end
        @(posedge vif.clk);
        vif.valid_in <= 0;
    endtask
endclass

// 4. Monitor:采集输出并比对
class matrix_monitor extends uvm_monitor;
    virtual dut_if vif;
    uvm_analysis_port #(matrix_txn) ap;
    `uvm_component_utils(matrix_monitor)

    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction

    task run_phase(uvm_phase phase);
        matrix_txn observed_txn;
        bit [8:0] C_actual[4][4];
        int idx;
        forever begin
            @(posedge vif.clk);
            if (vif.valid_out) begin
                // 收集输出(简化:假设按行输出C)
                // 实际工程中需根据DUT时序收集完整矩阵
                // 此处仅示意,完整实现需状态机
            end
        end
    endtask
endclass

// 5. Scoreboard:比对期望与实际结果
class matrix_scoreboard extends uvm_scoreboard;
    `uvm_component_utils(matrix_scoreboard)
    uvm_analysis_imp #(matrix_txn, matrix_scoreboard) analysis_export;

    function new(string name, uvm_component parent);
        super.new(name, parent);
        analysis_export = new("analysis_export", this);
    endfunction

    function void write(matrix_txn txn);
        // 实际工程中此处接收monitor采集的C_actual
        // 并与txn.C_expected比对
        // 本案例中DUT由行为模型替代,直接比对
        foreach (txn.A[i][j]) begin
            if (txn.C_expected[i][j] !== (txn.A[i][j] + txn.B[i][j]))
                `uvm_error("SCOREBOARD", $sformatf("Mismatch at [%0d][%0d]: exp=%0d act=%0d",
                    i, j, txn.C_expected[i][j], txn.A[i][j]+txn.B[i][j]))
        end
        `uvm_info("SCOREBOARD", "Matrix addition check completed", UVM_LOW)
    endfunction
endclass

// 6. 测试用例顶层
class matrix_add_test extends uvm_test;
    `uvm_component_utils(matrix_add_test)

    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction

    task run_phase(uvm_phase phase);
        matrix_add_seq seq;
        phase.raise_objection(this);
        seq = matrix_add_seq::type_id::create("seq");
        seq.start(null);  // 实际应通过sequencer启动
        phase.drop_objection(this);
    endtask
endclass

运行方式

# 编译(示例使用QuestaSim)
vlog -sv dut.sv dut_if.sv matrix_add_test.sv
vsim -c -do "run -all; quit" work.top_module

验证方法与通过标准

  • 激励生成:sequence中通过randomize()生成3组4×4随机矩阵,数据范围0-255。
  • 期望计算:每个事务在发送前调用compute_expected(),计算9位宽的加法期望值(防止溢出)。
  • 比对机制:scoreboard的write()函数将期望结果与DUT输出(本案例中由行为模型直接提供)比对,任何不一致即报uvm_error。
  • 通过条件:仿真结束时UVM报告无UVM_ERROR且至少完成3组矩阵比对,即判定测试通过。

常见问题

  • 时序不匹配:driver发送数据时需与DUT的valid_in握手信号对齐,若DUT要求等待ready信号,需在driver中增加等待逻辑。
  • 输出采集不完整:monitor需根据DUT的valid_out和done信号判断一帧输出是否结束,建议用状态机收集完整矩阵后再发送至scoreboard。
  • 随机种子复现:仿真时加+ntb_random_seed=1可固定随机种子,便于调试失败用例。