UVM Tutorial for Candy Lovers – 13. Configuration Database

Last Updated: July 24, 2016

This post will explain how configuration database (uvm_config_db) works. In Configurations, we used the uvm_config_db to store a jelly_bean_if, a jelly_bean_env_config, and two jelly_bean_agent_configs. This post will analyze how a configuration data is stored and retrieved. The class diagram related to the configuration database is shown below. Note that we only showed the classes related to the jelly_bean_if and jelly_bean_agent_config to avoid clutter. UVM standard library classes are shown in pink, while the UVM classes specialized with the jelly_bean_transaction type are shown in yellow.

Class Diagram Related to the Configuration Database

Setting a Virtual Interface

In Configurations, the top module stored two virtual interfaces (jb_if1 and jb_if2) into the configuration database as follows:

uvm_config_db#( virtual jelly_bean_if )::set( .cntxt     ( null     ), 
                                              .inst_name ( ""       ),
                                              .field_name( "jb_if1" ),
                                              .value     (  jb_if1  ) );
uvm_config_db#( virtual jelly_bean_if )::set( .cntxt     ( null     ), 
                                              .inst_name ( ""       ),
                                              .field_name( "jb_if2" ),
                                              .value     (  jb_if2  ) );

The diagram below shows what will be created after these set functions have been called.

Storing the Virtual Interfaces
These are the steps involved in this course of action.

  1. Since the top is a module, not a uvm_component, null is passed as the cntxt when set is called. In this case, uvm_top, which is the singleton object of uvm_root class, will be used as the cntxt.
  2. An entry of an associative array (m_rsc) is created using the cntxt object as the key, if this entry does not exist.
  3. The entry of the associative array points to a uvm_pool, which has another associative array (pool).
  4. An entry of the associative array is created using the inst_name and the field_name as the key (lookup). Note that the inst_name and the field_name are concatenated with a string (“__M_UVM__“) when the lookup is created.
  5. If the lookup does not exist, a uvm_resource object is created. If the inst_name is "", the full name of the cntxt, which is "uvm_test_top", is stored as the scope. The value passed to the set() function is also stored.
  6. The handle to the uvm_resource object is stored in two kinds of uvm_queues. The first kind of queues store the handles to the uvm_resource objects that have the common field_name. A uvm_queue is created for every unique field_name. In our case, two uvm_queues are created; one for the "jb_if1" and the other for the "jb_if2".
  7. The second kind of queues store the handles to the uvm_resource objects that have the common type. A uvm_queue is created for every unique type. In our case, one uvm_queue is created for the virtual jelly_bean_if type.
  8. The queues are registered in the singleton uvm_resource_pool. The uvm_resouce_pool has two associative arrays. One array (rtab) uses the field_name as the key.
  9. The other array (ttab) uses the type of the uvm_resource as the key.

Getting a Virtual Interface

In Configurations, the jelly_bean_base_test retrieved the jelly_bean_ifs as follows:

uvm_config_db#( virtual jelly_bean_if )::get( .cntxt     ( this     ), 
                                              .inst_name ( ""       ),
                                              .field_name( "jb_if1" ),
                                              .value( jb_agent_cfg1.jb_if ) );
uvm_config_db#( virtual jelly_bean_if )::get( .cntxt     ( this     ), 
                                              .inst_name ( ""       ),
                                              .field_name( "jb_if2" ),
                                              .value( jb_agent_cfg2.jb_if ) );

The diagram below shows how the jb_if1 is retrieved.

Retrieving the Virtual Interface
These are the steps involved in this course of action.

  1. Look up the name table of the uvm_resource_pool with the field_name as the key.
  2. If the name matched, get the handle of the uvm_queue.
  3. Traverse the entries of the queue.
  4. If the scope and the type of the object matches, that uvm_resource is stored in a newly created uvm_queue.
  5. The uvm_resource with the highest precedence is retrieved.
  6. The value of the resource is returned.

Setting an Agent Configuration

Now let’s look at another example. In Configurations, the jelly_bean_env stored the jelly-bean-agent configurations (jb_agent_cfg1 and jb_agent_cfg2) as follows:

uvm_config_db#( jelly_bean_agent_config )::set( .cntxt     ( this           ), 
                                                .inst_name ( "jb_agent1*"   ),
                                                .field_name( "jb_agent_cfg" ),
                                                .value( jb_env_cfg.jb_agent_cfg1 ) );
uvm_config_db#( jelly_bean_agent_config )::set( .cntxt     ( this           ), 
                                                .inst_name ( "jb_agent2*"   ),
                                                .field_name( "jb_agent_cfg" ),
                                                .value( jb_env_cfg.jb_agent_cfg2 ) );

The diagram below shows what will be created after these set functions have been called.

Storing the Agent Configurations
These are the steps involved in this course of action.

  1. The jelly_bean_env calls the set() function with this (jb_env) as the cntxt.
  2. An entry of an associative array (m_rsc) is created using the cntxt object as the key, if this entry does not exist.
  3. The entry of the associative array points to a uvm_pool, which has another associative array (pool).
  4. An entry of the associative array is created using the inst_name and the field_name as the key (lookup). Note that the inst_name and the field_name are concatenated with a string (“__M_UVM__“) when the lookup is created.
  5. If the lookup does not exist, a uvm_resource object is created. Unlike the virtual-interface case, the inst_name is not "" this time. In this case, the full name of the cntxt object concatenated with the inst_name is stored as the scope. The value passed to the set() function is also stored.
  6. The handle to the uvm_resource object is stored in two kinds of uvm_queues. The first kind of queues store the handles to the uvm_resource objects that have the common field_name. A uvm_queue is created for every unique field_name. In our case, one uvm_queue is created for the "jb_agent_cfg".
  7. The second kind of queues store the handles to the uvm_resource objects that have the common type. A uvm_queue is created for every unique type. In our case, one uvm_queue is created for the jelly_bean_agent_config type.
  8. The queues are registered in the singleton uvm_resource_pool. The uvm_resouce_pool has two associative arrays. One array (rtab) uses the field_name as the key.
  9. The other array (ttab) uses the type of the uvm_resource as the key.

One difference from the previous example is that the rtab has only one entry because both the uvm_agent_cfg1 and the uvm_agent_cfg2 use the same field_name ("uvm_agent_cfg").

Getting an Agent Configuration

In Configurations, the jelly_bean_agent retrieved the jb_agent_cfg as follows:

uvm_config_db#( jelly_bean_agent_config )::get( .cntxt     ( this           ), 
                                                .inst_name ( ""             ), 
                                                .field_name( "jb_agent_cfg" ),
                                                .value     (  jb_agent_cfg  ) );

The diagram below shows how the jb_agent_cfg is retrieved.

Retrieving the Agent Configuration
These are the steps involved in this course of action.

  1. Look up the name table of the uvm_resource_pool with the field_name as the key.
  2. If the name matched, get the handle of the uvm_queue.
  3. Traverse the entries of the queue.
  4. If the scope and the type of the object matches, that uvm_resource is stored in a newly created uvm_queue. Note that there are two uvm_resource objects, but their scopes are different. The full name of the cntxt object passed to the get() function distinguishes between the two.
  5. The uvm_resource with the highest precedence is retrieved.
  6. The value of the resource is returned.

Summary

That’s about the configuration database. This diagram shows the main objects involved in this article. I hope this post helped you to understand how the configuration database works.

UVM Objects Involved in This Article
UVM Objects Involved in This Article

24 thoughts on “UVM Tutorial for Candy Lovers – 13. Configuration Database”

  1. Hi Keisuke,

    What is the diference between uvm_config_db and uvm_resource_db?

    How to choose between these two?

    Regards,
    Siva

    1. As it is correctly said by “Keisuke Shimizu”, Both the uvm_config_db and the uvm_resource_db provide a convenience interface for the resource database. UVM recommends using the uvm_config_db as it is more robust.

      uvm_config_db is more robust because with uvm_config_db, we can restrict the db so that it will not be visible to certain components. In other words, while setting up the config_db, we can set a path. So, that this db can be accessed only by the components that are existing in the specified path… But, uvm_resource_db has no such facility (since this db can be accessed by any of the components inTB) uvm_resource_db has global visibility.

      1. It is user’s responsibility to set correct scope when you call uvm_resource_db#(T)::set( input string scope, input string name, T val, input uvm_object accessor = null ) as the scope can be any string. On the other hand, when you call uvm_config_db#(T)::set( uvm_component cntxt, string inst_name, string field_name, T value ), you give a uvm_component for the scope (or context, cntxt), which is less error-prone.

  2. Hi Keisuke,

    How is uvm_config_db diffrent from set_inst_override or set_type_override, In which context should we use each of them. Can you please explain, Thanks in advance !!

    1. The uvm_config_db is used to share information between components and objects. For example, the top-level test-bench module often stores a virtual interface to the uvm_config_db. Then a uvm_driver can get the interface from the database for pin wiggling. Please see this and this for how we did it.

      The set_inst_override and set_type_override are used to substitute a component or an object for another without changing the test-bench code. For example, a user can substitute a transaction for an error transaction. Please see this for how we substituted a jelly bean with a sugar-free jelly bean from a test.

  3. Hi
    The article is a bit too sophisticated, it explains more about behind the scenes, then about the actual way of using it, as you did in the previous articles. Can you please add some more examples for each field in the set/get of the config_db.
    Thanks

  4. Please ignore my earlier reply, I continued on and saw you elaborated on the issue in another chapter.
    Thanks again

  5. Hi Keisuke,

    Thanks for the very nice explanation on config db. No I know how to appropriately give the .cntxt, .instname, arguments.
    I have a question. I see the configuration object retrieval is only concerned with the associative array in uvm_resource_pool and the handles to uvm_queue they point to. I wanted to know why initially an entry is made in “m_rsc” table and a “uvm_pool” is created with another associative array? Why not start by creating uvm_resource objects for the item and get their handle in uvm_queue and make an entry in the arrays in uvm_resource_pool singleton? What benefit is gained from extra book keeping done initially?

    Regards,
    Gautam

  6. Hi Keisuke,

    From where do I read about the internal working of things like you have explained about internal working of a configuration database? What materials do I refer to?

    Regards,
    Gautam

    1. You should be able to do like this:

      // in your test class
      int foo = 123;
      uvm_config_db#(int)::set( .cntxt( null ), .inst_name( "uvm_test_top" ), .field_name( "foo" ), .value( foo ) );
       
      // in your test-bench module
      int bar;
      uvm_config_db#(int)::get( .cntxt( null ), .inst_name( "uvm_test_top" ), .field_name( "foo" ), .value( bar ) );
  7. Hi Keisuke,

    I have a question regarding to input a name in a interface.

    I have a interface like below:

    interface general_bfm
    (
       input string name,
       input bit clk,
       input bit resetn
    );
    

    then in my testbench top level, I have declare a instance like below:

    general_bfm               i_general_bfm("i_general_bfm",clk, resetn);
    

    However, it compiles with NCSIM successfully. But when I tried to compile with VCS, it gave me error:

    "i_general_bfm"
      Argument: 
      String may not be used in non-procedural context.
    

    I am not sure what the error exactly means? Can you please help me on that?

    many thanks
    Steve

    1. It seems you cannot use string as the interface port type. You could either define the name as an interface item like this:

      interface general_bfm( input bit clk, input bit resetn );
        string name;
      endinetrface
       
      module top;
        general_bfm i_general_bfm( clk, resetn );
       
        initial i_general_bfm.name = "i_general_bfm";
      endmodule

      or use a parameterized interface:

      interface general_bfm #( string name = "" ) ( input bit clk, input bit resetn );
      endinetrface
       
      module top;
        general_bfm #( "i_general_bfm" ) i_general_bfm( clk, resetn );
      endmodule
  8. Hi
    We have uvm_pool class which is helpful for seting and getting objects, but why still we need config_db ? we can achieve same thing in uvm_pool also but why do we go for uvm_config_db?

    1. You could use uvm_pool if simple lookup of uvm_object is good enough. However, if you want scope resolution for example, uvm_resource needs to be involved. Look at the diagram in Summary section and see how many objects are involved to resolve the scope. The uvm_config_db provides simple API for this.

  9. hi
    below code iam using config db set and get

    	uvm_config_db#( apb_reg_block)::set(this,"*","RAL",ral);
            uvm_config_db#(apb_reg_block)::get(this," ","RAL",ral); 
    

    but in this line error is pointing like this

    error: (vsim-3978) apb_seq_lib.sv(41): Illegal assignment to class type uvm_component [in work/uvm_pkg::uvm_component] from class type apb_reg_seq [in work/ahb_svh_unit::apb_reg_seq]
    #    Time: 0 ns  Iteration: 0  Region: /ahb_svh_unit::apb_reg_seq File: E:/uvm doc/ahb interconnect/ahb.svh
    # Error loading design
    

    can u plz help me out

Leave a Reply