电子说
当我们在创建动态仿真case时,使用命令行参数可以非常方便地控制DUT和TB的行为,比如配置寄存器、控制激励的发送数量、打开或关闭某些scoreboard等。
当面对很多验证组件,并且有很多命令行参数需要传递的时候,如何才能简单地实现给这些组件传递命令行参数呢?
大家首先想到的可能是plusargs。
没错,plusargs的确是一个非常简单易用的机制。对于规模小的项目,完全够用。但是对于像包含了100个agent、涉及多个工程师共同开发的复杂大项目,plusargs的缺陷就暴露出来了。
1 plusargs的缺陷
首先,plusargs使用的位置太随意了,可以在module、class、interface等等任何能使用begin/end块的地方解析命令行参数。这就埋下了彼此影响的坑。看过jerry之前文章“$test$plusargs(),$value$plusargs()怎么用?有什么坑?”的朋友们还记得,plusargs不仅仅认为“张三”和“张三”同名,连“张三”和“张三丰”也算同名!使用的时候,稍有不慎,可能会因为同名误伤了其他组件。
其次,plusargs使用的时候,命令行参数的格式必须跟解析的格式一样,否则会传递失败,甚至传递错误的值。比如:按照16进制解析的参数,如果按10进制传递,就会传递错误的值。即使传递16进制,到底是按照+data=‘hff00传递,还是按照+data=32’hff00传递,又或者按照+data=ff00传递,也需要提前约定并统一风格。这在大型项目里,直接增加了沟通成本。
另外,plusargs不支持数组和枚举类型的数据。用户必须自己编写额外代码进行解析。
2 uvm命令行参数简介
uvm定义了一套自己的命令行参数规则,可以非常方便地从命令行传递常用类型数据到tb里,完美地解决了上面提到的几个缺陷。下面逐一举例说明。
2.1 传递int类型参数
图1 代码片段1
如图1代码片段所示,32和33行定义了40-bit变量data和int变量data_vld,为了让uvm命令行参数可以传递值给它们,需要添加36和37行的声明。
这里需要注意下,uvm_field_int只是声明这个变量按照整数数据类型解析(而不是real浮点数、string字符串、enum枚举或者数组等类型), 大家不用担心40比特data的高位会丢失。
然后使用下面的命令行参数就可以分别传递参数值给data和data_vld。
图2 uvm命令行传递int参数
眼尖的朋友们有没有发现,这个命令行参数是不是有些眼熟?没错,之前我们聊uvm_info高级技巧的时候,用到的也是类似的命令行参数。(uvm_info高级技巧(1) ---如何屏蔽某些刷屏的啰嗦调试信息)
1. 等号后面第一个参数就是component的路径,也就是当你调用get_full_name所返回的完整路径名。因为有路径限制,可以避免不同component的同名问题。类似之前讲过的,这里的路径可以使用星号通配符。
2. 第二个参数就是要赋值的变量名字,不用再担心plusargs误把“张三”和“张三丰”当作同名的问题了。
3. 第三个参数是传递的数值,这个值可以是10进制也可以是16进制、2进制、8进制,只要符合verilog语法就可以了,uvm会自动解析的。因为要符合verilog语法,所以16进制不能只写ff00,可以写32’hff00, ‘hff00, ‘shff00。除此之外,写成0xff00也是可以的。
2.2 传递string类型参数
对于string类型的数据,使用方法跟int类似。定义方法如图3所示,命令行参数如图4所示。
图3 定义string类型参数
图4 传递string类型参数
2.3 传递enum类型参数
对于plusargs传参数,如果要处理enum变量,要么放弃可读性按照int传递,要么先按照string传递然后再另外写case语句解析enum。
对于uvm命令行参数,处理enum就简单多了。闲言少叙,直接上代码,如图5所示。
图5 定义枚举类型变量
82行定义了fruit_e这个枚举类型,85行定义了fruit_e类型的枚举变量m_frt。88行声明枚举变量的时候,相比int或string类型,多了第一个参数,就是枚举变量具体的枚举类型。
命令行参数如图6所示,直接按照枚举进行传递就好了,uvm会自动解析。勘误,下图参数enum当为string
图6 传递枚举类型参数
2.4 传递int数组参数
除了单个的变量,uvm也支持命令行传递数组类型的参数。如图7所示。
图7 声明数组类型变量
那么问题来了,对于这100个参数,如果传递的大部分参数都是同样的值,只有少数值不一样,难道还要写100个命令行参数吗?
答案当然是否定的。uvm命令行参数不仅支持component路径使用通配符,传递数组的时候,也可以通配。
图8 使用通配符给数组传递参数
如图8所示。先给所有的data元素赋值’hff, 然后单独给data[88]赋值1。因为命令行参数后面赋值会覆盖掉前面的赋值,所以data[88]最终传递的是1.
这里大家注意下,数组类型声明的是uvm_field_sarray_int, 传递参数的时候还是按照int的方式传递,只不过变量名字带了数组元素下表,并且支持星号通配符。
2.5 传递string数组和enum数组
string数组、enum数组的使用方法和int数组类似,没什么可说的。参照图9的声明方法和图10的命令行参数。
图9 声明string数组和enum数组
图10 传递参数给string数组和enum数组
除了上面提到的这些简单常用的参数类型,还有类似real浮点数、动态数组、关联数组等类型。大家如果有需求,可以自行参考uvm官方文档学习使用。限于篇幅,不再赘述。
2.6 uvm命令行参数什么时候生效的?
善于思考的朋友们,有没有想到一个问题:既然uvm会自动解析uvm_set_config命令行参数,那么这些参数值是什么时候传递给tb里面的变量的?会不会跟我们初始化的值冲突?
图11的示例代码,我们加上变量初始化和打印语句。
图11 调试uvm命令行参数生效时间
传递命令行参数+uvm_set_config_int = uvm_test_top.env.agt[0],data,’hdeadbeef,根据图12的测试结果可以看到,40行打印出的data值是32行初始化的,45行打印的data值是uvm_set_config_int命令行参数传递的,41行的赋值被命令行参数给覆盖掉。
也就是说,uvm_set_config_int是在new之后,build_phase之前完成的。大家在使用的时候一定要注意这个失效时间,避免多次赋值互相干扰。
图12 uvm_set_config参数生效时间测试结果
3 uvm命令行参数的限制
1. uvm命令行参数目前只能给component传递,不能给object传递。不过大家可以换一个思路,比如sequence里面要用参数mydata的话,可以传递到对应的sequencer,在sequence里面通过调用p_sequencer.mydata就可以了。
图13 间接传递uvm命令行参数给sequence
2. uvm命令行参数指定component路径的时候,如果要使用星号通配符,需要格外注意。因为uvm会把env*.sequencer匹配成env.sequencer、env0.sequencer、env0.agent.sequencer、env1.sequencer、env1.agent.sequencer等等(精通正则表达式的朋友们可以继续发散)。
3. uvm命令行参数会自动在new之后,build phase之前解析。使用的时候注意不要被覆盖了。
总结
Q哥今天给大家安利了uvm命令行传递参数的小技巧。相比plusargs,uvm命令行参数当然不是完美的,但是用到恰当的场景下,还是可以事半功倍的。大家在使用的时候,注意下上面的提到的几个问题。
审核编辑:汤梓红
全部0条评论
快来发表一下你的评论吧 !