4. 实现UVM寄存器模型集成

任务目标

为RISC-V处理器验证环境中的CSR(控制状态寄存器)建立UVM寄存器模型,并将其集成到现有UVM验证平台中,使测试序列能够通过前门(总线协议访问)和后门(直接内存/层次路径访问)两种方式读写CSR。

环境依赖

本案例基于以下环境实现:

  • UVM-1.2(或更高)
  • RISC-V处理器RTL模型(含CSR接口,支持前门访问协议)
  • 验证环境已具备:uvm_agent(含sequencer/driver/monitor)、uvm_scoreboard

CSR寄存器模型定义

以RISC-V中三个典型CSR为例:mstatus(机器状态)、mtvec(机器陷阱向量基址)、mepc(机器异常PC)。前门访问通过RISC-V的CSR读写指令(csrrw/csrrs)经由指令总线完成;后门访问通过层次路径直接操作RTL内部寄存器变量。

关键完整代码

// 文件: riscv_csr_reg_model.sv
// CSR寄存器模型:定义单个CSR寄存器及寄存器块
class riscv_csr_reg extends uvm_reg;
    `uvm_object_utils(riscv_csr_reg)

    rand uvm_reg_field csr_field;

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

    virtual function void build();
        csr_field = uvm_reg_field::type_id::create("csr_field");
        // 参数: parent, size, lsb_pos, access, volatile, reset, has_reset, is_rand, individually_accessible
        csr_field.configure(this, 32, 0, "RW", 0, 'h0, 1, 1, 0);
    endfunction
endclass

class riscv_csr_reg_block extends uvm_reg_block;
    `uvm_object_utils(riscv_csr_reg_block)

    rand riscv_csr_reg mstatus_reg;
    rand riscv_csr_reg mtvec_reg;
    rand riscv_csr_reg mepc_reg;

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

    virtual function void build();
        // 创建寄存器实例
        mstatus_reg = riscv_csr_reg::type_id::create("mstatus_reg");
        mtvec_reg   = riscv_csr_reg::type_id::create("mtvec_reg");
        mepc_reg    = riscv_csr_reg::type_id::create("mepc_reg");

        // 配置寄存器并添加到块中
        mstatus_reg.configure(this);
        mtvec_reg.configure(this);
        mepc_reg.configure(this);

        mstatus_reg.build();
        mtvec_reg.build();
        mepc_reg.build();

        // 地址映射(前门地址)
        mstatus_reg.add_map("csr_map", 'h300, UVM_LITTLE_ENDIAN);
        mtvec_reg.add_map("csr_map", 'h305, UVM_LITTLE_ENDIAN);
        mepc_reg.add_map("csr_map", 'h341, UVM_LITTLE_ENDIAN);

        // 默认映射
        default_map = get_map("csr_map");

        // 后门访问配置:指定RTL层次路径
        // 假设DUT顶层为 dut,CSR模块实例名为 csr_unit
        mstatus_reg.set_backdoor(riscv_csr_backdoor::type_id::create("mstatus_bd"));
        mstatus_reg.get_backdoor().set_hdl_path("dut.csr_unit.mstatus");
        mtvec_reg.set_backdoor(riscv_csr_backdoor::type_id::create("mtvec_bd"));
        mtvec_reg.get_backdoor().set_hdl_path("dut.csr_unit.mtvec");
        mepc_reg.set_backdoor(riscv_csr_backdoor::type_id::create("mepc_bd"));
        mepc_reg.get_backdoor().set_hdl_path("dut.csr_unit.mepc");

        lock_model();
    endfunction
endclass

// 后门访问实现(HDL路径直接读写)
class riscv_csr_backdoor extends uvm_reg_backdoor;
    `uvm_object_utils(riscv_csr_backdoor)

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

    virtual task read(uvm_reg_item rw);
        uvm_reg reg = rw.element;
        string path;
        reg.get_backdoor().get_hdl_path(path);
        // 使用UVM的HDL路径读取(需仿真器支持)
        rw.value[0] = uvm_hdl_read(path);
    endtask

    virtual task write(uvm_reg_item rw);
        uvm_reg reg = rw.element;
        string path;
        reg.get_backdoor().get_hdl_path(path);
        uvm_hdl_deposit(path, rw.value[0]);
    endtask
endclass

// 集成到验证环境:在env中创建并连接
class riscv_env extends uvm_env;
    `uvm_component_utils(riscv_env)

    riscv_csr_reg_block reg_model;
    riscv_adapter csr_adapter;  // 前门适配器(需实现uvm_reg_adapter)

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

    virtual function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        reg_model = riscv_csr_reg_block::type_id::create("reg_model");
        reg_model.build();
        reg_model.lock_model();
        csr_adapter = riscv_adapter::type_id::create("csr_adapter");
        // 将寄存器模型默认映射连接到sequencer(前门)
        reg_model.default_map.set_sequencer(env.agent.sequencer, csr_adapter);
    endfunction
endclass

前门适配器实现要点

// 文件: riscv_adapter.sv(前门适配器关键代码)
class riscv_adapter extends uvm_reg_adapter;
    `uvm_object_utils(riscv_adapter)

    function new(string name = "riscv_adapter");
        super.new(name);
        supports_byte_enable = 0;
        provides_responses = 1;
    endfunction

    virtual function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw);
        // 将寄存器操作转换为RISC-V CSR指令序列项
        riscv_csr_seq_item item = riscv_csr_seq_item::type_id::create("item");
        item.csr_addr = rw.addr;
        item.write = (rw.kind == UVM_WRITE);
        item.data = rw.data;
        return item;
    endfunction

    virtual function void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);
        riscv_csr_seq_item item;
        if (!$cast(item, bus_item))
            `uvm_fatal("ADAPTER", "Bus item type mismatch")
        rw.kind = item.write ? UVM_WRITE : UVM_READ;
        rw.addr = item.csr_addr;
        rw.data = item.data;
        rw.status = UVM_IS_OK;
    endfunction
endclass

运行与调用方式

  1. 在测试用例中通过uvm_reg_hw_reset_seq或自定义序列执行前门访问:
// 测试序列中前门访问示例
class csr_access_test_seq extends uvm_sequence;
    `uvm_object_utils(csr_access_test_seq)

    virtual task body();
        uvm_status_e status;
        uvm_reg_data_t value;

        // 前门写
        p_sequencer.reg_model.mstatus_reg.write(status, 32'h00000001);
        // 前门读
        p_sequencer.reg_model.mstatus_reg.read(status, value);

        // 后门写(直接修改RTL内部值)
        p_sequencer.reg_model.mtvec_reg.set(32'h80000000);
        p_sequencer.reg_model.mtvec_reg.update(status, UVM_BACKDOOR);
        // 后门读
        p_sequencer.reg_model.mepc_reg.peek(status, value);
    endtask
endclass
  1. 编译运行(以VCS为例):
vcs -sverilog -ntb_opts uvm-1.2 -f filelist.f -o simv
./simv +UVM_TESTNAME=csr_access_test

验证方法与通过条件

  • 前门验证:在测试序列中执行CSR写后立即读回,比较读回值与写入值一致;同时通过monitor观察总线上的csr指令序列与预期地址/数据匹配。
  • 后门验证:使用peek读取RTL内部寄存器值,与通过前门写入的期望值比对;或先使用poke直接修改RTL内部值,再通过前门读回确认值已生效。
  • 回归检查:运行包含上述两种访问方式的测试,UVM报告UVM_TEST_DONE且无uvm_reg_prediction错误,即判定通过。

常见问题

  • 后门路径需与DUT层次严格一致,否则uvm_hdl_read返回X态;可通过uvm_hdl_check_path预检查。
  • 前门适配器需正确实现reg2bus/bus2reg,否则寄存器操作无法正确转换为总线事务。
  • 若CSR支持非32位访问(如部分字段只读),需在uvm_reg_field中设置正确的access策略(如"RO"),并在adapter中处理掩码。