UVM寄存器模型的使用

前门访问:指的是通过模拟cpu在总线上发出读指令,进行读写操作。在 这个过程中,仿真时间($time函数得到的时间)是一直往前走的。有了寄存器模型后,可以在任何耗费时间的phase中使用寄存器模型以前门访问或后门(BACKDOOR)访问的方式来读取寄存器的值。

后门访问:从广义上来说,所有不通过DUT的总线而对DUT内部的寄存器或者存储器进行存取的操 作都是后门访问操作。不通过总线进行读写操作,而是直接通过层次化的引用来改变寄存器的值。所有后门访问操作都是不消耗仿真时间(即$time打印的时间)而只消耗运行时间的。

在验证平台中离不开寄存器的使用,通过寄存器来控制数据行为。那么在验证平台中如何将寄存器的值传递给参考模型和duv呢?和正常的数据item通过intf有什么区别?为什么多了个寄存器模型?

通常来说,DUT中会有一组控制端口,通过控制端口,可以配置DUT中的寄存器, DUT可以根据寄存器的值来改变其行为。这组控制端口就是寄存器配置总线。可以通过外挂agent产生的item通过intf传递给duv。总线协议规定了读写操作等,由上图可以好理解写入改变寄存器的值。也可以如in_agent将控制数据传递给intf。那么DUT中寄存器的值应该和refm控制数据的值是保持一致的,都是由外部bus_agent产生写入的。

假如DUT中某个寄存器会根据RTL内部代码行为,不同的状态下改变自己的值,即这个值不是外部agent产生同时写入refm和DUV的,那么refm如何获取DUT中寄存器的值?只能先通过使用bus_driver向总线上发送读指令,并给出要读的寄存器地址来查看一个寄存器的值。要实现这个过程,需要启动一个sequence,这个sequence会发送一个transaction给bus_driver。所以第一个问题是如何在参考 模型的控制下来启动一个sequence以读取寄存器。第二个问题是,sequence读取的寄存器的值如何传递给参考模型。refm可以通过全局变量控制何时读寄存器,不使用全局变量则可以通过config_db结合事件搭配,启动seq读寄存器,同样利用config_db机制将读取到的寄存器值config_db给refm。过程相当麻烦。为了方便使用,UVM多了寄存器模型的功能。启动seq以及读取寄存器的值返回的事情都可以由寄存器模型自动完成。

寄存器模型基本概念,从大到小:

uvm_reg_block:寄存器块,里边可以包含很多uvm_reg和其它的uvm_reg_block,还会有uvm_refg_map。

uvm_reg:寄存器。

uvm_reg_field:寄存器域段,根据不同的bit代表不同的功能,reserved不算域段。为寄存器模型的最小单位。

uvm_reg_map:存储寄存器地址,并将其转换成可以访问的物理地址(因为加入寄存器模型中的寄存器地址一般都是偏移地址,而不是绝对地址)。当寄存器模型使用前门访问方式来实现读或 写操作时,uvm_reg_map就会将地址转换成绝对地址,启动一个读或写的sequence,并将读或写的结果返回。在每个reg_block内 部,至少有一个(通常也只有一个)uvm_reg_map。

建造寄存器模型的步骤:

1)从uvm_reg派生一个类,并在此类(寄存器)中声明寄存区域段。每一个派生自uvm_reg的类都有一个build,但和uvm_component中的build_phase不一样,并不会自动执行,只能手动调用。在寄存器的build中完成寄存器域段的实例化。在实例化后要调用configure函数配置这个域段。在new函数中,要将寄存器的宽度作为参数传递给super.new函数。这里的宽度并不是指这个寄存器的有效宽度,而是指 这个寄存器中总共的位数。build完成的是对寄存器域段的实例化,new函数完成的是寄存器的实例化,寄存器的注册宏用的是uvm_object_utils类型。

    class reg extends uvm_reg;
5
6         rand uvm_reg_field reg_data;
7
8      virtual function void build();
9         reg_data = uvm_reg_field::type_id::create("reg_data");
10         // parameter: parent, size, lsb_pos, access, volatile, reset value,         
            has_reset, is_rand, individually accessible
11         reg_data.configure(this, 1, 0, "RW", 1, 0, 1, 1, 0);
12     endfunction
13
14     `uvm_object_utils(reg)
15
16     function new(input string name="reg");
17         //parameter: name, size, has_coverage
18         super.new(name, 16, UVM_NO_COVERAGE);
19     endfunction
20  endclass

定义好此寄存器后,需要在一个由reg_block派生的类中将其实例化。

22 class reg_model extends uvm_reg_block;
23     rand reg reg1;
24
25     virtual function void build();
26         default_map = create_map("default_map", 0, 2, UVM_BIG_ENDIAN, 0);
27
28         reg1 = reg::type_id::create("reg", , get_full_name());
29         reg1.configure(this, null, "");
30         reg1.build();
31         default_map.add_reg(reg1, 'h9, "RW");
32     endfunction
33
34     `uvm_object_utils(reg_model)
35
36     function new(input string name="reg_model");
37         super.new(name, UVM_NO_COVERAGE);
38     endfunction
39
40 endclass

一个uvm_reg_block中一定要对应一个uvm_reg_map,系统已经有一个声明好的default_map,只需要在build中将其实例化。这 个实例化的过程并不是直接调用uvm_reg_map的new函数,而是通过调用uvm_reg_block的create_map来实现,create_map有众多的 参数,其中第一个参数是名字,第二个参数是基地址,第三个参数则是系统总线的宽度,这里的单位是byte而不是bit,第四个参 数是大小端,最后一个参数表示是否能够按照byte寻址。

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

随后实例化reg1并调用reg1.configure函数。这个函数的主要功能是指定寄存器进行后门访问操作时的路径。其第一个参数是此寄存器所在uvm_reg_block的指针,这里填写this,第二个参数是reg_file的指针这里暂时填 写null,第三个参数是此寄存器的后门访问路径,这里暂且为空。当调用完configure时,需要手动调用 reg1的build函数,将寄存器中的域实例化。 最后一步则是将此寄存器加入default_map中。uvm_reg_map的作用是存储所有寄存器的地址,因此必须将实例化的寄存器加 入default_map中,否则无法进行前门访问操作。add_reg函数的第一个参数是要加入的寄存器,第二个参数是寄存器的地址,这里 是16’h9,第三个参数是此寄存器的存取方式。 到此为止,一个简单的寄存器模型已经完成。

建立寄存器模型需要从uvm_reg_block类派生类并在此实例化寄存器,需要从uvm_reg派生寄存器类并在类中实例化寄存器域段。并在寄存器块uvm_reg_block的子类中添加地址,添加地址的方式是使用default_map。另外还要调用configure函数对寄存器和寄存器域段加以配置,如读写类型,宽度等。

建立好寄存器模型,还需要将寄存器模型集成到平台上。寄存器模型通过seq访问寄存器,seq会产生一个uvm_reg_bus_op的变量。此变量存储这操作类型、操作地址(写和操作数据)。此变量的信息要经过一个转换器转换成bus_seqr能接收的类型。

UVM提供了一个uvm_reg_adapter类,在其中定义了两个函数,一个是reg2bus,另外一个是bus2reg。寄存器模型通过seq产生的item经reg2bus函数后被bus_seqr接收进而发给bus_drv,bus_drv最终实现总线的读写操作。bus_drv在驱动总线进行读操作时能够顺便获取总线的值再返回给adapter,经bus2reg返回给寄存器模型能够接收的值。通过寄存器模型传递给refm。

寄存器模型可以在所需的位置声明例化,比如env或testbase。此外还需要声明adapter。

声明后需要将其实例化,并调用对象的build函数将其中的寄存器实例化。

还要调用lock_model函数,调用此函数后,reg_model中就不能再加入 新的寄存器了。

第四是调用reset函数,如果不调用此函数,那么reg_model中所有寄存器的值都是0,调用此函数后,所有寄存器 的值都将变为设置的复位值。

寄存器模型的前门访问操作最终都将由uvm_reg_map完成,因此在connect_phase中,需要将转换器和bus_sequencer通过 set_sequencer函数告知reg_model的default_map,并将default_map设置为自动预测状态。寄存器模型在添加寄存器时有设置初始复位值,reg_cfg中也会有reset及相应的初始复位值,在用例中设置reg_cfg中变量的名,reg_seq中获取reg_cfg后,将相应的变量set给寄存器,如若在寄存器模型中寄存器的默认值设置错误,比如寄存器a默认值为1,寄存器模型中设置错误为0,此时再在用例中设置reg_cfg.a=0,在reg_seq中set后就会set为0,此时期望值和镜像值就为一致,不会进行update操作。所谓寄存器模型auto_predict都是平台中寄存器模型的值,不会真正去rtl中读取。镜像值初始也就是寄存器模型设置的默认值。

在用例build_phase中set reg_cfg给reg_seq后,再在connect_phase或configure_phase中把reg_cfg中变量的值改变,reg_seq获取的是改变后的值,虽然reg_cfg已经发送过去,但reg_seq是在main_phase后才使用,因此虽然已经发送过reg_cfg句柄,但仍可以在main_phase前改变reg_cfg的变量的值。

在其它component中使用寄存器模型需要把寄存器模型传递过去,类似于virtual_seqr和seqr之间的关系。寄存器模型提供了read和write函数,可以通过mo_reg_model.mo_reg.mo_reg_field.read/write的方式进行读取或写入。

本文章参考《UVM实战》、《芯片验证漫游指南》等资料整理而成,仅作学习心得交流,如果涉及侵权烦请请告知,我将第一时间处理。

  • 6
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
UVM(Universal Verification Methodology)寄存器模型是一用于验证芯片寄存器功能的标准方法。它提供了一个统一的、可重用的框架,用于建立和管理寄存器模型,以及执行寄存器访问和验证。 UVM寄存器模型的主要组成部分包括寄存器模型寄存器层次结构、寄存器操作和寄存器验证环境。 1. 寄存器模型UVM寄存器模型是一个抽象的表示,用于描述芯片内部的寄存器寄存器字段。它提供了一种结构化的方式来定义寄存器的属性、寄存器字段的位宽和访问权限等。 2. 寄存器层次结构:UVM寄存器模型支持多层级的寄存器结构,可以通过层级关系来描述芯片内部的寄存器模块和子模块。这样可以更好地组织和管理寄存器模型,并提供寄存器之间的相互作用和访问。 3. 寄存器操作:UVM提供了一系列的API,用于执行寄存器读写操作。通过这些API,可以向寄存器模型发送读写请求,并获取响应。同时,还可以对寄存器的访问进行配置和控制,如重置、写入默认值等。 4. 寄存器验证环境:UVM寄存器模型可以与其他验证环境进行集成,以验证寄存器功能的正确性。通过使用事务级建模(TLM)接口,可以将寄存器操作与其他验证组件进行交互,并进行功能验证、覆盖率分析和错误注入等。 总之,UVM寄存器模型提供了一种规范化的方法来描述和验证芯片寄存器功能。它具有可重用性、灵活性和扩展性,并能与其他验证组件进行集成,从而提高验证效率和可靠性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值