说在前头

目前接触的UVM内容都是基于systemVerilog的。所以,碰到的问题主要基于sv。

一、低级语法错误

此类错误是由于一些低级操作或常识不清导致的,并很容易解决。
多为语法错误。

1、信号赋值

信号主要分为wire型和reg型。
在进行信号赋值时,wire型信号不能出现在等号左边只能在右边,reg型信号可以出现在等号左边和右边。故,当不小心把wire型信号放在等号左边,就会报错。
信号赋值,左右两边的信号必须是相同类型,否则无法赋值。

当然,现在systemVerilog中引入了logic类型的变量,可看作是wire型和reg型的综合。使用logic类型的信号,就能避免上面的问题。

2、拼写错误

因为每天敲很多代码,经常由于错敲、多敲、漏敲字母导致信号名错误,使得编译报错:该信号未定义。
另外,由于屏幕分辨率及字体原因,导致小写的“i”与数字”1”不易区分,数字“0”和字母“O”不易区分,稍不留意就会认错。

3、语法错误

有时编译报错为语法错误,但是仔细反复检查那一行,却找不出问题。这时候,不妨往上一行看看。
原因是,经常由于上一行末尾少敲结尾的分号“;”,导致下一行编译报语法错误。

4、invalid错误

实际工程中,自己动手敲代码,编译发现invalid类型错误。
比如,某个类型的对象,编译提示该类型未定义,invalid。但是,去检查代码,自己明明定义了的。
原因是什么?
哈哈,其实很简单,不是你定义出问题了(若是,那也不会是invalid的了!)。
解决问题分两步:

第一,检查自己的类型名称拼写是否有错误。

对的,很有可能是敲错了!
名称对不上,自然是invalid。

第二,检查代码编译顺序。

已经排除拼写错误,那么自然类型定义是没有问题的。
那是什么原因?
因为编译时会将include的文件按照代码顺序编译,于是,很有可能因为顺序问题,导致应用类型的代码比定义类型的代码先编译,那么,基于先定义再使用的原则,显然该类型在编译到应用该类型时,自然是未定义的,也就是invalid。
解决办法:只需要将定义的代码说在文件的include顺序提前到应用类型的文件前面。
对,就是这么简单!
你以为多深奥?
再说一遍,核心办法是:查看在顶层中一大堆include“xxxx”的顺序,确定类型的先定义再应用的顺序。
经验之谈:一般,将最底层的文件放在开头,也就是自己纯定义类型,不应用别的类型;应用类型的文件,一定放在该类型的定义文件之后。

5、使用UVM机制

UVM中提供了各种功能强大的机制,提高工作效率。比如factory机制。
但是,要使用这些机制,定义的object、component都必须用特定的宏注册。
否则,无法使用对应机制。
并造成对应定义的object或component存在invalid问题。

6、类变量的invalid

UVM使用systemVerilog,与C++、Java等面向对象语言有很多相同点。比如,类的对象,声明后,在使用前,需要实例化。在SystemVerilog中使用new()函数实现.
有时候,代码通过编译,但是在仿真时因出错而停止,会出现“NULL pointer dereference”的错误提示.
这种错误是说:出现空指针的引用。
就说明,当前错误的地方是因为声明的该实例没有实例化。
当正确声明一个变量,使用时,需要先进行new操作,即实例化。实例化,会申请对应空间供变量使用。否则,声明的变量就只是一个单纯的该类型指针,没有实际空间,无法直接使用。
比如:
A a;// class A
a.copy(); // 报错:NULL pointer dereference

修改为:
A a;// class A;
a = new(“a”);
a.copy() // 正确

7、虚拟接口

UVM中提供了虚拟接口,用于避免使用绝对路径。
每一组虚拟接口,在定义后,都必须完善连接。否则,由于接口没有连接上,使用会报错。

8、函数调用错误

调用函数,不管是系统函数还是自定义函数,函数中的参数个数要匹配,否则,会报错。
错误信息大概如下:
The above function/task call is done with more arguments than needed.
这类错误比较容易解决,直接去参看原函数定义,检查参数类型及数量,一一对应修改即可。

9、uvm_info引起的错误

通常,为了方便调试,会添加uvm_info打印信息,功能等同与C语言中的printf函数。
这个当然很好。
但是,一不小心,就会引起错误。还很莫名其妙。
比如,在某个UVM component的某个phase中,需要在执行进入该phase中,就打印相关信息,提醒当前已经执行到这里。
想法很好,uvm_info的语法也简单。
一般不会有错。
但是,假如uvm_info后面有变量声明,那么,不好意思,编译时会报错,提示添加“=”or“<=”。
WTF?
莫名其妙嘛!
解决办法,将uvm_info语句移到变量声明的后面即可。

二、高级逻辑错误

所谓高级错误,是为了和前方的低级错误相区别。
化用伟人的话说,此类错误,已经脱离了低级趣味。
不再是简单的语法、常识错误,而是更深层次的逻辑错误。
表现为:能正常通过编译环节,但是在仿真阶段会出错。
这类错误往往更难处理,需要对UVM各部件本身及部件之间的连接关系有深刻理解。

UVM有许多component和object。这些部件之间的相互协作有一定的工作顺序,当前后顺序错误时,同样导致仿真阶段无法正常工作。

1、sequence

最近(2016-10-10)碰到的一个问题。
仿真给出的错误信息如下:
reg_seq [SEQ] neither the item’s sequencer nor dedicated sequencer has been supplied to start item in reg_seq
从字面意思看,就是说在处理reg_seq的时候,对应的sequencer并未预先设置完成。
都说sequence就是子弹夹,sequencer就是枪。
子弹夹已经就绪,枪还没有,所以无法开枪。
那该怎么办?
经过两天的查看,发现问题是由于需要用到的sequencer等尚未定义,因此,此处使用sequencer当然就还没有准备好。

2、null object access(NOA)

昨天晚上调试验证平台代码,仿真阶段出现了NOA错误,错误信息显示,需要使用的object并没有预先完成实例化。
这类错误主要由于在声明object之后,忘记实例化造成的。
有人会问,怎么这么粗心大意?
这都能忘?
我:……
这么低级的错误,本小白肯定还不至于这么不小心。
经过自己梳理平台中object、component之间的调用关系,以及验证平台的执行顺序后,发现是昨晚刚对底层的component的定义做了修改,由于功能需要,添加了新的object,及操作。
经过反复检查确认,新添加的代码,本身没有问题,需要调用的地方也没问题。
问题出在其他位置调用此component的情况。
需要调用新object的地方,对新object做了相应处理。
而旧的调用的地方,则并没有做处理,于是问题就出来了。
在并不需要新object的地方,并没有进行实例化。而该object依然进行了相应的操作,比如赋值,于是,出现了NOA错误。

总结教训:以后修改底层文件,一定要小心,修改底层文件,那么凡是调用这个文件的地方,都要做检查,按照需要进行相应修改,否则,各种NOA,让人莫名其妙,烦不胜烦。最好的办法,不到万不得已,千万不要修改底层文件!