UVM入门-lab5

lab5

知识点

  • 寄存器:是模块之间相互交流的窗口,硬件的各个功能可以通过由处理器配置功能以及访问状态,与处理器之间的通话是通过寄存器的读写来实现的。

    • 寄存器按照地址索引的关系是按字word(32bits)对齐的,寄存器有多个域,每个域的属性可以不相同,reserved域表示该域所包含的比特位暂时保留以作日后的功能扩展所用,对保留域的读写不起任何作用。
    • 一个寄存器可以由多个域构成,而多个域可以包含多个比特位,一个功能模块中的寄存器可以组团构成一个寄存器模型(register model),下图中包含了寄存器模块和属于验证环境的寄存器模型。两个模块包含的寄存器信息是高度一致的,属于验证环境的寄存器模型可以抽象出一个层次化的寄存器列表;对于功能验证而言,可以将总线访问寄存器的方式抽象成寄存器模型访问的方式,这种方式便于寄存器后期的地址修改或者域的添加,且不会对已有的激励产生影响,从而提高了测试序列的复用性。

    UVM入门-lab5_第1张图片

  • 中心化管理方式:采用XML、Excel或者DOC等格式来保存寄存器的描述,因为单一源的管理方式可以尽量降低分歧和错误的可能。

  • uvm_reg相关概念:

UVM入门-lab5_第2张图片

  • 寄存器建模的基本要点和顺序:

    • 1、在定义单个寄存器时,需要将寄存器的各个域整理出来,在创建之后还应当通过uvm_ reg_field::configure()函数来进一步配置各自属性;
    • 2、在定义uvm_reg_block时, 读者需要注意reg_block与uvm_mem、uvm_reg以及uvm_reg_map的包含关系。首先uvm_reg和uvm_mem分别对应着硬件中独立的寄存器或者存储,而一个uvm_reg_block可以用来模拟一个功能模块的寄存器模型,其中可以容纳多个uvm_ reg和uvm_ mem实例;其次map的作用一方面用来表示寄存器和存储对应的偏移地址,同时由于一个reg_ block可以包含多个map,各个map可以分别对应不同总线或者不同地址段。在reg_block中创建了各个uvm_ reg之后,需要调用uvm_reg:configure()去配置各个uvm_reg实例的属性。
    • 3、考虑到uvm_reg_map也会在uvm_reg_block中例化,在例化之后需要通过uvm_reg_map:add_reg()函数来添加各个uvm_reg对应的偏移地址和访问属性等。只有规定了这些属性,才可以在稍后的前门访问(frontdoor)中给出正确的地址。
    • 4、uvm_reg_block可以对更大的系统做寄存器建模,这意味着uvm_reg_block之间也可以存在层次关系,上层uvm_reg_block的uvm_reg_map可以添加子一级uvm_ reg_block的uvm_reg map,用来构建更全局的“版图”,继而通过uvm_reg_block与uvm_reg_map之间的层次关系来构建更系统的寄存器模型。
  • 寄存器模型访问方式:

    • 1、前门访问:通过寄存器配置总线SPI(如APB协议、OCP协议、I2C协议)来对DUT进行操作,前门访问操作只有两种方式:读和写操作。
    • 2、后门访问:是与前门访问相对的操作,从广义上讲所有不通过DUT的总线而对DUT内部的寄存器或者存储器进行存取的操作都是后门访问,利用UVM DPI(uvm_hdl_read()、uvm_hdl_deposit()),而不通过物理总线访问。

    UVM入门-lab5_第3张图片

    • mirrored value(镜像值) 和desired value(期望值)是寄存器模型的属性,而actual value对应着硬件的真实数值。期望值是先利用寄存器模型修改软件对象值,而后利用该值更新硬件值,镜像值表示当前硬件的已知状态值,镜像值由模型预测给出,在前门访问时通过观察总线或者在后门访问的时候通过自动预测来给出镜像值。
  • **预测的分类:**自动预测和显示预测,显示预测更为准确,自动预测需调用uvm_reg_map::set_auto_predict()。

    • **1、自动预测:**如果没有在环境中集成独立的predictor,而是利用寄存器的操作来自动的记录每一次寄存器的读写数值,然后再后台自动调用predict() 。
    • **2、显示预测:**在物理总线上通过监视器来捕捉总线事务,然后传递给外部例化的predictor,该predictor是被例化在顶层环境中。
  • uvm_reg_block、uvm_reg、uvm_reg_field提供的访问寄存器的方法:

UVM入门-lab5_第4张图片

  • mirror()方法与read()方法类似,也可以选择前门门访问后者后门访问,不同的是mirror()不会返回读回的数值,但是会将对应的镜像值修改。在修改镜像值之前,用户还可以选择是否将读回的值与模型中的原镜像值进行比较。

————————————————版权声明:本文为CSDN博主「Thomas-w」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/weixin_38967029/article/details/125613813

UVM入门-lab5_第5张图片

寄存器模型的完善和嵌入

  • 1、声明uvm_reg(这里分ctrl_reg和stat_reg)类,在内部声明与DUT寄存器域相对应的uvm_reg_field类。
    • 声明覆盖组,其中覆盖点为每个域的镜像值。
    • new函数中,开启寄存器域的覆盖率测量,并检查寄存器域是否有覆盖模型,若声明的寄存器域有覆盖率模型,便例化该覆盖率模型。
    • build函数中,对每个寄存器域进行实例创建,并对每个域进行参数配置(参数意义分别是:父类,起、止bit位,读写功能,默认值,是否已复位,是否可被随机化,是否在寄存器中占有一个字节的通道)。
    • sample函数,是寄存器的功能覆盖率测量方法,其中用寄存器域的功能覆盖率测量方法。在调用write()或者read()方法时,会自动调用covergroup::sample()来完成功能覆盖率收集
    • sample_values函数,是寄存器域的功能覆盖率测量方法,其中检查寄存器域是否开启(例化)覆盖率模型,若开启(例化)便开始采样。
  • 2、声明uvm_reg_block类,在内部声明3个ctrl_reg、3个stat_reg句柄和一个uvm_reg_map类型句柄map。
    • build函数中。对每个寄存器句柄实例化,并配置它们的父类(就是这个block:所以是this),和调用寄存器的bulid函数(reg可以对其中的域进行配置了)。
    • creat_map,创建map。参数分别是:map实例名称,基本地址,地图总线字节库宽度4byte——32位,相同的地址上最不显著的字节在先。
    • map.add_reg()函数,添加各个uvm_reg对应的偏于地址个访问属性,只有规定了这些,才能在之后的前门访问中给出正确的地址。
    • add_hdl_path(“tb.dut.ctrl_regs_inst”);添加实际寄存器hdl路径,为了后门访问。
    • 最后递归地锁定整个寄存器模型并建立地址映射。
  • 实验1.1、实现reg2mcdf_adapter的类的方法reg2bus和bus2reg
    • uvm_reg_adapter类(寄存器桥接)reg2mcdf_adapter。作用是:完成uvm_reg_bus_op(寄存器模型操作的transaction)和总线transaction(这里是reg_pkg的trans类型))之间的转换。
    • reg2bus函数(uvm_sequence_item类型):将寄存器模型操作的trans类型rw中的变量,映射到总线reg_trans类型t中。
    • bus2reg函数:$cast(t, bus_item)——先将将子类(reg_trans类)句柄t转换为父类(uvm_sequence_item类)句柄,然后将总线reg_trans类型t中的变量,映射寄存器模型操作的trans类型rw中。
  • 实验1.2、在mcdf_env中声明register_block、adapter和predictor,并且完成例化,在connect_phase中完成句柄的连接:
    • 分别声明寄存器模型(rgm)、adapter、显示预测predictor句柄。
    • build_phase中,对三者实例进行创建(creat),并调用寄存器模型build()函数(递归地对寄存器域进行配置)。
    • connect_phase中,
      • rgm.map.set_sequencer(reg_agt.sequencer, adapter);——设置与rgm.map对应的sqr和adapter,该方法必须在启动任何基于uvm_reg_sequence的序列之前被调用。
      • reg_agt.monitor.mon_ana_port.connect(predictor.bus_in);——reg_agent中mon的ap与predictor内部自带的bus_in端口连接。
      • 将map和adapter句柄传入predictor。当reg_agt.monitor一旦捕捉到有效trans,就会发送给predictor,然后predictor将trans交给adapter进行转换,转换后的寄存器模型有关信息更新到map中。

寄存器模型的使用

  • 实验2.1、请完成register block句柄的传递
    • 将register block句柄rgm传递进mcdf_base_virtual_sequence和mcdf_virtual_sequencer中。
  • 实验2.2、把mcdf_data_consistance_basic_virtual_sequence原有的由总线sequence实现的寄存器读写,改为寄存器模型操作的寄存器读写方式
    • uvm_reg中内置函数write(status, wr_val)——参数意义分别为:返回的寄存器操作状态(有三种:UVM_IS_OK 操作成功完成;UVM_NOT_OK 操作完成后出现错误;UVM_HAS_X 操作成功完成位有未知位)、uvm_reg_data_t 类型要写入的值。
    • read函数和write一样。
    • 空闲周期还是由总线seq实现,应该是uvm_reg中没有IDLE这个操作。
  • 实验2.3、将mcdf_full_random_virtual_sequence原有的由总线sequence实现的寄存器读写,改为由寄存器模型预先设置寄存器值,再统一做总线寄存器更新的方式,并且稍后由后门读取的方式取得寄存器的值,加以比较。
    • 先set()——通过前门访问,修改三个控制寄存器的期望值(不是硬件实际值)。
    • 再updata()——通过前门或后门访问,判断寄存器模型的期望值/镜像值是否与硬件实际值相等,不等则修改硬件寄存器的实际值。
    • 最后mirror(status, UVM_CHECK, UVM_BACKDOOR)——通过后门访问,读回硬件实际值,与镜像值进行比较。
    • 空闲周期还是由总线seq实现。

寄存器内建序列的应用

  • UVM中针对寄存器模型内建(built_in)一些sequence,这些自建的序列可以作为验证项目开始前的健康检查必选项,可省下不少工作。

  • mcdf_reg_builtin_virtual_sequence类中:

    • 1、声明三个寄存器模型内建序列并例化。

    • 2、等待复位信号,并等待其拉高。

    • 3、

      `uvm_info("BLTINSEQ", "register reset sequence started", UVM_LOW)
      rgm.reset(); 													//前门访问:复位该block的期望值和镜像值
      reg_rst_seq.model = rgm;							 //将寄存器模型句柄交给该sqr
      reg_rst_seq.start(p_sequencer.reg_sqr); //该seq挂载到reg_sqr上,并调用body函数执行该seq
      `uvm_info("BLTINSEQ", "register reset sequence finished", UVM_LOW)
      
    • 4、复位,等五个clk周期,复位信号拉高,开始下一个内建序列

      p_sequencer.intf.rstn <= 'b0;
      repeat(5) @(posedge p_sequencer.intf.clk);
      p_sequencer.intf.rstn <= 'b1;
      

你可能感兴趣的:(IC验证学习笔记,fpga开发,硬件工程)