当前位置: 首页 > news >正文

【UVM】寄存器模型

寄存器模型的优势

  1. sequence复用性高,方便对 DUT 中寄存器进行读写;
  2. 提供了后门访问方式,可以不耗时的获取寄存器的值;
  3. 可以很方便的对寄存器的 coverage 验证点的收集

寄存器模型基本概念

寄存器模型概念作用
uvm_reg_field寄存器模型中的最小单位
uvm_reg比uvm_reg_field高一个级别,一个寄存器中至少包含一个uvm_reg_field
uvm_reg_block比uvm_reg高一个级别,可以加入许多的uvm_reg,也可以加入其他的uvm_reg_block。一个寄存器模
型中至少包含一个uvm_reg_block。
uvm_reg_map

存储寄存器的偏移地址,并将其转换成可以访问的物理地址

在每个reg_block内部,至少有一个(通常也只有一个)uvm_reg_map。

uvm_reg_file用于区分不同的hdl路径

简单的寄存器模型

实例

  • uvm_reg:
class my_reg extends uvm_reg;rand uvm_reg_field data;`uvm_object_utils(my_reg)virtual function void build();data = uvm_reg_field::type_id::create("data");//parameter:    parent, size, lsb_pos, access, volatile, reset value, has_reset, is_rand, individually accessibledata.configure(this,  16,  0,  "RW",  1,   0,   1,    1,    0);endfunctionfunction  new(input string name="my_reg");//parameter: name, size, has_coveragesuper.new(name, 16, UVM_NO_COVERAGE);endfunction
endclass
  • uvm_reg_block:
class my_regmodel extends uvm_reg_block;rand my_reg version;function void build();default_map = create_map("default_map", 0, 2, UVM_LITTLE_ENDIAN, 0);version = my_reg::type_id::create("version", , get_full_name());version.configure(this, null, "version");version.build();default_map.add_reg(version, 16'h47, "RW");endfunction`uvm_object_utils(my_regmodel)function new(input string name="my_regmodel");super.new(name, UVM_NO_COVERAGE);endfunction
endclass 

步骤总结

  1. 从uvm_reg派生一个寄存器类:
    1. 声明build函数,并在其实例化uvm_reg_field类;

      build函数负责所有uvm_reg_field的实例化

    2. 调用configure函数,配置上述域

      configure字段的含义:
      参数一,是此域的父辈,也就是此域位于哪个寄存器中,即是 this;
      参数二,是此域的宽度;
      参数三,是此域的最低位在整个寄存器的位置,从0开始计数;
      参数四,表示此字段的存取方式,共支持25种;
      参数五,表示是否是易失的(volatile),这个参数一般不会使用;
      参数六,表示此域上电复位后的默认值;
      参数七,表示此域时都有复位;
      参数八,表示这个域是否可以随机化;
      参数九,表示这个域是否可以单独存取

  2. 在一个由reg_block派生的类中将上述寄存器配置:
    1. 声明一个build函数

      build函数作用:实现所有寄存器的实例化

    2. default_map实例化;

      调用 create_map 函数完成 default_map 的实例化,default_map = create_map(“default_map”, 0, 2, UVM_LITTLE_ENDIAN, 0)
      第一个参数,表示名字;
      第二个参数,表示该 reg block 的基地址;
      第三个参数,表示寄存器所映射到的总线的宽度(单位是 byte,不是 bit );
      第四个参数,表示大小端模式;
      第五个参数,表示该寄存器能否按 byte 寻址。

    3. 调用configure函数;

      configure ( uvm_reg_block blk_parent, uvm_reg_file regfile_parent = null, string hdl_path = "" )
      第一个参数,表示所在 reg block 的指针;
      第二个参数,表示 reg_file 指针;
      第三个参数,表示寄存器后面访问路径 - string 类型

    4. 调用build函数将域实例化;
    5. 将此寄存器加入default_map

      default_map.add_reg (version, 16'h47, "RW") ;
      第一个参数,表示要添加的寄存器名;
      第二个参数,表示地址;
      第三个参数,表示寄存器的读写属性。

复杂的寄存器模型

 实例

class global_blk extends uvm_reg_block;...
endclassclass buf_blk extends uvm_reg_block;...
endclassclass mac_blk extends uvm_reg_block;...
endclassclass reg_model extends uvm_reg_block;rand global_blk gb_ins;rand buf_blk bb_ins;rand mac_blk mb_ins;virtual function void build();default_map = create_map("default_map", 0, 2, UVM_BIG_ENDIAN, 0);gb_ins = global_blk::type_id::create("gb_ins");gb_ins.configure(this, "");gb_ins.build();gb_ins.lock_model();default_map.add_submap(gb_ins.default_map, 16'h0);bb_ins = buf_blk::type_id::create("bb_ins");bb_ins.configure(this, "");bb_ins.build();bb_ins.lock_model();default_map.add_submap(bb_ins.default_map, 16'h1000);mb_ins = mac_blk::type_id::create("mb_ins");mb_ins.configure(this, "");mb_ins.build();mb_ins.lock_model();default_map.add_submap(mb_ins.default_map, 16'h2000);endfunction`uvm_object_utils(reg_model)function new(input string name="reg_model");super.new(name, UVM_NO_COVERAGE);endfunctionendclass

步骤总结

  •  第一步是先实例化子reg_block。

  • 第二步是调用子reg_block的configure函数(如果需要使用后门访问,则在这个函数中要说明子reg_block的路径,这个路径不是绝对路径,而是相对于父reg_block来说的路径)

  • 第三步是调用子reg_block的build函数。

  • 第四步是调用子reg_block的lock_model函数。

  • 第五步则是将子reg_block的default_map以子map的形式加入父reg_block的default_map中

寄存器模型中加入存储器

  • 存储器同样支持read/write/peek/poke进行读写,不过会额外多一个offset参数,表示存储器的哪个地址
/*从uvm_mem中派生存储器类,深度1024,宽度16*/
class my_memory extends uvm_mem;function new(string name="my_memory");super.new(name, 1024, 16);endfunction`uvm_object_utils(my_memory)
endclassclass reg_model extends uvm_reg_block;rand my_memory mm;virtual function void build();default_map = create_map("default_map", 0, 2, UVM_BIG_ENDIAN, 0);mm = my_memory::type_id::create("mm", , get_full_name());mm.configure(this, "stat_blk.ram1024x16_inst.array");default_map.add_mem(mm, 'h100);endfunction
endclass

寄存器模型集成到环境

寄存器模型转换到总线上操作

  •  adapter的作用:
    • reg2bus:其作用为将寄存器模型通过sequence发出的uvm_reg_bus_op型的变量转换成bus_sequencer能够接受的形式,
    • bus2reg:其作用为当监测到总线上有操作时,它将收集来的transaction转换成寄存器模型能够接受的形式,以便寄存器模型能够更新相应的寄存器的值
class adapter extends uvm_reg_adapter;string tID = get_type_name();`uvm_object_utils(my_adapter)function new(string name="my_adapter");super.new(name);endfunction : newfunction uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw);bus_transaction tr;tr = new("tr");tr.addr = rw.addr;tr.bus_op = (rw.kind == UVM_READ) BUS_RD: BUS_WR;if (tr.bus_op == BUS_WR)tr.wr_data = rw.data;return tr;endfunction : reg2busfunction void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);bus_transaction tr;if(!$cast(tr, bus_item)) begin`uvm_fatal(tID,"Provided bus_item is not of the correct type. Expecting bus_trans action")return;endrw.kind = (tr.bus_op == BUS_RD) UVM_READ : UVM_WRITE;rw.addr = tr.addr;rw.byte_en = 'h3;rw.data = (tr.bus_op == BUS_RD) tr.rd_data : tr.wr_data;rw.status = UVM_IS_OK;endfunction : bus2reg
endclass : adapter

在验证平台中使用寄存器模型

 实例

  • base_test:加入REG_MODEL
class base_test extends uvm_test;my_env env;my_vsqr v_sqr;reg_model rm;adapter reg_sqr_adapter;…endclassfunction void base_test::build_phase(uvm_phase phase);super.build_phase(phase);env = my_env::type_id::create("env", this);v_sqr = my_vsqr::type_id::create("v_sqr", this);rm = reg_model::type_id::create("rm", this);rm.configure(null, "");rm.build();rm.lock_model();rm.reset();rm.set_hdl_path_root("top_tb.my_dut");reg_sqr_adapter = new("reg_sqr_adapter");env.p_rm = this.rm;endfunctionfunction void base_test::connect_phase(uvm_phase phase);super.connect_phase(phase);v_sqr.p_my_sqr = env.i_agt.sqr;v_sqr.p_bus_sqr = env.bus_agt.sqr;v_sqr.p_rm = this.rm;rm.default_map.set_sequencer(env.bus_agt.sqr, reg_sqr_adapter);rm.default_map.set_auto_predict(1);endfunction

步骤总结

  1. 定义 reg_model和reg_sqr_adapter;
  2. 在build_phase中将其实例化
  3. 配置reg_model

    第一是调用configure函数,其第一个参数是parent block,由于是最顶层的reg_block,因此填写null,第二个参数是后门访问路径
    第二是调用build函数,将所有的寄存器实例化。
    第三是调用lock_model函数,调用此函数后,reg_model中就不能再加入新的寄存器了
    第四是调用reset函数,如果不调用此函数,那么reg_model中所有寄存器的值都是0,调用此函数后,所有寄存器的值都将变为设置的复位值

  4. 在connect_phase中,通过set_sequencer函数设置adapter的bus_sequencer,并将default_map设置为自动预测状态

寄存器模型中的访问

前门访问

定义

  • 前门访问操作就是通过寄存器配置总线(如APB协议、OCP协议、I2C协议等)来对DUT进行操作。前门访问操作只有两种:读操作和写操作。

方法函数

  • read
    extern virtual task read(output uvm_status_e            status,output uvm_reg_data_t          value,input uvm_path_e               path = UVM_DEFAULT_PATH,input uvm_reg_map              map = null,input uvm_sequence_base        parent = null,input int                      prior = -1,input uvm_object               extension = null,input string                   fname = "",input int                      lineno = 0);
  • write
    extern virtual task write(output uvm_status_e           status,input uvm_reg_data_t           value, input uvm_path_e               path = UVM_DEFAULT_PATH,input uvm_reg_map              map = null,input uvm_sequence_base        parent = null,input int                      prior = -1,input uvm_object               extension = null,input string                   fname = "",input int                      lineno = 0);
  • 参数说明:
    • 第一个是uvm_status_e型的变量,这是一个输出,用于表明操作是否成功;
    • 第二个是读/写的数值;
    • 第三个是读/写的方式,可选UVM_FRONTDOOR和UVM_BACKDOOR 

后门访问

定义

不消耗仿真时间(即$time打印的时间)而只消耗运行时间的操作为后门访问

实现方式

  1. 使用interface进行后门访问操作
  2. DPI+VPI实现后门访问
    函数作用
    uvm_hdl_read(string path, uvm_hdl_data_t value);后门读
    uvm_hdl_deposit(string path, uvm_hdl_data_t value);

    后门写

    可被信值覆盖

    uvm_hdl_force(string path, uvm_hdl_data_t value);

    force信号

    在release前不能被覆盖

    uvm_hdl_release(string path);release信号
    uvm_hdl_check_path(string path);检查HDL路径是否存在
    uvm_hdl_release_and_read(string path, uvm_hdl_data_t value);更新信号release后的值
    uvm_hdl_force_time(string path, uvm_hdl_data_t value, time force_time);force某个信号为特定值之后一段时间后再释放
  3. 寄存器模型中的后门访问函数
  • 实现前提:设置好configure函数的第三个路径参数;设置好根路径hdl_root
    class reg_model extends uvm_reg_block;…virtual function void build();counter_high.configure(this, null, "counter[31:16]");counter_low.configure(this, null, "counter[15:0]");…endfunction
    …
    endclassfunction void base_test::build_phase(uvm_phase phase);rm = reg_model::type_id::create("rm", this);rm.configure(null, "");rm.build();rm.lock_model();rm.reset();rm.set_hdl_path_root("top_tb.my_dut");
    endfunction
  • UVM_BACKDOOR形式的read和write:会在进行操作时模仿DUT的行为
  • peek/pook:完全不管DUT的行为
task uvm_reg::poke(output uvm_status_e status,input uvm_reg_data_t value,input string kind = "",input uvm_sequence_base parent = null,input uvm_object extension = null,input string fname = "",input int lineno = 0);task uvm_reg::peek(output uvm_status_e status,output uvm_reg_data_t value,input string kind = "",input uvm_sequence_base parent = null,input uvm_object extension = null,input string fname = "",input int lineno = 0);

前后门访问区别

区别前门访问后门访问
时间上由于是真实的物理操作,因此会 消耗仿真时间不消耗仿真时间
调试上任何前门访问都会 有波形文件,方便调试没有波形文件 的记录,调试难度增加
应用上能验证 总线协议本身 是否正确

大规模寄存器的 快速初始化

能够操作 只读寄存器

注入故障

寄存器模型对DUT的模拟

期望值与镜像值

  • 镜像值(mirrored value):与DUT保持同步的值
  • 期望值(desired value):期望DUT改变的值
/*设置期望值*/
p_sequencer.p_rm.invert.set(16'h1);
/*获取期望值*/
value = p_sequencer.p_rm.invert.get();
/*获取镜像值*/
value = p_sequencer.p_rm.invert.get_mirrored_value();
/*检查1镜像值和期望值是否一致,不一致则更新*/
p_sequencer.p_rm.invert.update(status,UVM_FRONTDOOR);

 常用操作及其对期望值和镜像值的影响

操作影响
read&write更新期望值和镜像值(二者相等)
peek&poke更新期望值和镜像值(二者相等)
get&set

set操作会更新期望值,但是镜像值不会改变

get操作会返回寄存器模型中当前寄存器的期望值

update检查寄存器的期望值和镜像值是否一致,如果不一致,那么就会将期望值写入DUT中,并且更新镜像值,使其与期望值一致
randomize

期望值将会变为随机出的数值,镜像值不会改变。一般和update一起使用

只有当configure时将其第八个参数设置为1时支持此功能

mirror更新期望值和镜像值
predict更新镜像值和期望值

寄存器模型的高级用法

  • auto predict:寄存器模型会更新寄存器的镜像值和期望值
    rm.default_map.set_auto_predict(1);
  • mirror:读取DUT中寄存器的值并将它们更新到寄存器模型;可以在uvm_reg和uvm_reg_block级别被调用
    task uvm_reg::mirror(output uvm_status_e status,input uvm_check_e check = UVM_NO_CHECK,input uvm_path_e path = UVM_DEFAULT_PATH,…);
    //第二个参数可选:UVM_CHECK和UVM_NO_CHECK,代表若镜像值与期望值不一致,是否更新前给出错误提示
  • predict:更新镜像值,但是同时又不对DUT进行任何操作
    function bit uvm_reg::predict(uvm_reg_data_t value,uvm_reg_byte_en_t be=-1,uvm_predict_e kind=UVM_PREDICT_DIRECT,uvm_path_e path=UVM_FRONTDOOR,);
    //第一个参数表示要预测的值
    //第二个参数是byte_en,默认-1的意思是全部有效
    //第三个参数是预测的类型,可选参数:
    typedef enum {UVM_PREDICT_DIRECT,UVM_PREDICT_READ,UVM_PREDICT_WRITE
    } uvm_predict_e;
    //第四个参数是后门访问或者是前门访问
  • 扩展位宽
    /*扩展位宽*/
    `ifndef UVM_REG_DATA_WIDTH
    `define UVM_REG_DATA_WIDTH 64 `endif
    /*地址位宽*/
    `ifndef UVM_REG_ADDR_WIDTH
    `define UVM_REG_ADDR_WIDTH 64 `endif
    /*字选择信号的位宽*/
    `ifndef UVM_REG_BYTENABLE_WIDTH
    `define UVM_REG_BYTENABLE_WIDTH ((`UVM_REG_DATA_WIDTH-1)/8+1) `endif
  • get_root_blocks
  • get_reg_by_offset

寄存器模型内建sequence

内建reg测试seq:

  1. uvm_reg_hw_reset_seq:
    • 功能:检查reg_model中寄存器的复位值与实际RTL是否一致
    • 测试级别:uvm_reg_block/uvm_reg
    • 原理:
      • 先reset reg_modle 。将reg_modle中的镜像值和期望值复位
      • 判断是否在外部设置了哪些 reg_block /reg不需要进行 reset 测试
      • 对所有需要进行测试的 reg 通过前门的方式读回DUT的硬件值,再与 reg_model 的 mirror 值进行对比,如果不一致,证明DUT的硬件复位值与reg_modle 的寄存器复位值不一致
    • 使用方式:
      uvm_reg_hw_reset_seq hw_reset_seq; //声明hw_reset_seq = new("hw_reset_seq"); //例化
      hw_reset_seq.model = XXX_RAL.xxx_reg; //与要被检查的寄存器模型相连
      hw_reset_seq.start(null); //启动
      /*这些内建sequence本质上并没有放到某个具体的sequencer执行,它只是用了sequence执行时调用body()的机制而已,因而我们传递任何的参数给sequence的start()只需要满足参数类型需求就行了,具体传递的值是多少并不重要*/
    • 设置reg不测试:
      uvm_resource_db#(bit)::set({"REG::",regmodel.blk.get_full_name(),".*"},"NO_REG_TESTS", 1, this);
      uvm_resource_db#(bit)::set({"REG::",regmodel.blk.reg.get_full_name(),".*"},"NO_REG_HW_RESET_TEST", 1, this);
      //reg_block/reg均可使用NO_REG_TESTS和NO_REG_HW_RESET_TEST
      //NO_REG_TESTS和NO_REG_HW_RESET_TEST区别在于前者针对所有的内建sequence都排除,而NO_REG_HW_RESET_TEST仅仅针对的是uvm_reg_hw_reset_seq这一单一sequence
  2. uvm_reg_single_bit_bash_seq
    • 功能:通过前门读写实现对寄存器读写访问域每个bit的遍历操作
    • 测试级别:uvm_reg
    • 原理:对每个可读可写域分别写入1和0并读出后座比较
    • 设置reg不测试:
  3. uvm_reg_bit_bash_seq
    • 功能:通过前门读写实现对所有寄存器读写访问域每个bit的遍历操作
    • 测试级别:uvm_reg_block
    • 原理:对包含所有uvm_reg都执行 <uvm_reg_bit_bash_seq> 序列
    • 注意事项:仅支持RW类型,对于其他类型如RO,RC这些需要exclude掉
    • 设置reg不测试:
  4. uvm_reg_single_access_seq
    • 功能:用来检查寄存器映射的有效性
    • 测试级别:uvm_reg
    • 原理:先从前门写入寄存器,而后从后门读回值对比;然后反过来后门写入再用前门读回,确保得到的结果值与镜像值匹配
    • 注意事项:
      • 要求寄存器的hdl路径完成映射
      • 没有可用后门的寄存器,或者只包含只读字段的寄存器,或者字段具有未知访问策略的寄存器,都不能被测试
    • 设置reg不测试:
  5. uvm_reg_access_seq
    • 功能:检查寄存器的读写
    • 测试级别:uvm_reg_block
    • 原理:对包含所有uvm_reg都执行 <uvm_reg_single_access_seq> 序列,来验证一个块内所有寄存器的可访问性
    • 注意事项: 需要在配置ral时,添加寄存器的层级路径,方便seq找到后门。
      //二者地址拼接
      XXX_RAL.set_hdl_path_root("xxx")
      xxx.configure(this,"")
    • 设置reg不测试:

  6.  uvm_reg_share_access_seq
    • 功能:用来检查所有可能访问寄存器路径的有效性
    • 测试级别:uvm_reg
    • 原理:先在一个地址映射中写入共享寄存器,然后通过其他所有可读的地址映射以及后门接口读取它,确保得到的值与镜像值一致
    • 设置reg不测试:
  7. uvm_reg_mem_hdl_paths_seq
    • 功能:检查hdl路径正确性

内建mem测试seq:

  1. uvm_mem_single_walk_seq:
  2. uvm_mem_walk_seq:
  3. uvm_mem_single_access_seq:
    1. 注意事项:需要存储模型的hdl路径已经指定
  4. uvm_mem_access_seq:
    1. 功能:
    2. 设置mem不测试:
      uvm_resource_db#(bit)::set({"REG::",rm.get_full_name(),".*"}, "NO_REG_TESTS", 1, this);
      uvm_resource_db#(bit)::set({"REG::",rm.get_full_name(),".*"}, "NO_MEM_TESTS", 1, this);
      uvm_resource_db#(bit)::set({"REG::",rm.invert.get_full_name(),".*"},"NO_MEM_ACCESS_TEST", 1, this);
  5. uvm_mem_shared_access_seq:
  6. uvm_mem_mam:动态内存分配
    1.  uvm_mem_mam介绍 
    2. Memory Allocation Manager


http://www.mrgr.cn/news/90212.html

相关文章:

  • Android多包路由方案: ARouter 路由库
  • mybatis 是否支持延迟加载?延迟加载的原理是什么?
  • centos 8和centos 9 stream x64的区别
  • 【PG】DROP TABLE ... CASCADE
  • 防火墙安全综合实验
  • 树形表查询方法
  • DeepSeek-R1:通过纯强化学习提升大模型推理能力,对于真正的强 AI (AGI/ASI),要放弃人类评审,让TA学会自我评估与博弈
  • 【前端】几种常见的跨域解决方案代理的概念
  • 【Mac排错】ls: command not found 终端命令失效的解决办法
  • Cherry Studio之DeepSeek联网/本地,建属于自己的AI助理!
  • Python分享20个Excel自动化脚本
  • 【C++语言】类和对象(下)
  • 大模型推理——MLA实现方案
  • nodejs - vue 视频切片上传,本地正常,线上环境导致磁盘爆满bug
  • DeepSeek模拟阿里面试——java面向对象
  • 以创新技术驱动Creo许可优化,赋能企业高效设计
  • JavaEE架构
  • AutoGen实战应用
  • 防火墙用户认证实验
  • zynq tcp万兆网和ftp协议分析
  • k8s部署elasticsearch
  • 【数据结构】双向链表(真正的零基础)
  • 【故障处理】ORA-19849 ORA-19612 0RA-17627 ORA-03114
  • 算法之 博弈问题
  • 工厂方法模式详解(Java)
  • 元数据、数据元、数据元素、数据项 和 主数据的概念