文章目录

  • 1. uvm_config_db 基础
    • 1.1. 简单set
    • 1.2. 多重set
  • 2. uvm_resource
    • 2.1. uvm_resource_base
    • 2.2. uvm_resource_types
    • 2.3. uvm_resource #(T)
      • 2.3.1. set系列函数
      • 2.3.2. get系列函数
      • 2.3.3. write()/read()
  • 3. uvm_resource_pool
    • 3.1. 数据存储结构
    • 3.2. set系列函数
    • 3.3. get_by_name()/get_by_type()
  • 4. uvm_resource_db
    • 4.1. set()系列函数
      • 4.1.1. set()
      • 4.1.2. set_override()
    • 4.2. write_by_name()/write_by_type()
    • 4.3. read_by_name()/read_by_type()
  • 5. uvm_config_db
    • 5.1. set()
    • 5.2. get()
  • 6. 其它
    • 6.1. 总结
      • 6.1.1. 各常用函数调用关系
      • 6.1.2. uvm_config_db与uvm_resource_db的异同点
    • 6.2. debug信息
      • 6.2.1. UVM_CONFIG/RESOURCE_DB_TRACE
      • 6.2.2. check_config_usage()
      • 6.2.2. dump_get_records()

1. uvm_config_db 基础

  UVM config机制是其提供的一种在验证平台中进行资源配置和获取的方法,在复杂的平台中,不同testcase对各种参数可能会有不同的配置要求。UVM提供了一个全局的资源池来存储这些配置资源,我们可以通过config机制将各种类型的资源存入其中,如int型,string型,uvm_object类型等等,还可以设置这些资源的访问范围scope,即验证平台中的哪些component节点可以访问,而另外的节点则无权访问。

1.1. 简单set

  来看一个简单的例子,我们在testcase里使用uvm_config_db的set()将pkt_num配置给driver:

function void my_test::build_phase(uvm_phase phase);super.build_phase(phase);uvm_config_db #(int)::set(this, "env.agt.drv", num, 10);...
endfunction

  静态函数set()在被调用时需要带上范围解析符::,它有四个参数,第一个参数是uvm_component类型,一般填入this,若这个参数为空,则会自动设置为uvm_root类型的uvm_top。第二个参数为要配置资源所在的实例路径,和第一个参数配合定位到相关component节点,上面的代码还可以改成:
  uvm_config_db #(int)::set(“uvm_test_top.env”, “agt.drv”, num, 10);
第三个参数是一个string标志符,第四个参数是要配置的值。
  在driver中使用uvm_config_db的get()拿到这个配置:

class my_driver extends uvm_driver;int pkt_num;function void build_phase(uvm_phase phase);super.build_phase(phase);uvm_config_db #(int)::get(this, "", num, pkt_num);...endfunctiontask run();for (int i=0; i<pkt_num; ++i) begin...endendtask...
endclass

  静态函数get()和set()类似,也有四个参数,第一个参数一般填入this,第二个参数为空,第三个参数是string类型标志符,必须与set()的第三个参数一致,第四个参数则是要设置的变量,set()第四个参数的值会赋值给这个变量。

1.2. 多重set

  在上面的例子中我们在testcase中设置driver的pkt_num的值,那如果从env中也对driver的pkt_num设置了另一个值呢?

function void my_env::build_phase(uvm_phase phase);super.build_phse(phase);uvm_config_db #(int)::set(this, "agt.drv", num, 20);...
endfunction

  答案是driver中pkt_num拿到的值依然是在testcase中设置的10,离uvm_root越近的节点设置的同一资源具有较高的优先级。
  若在testcase中对driver的pkt_num设置了两次(实践中基本不会这么用,这里只是举例说明问题)如下:

function void my_test::build_phase(uvm_phase phase);super.build_phase(phase);uvm_config_db #(int)::set(this, "env.agt.drv", num, 10);uvm_config_db #(int)::set(this, "env.agt.drv", num, 20);...
endfunction

  则driver中pkt_num会拿到的值为20,即在同一节点以先后顺序设置的资源,后设置的具有较高优先级。

2. uvm_resource

2.1. uvm_resource_base

  UVM提供了一个参数类uvm_resource #(type T=int)来储存不同类型的资源,这个类继承自uvm_resource_base类:

//uvm_resource.svh
virtual class uvm_resource_base extends uvm_object;protected string scope;protected bit modified;protected bit read_only;uvm_resource_types::access_t access[string];int unsigned precedence;static int unsigned default_precedence = 1000;...function new(string name = "", string s = "*");super.new(name);set_scope(s);modified = 0;read_only = 0;precedence = default_precedence;endfunctionpure virtual function uvm_resource_base get_type_handle();...function void set_scope(string s);scope = uvm_glob_to_re(s);endfunction...
endclass

  uvm_resource_base类中定义了字符串变量scope,int型变量precedence,bit类型的modified和read_only,此外还有以字符串为索引,储存内容为uvm_resource_types::access_t类型的联合数组access[](用来记录该笔资源被不同accessor访问的记录,请参考下文关于debug的部分),以及默认值为1000的静态变量default_precedence,其new()函数会将第二个字符串参数s作为参数调用set_scope(),然后将modified和read_only清零并把precedence设置为default_precedence的值。set_scope()会把字符串s经过uvm_glob_to_re()函数处理后返回的字符串设置给scope,uvm_glob_to_re()是UVM通过DPI接口调用的C函数,主要功能就是把字符串处理成正则形式,可以用正则表达式来匹配。纯虚函数get_type_handle()会在uvm_resource类中被重载。

2.2. uvm_resource_types

  此外,uvm_resource_base类中还定义了一个uvm_resource_types类中的变量access_t:

//uvm_resource.svh
class uvm_resource_types;// types uses for setting overridestypedef bit[1:0] override_t;typedef enum override_t { TYPE_OVERRIDE = 2'b01,NAME_OVERRIDE = 2'b10 } override_e;// general purpose queue of resourcextypedef uvm_queue#(uvm_resource_base) rsrc_q_t;// enum for setting resource search prioritytypedef enum { PRI_HIGH, PRI_LOW } priority_e;// access record for resources.  A set of these is stored for each// resource by accessing object.  It's updated for each read/write.typedef struct{time read_time;time write_time;int unsigned read_count;int unsigned write_count;} access_t;endclass

  这个结构体access_t中定义了资源被读写的时间和访问次数,用来记录每个资源被访问的信息。uvm_resource_types类中还定义了枚举类型override_t,用来表示资源被override的形式,枚举类型priority_e表示存储的资源优先级高低,存储类型为uvm_resource_base的uvm_queue的句柄rsrc_q_t。

2.3. uvm_resource #(T)

  来看uvm_resource类:

//uvm_resource.svh
class uvm_resource #(type T=int) extends uvm_resource_base;typedef uvm_resource#(T) this_type;// singleton handle that represents the type of this resourcestatic this_type my_type = get_type();// Can't be rand since things like rand strings are not legal.protected T val;...function new(string name="", scope="");super.new(name, scope);`ifndef UVM_NO_DEPRECATED
beginfor(int i=0;i<name.len();i++) beginif(name.getc(i) inside {".","/","[","*","{"}) begin`uvm_warning("UVM/RSRC/NOREGEX", $sformatf("a resource with meta characters in the field name has been created \"%s\"",name))break;end    end
end`endif    endfunctionstatic function this_type get_type();if(my_type == null)my_type = new();return my_type;endfunctionfunction uvm_resource_base get_type_handle();return get_type();endfunction...
endclass

  uvm_resource类中定义了T类型的变量val,用来存储类型T的资源的值。重载其父类的get_type_handle()函数直接返回静态函数get_type()的返回值,这里是一个singleton模式,get_type()会返回该类型在全局中的唯一实例句柄。new()函数直接调用父类uvm_resource_base的new()来设置name和scope。

2.3.1. set系列函数

  uvm_resource类提供了三个函数set(),set_override()和set_priority(),这三个函数都是首先调用uvm_resource_pool中的静态函数get()拿到全局资源池uvm_resource_pool的唯一句柄然后分别调用其相应函数,对于uvm_resource_pool类中的这些函数分析请参考后文。

//uvm_resource.svh
class uvm_resource #(type T=int) extends uvm_resource_base;...function void set();uvm_resource_pool rp = uvm_resource_pool::get();rp.set(this);endfunctionfunction void set_override(uvm_resource_types::override_t override = 2'b11);uvm_resource_pool rp = uvm_resource_pool::get();rp.set(this, override);endfunctionfunction void set_priority (uvm_resource_types::priority_e pri);uvm_resource_pool rp = uvm_resource_pool::get();rp.set_priority(this, pri);endfunction...
endfunction

2.3.2. get系列函数

  静态函数get_by_name()调用的uvm_resource_pool类中的get_by_name()函数,所不同的是后者多出的第三个参数my_type正是该uvm_resource #(T)类的全局唯一静态实例句柄。拿到uvm_resource_base类型句柄后调用$cast将其向下赋值给uvm_resource #(T)类型句柄并返回。

//uvm_resource.svh
class uvm_resource #(type T=int) extends uvm_resource_base;...static function this_type get_by_name(string scope,string name,bit rpterr = 1);uvm_resource_pool rp = uvm_resource_pool::get();uvm_resource_base rsrc_base;this_type rsrc;string msg;rsrc_base = rp.get_by_name(scope, name, my_type, rpterr);if(rsrc_base == null)return null;if(!$cast(rsrc, rsrc_base)) beginif(rpterr) begin$sformat(msg, "Resource with name %s in scope %s has incorrect type", name, scope);`uvm_warning("RSRCTYPE", msg);endreturn null;endreturn rsrc;endfunctionstatic function this_type get_by_type(string scope = "",uvm_resource_base type_handle);uvm_resource_pool rp = uvm_resource_pool::get();uvm_resource_base rsrc_base;this_type rsrc;string msg;if(type_handle == null)return null;rsrc_base = rp.get_by_type(scope, type_handle);if(rsrc_base == null)return null;if(!$cast(rsrc, rsrc_base)) begin$sformat(msg, "Resource with specified type handle in scope %s was not located", scope);`uvm_warning("RSRCNF", msg);return null;endreturn rsrc;endfunction...
endclass

  静态函数get_by_type()同样是调用的uvm_resource_pool类中的get_by_type()函数,拿到uvm_resource_base类型句柄后调用$cast将其向下赋值给uvm_resource #(T)类型句柄并返回。
  此外,uvm_resource类还提供了一个静态函数get_highest_precedence():

//uvm_resource.svh
class uvm_resource #(type T=int) extends uvm_resource_base;...static function this_type get_highest_precedence(ref uvm_resource_types::rsrc_q_t q);this_type rsrc;this_type r;int unsigned i;int unsigned prec;int unsigned first;if(q.size() == 0)return null;first = 0;rsrc = null;prec = 0;// Locate first resources in the queue whose type is Tfor(first = 0; first < q.size() && !$cast(rsrc, q.get(first)); first++);// no resource in the queue whose type is Tif(rsrc == null)return null;prec = rsrc.precedence;// start searching from the next resource after the first resource// whose type is Tfor(int i = first+1; i < q.size(); ++i) beginif($cast(r, q.get(i))) beginif(r.precedence > prec) beginrsrc = r;prec = r.precedence;endendendreturn rsrc;endfunction...
endclass

  这个函数是从参数传入的uvm_queue类型句柄的队列中首先找到第一个类型为uvm_resource #(T)的元素,然后记录下其precedence,然后遍历队列其余的元素找出相同类型的precedence最高的元素并返回。

2.3.3. write()/read()

  uvm_resource类还提供了两个函数read()和write()来对其中存储的val进行读写操作:

//uvm_resource.svh
class uvm_resource #(type T=int) extends uvm_resource_base;...function T read(uvm_object accessor = null);record_read_access(accessor);return val;endfunctionfunction void write(T t, uvm_object accessor = null);if(is_read_only()) beginuvm_report_error("resource", $sformatf("resource %s is read only -- cannot modify", get_name()));return;end// Set the modified bit and record the transaction only if the value// has actually changed.if(val == t)return;record_write_access(accessor);// set the value and set the dirty bitval = t;modified = 1;endfunction...
endclass

  read()函数首先调用record_read_access()记录下这次读操作的信息并返回返回val的值。write()函数会首先检查变量read_only是否为1,若是,则报错直接返回。若写入的值与当前val一致,则直接返回,否则调用record_write_access()记录下这次写操作并将val更新为新写入的值,将modified置为1。函数record_write_access()留待下文关于debug的部分分析。

3. uvm_resource_pool

3.1. 数据存储结构

  UVM提供了一个全局共享资源池uvm_resource_pool:

uvm_resource.svh
class uvm_resource_pool;static local uvm_resource_pool rp = get();uvm_resource_types::rsrc_q_t rtab [string];uvm_resource_types::rsrc_q_t ttab [uvm_resource_base];...local function new();endfunctionstatic function uvm_resource_pool get();if(rp == null)rp = new();return rp;endfunction...
endclass

  uvm_resource_pool类自成一体,没有继承自任何类型。其静态函数get()返回该类型在全局的唯一实例。主要储存资源的数据类型是两个联合数组rtab[]和ttab[],二者分别以字符串类型的name和uvm_resource_base类型的type为索引,储存内容为uvm_resource_types类中的静态uvm_queue类型的rsrc_q_t(我们暂且将rsrc_q_t理解为SV中的队列,下文中提到这个类型均以队列代称),也就是说我们可以通过name或者type为索引将一笔uvm_resource #(T)类型的资源分别存入以name/type为索引的队列rsrc_q_t中,很明显,以同一name为索引的这个队列也可以存入不同type的其它资源(他们的name是相同的),同理以同一type为索引的队列中也可以存入不同name的其它资源(他们的type是相同的)。UVM源码中给出了非常形象的数据结构示意图:

3.2. set系列函数

  前面提到uvm_resource类的set系列函数以及uvm_resource_pool类中定义的如set_override(),set_name_override()以及set_type_override()都是调用的uvm_resource_pool类的set()函数:

//uvm_resource.svh
class uvm_resource_pool;...function void set (uvm_resource_base rsrc, uvm_resource_types::override_t override = 0);uvm_resource_types::rsrc_q_t rq;string name;uvm_resource_base type_handle;`ifdef UVM_LOOKUP_SCOPE_FOR_FIELDS
//`ifndef UVM_ORIGINAL_LOOKUP_SCOPEint   i;string field_name;
`endif// If resource handle is ~null~ then there is nothing to do.if(rsrc == null)return;// insert into the name map.  Resources with empty names are// anonymous resources and are not entered into the name mapname = rsrc.get_name();if(name != "") beginif(rtab.exists(name))rq = rtab[name];elserq = new();// Insert the resource into the queue associated with its name.// If we are doing a name override then insert it in the front of// the queue, otherwise insert it in the back.if(override & uvm_resource_types::NAME_OVERRIDE)rq.push_front(rsrc);elserq.push_back(rsrc);rtab[name] = rq;`ifdef UVM_LOOKUP_SCOPE_FOR_FIELDS
//`ifndef UVM_ORIGINAL_LOOKUP_SCOPE// Following will add the overhead whenever the resource is added but the overhead will well justifiable // while doing apply_config_settings (mainly for bigger testbenches). // The idea is to avoid looking for the scope for the resources which are not in the component and // hence reduce calls to match_scope functions.// Just look for "[" or "." and save that in rtab_arr_obj table. This is the resource table that has resource name // which has "[" or "." in it (that is array type or object type).// does name have brackets [] in it?for (i = 0; i < name.len(); i++)if (name[i] == "[" || name[i] == ".")break;if (i < name.len()) beginfield_name = name.substr(0,i-1); // Extract the field name i.e. name before "[" or "."rtab_arr_obj[field_name][name] = 1; // If resource name is same but scope was different, // we don't need to create new entry at second dimension of array.end
`endifend// insert into the type maptype_handle = rsrc.get_type_handle();if(ttab.exists(type_handle))rq = ttab[type_handle];elserq = new();// insert the resource into the queue associated with its type.  If// we are doing a type override then insert it in the front of the// queue, otherwise insert it in the back of the queue.if(override & uvm_resource_types::TYPE_OVERRIDE)rq.push_front(rsrc);elserq.push_back(rsrc);ttab[type_handle] = rq;endfunction...
endclass

  函数set()有两个参数,第一个参数是uvm_resource_base类型句柄rsrc,指向要被存储的资源类uvm_resource #(T),第二个参数是uvm_resource_types类中的静态变量override_t,表示该笔资源在存到相应队列时是否要override之前已存在的资源。首先检查rsrc是否为null,若是则直接返回,否则调用get_name()返回字符串变量m_leaf_name,此时这个m_leaf_name其实就是我们在创建uvm_resource #(T)类实例调用new()时传入的第一个字符串参数。get_name()是uvm_object中定义的函数,返回其中的字符串变量m_leaf_name,在uvm_resource_base的new()函数中会调用uvm_object的new()函数,在其中把传入的字符串变量name赋值给m_leaf_name。若get_name()返回值name不是空字符串,则判断数组rtab[]是否已经有了以这个字符串为索引的内容,若没有则调用new()创建一个uvm_resource_types::rsrc_q_t类型队列,若override_t是NAME_OVERRIDE,则将第一个参数传入的rsrc句柄存入该队列最前端,否则存入该队列最后面,然后将该队列以name为索引存入数组rtab[]。
  接下来调用get_type_handle()拿到该uvm_resource #(T)类唯一对象句柄type_handle,若数组ttab[]中没有以该句柄为索引的内容,则调用new()创建一个uvm_resource_types::rsrc_q_t类型队列,若override_t是TYPE_OVERRIDE,则将第一个参数传入的rsrc句柄存入该队列最前端,否则存入该队列最后面,最后将该队列以type_handle为索引存入数组ttab[]。
  此外,uvm_resource_pool还定义了两个函数set_priority_type()和set_priority_name():

//uvm_resource.svh
class uvm_resource_pool;...function void set_priority_type(uvm_resource_base rsrc,uvm_resource_types::priority_e pri);uvm_resource_base type_handle;string msg;uvm_resource_types::rsrc_q_t q;if(rsrc == null) beginuvm_report_warning("NULLRASRC", "attempting to change the serach priority of a null resource");return;endtype_handle = rsrc.get_type_handle();if(!ttab.exists(type_handle)) begin$sformat(msg, "Type handle for resrouce named %s not found in type map; cannot change its search priority", rsrc.get_name());uvm_report_error("RNFTYPE", msg);return;endq = ttab[type_handle];set_priority_queue(rsrc, q, pri);endfunctionfunction void set_priority_name(uvm_resource_base rsrc,uvm_resource_types::priority_e pri);string name;string msg;uvm_resource_types::rsrc_q_t q;if(rsrc == null) beginuvm_report_warning("NULLRASRC", "attempting to change the serach priority of a null resource");return;endname = rsrc.get_name();if(!rtab.exists(name)) begin$sformat(msg, "Resrouce named %s not found in name map; cannot change its search priority", name);uvm_report_error("RNFNAME", msg);return;endq = rtab[name];set_priority_queue(rsrc, q, pri);endfunction...
endclass

  二者分别通过传入的资源句柄的type或name为索引在数组ttab[]或rta[]中找到相应的储存相关资源的队列,然后将uvm_resource_types::priority_e作为参数调用set_priority_queue():

//uvm_resource.svh
class uvm_resource_pool;...local function void set_priority_queue(uvm_resource_base rsrc,ref uvm_resource_types::rsrc_q_t q,uvm_resource_types::priority_e pri);uvm_resource_base r;int unsigned i;string msg;string name = rsrc.get_name();for(i = 0; i < q.size(); i++) beginr = q.get(i);if(r == rsrc) break;endif(r != rsrc) begin$sformat(msg, "Handle for resource named %s is not in the name name; cannot change its priority", name);uvm_report_error("NORSRC", msg);return;endq.delete(i);case(pri)uvm_resource_types::PRI_HIGH: q.push_front(rsrc);uvm_resource_types::PRI_LOW:  q.push_back(rsrc);endcaseendfunction...
endclass

  这个函数会把要设置的资源rsrc在队列中的位置根据priority_e的值进行调整,若priority_e为PRI_HIGH,则放入队列最前端,若priority_e为PRI_LOW,则放入队列最后。

3.3. get_by_name()/get_by_type()

  uvm_resource_pool提供了可以通过字符串scope和name来找到数组中相应资源类的函数get_by_name():

//uvm_resource.svh
class uvm_resource_pool;...function uvm_resource_base get_by_name(string scope = "",string name,uvm_resource_base type_handle,bit rpterr = 1);uvm_resource_types::rsrc_q_t q;uvm_resource_base rsrc;q = lookup_name(scope, name, type_handle, rpterr);if(q.size() == 0) beginpush_get_record(name, scope, null);return null;endrsrc = get_highest_precedence(q);push_get_record(name, scope, rsrc);return rsrc;endfunction...
endclass

  首先调用lookup_name()来找到符合条件的队列句柄:

//uvm_resource.svh
class uvm_resource_pool;...function uvm_resource_types::rsrc_q_t lookup_name(string scope = "",string name,uvm_resource_base type_handle = null,bit rpterr = 1);uvm_resource_types::rsrc_q_t rq;uvm_resource_types::rsrc_q_t q;uvm_resource_base rsrc;uvm_resource_base r;// ensure rand stability during lookupbeginprocess p = process::self();string s;if(p!=null) s=p.get_randstate();q=new();if(p!=null) p.set_randstate(s);end// resources with empty names are anonymous and do not exist in the name mapif(name == "")return q;// Does an entry in the name map exist with the specified name?// If not, then we're doneif(!rtab.exists(name)) beginif(rpterr) void'(spell_check(name));    return q;end    rsrc = null;rq = rtab[name];for(int i=0; i<rq.size(); ++i) begin r = rq.get(i);// does the type and scope match?if(((type_handle == null) || (r.get_type_handle() == type_handle)) &&r.match_scope(scope))q.push_back(r);endreturn q;endfunction...
endclass

  若传入的字符串name为空,则返回一个空队列。若数组rtab[]中没有相关记录且rpterr为1,则调用spell_check()并返回一个空队列,spell_check()会检查传入的字符串与数组中的索引相比较并给出信息。否则遍历以name为索引的这个队列,若参数传入的type_handle为空或者与队列中某个元素一致,并且match_scope()返回1,则这些元素放入一个uvm_resource_types::rsrc_q_t类型队列q并返回q。函数match_scope()会调用函数uvm_re_match()来检测scope是否可以与参数传入的字符串匹配。
  回到函数get_by_name(),若lookup_name()返回的队列为空,则调用push_get_record()记录下这次访问信息并直接返回。否则调用get_highest_precedence()取得优先级最高的那笔uvm_resource #(T)句柄并记录相关访问信息后返回这句柄。这个get_highest_precedence()函数定义在uvm_resource_pool类中,与之前分析的uvm_resource类中的静态函数get_highest_precedence()并不是同一个但是代码几乎一样,这里不再赘述。
总结:函数get_by_name()通过传入的字符串参数scope和name以及type句柄(若有)在uvm_resource_pool中寻找符合的uvm_resource #(T)资源类型并返回优先级最高的那个。
  uvm_resource_pool还提供了可以通过字符串scope和uvm_resource_base类型句柄type_handle来找到数组中相应资源类的函数get_by_type():

//uvm_resource.svh
class uvm_resource_pool;...function uvm_resource_base get_by_type(string scope = "",uvm_resource_base type_handle);uvm_resource_types::rsrc_q_t q;uvm_resource_base rsrc;q = lookup_type(scope, type_handle);if(q.size() == 0) beginpush_get_record("<type>", scope, null);return null;endrsrc = q.get(0);push_get_record("<type>", scope, rsrc);return rsrc;endfunction...
endclass

  首先调用lookup_type()取得符合条件的资源队列句柄q,若q为空队列,则调用push_get_record()记录下这次访问信息并返回。否则返回q中第一个元素并同样记录下这次访问信息。函数lookup_type()定义在

class uvm_resource_pool;...function uvm_resource_types::rsrc_q_t lookup_type(string scope = "",uvm_resource_base type_handle);uvm_resource_types::rsrc_q_t q = new();uvm_resource_types::rsrc_q_t rq;uvm_resource_base r;int unsigned i;if(type_handle == null || !ttab.exists(type_handle)) beginreturn q;endrq = ttab[type_handle];for(int i = 0; i < rq.size(); ++i) begin r = rq.get(i);if(r.match_scope(scope))q.push_back(r);endreturn q;endfunction

  若传入的参数type_handle为空句柄,或者数组ttab[]中不存在关于type_handle的信息,则直接返回uvm_resource_types::rsrc_q_t类型的空队列q。否则遍历ttab[]数组中以type_handle为索引的队列rq,若元素调用match_scope()返回值为1,则将其放入uvm_resource_types::rsrc_q_t类型的队列q,最后返回q。
  总结:函数get_by_type()通过传入的字符串参数scope和uvm_resource_base类型句柄type在uvm_resource_pool中寻找符合的uvm_resource #(T)资源类型并返回排在最前面的那个。

4. uvm_resource_db

  参数类uvm_resource_db #(T)也是自成一派,没有继承自任何类型。其中的静态函数get_by_type()和get_by_name()直接调用与其具有相同类型参数的uvm_resource #(T)类中的同名静态函数,最终调用uvm_resource_pool类中的同名函数,上文已经提及相关函数,此处不再赘述。

//uvm_resource_db.svh
class uvm_resource_db #(type T=uvm_object);typedef uvm_resource #(T) rsrc_t;protected function new();endfunctionstatic function rsrc_t get_by_type(string scope);return rsrc_t::get_by_type(scope, rsrc_t::get_type());endfunctionstatic function rsrc_t get_by_name(string scope,string name,bit rpterr=1);return rsrc_t::get_by_name(scope, name, rpterr);endfunction...
endclass

4.1. set()系列函数

4.1.1. set()

  uvm_resource_db的静态函数set()是其最常用的函数之一:

//uvm_resource_db.svh
class uvm_resource_db #(type T=uvm_object);...static function void set(input string scope, input string name,T val, input uvm_object accessor = null);rsrc_t rsrc = new(name, scope);rsrc.write(val, accessor);rsrc.set();if(uvm_resource_db_options::is_tracing())m_show_msg("RSRCDB/SET", "Resource","set", scope, name, accessor, rsrc);endfunction...
endclass

  set()有四个参数,前两个参数分别是string类型的scope和name,在函数内部uvm_resource #(T)根据这两个参数调用new()来创建一个自身实例对象rsrc,第三个参数是T类型的值val,第四个参数是uvm_object类型的accessor,主要是用来记录debug信息的(后文会分析相关内容),调用uvm_resource #(T)类的write()函数将val写入同时记录相关操作信息,然后调用set()将这笔资源句柄rsrc存入资源池。
  与set()函数类似的还有一个静态函数set_anonymous():

//uvm_resource_db.svh
class uvm_resource_db #(type T=uvm_object);...static function void set_anonymous(input string scope,T val, input uvm_object accessor = null);rsrc_t rsrc = new("", scope);rsrc.write(val, accessor);rsrc.set();if(uvm_resource_db_options::is_tracing())m_show_msg("RSRCDB/SETANON","Resource", "set", scope, "", accessor, rsrc);endfunction...
endclass

  这个函数与set()相比,不需要传入第二个参数name,uvm_resource #(T)在调用new()时参数name为空字符串,因而调用set()时只会存入数组ttab[]而不会存入rtab[]。

4.1.2. set_override()

  同样可以通过静态函数set_override()来使得当前这笔资源被存入相关队列最前端:

//uvm_resource_db.svh
class uvm_resource_db #(type T=uvm_object);...static function void set_override(input string scope, input string name,T val, uvm_object accessor = null);rsrc_t rsrc = new(name, scope);rsrc.write(val, accessor);rsrc.set_override();if(uvm_resource_db_options::is_tracing())m_show_msg("RSRCDB/SETOVRD", "Resource","set", scope, name, accessor, rsrc);endfunction...
endclass

  在set_override()中,用set_override()函数来取代set(),默认override_t为2’b11,会把当前这笔资源分别存入相关name队列和type队列的最前端。另外还可以通过静态函数set_override_type()和set_override_name()分别将当前这笔资源存入相关type队列最前端(同时name队列最后端)和name队列最前端(type队列最后端):

//uvm_resource_db.svh
class uvm_resource_db #(type T=uvm_object);...static function void set_override_type(input string scope, input string name,T val, uvm_object accessor = null);rsrc_t rsrc = new(name, scope);rsrc.write(val, accessor);rsrc.set_override(uvm_resource_types::TYPE_OVERRIDE);if(uvm_resource_db_options::is_tracing())m_show_msg("RSRCDB/SETOVRDTYP","Resource", "set", scope, name, accessor, rsrc);endfunctionstatic function void set_override_name(input string scope, input string name,T val, uvm_object accessor = null);rsrc_t rsrc = new(name, scope);rsrc.write(val, accessor);rsrc.set_override(uvm_resource_types::NAME_OVERRIDE);if(uvm_resource_db_options::is_tracing())m_show_msg("RSRCDB/SETOVRDNAM","Resource", "set", scope, name, accessor, rsrc);endfunction...
endclass

4.2. write_by_name()/write_by_type()

  uvm_resource_db类提供了静态函数write_by_name()和write_by_type():

//uvm_resource_db.svh
class uvm_resource_db #(type T=uvm_object);...static function bit write_by_name(input string scope, input string name,input T val, input uvm_object accessor = null);rsrc_t rsrc = get_by_name(scope, name);if(uvm_resource_db_options::is_tracing())m_show_msg("RSRCDB/WR","Resource", "written", scope, name, accessor, rsrc);if(rsrc == null)return 0;rsrc.write(val, accessor);return 1;endfunctionstatic function bit write_by_type(input string scope,input T val, input uvm_object accessor = null);rsrc_t rsrc = get_by_type(scope);if(uvm_resource_db_options::is_tracing())m_show_msg("RSRCDB/WRTYP", "Resource","written", scope, "", accessor, rsrc);if(rsrc == null)return 0;rsrc.write(val, accessor);return 1;endfunction...
endclass

  二者的区别在于前者的第二个参数需要传入一个字符串类型的name,而后者不需要,在函数中,前者调用get_by_name()而后者调用get_by_type()来取得我们想要写入的资源uvm_resource #(T)类型储存在uvm_resource_pool中的实例句柄rsrc,若该笔资源尚未被储存在资源池即rsrc为null,则直接返回0。否则调用write()更新该笔资源的val并返回1。可以看出这两个函数主要是用来更新资源池中已有资源的val,对于尚未被储存的资源来说,调用这两个函数并不会起什么作用。

4.3. read_by_name()/read_by_type()

  uvm_resource_db类还提供了静态函数read_by_name()和read_by_type()来从资源池中获取相应的资源:

//uvm_resource_db.svh
class uvm_resource_db #(type T=uvm_object);...static function bit read_by_name(input string scope,input string name,inout T val, input uvm_object accessor = null);rsrc_t rsrc = get_by_name(scope, name);if(uvm_resource_db_options::is_tracing())m_show_msg("RSRCDB/RDBYNAM","Resource", "read", scope, name, accessor, rsrc);if(rsrc == null)return 0;val = rsrc.read(accessor);return 1;endfunctionstatic function bit read_by_type(input string scope,inout T val,input uvm_object accessor = null);rsrc_t rsrc = get_by_type(scope);if(uvm_resource_db_options::is_tracing())m_show_msg("RSRCDB/RDBYTYP", "Resource","read", scope, "", accessor, rsrc);if(rsrc == null)return 0;val = rsrc.read(accessor);return 1;endfunction...
endclass

  与相关write函数类似,这两个函数也是前者的参数中多了一个字符串类型的name,在函数中二者调用get_by_name()和get_by_type()从资源池中拿到相应的uvm_resource #(T)类型的句柄rsrc,若该笔资源尚未被储存在资源池即rsrc为null,则直接返回0。否则调用read()读取该笔资源的val赋值给inout类型参数val并返回1。

5. uvm_config_db

  uvm_config_db类继承自uvm_resource_db类:

//uvm_config_db.svh
class uvm_config_db#(type T=int) extends uvm_resource_db#(T);// Internal lookup of config settings so they can be reused// The context has a pool that is keyed by the inst/field name.static uvm_pool#(string,uvm_resource#(T)) m_rsc[uvm_component];// Internal waiter list for wait_modifiedstatic local uvm_queue#(m_uvm_waiter) m_waiters[string];...
endclass

  其中定义了两个静态联合数组m_rsc[]和m_waiters[]。其中m_rsc[]以uvm_component类型为索引,储存内容为uvm_pool类型:

//uvm_pool.svh
class uvm_pool #(type KEY=int, T=uvm_void) extends uvm_object;const static string type_name = "uvm_pool";typedef uvm_pool #(KEY,T) this_type;static protected this_type m_global_pool;protected T pool[KEY];// Function: new//// Creates a new pool with the given ~name~.function new (string name="");super.new(name);endfunction...
endclass

  uvm_pool类是UVM提供的一种数据封装类型,类似于SV的联合数组或者python里的字典,其数据是以键-值对的形式存储,可以通过键KEY来索引其储存的值T。uvm_config_db里的静态数组m_rsc[]储存的uvm_pool类型以字符串为索引,储存内容为uvm_resource#(T)类型句柄。
  联合数组m_waiters[]以字符串为索引,储存内容为uvm_queue类型,而queue中储存的内容是m_uvm_waiter类型:

//uvm_config_db.svh
//Internal class for config waiters
class m_uvm_waiter;string inst_name;string field_name;event trigger;function new (string inst_name, string field_name);this.inst_name = inst_name;this.field_name = field_name;endfunction
endclass

  m_uvm_waiter类中只有两个字符串变量inst_name和field_name以及event变量trigger,其new()函数将两个参数分别赋值给inst_name和field_name。这个类型主要是用来表示在通过uvm_config_db来获取资源时的一些等待事件,如必须要等待该资源已经被set()存储在资源池中。

5.1. set()

  uvm_config_db类提供了静态函数set()来向资源池中存入资源:

//uvm_config_db.svh
class uvm_config_db#(type T=int) extends uvm_resource_db#(T);...static function void set(uvm_component cntxt,string inst_name,string field_name,T value);uvm_root top;uvm_phase curr_phase;uvm_resource#(T) r;bit exists;string lookup;uvm_pool#(string,uvm_resource#(T)) pool;string rstate;uvm_coreservice_t cs = uvm_coreservice_t::get();//take care of random stability during allocationprocess p = process::self();if(p != null) rstate = p.get_randstate();top = cs.get_root();curr_phase = top.m_current_phase;if(cntxt == null) cntxt = top;if(inst_name == "") inst_name = cntxt.get_full_name();else if(cntxt.get_full_name() != "") inst_name = {cntxt.get_full_name(), ".", inst_name};if(!m_rsc.exists(cntxt)) beginm_rsc[cntxt] = new;endpool = m_rsc[cntxt];// Insert the token in the middle to prevent cache// oddities like i=foobar,f=xyz and i=foo,f=barxyz.// Can't just use '.', because '.' isn't illegal// in field nameslookup = {inst_name, "__M_UVM__", field_name};if(!pool.exists(lookup)) beginr = new(field_name, inst_name);pool.add(lookup, r);endelse beginr = pool.get(lookup);exists = 1;endif(curr_phase != null && curr_phase.get_name() == "build")r.precedence = uvm_resource_base::default_precedence - (cntxt.get_depth());elser.precedence = uvm_resource_base::default_precedence;r.write(value, cntxt);if(exists) beginuvm_resource_pool rp = uvm_resource_pool::get();rp.set_priority_name(r, uvm_resource_types::PRI_HIGH);endelse begin//Doesn't exist yet, so put it in resource db at the head.r.set_override();end//trigger any waitersif(m_waiters.exists(field_name)) beginm_uvm_waiter w;for(int i=0; i<m_waiters[field_name].size(); ++i) beginw = m_waiters[field_name].get(i);if(uvm_re_match(uvm_glob_to_re(inst_name),w.inst_name) == 0)->w.trigger;  endendif(p != null)p.set_randstate(rstate);if(uvm_config_db_options::is_tracing())m_show_msg("CFGDB/SET", "Configuration","set", inst_name, field_name, cntxt, r);endfunction...
endclass

  这个函数有四个参数,第一个参数是uvm_component类型的cntxt,指出在哪个component中设置的这个资源,第二和第三个参数分别是字符串类型的inst_name和field_name,最后一个参数是要存入的资源类型T的值val。set()函数首先拿到一个process的句柄p,然后在函数开头和结尾分别调用get_randstate()和set_randstate(),这是两个SV的函数,此举是为了保证在set()函数执行过程中保持随机数发生器的稳定。接下来根据前两个参数决定inst_name,例如我们在testcase的build_phase中调用此函数,以下几种情形:

  1. uvm_config_db #(case_cfg)::set(this, “env.agt.drv”, “case_cfg”, cfg); (常见的推荐用法)
    则inst_name值为"uvm_test_top.env.agt.drv"。
  2. uvm_config_db #(case_cfg)::set(, “env.agt.drv”, “case_cfg”, cfg);
    则inst_name值为"uvm_top.env.agt.drv"。
  3. uvm_config_db #(case_cfg)::set(this, “”, “case_cfg”, cfg);
    则inst_name值为"uvm_top"。

  若数组m_rsc[]中没有以cntxt为索引的相关内容(很明显,第一次调用的时候肯定是没有的),那么调用new()函数创建一个以cntxt为索引的uvm_pool类型的句柄pool,接下来将inst_name和failed_name字符串中间加上字符"M_UVM"拼接起来,之所以中间要以特定字符串隔开是避免后面检索匹配时引起歧义,将拼接后的字符串赋值给lookup,以我们上面的第一种常见用法为例,此时lookup的值为"uvm_test_top.env.agt.drv__M_UVM__case_cfg"。若pool中不存在以lookup为索引的资源(很明显此时肯定是没有的),则将field_name和inst_name分别作为new()的两个参数name和scope创建一个uvm_resource#(T)类型的实例r(此即我们要写入的资源,其name=“case_cfg”,scope=“uvm_test_top.env.agt.drv”),调用add()以字符串lookup为索引,将r存入pool中。若这是第二次在testcase中调用set():
  uvm_config_db #(case_cfg)::set(this, “env.agt.drv”, “case_cfg”, new_cfg);
  第二次设置的是new_cfg,则由于inst_name和lookup都与第一次设置相同,所以直接在pool中以lookup为索引检索到第一次设置的资源将其赋值给句柄r,并把bit型变量exists置为1。
  接下来若set()函数是在build_phase()中被调用,则设置该笔资源的precedence为default_precedence减去cntxt的get_depth()返回值,这就是说,调用set()的component离uvm_top越远,其precedence越小,换句话说,处于UVM平台hireachy顶端的component设置的同样的资源具有较高优先级。注意,这个规则起作用的前提条件是在build_phase()中调用set(),否则,该笔资源r的precedence会被设置为default_precedence。然后调用write()将这笔资源的val更新并记录相关访问信息。
  接下来若exists为0(即这笔资源是第一次被存储),则直接调用set_override()将其分别存入name队列和type队列最前端。否则将uvm_resource_types::PRI_HIGH作为第二个参数调用uvm_resource_pool中的函数set_priority_name()将其更新到name队列最前端。由此可见,同一component中按先后顺序调用set()对同一资源进行设置,最后设置的资源具有最高优先级(无论是不是在build_phase中都成立)。
  最后检查数组m_waiters[]中是否有储存以field_name为索引的队列,若有则调用uvm_re_match()比较二者inst_name是否匹配,若返回1则触发trigger事件。这段代码主要是为了处理任务wait_modified()中的阻塞事件,在我们调用get()获取某项资源之前,可以调用该任务来确认该资源已经通过set()被存入资源池:

//uvm_config_db.svh
class uvm_config_db#(type T=int) extends uvm_resource_db#(T);...static task wait_modified(uvm_component cntxt, string inst_name,string field_name);process p = process::self();string rstate = p.get_randstate();m_uvm_waiter waiter;uvm_coreservice_t cs = uvm_coreservice_t::get();if(cntxt == null)cntxt = cs.get_root();if(cntxt != cs.get_root()) beginif(inst_name != "")inst_name = {cntxt.get_full_name(),".",inst_name};elseinst_name = cntxt.get_full_name();endwaiter = new(inst_name, field_name);if(!m_waiters.exists(field_name))m_waiters[field_name] = new;m_waiters[field_name].push_back(waiter);p.set_randstate(rstate);// wait on the waiter to trigger@waiter.trigger;// Remove the waiter from the waiter list for(int i=0; i<m_waiters[field_name].size(); ++i) beginif(m_waiters[field_name].get(i) == waiter) beginm_waiters[field_name].delete(i);break;endend endtask
endclass

  在wait_modified()中将inst_name和field_name作为参数new()函数创建一个m_uvm_waiter类型实例waiter,若数组m_waiters[]中不存在以field_name为索引的uvm_queue型队列,则创建一个uvm_queue型队列实例并将waiter存入,此后等待waiter中的trigger事件被触发(调用set()设置相关资源时会被触发),该事件被触发后,从m_waiters[field_name]对应的队列中将该waiter句柄删除。

5.2. get()

  与set()相对应,uvm_config_db提供了get()函数来获取资源池中的资源:

//uvm_config_db.svh
class uvm_config_db#(type T=int) extends uvm_resource_db#(T);...static function bit get(uvm_component cntxt,string inst_name,string field_name,inout T value);
//TBD: add file/lineint unsigned p;uvm_resource#(T) r, rt;uvm_resource_pool rp = uvm_resource_pool::get();uvm_resource_types::rsrc_q_t rq;uvm_coreservice_t cs = uvm_coreservice_t::get();if(cntxt == null) cntxt = cs.get_root();if(inst_name == "") inst_name = cntxt.get_full_name();else if(cntxt.get_full_name() != "") inst_name = {cntxt.get_full_name(), ".", inst_name};rq = rp.lookup_regex_names(inst_name, field_name, uvm_resource#(T)::get_type());r = uvm_resource#(T)::get_highest_precedence(rq);if(uvm_config_db_options::is_tracing())m_show_msg("CFGDB/GET", "Configuration","read", inst_name, field_name, cntxt, r);if(r == null)return 0;value = r.read(cntxt);return 1;endfunction...
endclass

  与set()函数的四个参数一致,get()也有四个参数,这里不再一一赘述。首先也是在函数中根据参数取得inst_name,然后分别将inst_name,field_name和uvm_resource#(T)的句柄作为参数调用uvm_resource_pool的lookup_regex_names():

//uvm_resource.svh
class uvm_resource_pool;...function uvm_resource_types::rsrc_q_t lookup_regex_names(string scope,string name,uvm_resource_base type_handle = null);return lookup_name(scope, name, type_handle, 0);endfunction...
endclass

  这个函数直接返回lookup_name()函数的返回值,后者会根据传入的参数在资源池的name队列rtab[]中拿到以name为索引的uvm_queue类型队列句柄,然后遍历这个队列找到与参数传入的scope匹配的元素并将其放入一个uvm_queue类型队列q,最后返回q。由此可见,若某一笔资源只被存入资源池以type为索引的数组ttab[]而没有被存入rtb[]中,调用uvm_config_db #(T)::get()是没有办法拿到这笔资源的。
总结:

  1. 之所以要求调用uvm_config_db的set()和get()函数时对应的第三个参数filed_name一定要一致,是因为这个字符串参数会在set()中调用new()创建这笔资源实例时作为其name参数传入,从而作为数组rtab[]的索引,而get()函数会通过其第三个参数filed_name在rtab[]中搜寻这笔资源所在的uvm_queue类型队列,若二者不一致(哪怕只相差一个字符),则难以搜寻到对应的这笔资源。
  2. 若在某component的build_phase中调用uvm_config_db #(T)::set()对同一目标component设置同一笔资源,第一个参数表示的component节点离uvm_root最近的那个具有最高优先级。若在其它phase如run-time phase中,则在仿真时间靠后的设置具有较高优先级(不论set()的第一个参数component节点是在UVM hireachy哪个层次)。
  3. 若同一component如在testcase中对同一笔资源先后设置两次,则后设置的资源具有较高优先级。
  4. 若处于UVM hireachy同一级别的component如agt和scb在其build_phase中分别对drv用set()设置了同一笔资源,则以build_phase后执行的那个component中设置的资源具有较高优先级(因为在set()中会调用set_override()将这笔资源写入rtab[]),build_phase执行顺序以其名字字符串排序顺序执行。

6. 其它

6.1. 总结

6.1.1. 各常用函数调用关系

  uvm_config_db和uvm_resource_db各常用函数在各个类间调用关系如下:

6.1.2. uvm_config_db与uvm_resource_db的异同点

  根据上面的分析,我们发现其实uvm_config_db和uvm_resource_db对资源的配置和读取都是通过全局唯一的共享资源池uvm_resource_pool实现的,uvm_config_db类继承自uvm_resource_db类并做了一些扩展,二者的区别主要在于:

  1. uvm_config_db的set(),除了指定该笔资源的name和scope外,还可以通过指定一个component类型指针表明设置该笔资源的UVM component节点,UVM据此判断同类型资源从不同节点设置时的优先级。这在验证平台开发和维护过程中可以方便工程师的工作,比如我们在env里边配置了一些cfg句柄或者virtual interface,或者是一些VIP的环境被集成到我们的验证平台时,可以直接在testcase中通过uvm_config_db来重载env或者VIP中的相关配置,而不必深入到env和VIP中更改代码(事实上一般也不能这么做),从而避免直接更改代码可能引发的其它意想不到的问题。
  2. uvm_resource_db的set()(没有限制),write_by_name/type()(该笔资源必须已在资源池中,否则调用无任何影响)函数,则除了指定该笔资源的name和scope外,不必给出一个component类型指针,其第四个参数uvm_object类型指针只是用来给出debug信息。
  3. uvm_config_db的get()函数同样需要指定一个component指针,资源的配置和获取都是受到以component为基础的scope的限制的,虽然可以使用通配符来扩大范围,但毕竟范围之外的component都是无权访问相关资源的,这样可以有效避免使用共享的全局资源引发的问题。
  4. uvm_resource_db的read_by_name/type()函数同样可以靠scope或name来获取相关资源,但是其使用比较灵活,scope字符串不必设置成以UVM hierarchy的component节点为基础的形式,可以设置任意有效字符来标记和读取这笔资源。

  综上所述,在某component节点中配置相关资源时,若有明确的目标component,推荐使用uvm_config_db相关函数。若想在一个sequence中直接获取相关配置,除了使用p_sequencer获得相关资源外(可参阅UVM sequence机制源码探微相关内容),还可以直接使用uvm_resource_db来进行配置和获取相关资源,但是在使用时要注意其scope和name字符串的创建。

6.2. debug信息

6.2.1. UVM_CONFIG/RESOURCE_DB_TRACE

  在调用uvm_config_db的set()/get()等函数时时,这些函数最后会判断uvm_config_db_options::is_tracing()的返回值,若为1则调用m_show_msg()把当前操作的细节打印出来:

class uvm_resource_db #(type T=uvm_object);...protected static function void m_show_msg(input string id,input string rtype,input string action,input string scope,input string name,input uvm_object accessor,input rsrc_t rsrc);T foo;string msg=`uvm_typename(foo);$sformat(msg, "%s '%s%s' (type %s) %s by %s = %s",rtype,scope, name=="" ? "" : {".",name}, msg,action,(accessor != null) ? accessor.get_full_name() : "<unknown>",rsrc==null?"null (failed lookup)":rsrc.convert2string());`uvm_info(id, msg, UVM_LOW)endfunction...
endclass

  静态函数m_show_msg()定义在uvm_resource_db类中,就是根据输入的参数把对一波资源的操作信息打印出来。来看uvm_config_db_options类:

//uvm_config_db.svh
class uvm_config_db_options;static local bit ready;static local bit tracing;...static function bit is_tracing();if (!ready) init();return tracing;endfunctionstatic local function void init();uvm_cmdline_processor clp;string trace_args[$];clp = uvm_cmdline_processor::get_inst();if (clp.get_arg_matches("+UVM_CONFIG_DB_TRACE", trace_args)) begintracing = 1;endready = 1;endfunctionendclass

  这个类主要就是为uvm_config_db中的函数提供debug信息的开关。其中定义了两个静态bit型变量ready和tracing。静态函数is_tracing()首先检查ready是否为0,若是则调用静态函数init()进行初始化,init()会检测仿真命令行是否传入“UVM_CONFIG_DB_TRACE”字符串,若是则把tracing置为1,然后is_tracing()返回tracing的值,表示当前需要打印出debug信息。除了通过在命令行传入字符串来开启debug信息打印功能之外,uvm_config_db_options还提供了另外两个静态函数turn_on_tracing()和turn_off_tracing()来分别控制该功能的开启和关闭:

//uvm_config_db.svh
class uvm_config_db_options;...static function void turn_on_tracing();if (!ready) init();tracing = 1;endfunctionstatic function void turn_off_tracing();if (!ready) init();tracing = 0;endfunction...
endclass

  与之类似,UVM还提供了uvm_resource_db_options类来控制uvm_resource_db中相关函数debug信息的打印功能,我们既可以通过在仿真命令行传入字符串“UVM_RESOURCE_DB_TRACE”来开启该功能,也可以通过直接调用uvm_resource_db_options::turn_on_tracing()和uvm_resource_db_options::turn_off_tracing()来分别开启和关闭debug信息的打印功能。

6.2.2. check_config_usage()

  之前我们在分析uvm_resource #()类的write()/read()函数时,在对每一笔资源进行读写操作时会分别调用record_write_access()和record_read_access():

//uvm_resource.svh
virtual class uvm_resource_base extends uvm_object;...function void record_read_access(uvm_object accessor = null);string str;uvm_resource_types::access_t access_record;// If an accessor object is supplied then get the accessor record.// Otherwise create a new access record.  In either case populate// the access record with information about this access.  Check// first to make sure that auditing is turned on.if(!uvm_resource_options::is_auditing())return;// If an accessor is supplied, then use its name// as the database entry for the accessor record.// Otherwise, use "<empty>" as the database entry.if(accessor != null)str = accessor.get_full_name();elsestr = "<empty>";// Create a new accessor record if one does not existif(access.exists(str))access_record = access[str];elseinit_access_record(access_record);// Update the accessor recordaccess_record.read_count++;access_record.read_time = $realtime;access[str] = access_record;endfunctionfunction void record_write_access(uvm_object accessor = null);string str;// If an accessor object is supplied then get the accessor record.// Otherwise create a new access record.  In either case populate// the access record with information about this access.  Check// first that auditing is turned onif(uvm_resource_options::is_auditing()) beginif(accessor != null) beginuvm_resource_types::access_t access_record;string str;str = accessor.get_full_name();if(access.exists(str))access_record = access[str];elseinit_access_record(access_record);access_record.write_count++;access_record.write_time = $realtime;access[str] = access_record;endendendfunction...function void init_access_record (inout uvm_resource_types::access_t access_record);access_record.read_time = 0;access_record.write_time = 0;access_record.read_count = 0;access_record.write_count = 0;endfunction
endclass

  若函数uvm_resource_options::is_auditing()返回值为1,则首先调用参数传入的uvm_object类型的句柄accessor的get_full_name()函数取得name字符串,然后检查该笔资源的数组access[]中是否有关于这个accessor内容(name为索引),这个access[]我们之前在分析uvm_resource_base类时就看到过,其中储存的是该笔资源被每一个accessor访问的信息。若access[]中不存在关于这个accessor访问此笔资源的内容,则创建一个uvm_resource_types::access_t类型的实例access_record用于记录这个accessor对这笔资源的访问信息并调用init_access_record()对其进行初始化,把所有记录信息归零。最后把这笔access_record记录信息的write_count或者read_count加1并更新对应时间,这次访问信息就被记录完毕。我们检查这笔资源的数组access[]就会知道哪个accessor在什么时间对其进行了几次读写操作。
  UVM提供了uvm_resource_options类来控制是否会开启访问信息的记录功能:

//uvm_resource.svh
class uvm_resource_options;static local bit auditing = 1;static function void turn_on_auditing();auditing = 1;endfunctionstatic function void turn_off_auditing();auditing = 0;endfunctionstatic function bit is_auditing();return auditing;endfunction
endclass

  在uvm_resource_options类中定义了一个静态bit类型变量auditing,默认值为1,静态函数is_auditing()会返回这个变量的值。我们还可以分别通过直接调用静态函数uvm_resource_options::turn_on_auditing()和uvm_resource_options::turn_off_auditing()来动态开启和关闭对平台中资源访问信息的记录功能。
  在资源的配置和获取过程中,UVM是通过我们传入的scope字符串来存储和查找是否有该笔资源,一旦在函数调用时出现拼写失误,就会出现意想不到的问题,UVM并不会给出拼写有误的提示,这个时候去逐个检查验证平台的所有相关代码是很麻烦的一件事情,我们可以在testcase的build_phase之后如connect_phase(一般资源配置和获取都在build_phase中)中调用check_config_usage():

//uvm_component.svh
function void uvm_component::check_config_usage ( bit recurse=1 );uvm_resource_pool rp = uvm_resource_pool::get();uvm_queue#(uvm_resource_base) rq;rq = rp.find_unused_resources();if(rq.size() == 0)return;uvm_report_info("CFGNRD"," ::: The following resources have at least one write and no reads :::",UVM_INFO);rp.print_resources(rq, 1);
endfunction

  这个函数首先会调用uvm_resource_pool的find_unused_resources()函数:

//uvm_resource.svh
class uvm_resource_pool;...function uvm_resource_types::rsrc_q_t find_unused_resources();uvm_resource_types::rsrc_q_t rq;uvm_resource_types::rsrc_q_t q = new;int unsigned i;uvm_resource_base r;uvm_resource_types::access_t a;int reads;int writes;foreach (rtab[name]) beginrq = rtab[name];for(int i=0; i<rq.size(); ++i) beginr = rq.get(i);reads = 0;writes = 0;foreach(r.access[str]) begina = r.access[str];reads += a.read_count;writes += a.write_count;endif(writes > 0 && reads == 0)q.push_back(r);endendreturn q;endfunction
endclass

  函数find_unused_resources()会遍历资源池数组rtab[]中的每一笔资源,检查数组access[]中储存的所有不同accessor对其的访问信息,若某资源只有写入记录而从未有过任何读记录,则将其放入队列q中,最后返回q。
  回到check_config_usage()中,当拿到资源池中所有的从未被读取过的资源后,调用uvm_resource_pool的print_resources()函数将这些信息打印出来,我们可以根据这些提示信息进行debug,函数print_resources()定义在uvm_resource_pool类中,其中会对打印信息的格式做一些设置,这里不再赘述。
  此外,我们还可以直接调用uvm_resources.dump(1)将储存在资源池数组ttab[]中的所有资源打印出来:

//uvm_resource.svh
class uvm_resource_pool;...function void dump(bit audit = 0);uvm_resource_types::rsrc_q_t rq;string name;`uvm_info("UVM/RESOURCE/DUMP","\n=== resource pool ===",UVM_NONE)foreach (rtab[name]) beginrq = rtab[name];print_resources(rq, audit);end`uvm_info("UVM/RESOURCE/DUMP","=== end of resource pool ===",UVM_NONE)endfunction
endclass
...
//----------------------------------------------------------------------
// static global resource pool handle
//----------------------------------------------------------------------
const uvm_resource_pool uvm_resources = uvm_resource_pool::get();

6.2.2. dump_get_records()

  uvm_resource_pool还提供了一个函数dump_get_records()用来记录所有通过调用get_by_name/type()而留下的访问记录:

//uvm_resource.svh
class get_t;string name;string scope;uvm_resource_base rsrc;time t;
endclass
...
class uvm_resource_pool;...get_t get_record [$];...function void dump_get_records();get_t record;bit success;string qs[$];qs.push_back("--- resource get records ---\n");foreach (get_record[i]) beginrecord = get_record[i];success = (record.rsrc != null);qs.push_back($sformatf("get: name=%s  scope=%s  %s @ %0t\n",record.name, record.scope,((success)?"success":"fail"),record.t));end`uvm_info("UVM/RESOURCE/GETRECORD",`UVM_STRING_QUEUE_STREAMING_PACK(qs),UVM_NONE)endfunction...
endclass

  UVM提供了一个类get_t用来记录访问相关的字符串类型的name和scope,以及访问到的这笔资源指针和时间。函数dump_get_records()会遍历uvm_resource_pool中定义的get_t类型的队列get_record[$]并打印出所有相关信息。
  我们之前在分析函数get_by_name/type()时提到过,调用这两个函数在资源池中寻找特定的一笔资源时,会调用push_get_record():

//uvm_resource.svh
class uvm_resource_pool;...function void push_get_record(string name, string scope,uvm_resource_base rsrc);get_t impt;// if auditing is turned off then there is no reason// to save a get recordif(!uvm_resource_options::is_auditing())return;impt = new();impt.name  = name;impt.scope = scope;impt.rsrc  = rsrc;impt.t     = $realtime;get_record.push_back(impt);endfunction...
endclass

  这个函数就是创建一个get_t类型实例,并把参数传入的name,scope以及这笔资源句柄和时间信息记录下来然后存入队列get_record[$]。

UVM config_db机制源码探微相关推荐

  1. UVM factory机制源码探微

    文章目录 1. UVM factory机制的使用 2. class 在factory 中的注册过程 2.1. `uvm_component_utils() 2.1.1. m_uvm_component ...

  2. React事件机制 - 源码概览(下)

    上篇文档 React事件机制 - 源码概览(上)说到了事件执行阶段的构造合成事件部分,本文接着继续往下分析 批处理合成事件 入口是 runEventsInBatch // runEventsInBat ...

  3. Apache Storm 实时流处理系统通信机制源码分析

    我们今天就来仔细研究一下Apache Storm 2.0.0-SNAPSHOT的通信机制.下面我将从大致思想以及源码分析,然后我们细致分析实时流处理系统中源码通信机制研究. 1. 简介 Worker间 ...

  4. JVM-白话聊一聊JVM类加载和双亲委派机制源码解析

    文章目录 Java 执行代码的大致流程 类加载loadClass的步骤 类加载器和双亲委派机制 sun.misc.Launcher源码解析 Launcher实例化 Launcher 构造函数 双亲委派 ...

  5. Zookeeper--Watcher机制源码剖析二

    Watcher触发 我们从实际操作时候的表现来看Watcher的触发,比如Zookeeper中NodeDataChanged时间的触发是"Watcher监听的对应数据节点的数据内容发生变更& ...

  6. Zookeeper--Watcher机制源码剖析一

    Watcher-- 数据变更通知 我们知道Zookeeper提供来分布式数据的订阅/发布功能,一个典型的发布/订阅模型系统定义了一种一对多的订阅关系,能让多个订阅者同时监听某个主题对象,当这个被监听对 ...

  7. RocketMQ高性能通信实现机制源码精读

    精度RocketMQ通信机制源码 异步通信机制 源码精读 生产者发送消息 图1 异步监听消息发送结果 图2 创建一个线程池用其中的一个线程去执行发送消息的动作 图3 监听发送结果如果有回调函数则调用回 ...

  8. ART虚拟机 | Cleaner机制源码分析

    目录 思考问题 1.Android为什么要将Finalize机制替换成Cleaner机制? 2.Cleaner机制回收Native堆内存的原理是什么? 3.Cleaner机制源码是如何实现的? 一.版 ...

  9. 从Masonry 源码探 Objective-C [源码学习开篇]

    原文链接 : http://blog.csdn.net/sinat_30162391/article/details/53321046 前言: 这个系列可能不会分析, Masonry如何如何好, 估计 ...

最新文章

  1. 软件测试实验--数据工厂DataFactory+MySQL数据构造
  2. 编写矩形类 计算矩形面积
  3. 重采样算法实践 --- (一)
  4. SharePoint判断页面(或WebPart)是否处于编辑模式.
  5. 中国古代天文星象学的入门书:《步天歌》
  6. 固定布局(fixed layout)、流体布局(fluid layout)、弹性布局(elastic layout)、响应布局(responsive layout)理解和技巧归纳
  7. matlab主程序和子函数不在一个文件夹下,怎么调用?
  8. (230)数据处理或加速方法(加速处理器)
  9. 在网页中放入贴纸插画是怎样的体验?这样的UI素材,你还不收藏!
  10. C++学习笔记39:进程概念
  11. Java Web下访问外部jar,实例后的Object类型转化的问题
  12. 如何解决Mac电脑键盘上的大写锁定键灯不亮?
  13. 电容或电感的电压_眼见不一定为实!电阻、电容和电感的实际等效模型
  14. 四大汽车总线:LIN、CAN、FlexRay、MOST简述
  15. android 4.4 x86 iso,android x86官方版下载_android x86 4.4 iso 官方最新版[网盘资源]_零度软件园...
  16. Vscode Remote SSH 远程连接失败过程报错:试图写入的管道不存在
  17. 微信开发者工具测试小程序
  18. 2020 China Collegiate Programming Contest Weihai Site H.Message Bomb
  19. 灰度变换-分段线性函数
  20. 网络安全实验之《ARP欺骗攻击》实验报告

热门文章

  1. 新光维医疗更新招股书:年内亏损超5亿 高瓴与礼来是股东
  2. 服务器正文22:linux内核网络模块笔记:收包、发包、内核如何与用户进程协同合作(上)
  3. 2021中国大学综合排名分析+可视化
  4. Federated Continual Learning with Weighted Inter-client Transfer——论文笔记
  5. hbase的架构及设计
  6. processflow利用drawio实现多人协作画流程图功能
  7. java线程运行的时间片长度_CPU核心数,线程数,时间片轮转机制解读
  8. 结构体、枚举、联合体
  9. python中的replaceall_java基础 — String中replace和replaceAll方法
  10. mysql libs 5.6.27_mysql-5.6.27源码安装及错误解决办法