电子说
作为一名DV,开发验证环境,编写验证环境也算是必备基础技能了。虽然每天都会coding,但最终写出来的代码,是一次性代码,还是方法?
何为一次性代码?可以从复用性考虑。
代码的复用可大可小。IP验证内部,写出的代码是否可以在不同case间复用,还是仅针对具体的需求,点对点地写代码。一个简单的例子就是,不同case的实现,是在不停地复制粘贴,还是精简的实现;不同case的仿真,是否需要重新编译验证环境。
system或者soc验证时,写出的case是否可以方便地移植或者为他人所使用。一个长达数百行,for/if/while/fork来回嵌套的函数,大概率会直接打消大家review的念头。其大概率也是一次性代码,复用性不高。
一次性代码和方法的区别,换个角度看,也是是验证环境的静态和动态性的一种体现。如果环境中的某些参数或者特性在编译阶段就已经确定,可认为是静态性的。如果某些参数或者code执行在运行时才会确定,则是动态性的,即runtime。
使用动态性的代码,可以使得验证环境具备较好的灵活性。尽可能少的重新编译环境,尽可能高效的debug case,在芯片规模较大时显得尤为重要。尤其在使用一些如增量编译,MSIE(multi snapshot incremental elaboration),simulation based snapshot方法时,对环境结构和灵活性的要求会更高。这里分享一些静态式和动态式的例子和思路。
ifdef/ifndef双刃剑
条件编译宏在环境开发中经常会使用到,作者此前也分享过相关的总结,读者可参阅:SystemVerilog Macro宏使用场景小结。
testbench中的env部分尽量减少使用条件编译宏。比如下面的例子:
在环境部分使用ifdef/ifndef,就意味着在不同的场景下,需要重新编译环境,是一种静态式的思路。
去除ifdef/ifndef,可以使用configuration来实现,根据config的结果来决定要不要例化或者启动某个组件。
用好plusargs
既然是runtime,动态式,那就从仿真命令中获取数据好了。plusargs有两个函数,plusargs和plusargs。前者检测是否有制定了某个参数,后者用于获取命令行输入的参数值。
使用plusargs,可以在仿真前指定某个参数的取值,如时钟频率、module选择、仿真超时域值等。甚至可以从plusargs中传入对随机参数的约束!而且UVM库中也有uvm_cmdline_processor,用于处理命令行的输入参数。同样的,前面去除ifdef/ifndef的事情,也可以用plusargs来解决。
plusarg的使用可谓"五花八门"。如果后面有机会,作者也会做类似的分享,有兴趣的读者可私信交流和学习。总之,用好plusargs可以在case的调试时,带来效率上的提升。
需求一直在变,做好数据驱动
不直接面向具体的数据编写代码,而是编写对数据的处理和操作方法。比如环境中需要对A0~A9寄存器进行配置,以完成DUT的初始化,或者需要对某些信号进行force和check。需求变化和明天,不知道哪个会先来,虽然今天的需求是配置A0~A9, force a0~a9,明天可能就是变成B0~B9。
这里就可以做好通用的寄存器配置方法,需要配置的寄存器从plusargs或者配置文件中导入,而不是看到具体的A0或者B0寄存器。需要force和check的信号,可以从文件中导入,环境中编写从force到check的逻辑方法。
数据驱动中,联合数组是一个比较好的数据结构,在其他语言中也称为哈希数组或者字典。这种数据结构有key和value两个属性,你所需要的信息可以设置到key和value上,而不用关心key和value具体是什么,是数据驱动的天然好帮手。比如只使用一个int cfg[string]类型的联合数组作为函数的入参,就可以实现多个参数的配置传递,具体的配置参数名体现在cfg的string类型的key中,参数取值体现在cfg的int类型的value中。
审核编辑:刘清
全部0条评论
快来发表一下你的评论吧 !