预备代码语法
宏定义
很少有验证工程师使用SV宏来处理一些其他的事情,即使作为验证验证工程师人手必备的绿皮书也只是在数据结构章节简略提到了使用`define定义参数。SV宏作为Systemverilog中最强大的功能之一,如果透彻的了解,正确地应用于验证平台中,可以节省大量的时间,提高代码的可读性和高效性。
单行宏与多行宏
单行宏顾名思义只有一行,不做介绍。
多行宏要注意:非尾行的行尾要有 / 来告诉编译器当前宏定义还没结束。
人言:多行宏除了最后一行都要加/
宏的命名规范
-
如果使用宏定义a function或task使用大写宏名称和小写参数名称。
-
如果使用宏定义class,代码片段等等-使用小写宏名和大写参数名称。
-
宏名称中的单词用下划线分隔。
带参数的宏
宏可以向函数一样接收传入参数,具体用法如下:
宏的三种特殊符号
1. ` ” (Backtick and Quotes)反引号和引号
如果宏文本被括在双引号(")中,它本质上就变成了一个字符串,不会发生宏的替换。
双引号内的参数不会被替换,
如果宏文本内嵌了其他宏,则也不会展开它们。
符号`"的作用告诉编译器,“”字符串文本中的参数和内嵌宏都应该被替换:
2. `` (Double Backtick)双反引号
本质上是分隔符标记,帮助编译器清楚地区分宏文本中的参数和字符串的其余部分
3 `\`" (顿号反斜杠顿号引号)
转义为双引号,只要理解为 `\`" = " 就可以了
参数化类
参数化类就是class在定义的时候添加可选的参数,用 # 标识,创建实例的时候传入参数,来创建不同的类
参数化类的作用就是提高类的可扩展性
简单的一个例子:
class stack # (type T = int, int W = 5);
T [W:0] data;
endclass : stask
stack # (bit [1:10]) S_bit;
在声明S_bit句柄时,传入的type类型为bit [1:10]即T = bit [1:10],W没有指定,则默认为5
下面是UVM类库中的uvm_component_registry类,可见该类是一个参数化类,接受一个自定义类型T和字符串为参数。
这里疑惑的是
typedef uvm_component_registry #(T,Tname) this_type;
为什么要给当前的类用typedef起一个别名呢?查阅的说法是为了表示简洁。考虑了半天感觉并不是这么简单。
自己的想法:参数化类内部,很有必要用typedef来重新声明类型,给它起个别名。因为参数化类,意味着具体的类型不确定,如果要在某个参数化类内引用当前类的类型,只用参数化类的类名是不准确的,还必须带上参数,所以有必要将类名和当前的参数重新声明为一个新类型,在类中使用。
静态变量/方法
UVM中主要用到静态变量/方法的如下特点:
- 静态变量的生命周期始于编译阶段,即一个含有静态变量的类在定义的时候,其内部的静态变量就会被分配内存
- 因为上条特点,类在没有例化对象的时候,依然可以访问内部的静态变量/方法,调用格式: 类名::静态变量/方法名
- 一个类的所有对象,共享同一个静态变量/方法,即不论类例化多少对象,静态变量只有一个
来看对应的UVM中uvm_component_registry类的下述关键代码:
这段代码是一个单例模式的典型写法,单例模式在UVM中经常用到,比如factory,coreservice_t等类中都有出现
上述单例模式得以实现,离不开静态变量me的使用。
由于me是静态变量,所以当我们在自定义的类中声明了类type_id时(注意,仅声明了句柄,没有实例化),me就会被创建,创建方式是通过调用静态函数get().
多说一句,在get()方法中也完成了自定义类的工厂注册。
实际在我们工程的整个生命周期内,每一个在工厂注册的类,都含有一个uvm_component_registry类型的成员变量,但每个类唯一例化过的uvm_component_registry类对象就是me,这也体现了单例模式。