UVM lab5 学习笔记

学习目标:掌握写寄存器模型,同时将其集成到验证环境中

目录

1. 写寄存器模型

1.1寄存器模型中的小单元reg的实现

1.2 寄存器模型register model(rgm)的实现

1.3 adapter的实现

2. 将写的寄存器模型集成到环境中

3. 将通过总线发送sequence的方式修改为通过寄存器模型来发送

4. 使用寄存器模型自带的sequence完成寄存器的常规验证

疑难点:

1.adapter和rgm是uvm_object类型,为什么用和predictor一样的创建方法呢?predictor是component!

补充内容:

1.uvm_reg的configure方法(上)和uvm_reg_field的configure方法(下)是不一样的

 2. uvm_status_e​编辑

 3.model


1. 写寄存器模型

        一个完整的寄存器模型包括寄存器,寄存器块,适配器,预测器四个部分,我们可以单独写一个package来包含前三部分,他们三个分别继承于UVM包中的uvm_reg, uvm_reg_block, uvm_reg_adapter。注意这三个都是object,并不在uvm的component树里面;

拿MCDF举例,分别定义了

控制寄存器ctrl_reg和状态寄存器stat_reg继承于寄存器uvm_reg;

寄存器模型mcdf_rgm继承于寄存器块uvm_reg_block;

适配器reg2mcdf_adapter继承于uvm_reg_adapter;

1.1寄存器模型中的小单元reg的实现

在reg中,我们需要完成对域的划分和覆盖率采集控制。即我们需要将一个寄存器32bit位划分成不同的域,从而代表不同的功能,通过uvm_reg_field(继承于uvm_object的一个类)来声明不同的域,在MCDF中将控制寄存器的域划分如下        

    

 随后我们需要在build函数(uvm_reg自带)里面对我们声明的域完成创建(UVM常规的create创建方法),随后通过configure(uvm_reg自带)将不同bit位分配给不同的域,自此,我们完成了写reg中最重要的域的划分。

 第二个重要的部分是覆盖率收集,uvm自带的sample函数会在我们通过寄存器模型对寄存器进行读写的时候自动调用,也就是自动收集覆盖率。我们需要定义覆盖率,随后定义sample函数;定义一个覆盖组,通过coverpoint来定义不同的覆盖点;

 随后在new函数中设置一个开关,has_coveragre,可以选择是否打开覆盖率测量;

 reg的最后定义了sample;

 状态寄存器的定义大同小异,不再赘述。

1.2 寄存器模型register model(rgm)的实现

我们如果已经定义了各式各样的寄存器reg,那么我们需要一个统一的载体来存放它们,同时还需要一个能标识其位置的类uvm_reg_map(继承于uvm_object的一个类),有了map,我们就能在rgm中存放各种寄存器,同时还能方便的寻找其位置了;

 我们在同样的函数build中对其进行构建,将各个reg在其中创建,随后通过上层configure设置当前reg挂载在this寄存器模型上(注意这里是reg寄存器的configure方法,不是uvm_reg_field的configure,具体可以看文章底部的注意)以及调用下层reg的build完成配置;

 随后就是map的实现,通过create_map(uvm_reg_block中自带的一个函数,以及后面的add_reg也是寄存器块自带的,都是UVM为了方便在寄存器块中构建寄存器地图而提前预设好的),至此我们已经完成了将寄存器放置在寄存器块中,同时画好了地图方便我们搜寻。我们可以通过地图找到寄存器模型中对应的寄存器通过前门访问寄存器了。

 

 最后为了实现后门访问,我们还需要添加一个快捷路径来实现,具体代码如下,它们是通过uvm_reg自带的函数add_hdl_path_slice和uvm_reg_block自带的函数add_hdl_path来实现的。

1.3 adapter的实现

我们通过寄存器模型输出的数据并不能直接发送到总线上,因为数据类型不同(总线事务根据不同的总线协议有所不同,而reg trans却是固定的),因此我们需要在寄存器模型和总线之间引入一个适配器来完成数据类型的转化,总之,总线和寄存器模型之间要进行数据传输,必须通过adapter进行转化,因此adapter中主要就是实现了两个函数,reg2bus和bus2reg;

至此,我们将寄存器模型建立完毕了,向下完成了寄存器的创建,域的划分和覆盖组的定义,随后将寄存器添加到寄存器块中,画了一张地图用于寄存器的寻址。向上我们定义了适配器,用于实现数据传输到总线上时的数据类型转化。

2. 将写的寄存器模型集成到环境中

在环境env中,我们将寄存器块rgm,适配器adapter例化,同时我们为寄存器模型补上了最后一块拼图predictor

 随后我们在build_phase里面将寄存器模型的三个部分创建;

然后在connect_phase里面将adapter挂接在sequencer上;然后将寄存器monitor上接收事务的端口与bus_in(负责接收来自总线端口上的数据)相连,将predictor的map和adapter与对应的部分相连,也可以说是将句柄相连,这样可以在predictor中访问adapter和map的数据成员;

 至此,我们在env中完成了寄存器模块三个部分的声明,创建和连接。连接最重要的是predictor与两端的连接,一端是寄存器模型端,即将map和adapter的句柄都传递到predictor中(map和adapter都需要提前一起绑定在sqr上),另一端是bus总线端,通过ana_port来实现。最后,rgm的句柄传递到virt_sqr中,便于顶层控制。

3. 将通过总线发送sequence的方式修改为通过寄存器模型来发送

如果要在sequence中用寄存器模型来发送事务,那么需要sequence中声明rgm,同时在sequence的body中实现与sqr挂接;

 然后我们就可以在do_reg这个专门发送寄存器事务的任务中去实现从寄存器模型端的寄存器访问,通过rgm.reg.write()/read();注意status表示寄存器当前的状态(具体见文末注意);

期望值是我们希望DUT中寄存器实现的值,镜像值用于最大可能的与实际值同步。上述读写的wr_val和rd_val就是我们期望的值,通过总线可以使得寄存器的实际值变为我们期望的值。但如果我们想对单一的域进行修改的话,也可以尝试另一种方法,通过set和update。

就是我们将期望值通过set设置为1,然后调用update,UVM会检查期望值和镜像值是否相等,不等则会像DUT中输入1,并且更新镜像值(在当前的lab中用了该两种方法,都需要学习);

如果我们完成了reg.set();和reg.update();也可以再做一个检查,通过后门做reg.mirror();,这样会读回硬件值,同时更新镜像值。当然我们前面已经通过前门update更新了,再做一个后门的检查就更加完善了;

4. 使用寄存器模型自带的sequence完成寄存器的常规验证

这个小实验,只需要我们在virtual sequence或hierarchical sequence内部的body();下例化创建对应寄存器模型自带的seq就行; 

随后完成seq与寄存器模型的绑定(通过model指定该seq应用的rgm是谁,具体可看文末注意部分),然后调用start即可;

疑难点:

1.adapter和rgm是uvm_object类型,为什么用和predictor一样的创建方法呢?predictor是component!

其实创建object时的this可加可不加;

补充内容:

1.uvm_reg的configure方法(上)和uvm_reg_field的configure方法(下)是不一样的

 2. uvm_status_e

 3.model

猜你喜欢

转载自blog.csdn.net/weixin_55225128/article/details/126983068
今日推荐