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

搭建UVM验证环境基本框架 学习卡片

任务目标

搭建一个最小可运行的UVM验证环境,包含driver、monitor、sequencer、agent、env、test六个核心组件,并驱动一个简单的APB读写序列,验证环境能够正确编译并完成仿真。

环境依赖

需要支持UVM-1.2的仿真器(如VCS、QuestaSim),并已设置好UVM库路径。以下代码基于QuestaSim的uvm_pkg与questa_uvm_pkg,若使用其他仿真器,仅需调整编译命令中的库映射。

完整实现

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

// 1. 简单APB事务
class apb_transfer extends uvm_sequence_item;
    rand bit [31:0] addr;
    rand bit [31:0] data;
    rand bit write;  // 1:写, 0:读
    `uvm_object_utils_begin(apb_transfer)
        `uvm_field_int(addr, UVM_ALL_ON)
        `uvm_field_int(data, UVM_ALL_ON)
        `uvm_field_int(write, UVM_ALL_ON)
    `uvm_object_utils_end
    function new(string name = "apb_transfer");
        super.new(name);
    endfunction
endclass

// 2. 简单序列:先写后读
class apb_rw_seq extends uvm_sequence #(apb_transfer);
    `uvm_object_utils(apb_rw_seq)
    function new(string name = "apb_rw_seq");
        super.new(name);
    endfunction
    task body();
        apb_transfer t;
        // 写操作
        t = apb_transfer::type_id::create("write_t");
        t.addr = 32'h0000_0004;
        t.data = 32'hDEAD_BEEF;
        t.write = 1;
        start_item(t);
        finish_item(t);
        // 读操作
        t = apb_transfer::type_id::create("read_t");
        t.addr = 32'h0000_0004;
        t.write = 0;
        start_item(t);
        finish_item(t);
    endtask
endclass

// 3. Driver:将事务驱动到虚拟接口
class apb_driver extends uvm_driver #(apb_transfer);
    `uvm_component_utils(apb_driver)
    virtual apb_if vif;
    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction
    function void build_phase(uvm_phase phase);
        if (!uvm_config_db#(virtual apb_if)::get(this, "", "vif", vif))
            `uvm_fatal("NOVIF", "virtual interface not found")
    endfunction
    task run_phase(uvm_phase phase);
        apb_transfer req;
        vif.psel = 0; vif.penable = 0; vif.pwrite = 0;
        forever begin
            seq_item_port.get_next_item(req);
            @(posedge vif.pclk);
            vif.psel = 1;
            vif.paddr = req.addr;
            vif.pwrite = req.write;
            if (req.write) begin
                vif.pwdata = req.data;
                @(posedge vif.pclk);
                vif.penable = 1;
                @(posedge vif.pclk);
                vif.penable = 0;
            end else begin
                @(posedge vif.pclk);
                vif.penable = 1;
                @(posedge vif.pclk);
                req.data = vif.prdata;
                vif.penable = 0;
            end
            vif.psel = 0;
            seq_item_port.item_done();
        end
    endtask
endclass

// 4. Monitor:采样总线并发送给scoreboard(此处仅打印)
class apb_monitor extends uvm_monitor;
    `uvm_component_utils(apb_monitor)
    virtual apb_if vif;
    uvm_analysis_port #(apb_transfer) mon_ap;
    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);
        if (!uvm_config_db#(virtual apb_if)::get(this, "", "vif", vif))
            `uvm_fatal("NOVIF", "virtual interface not found")
    endfunction
    task run_phase(uvm_phase phase);
        apb_transfer t;
        forever begin
            @(posedge vif.pclk);
            if (vif.psel && vif.penable) begin
                t = apb_transfer::type_id::create("mon_t");
                t.addr = vif.paddr;
                t.write = vif.pwrite;
                if (vif.pwrite) t.data = vif.pwdata;
                else t.data = vif.prdata;
                mon_ap.write(t);
                `uvm_info("MON", $sformatf("Observed: %s addr=0x%0h data=0x%0h",
                    t.write ? "WR" : "RD", t.addr, t.data), UVM_MEDIUM)
            end
        end
    endtask
endclass

// 5. Sequencer
class apb_sequencer extends uvm_sequencer #(apb_transfer);
    `uvm_component_utils(apb_sequencer)
    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction
endclass

// 6. Agent
class apb_agent extends uvm_agent;
    `uvm_component_utils(apb_agent)
    apb_driver drv;
    apb_monitor mon;
    apb_sequencer sqr;
    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction
    function void build_phase(uvm_phase phase);
        sqr = apb_sequencer::type_id::create("sqr", this);
        drv = apb_driver::type_id::create("drv", this);
        mon = apb_monitor::type_id::create("mon", this);
    endfunction
    function void connect_phase(uvm_phase phase);
        drv.seq_item_port.connect(sqr.seq_item_export);
    endfunction
endclass

// 7. Env
class apb_env extends uvm_env;
    `uvm_component_utils(apb_env)
    apb_agent agent;
    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction
    function void build_phase(uvm_phase phase);
        agent = apb_agent::type_id::create("agent", this);
    endfunction
endclass

// 8. Test:启动序列
class apb_base_test extends uvm_test;
    `uvm_component_utils(apb_base_test)
    apb_env env;
    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction
    function void build_phase(uvm_phase phase);
        env = apb_env::type_id::create("env", this);
    endfunction
    task run_phase(uvm_phase phase);
        apb_rw_seq seq;
        seq = apb_rw_seq::type_id::create("seq");
        phase.raise_objection(this);
        seq.start(env.agent.sqr);
        #100ns;
        phase.drop_objection(this);
    endtask
endclass

// 9. 接口与顶层模块
interface apb_if(input bit pclk);
    logic presetn;
    logic [31:0] paddr;
    logic pwrite;
    logic psel;
    logic penable;
    logic [31:0] pwdata;
    logic [31:0] prdata;
endinterface

module top;
    bit pclk;
    always #5 pclk = ~pclk;
    apb_if apb_if_inst(pclk);
    // 简单APB从设备模型:寄存器文件
    reg [31:0] mem [int];
    assign apb_if_inst.prdata = (apb_if_inst.pwrite) ? 32'h0 :
        (apb_if_inst.paddr == 32'h4) ? mem[32'h4] : 32'h0;
    always @(posedge pclk) begin
        if (apb_if_inst.psel && apb_if_inst.penable && apb_if_inst.pwrite)
            mem[apb_if_inst.paddr] = apb_if_inst.pwdata;
    end
    initial begin
        apb_if_inst.presetn = 0;
        #20 apb_if_inst.presetn = 1;
        uvm_config_db#(virtual apb_if)::set(null, "uvm_test_top.env.agent.*", "vif", apb_if_inst);
        run_test("apb_base_test");
    end
endmodule

编译与运行

# QuestaSim/ModelSim
vlib work
vlog +incdir+$UVM_HOME/src $UVM_HOME/src/uvm_pkg.sv $UVM_HOME/src/questa_uvm_pkg.sv apb_uvm_env.sv
vsim -c -do "run -all; quit" work.top

验证方法与通过标准

仿真运行后,观察UVM报告。通过标准为:

  • 编译无错误,仿真无fatal;
  • monitor打印出两条事务:WR addr=0x4 data=0xdeadbeef 和 RD addr=0x4 data=0xdeadbeef;
  • UVM报告显示UVM_ERROR : 0,仿真正常退出。

预期输出(由代码逻辑直接推出):

UVM_INFO MON: Observed: WR addr=0x4 data=0xdeadbeef
UVM_INFO MON: Observed: RD addr=0x4 data=0xdeadbeef
UVM_INFO /uvm-1.2/uvm_report_server.svh: Reporting: 0 errors