中文第一计算机图形学社区OpenGPU 版权所有2007-2018

 找回密码
 注册

扫一扫,访问微社区

搜索
查看: 16973|回复: 10

谈谈验证中的SystemVerilog和CPP

[复制链接]
发表于 2011-8-11 02:21:58 | 显示全部楼层 |阅读模式
谈谈验证中的SystemVerilog和CPP



两种语言都用了几年了,一直想找个机会总结一下。今天有空说一说我的理解。



1 函数的参数传递


SV:SV默认为值传递,即使是传递对象和数组,也就是说对参数的改变只在函数内有效,无论input,output还是inout都会在函数内部进行参数值的拷贝,只是拷贝的具体时间不同,input是在执行前拷贝,output是在执行后拷贝,inout是拷贝两次,只有使用ref关键字才能做引用传递。

CPP:提供值传递,指针传递和引用传递三种方式,因此对象和数组使用指针传递,而虚函数多态的时候则使用引用传递。



2 函数参数类型检查


SV:在进行引用传递的时候,类型必须匹配,不能强制转换。可通过强制转换void忽略一个函数的返回值。

CPP:由于子类转父类是自动完成的,且子类的信息全部丢失,因此一般在虚函数多态时采用引用传递。同样可以忽略函数的返回值。



3 操作符重载


SV:使用下面的语句可以重载一个操作符,返回值和参数可以隐式或强制的转换。

bind overload_operator function data_type function_identifier (overload_proto_formals);

CPP:使用下面语句重载操作符,同样返回值和参数可以隐式或强制转换。

bool operator==( const String &str1, const String &str2 );



4 类的继承和方法的重载,覆盖和隐藏


SV:SV所提供的机制被称为单继承,也就是说,每一个子类都由一个单一的父类继承而来,父类的方法可以被覆盖或隐藏。不支持同一类域中同名不同参的方法重载。父类中需要多态的方法必须被声明成virtual以便被覆盖。普通类的virtual方法必须被具体实现,virtual类的方法可以没有具体的实现。virtual类不能被实例,只能被继承。super关键字在一个子类的内部使用,可以用来引用其父类的成员。

CPP:支持多继承。同一类域中同名不同参的方法可以被重载;派生类将覆盖基类中同名同参的virtual方法;派生类将隐藏基类中的同名不同参的方法;派生类将隐藏基类中同名同参且不是virtual的方法。



5 内存管理


SV:对象、字符串、动态数组、以及联合数组的内存是动态分配的。当对象不再需要的时候,SV自动地释放内存以便这些内存能够被重新使用,由于碎片收集是自动的,用户不需要担心悬挂的引用、过早的释放、或者内存泄漏。动态数组和联合数组只能是一维的。

CPP:程序运行的时候用malloc,new和free,delete手动的申请和释放内存。指针随生命期消亡并不代表它所指的内存被自动释放。动态数组可以是多维的。



6 构造函数


SV:声明并不会隐式的调用构造函数,只会生成一个句柄变量,需要显式的调用new函数才会产生一个对象。构造函数new()必须是无阻塞的。每个类都有一个内建的new()函数,它将调用super.new(),然后初始化类的属性和对象。

CPP:构造函数与类同名,用重载机制(不同参数的构造函数)实现不同对象的构造。



7 类的拷贝


SV:不同类对象之间的拷贝是非法的。对相同类对象的拷贝是浅拷贝,对象中的变量被拷贝,但对象中的对象只拷贝其句柄。

CPP:在声明的同时赋值,调用的是拷贝构造函数

class1 A("af");class1 B=A;

而在声明之后赋值,调用的是赋值函数

class1 A("af");class1 B;B=A;

无论调用拷贝构造函数还是赋值函数都是浅拷贝,需要被重载来完成深拷贝。



8 typedef


SV:如果两个类中的每一个类都需要另外一个类的句柄。在编译器处理第一个类的声明期间,编译器遇到了第二个类的引用,而这个引用是未定义的,因此编译器会将它标记为一个错误。通过使用typedef来为第二个类提供提前声明就可以解决这个问题。

CPP:由于CPP一般将声明放在头文件中,实现放在CPP文件中,头文件被预先编译,因此不存在这个问题。



9 多态性


SV&CPP:多态性使父类中的一个对象能够保存子类对象,并且能够直接从父类对象中引用子类的方法。由于方法被声明成虚拟的,所以可以从父类中通过不同参数访问恰当的子类方法,即使编译器在编译的时候并不知道它将要加载哪一个方法。需要多态的方法必须在父类中被声明成virtual的,否则从父类对象引用时,子类的方法将被隐藏。SV和CPP都是向上转类型,也就是说,子类对象可以赋给父类对象,但父类赋给子类是非法的,除非父类句柄引用了子类句柄。



10 强制类型转换


SV:用col = col'(2)进行编译时的类型转换;用$cast进行运行时的类型转换,返回0或1,可以用来检查强制转换是否合法。

CPP:用dynamic_cast进行运行时的类型转换,引用类型的不成功转换将会引起一个异常。用typeid指出父类对象的派生类的类型。



11 数据的隐藏与封装


SV:类属性可以被标识成local,只能本地使用。类方法可以被标识为protected,只能被其成员函数和派生类访问。没有友元的概念。

CPP:访问限定符包括public,private,protected。

Public-在程序的任何地方可以被访问。

Private-只能被成员函数和类的友元访问。

Protected-对派生类相当于public,对其他程序则相当于private。



12 域

SV:域操作符::只支持类域,应用于从类的外部访问一个类(注意不是对象)的所有静态成员(静态类属性、静态方法、typedef、枚举、结构体、联合体以及嵌套的类声明),并能够在继承类内访问超类。常常和extern配合实现方法的类外声明。

CPP:域操作符::支持局部域local scope;名字空间域namespace scope;类域class scope。


13 模板

SV:定义一个参数化的类并在实例的时候传递参数。

class vector #(int size = 1); vector#(5) v;

CPP:定义一个模板类并在实例的时候传递参数。

Template <typename T>

Class collection;

Collection<int> c;



14 指针,句柄和chandle

操作
C指针
SV对象句柄
SV chandle
算术运算(例如递增)
允许
不允许
不允许
指向任意数据类型
允许
不允许
不允许
当为null时废弃
错误
不允许
不允许
强制类型转换
允许
受限制
不允许
赋值为一个数据类型的地址
允许
不允许
不允许
未引用的对象被当作垃圾数据收集
不是
是的
不是
缺省值
未定义
null
null
指向类
(C++)
允许
不允许




小结:验证环境选用SV还是CPP,要考虑项目的复杂程度和验证层次的高低。CPP的多继承和友元机制,可以使代码更易于维护,结构更清晰;CPP的namespace scope,方便了不同项目特别是不同部门之间代码的传承;在通过参数传递一个对象或数组时,CPP的指针,引用和隐式类型转换能够提供更好的函数多态性,同时提高了执行效率。然而不幸的是,CPP的手动内存管理,是多个项目组协同工作的一个噩梦。另外,为了使CPP能够方便的访问RTL,我们不得不借用SystemC或PLI来实现底层函数,这些底层函数是否复杂?能否复用?直接关系到项目的进度。当然,如果底层函数和上层函数是分开研发的,上层函数需要在底层函数不同的情况下被复用,CPP更加灵活的多态性正好可以满足这一需求。


      验证TEST CASE选用SV还是CPP,我认为在能够使用CPP驱动的验证环境中,即使激励和自检测都是由SV来完成的,也应当尽量使用CPP来写test,这虽然会带来附加的工作量,但主要有三大好处:

  • 节省compile的时间。这也是最重要的原因。因为SV是需要和Verilog一起编译的,并且不支持分部编译,因此如果用SV写TEST,一定是每个TEST都要重新编译整个TestBench。而如果用CPP写TEST,每次编译基本只需要编译一个C文件。

  • 方便系统级重用TEST。由于系统级一般是软硬件协同工作,无论是SOC芯片还是仅仅是协议层,都需要大量的软件操作,而用SV写的TEST在系统级相当于需要重写。

  • 方便与系统级配置协同工作。系统级的初始化和中断处理一般都是由软件完成的,这是一个验证环境最开始且最重要的工作,因此它会非常稳定,用CPP写TEST可以方便的与这些function协同工作。

      
那么如何做到用CPP写TEST呢?我认为最好的方法是在vector(cpp test) compile之前,编译并运行SV的test和generator,这里generator的底层,并不是BFM,而是生成相应的C代码(也可以只是头文件),以便最大限度的利用SV生成随机激励,然后进行vector compile和RTL compile,这样,无论如何修改TEST,整个testbench的compile只需要一次。

      
如果验证环境不能方便的使用CPP驱动,且RTL很庞大,也可以实现多个TEST一次编译。有三种方法:

  • 用SV或VERILOG实现底层函数,然后TEST使用DPI来调用,或直接用SC实现接口,这种方法的工作量最大,也最彻底。

  • 在CPP里force signal,实际也是用PLI来做接口,虽然简单,但只适用于不复杂的操作。

  • 当SV生成头文件后,脚本从文件里拿到结构体,在仿真时通过VCS传递给model,这种方法需要结构体定义的非常完善,能够覆盖到所有的情况。

      
我认为,只有在RTL较小,激励相对随机性很大,不适用于系统级重用的情况下,用SV来写Test Case才是make sense的.



from : http://blog.sina.com.cn/s/blog_525265890100lih5.html
 楼主| 发表于 2011-8-11 03:10:27 | 显示全部楼层
有几点异议,请教一下大牛。

1. 在SV中package也是一种域的概念吧,就像C++下面的namespaces

2. 关于TestCase,我认为这应该写成文本格式啊,然后让TB从文本读取,从而灌入TB,而不是把它做成TB的一部分。这就叫做可配置。最好能用C来做这个读取的功能,然后把这个功能作为一个单独的函数通过DPI链接到TB的stimulus generator上,从而让Cmodel和SV TB都能读取同一种Testcase,避免重复劳动。

3,用SV的好处就是有现成的UVM、OVM可以用,节省了从轮子开始造的尴尬。另外调试比较容易,要知道Cpp做成的TB被编译成为dll然后再输入仿真器,那运行时候可就跟不进去了,但是SV TB就可以。况且SV还有并行特性,可以使得顺序描述复杂的东西,用fork...join轻松搞定。

评分

1

查看全部评分

发表于 2011-8-11 09:38:16 | 显示全部楼层
回复 ic.expert 的帖子

感谢IC大的转载。不知道作者是否已经注册咱们论坛,我已经在她博客上发评论/纸条,邀请其加入OpenGPU, 希望该位大牛能到这个帖子来讨论讨论。

评分

1

查看全部评分

发表于 2011-8-11 10:06:03 | 显示全部楼层
回复 ic.expert 的帖子

package 是一种域的概念,它的主要作用是能够在整个验证环境中重用。

testcase 是可以写成文本的,但是这样的话就会影响随机的应用。

关于sv的好处很多,它既有c++的特性,又有verilog的特性,自成一体,非常方便;
至于各种MM方法,个人觉得是pros and cons并存吧,方法学强调的是重用,但在实际项目中并不像demo中的那么容易,总之,谁用谁知道,呵呵。

评分

1

查看全部评分

发表于 2011-8-14 08:57:57 | 显示全部楼层
回复 hi_johnson 的帖子

觉得在testcase这里的概念应该有些区别,一些feature对应的数据是可以random的。IC老大提到的放在文本中的应该是一些数据流,这些是和Cmodel共用的,也是作为激励的一部分输入到tb中。

vmm这样的东东,在tb中可灵活使用,不能方便的一些特性可以不用。tb的搭建应该是,自己team主导的,不用去迎合某种验证方法学 ~~
发表于 2012-2-14 13:11:40 | 显示全部楼层
初学SV,这个总结收藏了!
发表于 2012-2-28 01:32:22 | 显示全部楼层
比较SystemC和SystemVerilog更好一些吧。CPP太广谱了。
发表于 2012-5-24 15:53:52 | 显示全部楼层
看看大家的回帖很受教哈
发表于 2012-8-7 15:55:26 | 显示全部楼层
谢谢,学习了
发表于 2013-6-19 11:17:21 | 显示全部楼层
谢谢分享,学习了!
发表于 2013-11-9 13:12:43 | 显示全部楼层
多谢分享,感恩。。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

QQ|关于我们|小黑屋|Archiver|手机版|中文第一计算机图形学社区OpenGPU

GMT+8, 2019-1-18 08:29 , Processed in 1.194563 second(s), 20 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表