本篇不是最近总结系列的一部分,只不过因为跟前两篇的讨论内容一脉相承,因此放在一起吧。
【芯片设计】聊聊配置寄存器的动态改配场景与等级
【芯片验证】聊聊配置寄存器的动态改配测试与等级
本篇可以视为继续上一篇的讨论,对于不关键的配置寄存器我们可以通过白噪声的方式来提高系统健壮性测试和覆盖率收集速度,那么对于关键的不能带流改配的配置寄存器,应该如何尽可能的覆盖所有可能场景?此外,目前基于sv的验证方法学均为随机验证而非定向验证,那么对于寄存器配置而言,应该如何体现随机验证思想呢?
寄存器域的随机性使用ralgen生成的寄存器本身是rand属性的,也就是说其自身是可以通过约束随机的方式在用例中进行随机性配置的,比如下面这个寄存器:
class ral_reg_REG_PRJ_sys_cfg_base_config extends uvm_reg; rand uvm_reg_field mode; rand uvm_reg_field kernel_size; rand uvm_reg_field stride;
function new(string name = "REG_PRJ_sys_cfg_base_config"); super.new(name, 32,build_coverage(UVM_NO_COVERAGE)); endfunction: new virtual function void build(); this.mode = uvm_reg_field::type_id::create("mode",,get_full_name()); this.mode.configure(this, 2, 0, "RW", 1, 2'h0, 1, 1, 0); this.kernel_size = uvm_reg_field::type_id::create("kernel_size",,get_full_name()); this.kernel_size.configure(this, 20, 2, "RW", 1, 20'hab, 1, 1, 0); this.stride = uvm_reg_field::type_id::create("stride",,get_full_name()); this.stride.configure(this, 3, 22, "RW", 1, 3'h1, 1, 1, 0); endfunction: build
`uvm_object_utils(ral_reg_REG_PRJ_sys_cfg_base_config)
endclass : ral_reg_REG_PRJ_sys_cfg_base_config每一个域均是rand uvm_reg_field类型。不过如果我们在用例中进行randomize()或者通过类继承的方式进行随机时,很大概率仿真过程中会报一个error告知你约束冲突了,不允许改约束。这时你需要检查一下field的config函数:this.mode.configure(this, 2, 0, "RW", 1, 2'h0, 1, 1, 0);config函数在中的第8项为是field是否可以进行约束和随机的标志,如果为1则表示随机,为0则表示不能随机。那么如何使这个配置固定为1呢?这需要在生成ral_model时添加-all_fields_rand选项: ralgen -all_fields_rand -full64 -t top_module -I reg -uvm top_module.ralf如此一来,生成的ral_model.sv中所有field.configure关于随机的使能便都会打开。寄存器随机的策略选择基于对配置寄存器进行随机的思路,在用例中通常来说我们有两种策略选择:
加法式随机:根据用例的测试目的和场景,对关心的寄存器添加constraint进行范围内的随机,对其他的寄存器进行定向配置或集成base_test中的配置;
减法式随机:默认对所有寄存器进行全随机,然后根据用例的测试目的和场景,对关心的寄存器进行定向随机或定向配置;
这两种方式没有对错之分,个人而言我觉得第二种方式更加符合随机测试的本质,更加容易冲击到各类配置组合的场景,所以选取了第二种随机策略。那么基于这种策略,应当如何对组织寄存器呢?要明确的是,即使对所有寄存器进行全随机,一般而言也不是不加约束的随机。比如某个field的位宽为3bit,而只允许配置0~5这6种情况,6/7为禁止配置值,那么就需要对这个field添加额外的constraint。鉴于大部分寄存器其实都是这个情况,尤其很多寄存器之间还有关联约束(reg A == 0 -> reg B == 2),因此从生成的uvm_reg_block派生出一个新的reg_block无疑是个很好的选择。对于派生的reg_block,其基本的内容如下:
class rand_ral_block_REG_PRJ_sys_cfg extends ral_block_REG_PRJ_sys_cfg; function new(string name = "rand_ral_block_REG_PRJ_sys_cfg"); super.new(name); endfunction constraint field_value_cons{ this.base_config_mode.value dist {'0:/10, ['0:'1]:/80, '1:/10}; //default: 2'h0 this.base_config_stride.value dist {'0:/10, ['0:'1]:/80, '1:/10}; //default: 3'h1 this.cfg_info0_layer_field0.value dist {'0:/10, ['0:'1]:/80, '1:/10}; //default: 3'h1 ... this.cal_config_cal_round.value dist {'0:/10, ['0:'1]:/80, '1:/10}; //default: 3'h0 this.cal_config_cvt_mode.value dist {'0:/10, ['0:'1]:/80, '1:/10}; //default: 3'h0 } `uvm_object_utils(rand_ral_block_REG_PRJ_sys_cfg)endclass: rand_ral_block_REG_PRJ_sys_cfg每个field的随机策略均是,最大值最小值各占10%,中间区域共占80%。之后基于这个全随机的子类,需要去梳理每个field真正的随机情况并修改派生的ral_model,比如增加约束:
this.base_config_mode.value == 1'b1 -> this.base_config_stride.value == 2;this.base_config_mode.value == 1'b0 -> this.base_config_stride.value dist {1:/10, 2:/10};脚本化操作基于以上的策略,对于每一个ral_model.sv可以进行脚本化操作,在同目录下生成rand_ral_model.sv文件,脚本使用为:
./gen_ral_rand.py xx/xx/ral_model.sv而后将派生的reg_block类加入编译列表,并在环境中进行factory override操作:set_type_override_by_type(ral_block_XXXXX::get_type(), rand_ral_block_XXXXX::get_type());而后在寄存器update()操作前,进行uvm_reg.randomize()操作即可。脚本路径详见“阅读原文”。
系列文章入口——
【芯片设计】SoC 101(一):绪论 | 【芯片设计】FIFO漫谈(零)从无处不在的FIFO开始说起 | 【芯片设计】计算机体系结构(一)虚拟内存 | 【芯片设计】深入理解AMBA总线(零)绪论
| 【芯片设计】握手协议的介绍与时序说明 | 【芯片设计】复位那些小事 —— 复位消抖 | 【芯片设计】快速入门数字芯片设计(一)Introduction | 【芯片验证】UVM源码计划(零)下定决心读源码前的自测环节
| 【芯片设计】异步电路碎碎念(一) 到底什么是异步电路
| 【芯片设计】从RTL到GDS(一):Introduction
| 其他文章链接——
【芯片验证】sva_assertion: 15道助力飞升的断言练习 | 【芯片验证】可能是RTL定向验证的巅峰之作 | 【芯片验证】RTL仿真中X态行为的传播 —— 从xprop说起 | 【芯片验证】年轻人的第一个systemVerilog验证环境全工程与解析 |
【芯片设计】verilog中有符号数和无符号数的本质探究
| 【芯片设计】论RTL中always语法的消失术 | 【芯片设计】代码即注释,注释即代码 | 【芯片设计】700行代码的risc处理器你确实不能要求太多了 |
入职芯片开发部门后,每天摸鱼之外的时间我们要做些什么呢 | 如何计算系统的outstanding 和 burst length? | 芯片搬砖日常·逼死强迫症的关键词不对齐事件 | 熟人社会里,一群没有社会价值的局外人 |
|