ryanhunter的个人空间 https://blog.eetop.cn/1765812 [收藏] [复制] [分享] [RSS]

空间首页 动态 记录 日志 相册 主题 分享 留言板 个人资料

日志

UVM REG Model入门

热度 1已有 2622 次阅读| 2020-11-5 22:44 |个人分类:验证|系统分类:芯片设计| UVM

1.          UVM REG Model入门

本章通过一个非常小的示例来解释UVM REG中一些常用操作的实现。

1.1.               示例

            假设某个功能模块中包含如下寄存器,且寄存器通过APB接口进行配置。

1.1.1.     status

状态寄存器,只读,Offset: 0h, Width: 32bits

Field

access

width

reset

comment

Reserved

RO

[31:4]

0

保留位

Error

RO

[3:1]

0

每一位分别表示一种错误类型

Done

RO

[0]

0

完成标志位

 

1.1.2.     threshold

阈值寄存器,可读可写,Offset: 04h, Width: 32bits

Field

access

width

reset

comment

Reserved

RO

[31:16]

0

保留位

THD

RW

[15:0]

11h

阈值位

 

1.2.               建立寄存器模型

            针对上述设计,需要建立一个相应的寄存器模型, 以下为一种实现方法,注:实现方法不唯一,仅供参考。

            首先我们需要针对不同的寄存器去定义一些新的寄存器类,这些类都是继承自uvm_reg,包含了所有reg操作的一些信息

 

1.2.1.     status_reg

            首先定义一个status_reg类,继承自uvm_reg,该类就表示了status寄存器。

class status_reg extends uvm_reg; endclass

            定义了一个reg之后需要定义其中的reserveerrordone三个field(保留位在一般情况下不需要定义一个额外的field,这里将所有的bit都填充进reg中)。

uvm_reg_field rsv_field

uvm_reg_field err_field

uvm_reg_field done_field

            接下来就需要实例化这些reg_field,实例化的方式有很多,可以在status_reg中自定义一个类似build的函数用于实例化,这里将直接实例化在new函数中,实例化reg_field之后还需要对reg_field进行configure,用于指定一些初始信息,这里也都放在regnew函数中。接下来定义new函数:

function new (name string="",int unsigned n_bits,int has_coverage);

            super.new(name,n_bits,has_coverage);

            rsv_field=new("rsv_field");

            err_field=new("err_field");

            done_field=new("done_field");

            done_field.configure(this,1,0,"RO",0,0,1,0,0);

            err_field.configure(this,3,1,"RO",0,0,1,0,0);

            rsv_field.configure(this,28,4,"RO",0,0,1,0,0);

endfunction

            reg_fieldconfigure函数参数原型:

·      uvm_reg parent,表示该reg_field属于哪个寄存器对象

·      int unsigned size,指示reg_field的大小

·      int unsigned lsb_pos,指示reg_field的最低位在其parent reg中的索引

·      string access,访问类型,这里都是RO类型

·      bit volatilereg_field易失性,如果为1,每次调用fieldneeds_update都会返回需要update,另外默认的寄存器检查也会设置为NO_CHECK

·      uvm_reg_data_t reset,复位值,仅表示HARD类型的复位值

·      bit has_reset,如果为1,则表示默认HARD类型的reset有效,即有硬件复位,一般都选择设置为1

·      bit is_rand,表示reg_fieldvalue值是否可随机,一般设置为0即可

·      bit individually_accessible,是否可单独访问reg_field,通常用不到,一般选0

            另外提一下,uvm_reg中有一个add_field的函数,用于将当前reg和其包含的reg_field进行关联,不过我们不需要显示调用该函数,因为在reg_fieldconfigure中,只要指定了包含它的regUVM会自动调用这个add_field

            至此,一个status_reg寄存器类建立完成。threshold_reg类的建立与status_reg基本相同,区别在于他们的reg_field不太一样,所以配置时需要注意reset值和access类型。

1.2.2.     reg_block

            建立完所有的reg类之后,需要建立一个reg_block类,将一组reg包含进对应的reg_block中。reg_block继承自uvm_reg_block

class reg_block extends uvm_reg_block;

            然后声明之前定义的reg类。

status_reg status;

threshold_reg thres;

            然后例化这些reg,同样例化在reg_blocknew函数中。

function new (string name="",int has_coverage=UVM_NO_COVERAGE);

            super.new(name,has_coverage);

            status = new("status",32,has_coverage);

            thres = new("thres",32,has_coverage);

            status.configure(this);

            thres.configure(this);

endfunction

            uvm_regnew函数,带3个参数,除了namecoverage之外,中间的参数表示regsize

            uvm_regconfigure,也有3个参数,

·      uvm_reg_block blk_parent,表示包含regreg_block对象

·      uvm_reg_file regfile_parent=null,一个用于处理hdl_path的类,这里不指定,默认

·      string hdl_path=“”,用于指定hdl_path,后门的一种,这里不指定,默认

            当然,reg_block中的add_reg关联操作也会在regconfigure中被调用。至此,一个完整的reg_block创建完成,可以直接在testenv或者其他component中进行例化。

1.2.3.     uvm_reg_map

            关于uvm_reg_map的创建,方法也很多,第一种,在例化uvm_reg_blockcomponent中也例化uvm_reg_map,然后调用其configure函数,配置base address,大小端,关联的uvm_reg_block等。这里介绍另一种方法,uvm_reg_block自带了一个create_map函数,我们直接调用该函数,假设我们在uvm_test中定义了reg_block, regm。然后我们在build_phase中创建和配置regm

function void build_phase (uvm_phase phase);

            uvm_reg_map dmap;

            reg_adapter reg_adp = new("adp");

            super.build_phase(phase);

            regm = new("regm");

            regm.configure();

            regm.create_map("default_map",'h0,8,UVM_LITTLE_ENDIAN);

            dmap = regm.get_default_map();

            dmap.set_sequencer(m_env.apbm.m_seqr,reg_adp);

            regm.lock_model();

endfunction

            因为是最简单的寄存器模型,因此regmconfigure我们全部采用默认参数,即regm不再处于某个uvm_reg_block中,且没有后门操作。

            create_map,用于uvm_reg_block直接定义,创建并且配置一个uvm_reg_map对象,相关配置由本函数的参数提供:

·      string name,定义map的名字

·      uvm_reg_addr_t base_addr,基地址

·      int unsigned n_bytesmap包含的范围,以byte为单位

·      uvm_endianness_e endian,大小端

·      bit byte_address=1,按byte访问,默认为1

            由于我们在本reg_block中只定义了一个map,因此可以直接用get_default_map获取该map,然后对该map设置sequencerreg_adaptersequencer其实就是在环境中用于配置寄存器的driver对应的那个sequenceruvm_reg本质就是通过reg中的writereadAPI,将读写请求,数据等通过API启动sequence发送给driver,然后通过driver去驱动interface写往DUT

            reg_adapter其实就是基于uvm_reg_adapter定义的一个类,主要重载了bus2regreg2bus两个函数。

1.2.4.     reg_adapter

            定义reg_adapter

class reg_adapter extends uvm_reg_adapter;

            重载reg2bus,该函数主要是在发送write/read请求时,用于将reg item信息中关键的kinddataaddr信息转化到对应driver可识别的item,这里用了apb interface来配置DUT寄存器。

function uvm_sequence_item reg2bus (const ref uvm_reg_bus_op rw);

            apb_sequence_item busitem = new("busitem");

            if (rw.kind == UVM_WRITE) busitem.pwrite = 1; else busitem.pwrite = 0;

            busitem.addr = rw.addr;

            busitem.pwdata = rw.data;

            return busitem;

endfunction

            bus2reg则相反,本例中不使用provides_responses属性,即返回的rdata直接存在于apb_sequence_item中。

function void bus2reg (uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);

            apb_sequence_item apbitem;

            if ($cast(apbitem,bus_item))

                        `uvm_fatal("FATAL","bus item casting failed")

            if (apbitem.pwrite == 1) rw.kind = UVM_WRITE; else rw.kind = UVM_READ;

            rw.addr = apbitem.addr;

            rw.rdata = apbitem.prdata;

endfunction

            首先$cast用于将父类的uvm_sequence_item转化为我们需要的子类apb_sequence_item,然后将apbitem中的读写信息,数据信息转化为uvm_reg_bus_op中的相关信息。

1.3.               使用寄存器模型

            上一节最后在uvm_test中建立了寄存器模型regm。这一节以regm为例描述一下基本用法。

1.3.1.     reg.write

            如果要对一个寄存器进行写操作,例如修改threshold,为'h100,先定义一个uvm_status_e,用于保存返回的状态,是否成功。然后调用:regm.thres.write(st,'h100)。基本调用过程为uvm_reg.write -> uvm_reg.do_write -> uvm_reg_map.do_write -> uvm_reg_map.do_bus_write -> uvm_reg_map.do_bus_access -> uvm_reg_map.perform_accesses -> uvm_reg_adapter.reg2bus -> start_item(bus_req) -> apb_driver -> apb_interface -> DUT

            当然后面还会返回status等等,真是的UVM操作很复杂,但总体驱动流程就是这样。

1.3.2.     reg.read

            读操作与写类似,不过需要再定义一个uvm_reg_data_t dat用于接收读数据:regm.thres.read(t,dat);

            流程与write大致一样:uvm_reg.read -> uvm_reg.do_read -> uvm_reg_map.do_read -> uvm_reg_map.do_bus_read -> uvm_reg_map.do_bus_access -> uvm_reg_map.perform_accesses -> uvm_reg_adapter.reg2bus -> start_item(bus_req) -> apb_driver -> apb_interface -> DUT -> uvm_reg_adapter.bus2reg 最后返回到read,然后获取valuestatus信息。

1.3.3.     reg.predict

            regm.thres.predict('h100); regpredict本质就是依次去调用其中所有reg_fieldpredict函数,然后将reg predict参数中的寄存器值分别拆成对应fieldpredict,传给reg_field.predict。针对UVM_DIRECT_PREDICT,也是一般验证中常用的predict,无论regaccess为甚么类型,都能够直接修改valuem_desiredm_mirrored成为我们期望predict的值。predict函数相当于将寄存器的值改成用户期望的值,但不会立刻与DUT进行比较,只有在mirror,或者read操作的时候才会将DUT中读出来的值与相应的mirror或者desire值进行比较,如果不一致,则会报error(当然前提是用户打开了check开关)。另外关于reg_mapauto_predict,一旦打开auto_predict,则每次在调用write的时候UVM会自动进行一次predictread的时候一样会check。综上所述,predict是根据不同的predict类型将期望值提前设置到reg_field中,然后在每次操作mirror或者read时都会将当前的mirror或者desire值与读到的DUT值进行比较,mirror操作比较mirror值与DUT值,read操作比较desire值与DUT值。

1.3.4.     reg.update

            regm.thres.update(st); 先检查needs_update,如果mirror值和desire值不同,则根据不同的access类型,获取一个经过操作的desire值,一般来讲像RW这种access类型,就是直接获取一个desire值,然后将该值write到寄存器。

1.3.5.     reg.mirror

            regm.thres.mirror(st,UVM_CHECK); mirror操作就是从DUT读一个寄存器,然后与当前的mirror值做一个比较,可用于检查DUT是否与期望模型中的reg模型一致。

1

点赞

刚表态过的朋友 (1 人)

全部作者的其他最新日志

评论 (0 个评论)

facelist

您需要登录后才可以评论 登录 | 注册

  • 关注TA
  • 加好友
  • 联系TA
  • 0

    周排名
  • 0

    月排名
  • 0

    总排名
  • 0

    关注
  • 1

    粉丝
  • 0

    好友
  • 1

    获赞
  • 0

    评论
  • 90

    访问数
关闭

站长推荐 上一条 /1 下一条


小黑屋| 手机版| 关于我们| 联系我们| 隐私声明| EETOP 创芯网
( 京ICP备:10050787号 京公网安备:11010502037710 )

GMT+8, 2024-12-28 02:31 , Processed in 0.015080 second(s), 8 queries , Gzip On, Redis On.

eetop公众号 创芯大讲堂 创芯人才网
返回顶部