读取寄存器值_UVM中的寄存器模型

本文介绍了UVM中寄存器模型的构建与使用,包括寄存器模型简介、前门访问与后门访问、简单的寄存器模型创建以及寄存器模型对DUT的模拟。通过UVM的寄存器模型,可以更方便地读取和写入寄存器值,简化验证流程。此外,文章还详细讲解了如何进行前门访问和后门访问,以及如何利用UVM内置的sequence进行寄存器功能检查。
摘要由CSDN通过智能技术生成

寄存器模型简介

1.

通常来说,DUT中会有一组控制端口,通过控制端口,可以配置DUT中的寄存器,DUT可以根据寄存器的值来改变其行为。这组控制端口就是寄存器配置总线。

在没有寄存器模型之前,只能启动sequence通过前门(FRONTDOOR)访问的方式来读取寄存器,局限较大,在scoreboard(或者其他component)中难以控制。而有了寄存器模型之后,scoreboard只与寄存器模型打交道,无论是发送读的指令还是获取读操作的返回值,都可以由寄存器模型完成。有了寄存器模型后,可以在任何耗费时间的phase中使用寄存器模型以前门访问或后门(BACKDOOR)访问的方式来读取寄存器的值,同时还能在某些不耗费时间的phase(如check_phase)中使用后门访问的方式来读取寄存器的值。

前门访问与后门访问是两种寄存器的访问方式。所谓前门访问,指的是通过模拟cpu在总线上发出读指令,进行读写操作。在这个过程中,仿真时间($time函数得到的时间)是一直往前走的。

UVM寄存器模型的本质就是重新定义了验证平台与DUT的寄存器接口,使验证人员更好地组织及配置寄存器,简化流程、减少工作量。

2.

  • uvm_reg_field:这是寄存器模型中的最小单位。

  • uvm_reg:它比uvm_reg_field高一个级别,但是依然是比较小的单位。一个寄存器中至少包含一个uvm_reg_field。

  • uvm_reg_block:它是一个比较大的单位,在其中可以加入许多的uvm_reg,也可以加入其他的uvm_reg_block。一个寄存器模型中至少包含一个uvm_reg_block。

  • uvm_reg_map:每个寄存器在加入寄存器模型时都有其地址,uvm_reg_map就是存储这些地址,并将其转换成可以访问的物理地址。

  • 当寄存器模型使用前门访问方式来实现读或写操作时,uvm_reg_map就会将地址转换成绝对地址,启动一个读或写的sequence,并将读或写的结果返回。在每个reg_block内部,至少有一个(通常也只有一个)uvm_reg_map。

简单的寄存器模型

1.

假设DUT非常简单,它只有一个寄存器invert。要为其建造寄存器模型,首先要从uvm_reg派生一个invert类:

 class reg_invert extends uvm_reg;    rand uvm_reg_field reg_data;    virtual function void build();        reg_data = uvm_reg_field::type_id::create("reg_data");        // parameter: parent, size, lsb_pos, access, volatile, reset value, has_reset, is_rand,        reg_data.configure(this, 1, 0, "RW", 1, 0, 1, 1, 0);    endfunction    `uvm_object_utils(reg_invert)    function new(input string name="reg_invert");        //parameter: name, size, has_coverage        super.new(name, 16, UVM_NO_COVERAGE);    endfunctionendclass

在new函数中,要将invert寄存器的宽度作为参数传递给super.new函数。这里的宽度并不是指这个寄存器的有效宽度,而是指这个寄存器中总共的位数。如对于一个16位的寄存器,其中可能只使用了8位,那么这里要填写的是16,而不是8。这个数字一般与系统总线的宽度一致。super.new中另外一个参数是是否要加入覆盖率的支持,这里选择UVM_NO_COVERAGE,即不支持。

每一个派生自uvm_reg的类都有一个build,这个build与uvm_component的build_phase并不一样,它不会自动执行,而需要手工调用,与build_phase相似的是所有的uvm_reg_field都在这里实例化。当reg_data实例化后,要调用data.configure函数来配置这个字段。

configure的第一个参数就是此域(uvm_reg_field)的父辈,也即此域位于哪个寄存器中,这里当然是填写this了。第二个参数是此域的宽度,由于DUT中invert的宽度为1,所以这里为1。

第三个参数是此域的最低位在整个寄存器中的位置,从0开始计数。假如一个寄存器如图所示,其低3位和高5位没有使用,其中只有一个字段,此字段的有效宽度为8位,那么在调用configure时,第二个参数就要填写8,第三个参数则要填写3,因为此reg_field是从第4位开始的。

428caaa08492766cf3dd5f4b79e88bf6.png

第四个参数表示此字段的存取方式。UVM共支持如下25种存取方式:1)RO:读写此域都无影响。2)RW:会尽量写入,读取时对此域无影响。3)RC:写入时无影响,读取时会清零。4)RS:写入时无影响,读取时会设置所有的位。5)WRC:尽量写入,读取时会清零。6)WRS:尽量写入,读取时会设置所有的位。

7)WC:写入时会清零,读取时无影响。8)WS:写入时会设置所有的位,读取时无影响。9)WSRC:写入时会设置所有的位,读取时会清零。10)WCRS:写入时会清零,读取时会设置所有的位。11)W1C:写1清零,写0时无影响,读取时无影响。12)W1S:写1设置所有的位,写0时无影响,读取时无影响。13)W1T:写1入时会翻转,写0时无影响,读取时无影响。14)W0C:写0清零,写1时无影响,读取时无影响。15)W0S:写0设置所有的位,写1时无影响,读取时无影响。16)W0T:写0入时会翻转,写1时无影响,读取时无影响。17)W1SRC:写1设置所有的位,写0时无影响,读清零。18)W1CRS:写1清零,写0时无影响,读设置所有位。

19)W0SRC:写0设置所有的位,写1时无影响,读清零。20)W0CRS:写0清零,写1时无影响,读设置所有位。21)WO:尽可能写入,读取时会出错。22)WOC:写入时清零,读取时出错。23)WOS:写入时设置所有位,读取时会出错。24)W1:在复位(reset)后,第一次会尽量写入,其他写入无影响,读取时无影响。25)WO1:在复位后,第一次会尽量写入,其他的写入无影响,读取时会出错。

第五个参数表示是否是易失的(volatile),这个参数一般不会使用。第六个参数表示此域上电复位后的默认值。第七个参数表示此域是否有复位,一般的寄存器或者寄存器的域都有上电复位值,因此这里一般也填写1。第八个参数表示这个域是否可以随机化。这主要用于对寄存器进行随机写测试,如果选择了0,那么此域将不会随机化,而一直是复位值,否则将会随机出一个数值来。这一个参数当且仅当第四个参数为RW、WRC、WRS、WO、W1、WO1时才有效。

第九个参数表示这个域是否可以单独存取。定义好此寄存器后,需要在一个由reg_block派生的类中将其实例化:

 class reg_model extends uvm_reg_block;    rand reg_invert invert;    virtual function void build();        default_map = create_map("default_map", 0, 2, UVM_BIG_ENDIAN, 0);        invert = reg_invert::type_id::create("invert", , get_full_name());        invert.configure(this, null, "");        invert.build();        default_map.add_reg(invert, 'h9, "RW");    endfunction`uvm_object_utils(reg_model)function new(input string name="reg_model");super.new(name, UVM_NO_COVERAGE);endfunctionendclass

uvm_reg派生的类一样,每一个由uvm_reg_block派生的类也要定义一个build函数,一般在此函数中实现所有寄存器的实例化。

一个uvm_reg_block中一定要对应一个uvm_reg_map,系统已经有一个声明好的default_map,只需要在build中将其实例化。这个实例化的过程并不是直接调用uvm_reg_map的new函数,而是通过调用uvm_reg_blockcreate_map来实现。create_map有众多的参数:

其中第一个参数是名字,第二个参数是基地址,第三个参数则是系统总线的宽度,这里的单位是byte而不是bit,第四个参数是大小端,最后一个参数表示是否能够按照byte寻址。

随后实例化invert并调用invert.configure函数。这个函数的主要功能是指定寄存器进行后门访问操作时的路径。其第一个参数是此寄存器所在uvm_reg_block的指针,这里填写this,第二个参数是reg_file的指针,这里暂时填写null,第三个参数是此寄存器的后门访问路径,这里暂且为空。当调用完configure时,需要手动调用invert的build函数,将invert中的域实例化。

最后一步则是将此寄存器加入default_map中。uvm_reg_map的作用是存储所有寄存器的地址,因此必须将实例化的寄存器加入default_map中,否则无法进行前门访问操作。

add_reg函数的第一个参数是要加入的寄存器,第二个参数是寄存器的地址,这里是16’h9,第三个参数是此寄存器的存取方式。

到此为止,一个简单的寄存器模型已经完成。下面总结一下:

  • uvm_reg_field是最小的单位,是具体存储寄存器数值的变量,可以直接用这个类。

  • uvm_reg则是一个“空壳子”,或者用专业名词来说,它是一个纯虚类,因此是不能直接使用的,必须由其派生一个新类,在这个新类中至少加入一个uvm_reg_field,然后这个新类才可以使用。

  • uvm_reg_block则是用于组织大量uvm_reg的一个大容器。

打个比方说,uvm_reg是一个小瓶子,其中必须装上药丸(uvm_reg_field)才有意义,这个装药丸的过程就是定义派生类的过程,而uvm_reg_block则是一个大箱子,它中可以放许多小瓶子(uvm_reg),也可以放其他稍微小一点的箱子(uvm_reg_block)。整个寄存器模型就是一个大箱子(uvm_reg_block)。

2.

寄存器模型的前门访问操作可以分成读和写两种。无论是读或写,寄存器模型都会通过sequence产生一个uvm_reg_bus_op的变量

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值