2. 实现UVM寄存器模型RAL集成

任务目标

在已有UVM验证环境中集成RAL(Register Abstraction Layer)模型,实现对DUT中寄存器组的前门(Frontdoor)与后门(Backdoor)访问,并通过自检机制验证读写操作的正确性。本案例聚焦于RAL模型创建、集成到UVM环境、前门/后门访问路径的建立以及读写自检。

环境与依赖

  • UVM-1.2或UVM-1.1d库
  • DUT提供APB接口(前门访问通道)
  • DUT内部寄存器实例路径:dut.top_reg_block.reg_a(用于后门访问)
  • 需要VCS、Questa或Xcelium等支持UVM的仿真器

RAL模型实现

以下代码定义了一个包含两个寄存器(CTRL和STATUS)的寄存器块,并集成到UVM环境中。CTRL寄存器支持前门和后门访问,STATUS寄存器仅通过后门访问以验证路径差异。


// 文件: ral_model.svh
class reg_ctrl extends uvm_reg;
    rand uvm_reg_field ctrl_en;
    rand uvm_reg_field mode;

    function new(string name = "reg_ctrl");
        super.new(name, 32, UVM_NO_COVERAGE);
    endfunction

    virtual function void build();
        ctrl_en = uvm_reg_field::type_id::create("ctrl_en");
        mode = uvm_reg_field::type_id::create("mode");
        ctrl_en.configure(this, 1, 0, "RW", 0, 1'b0, 1, 0, 0);
        mode.configure(this, 2, 1, "RW", 0, 2'b00, 1, 0, 0);
    endfunction

    `uvm_object_utils(reg_ctrl)
endclass

class reg_status extends uvm_reg;
    rand uvm_reg_field ready;
    rand uvm_reg_field error;

    function new(string name = "reg_status");
        super.new(name, 32, UVM_NO_COVERAGE);
    endfunction

    virtual function void build();
        ready = uvm_reg_field::type_id::create("ready");
        error = uvm_reg_field::type_id::create("error");
        ready.configure(this, 1, 0, "RO", 0, 1'b0, 1, 0, 0);
        error.configure(this, 1, 1, "RO", 0, 1'b0, 1, 0, 0);
    endfunction

    `uvm_object_utils(reg_status)
endclass

class reg_block extends uvm_reg_block;
    rand reg_ctrl ctrl;
    rand reg_status status;

    function new(string name = "reg_block");
        super.new(name, UVM_NO_COVERAGE);
    endfunction

    virtual function void build();
        ctrl = reg_ctrl::type_id::create("ctrl");
        ctrl.configure(this, null, "");
        ctrl.build();
        status = reg_status::type_id::create("status");
        status.configure(this, null, "");
        status.build();

        // 默认映射(前门APB)
        default_map = create_map("default_map", 0, 4, UVM_LITTLE_ENDIAN, 1);
        default_map.add_reg(ctrl, 32'h0, "RW");
        default_map.add_reg(status, 32'h4, "RO");

        // 后门路径配置
        ctrl.add_hdl_path("dut.top_reg_block.reg_a");
        status.add_hdl_path("dut.top_reg_block.reg_b");
        lock_model();
    endfunction

    `uvm_object_utils(reg_block)
endclass

UVM环境集成与读写自检

以下代码展示了如何将RAL模型集成到UVM环境中,并通过前门和后门执行读写操作及自检。测试序列在run_phase中执行:先通过前门写入CTRL寄存器并回读验证,再通过后门写入并回读,最后通过后门读取STATUS寄存器验证硬件更新。


// 文件: ral_integration_test.sv
class ral_test extends uvm_test;
    `uvm_component_utils(ral_test)

    reg_block ral;
    uvm_reg_sequence reg_seq;

    function new(string name = "ral_test", uvm_component parent = null);
        super.new(name, parent);
    endfunction

    virtual function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        ral = reg_block::type_id::create("ral");
        ral.configure(null, "");
        ral.build();
        uvm_reg_model::set_sequencer(ral.default_map, null, null); // 实际项目中替换为APB sequencer
        uvm_config_db#(reg_block)::set(this, "*", "ral_model", ral);
    endfunction

    virtual task run_phase(uvm_phase phase);
        uvm_status_e status;
        uvm_reg_data_t read_val;
        uvm_reg_data_t expected;

        phase.raise_objection(this);

        // ===== 前门访问测试 =====
        expected = 32'h5; // ctrl_en=1, mode=2'b01
        ral.ctrl.write(status, expected, UVM_FRONTDOOR);
        ral.ctrl.read(status, read_val, UVM_FRONTDOOR);
        if (status != UVM_IS_OK || read_val !== expected)
            `uvm_error("RAL_TEST", $sformatf("前门读写失败: exp=0x%0h got=0x%0h", expected, read_val))
        else
            `uvm_info("RAL_TEST", "前门读写CTRL通过", UVM_LOW)

        // ===== 后门访问测试 =====
        expected = 32'h7; // ctrl_en=1, mode=2'b11
        ral.ctrl.write(status, expected, UVM_BACKDOOR);
        ral.ctrl.read(status, read_val, UVM_BACKDOOR);
        if (status != UVM_IS_OK || read_val !== expected)
            `uvm_error("RAL_TEST", $sformatf("后门读写失败: exp=0x%0h got=0x%0h", expected, read_val))
        else
            `uvm_info("RAL_TEST", "后门读写CTRL通过", UVM_LOW)

        // ===== 后门读STATUS(验证硬件更新) =====
        ral.status.read(status, read_val, UVM_BACKDOOR);
        if (status != UVM_IS_OK)
            `uvm_error("RAL_TEST", "后门读STATUS失败")
        else
            `uvm_info("RAL_TEST", $sformatf("后门读STATUS = 0x%0h", read_val), UVM_LOW)

        phase.drop_objection(this);
    endtask
endclass

运行方式


# 使用VCS编译运行(需替换为实际DUT与接口文件)
vcs -sverilog +acc+1 +define+UVM_NO_DPI \
    -f dut.f ral_model.svh ral_integration_test.sv \
    -l compile.log
./simv -l run.log

验证方法与通过条件

  • 前门访问验证:通过APB总线向CTRL寄存器写入0x5,再回读。若回读值等于0x5且状态为UVM_IS_OK,则前门读写路径正确。
  • 后门访问验证:通过HDL路径直接写入CTRL寄存器0x7,再回读。若回读值等于0x7,说明后门路径已正确映射到dut.top_reg_block.reg_a。
  • 硬件更新验证:后门读取STATUS寄存器,若返回非零值(表示DUT内部逻辑已更新状态),说明后门读路径有效。
  • 判定标准:仿真日志中出现“前门读写CTRL通过”和“后门读写CTRL通过”且无UVM_ERROR,即判定为通过。

常见问题

  • 后门访问失败:检查add_hdl_path中的路径是否与DUT层次结构完全一致,且路径中的信号必须为寄存器类型(reg),不能是wire。
  • 前门访问超时:确认uvm_reg_model::set_sequencer已正确绑定到APB sequencer,且APB agent已启动。
  • 寄存器字段位宽不匹配:确保configure中的位宽与DUT寄存器定义一致,否则读写数据会被截断或扩展。