• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 众包
  • 赞助商
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录

SOC/IP验证工程师

  • 博客园
  • 联系
  • 订阅
  • 管理

公告

View Post

搭建寄存模型时使用add_hdl_path和add_hdl_path_slice

在UVM(Universal Verification Methodology)中,add_hdl_path_slice是用于将HDL(硬件描述语言)中的信号路径映射到UVM寄存器模型(uvm_reg_model)中的一种方法。它通常在寄存器模型构建过程中使用,以便在寄存器读写操作时能够通过前门访问(front-door access)直接访问硬件信号。

具体来说,add_hdl_path_slice方法用于将寄存器模型中的某个寄存器或字段与HDL路径中的特定信号片段(slice)关联起来。这样,当通过寄存器模型进行读写操作时,UVM可以找到对应的HDL信号路径,从而进行硬件级别的读写。

函数原型:
systemverilog
function void add_hdl_path_slice(string name, int offset, int size, string kind = "RTL");
参数说明:
name:HDL路径中信号的名字。通常是一个字符串,表示从顶层开始的路径,但这里只是信号名,完整的路径由之前调用add_hdl_path时指定。

offset:该信号片段在寄存器中的起始位偏移。

size:该信号片段的位宽。

kind:可选的字符串,用于指定HDL路径的类型,默认为"RTL"。可以是"RTL"或"Gate"等,用于区分不同的设计层次。

使用步骤:
首先,在寄存器模型中,为每个寄存器或寄存器块调用add_hdl_path来指定顶层的HDL路径。

然后,对于寄存器中的每个字段或整个寄存器,调用add_hdl_path_slice来指定该字段或寄存器对应的信号在HDL路径中的具体位置。

示例:
假设我们有一个寄存器模块,其中包含一个32位的寄存器status_reg,该寄存器由两个字段组成:fifo_empty(位0)和fifo_full(位1)。在HDL中,该寄存器的路径为top.dut.status_reg。

在UVM寄存器模型中,我们可以这样映射:

systemverilog
class status_reg extends uvm_reg;
   `uvm_object_utils(status_reg)

   uvm_reg_field fifo_empty;
   uvm_reg_field fifo_full;

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

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

class reg_block extends uvm_reg_block;
   `uvm_object_utils(reg_block)

   status_reg status;

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

   virtual function void build();
      // 创建寄存器
      status = status_reg::type_id::create("status");
      status.configure(this, null, "");//此寄存器所在block的指针,reg_file的指针,此寄存器后门访问的路径
      status.build();

      // 指定默认的HDL路径(相对路径)
      this.add_hdl_path("top.dut");

      // 为status寄存器添加HDL路径片段
      // 假设status_reg在HDL中是32位,我们将其映射到top.dut.status_reg
      // 注意:这里我们使用add_hdl_path_slice将整个寄存器映射到一个32位的信号
      // 实际上,也可以为每个字段单独映射,但通常整个寄存器映射一次即可。
      status.add_hdl_path_slice("status_reg", 0, 32); // 从第0位开始,32位宽

      // 或者,如果寄存器中的字段分散在不同的HDL信号中,我们可以为每个字段单独映射:
      // status.fifo_empty.add_hdl_path_slice("fifo_empty", 0, 1);
      // status.fifo_full.add_hdl_path_slice("fifo_full", 1, 1);
   endfunction
endclass
注意事项:
add_hdl_path_slice通常用于前门访问(front-door access)的映射。如果只使用后门访问(back-door access),则不一定需要。

在调用add_hdl_path_slice之前,通常需要先调用add_hdl_path来设置基路径。

对于寄存器模型中的每个寄存器或字段,可以多次调用add_hdl_path_slice,以映射到不同的HDL路径(例如,RTL级和门级)。

使用add_hdl_path_slice时,需要确保偏移和位宽与HDL中的信号位置一致。

通过这种方式,UVM寄存器模型就能够知道如何通过前门访问来读写硬件寄存器,从而在仿真中实现寄存器的硬件访问。

uvm_reg::add_hdl_path_slice() 是 UVM 寄存器模型(RGM)中用于建立寄存器与硬件信号路径映射的关键方法,主要用于前门访问(front-door access)。

基本功能

将寄存器/字段映射到 HDL 中的具体信号路径,使得寄存器模型可以通过层次化路径直接访问硬件信号。

方法原型

function void add_hdl_path_slice(
    string name,        // 信号名称
    int offset,         // 位偏移
    int size,           // 位宽
    bit first = 0,      // 是否为首个slice
    string kind = "RTL" // 路径类型
);

使用场景

1. 单个寄存器映射

class my_reg extends uvm_reg;
    `uvm_object_utils(my_reg)
    
    uvm_reg_field data;
    
    virtual function void build();
        data = uvm_reg_field::type_id::create("data");
        data.configure(this, 32, 0, "RW", 0, 32'h0, 1, 1, 0);
    endfunction
    
    function new(string name = "my_reg");
        super.new(name, 32, UVM_NO_COVERAGE);
    endfunction
endclass

// 在寄存器块中配置
function void my_reg_block::build();
    my_reg = my_reg::type_id::create("my_reg");
    my_reg.configure(this, null, "");
    my_reg.build();
    
    // 添加HDL路径
    my_reg.add_hdl_path("reg_file.reg1");           // 根路径
    my_reg.add_hdl_path_slice("data", 0, 32, 1);   // 32位数据信号
endfunction

2. 寄存器分片映射

当寄存器分散在多个硬件信号中时:

// 假设64位寄存器分在两个32位信号中
my_reg.add_hdl_path("reg_file.complex_reg");
my_reg.add_hdl_path_slice("low_word", 0, 32, 1);    // 低32位
my_reg.add_hdl_path_slice("high_word", 32, 32, 0);  // 高32位

3. 字段级映射

class status_reg extends uvm_reg;
    uvm_reg_field error;
    uvm_reg_field busy;
    uvm_reg_field done;
    
    virtual function void build();
        error = uvm_reg_field::type_id::create("error");
        busy  = uvm_reg_field::type_id::create("busy");
        done  = uvm_reg_field::type_id::create("done");
        
        error.configure(this, 1, 0, "RO", 0, 1'b0, 1, 0, 0);
        busy.configure(this, 1, 1, "RO", 0, 1'b0, 1, 0, 0);
        done.configure(this, 1, 2, "RO", 0, 1'b0, 1, 0, 0);
    endfunction
    
    function new(string name = "status_reg");
        super.new(name, 3, UVM_NO_COVERAGE);
    endfunction
endclass

// 为每个字段单独映射
status_reg.add_hdl_path("top.status");
status_reg.error.add_hdl_path_slice("error_bit", 0, 1, 1);
status_reg.busy.add_hdl_path_slice("busy_bit", 1, 1, 0);
status_reg.done.add_hdl_path_slice("done_bit", 2, 1, 0);

关键参数详解

offset 和 size

  • offset: 信号在寄存器中的起始位
  • size: 信号位宽
  • 示例:add_hdl_path_slice("field2", 8, 4) 表示映射到位[11:8]

kind 参数

// 支持多种设计表示
reg.add_hdl_path("design.reg", "RTL");     // RTL级路径
reg.add_hdl_path("gate_design.reg", "GATES"); // 门级路径

// 使用时指定
reg.set_hdl_path_root("TB.dut", "RTL");    // 使用RTL路径
reg.set_hdl_path_root("TB.dut_gates", "GATES"); // 使用门级路径

完整示例

class ral_block_sys extends uvm_reg_block;
    `uvm_object_utils(ral_block_sys)
    
    rand control_reg ctrl;
    rand status_reg  stat;
    
    virtual function void build();
        // 创建寄存器实例
        ctrl = control_reg::type_id::create("ctrl");
        stat = status_reg::type_id::create("stat");
        
        // 配置寄存器
        ctrl.configure(this, null, "ctrl_reg");
        stat.configure(this, null, "stat_reg");
        
        ctrl.build();
        stat.build();
        
        // 设置默认路径前缀
        set_hdl_path_root("tb.dut");
        
        // 映射control寄存器
        ctrl.add_hdl_path("ctrl_reg");
        ctrl.enable.add_hdl_path_slice("en", 0, 1, 1);
        ctrl.mode.add_hdl_path_slice("mode", 1, 2, 0);
        
        // 映射status寄存器
        stat.add_hdl_path("stat_reg");
        stat.add_hdl_path_slice("status", 0, 8);  // 整个寄存器映射
        
        // 锁定模型
        lock_model();
    endfunction
endclass

注意事项

  1. 层级关系:先调用 add_hdl_path() 设置根路径,再调用 add_hdl_path_slice() 添加具体信号
  2. 位对齐:确保 offset 和 size 与硬件设计一致
  3. 多次调用:一个寄存器可以映射到多个 HDL 路径(不同 abstraction level)
  4. 验证环境集成:
// 在base_test中集成
virtual function void connect_phase(uvm_phase phase);
    // 设置适配器
    reg2adapter = reg2bus_adapter::type_id::create("reg2adapter");
    predictor = uvm_reg_predictor#(bus_transaction)::type_id::create("predictor");
    
    // 设置寄存器模型HDL路径
    reg_model.set_hdl_path_root("tb.dut");
    
    // 连接predictor
    predictor.map = reg_model.default_map;
    predictor.adapter = reg2adapter;
    bus_monitor.item_collected_port.connect(predictor.bus_in);
endfunction

调试技巧

// 1. 检查映射关系
reg_model.print_hdl_paths();

// 2. 使用UVM_INFO查看路径
`uvm_info("HDL_PATH", 
    $sformatf("Path for %s: %s", 
    reg.get_full_name(), reg.get_hdl_path()), 
    UVM_MEDIUM)

// 3. 运行时验证
if (!reg.has_hdl_path()) begin
    `uvm_error("NO_HDL_PATH", "HDL path not configured")
end

正确使用 add_hdl_path_slice() 可以实现精确的寄存器-硬件映射,是前门访问(通过总线接口访问寄存器)的基础。

posted on 2026-01-31 15:09  SOC验证工程师  阅读(0)  评论(0)    收藏  举报

刷新页面返回顶部
 
博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3