UVM基础-config_db的高级应用

      组件之间点对点的寄信和收信,是config_db最基础的用法,而在真正实战的过程中,一般要多层次之间相互寄信和收信,甚至一个组件在不同时刻对同一个组件的一个变量要寄不同值的信。或者说不同的组件要给同一个组件传输变量等。

1 跨层次多重寄信

       想象一个场景,如果在tc中要对driver的drv_pkt_num寄信,而env中也要对driver的drv_pkt_num寄信,例如如下所示代码:

tc中的build_phase:

1.	function void my_env::build_phase(uvm_phase phase);
2.	      super.build_phase(phase);
3.	      config_db#(int)::set(this, "i_agt.drv", "drv_pkt_num", 1);
4.	......
5.	endfunction:build_phase

      此时driver中get的是env的寄信还是tc的寄信?其实config_db在收信的过程是存在优先级的,一般越靠近uvm_top的组件,优先级越高,因此driver中获取的drv_pkt_num值为tc中的100;

       那如果将代码改一下,变为:

tc中:

1.	function void my_tc::build_phase(uvm_phase phase);
2.	      super.build_phase(phase);
3.	      config_db#(int)::set(null, "uvm_test_top.env.i_agt.drv", "drv_pkt_num", 100);
4.	......
5.	endfunction:build_phase

env中:

1.	function void my_env::build_phase(uvm_phase phase);
2.	      super.build_phase(phase);
3.	      config_db#(int)::set(null, "uvm_test_top.env.i_agt.drv", "drv_pkt_num", 1);
4.	......
5.	endfunction:build_phase

此时寄信的组件第一个参数都变为了null,那么driver中如何收信?此时会认为是都从最顶层的uvm_top寄信,具备同等优先级,此时就会变为时间优先,那么drv_pkt_num将会变成后寄信的组件所寄出的值,因为build_phase是自顶向下运行,因此会先运行tc的build_phase,在运行env的build_phase,因此从时间上讲,后寄信的值为1,此时drv_pkt_num为1。

总结一下:

  1. config_db set的第一个参数,决定了寄信方的顶层位置,如果是this,那就是当前组件,如果是null,那就是树形结构中最高层的uvm_top,或者写一个路径,那就是路径的点,比如this.m_parent,那就是组件的父节点寄信,一定程度上与在哪个组件调用config_db无关;
  2. 不同层级的寄信组件之间,收信方按照优先级收信,先收到高优先级组件的寄信,越贴近uvm树形结构顶层的组件,拥有越高的优先级
  3. 对于同一个等级的组件寄信,收信方按照时间的顺序,后寄出信的内容,为实际的收信值。

2 同层次多重寄信

       同层次的组件在多次寄信时,收信组件是按照时间的顺序收信,以后来寄信的值为准,其实在同一个组件中先后写两个config_db set,是没有任何意义的,这是纯粹的冗余代码,或者无效代码,那么什么样的场景适配呢?一个就是上述1的例子;另外一个,想象一个例子,在实战中常用的一个操作,就是将一些tc通用的方法,封装到base_tc中,或者通用的激励调用流程,并且会在base_tc中实例化env,而特定的tc只负责写特定测试点的用例,那么如果有100条用例要发的包的数量为drv_pkt_num=100,那么此时有一条特殊的用例要发80个包,一般会在base_tc的build_phase中寄信为100:

1.	function void base_tc::build_phase(uvm_phase phase);
2.	      super.build_phase(phase);
3.	      config_db#(int)::set(this, "env.i_agt.drv", "drv_pkt_num", 100);
4.	......
5.	endfunction:build_phase

在特殊的tc的build_phase中寄信为80:

1.	function void sp_tc::build_phase(uvm_phase phase);
2.	      super.build_phase(phase);
3.	      config_db#(int)::set(this, "env.i_agt.drv", "drv_pkt_num", 80);
4.	......
5.	endfunction:build_phase

此时driver收到的为80,为什么呢,base_tc和sp_tc实际上是同层级的组件,那么在sp_tc的build_phase中调用super.build_phase,会先执行base_tc的build_phase,在调用过super之后,再寄信,那么driver会先收到100,后收到80,同层级组件以后收到的信为主,因此收信为80。

总结:

  1. 在树形结构的同层级组件向同一个组件寄信,以后寄出的信为最后收到的值
  2. 树形结构是在类中例化了哪些其他类,继承类不能作为树形结构的节点。

3 多层次收信

       其实config_db最强大的用法,不是跨层次的寄信收信,或者说同层次的多次寄信收信,而是不同组件之间对同一个变量的收信,以这种方式实现变量在整个验证环境的“共享”。

       想象一个场景,scoreboard想要实现数据包的比对,本身也需要获取driver到底发了多少个pkt,那么scoreboard中也想获取driver中的drv_pkt_num变量。而scoreboard和driver本身属于一种“叔侄”关系,而建立在config_db中路径的相关机制,可以实现,例如如下代码:

在tc中:

1.	function void base_tc::build_phase(uvm_phase phase);
2.	      super.build_phase(phase);
3.	      config_db#(int)::set(this, "env.i_agt.drv", "drv_pkt_num", 100);
4.	......
5.	endfunction:build_phase

此时TC中只需要将信寄给driver,无需寄给scoreboard,即可实现信的“共享”。

在driver中:

1.	function void my_driver::build_phase(uvm_phase phase);
2.	      super.build_phase(phase);
3.	      config_db#(int)::get(this, "", "drv_pkt_num", drv_pkt_num);
4.	......
5.	endfunction:build_phase

在scoreboard中:

1.	function void my_scoreboard::build_phase(uvm_phase phase);
2.	      super.build_phase(phase);
3.	      config_db#(int)::get(this.m_parent, "i_agt.drv", "drv_pkt_num", drv_pkt_num);
4.	......
5.	endfunction:build_phase

值得注意的是,scoreboard中get的前两个参数,要写明指向driver的路径,这里scoreboard与driver的“叔侄”关系本身无法直接获取到对方的变量,因此要借助env,第一个参数指向scoreboard的父节点,也就是env,第二个参数相对与env指向driver,就可以获取到driver的变量;此过程第一个参数也可以写为null,那么第二个参数就要写成:uvm_test_top.env.i_agt.drv。

实际上,这里有一个“深坑”,这种变量环境“共享”的使用,很依赖组件的build关系,也就是uvm树形结构建立的顺序,总不能driver中还没有build完成,也就还没有收到drv_pkt_num,就从scoreboard的build_phase中获取driver的这个变量,这样是获取不到的。怎么解决,一种方式是,要刻意设置组件的实例化名:

  1. 明白build_phase的执行顺序,build_phase是自顶向下执行,那么同层次的build_phase怎么执行?其实是按照实例化名字的字典序执行,也就是create函数的第一个字符串参数,参数的首字母字典序越靠前,先执行,也就是说,i_agt和scoreboard哪个先执行,取决于在env的build_phase中实例化i_agt和scoreboard的create的传参,此例中先执行i_agt的build_phase。
  2. 而scoreboard的build_phase和driver的哪个限制性呢?uvm对树的遍历,采用深度优先序,先执行完i_agt的build_phase,会先执行i_agt的子节点的build_phase,全部执行完之后,在执行scoreboard的,因此此例中,scoreboard中get driver的变量可行。

第二种方式,更为简单,就是在确保driver执行过get后,再执行scoreboard的get,即将scoreboard的get放置于build_phase之后的phase执行。

4 config_db的“坑”与调试

       使用config_db的过程中,其实“坑”还不少,坑多的原因是,config_db在设置错路径的时候,并不会报错,因为uvm允许给不存在的组件寄信,也不会提示错误,这样就给调试带来了很大难度,考虑到这一点,uvm提供了三种config_db的调试方法:

  1. 调用check_config_usage(),调用时候应该要注意,这个函数要在组件执行完寄信收信再调用,如果寄信收信发生在build_phase,那么要在build_phase之后的phase调用这个函数,最后会在log中看到那些组件的那些变量被read或者被write。
  2. 调用print_config(1),调用的注意项与1相同,同样可以看到变量是否被read和write。
  3. 在仿真器命令行输入:+UVM_CONFIG_DB_TRACE,同样可以看到log打印效果。

更加推荐使用命令行的方式,方便快捷。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值