前言
该内容与我还没有写的<uvm field机制以及UVM树>紧密相关,你可以先去看看别人写的内容理解一下
假设已经建立了一个UVM的体系,UVM层次结构如下图所示:
一.config机制
1.1机制概要
可以把config_db当成一种邮箱功能,既有寄信方,又有收信方。送到哪的信,则必须去哪里取,所以收发信件的格式如下所示。
寄信方的邮件格式:
uvm_config_db#(邮件类别)::set(发信地址,“相对于发信地址的收件地址”,“邮件名字”,邮件内容);
收信方的邮件格式:
uvm_config_db#(邮件类别)::get(A,“B”,“邮件名字”,邮件内容);
其中AB合起来为收件地址
现在针对函数内容逐一进行讲解。这里先提一遍,后面会着重强调,无论是set还是get,在第二个括号内的前两个内容,都必须是component类型的,也就是注册在上面层次结构中的class。不能是object类型的,包括sequence。在后面会对他在运用中进行解释。
1.2 邮件类别,邮件名字和邮件内容
邮件类别分为两种,一种是sv自带的参数类别,包括bit,int,integer等基础参数类型,一种是由uvm_object继承而来的类和其他类,包括interface,transaction等。不同的邮件类别,发送的内容是不同的。
1.基础参数类型
基础参数类型发送的邮件内容可以是个变量也可以是定值,定值就发送这个值,变量就发送在set时该变量的值。
无论后续该参数如何改变,信件内容将不会发生任何改变,取邮件的class将获得的是一个定值。如果后续参数将发生改变,而需要获得的是实时的值,需要在循环块每次发生改变后设置set,然后再在get中获得该值,也就是进行重复调用set和get函数。
2.类
SV是一种面对对象的语言,也就引入了类的概念,类在定义的时候是虚的,只有在实例化之后才会为这个类分配mem。信件类别如果是类的话,信件内容将不会是当前固定的值,而是一个指针,一个地址,当你去取信件的时候,获得地址,然后再去地址里面取数据,这个时候,取到的数据将是实时更新的值。因为这个实例化之后的类,里面所有的值改变之后都会存在这个地址里面,你取到的就是最新的值,只需要设置一次就可以一劳永逸。
这里简要说明一下设置时uvm_object和interface的区别,以及信件名字和信件内容该如何写:
1.uvm_object:
①.类的名字
是定义时取的类名字。是写在文件第一行的文字,比如说:
my_tr.sv : class my_transaction extends uvm_sequence_item;
所有的类都需要实例化,所以在这个类里面会包含一个实例化的函数:
function new(string name = “my_tr”);
这里的my_transaction是类的名字,后面的my_tr是实例化的参数,一个字符串,在object类型中,一般来说用不到。只有在component中需要形成层次结构时,才需要用到。但是一般建议写上,任何字符串都是可以的。
②.调用的名字
drv.sv : my_transaction tr;
tr = new(“A”);
此时A只是一个覆盖my_tr的字符串,也是没有任何意义的。也可以不写,写成 tr = new(); 这时候tr就实例化完成了。在config db中需要使用my_transaction作为类,而你真正调用的tr作为内容
uvm_config_db#(my_transaction)::set(null,“uvm_test_top.drv”,“tr”,tr);
interface
interface和transaction或者其他的类不同,是一个在UVM系统之外的类,在调用的时候不需要你再次进行实例化,所以也不能直接调用,需要加virtual,也就是在整个uvm系统中,只要用到interface,都需要加virtual interface进行调用。
if.sv : interface my_if();
dri.sv : virtual my_if vif;
这个时候vif就是一个指向my_if的指针,而在写config db的时候,需要这样调用:
uvm_config_db#(virtual my_if)::set(null,“uvm_test_top.drv”,“vif”,vif);
1.3 发信地址和优先级
在第一张图的在set函数中将第二个括号的第一个参数定义为发信地址,事实上是为了更好地理解优先级。
首先,这个函数应该在变量定义的地方编写,但是发信的地方可以是任何地方。我们可以这么理解,有一个int类型的A值,值是在mdl中定义的,那么mdl就应该是写函数的地方(就是写config_db函数的地点),但是信件可以让env来发送(由config_db函数来定义)。那这个时候,这个函数就应该长这样:
mdl.sv:uvm_config_db#(int)::set(this.m_parent,“scb”,“A”,5);
假如在不同的地方都填写了一个寄到同一个目的地址的信,最后这个信件的内容是由谁来决定的呢?
信件的内容就由发信方在层次结构中的等级决定,等级越高,谁的信将会被真正地送到。
比如你在mdl和scb都写了函数,但是mdl的信件是由env发送的,i_agt的信件是由自己发送的
mdl.sv :uvm_config_db#(int)::set(this.m_parent,“i_agt.sqr",“A”,a);
i_agt.sv:uvm_config_db#(int)::set(this,“sqr",“A”,a);
虽然他们发送的是同样的地方,但是只有mdl的信件会真正地送到,就算后面i_agt的信送到了,也将不会进入信箱。当然,假如i_agt在1时刻发送,sqr在2时刻接受,而mdl在3时刻发送,那收到的将会是i_agt的信,毕竟就算等级高,来的迟也没用。但是如果i_agt在1时刻发送,而mdl在2时刻发送,sqr在3时刻接收,因为env的等级更高一些,信件的内容也将是由mdl来决定的。
又假如是由同一个地方发出呢?
mdl.sv :uvm_config_db#(int)::set(this.m_parent,“i_agt.sqr",“A”,a);
i_agt.sv:uvm_config_db#(int)::set(this.m_parent,“i_agt.sqr",“A”,a);
那么他们两个都可以发送这个信件,谁来的迟就会丢掉前面的信件,所以在sqr中取到的将是最新的值。
1.4 相对地址和收件地址
假如你学习过uvm的factory机制,将会知道,uvm的component类继承而来的类在实例化之后,会在工厂中建立一个层次结构,如文章首图一样。建立完之后,结构中的每一员,都可以根据这个层次结构和实例化之后的名字,找到自己下面的所有结构。
所以,当你设置在i_agt发送信件的时候,就可以将下面的所有机构,或者自己,设置成收件地址。却不能将上层的任何机构设置成收件地址。
i_agt.sv : uvm_config_db#(int)::set(this,“sqr",“A”,a);
this就是该机构此时在层次结构中所处的位置。如果收件地址设置为自己,就可以把第二个字符串设置为空。
那如果需要向该地址的上层设置为收件地址呢,由于发信地址是可以随意设置的,就可以这么写:
i_agt.sv : uvm_config_db#(int)::set(uvm_root::get(),“uvm_test_top.env",“A”,a);
i_agt.sv : uvm_config_db#(int)::set(null,“uvm_test_top.env",“A”,a);
由于此时发件地址变成了最顶层,即uvm_test_top的上层,这个函数也将是优先级最高的,只会并列而不会被覆盖。
收件地址也是类似的,只要两个参数合并起来,set和get指向的是同一个地址,同时类型和信件名字保持一致,这样通讯就建立完成。
1.5 set和get的时间顺序问题
set必须要写在get之前,一般来说,这个函数都会写在build_phase中,build_phase的运行顺序是从层次结构高的到层次结构低的,假如有一个需要从sqr发送到env的数据,写在build_phase中是会报错的。那么就可以改变函数的位置,在env中,把函数改写到后面的任何一个phase都是可以的。
如果收信和发信都写在main_phase中,就要格外注意在时间上的先后顺序,否则将会报错。
这是没有发信但是收信了的情况。
如果发信了但是没有收信的,将不会报错。
1.6 通配符的使用
在config_db中的set中,是可以使用通配符的,通配符的使用很好理解,就是get设置了之后,如果遇到可以匹配的get将会自动匹配补全。在两种情况下使用的比较多。
1.一个发送方,但是有多个接收方(interface 的发送等)
tb_top.sv : uvm_config_db#(virtual my_if)::set(null,"uvm_test_top.",“vif”,vif);
drv.sv : uvm_config_db#(virtual my_if)::get(this," “,“vif”,vif);
scb.sv : uvm_config_db#(virtual my_if)::get(this,” ",“vif”,vif);*
这样取同样的名字,而在其他地方只要设置this就可以收到。
2.不确定具体实例化的名字(sequence的接收)
drv.sv : uvm_config_db#(int)::set(null,"uvm_test_top.",“A”,a);
seq.sv : uvm_config_db#(int)::get(null,get_full_name(),“A”,a);*
这样,不管你的seq.sv改成什么名字,都可以获得drv设置的值。这里有两个注意点,一个是一般来说seq的层次等级比较高,所以不管在哪里设置的set参数,最好第一个发信地址都写成null。第二个是,虽然在seq中可以获得full name,但seq不存在于整个层次结构,所以不能写成this,必须得写成上述形式。
当然,还有别的设置方式,将在UVM config_db详解(二)中举例对该文内容进行深入分析