Verification Platform
下图显示了RAL相关类的图。 标准UVM类以粉色显示,而 jelly-bean类以浅蓝色显示。 该图看起来乱糟糟一团,但要请放轻松,我会逐一解释每个 jelly-bean类。
Diagram of the Jelly-Bean-Register Related Classes
Register Definitions
在之前的文章中,DUT没有可访问的寄存器。我们将添加容纳 jelly-bean食谱信息和味道的寄存器。我们还将一个命令输入端口添加到DUT中,以便我们可以向寄存器写入果冻配方并阅读它的味道。下图显示了DUT的寄存器定义。
DUT Registers
DUT的源代码(jelly_bean_taster)如下所示。当命令输入为WRITE时,flavor,color,sugar_free和sour输入端口的值被写入RECIPE寄存器(第22至25行)。当命令输入为READ时,TASTE寄存器被读出并且味道输出相应地被驱动(第27行)。
module jelly_bean_taster( jelly_bean_if.slave_mp jb_slave_if );
import jelly_bean_pkg::*;
reg [2:0] flavor;
reg [1:0] color;
reg sugar_free;
reg sour;
reg [1:0] command;
reg [1:0] taste;
initial begin
flavor = 0;
color = 0;
sugar_free = 0;
sour = 0;
command = 0;
taste = 0;
end
always @ ( posedge jb_slave_if.clk ) begin
if ( jb_slave_if.command == jelly_bean_types::WRITE ) begin
flavor < = jb_slave_if.flavor;
color <= jb_slave_if.color;
sugar_free <= jb_slave_if.sugar_free;
sour <= jb_slave_if.sour;
end else if ( jb_slave_if.command == jelly_bean_types::READ ) begin
jb_slave_if.taste <= taste;
end
end
always @ ( posedge jb_slave_if.clk ) begin
if ( jb_slave_if.flavor == jelly_bean_types::CHOCOLATE &&
jb_slave_if.sour ) begin
taste <= jelly_bean_types::YUCKY;
end else if ( jb_slave_if.flavor != jelly_bean_types::NO_FLAVOR ) begin
taste <= jelly_bean_types::YUMMY;
end
end
endmodule: jelly_bean_taster
Register Model
RECIPE寄存器的模型是通过扩展uvm_reg类来定义的。寄存器的每个字段定义为一个uvm_reg_field(第4至7行)。这些字段在构建函数中配置。请注意build方法是为了方便而使用的。不要将它与uvm_component的build_phase混淆,因为uvm_reg不是uvm_component。
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 != jelly_bean_types::NO_FLAVOR;
flavor.value == jelly_bean_types::APPLE
-> color.value != jelly_bean_types::BLUE;
flavor.value == jelly_bean_types::BLUEBERRY
-> color.value == jelly_bean_types::BLUE;
flavor.value < = jelly_bean_types::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 ) );
endfunction: build
endclass: jelly_bean_recipe_reg
TASTE寄存器的模型被类似地定义。
class jelly_bean_taste_reg extends uvm_reg;
`uvm_object_utils( jelly_bean_taste_reg )
rand uvm_reg_field taste;
function new( string name = "jelly_bean_taste_reg" );
super.new( .name( name ), .n_bits( 2 ), .has_coverage( UVM_NO_COVERAGE ) );
endfunction: new
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 ( 1 ),
.individually_accessible( 1 ) );
endfunction: build
endclass: jelly_bean_taste_reg
Register Block
jelly_bean_reg_block包含上面创建的两个寄存器并定义一个寄存器映射。
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" ) );
lock_model(); // finalize the address mapping
endfunction: build
endclass: jelly_bean_reg_block
Register Adapter
jelly_bean_reg_adapter类提供了两个函数来在uvm_reg_bus_op和jelly_bean_transaction之间进行转换。 reg2bus函数将uvm_reg_bus_op转换为jelly_bean_transaction,而bus2reg函数将jelly_bean_transaction转换回uvm_reg_bus_op。
uvm_reg_adapter是一个uvm_object,而不是uvm_component。
高级主题:即使uvm_sequence是uvm_sequence_item,也不能让reg2bus()函数创建uvm_sequence并将其返回。这是因为当读取/写入寄存器时,uvm_reg_map调用uvm_sequence_base :: start_item()传递由reg2bus()返回的对象,但start_item()不指向uvm_sequence。这将导致致命的错误。有关更多详细信息,请参阅寄存器访问方法。
class jelly_bean_reg_adapter extends uvm_reg_adapter;
`uvm_object_utils( jelly_bean_reg_adapter )
function new( string name = "" );
super.new( name );
supports_byte_enable = 0;
provides_responses = 0;
endfunction: new
virtual function uvm_sequence_item reg2bus( const ref uvm_reg_bus_op rw );
jelly_bean_transaction jb_tx
= jelly_bean_transaction::type_id::create("jb_tx");
if ( rw.kind == UVM_READ ) jb_tx.command = jelly_bean_types::READ;
else if ( rw.kind == UVM_WRITE ) jb_tx.command = jelly_bean_types::WRITE;
else jb_tx.command = jelly_bean_types::NO_OP;
if ( rw.kind == UVM_WRITE )
{ jb_tx.sour, jb_tx.sugar_free, jb_tx.color, jb_tx.flavor } = rw.data;
return jb_tx;
endfunction: reg2bus
virtual function void bus2reg( uvm_sequence_item bus_item,
ref uvm_reg_bus_op rw );
jelly_bean_transaction jb_tx;
if ( ! $cast( jb_tx, bus_item ) ) begin
`uvm_fatal( get_name(),
"bus_item is not of the jelly_bean_transaction type." )
return;
end
rw.kind = ( jb_tx.command == jelly_bean_types::READ ) ? UVM_READ : UVM_WRITE;
if ( jb_tx.command == jelly_bean_types::READ )
rw.data = jb_tx.taste;
else if ( jb_tx.command == jelly_bean_types::WRITE )
rw.data = { jb_tx.sour, jb_tx.sugar_free, jb_tx.color, jb_tx.flavor };
rw.status = UVM_IS_OK;
endfunction: bus2reg
endclass: jelly_bean_reg_adapter
Register Predictor
寄存器预测器根据观察到的总线事务更新寄存器模型的值。由于果冻豆寄存器预测器不涉及任何类型的扩展功能,因此使用uvm_reg_predictor。
typedef uvm_reg_predictor#( jelly_bean_transaction ) jelly_bean_reg_predictor;
Agent
jelly_bean_agent实例化jelly_bean_reg_adapter(第35行)。
class jelly_bean_agent extends uvm_agent;
`uvm_component_utils( jelly_bean_agent )
uvm_analysis_port#( jelly_bean_transaction ) jb_ap;
jelly_bean_agent_config jb_agent_cfg;
jelly_bean_sequencer jb_seqr;
jelly_bean_driver jb_drvr;
jelly_bean_monitor jb_mon;
jelly_bean_reg_adapter jb_reg_adapter;
function new( string name, uvm_component parent );
super.new( name, parent );
endfunction: new
function void build_phase( uvm_phase phase );
super.build_phase( phase );
if ( ! uvm_config_db#( jelly_bean_agent_config )::get( .cntxt( this ),
.inst_name ( "" ),
.field_name( "jb_agent_cfg" ),
.value( jb_agent_cfg ))) begin
`uvm_error( "jelly_bean_agent", "jb_agent_cfg not found" )
end
jb_ap = new( .name( "jb_ap" ), .parent( this ) );
if ( jb_agent_cfg.active == UVM_ACTIVE ) begin
jb_seqr = jelly_bean_sequencer::type_id::create( .name( "jb_seqr" ),
.parent( this ) );
jb_drvr = jelly_bean_driver::type_id::create( .name( "jb_drvr" ),
.parent( this ) );
end
jb_mon = jelly_bean_monitor::type_id::create( .name( "jb_mon" ),
.parent( this ) );
jb_reg_adapter = jelly_bean_reg_adapter::type_id::create( .name( "jb_reg_adapter" ) );
endfunction: build_phase
function void connect_phase( uvm_phase phase );
super.connect_phase( phase );
jb_mon.jb_if = jb_agent_cfg.jb_if;
if ( jb_agent_cfg.active == UVM_ACTIVE ) begin
jb_drvr.seq_item_port.connect( jb_seqr.seq_item_export );
jb_drvr.jb_if = jb_agent_cfg.jb_if;
end
jb_mon.jb_ap.connect( jb_ap );
endfunction: connect_phase
endclass: jelly_bean_agent
Environment Configuration
jelly_bean_env_config具有jelly_bean_reg_block的句柄,以便jelly_bean_env可以访问寄存器模型。
class jelly_bean_env_config extends uvm_object;
`uvm_object_utils( jelly_bean_env_config )
bit has_jb_agent = 1;
bit has_jb_sb = 1;
jelly_bean_agent_config jb_agent_cfg;
jelly_bean_reg_block jb_reg_block;
function new( string name = "" );
super.new( name );
endfunction: new
endclass: jelly_bean_env_config
Enviroment
jelly_bean_env实例化jelly_bean_agent和jelly_bean_reg_predictor(第28至31行),然后连接与寄存器相关的对象:
首先,在connect_phase()中,set_sequencer()函数将果冻豆序列器和寄存器适配器与寄存器映射关联(第45和46行)。必须在基于uvm_reg_sequence开始序列之前调用set_sequencer()。寄存器块可能有多个寄存器映射。
其次,寄存器映射和寄存器适配器与寄存器预测器相关联(第49和50行)。寄存器预测器将使用寄存器映射和寄存器适配器将jelly_bean_transaction转换回寄存器操作。
最后,寄存器预测器连接到代理以订阅来自代理的jelly_bean_transactions(第51行)。
class jelly_bean_env extends uvm_env;
`uvm_component_utils( jelly_bean_env )
jelly_bean_env_config jb_env_cfg;
jelly_bean_agent jb_agent;
jelly_bean_fc_subscriber jb_fc_sub;
jelly_bean_scoreboard jb_sb;
jelly_bean_reg_predictor jb_reg_predictor;
function new( string name, uvm_component parent );
super.new( name, parent );
endfunction: new
function void build_phase( uvm_phase phase );
super.build_phase( phase );
if ( ! uvm_config_db#( jelly_bean_env_config )::get
( .cntxt( this ),
.inst_name( "" ),
.field_name( "jb_env_cfg" ),
.value( jb_env_cfg ) ) ) begin
`uvm_fatal( get_name(), "jb_env_cfg not found" )
end
uvm_config_db#( jelly_bean_agent_config )::set( .cntxt( this ),
.inst_name( "jb_agent*" ),
.field_name( "jb_agent_cfg" ),
.value( jb_env_cfg.jb_agent_cfg ) );
jb_agent = jelly_bean_agent::type_id::create( .name( "jb_agent" ),
.parent( this ) );
jb_reg_predictor = jelly_bean_reg_predictor::type_id::create( .name( "jb_reg_predictor" ),
.parent( this ) );
if ( jb_env_cfg.has_jb_sb ) begin
jb_sb = jelly_bean_scoreboard::type_id::create( .name( "jb_sb" ),
.parent( this ) );
end
jb_fc_sub = jelly_bean_fc_subscriber::type_id::create( .name( "jb_fc_sub" ),
.parent( this ) );
endfunction: build_phase
function void connect_phase( uvm_phase phase );
super.connect_phase( phase );
jb_agent.jb_ap.connect( jb_fc_sub.analysis_export );
jb_agent.jb_ap.connect( jb_sb.jb_analysis_export );
if ( jb_env_cfg.jb_reg_block.get_parent() == null ) begin // if the top-level env
jb_env_cfg.jb_reg_block.reg_map.set_sequencer( .sequencer( jb_agent.jb_seqr ),
.adapter( jb_agent.jb_reg_adapter ) );
end
jb_env_cfg.jb_reg_block.reg_map.set_auto_predict( .on( 0 ) );
jb_reg_predictor.map = jb_env_cfg.jb_reg_block.reg_map;
jb_reg_predictor.adapter = jb_agent.jb_reg_adapter;
jb_agent.jb_ap.connect( jb_reg_predictor.bus_in );
endfunction: connect_phase
endclass: jelly_bean_env
Base Test
基本测试实例化jelly_bean_reg_block(第16和17行)并将其句柄存储在jelly_bean_env_config(第19和20行)中。
class jelly_bean_base_test extends uvm_test;
`uvm_component_utils( jelly_bean_base_test )
jelly_bean_env jb_env;
jelly_bean_env_config jb_env_cfg;
jelly_bean_agent_config jb_agent_cfg;
jelly_bean_reg_block jb_reg_block;
function new( string name, uvm_component parent );
super.new( name, parent );
endfunction: new
function void build_phase(uvm_phase phase);
super.build_phase(phase);
jb_reg_block = jelly_bean_reg_block::type_id::create( "jb_reg_block" );
jb_reg_block.build();
jb_env_cfg = jelly_bean_env_config::type_id::create( "jb_env_cfg" );
jb_env_cfg.jb_reg_block = jb_reg_block;
jb_agent_cfg = jelly_bean_agent_config::type_id::create( "jb_agent_cfg" );
if ( ! uvm_config_db#( virtual jelly_bean_if )::get( .cntxt( this ),
.inst_name( "" ),
.field_name( "jb_if" ),
.value( jb_agent_cfg.jb_if ))) begin
`uvm_error( "jelly_bean_test", "jb_if not found" )
end
jb_env_cfg.jb_agent_cfg = jb_agent_cfg;
uvm_config_db#(jelly_bean_env_config)::set( .cntxt( null ),
.inst_name( "*" ),
.field_name( "jb_env_cfg" ),
.value( jb_env_cfg ) );
jb_env = jelly_bean_env::type_id::create( .name( "jb_env" ),
.parent( this ) );
endfunction: build_phase
virtual function void start_of_simulation_phase( uvm_phase phase );
super.start_of_simulation_phase( phase );
uvm_top.print_topology();
endfunction: start_of_simulation_phase
endclass: jelly_bean_base_test
Sequence without Register Abstraction
我们现在已经创建了所有组件,并且我们准备创建一个寄存器序列。但在这之前,我们先创建一个“常规”序列,而不使用寄存器抽象,以便进行比较。 jelly_bean_sequence是生成 sour-green-apple jelly_bean的序列。该序列的主体创建一个jelly_bean_transaction,驱动程序将使用它驱动引脚。class jelly_bean_sequence extends uvm_sequence#( jelly_bean_transaction );
`uvm_object_utils( jelly_bean_sequence )
function new( string name = "" );
super.new( name );
endfunction: new
task body();
jelly_bean_transaction jb_tx;
jb_tx = jelly_bean_transaction::type_id::create( .name( "jb_tx" ),
.contxt( get_full_name()));
start_item( jb_tx );
jb_tx.flavor = jelly_bean_types::APPLE;
jb_tx.color = jelly_bean_types::GREEN;
jb_tx.sugar_free = 0;
jb_tx.sour = 1;
finish_item(jb_tx);
endtask: body
endclass: jelly_bean_sequence
Sequence Using Register Abstraction
jelly_bean_reg_sequence是另一种产生 sour-green-apple jelly_bean的序列,但是使用了寄存器抽象。该序列从uvm_reg_sequence类扩展,以便我们可以使用诸如write_reg()和read_reg()之类的便利功能。序列的主体将一个配方(第23行)写入RECIPE寄存器,然后从TASTE寄存器(第24行)回读其味道。请注意,我们不会在序列中创建jelly_bean_transaction。注册适配器将把注册操作转换成相应的jelly_bean_transactions。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;
jelly_bean_types::flavor_e flavor;
jelly_bean_types::color_e color;
bit sugar_free;
bit sour;
uvm_status_e status;
uvm_reg_data_t value;
$cast( jb_reg_block, model );
flavor = jelly_bean_types::APPLE;
color = jelly_bean_types::GREEN;
sugar_free = 0;
sour = 1;
write_reg( jb_reg_block.jb_recipe_reg, status, { sour, sugar_free, color, flavor } );
read_reg ( jb_reg_block.jb_taste_reg, status, value );
endtask: body
endclass: jelly_bean_reg_sequence
Register Test
jelly_bean_reg_test创建一个我们刚刚创建的jelly_bean_reg_sequence并启动序列(第12行至第15行)。
class jelly_bean_reg_test extends jelly_bean_base_test;
`uvm_component_utils( jelly_bean_reg_test )
function new( string name, uvm_component parent );
super.new( name, parent );
endfunction: new
task main_phase( uvm_phase phase );
jelly_bean_reg_sequence jb_reg_seq;
phase.raise_objection( .obj( this ) );
jb_reg_seq = jelly_bean_reg_sequence::type_id::create( .name( "jb_reg_seq" ),
.contxt( get_full_name()));
jb_reg_seq.model = jb_reg_block;
jb_reg_seq.start( .sequencer( jb_env.jb_agent.jb_seqr ) );
#100ns;
phase.drop_objection( .obj( this ) );
endtask: main_phase
endclass: jelly_bean_reg_test
Simulation
我们来看一个仿真结果。仿真成功地生成了一个 sour-green苹果,并从DUT读回它的味道。
UVM_INFO jb3.sv(727) @ 30: uvm_test_top.jb_env.jb_sb [jelly_bean_scoreboard] You have a good sense of taste.
---------------------------------------------------------
Name Type Size Value
---------------------------------------------------------
jb_tx jelly_bean_transaction - @7929
flavor jelly_bean_types::flavor_e 3 APPLE
color jelly_bean_types::color_e 2 GREEN
sugar_free integral 1 'h0
sour integral 1 'h1
command jelly_bean_types::command_e 2 WRITE
taste jelly_bean_types::taste_e 2 NO_TASTE
---------------------------------------------------------
UVM_INFO jb3.sv(727) @ 60: uvm_test_top.jb_env.jb_sb [jelly_bean_scoreboard] You have a good sense of taste.
----------------------------------------------------------
Name Type Size Value
----------------------------------------------------------
jb_tx jelly_bean_transaction - @7928
flavor jelly_bean_types::flavor_e 3 NO_FLAVOR
color jelly_bean_types::color_e 2 NO_COLOR
sugar_free integral 1 'h0
sour integral 1 'h0
command jelly_bean_types::command_e 2 READ
taste jelly_bean_types::taste_e 2 YUMMY
----------------------------------------------------------
我希望本教程能帮助你理解UVM寄存器抽象。
您可以在EDA Playground上查看并运行代码。