谈谈验证中的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,这虽然会带来附加的工作量,但主要有三大好处:
A 节省compile的时间。这也是最重要的原因。因为SV是需要和Verilog一起编译的,并且不支持分部编译,因此如果用SV写TEST,一定是每个TEST都要重新编译整个TestBench。而如果用CPP写TEST,每次编译基本只需要编译一个C文件。
B 方便系统级重用TEST。由于系统级一般是软硬件协同工作,无论是SOC芯片还是仅仅是协议层,都需要大量的软件操作,而用SV写的TEST在系统级相当于需要重写。
C 方便与系统级配置协同工作。系统级的初始化和中断处理一般都是由软件完成的,这是一个验证环境最开始且最重要的工作,因此它会非常稳定,用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一次编译。有三种方法:
1 用SV或VERILOG实现底层函数,然后TEST使用DPI来调用,或直接用SC实现接口,这种方法的工作量最大,也最彻底。
2 在CPP里force signal,实际也是用PLI来做接口,虽然简单,但只适用于不复杂的操作。
3 当SV生成头文件后,脚本从文件里拿到结构体,在仿真时通过VCS传递给model,这种方法需要结构体定义的非常完善,能够覆盖到所有的情况。
我认为,只有在RTL较小,激励相对随机性很大,不适用于系统级重用的情况下,用SV来写Test Case才是make sense的.
posted on 2015-03-25 10:10 Module_Sun 阅读(1992) 评论(0) 编辑 收藏 举报