This is follow-up post about automatic setting of a variable in UVM component after being set in config_db. Like anything in UVM,there are 2 parts. The registration and actual call. Let’s do the registration first.

For a component, you can define fields using uvm_field_* macros between uvm_component_utils_begin and uvm_component_utils_end. In this example, I will trace uvm_field_int.

  `uvm_component_utils_begin(foobar)
    `uvm_field_int      (addr, UVM_ALL_ON)
  `uvm_component_utils_end

In src/macros/uvm_object_defines.svh, these macros are defined as follows. uvm_field_utils_begin defines __m_uvm_field_automation which calls super.__m_uvm_field_automation() before the fields macros expand to switch-case for each of the operations.


`define uvm_field_utils_begin(T) \
   function void __m_uvm_field_automation (uvm_object tmp_data__, \
                                     int what__, \
                                     string str__); \
   begin \
     T local_data__; /* Used for copy and compare */ \
     typedef T ___local_type____; \
     string string_aa_key; /* Used for associative array lookups */ \
     uvm_object __current_scopes[$]; \
     if(what__ inside {UVM_SETINT,UVM_SETSTR,UVM_SETOBJ}) begin \
        if(__m_uvm_status_container.m_do_cycle_check(this)) begin \
            return; \
        end \
        else \
            __current_scopes=__m_uvm_status_container.m_uvm_cycle_scopes; \
     end \
     super.__m_uvm_field_automation(tmp_data__, what__, str__); \
     /* Type is verified by uvm_object::compare() */ \

In the same file, uvm_field_int defines several branches for difference operation. For UVM_SETINT which we are interested in, we can see that it matches the name, and if it’s writable, it will assign to ARG from uvm_object::__m_uvm_status_container.bitstream

`define uvm_field_int(ARG,FLAG) \
  begin \
    case (what__) \
      ...
      ...
      UVM_SETINT: \
        begin \
          bit matched; \
          __m_uvm_status_container.scope.set_arg(`"ARG`"); \
          matched = uvm_is_match(str__, __m_uvm_status_container.scope.get()); \
          if(matched) begin \
            if((FLAG)&UVM_READONLY) begin \
              uvm_report_warning("RDONLY", $sformatf("Readonly argument match %s is ignored",  \
                 __m_uvm_status_container.get_full_scope_arg()), UVM_NONE); \
            end \
            else begin \
              if (__m_uvm_status_container.print_matches) \
                  uvm_report_info("STRMTC", {"set_int()", ": Matched string ", str__, " to field ", __m_uvm_status_container.get_full_scope_arg()}, UVM_LOW); \
              ARG = uvm_object::__m_uvm_status_container.bitstream; \
              uvm_object::__m_uvm_status_container.status = 1; \
            end \
          end \
          __m_uvm_status_container.scope.unset_arg(`"ARG`"); \
        end \
    endcase \
  end

Let’s move to actual call side, __m_uvm_field_automation is called from set_int_local. uvm_object::__m_uvm_status_container.bitstream is set to the value passed from the called. We will see it in a second.

function void  uvm_object::set_int_local (string      field_name,
                                          uvm_bitstream_t value,
                                          bit         recurse=1);
  __m_uvm_status_container.cycle_check.delete();
  __m_uvm_status_container.m_uvm_cycle_scopes.delete();

  this.__m_uvm_status_container.status = 0;
  this.__m_uvm_status_container.bitstream = value;

  __m_uvm_field_automation(null, UVM_SETINT, field_name);

set_local_int is called from apply_config_settings after looking up values from config_db. I am pasting the related code, but there is several lookups happening before the call. Read your UVM.

Note, value passed is the value passed read from the config db.

function void uvm_component::apply_config_settings (bit verbose=0);

  ...
  ...
    begin
    uvm_resource#(uvm_bitstream_t) rbs;
    if($cast(rbs, r))
      set_int_local(name, rbs.read(this));
    else begin
      uvm_resource#(int) ri;
      if($cast(ri, r))
        set_int_local(name, ri.read(this));
      else begin
        uvm_resource#(int unsigned) riu;
        if($cast(riu, r))
          set_int_local(name, riu.read(this));

apply_config_setting is called by the build_phase from the uvm_component.svh. That’s it.

function void uvm_component::build_phase(uvm_phase phase);
  m_build_done = 1;
  build();
endfunction

// Backward compatibility build function

function void uvm_component::build();
  m_build_done = 1;
  apply_config_settings(print_config_matches);
  if(m_phasing_active == 0) begin
    uvm_report_warning("UVM_DEPRECATED", "build()/build_phase() has been called explicitly, outside of the phasing system. This usage of build is deprecated and may lead to unexpected behavior.");
  end
endfunction

Fin.