1. 搭建UVM验证环境基本框架

任务目标

为矩阵加法模块(matrix_add)搭建一个最小可运行的UVM验证环境,包含driver、monitor、scoreboard三个基础组件,通过一个简单测试用例完成对加法功能的基本验证。本案例聚焦于UVM环境的结构搭建与数据流贯通,不涉及时序协议细节。

环境依赖

需要支持UVM-1.2的仿真器(如VCS、QuestaSim),并已配置UVM库路径。DUT为组合逻辑矩阵加法器,接口信号:clk、rst_n、valid_i、a[7:0]、b[7:0]、valid_o、sum[8:0](9位,防止溢出)。

UVM环境结构

环境采用标准UVM树形结构:test → env → agent(内含driver、monitor)→ scoreboard。driver负责驱动输入激励,monitor采样输出结果,scoreboard通过analysis端口接收driver的输入激励副本和monitor的输出结果,进行数据比对。

关键完整代码

// 文件: matrix_add_tb.sv
`include "uvm_macros.svh"
import uvm_pkg::*;

// 接口定义
interface matrix_add_if(input logic clk);
    logic rst_n;
    logic valid_i;
    logic [7:0] a, b;
    logic valid_o;
    logic [8:0] sum;
endinterface

// 事务对象
class my_transaction extends uvm_sequence_item;
    rand logic [7:0] a, b;
    logic [8:0] expected_sum;
    `uvm_object_utils_begin(my_transaction)
        `uvm_field_int(a, UVM_ALL_ON)
        `uvm_field_int(b, UVM_ALL_ON)
    `uvm_object_utils_end
    function new(string name = "my_transaction");
        super.new(name);
    endfunction
endclass

// Driver:驱动输入激励
class my_driver extends uvm_driver #(my_transaction);
    `uvm_component_utils(my_driver)
    virtual matrix_add_if vif;
    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction
    function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        if (!uvm_config_db#(virtual matrix_add_if)::get(this, "", "vif", vif))
            `uvm_fatal("NOVIF", "virtual interface not found")
    endfunction
    task run_phase(uvm_phase phase);
        my_transaction tx;
        forever begin
            seq_item_port.get_next_item(tx);
            @(posedge vif.clk);
            vif.a <= tx.a;
            vif.b <= tx.b;
            vif.valid_i <= 1;
            @(posedge vif.clk);
            vif.valid_i <= 0;
            seq_item_port.item_done();
        end
    endtask
endclass

// Monitor:采样输出
class my_monitor extends uvm_monitor;
    `uvm_component_utils(my_monitor)
    virtual matrix_add_if vif;
    uvm_analysis_port #(my_transaction) mon_ap;
    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction
    function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        if (!uvm_config_db#(virtual matrix_add_if)::get(this, "", "vif", vif))
            `uvm_fatal("NOVIF", "virtual interface not found")
        mon_ap = new("mon_ap", this);
    endfunction
    task run_phase(uvm_phase phase);
        my_transaction tx;
        forever begin
            @(posedge vif.clk);
            if (vif.valid_o) begin
                tx = my_transaction::type_id::create("tx");
                tx.a = vif.a;  // 采样输入作为参考
                tx.b = vif.b;
                tx.expected_sum = vif.sum;
                mon_ap.write(tx);
            end
        end
    endtask
endclass

// Scoreboard:数据比对
class my_scoreboard extends uvm_scoreboard;
    `uvm_component_utils(my_scoreboard)
    uvm_analysis_imp #(my_transaction, my_scoreboard) sb_imp;
    int match_count, mismatch_count;
    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction
    function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        sb_imp = new("sb_imp", this);
    endfunction
    function void write(my_transaction tx);
        logic [8:0] calc_sum;
        calc_sum = tx.a + tx.b;
        if (calc_sum == tx.expected_sum) begin
            match_count++;
            `uvm_info("SB_MATCH", $sformatf("a=%0d b=%0d sum=%0d PASS", tx.a, tx.b, tx.expected_sum), UVM_MEDIUM)
        end else begin
            mismatch_count++;
            `uvm_error("SB_MISMATCH", $sformatf("a=%0d b=%0d exp=%0d got=%0d", tx.a, tx.b, calc_sum, tx.expected_sum))
        end
    endfunction
endclass

// Agent:封装driver和monitor
class my_agent extends uvm_agent;
    `uvm_component_utils(my_agent)
    my_driver drv;
    my_monitor mon;
    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction
    function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        drv = my_driver::type_id::create("drv", this);
        mon = my_monitor::type_id::create("mon", this);
    endfunction
endclass

// Env:顶层环境
class my_env extends uvm_env;
    `uvm_component_utils(my_env)
    my_agent agent;
    my_scoreboard sb;
    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction
    function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        agent = my_agent::type_id::create("agent", this);
        sb = my_scoreboard::type_id::create("sb", this);
    endfunction
    function void connect_phase(uvm_phase phase);
        super.connect_phase(phase);
        agent.mon.mon_ap.connect(sb.sb_imp);
    endfunction
endclass

// 简单测试序列
class simple_seq extends uvm_sequence #(my_transaction);
    `uvm_object_utils(simple_seq)
    function new(string name = "simple_seq");
        super.new(name);
    endfunction
    task body();
        my_transaction tx;
        repeat(10) begin
            tx = my_transaction::type_id::create("tx");
            start_item(tx);
            assert(tx.randomize());
            finish_item(tx);
        end
    endtask
endclass

// Test:顶层测试
class my_test extends uvm_test;
    `uvm_component_utils(my_test)
    my_env env;
    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction
    function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        env = my_env::type_id::create("env", this);
    endfunction
    task run_phase(uvm_phase phase);
        simple_seq seq;
        phase.raise_objection(this);
        seq = simple_seq::type_id::create("seq");
        seq.start(env.agent.drv.seq_item_port);
        #100;
        phase.drop_objection(this);
    endtask
    function void report_phase(uvm_phase phase);
        super.report_phase(phase);
        `uvm_info("REPORT", $sformatf("match=%0d mismatch=%0d", env.sb.match_count, env.sb.mismatch_count), UVM_LOW)
    endfunction
endclass

// DUT:矩阵加法模块(组合逻辑)
module matrix_add(
    input logic clk,
    input logic rst_n,
    input logic valid_i,
    input logic [7:0] a, b,
    output logic valid_o,
    output logic [8:0] sum
);
    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            valid_o <= 0;
            sum <= 0;
        end else begin
            valid_o <= valid_i;
            sum <= a + b;
        end
    end
endmodule

// 顶层模块:连接DUT与接口
module top;
    logic clk = 0;
    always #5 clk = ~clk;
    matrix_add_if mif(clk);
    matrix_add dut(
        .clk(clk), .rst_n(mif.rst_n), .valid_i(mif.valid_i),
        .a(mif.a), .b(mif.b), .valid_o(mif.valid_o), .sum(mif.sum)
    );
    initial begin
        mif.rst_n = 0;
        #20 mif.rst_n = 1;
        uvm_config_db#(virtual matrix_add_if)::set(null, "uvm_test_top.env.agent.drv", "vif", mif);
        uvm_config_db#(virtual matrix_add_if)::set(null, "uvm_test_top.env.agent.mon", "vif", mif);
        run_test("my_test");
    end
endmodule

运行方式

# VCS编译运行示例
vcs -sverilog +incdir+$UVM_HOME/src matrix_add_tb.sv $UVM_HOME/src/uvm.sv -R -l run.log

验证方法与判定标准

运行仿真后,观察日志中的SB_MATCH信息。判定通过条件:mismatch=0且match=10(对应10个随机激励)。若出现SB_MISMATCH或UVM_ERROR,则说明环境数据通路或DUT功能存在错误。可通过修改simple_seq中的repeat次数来增加验证规模。

常见问题

  • 接口未找到:检查uvm_config_db的路径字符串是否与组件层次一致,特别是uvm_test_top前缀。
  • 比对不通过:确认monitor采样时刻是否正确,DUT为时序输出,需在valid_o拉高且数据稳定后采样。
  • 仿真不结束:检查objection的raise/drop是否配对,确保run_phase能正常退出。