“优秀的验证工程师,一定是在犯错中成长起来的。”
来源| 杰瑞IC验证(ID:Jerry_IC) |原创
作者| Q哥
泰戈尔曾经说过,“如果你对一切错误关上了门,那么真理也将将你关在门外。”
毛爷爷曾经说过,“错误是正确的先导。”
Q哥曾经说过,“优秀的验证工程师,一定是在犯错中成长起来的。”
没错,Q哥就是我本人了,你们在Jerry乱侃之余,我来正经的带各位初学者一起踩坑,让我们一起从错误中汲取营养,快速茁壮成长起来~。
来,bug们,向我开炮~
01谈一个关于class句柄传递的坑
假设现在有如下的一个类MyClass:
大家都知道,class变量本身呢只是一个句柄(有点像C语言里面的指针),没有new之前,指向空(null)。
当我们进行赋值的时候,只是传递句柄,并没有创建新的对象。比如:
看似很简单吧,但是随着验证环境的复杂,容易忽视这一点:
比如在函数调用的时候:
可以看到funcX的第一个参数是input int类型。在函数调用的时候进行了单向复制,之后在funcX里面修改b(15行),并没有影响到funcY里面a的值;在funcY里面修改a(33行),也没有传递给b。
但是第二个参数就不一样了,由于是class类型,所以函数调用传递了句柄,也就是说,funcX里面的B 和funcY里面的A指向了同一个对象,操作B.ID和操作A.ID是等效的。 16行改变了ID值,所以17行和30行打出的ID值是一样的。之后,34行改变了A.ID的值,而A和B因为指向同一个对象,所以19行打印出的B.ID和25行的A.ID是一样的。
所以,如果不希望这两个类变量指向同一个对象,需要在funcX的一开始,就手动拷贝一份。
这样之后对C的操作不会传递给A,A的操作也不影响C。
对象就像是一个提线木偶,句柄就是线!!
还是很简单?又比如在uvm port传递transaction的时候,monitor中抓transaction就发给scoreboard,如果scoreboard处理过程中,monitor又发了新的transaction,后果不堪设想啊!
再来,当我们把一个对象push进队列的时候,其实保存的也只是句柄:
// 结果是
myclass_q[0].ID = 2
myclass_q[1].ID = 2
myclass_q[2].ID = 2
所以,需要每次create一个新的对象,再放入队列,这是一个好的coding sytle:
02谈一个枚举类型拼位的坑
今天再讲一个小的枚举相关的问题代码:
Fruit_e默认是32 bit;
如果需要声明成2 bit,需要改成: