这篇文章将增加对寄存器抽象中定义的寄存器的后门访问。 通过附加几行代码,您可以通过后门访问寄存器。
DUT
我们使用与寄存器抽象中定义的相同的DUT(jelly_bean_taster)。 被测设备有两个寄存器,如下所示。
DUT 寄存器
DUT将寄存器的每个字段定义为reg(第4至8行)。
module jelly_bean_taster( jelly_bean_if.slave_mp jb_if );
import jelly_bean_pkg::*;
reg [1:0] taste; // TASTE register
reg [2:0] flavor; // RECIPE register
reg [1:0] color;
reg sugar_free;
reg sour;
reg [1:0] command;
initial begin
flavor = 0;
color = 0;
sugar_free = 0;
sour = 0;
command = 0;
taste = 0;
end
always @ ( posedge jb_if.clk ) begin
if ( jb_if.command == JB_WRITE ) begin
flavor < = jb_if.flavor;
color <= jb_if.color;
sugar_free <= jb_if.sugar_free;
sour <= jb_if.sour;
end else if ( jb_if.command == JB_READ ) begin
jb_if.taste <= #2ns taste;
end
end
always @ ( posedge jb_if.clk ) begin
if ( jb_if.flavor == CHOCOLATE && jb_if.sour ) taste <= YUCKY;
else if ( jb_if.flavor != NO_FLAVOR ) taste <= YUMMY;
end
endmodule: jelly_bean_taster
Testbench
top测试平台将jelly_bean_taster实例化为dut(第6行)。
module top;
import uvm_pkg::*;
reg clk;
jelly_bean_if jb_if( clk );
jelly_bean_taster dut( jb_if ); // DUT
initial begin // clock
clk = 1;
forever #5ns clk = ! clk;
end
initial begin // waveform
$dumpfile( "dump.vcd" );
$dumpvars( 0, top );
end
initial begin
uvm_config_db#( virtual jelly_bean_if )::set( .cntxt( null ),
.inst_name( "uvm_test_top*" ),
.field_name( "jb_if" ),
.value( jb_if ) );
run_test();
end
endmodule: top
Register Block
要通过后门访问DUT寄存器,我们需要通知寄存器块关于其对应的HDL路径(第27行)。 在我们的例子中,寄存器块对应的层次HDL路径是“top.dut”。
class jelly_bean_reg_block extends uvm_reg_block;
`uvm_object_utils( jelly_bean_reg_block )
rand jelly_bean_recipe_reg jb_recipe_reg;
rand jelly_bean_taste_reg jb_taste_reg;
uvm_reg_map reg_map;
function new( string name = "jelly_bean_reg_block" );
super.new( .name( name ), .has_coverage( UVM_NO_COVERAGE ) );
endfunction: new
virtual function void build();
jb_recipe_reg = jelly_bean_recipe_reg::type_id::create( "jb_recipe_reg" );
jb_recipe_reg.configure( .blk_parent( this ) );
jb_recipe_reg.build();
jb_taste_reg = jelly_bean_taste_reg::type_id::create( "jb_taste_reg" );
jb_taste_reg.configure( .blk_parent( this ) );
jb_taste_reg.build();
reg_map = create_map( .name( "reg_map" ), .base_addr( 8'h00 ),
.n_bytes( 1 ), .endian( UVM_LITTLE_ENDIAN ) );
reg_map.add_reg( .rg( jb_recipe_reg ), .offset( 8'h00 ), .rights( "WO" ) );
reg_map.add_reg( .rg( jb_taste_reg ), .offset( 8'h01 ), .rights( "RO" ) );
// for back-door access
add_hdl_path( .path( "top.dut" ) );
lock_model(); // finalize the address mapping
endfunction: build
endclass: jelly_bean_reg_block
Registers
我们还需要通知每个寄存器抽象类有关寄存器字段的HDL路径(第31行)。 在我们的例子中,taste寄存器字段对应于DUT中的taste寄存器。
class jelly_bean_taste_reg extends uvm_reg;
`uvm_object_utils( jelly_bean_taste_reg )
rand uvm_reg_field taste;
//----------------------------------------------------------------------------
// Function: new
//----------------------------------------------------------------------------
function new( string name = "jelly_bean_taste_reg" );
super.new( .name( name ), .n_bits( 2 ), .has_coverage( UVM_NO_COVERAGE ) );
endfunction: new
//----------------------------------------------------------------------------
// Function: build
//----------------------------------------------------------------------------
virtual function void build();
taste = uvm_reg_field::type_id::create( "taste" );
taste.configure( .parent ( this ),
.size ( 2 ),
.lsb_pos ( 0 ),
.access ( "RO" ),
.volatile ( 1 ),
.reset ( 0 ),
.has_reset ( 1 ),
.is_rand ( 0 ),
.individually_accessible( 0 ) );
// for back-door access
add_hdl_path_slice( .name( "taste" ), .offset( 0 ), .size( 2 ) );
endfunction: build
endclass: jelly_bean_taste_reg
我们也将HDL路径设置为RECIPE寄存器(第66至69行)。在这种情况下,我们添加四个HDL路径(每个DUT寄存器一个路径)。
class jelly_bean_recipe_reg extends uvm_reg;
`uvm_object_utils( jelly_bean_recipe_reg )
rand uvm_reg_field flavor;
rand uvm_reg_field color;
rand uvm_reg_field sugar_free;
rand uvm_reg_field sour;
constraint flavor_color_con {
flavor.value != NO_FLAVOR;
flavor.value == APPLE -> color.value != BLUE;
flavor.value == BLUEBERRY -> color.value == BLUE;
flavor.value < = CHOCOLATE;
}
function new( string name = "jelly_bean_recipe_reg" );
super.new( .name( name ), .n_bits( 7 ), .has_coverage( UVM_NO_COVERAGE ) );
endfunction: new
virtual function void build();
flavor = uvm_reg_field::type_id::create( "flavor" );
flavor.configure( .parent ( this ),
.size ( 3 ),
.lsb_pos ( 0 ),
.access ( "WO" ),
.volatile ( 0 ),
.reset ( 0 ),
.has_reset ( 1 ),
.is_rand ( 1 ),
.individually_accessible( 0 ) );
color = uvm_reg_field::type_id::create( "color" );
color.configure( .parent ( this ),
.size ( 2 ),
.lsb_pos ( 3 ),
.access ( "WO" ),
.volatile ( 0 ),
.reset ( 0 ),
.has_reset ( 1 ),
.is_rand ( 1 ),
.individually_accessible( 0 ) );
sugar_free = uvm_reg_field::type_id::create( "sugar_free" );
sugar_free.configure( .parent ( this ),
.size ( 1 ),
.lsb_pos ( 5 ),
.access ( "WO" ),
.volatile ( 0 ),
.reset ( 0 ),
.has_reset ( 1 ),
.is_rand ( 1 ),
.individually_accessible( 0 ) );
sour = uvm_reg_field::type_id::create( "sour" );
sour.configure( .parent ( this ),
.size ( 1 ),
.lsb_pos ( 6 ),
.access ( "WO" ),
.volatile ( 0 ),
.reset ( 0 ),
.has_reset ( 1 ),
.is_rand ( 1 ),
.individually_accessible( 0 ) );
// for back-door access
add_hdl_path_slice( .name( "flavor" ), .offset( 0 ), .size( 3 ) );
add_hdl_path_slice( .name( "color" ), .offset( 3 ), .size( 2 ) );
add_hdl_path_slice( .name( "sugar_free" ), .offset( 5 ), .size( 1 ) );
add_hdl_path_slice( .name( "sour" ), .offset( 6 ), .size( 1 ) );
endfunction: build
endclass: jelly_bean_recipe_reg
这就是你需要的一切。我们来测试后门。
Register Sequence
这个序列演示了通过后门访问寄存器的几种方法。作为进修人员,我们首先通过前门访问寄存器。
- 第24行使用uvm_reg_sequence类的write_reg任务写入RECIPE寄存器。
- 第27行使用uvm_reg_sequence类的read_reg任务从TASTE寄存器中读取。
然后,我们用三种不同的方式通过后门写RECIPE寄存器。
- 第32行使用uvm_reg_sequence类的poke_reg任务。
- 第36行使用带有UVM_BACKDOOR选项的uvm_reg_sequence类的write_reg任务。
- 第41行使用uvm_reg类的写入任务和UVM_BACKDOOR选项。
同样,我们通过三种不同的方式通过后门读取TASTE寄存器。
- 第46行使用uvm_reg_sequence类的peek_reg任务。
- 第49行使用uvm_reg_sequence类的read_reg任务和UVM_BACKDOOR选项。
- 第52行使用带有UVM_BACKDOOR选项的uvm_reg类的读取任务。
class jelly_bean_reg_sequence extends uvm_reg_sequence;
`uvm_object_utils( jelly_bean_reg_sequence )
function new( string name = "" );
super.new( name );
endfunction: new
virtual task body();
jelly_bean_reg_block jb_reg_block;
flavor_e flavor;
color_e color;
bit sugar_free;
bit sour;
uvm_status_e status;
uvm_reg_data_t value;
$cast( jb_reg_block, model );
flavor = APPLE;
color = GREEN;
sugar_free = 0;
sour = 1;
// front-door write
write_reg( jb_reg_block.jb_recipe_reg, status, { sour, sugar_free, color, flavor } );
// front-door read
read_reg( jb_reg_block.jb_taste_reg, status, value );
#20ns ;
// back-door writes
flavor = BLUEBERRY;
poke_reg( jb_reg_block.jb_recipe_reg, status, { sour, sugar_free, color, flavor } );
#10ns ;
flavor = BUBBLE_GUM;
write_reg( jb_reg_block.jb_recipe_reg, status, { sour, sugar_free, color, flavor },
UVM_BACKDOOR );
#10ns ;
flavor = CHOCOLATE;
jb_reg_block.jb_recipe_reg.write( status, { sour, sugar_free, color, flavor },
UVM_BACKDOOR, .parent( this ) );
#10ns ;
// back-door reads
peek_reg( jb_reg_block.jb_taste_reg, status, value );
assert( value == YUMMY );
read_reg( jb_reg_block.jb_taste_reg, status, value, UVM_BACKDOOR );
assert( value == YUMMY );
jb_reg_block.jb_taste_reg.read( status, value, UVM_BACKDOOR, .parent( this ) );
assert( value == YUMMY );
#10ns ;
endtask: body
endclass: jelly_bean_reg_sequence
Simulation
这是一个带注释的波形。前门访问使用jb_if,而后门访问则直接更新DUT的寄存器值。请注意,即使DUT应该对YUCKY响应酸味和巧克力的组合,后门仍然不会更新DUT的taste字段。这是因为DUT会响应jb_if上的值更新taste字段,但不会响应内部RECIPE寄存器值。