UVM Tutorial for Candy Lovers – 16. Register Access Methods

Last Updated on April 11, 2014

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

Properties of uvm_reg_field

Before diving into the register-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 bits of a register. The uvm_reg_field uses several properties to store a variety 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 as the key.
  • m_mirrored stores the value of what we think in our design under test (DUT).
  • m_desired stores the value of what we want to set to the DUT.
  • value stores the value to be sampled in a functional coverage, or the value to be constrained when the field is randomized.

Note that among these properties, only the value property is public. The other properties are local, thus we cannot access them directly from the out side of the class. We will show you how to access these local properties using register-access methods later.

Properties of uvm_reg_field
Properties of uvm_reg_field

configure()

The first thing we do after creating a uvm_reg_field is configuring it. In Register Abstraction, we configured the flavor field as follows. Note that in Register Abstraction, we defined the flavor field as "WO" (write-only), but we defined it as "RW" (read/write) here to make the field more generic.

1
2
3
4
5
6
7
8
9
10
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                ( 1    ),
                  .individually_accessible( 0    ) );

If the has_reset argument is 1, the value of reset argument 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 match the reset state of the DUT. If you want to modify the reset value after the configuration, you can use set_reset() method.

flavor.set_reset( .value( 0 ), .kind( "HARD" ) ); // kind == "HARD" by default
How configure() and set_reset() methods work
How the configure() and set_reset() methods work

reset()

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

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 register field. The set() method does not set 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 set the value to the register in the DUT, use write() or update() method. These methods will be explained later.

flavor.set( .value( 1 ) );
How set() method works
How the set() method works

get()

The get() method gets the desired value of a register field. The get() method does not get the value from a register in the DUT. It only gets the value of the m_desired property. To actually get the value from the DUT, use read() or mirror() methods. These methods will be explained later. Similarly to the get() method, there are two more getters to access 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 property.

uvm_reg_data_t desired_value  = flavor.get();
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 property 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 property if the rand_mode of the value property is OFF.

assert( flavor.randomize() );
How randomize() method works
How the randomize() method works

write()

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

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

The write() method involves multiple steps.

  1. A uvm_reg_item object corresponding 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 bus transaction to a corresponding register operation.
  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.

Note that if the individually_accessible argument was 0 when the register field was configured, the entire register containing the field is written, because the field is not 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 register value from the DUT.

uvm_status_e   status;
uvm_reg_data_t value;
 
flavor.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 mean time, the uvm_monitor captures the bus transaction.
  7. The uvm_reg_predictor asks the uvm_reg_adapter to convert the bus transaction to a corresponding register operation.
  8. The register operation is converted to a uvm_reg_item.
  9. The uvm_reg_item is used to update the value, m_mirrored, and m_desired properties.

Note that if the individually_accessible argument was 0 when the register field was configured, the entire register containing the field is read. In this case, 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 class. The uvm_reg_field class does not have the update() method.

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 property as the value to write.
  • The update() method writes the value only if the m_mirrored and the m_desired are not 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 actually reads a register from the DUT.

uvm_status_e status;
 
flavor.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 not return the register value. The mirror() method only updates the value of the m_mirrored property.
  • The mirror() method compares the read value against the m_desired if the value of the check argument is UVM_CHECK. Note that the UVM Class Library document states that it compares the read value against the mirrored value, but if you look at the line 2,944 of uvm_reg.svh of uvm-1.1c code base, it actually compares against the desired value, not against the mirrored value.

    April 11, 2014: uvm-1.1d code base has corrected this issue. The mirror() compares the read value against the mirrored value now. Please see the line 2,951 of uvm_reg.svh if you are curious about this fix.)

    Another caveat about the check is that if you set the volatile argument to be 1 when you configured the register field, the register field won’t be checked even though you set the check argument to be UVM_CHECK. This is because we 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.

flavor.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 register-field object.

Method m_reset
["HARD"]
value m_desired m_mirrored DUT
configure
(.reset(val),
.has_reset(1))
set the value of val
set_reset(val) set the value of val
reset() copy the value of m_reset
["HARD"]
copy the value of m_reset
["HARD"]
copy the value of m_reset
["HARD"]
set(val) set 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)) set the value of val set 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() set the value of m_desired set the value of m_desired set the value of m_desired write the value of m_desired
mirror() set the read value set the read value set the read value read the register
predict
(.value(val))
set the value of val set the value of val set the value of val
In this post, we only covered so-called front-door access. We will cover back-door access in a separate post. I hope this tutorial helped you to understand the register access methods.

92 thoughts on “UVM Tutorial for Candy Lovers – 16. Register Access Methods”

  1. Hi Shimizu,

    Great work. When are you expecting to post information about back-door access? There are many confusions and also most of the times the backdoor methods aren’t working properly.

    Thanks,
    Arun

  2. Hi, great pos and great blog. Do you know if there can be any race conditions between the predictor updating a register value and another thread trying to get the mirrored value in the same tick ? I wonder if there is some intelligence in the register model to order same tick set/get calls. Cheers

    1. Good question. If you explicitly call the predict(value) to update the m_mirrored value while the same register field is being accessed by the front-door (either read or write), the front-door access “wins”. But as far as I see the source code of the uvm_reg_field, the m_mirrored is not thread-safe between set (do_predict) and get (get_mirrored_value).

      1. Thanks for your reply. In my case I have indeed a predictor component that captures system transactions and performs register predict(value) considering these transactions, hence any subsequent reads to these registers should match the read data against the mirrored value. Extra I have some callbacks registered on the registers which trigger uvm_events taken from the uvm_event_pool. Because these events are triggered in post_predict I think it’s probably safe to sync on them on checking components.

  3. Hi have a small doubt in case of Mirror .
    As you mentioned It compare with desired value. But in my case I am doing like this.
    1.First doing write on reg which is changing both m_desired and m_mirrored to say ‘h55
    2.Doing predict which changes both values to new values say ‘h66
    2.Now doing set which is changing m_desired to new value say ‘h77
    3.Doing mirror with check
    It showing following uvm_error

    UVM_ERROR /tools/cadence/INCISIV131/tools/uvm/uvm_lib/uvm_sv/sv/reg/uvm_reg.svh(2892) @ 885000000: reporter [RegModel] Register “reg_block_h.CH0SBREG7_h” value read from DUT (0x0000000000000055) does not match mirrored value (0x0000000000000066)
    UVM_INFO /tools/cadence/INCISIV131/tools/uvm/uvm_lib/uvm_sv/sv/reg/uvm_reg.svh(2911) @ 885000000: reporter [RegModel] Field ch0_dev_head (reg_block_h.CH0SBREG7_h[7:0]) mismatch read=8’h55 mirrored=8’h66

    which proves that it compares with m_mirrored value not m_desired

    any comment ??

  4. Hi,
    great article.
    I have a question about mirror value updating. if I rely on reg_block ‘update’ method to do register write, it will look at m_desired and m_mirror.
    my question is : do we need to use a predictor to update that mirror data?
    if using analysis port bus_in in predictor, ‘write’ imp in predictor will call adapter.bus2reg and corresponding reg predictor method to update mirror, but I also see uvm_reg_map will call adapter bus2reg in its ‘do_bus_write’ function, which is called when a reg do write.

    if I don’t put that predictor in my env, is mirror value updated well? any impact to collect RAL coverage?
    if predictor is not necessary, what’s its usage?

    Thanks.Pan

  5. searching something from website, seems if DUT is only modified through RAL reg access method, predictor is not necessary in env, if DUT is updated by other sources (not from RAL), env has to need a predictor to sync RAL register value with DUT to keep everything up to date.
    is my understanding correct?

  6. Hi,
    I have some doubt regarding ral??

    I want to know the perfect usage of set_sequencer??why it is used??
    I want to know whether the read() method clears the regsiter or not??
    I want to know the example of mirror where to use in ral?

      1. When a register is read or written using RAL, a uvm_reg_adapter converts the register request into a bus-specific uvm_sequence_item. Then, a uvm_sequencer executes the uvm_sequence_item. The set_sequencer function of the uvm_reg_map class sets this adapter and sequencer to a register map.
      2. The read method does not clear the register in the DUT. It updates the m_mirrored, value and m_desired properties of RAL with the read value.
      3. You can use mirror method to read all of the registers in a register block (the read method cannot do this). The mirror method gives you an option to compare the read value against the current mirrored value, too.
  7. Thanks Sir,
    understand nicely

    I want to ask some more doubts..
    a) use of volatile and its need..when to make that bit one?
    b) not able to read the burst of data using burst_read()…i seen some other guys are also facing the same issue.

    Thanks,
    sagar

    1. a) The volatile bit should be set if the DUT may update the value of the register field. A status register is an example. If the value of the register is not predictable, set the volatile bit.
      b) I need more information in order to answer your question.

      1. Hi Great Article.
        I am curious about to know how the update happen from the DUT register to the Register model’s Register, if the volatile bit made High during configuration.

        1. The mirrored value of the register model is updated when there is a read or write activity. This is regardless of the volatile bit value. Setting the volatile bit means that the value of the register is not predictable because the DUT may change the value. If the volatile bit is set, the register read value won’t be compared against the mirrored value because the mirrored value may hold a stale value.

  8. Hello sir,

    one more question!!

    I have one more doubt!!

    I dont understand the use of these two arguments in every method like write,read,mirror,etc

    what is the use of following two arguments??
    some examples?

    input int prior = -1
    input uvm_object extension = null,

    Thanks,
    sagar

    1. 1. The prior is to specify the priority of the register transaction when it is executed on a sequencer. If not specified (-1), default priority (100) is used. Higher numbers indicate higher priority. You might want to set higher priority when you access an interrupt status register, for example.
      2. The extension is to pass extra information that can be used when you convert a register transaction into a bus transaction. For example, if you want to use a specific thread ID of the target bus, you can pass the thread ID information as the extension.

  9. Thank you sir for your nice answers.!!
    I would like to ask one more question regarding ral!

    In explicit prediction my register model is not getting updated..after i set set_auto_predict(0).
    so my question is do i need to write separate code for explicit prediction.or it should work automatically?

  10. Hi
    I m trying a UVM backdoor write from a sequence.
    The steps which i follow are :
    1. In the build phase of the reg file where the register classes are built, i add
    reg_name.add_hdl_path_slice( inst1.inst2.inst3.reg_name_sig,0,32); where reg_name is the instance of the register of interest and reg_name_sig is the output of the instance inst3 in the design.
    2. In the build phase of the reg model where the reg file is built ( rf is the register file instance) i add
    add_hdl(uvm_env_top.tbench_top.DUT, RTL); where uvm_env_top is a module in which the testbench top module is instantiated. The DUT is the instance of the RTL in testbenchtop module
    3. In the register sequence, i call the following API
    rm.rf.reg_name.write(status, data, UVM_BACKDOOR); where rm is the register model instance
    4. In the log file, I observe the statement :
    Wrote register via DPI backdoor: rm.reg_name = 0x01
    But I do not observe the same in the waveform.
    The signal DUT.inst1.inst2.inst3.reg_name_sig is still 0 and the write doesnt take effect
    Can anyone please give me inputs on whether the method i followed for backdoor write is correct. If not, what is to be done?

  11. Hii,

    your tutorial was supereb.
    all my theoratical doughts related to ral is now solved.
    Thank you… 🙂

    But, I’m facing problem when I’m trying to use explicit predictor.
    my register model is not updating.

    in environment I have return this code.
    ///////////////////////// connect_phase
    cfg.wdogcfg_rb.wdog_map.set_sequencer(m_top.seqrh,reg2wrtr);

    wrtr2reg_predictor.map=cfg.wdogcfg_rb.wdog_map;

    wrtr2reg_predictor.adapter=reg2wrtr;

    cfg.wdogcfg_rb.wdog_map.set_auto_predict(0);

    m_top.mmon.monitor_port.connect(wrtr2reg_predictor.bus_in); ///it creates any problem?
    ///////////////////////

    in your example you have taken one analysis port in agent and then you made connection between predictor and( agent,monitor) In my code I have connected predictor directly to monitor.

    is there anything extra code we need to write in other component or object class.??

    Thank you

      1. Thanx for the rply.

        actuly in monitor I was storing data as xtn.pwdata and xtn.prdata. while in adapter I writtern rw.data=xtn.data thats why its not updating the regmodel.so now monitor also storing data as xtn.data and its working correctly. thanks for the support 🙂

  12. Hello !!

    I tried declaring some of the reserved registers with access type “NOACCESS” but it throws the below error.

    UVM_ERROR /packages/uvm/uvm-1.2/src/reg/uvm_reg_field.svh(832) @ 0: reporter [RegModel] Access policy ‘NOACCESS’ for field ‘blue_reg_model.BLACK.CTRL.RES18’ is not defined. Setting to RW.

    What access type can we define for reserved registers ?? [Note: Nothing should happen if those registers are accessed]

    Appreciate Your Help!

    Desperado !!

    1. "NOACCESS" policy is not predefined in the uvm_reg_field class unlike other access policies. Try calling void'(uvm_reg_field::define_access("NOACCESS")); before you call configure, or simply use "RO" as the access policy.

      1. Hello Keisuke !! Appreicate your suggestion !! I did define my user defined access type with a callback and implemented the same and it goes fine. It would be great if you can shed some light on the other questions as well !! Thanks !

  13. Register Size/Definition Changes based on Mode Register Selection !! Suggestion for a better UVM Register Model ?

    Hello !!

    a. Have a scenario where my DUT works in two different modes. Say mode X and mode Y. And this selection is made using the mode register.
    b. And based on the mode selection [either X or Y], the definition of other register changes.

    For Example:
    In Mode X, Register A size is 32-Bits.
    Whereas in Mode Y, Register A size is 16-Bits.

    In Mode X, definition of Register B is CTRL register of 32-bits in size.
    Whereas in Mode Y, definition of Register B is DEBUG register of 32-bits in size with different field elements.

    c. Keeping the above scenario, which would be a better way to define the UVM Register Model ? Is it better to have two separate register model one for Mode X and other for Mode Y ? Or to maintain only one register mode and internally change the register/field configurations based on the mode selection ?

    Which would be a better approach and better for control-ability. Share in you inputs/suggestions !!

    Regards,
    Desperado !!

    1. I would create two registers and two reg maps. For example, in the build method of the reg block class, I would do:

      virtual function void build();
        CTRL  = CTRL_reg ::type_id::create( "CTRL"  );
        DEBUG = DEBUG_reg::type_id::create( "DEBUG" );
        // configure and build regs here...
        reg_map_X = create_map( /* ... */ );
        reg_map_Y = create_map( /* ... */ );
        reg_map_X.add_reg( CTRL, / * ... */ );
        reg_map_Y.add_reg( DEBUG, /* ... */ );
        // ...
      endfunction

      If the auto-predict mode is not used, you will need to switch the reg map of the predictor depending on the mode, too.

      if ( mode == MODE_X ) reg_predictor.map = reg_map_X;
      else                  reg_predictor.map = reg_map_Y;
  14. Register Adapter With Strobe Based Register Access ?

    a. I have a scenario where in the Block “x” registers are configured through APB bus !
    b. But the tricky part is that the APB bus is strobe based access !!!
    c. If I do a register write, then the adapter basically have to convert the register based TX to the Bus based TX. But here the proper register offset address and the strobe value needs to be set instead of default register address !

    Had anyone came across such situation i.e. block register access using APB with Strobe based access ? If so share in your thought process !!

    Appreciate Your Help !!

    1. I don’t quite understand your question, but isn’t it enough to set the PADDR and PSTRB of the APB in the reg2bus function of the reg adapter, based on the value of addr and byte_en of the uvm_reg_bus_op structure?

  15. Hii,

    Mirror method will gives error whenever there is a missmatch of data.
    is it possible to print some other msg also with the uvm_error when data missmatch.??

    1. If you just want to print a message every time a specific register is read, you could use the post_read callback of the uvm_reg class. The following example shows how to print the register information every time the jelly_bean_taste_reg is read.

      class jelly_bean_taste_reg extends uvm_reg;
        // ...
        virtual task post_read( uvm_reg_item rw );
          `uvm_info( "post_read", rw.convert2string(), UVM_NONE )
        endtask
      endclass

      If you want to print a message only if a read data is mismatched, you could override the do_check function as follows. This function is not documented, so use it at your own risk 😉

      class jelly_bean_taste_reg extends uvm_reg;
        // ...
        virtual function bit do_check( input uvm_reg_data_t expected,
                                       input uvm_reg_data_t actual,
                                       uvm_reg_map map );
          do_check = super.do_check( expected, actual, map );
          if ( do_check == 0 ) begin
            `uvm_info( "do_check", "read data mismatched", UVM_NONE )
          end
        endfunction
      endclass
  16. Hi,

    One more question Sir,

    how to use “set_report_severity_id_file” I tried many things but either its give error or it will not do anything,
    so can you tell me with small example.

    Thank you
    Khushmit

    1. The reg2bus is called when you read/write the registers. The function converts the register operation into a bus transaction. For example, if you use AHB, the function returns an AHB transaction corresponding to the register access. On the other hand, the bus2reg converts in opposite direction. This function is called when you have a register read/write on the bus. For example, a register predictor calls this function to convert an AHB transaction into a register operation. Please see Register Adapter section of Register Abstraction for an example.

  17. Thank you for the response. I would appreciate if you inputs on how to write adapter code when address and data channels are independent., for example for AXI. Do we have to manage this in the driver or is there any other way to do this? though writes are working i have problem with reads

    1. Sorry for the slow response. The driver should take care of the read data. You need to return the read data either by storing the data to the read request itself, or by returning it as a separate response. In the latter case, set provides_responses bit in the adapter. If this bit is set, the reg map uses the response object when it calls the bus2reg function. Otherwise, the request object is used. The adapter functions (reg2bus and bus2reg) should simply convert the uvm_reg_bus_op to/from the corresponding bus transaction. Let me know if you need further clarification.

      1. Thank you for the great tutorial. Do you have any examples using provides_responses? I am not able to get responses to come back from the driver. Where do you set the response object or request object? I am expecting a completion as a response from a read from my DUT. Thank you.

  18. Hello Keisuke,

    I am trying to write a MSB of a register with bit_size 32(data[]: 0x80000000) and I am getting the following warning:

    UVM_WARNING /uvm_reg_field.svh(1481) @ : reporter [RegModel] uvm_reg_field::write(): Value greater than field

    Can you please explain me why I am getting this warning

      1. Hello Keisuke, Thanks for your reply. I am getting the following warning when I try to set a value in a register field

        UVM_WARNING (UVM/FLD/SET/BSY) Setting the value of field “xxx” while containing register “yyy” is being accessed may result in loss of desired field value. A race condition between threads concurrently accessing the register model is the likely cause of the problem.

        Can you please explain me why I am getting this warning!! Your assistance is kindly appreciated 🙂

  19. Hello Keisuke,

    First of all thank you for this tutorial,

    as I’m a beginner in the world of UVM,I want to know the difference between : sequencer , sequences and adapter.

    Thanks.

    Mourad.

    1. Roughly speaking, you define the series of transactions (stimuli) in a sequence. Sequencer arbitrates the sequences and send the series of transactions to a driver. Adapter converts one kind of transaction into another. In the RAL context, it converts generic register transactions from/to specific bus transactions (like AHB).

  20. Hello Keisuke,
    Excellent post . Can you please throw some light on predict method ?
    What if I set auto_predict for certain register model ?

    1. If you set the auto-predict mode, the register model will automatically update its mirror when you read/write the register using the RAL. However, it won’t capture the read/write not initiated by the RAL. Please see this for more information.

  21. Shimizu-san,

    Thank you very much for all you have done for the UVM community. I have a short question on the read() diagram you have above. I noticed that in step 4), the register adapter generates a bus2reg transaction (without waiting for the step 6 and 7) to come back. Can you explain why that is done? I am having problem when the read() transaction is meant to be an invalid transaction and I am blocking the result from step 6 and 7 (through the blocking of the uvm_monitor) but unable to block the feedback from step 4 as it seems “automatic”. Thank you.

    Regards,
    Bennett

    1. Does your driver return a separate response for the read data? If so, make sure you set the provides_responses bit in your reg adapter. Otherwise, the reg map calls bus2reg() as soon as the driver has finished a request.

  22. Hi Keisuke,
    Thanks for this great post.
    I’m trying to model DFT registers at Register Abstract Level.
    I ran into a problem is that DFT registers are usually larger than 64 bits width. When I implement bus2reg() function, I don’t know how to pass the extra data back to the register read operation, since uvm_reg_bus_op.data defaults to 64bits.
    Could you give me some suggestions?
    Thanks in advance!

    Best Regards,
    Rui

      1. In case you want to redefine the UVM_REG_DATA_WIDTH then would you have to recompile the whole UVM library? Is not there any other way? Why this hard limitation?

        1. Yes, you need to recompile the library because the UVM_REG_DATA_WIDTH defines the bit width of the packed array, uvm_reg_data_t. I think the reason behind this is to optimize the memory footprint of the register models.

  23. Hi Keisuke,

    Thanks a lot for the detailed explanation here. I have a question. From a single read() I want rw.addr to be written on the bus and then data to be read. I got the information that I can do this using custom frontdoor and set_frontdoor. But I am not getting how exactly I am supposed to change rw.kind=UVM_READ to UVM_WRITE first and then again to UVM_READ? will I be able to get 2 different bus items from a single reg item? Can you please help?

    1. I assume you need two separate bus transactions for a read. For example, you write an address to the address register, then read back data from the data register.

      Unfortunately, the reg2bus() function cannot return two bus transactions or a uvm_sequence. My solution would be building layered sequencers and monitors. In the diagram, the reg2bus() function returns a read transaction object. Then the Transaction Sequencer converts the read transaction into two bus transactions, one for address write and the other for data read.

      On the predictor side, the Bus Monitor captures these two bus transactions. Then the Transaction Monitor converts back them into a read transaction, which goes to the Register Predictor. Hope this helps.
      Two Pass RAL

  24. Hi Keisuke,

    It seems that uvm_reg::write task updates value in regmodel even in explicit prediction mode.
    I have used set_auto_predict(0), so I expected that register.write will only launch bus transaction, but it also directly/immediately updated regmodel value(mirror register). Same code was working well with UVM-1.0 while in UVM-1.1 this issues appears.

    This is because in uvm_reg::write (UVM-1.1), there is unconditional call of set function which was not there in UVM-1.0

    1. Yes, as you pointed out, the value is firstly updated by the write, then updated again when do_predict is called by the write itself (auto predict) or by the uvm_reg_predictor (manual predict).

  25. Hi.. Very helpful explanations related to RAL. I have situation in which I need to compare the present and previous value of the register.From the above post I understand tat all the values- m_mirrored,m_desired and value gets updated when read is done on the register. I want the previous value of the register stored somewhere…How can I do tat ?

    Thanks in advance

    1. The easiest way would be using the mirror task.

      uvm_status_e status;
      your_reg.mirror( status, .check( UVM_CHECK ) );

      The mirror task compares the read-back value against the last mirrored value if check is UVM_CHECK. The task issues an error message if the values do not match. You can disable the check field-by-field by calling your_reg_field.set_compare( UVM_NO_CHECK ) before calling the mirror.

      If the previous register values need to be stored somewhere for more complex comparison, then I would create a subscriber that keeps track of the register values.

  26. Hi Keisuke,

    I have some question about RAL, would you please answer those:
    1. I understand that value of m_mirrored always equal value of register in DUT, is that right ?

    2. As you said “The update() method writes the value only if the m_mirrored and the m_desired are not equal.” Would you please so example to make m_mirrored and the m_desired are not equal.

    Thank.
    Tuyend

      1. Not necessarily. The m_mirrored value is updated only when the register is read or written. If the DUT modifies the value of register internally (like a counter), the mirrored value becomes outdated.
      2. Suppose you read a register and the value was 0. Then if you call set( 1 ), the m_mirrored and m_desired become unequal.
      1. Hi Keisuke,
        I have two maps in my ral mode one is ahb and spi. when I was using spi map as default map mirror operation is working(its doing score board properly) after that I added the ahb map and removed spi s default map and created two maps(spi , ahb).
        Now mirror is not working. can you pls advice what code I should add for mirror to work.
        What is uvm_status in reg for me its failing uvm_status has x althoug the reg value is zero.
        Thanks,
        Sampath

  27. Hi Shimzu San ,
    Hajimi masthe . I tried doing update method using following method

    spi_reg.set(8’h80)
    spi_reg.update(status)
    spi_reg.get() –> This will yild as ‘h80
    spi_reg.get_mirrored_value -> 8’h0

    I dont understand y update is not writing with m_desired value

    1. To write a register field individually,

      • the underlying bus must support byte enables, and
      • the field must be the sole occupant of the byte lane(s).

      If both of them are true, then set

      • the supports_byte_enable property of the uvm_reg_adapter, and
      • the individually_accessible argument of the configure function of the uvm_reg_field.
  28. Hi Keisuke, you tutorial is very much useful. Thanks a lot to you and all the friends for their doubts and your responses.
    its gives a log of insight theoretically as well as at the application/usage

  29. Hi Keisuke,
    1. Can we set same adapter for two different regmaps?
    like this
    regmap1.set_sequencer(sequencer1,adapter);
    regmap2.set_sequencer(sequencer2,adapter)

    2. can two regmaps share a single sequencer?
    regmap1.set_sequencer(sequence1,adapter1);
    regmap2.set_sequencer(sequencer1,adapter2);

  30. Hi Keisuke,

    In below code I am using first predict to write value 3 into m_desired and m_mirrored of ral model as per theory there should not be any effect in dut but when I am trying to read dut using read_reg it is giving me m_desired value and m_mirrored value equal to 3 where I am expecting if predict is not writing dut reg then after read my mirrored value should be equal 2.

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

         jb_reg_block.jb_recipe_reg.flavor.write( .status( status ), .value( 2) );
        
         desired_value = jb_reg_block.jb_recipe_reg.flavor.get();
         $display($time,,"Desired_Value of Falvor is ",desired_value);
         mirror_value = jb_reg_block.jb_recipe_reg.flavor.get_mirrored_value();
         $display($time,,"Mirrored_Value of Falvor is ",mirror_value);  
        
         jb_reg_block.jb_recipe_reg.flavor.predict( .value( 3 ) );
       
         desired_value = jb_reg_block.jb_recipe_reg.flavor.get();
         $display($time,,"Desired_Value of Falvor is ",desired_value);
         mirror_value  = jb_reg_block.jb_recipe_reg.flavor.get_mirrored_value();
         $display($time,,"Mirrored_Value of Falvor is ",mirror_value);
         
         read_reg( jb_reg_block.jb_recipe_reg, status, value );
        
         desired_value = jb_reg_block.jb_recipe_reg.flavor.get();
         $display($time,,"Desired_Value of Falvor is ",desired_value);
         mirror_value = jb_reg_block.jb_recipe_reg.flavor.get_mirrored_value();
         $display($time,,"Mirrored_Value of Falvor is ",mirror_value);

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

  31. Hi Keisuke,

    There is available an example for reading the default values of the DUT registers (without any previous write operation)?

    Thank you.

    1. If you do your_reg.read( status, value ), it will actually read the register from the DUT. If you just wanted to know the reset value of the model, you can do your_reg.get_reset(). This function DOES NOT read the value from the DUT. It just returns the reset value of the model.

  32. Thank you for the great post.
    I have a question about m_mirrored value. I understand that m_mirrored can get updated through ral functions like write/read/update/mirror/peek/poke, etc since it uvm_monitor captures bus transactions and eventually update it.
    Does this apply to non-ral register access too ? Namely if I issue register access not through ral functions, is the bus transaction still going to be captured and finally gets updated to the m_mirrored ?

Leave a Reply to Keisuke Shimizu Cancel reply

Your email address will not be published. Required fields are marked *