[UVM源代码研究] 当我们调用uvm_config_db里的函数时uvm内部都是怎么工作的
我们已知的uvm_config_db有4个静态方法(所以调用方法是uvm_config_db::):
- set
- get
- exists
- wait_modified
今天我们就通过这四个method来一点点揭开uvm_config_db神秘的面纱。
首先我们最先有的疑惑就是,当我们调用set这个function的时候,我们所set的变量被存在了何处?
我们看到uvm_config_db是从uvm_resource_db继承而来的。
这个uvm_resource是个什么数据类型?
进一步挖下去看看uvm_resource_base是什么。
可以看到只是一个从uvm_object继承而来的virtual类而已,uvm_resource继承后增加了个参数化,而uvm_resource_db中定义(typedef)了一个跟其参数一致的参数化的uvm_resource类型rsrc_t。
后面用的一些函数的返回值或者中间数据都是rsrc_t类型的,本质上是uvm_resource类型。
现在我们回到最开始讨论的问题,set的数据究竟放在哪里了?那我们就需要看看uvm_config_db中关于set函数的定义。首先我们先来看看源代码中关于set这个函数的说明:
从这个函数描述中我们能得出以下结论:
1、在build阶段时,如果set的目的地相同,cntxt决定的hierarchy层次(uvm树形结构中的位置)高的会优先取代层次低的。如果cntxt对应相同的层次,那么后执行的会覆盖先执行的。
2、build完成后,遵循后执行覆盖先执行的原则,也就是说跟执行层次cntxt无关了。
3、set_config_int/set_config_string/set_config_object是参数固定后的uvm_config_db#(int)::set等的简写。
接下来我们再看set函数内的执行代码
我们还需要知道几个变量的来历:
m_rsc是一个key为string,value为uvm_resource#(T)的静态键值对(其他语言里也有叫字典等)(uvm里给之取名叫uvm_pool)组成的以uvm_component索引的数组(这数据类型真是让人一脸懵逼!),这个m_rsc数组可以理解为一个以uvm_pool为value以uvm_component为key的联合数组,只不过这里的value本身又是一个键值对uvm_pool而已。m_waiters讲wait_modified的时候再来分析,先搁置。
再回过头来看下set函数里的行为,173行之前是在做一些变量的声明以及获取一些全局变量包括uvm_top、phase、set的目标路径inst_name等等。174-178是在查看该发送路径(cntxt)是否发起过set,没有则创建,随后将创建完成的键值对赋给前面声明的uvm_pool(string, uvm_resource#(T))对象,这一步相当于把之前让人一脸懵逼的键值对数组的key产生了(即set的第一个参数cntxt),下一步就是产生联合数组的value,即uvm_pool。
这个uvm_pool本身还包含了key和value,key是string类型,value是一个uvm_resource#(T)类型,那下面的工作就是产生这两个类型的值。183行产生了key,185-192产生了value,其中还包含了个查看键值对是否已存在的检查(上面174-176对外层键值对也存在一个检查)。如果已存在了需要将变量exists赋值为1,后面判断替换关系201-208的时候会用到。210-218是在trigger一个event,目的是使得wait_modified的task能够顺利往下执行。220-223超纲了,暂时先不解读了,毕竟我们关心的是set到哪里。这里我们知道set的内容放到了一个键值对联合数组m_rsc里了,由于这个键值对联合数组是一个static类型,所以通过uvm_config_db类都可以访问,并且这个键值对联合数组在set函数里就已经做了优先级替换的判断了,于是这就回答了我们上面提出的第一个问题。
剩下的get函数无非就是到键值对联合数组里取值,就比较简单了。
至于exists和wait_modified无非也是对上面键值对联合数组的一些条件判断而已,这里只贴下代码,就不做具体解读了。
为了帮助大家理解config_db的set和get的过程,我们引用于cluelogic中的图示来结合我们上面的源代码再次帮大家巩固下这方面的知识
set过程的图示
我们首先在env的build_phase()中执行如下set
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 ) );
在UVM中执行过程如下图示
以上执行过程描述如下:
- The jelly_bean_env calls the set() function with this (jb_env) as the cntxt.
- An entry of an associative array (m_rsc) is created using the cntxt object as the key, if this entry does not exist.
- The entry of the associative array points to a uvm_pool, which has another associative array (pool).
- 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.
- 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.
- 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”.
- 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.
- 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.
- The other array (ttab) uses the type of the uvm_resource as the key.
get
我们再看下在agent中的get的执行过程
uvm_config_db#( jelly_bean_agent_config )::get( .cntxt ( this ),
.inst_name ( "" ),
.field_name( "jb_agent_cfg" ),
.value ( jb_agent_cfg ) );
在UVM中执行过程如下图示
以上执行过程描述如下:
- Look up the name table of the uvm_resource_pool with the field_name as the key.
- If the name matched, get the handle of the uvm_queue.
- Traverse the entries of the queue.
- 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.
- The uvm_resource with the highest precedence is retrieved.
- The value of the resource is returned.
希望补充的上面引用的内容能够帮助大家更清晰地理解configure_db的set和get的过程。