Last Updated on November 6, 2016
This post will explain how to use the UVM Register Abstraction Layer (RAL) to generate register transactions. The figure below shows the verification platform used for this post. Among other things, the jelly_bean_reg_block
, the jelly_bean_reg_adapter
, and the jelly_bean_reg_predictor
are the classes used for the register abstraction.
The figure below shows the diagram of the RAL-related classes. The standard UVM classes are shown in pink, while the jelly-bean classes are shown in light blue. The diagram looks busy, but bear in mind that I will explain each jelly-bean class one by one.
Register Definitions
In the previous posts, the DUT had no accessible registers. We are going to add the registers that hold jelly-bean recipe information and its taste. We will also add a command input port to the DUT so that we can write a jelly-bean recipe to the register and read its taste. The figure below shows the register definition of the DUT.
The source code of the DUT (jelly_bean_taster
) is shown below. When the command
input is WRITE
, the values of flavor
, color
, sugar_free
, and sour
input ports are written to the RECIPE register (line 22 to 25). When the command
input is READ
, the TASTE register is read out and the taste
output is driven accordingly (line 27).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | 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
The model of the RECIPE register is defined by extending the uvm_reg
class. Each field of the register is defined as a uvm_reg_field
(line 4 to 7). The fields are configured in the build
function. Note that the name, build
, is used for convenience. Do not confuse it with the build_phase
of the uvm_component
because the uvm_reg
is not a uvm_component
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | 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 |
The model of the TASTE register is similarly defined.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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
The jelly_bean_reg_block
contains the two registers created above and defines a register map.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | 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
The jelly_bean_reg_adapter
class provides two functions to convert between a uvm_reg_bus_op
and a jelly_bean_transaction
. The reg2bus
function converts a uvm_reg_bus_op
into a jelly_bean_transaction
, whereas the bus2reg
function converts a jelly_bean_transaction
back to a uvm_reg_bus_op
.
The
uvm_reg_adapter
is auvm_object
, not auvm_component
.
Advanced Topic: Even though a
uvm_sequence
is auvm_sequence_item
, you cannot let thereg2bus()
function create auvm_sequence
and return it. This is because when a register is read/written, theuvm_reg_map
callsuvm_sequence_base::start_item()
passing the object returned by thereg2bus()
, but thestart_item()
does not expect auvm_sequence
. This will cause a fatal error. For more details, please see Register Access Methods.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | 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
The register predictor updates the values of the register model based on observed bus transactions. As the jelly-bean register predictor is not involved with any kind of extended features, the uvm_reg_predictor
is used as is.
typedef uvm_reg_predictor#( jelly_bean_transaction ) jelly_bean_reg_predictor; |
Agent
The jelly_bean_agent
instantiates the jelly_bean_reg_adapter
(line 35).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | 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
The jelly_bean_env_config
has a handle to the jelly_bean_reg_block
so that the jelly_bean_env
can access the register model.
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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 |
Environment
The jelly_bean_env
instantiates the jelly_bean_agent
and the jelly_bean_reg_predictor
(line 28 to 31), then connects register-related objects:
Firstly, in the connect_phase()
, the set_sequencer()
function associates the jelly-bean sequencer and the register adapter with the register map (line 45 and 46). The set_sequencer()
must be called before starting the sequence based on a uvm_reg_sequence
. A register block may have more than one register map.
Secondly, the register map and the register adapter are associated with the register predictor (line 49 and 50). The register predictor will use the register map and the register adapter to convert a jelly_bean_transaction
back to a register operation.
Lastly, the register predictor is connected to the agent to subscribe the jelly_bean_transaction
s from the agent (line 51).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | 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
The base test instantiates a jelly_bean_reg_block
(line 16 and 17) and stores its handle in the jelly_bean_env_config
(line 19 and 20).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | 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
We have created all the components by now and we are ready to create a register sequence. But before doing that, let’s create a “regular” sequence without using the register abstraction for comparison. The jelly_bean_sequence
is a sequence to generate a sour-green-apple jelly bean. The body
of the sequence creates a jelly_bean_transaction
, which will be used by the driver to do pin wiggling.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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
The jelly_bean_reg_sequence
is another sequence to generate a sour-green-apple jelly bean, but using the register abstraction. This sequence is extended from the uvm_reg_sequence
class so that we can use the convenience functions such as write_reg()
and read_reg()
. The body
of the sequence writes a recipe (line 23) to the RECIPE register, then reads back its taste from the TASTE register (line 24). Note that we do not create a jelly_bean_transaction
in the sequence. The register adapter will convert the register operations into the corresponding jelly_bean_transaction
s.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | 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
The jelly_bean_reg_test
creates a jelly_bean_reg_sequence
we have just created and starts the sequence (line 12 to 15).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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
Let’s look at a simulation result. The simulation successfully generated a sour-green apple and read back its taste from the DUT.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | 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---------------------------------------------------------- |
I hope this tutorial helped you to understand the UVM Register Abstraction.
You can view and run the code on EDA Playground.
I am interested in how you create your diagrams: are the block diagrams from visio or some other tool? Also, the class hierarchy looks like the dot-language. Am I correct? If so, is this automatically generated (e.g. from within NaturalDocs?).
Hi Jeremy,
Yes, I used Visio to draw the block diagrams. For the class diagrams, I used UMLGraph. UMLGraph takes Java files and generates the class diagrams in dot language. I wrote UVM classes (but only skeletons) in Java. For the sequence diagrams, I used Web sequence diagrams, another tool I like.
Is the arrow direction from jelly_bean_recipie_reg to uvm_reg_map correct?
Shouldn’t it be vice versa, meaning that uvm_reg_map depends on jelly_bean_recipie_reg?
You are right. I have fixed the class diagram. Thank you for pointing this out.
Hi,
I Am Anand, Trying to model UVM RAL Model for my IP by following your example.
I am following your blog, It was very useful for me to understand & trying to mimic same in my environment, Here i am facing following problem:
* When i am setting the sequencer & adapter, like
env_cfg0.reg_block.reg_map.set_sequencer( .sequencer(apb_env0.master.sequencer), .adapter(adapter0) )
says, null pointer at this line.
I am having a virtual sequence to control multiple interfaces in my env.
Could you help me out to fix the above issue.
Let me know you if require further details to understand the issue better.
Thanks in advance…
Regards,
Anand
Hi Anand,
I am glad that my blog was useful.
Regarding the null pointer problem, I am not sure which object is null by just looking at the line. Have you instantiated the
apb_env0.master.sequencer
and theadapter0
? If you could paste more lines and/or the error message, I might be able to figure out.Hi Shimizu,
I could figure out problem in the above line, As i was not calling the “reg_block.build()”, it was reporting null pointer error.
Now i am deviating problem statement to uvm_reg_sequence level, where i am casting ‘model’ with ‘reg_block’, i am getting null pointer access.
Thanks and Regards,
Anand
sorry, the error was pointing at line no.22
i,e reg_block.intr_en_reg.write(status, ‘h10)
I could fix the issue..,now it is working…:) !!
Thank you once again..
Thanks and Regards,
Anand
Good to hear that it is working.
Hey Kiesuke,
I had few questions:
1)Why is the DUT coding (especially for read) is different on this page with respect to the same example on GitHub?
2)Also I want to ask, for read, the value that is returned has to be given by driver? But in real scenarios this doesn’t seem practical.
Thank you,
Regards,
Muneeb Ulla Shariff
taste
without aREAD
command.taste
) from thejelly_bean_if
. It does not drive thetaste
.What was the issue Anand? Is it that you cannot pass an object via uvm_do_on_with??
No that was not the problem Manjunath, I forgot to call the build() method of reg_block in the env.
so what happened was when i am trying to access the registers inside the reg_seq to perform write/read operations, i was getting null pointer access.
Hope you got the problem statement…
Thanks,
Anand Kola
Hi,
I need help in getting the functional coverage for register model. I generated Register model package and covergroups for each register using the Register Assistant tool from Mentor Graphics. Small code snippet for single register covergroup as shown below:
In the base test class, I have written the following code:
In the makefile, I used the following command:
But, Questasim tool was not able to generate any UCDB files related to coverage.
Is there any code should be written to get the Register coverage? Please help me…
Thanks,
Sivaji
Hi Sivaji,
I don’t have access to Mentor’s tools, so I might be wrong, but did you call the
proj_rm.sample_values()
from your testbench to sample the register values?Keisuke
I tried by calling proj_rm.sample() from my testbench, but no file generated related to coverage.
Please suggest me, if there is any option.
Hi,
I have multiple bus interfaces in the testbench, i control sequences through common virtual sequencer. Now i need to incorporate RAL at this bus virtual sequencer level. How to do that ? ie. using a single generic adaptor for all interfaces.
Hi Gayathri,
I assume your DUT has two bus interfaces (bus_A and bus_B) and you can access the registers in the DUT through the bus_A and the bus_B. If that is the case, I would suggest:
reg_block
) as usual.reg_map_A
andreg_map_B
) in the register block usingcreate_map()
. Thereg_map_A
defines the address map of the registers using the bus_A, while thereg_map_B
defines the address map of the registers using the bus_B.reg_adapter_A
andreg_adapter_B
). Thereg_adapter_A
converts a register operation into a bus_A transaction, while thereg_adapter_B
converts a register operation into a bus_B transaction.set_sequencer()
so that thereg_map_A
will use thereg_adapter_A
and thereg_map_B
will use thereg_adapter_B
.reg_block.reg_X.read(status, value, .map(reg_block.reg_map_A))
.Let me know if you need further clarification.
Hi Keisuke,
In the above case what happens if both drivers initiate a write transaction to same register with different address map, Which transaction will get a priority? Is testbanch has to be smart enough to do arbitration, If so then in which component/object we need code this arbitration logic?
If you write the same register from two drivers, there is a race condition. Either your virtual sequence or upper protocol need to take care of it if arbitration is required.
Hi Keisuke,
If I have to write more than 2 registers in the reg2bus or bus2reg methods under same condition, like in adapter class,
In that case, how do we manage with rw.data value for two registers ?
How can we define a bunch of registers that adopt the similar properties and are to be written under same condition. Eg: During reset phase.
Thank You..!
S
As far as I know, you cannot associate a
bus_item
to more than onerw
in thebus2reg
. If all you need is to set some values to multiple registers, you could do:As all your registers are the descendants of
uvm_reg
, you can set thesome_value
without knowing the actual type of the registers (the beauty of polymorphism). Let me know if I did not answer your question.Thank you very much Keisuke..!
That solves my puzzle..!
You are welcome.
Hi,
I was wondering that the reg_adapter may be better not to included in agent, instead it may be included in reg model. the adapter looks more close to reg model. Am I right?
The idea behind the reg adapter is to convert a generic
uvm_reg_bus_op
to/from a specific bus transaction. For example, thejelly_bean_reg_adapter
converts auvm_reg_bus_op
to/from ajelly_bean_transaction
, but you might want to create another adapter for likegummy_bear_transaction
later. Since the adapter is dependent of underlying bus transaction, I put it in the agent.I am using Register model in my project, now I have to create an adaptor class. But the problem is to convert the bus_sequence_item into reg_item, I have to see what is value already set in the registers according to which the conversion have to be done. I can’t take handle of configuration in apaptor class because its an object. What is the possible solution for this.
I am not sure if I fully understand your question, but your agent that creates the adapter can pass the handle to the configuration object after the agent creates the adapter.
Thank you for your time Keisuke.
Passing handle will work, but I was thinking if there is something similar to uvm_config_db :: get which is for components and not objects, to make it(adapter) more independent.
My concern is that in one of the registers there are fields which tells how the data is distributed on the data line, it can be either serial or staggered and other such things. Now when the further transactions take place, I have to convert the data and store in register accordingly.
Although the
uvm_reg_adapter
is auvm_object
, we can still use theuvm_config_db
. For example, we could create a new configuration setting in a test like:Then, in the
jelly_bean_reg_adapter
, we could get the value by:Note that since the
uvm_reg_adapter
is not auvm_component
, it does not have a hierarchical name. Thus, we usednull
as the context. Theget
can findjb_env_cfg
as long as theinst_name
matches.Hi Keisuke,
I was wondering why in reg2apb() function of uvm_reg_adapter we have arguments with const ref uvm_reg_bus_op, what is the significance of using const ref there !! Similarly in bus2reg() why only ref is enough .. Plase explain
Best Regards,
Vittal
Since the
uvm_reg_bus_op
is a structure, not a class handle, each member of the structure will be copied (pass by value) when it is passed as an argument to a function. To avoid the expensive copy,ref
(pass by reference) is used, and to protect therw
from being modified by thereg2bus
,const
is also used.Unless
ref
is used, the caller ofbus2reg
won’t see the change ofrw
because pass by value is the default mechanism for passing arguments to functions. You cannot useconst
here because you need to modify therw
.Hi,
Thanks for your useful tutorial. I am new in UVM. I want to know the meaning and importance of ” $cast( jb_reg_block, model ) ” this statement.what is this “model”? .
Regards,
Kousik
The
model
is a property ofuvm_reg_sequence
class (the base class of thejelly_bean_reg_sequence
). Themodel
holds the register block abstraction theuvm_reg_sequence
executes on. We used ourjb_reg_block
as themodel
(please see the line 14 ofjelly_bean_reg_test
). Since the type ofmodel
isuvm_reg_block
, we needed the$cast( jb_reg_block, model )
to access the properties specific to thejb_reg_block
, such asjb_recipe_reg
andjb_taste_reg
.Hi Keisuke,
Great Article.
I have one problem regarding Explicit Prediction method.
I have created RAL Model for my UART Protocol and I made necessary connections for that.
Everything is working fine.
But the problem is, when is use explicit prediction method, the mirror value is not being updated.
In Auto prediction, Its working Fine.
For Explicit prediction, do I need to add some extra code to update the mirror value of registers.
Please help me, I am struggling with this problem.
Check the connection between your analysis port and the
bus_in
of your predictor. Does your monitor callwrite
to the analysis port? Also check whether thebus2reg
function of your adapter is called.Hi Keisuke,
Can you explain how Predict method works ?
The
predict
method is used to update the mirrored value of the register model. Please see Register Access Methods for more detail.Hi Keisuke,
First of all, thank you for this page that makes order in all these uvm objects and how they work together.
In my verification environment, the reads and writes of registers are done using a UART agent. The problem is that for each write or read of a register I need to generate multiple uart_trans items, because each register access consists of an entire message (header, command, address and data, and checksum).
What is the way to associate the reg_adaptor with such a mechanism? will I need then a virtual agent that will process the UART transactions in a transaction layer?
Thanks
I am glad to hear that my blog was useful. For your UART, I would create a class that groups together the
uart_trans
like:The
trans
array will hold UART header, command, address, data, etc. Then, I would define thereg2bus
function that converts a register read/write into this class and fills thetrans
array.For the driver side, I would create a layered sequencer; one to process the
uart_trans_group
and the other to process theuart_trans
in the group. The downstream sequencer runs a sequence that breaks down auart_trans_group
into multipleuart_trans
.Similarly for the predictor side, I would create a layered monitor; one to monitor UART signals and the other to group the
uart_trans
. There might be a better solution. I welcome any suggestions.Hi Keisuke,
Can you explain how mirror value gets updated during explicit prediction?
The steps 5 and 6 of How the write() method works and the steps 7 and 8 of How the read() method works might answer your question.
Thanks for the reply I have one more doubt how UVM_IS_X status is used ?.
You can set
rw.status = UVM_HAS_X
in thebus2reg
function of your reg adapter if the bus data contains X. But as far as I know, the RAL treatsUVM_HAS_X
asUVM_IS_OK
, so there is no difference whether you useUVM_HAS_X
or not.Hi Keisuke,
Do you consider this is a bug in uvm? Why not fix uvm source code to handle x detection in register checking?
BTW, thanks for the great work!
Hi Keisuke, what is the software you are using to generate those wonderful diagrams ?
I used UMLGraph to generate the class diagram. I used Microsoft Visio® for the rest.
Hi Keisuke,
Can you explain a little about how the address of a register is get passed to a driver in a memory based system?
I mean in the reg_map which I snipped as following, since really_bean_taster is not address based configuration system, the addresses’ of the two register don’t used in your example, but for a complex real application, how the address of the registers’ is transferred/passed to a driver?
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
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” ) );
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Thanks,
Rui Huang
To make the address availble on the bus, define the address in the transaction:
Then, assign the
addr
in the functions of the reg adapter:Based on the base-address (
8'h00
) and the offset (8'h00
or8'h01
) defined in thejelly_bean_reg_block
, you will see either8'h00
(jb_recipe_reg
) or8'h01
(jb_taste_reg
) on the address bus.Hi Keisuke,
I have been following you tutorial for some time now. Thanks for all the effort you have put into this tutorial. It is very beneficial for people like me.
I just started going through this tutorial. I have a few questions.
1) I am not able to understand how register modelling is helpful. I see that here the register values in register block follow the value in the design’s register. I am not able to figure out where the comparison for correctness is done.
2) Also I see in the tutorial source you have provided tutorial_9.sv “https://github.com/cluelogic/uvm-tutorial-for-candy-lovers/blob/master/src/tutorial_9.sv” , you have coded a scoreboard component. In the class diagram it is missing. Is it required here?
3) Under what circumstances do we use a register model?
4) The reg predictor class, is it for predicting expected register values?
Regards,
Gautam
How to write register sequences for UVM Register Model?
What would be the desired and mirrored values w.r.t. read & write access methods?
The jelly_bean_reg_sequence is an example of register sequences. Both
read
andwrite
methods update thedesired
and themirrored
values. Please see how the read method works and how the write method works for more detail.Hi Mr.Keisuke Shimizu, I have basic doubt. In RAL based sequence of READ (i.e Auto predict mode, not explicit prediction ) how the read data got updated into the Register models ??? Thanks in advance for your graphical answer :).
When the auto-predict mode is set, the
read()
task will call thedo_predict()
function to update the mirrored value, etc. before the task returns the read data.Hey Kiesuke,
Your posts are amazing. They are very informative and very easy to follow.
I have a question with regards to auto predict mode:
Since, we are not monitoring the bus to see what value the DUT gives on read, which value does the do_predict use to update the mirror value?
Thank you,
Regards,
Muneeb Ulla Shariff
The
do_predict
uses the value theread
returns. It uses the value shown as (6) in the above diagram.Hi,
I am trying to make use of the RAL generated coverage at the full chip level. From monitor I am collecting data, and using base address and offset I am getting register name(get_reg_by_offset). I am facing issue in the next step, if I try to use write method of the register I am getting error saying sequencer not set, so I tried using sample method of the register in couple of ways that is also not worked. Could you please help me to resolve this issue.
thanks
Sudhish
Did you call
your_reg_map.set_sequencer()
? If you could paste the snippet of your code, I might be able to help.Hi Keisuke Shimizu,
First of all thank you for this wonderful blog, helped a lot in understanding and developing a RAL model.
I have a simple memory interface, in which
* Write & Read takes one clk to update-to and read-out from memory.
* o_read_data is the memory output with o_data_vld (and its working as expected for the given address)
* Whereas on the RAL side, the o_read_data is always having the value zero, though I am sampling its value when risign_edge of o_data_vld,
hence
I always get the data mismatch UVM_ERROR for predictor-mirror. and also for .read operation I will have the read_value zero.
Please help me out in resolving this issue.
Thank you,
Tejas T V
Did you see the correct
o_rd_data
value in your monitor? It seems that theo_rd_data
has not changed@ ( posedge v_if.o_data_vld )
. Can you try something like this to avoid the race?Hi Keisuke,
I have tried with your suggestion, but still I see a problem here, RAL is comparing the data before the valid data is seen in the monitor.
It seems that the
mirror()
returns too soon, even before the monitor sees the transaction on the bus. Do you use the auto-predict mode? If so, check if your driver returns the read value to the sequencer correctly.Do not use @(posedge v_if.o_data_vld)
…. in monitor..
(if you want this task to end, you can quit out of the forever loop once you see your txn..but I assume your monitor would just run till the phase was terminated.)
Note that this will show up at the “next clock edge” to when the valid is present. This is when any “hardware” might sample it anyway, so that should be reasonable.
If you were to go to gate level, the timing of the valid and data relative to clock might be such that it would not become valid immediately after the rising edge of the clock.
I recommend you look into clocking blocks as part of your interface as well. And use the clocking vars and clocking events instead of the interface wires if you ever plan on this working with gate level delays in your RTL (DUT).
By way of explanation,
your code:
Please don’t take this as a criticism. my “comments” are just information, not putdowns.
I’m an architect and RTL designer, not really much of a validator but I am learning .. thanks,
Hi Keisuke Shimizu,
I am trying to implement RAL model UVM env by taking your example as reference. But I am getting following error.
Can you please look into it and help me.
UVM_WARNING /lin_tools/synopsys/vcs_mx_J-2014.12-sp3/etc/uvm/reg/uvm_reg_sequence.svh(137) @ 0: uvm_test_top.env.sagent.ssp_seqr@@s_seq [REG_XLATE_NO_SEQR] Executing RegModel translation sequence on sequencer uvm_test_top.env.sagent.ssp_seqr’ does not have an upstream sequencer defined. Execution of register items available only via direct calls to ‘do_rw_access’
Thanks,
Jay
Hi Keisuke
I solved the above mentioned issue. It was mistake. My sequence was extended from uvm_reg_sequence and I was calling “super.body” in actual body method of sequence. I removed it. It works fine. Thanks for your blog. Great Help
I am not aware of internal details of uvm_reg_sequence. If possible can you explain why body method is present in uvm_reg_sequence, if it is required, Why can’t I use super.body.
Thanks in Advance.
The
uvm_reg_sequence
can be used as two ways. One way is as the base class of a user-defined register sequence as you did. The other way is as a register-to-bus-transaction translator. If the upstream sequencer (reg_seqr
) is specified, thebody
method of theuvm_reg_sequence
gets auvm_reg_item
from thereg_seqr
and converts it to ajelly_bean_transaction
(see the figure below).The reason you must not call
super.body()
in a user-defined sequence is that the translation logic mentioned above will execute otherwise.Hi,
Though this is not a reply, but I could not find a switch to post a question.
It is related to the user defined uvm_reg_sequence.
Consider a case where a register field is written followed by another write to same register but to a different field.
Now, two things can be done:
1. If these are back to back accesses, they can be clubbed to a single write on bus interface. This intelligence has to be built in user extended uvm_reg_sequence?
2. If these two writes are not back to back, so user_reg_sequence (extended from uvm_reg_sequence) has to do read-modify-write. Is that correct? Or, is it taken care by some magic in uvm?
You can control how the fields are written. For example, if you want to combine the two writes to one, you could do:
Or, if you want two separate writes, you could do:
In the latter case, if the target bus supports byte-enable and the field is the only field in a byte lane, then only the field is written. Otherwise, the entire register is written using the mirrored values of the other fields. So, you don’t have to use Read-Modify-Write if you think mirrored values are good enough. I hope I answered your questions.
Hi Simizu san,
I would like to know how can i define a register which has Read Only access type for BUS_A and WRitable access type for BUS_B. Is it possible to create a map that overwrites the default access type configured in the register?. How can this be done?. is it with the add_reg function to the register map?.
I came to this problem with the register assistant from Mentor. This tool generates the systemverilog registermodel classes automatically for you from excel tables where the registers are described. However, this tool doesn´t allow you to define different access type for individual register maps. The register maps can only have different address offsets but not different access types. Makes this sense or is it a tool limitation?. Any idea?
Thanks in advance
You should be able to set different accessibility when you do
add_reg()
as you mentioned. For example, you could do:The register model restricts the accessibility based on the
rights
argument of theadd_reg()
on top of the field access policy.Unfortunately, I don’t have access to the tool you mentioned, so I don’t know if this is a tool limitation or not.
Hi Keisuke,
No I am not using auto-predict mode.
Does your driver use
get_next_item()
/item_done()
orget()
/put(rsp)
? If the latter, do you set theprovides_responses
to1
in your register adapter?I am using get_next_item and item_done and I have not made use of response port.(is this causing early execution of bus2reg ?).
and my provides_responses is always zero(set during the construction of adapter class, and not modifying there after)
The driver must set the
txn.rd_data
value before calling theitem_done()
.Can you please explain why driver has to set the txn.rd_data ? as it is DUT’s output and it shall be monitored by a monitor ?
The
mirror
task compares the data returned from the driver against the mirrored value regardless of the prediction mode.Thank you, this resolved my problem.
but please clarify the below doubts,
1. If the mirror function compares the returned transaction from driver, why we connect the monitor ap to predictor’s bus_in implementation port ?
2. Shouldn’t be driver independent of slave’s response ? why not monitor tells the predictor the valid transaction ?
Hi
I am Sasha and I am having the same issue as Tejas T V reported while ago. The mirror() task starts comparison before the bus monitor collects the transaction on the bus. As result, I get this error – “model.reg_xxx” value read from DUT (0x0000000000000000) does not match mirrored value (0x00000000f0031060). However, I clearly see the correct data was read back and captured by the monitor. Also, the mirrored value has been updated with the correct data, but somehow this two processes are not sync’ed.
My driver use get_next_item()/item_done() for handshaking with the sequencer. Also, in my register adapter I do not use provides_responses.
What should I change in order to make the mirror() pass?
Thank you
Does your driver set the read value to the transaction before calling
item_done()
?I did as you suggested, now mirror() method works fine.
Thank you very much
I’m glad to hear it worked. Thanks.
Hi,
First, Thanks for yours blog.
I try used uvm_reg_block that contains uvm_reg_block in my code.
after the crate model, I can see that each register allocated in 4 address(offset), and when I using write_mem I getting 4 transactions.
I can’t understand what wrong with my code.
Thanks.
If you could share your code, I might be able to help.
Hi Keisuke,
How do I write/read to a register in my test using the fields defined in the register model. In some instances, some of the fields get shrunk/expanded or in some rare occasion moved around within the register during development. How do I make my code immune to these type of changes?
Thanks,
Del
In the
jelly_bean_reg_sequence
, we wrote to thejb_recipe_reg
like this:Suppose the location of the
sour
field and thesugar_free
field are swapped due to the specification change. As you guessed, we have to rewrite the code to reflect the change.To make the code immune from the change, we could write to the each register field instead of writing to the entire register:
However, this will generate four front-door register writes. Better approach would be using the
set
andupdate
:This will generate one register write at most. Similarly, we could read the
jb_taste_reg
without worrying about the location of thetaste
field like this:Hi Keisuke,
Thank you for the blog. Even I had the same issue as Tejas T V & Sasha. but got it resolved as I returned the rd value from the driver.
1. But my question is, If the mirror value compares the data against the data returned from driver with the RTL output, then what is the necessary of establishing the connection with predictor’s bus_in imp port with the monitor’s ap?
2. Is it the only fix that we have to return the data from driver ? Cannot we send data to mirror method from the monitor as the bus transactions are captured at monitor?
Thanks in advance,
Sheela B Patil
mirror
methods.Hello Keisuke,
Please help in knowing the error while executing your code in questasim-10.2b. The error is:
** Error: (vlog-8386) jelly_bean_sequence.sv(13): An enum variable ‘jb_tx.color’ of type ‘color_e’ may only be assigned the same enum typed variable or one of its values. Variable ‘GREEN’ of type ‘color_e’ is not valid.
Please guide.
Thanks and Regards
Sunil
I don’t know the reason of the error. Have you tried a newer simulator?
Hi Keisuke,
I have some doubts in RAL model ,
1. Why we need randomize the class in reg block. rand Class_Name C_N_handle. If we are doing every field as ‘rand’ why again we need to declare rand.
2. what is the exact functionality of support_byte_enable and provide_response?
3. what is the use of lock_model?
rand
type-modifier to the registers in the register block, you can randomize the entire register block likejb_reg_block.randomize()
. Otherwise, you need to randomize the registers one by one.supports_byte_enable
to 1. Otherwise, set it to 0.get(req)
andput(resp)
for example, then setprovides_responses
to 1. If the target driver does not provide separate responses usingget_next_item(req)
anditem_done()
for example, then set it to 0.lock_model()
checks the address overlapping and builds the address maps.Hi Dear Keisuke,
Thank you very much for all your post with so many details.
I try to use the serial bus like i2c to program the registers at the beginning of the simulation. I have some questions about it. would you please take a look at it?
1. I have finished the regmodel. But I am not sure if a reg sequence is good enough, or we need to have a i2c sequence still. ( As my understanding, virtual sequence will start the reg sequence and pass the transaction all the way down to DUT interface if I do the connection correctly).
2. Since the bus is serial, we need to separate the transaction from regmodel into bit and drive to i2c interafce. . Which way do you think is better for the i2c driver? In some example , it use seq_item_port. get_next_item and item_done(rsp) to do the conversation. Do you think it s a good way to do it? Do you have any example for it?
3. In the DUT( jalley_bean_taster.v) I saw these code as follows, Should it be part of the jb_slave_if driver? I am a little bit confused about it.
Thanks you very much for your time and help.
i2c_transaction
) that contains I2C address and data information, and the I2C driver that converts an I2C transaction into SDA/SCL wiggling. If so, all you need is a register adapter that converts register operation from/to ani2c_transaction
. Once you let your reg_map know the adapter by callingset_sequencer()
, you should be able to doyour_reg.write(status, value)
, etc. in your sequence.get_next_item
anditem_done
as you mentioned.Let me know if you need further clarification.
Hi Keisuke,
Thank you very much for your replay. The diagram is great !!
Yes, I write my own SCCB driver( i2c like interface). I still have some questions about the reg model to interface.
1. “you should be able to do your_reg.write(status, value), etc. in your sequence.”
You mention above that I am able to do reg.write in my sequence. Do you mean test sequence? As my understanding now, I do not need to write an SCCB sequence. I could use the virtual sequence in the test cases to program register. Please correct me if i am wrong. For read I have more questions.
Write:
virtual sequence—>reg_sequence —(uvm_reg_bus_op, write)—>SCCB_adapter.reg2bus—(SCCB_trans)—>SCCB_sequencer—>SCCB_driver—>sda/scl.
Read:
virtual sequence—>reg_sequence —(uvm_reg_bus_op. read)—>SCCB_adapter.reg2bus—(SCCB_trans)—>SCCB_sequencer—>SCCB_driver—>sda/scl—>SCCB_driver get read data—> save it to SCCB_trans.
After read operation, driver will get data from sda/scl and pack it into SCCB_trans, do you think it is necessary to use a tlm_fifo to hold the data? ( As my understading, tlm_fifo is better). I am not sure where should we use the SCCB monitor to grep the SCCB_trans.
And how do we compare the value with the value in the regmodel.
2. Do we need to call reg2bus and bus2reg anywhere in the code? ( I do not find it). Or once we we do reg.write and reg.read in the reg_sequence, it will be called automatically. I understand the usage of reg2bus, what why do we need bus2reg? In the read flow above, I do not find the place to use bus2reg.
3. Some of the Read/Write are not single read and write. But the access operations are only UVM_READ and UVM_WRITE( I search the Reference manual). Does that mean the adapter do not handle complicated R/W. Those control will located in the SCCB driver.
Best,
uvm_tlm_fifo
. In your sequence, callyour_reg.read(status, value)
, then compare thevalue
against your expected value. Regarding the monitor location, please see theread()
section of Register Access Methods.reg2bus
orbus2reg
. Your register map callsbus2reg
after your sequence has completed a register read transaction on the bus. Then the converted value is used to update the register model.uvm_reg_bus_op
and the SCCB_trans. Serialization and deserialization should be done by the driver.Thanks Keisuke for your detailed explanation. I will try to implement it and let you know if it work.
Thank you very much.
Best
Hi Keisuke,
I try to use virtual sequence and virtual sequencer to control the test. Would you please take a look at the questions as follows,
1. I wonder if I connect the m_reg_seq to p_sequencer.m_sccb_sqr will work? I try to control the RAL sequence though virtual sequence and virtual sequencer in the test.
2. Since I do m_vseq.start(m_env.m_vsqr); Do I need to do anything else to instantiate the vseq?
Such as connecting m_vseq.m_sccb_sqr to env.m_vsqr.m_sccb_sqr?? I think i already connect them in the env so that it should be OK.
3. p_sequencer seems not quite easy to connect. Do you think just use virtual sequence will be better?
m_vseq.start(m_env.m_vsqr)
should be enough.Awesome post!
Hi Keisuke,
Thanks a lot for this topic, but I have some quastion. What I should do if I have 2 interaction register fields in 2 registers. For example reg A.field1(RO), but always have value from reg B.field1(RW) . How I can implement this using RAL.
I wrote a new article to answer your question. Let me know if you have further questions.
Hi Keisuke,
I am not clear how we can read the reset value for the RAL registers.
I’ve tried your example “http://cluelogic.com/2012/10/uvm-tutorial-for-candy-lovers-register-abstraction/” inside the EDA playground and modified it a little bit
https://www.edaplayground.com/x/XT2
Modifications:
1. inside ral.svh, changed the reset values to be different than 0 for “jelly_bean_recipe_reg”.
– same for “sugar_free” and “sour” fields;
2. inside sequences.svh file, at jelly_bean_reg_sequence class, commented the write_reg and added a print message for the read value
Run the test again, but the read value is not as expected:
after read jb_reg_block.jb_taste_reg=0000000000000000
I was expected to have the default values for each field returned by the read_reg function.
Am I wrong?
Can you please advise, why is this behavior OR how can we do to read and check the default reset value for a register (from the uvm-reg-model) to be compared against the DUT one?
Thank you.
Adi
The
reset
argument of theconfigure
function sets the reset value of the register model. It has nothing to do with the DUT. On the other hand, theread_reg
task actually reads the register from the DUT. Since the reset value of thejb_taste_reg
in the DUT is 0, you get 0.One way to check the default value of a register is:
For more information, please see this article.
Hi Keisuke-san,
Please explain the purpose of line 44 of jelle_bean_env:
if ( jb_env_cfg.jb_reg_block.get_parent() == null ) begin // if the top-level env
It seems to me that since jb_reg_block has a parent (jelle_bean_base_test created it), this if clause would be evaluated to be false, and the set_sequencer in the next line won’t be executed.
Thanks,
The
if (jb_env_cfg.jb_reg_block.get_parent() == null)
checks if thejb_reg_block
has a parent register block. Don’t confuse this withuvm_component::get_parent()
, which returns a parent component. This code means if thejb_reg_block
is the top-level register block (true in this case), then use the sequencer and the adapter in thejb_agent
in thisjelly_bean_env
. Otherwise (thejb_reg_block
has a parent register block), an upper environment (or a test) that has the access to the parent register block should call this function.Hi Shimizu-san,
I have some doubt in uvm_reg_predictor.
In my environment, I have connected predictor bus_in port to output analysis port of monitor. I have also implement reg_adapter bus2reg function and connect adapter to predictor. I’m using passive prediction (https://verificationacademy.com/cookbook/registers/integrating). The mirror value of uvm_reg should be updated as long as there’s transaction sent from monitor. However I did not see that happen. When I check the source code for uvm_reg_predictor, it seems like it failed in get_reg_by_offset() function so that it did not get uvm_reg object. Have you met similar issue? If so, what’s your solution? Thanks.
Make sure you use the top address map in your predictor if you have a hierarchical address map. Also make sure you use the address with respect to the top address map in your
bus2reg
function.Hi Keisuke,
I have some problem on modeling a large data bus (e.g. 128-bit) with word-enable bus access,
here is the example:
I use the +define+UVM_REG_DATA_WIDTH=128 for vlog compilation paired with RALF generated reg model:
the sequence part, I choose uvm_reg_sequemce paired with write_reg() or write_mem() calls
no matter what type of access I tried, the default byte_en always 0xffff, it means my uvm_reg_adapter has no get any chance to distinguish if I do want to do
just separated words, like 0xff00, 0x0f0f, 0xff0f, 0x0f00 … etc., in one bus transaction.
do you have any advise on modeling such thing in easy way?
thanks for any help from you in advance.
The
byte_en
ofuvm_reg_bus_op
is properly set only if you use theread
orwrite
task ofuvm_reg_field
. Thewrite_reg
andwrite_mem
tasks always write the entire bus. You could model yourrf00
as auvm_reg
instead of auvm_reg_block
, and model yourctl
register as auvm_reg_field
. Then doregmodel.rf00.ctl.write( status, 32'h21414 )
for a partial write. Make sure you set thesupports_byte_enable
property of theuvm_reg_adapter
.Hi.
A have a question related to DUT lookup tables.
I am using UVM register package to test the registers and LUTs for my DUT.
I’ve model the LUTs as uvm_mem inside my RAL.
I can write them/read them (issuing transactions on AMBA AHB bus), but I am not clear how to check them.
For uvm_reg, there is a mirror mechanism (predictor) that helps with checking, but not for uvm_mem (from what I know).
Do you have a suggestion on how to deal with checking of the LUTs (or what is the best practice here)?
Thank you.
Adi
As you mentioned, there is no built-in mechanism to compare the data in memory. You could have an associative array to store the expected data. Then use the
peek
task of theuvm_mem
to read the memory using a back-door access and compare.what is the necessity of casting statement? Can you pease explain?
The type of
model
isuvm_reg_block
, while the type ofjb_reg_block
isjelly_bean_reg_block
, which extendsuvm_reg_block
.$cast
is required to assign a superclass handle to a variable of a subclass type.