Last Updated on April 11, 2014

The register abstraction layer (RAL) of UVM provides several methods to access registers. This postal service will explain how the register-access methods piece of work. In Annals Abstraction, nosotros introduced the overview of RAL and explained how to define registers. In this post, we will cover how to access the registers.

Backdrop of uvm_reg_field

Before diving into the annals-access methods, let's look at how a register value is stored. As seen in Register Abstraction, uvm_reg_field is the lowest register-abstraction layer which represents the $.25 of a register. The uvm_reg_field uses several backdrop to store a diversity of register-field values:

  • m_reset["Hard"] stores a hard reset value. Note that the m_reset is an associative array with a kind of reset every bit the key.
  • m_mirrored stores the value of what we think in our design under examination (DUT).
  • m_desired stores the value of what we want to set to the DUT.
  • value stores the value to exist sampled in a functional coverage, or the value to be constrained when the field is randomized.

Note that among these backdrop, only the value property is public. The other properties are local, thus we cannot access them direct from the out side of the grade. We volition show you how to access these local properties using register-admission methods after.

Properties of uvm_reg_field
Properties of uvm_reg_field

configure()

The first affair we do later creating a uvm_reg_field is configuring information technology. In Register Abstraction, we configured the season field as follows. Note that in Register Brainchild, we defined the flavor field every bit "WO" (write-but), but we defined it every bit "RW" (read/write) here to make the field more generic.

1 2 3 4 5 vi 7 8 nine 10                        
flavour                          =                          uvm_reg_field::type_id::create(                          "season"                          )                          ;                          flavour.configure(                          .parent                          (                          this                          )                          ,                          .size                          (                          iii                          )                          ,                          .lsb_pos                          (                          0                          )                          ,                          .access                          (                          "RW"                          )                          ,                          .volatile                          (                          0                          )                          ,                                                      .reset                            (                            0                            )                            ,                                                                                .has_reset                            (                            one                            )                            ,                                                    .is_rand                          (                          ane                          )                          ,                          .individually_accessible(                          0                          )                          )                          ;                        

flavor = uvm_reg_field::type_id::create( "flavor" ); flavor.configure( .parent ( this ), .size ( 3 ), .lsb_pos ( 0 ), .access ( "RW" ), .volatile ( 0 ), .reset ( 0 ), .has_reset ( 1 ), .is_rand ( one ), .individually_accessible( 0 ) );

If the has_reset argument is 1, the value of reset statement is taken as the "HARD" reset value. If the has_reset value is 0, the value of reset is ignored. The value of reset should friction match the reset state of the DUT. If you want to change the reset value after the configuration, yous can use set_reset() method.

flavor.set_reset(                    .value(                    0                    )                    ,                    .kind(                    "Difficult"                    )                    )                    ;                    // kind == "Difficult" past default                  

flavor.set_reset( .value( 0 ), .kind( "HARD" ) ); // kind == "HARD" past default

How configure() and set_reset() methods work
How the configure() and set_reset() methods piece of work

reset()

The reset() method resets the backdrop of a register field, if the m_reset[kind] exists. The default kind is "Difficult". If the m_reset[kind] does not be, the reset() method does zilch. Note that the reset() method does not reset a register in the DUT. It only resets the properties of a register-field object.

season.reset(                    .kind(                    "HARD"                    )                    )                    ;                    // kind == "HARD" by default                  

flavor.reset( .kind( "HARD" ) ); // kind == "HARD" by default

How reset() method works
How the reset() method works

set()

The set() method sets the desired value of a annals field. The fix() method does not fix the value to a register in the DUT. It only sets the value to the m_desired and the value properties of a register-field object. To actually gear up the value to the register in the DUT, utilise write() or update() method. These methods will be explained subsequently.

flavor.set                    (                    .value(                    1                    )                    )                    ;                  

flavour.gear up( .value( 1 ) );

How set() method works
How the gear up() method works

get()

The go() method gets the desired value of a register field. The become() method does not get the value from a register in the DUT. It only gets the value of the m_desired holding. To really get the value from the DUT, use read() or mirror() methods. These methods will be explained later. Similarly to the go() method, there are ii more getters to admission the local properties. The get_reset() retrieves the value of the m_reset[kind] property, while the get_mirrored_value() method retrieves the value of the m_mirrored belongings.

uvm_reg_data_t desired_value                    =                    flavor.go                    (                    )                    ;                    uvm_reg_data_t reset_value                    =                    season.get_reset(                    .kind(                    "HARD"                    )                    )                    ;                    // kind == "HARD" past default                    uvm_reg_data_t mirrored_value                    =                    flavor.get_mirrored_value(                    )                    ;                  

uvm_reg_data_t desired_value = flavor.become(); uvm_reg_data_t reset_value = flavor.get_reset( .kind( "Hard" ) ); // kind == "Hard" by default uvm_reg_data_t mirrored_value = flavor.get_mirrored_value();

How get(), get_reset(), and get_mirrored_value() methods work
How the get(), get_reset(), and get_mirrored_value() methods work

randomize()

The randomize() method is a SystemVerilog method. It randomizes the value holding of a register-field object. After the randomization, the post_randomize() method copies the value of the value property to the m_desired property. Note that the pre_randomize() method copies the value of the m_desired to the value belongings if the rand_mode of the value belongings is OFF.

                    assert                    (                    flavor.randomize                    (                    )                    )                    ;                  

assert( season.randomize() );

How randomize() method works
How the randomize() method works

write()

The write() method really writes a value to the DUT.

uvm_status_e                      status                      ;                      flavor.write(                      .status                      (                      status                      )                      ,                      .value(                      1                      )                      )                      ;                    

uvm_status_e condition; flavour.write( .condition( condition ), .value( one ) );

The write() method involves multiple steps.

  1. A uvm_reg_item object respective to the write operation is created.
  2. The uvm_reg_adapter converts the write operation to a corresponding bus transaction.
  3. The uvm_driver executes the bus transaction to the DUT.
  4. The uvm_monitor captures the bus transaction.
  5. The uvm_reg_predictor asks the uvm_reg_adapter to convert the coach transaction to a corresponding register functioning.
  6. The register operation is converted to a uvm_reg_item.
  7. The uvm_reg_item is used to update the value, m_mirrored, and m_desired properties.

Notation that if the individually_accessible argument was 0 when the register field was configured, the entire annals containing the field is written, because the field is non individually accessible. In this case, the m_mirrored values are used as the write values for the other fields.

How write() method works
How the write() method works

read()

The read() method actually reads a annals value from the DUT.

uvm_status_e                      status                      ;                      uvm_reg_data_t value;                      flavor.read(                      .status                      (                      condition                      )                      ,                      .value(                      value                      )                      )                      ;                    

uvm_status_e status; uvm_reg_data_t value; season.read( .status( status ), .value( value ) );

Similarly to the write() method, the read() method involves multiple steps.

  1. A uvm_reg_item object corresponding to the read operation is created.
  2. The uvm_reg_adapter converts the read operation to a corresponding bus transaction.
  3. The uvm_driver executes the bus transaction to the DUT.
  4. The uvm_reg_apapter converts the bus transaction with read data to a register operation.
  5. The read() method returns the read value to the caller.
  6. In the hateful fourth dimension, the uvm_monitor captures the bus transaction.
  7. The uvm_reg_predictor asks the uvm_reg_adapter to convert the bus transaction to a respective register operation.
  8. The register functioning is converted to a uvm_reg_item.
  9. The uvm_reg_item is used to update the value, m_mirrored, and m_desired backdrop.

Note that if the individually_accessible statement was 0 when the register field was configured, the unabridged register containing the field is read. In this instance, the m_mirrored values are updated for the other fields as well.

How read() method works
How the read() method works

update()

The update() method actually writes a register value to the DUT. The update() method belongs to the uvm_reg course. The uvm_reg_field grade does not have the update() method.

uvm_status_e                    status                    ;                    jb_recipe_reg.update(                    .status                    (                    status                    )                    )                    ;                  

uvm_status_e status; jb_recipe_reg.update( .status( status ) );

The differences between the write() method and the update() method are:

  • The write() method takes a value as its argument, while the update() method uses the value of the m_desired holding equally the value to write.
  • The update() method writes the value only if the m_mirrored and the m_desired are non equal.
Before update()
Before the update() is executed

The update() method internally calls the write( .value( m_desired ) ). Because of this, the value of the m_mirrored will be updated as well, after the update.

After update()
After the update() is executed

mirror()

The mirror() method really reads a register from the DUT.

uvm_status_e                    condition                    ;                    flavor.mirror(                    .status                    (                    status                    )                    ,                    .check(                    UVM_CHECK                    )                    )                    ;                  

uvm_status_e condition; flavour.mirror( .status( status ), .check( UVM_CHECK ) );

The differences between the read() method and the mirror() method are:

  • The read() method returns the register value to the caller, while the mirror() method does non return the annals value. The mirror() method only updates the value of the m_mirrored property.
  • The mirror() method compares the read value confronting the m_desired if the value of the check argument is UVM_CHECK. Notation that the UVM Class Library document states that it compares the read value against the mirrored value, just if you look at the line 2,944 of uvm_reg.svh of uvm-1.1c lawmaking base of operations, it really compares against the desired value, not against the mirrored value.

    April 11, 2014: uvm-one.1d code base has corrected this issue. The mirror() compares the read value confronting the mirrored value at present. Please come across the line two,951 of uvm_reg.svh if you are curious nearly this prepare.)

    Another caveat nearly the check is that if y'all set the volatile argument to be 1 when you configured the register field, the register field won't exist checked even though you lot set up the cheque argument to be UVM_CHECK. This is because nosotros cannot predict the value of the register field deterministically as it might have been changed (volatile) in the DUT.

The mirror() method internally calls do_read() method. This is the same method the read() method internally calls. Because of this, the mirror() method will update the value and the m_desired properties, in addition to the m_mirrored property.

How mirror() method works
How the mirror() method works

predict()

The predict() method updates the mirrored value.

season.predict(                    .value(                    1                    )                    )                    ;                  

flavour.predict( .value( 1 ) );

The predict() method also updates the value and the m_desired properties.

How predict() method works
How the predict() method works

Summary

The table below summarizes how each method updates the properties of the annals-field object.

Method m_reset
["Difficult"]
value m_desired m_mirrored DUT
configure
(.reset(val),
.has_reset(1))
prepare the value of val
set_reset(val) prepare the value of val
reset() re-create the value of m_reset
["Hard"]
copy the value of m_reset
["HARD"]
copy the value of m_reset
["HARD"]
set(val) prepare the value of val set the value of val
get_reset() return the value of m_reset
["Hard"]
get() return the value of m_desired
get_mirrored_value() return the value of m_mirrored
randomize() randomize copy the value of value
write(.value(val)) ready the value of val prepare the value of val set the value of val write the value of val
read(.value(val)) set the read value set the read value set the read value read the register
update() prepare the value of m_desired gear up the value of m_desired set the value of m_desired write the value of m_desired
mirror() set the read value ready the read value set up the read value read the register
predict
(.value(val))
fix the value of val fix the value of val set the value of val

In this post, we only covered so-chosen front-door access. We volition cover back-door access in a separate mail service. I hope this tutorial helped yous to empathize the annals access methods.