这篇文章将增加对寄存器抽象中定义的寄存器的后门访问。 通过附加几行代码,您可以通过后门访问寄存器。
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寄存器值。