uvm中搭建寄存器模型时,使用add_hdl_path和add_hdl_path_slice进行后门访问
在UVM中,寄存器模型的后门访问允许直接通过层次路径访问硬件寄存器,而不通过总线。这可以用于在仿真中快速读取或写入寄存器的值,而不依赖于前门访问(即通过总线序列)。为了使用后门访问,我们需要在寄存器模型中为每个寄存器和其域指定HDL路径。
add_hdl_path和add_hdl_path_slice是用于在UVM寄存器模型中设置后门访问路径的两个重要方法。
1. add_hdl_path方法
add_hdl_path方法用于为寄存器模型中的块(uvm_reg_block)或寄存器(uvm_reg)添加HDL路径。这个路径是一个字符串,指定了在硬件设计中该块或寄存器的层次路径。
2. add_hdl_path_slice方法
add_hdl_path_slice方法用于为寄存器中的每个域(uvm_reg_field)指定其在寄存器中的位置(即位的范围)以及可选的HDL路径片段。如果寄存器的所有域都位于同一个层次路径下,那么可以为整个寄存器指定一个路径,然后为每个域指定位的范围。但是,有时一个寄存器的不同域可能分散在不同的层次路径中,这时就需要为每个域分别指定HDL路径片段。
使用步骤
步骤1: 在寄存器块中设置根路径
通常在顶层寄存器块中,使用add_hdl_path来设置根路径。然后,在块中的每个寄存器可以使用相对路径。
步骤2: 为寄存器添加HDL路径
对于每个寄存器,使用add_hdl_path来设置该寄存器的路径。这个路径可以是绝对的(从设计顶层开始)或相对的(相对于所在块的路径)。
步骤3: 为寄存器的每个域添加路径切片
对于寄存器中的每个域,使用add_hdl_path_slice来指定该域在寄存器中的位范围,以及可选的路径。如果寄存器的所有域都在同一个路径下,那么可以只指定一次寄存器的路径,然后为每个域指定位范围,而不需要指定路径。
示例
假设我们有一个设计,其中有一个寄存器块my_block,它包含一个寄存器my_reg,该寄存器有两个域:field1(位[7:0])和field2(位[15:8])。并且,这个寄存器在硬件中的层次路径为:top.dut.reg_block.my_reg。
在寄存器模型中,我们可以这样设置后门访问路径:
systemverilog
class my_reg extends uvm_reg;
rand uvm_reg_field field1;
rand uvm_reg_field field2;
function new(string name = "my_reg");
super.new(name, 16, UVM_NO_COVERAGE);
endfunction
virtual function void build();
field1 = uvm_reg_field::type_id::create("field1");
field2 = uvm_reg_field::type_id::create("field2");
// 配置域
field1.configure(this, 8, 0, "RW", 0, 8'h00, 1, 1, 1);
field2.configure(this, 8, 8, "RW", 0, 8'h00, 1, 1, 1);
endfunction
// 添加后门访问路径
virtual function void add_hdl_path_slices(string path = "");
// 为整个寄存器添加路径
add_hdl_path(path);
// 为每个域添加路径切片,注意这里的路径为空,表示使用寄存器的路径
// 位范围由参数指定:field1是位[7:0],field2是位[15:8]
field1.add_hdl_path_slice("field1", 0, 8); // 在寄存器的路径下,field1的位范围是0到7
field2.add_hdl_path_slice("field2", 8, 8); // 在寄存器的路径下,field2的位范围是8到15
endfunction
endclass
class my_block extends uvm_reg_block;
rand my_reg reg1;
function new(string name = "my_block");
super.new(name, UVM_NO_COVERAGE);
endfunction
virtual function void build();
// 创建寄存器
reg1 = my_reg::type_id::create("reg1");
reg1.configure(this, null, "");
reg1.build();
// 设置寄存器的偏移地址(前门访问)这里省略
// ...
// 设置后门访问路径
// 首先为整个块设置根路径
add_hdl_path("top.dut.reg_block");
// 然后为寄存器设置路径,这里使用相对路径,因为块已经有根路径
reg1.add_hdl_path("my_reg");
// 或者,如果寄存器的路径是绝对的,可以这样:
// reg1.add_hdl_path("top.dut.reg_block.my_reg");
// 但是,使用相对路径更灵活,因为如果块的路径改变,只需要改一个地方。
// 调用寄存器的方法来添加域的路径切片
reg1.add_hdl_path_slices(); // 这里使用寄存器的路径,已经在上面设置
endfunction
endclass
在上面的示例中,我们为块my_block设置了根路径"top.dut.reg_block",然后为寄存器reg1设置了相对路径"my_reg"。然后,在my_reg的add_hdl_path_slices方法中,我们为每个域添加了路径切片,指定了位范围。注意,这里我们假设寄存器的两个域都在同一个层次路径下,即top.dut.reg_block.my_reg,并且域在寄存器中的位范围是连续的。
注意事项
后门访问路径必须与设计的层次路径完全匹配,包括大小写。
如果设计的层次结构发生变化,需要更新这些路径。
后门访问通常用于仿真,不能用于硬件综合。
在使用后门访问时,需要确保仿真器支持通过层次路径访问信号。
通过这样的设置,我们就可以在测试中使用后门访问来读取或写入寄存器的值,例如:
systemverilog
reg1.field1.read(status, value, UVM_BACKDOOR);
reg1.field2.write(status, 8'hFF, UVM_BACKDOOR);
这样,寄存器模型就会通过后门路径直接访问硬件信号,而不通过总线。
在UVM中,add_hdl_path和add_hdl_path_slice用于配置寄存器模型的后门访问路径。以下是详细的使用方法:
1. 基本概念
add_hdl_path: 为寄存器块或寄存器添加硬件路径add_hdl_path_slice: 为寄存器字段添加具体的位域范围
2. 在寄存器块中的配置
class my_reg_block extends uvm_reg_block;
`uvm_object_utils(my_reg_block)
rand my_reg reg1;
rand my_reg reg2;
virtual function void build();
// 创建寄存器
reg1 = my_reg::type_id::create("reg1");
reg1.configure(this, null, "");
reg1.build();
reg2 = my_reg::type_id::create("reg2");
reg2.configure(this, null, "");
reg2.build();
// 为整个块添加硬件路径
add_hdl_path("top_tb.DUT.reg_block");
// 为每个寄存器添加路径
reg1.add_hdl_path("reg1");
reg2.add_hdl_path("reg2");
// 锁定模型
lock_model();
endfunction
endclass
3. 在寄存器中的配置
方法一:统一路径
class my_reg extends uvm_reg;
`uvm_object_utils(my_reg)
rand uvm_reg_field field1;
rand uvm_reg_field field2;
rand uvm_reg_field field3;
function new(string name = "my_reg");
super.new(name, 32, UVM_NO_COVERAGE);
endfunction
virtual function void build();
field1 = uvm_reg_field::type_id::create("field1");
field2 = uvm_reg_field::type_id::create("field2");
field3 = uvm_reg_field::type_id::create("field3");
// 配置字段
field1.configure(this, 8, 0, "RW", 0, 8'h00, 1, 1, 1);
field2.configure(this, 8, 8, "RW", 0, 8'h00, 1, 1, 1);
field3.configure(this, 16, 16, "RW", 0, 16'h0000, 1, 1, 1);
// 添加硬件路径(整个寄存器)
add_hdl_path("reg_module");
// 为每个字段添加位域切片
field1.add_hdl_path_slice("field1", 0, 8);
field2.add_hdl_path_slice("field2", 8, 8);
field3.add_hdl_path_slice("field3", 16, 16);
endfunction
endclass
方法二:分离路径(字段位于不同模块)
class split_reg extends uvm_reg;
`uvm_object_utils(split_reg)
rand uvm_reg_field field_a;
rand uvm_reg_field field_b;
function new(string name = "split_reg");
super.new(name, 32, UVM_NO_COVERAGE);
endfunction
virtual function void build();
field_a = uvm_reg_field::type_id::create("field_a");
field_b = uvm_reg_field::type_id::create("field_b");
field_a.configure(this, 16, 0, "RW", 0, 16'h0000, 1, 1, 1);
field_b.configure(this, 16, 16, "RW", 0, 16'h0000, 1, 1, 1);
// 字段A位于模块A
field_a.add_hdl_path_slice("module_a.field_a", 0, 16);
// 字段B位于模块B
field_b.add_hdl_path_slice("module_b.field_b", 16, 16);
endfunction
endclass
4. 复杂示例:多层路径
class complex_reg_model extends uvm_reg_block;
`uvm_object_utils(complex_reg_model)
rand sub_block1 blk1;
rand sub_block2 blk2;
virtual function void build();
// 创建子块
blk1 = sub_block1::type_id::create("blk1");
blk1.configure(this, "");
blk1.build();
blk1.add_hdl_path("sub_system1");
blk2 = sub_block2::type_id::create("blk2");
blk2.configure(this, "");
blk2.build();
blk2.add_hdl_path("sub_system2");
// 设置默认路径
set_default_hdl_path("top_tb.DUT");
// 锁定模型
lock_model();
endfunction
endclass
5. 使用宏简化配置
`define ADD_REG_FIELD_SLICE(reg_name, field_name, offset, width) \
reg_name.field_name.add_hdl_path_slice(`"field_name`", offset, width)
class my_reg extends uvm_reg;
// ... 字段声明 ...
virtual function void build();
// ... 字段配置 ...
// 使用宏添加路径切片
`ADD_REG_FIELD_SLICE(this, field1, 0, 8)
`ADD_REG_FIELD_SLICE(this, field2, 8, 8)
`ADD_REG_FIELD_SLICE(this, field3, 16, 16)
endfunction
endclass
6. 后门访问的使用
// 在测试序列中使用后门访问
class my_test_seq extends uvm_sequence;
task body();
uvm_status_e status;
uvm_reg_data_t data;
// 后门读取
reg_model.reg1.field1.read(status, data, .path(UVM_BACKDOOR));
// 后门写入
reg_model.reg1.field2.write(status, 8'hFF, .path(UVM_BACKDOOR));
// 混合访问:前门写,后门验证
reg_model.reg2.write(status, 32'h12345678, .path(UVM_FRONTDOOR));
reg_model.reg2.read(status, data, .path(UVM_BACKDOOR));
if(data == 32'h12345678)
`uvm_info("CHECK", "Register write successful", UVM_LOW)
endtask
endclass
7. 注意事项
-
路径格式:
- 使用"."作为层次分隔符
- 路径必须与实际RTL层次结构匹配
-
路径类型:
add_hdl_path("path", "RTL"); // RTL路径(默认) add_hdl_path("path", "GATES"); // 门级网表路径 add_hdl_path("path", "ALL"); // 所有类型 -
错误处理:
- 确保路径在验证环境构建时已存在
- 使用
check_hdl_path()验证路径有效性
-
性能考虑:
- 后门访问比前门访问快,但不利于验证总线协议
- 建议主要用于初始化和快速检查
8. 调试技巧
// 打印所有HDL路径
reg_model.print_hdl_paths();
// 检查特定路径
if(reg_model.check_hdl_path()) begin
`uvm_info("PATH", "HDL path is valid", UVM_LOW)
end
// 获取路径信息
string paths[$];
reg_model.get_hdl_paths(paths);
这些配置使得UVM寄存器模型能够通过后门直接访问DUT中的寄存器信号,提高仿真效率。
浙公网安备 33010602011771号