书接上回:
sv标准研读第一章-综述
sv标准研读第二章-标准引用
sv标准研读第三章-设计和验证的building block
sv标准研读第四章-时间调度机制
sv标准研读第五章-词法
sv标准研读第六章-数据类型
sv标准研读第七章-聚合数据类型
sv标准研读第八章-class
sv标准解读第九章-进程
sv标准研读第十章-赋值语句
sv标准研读第十一章-操作符和表达式
sv标准研读第十二章-过程性编程语句
第13章 task和function
13.1 总览
-task声明
-function声明
-调用task和function
13.2 概述
Task和function的区别:
-function不消耗仿真时间,task可以消耗仿真时间
-function里不可以调用task,但是task里可以调用function和task
-function可以return一个返回值,但是task不可以
-function可以放在一个表达式里当做一个操作数,这个操作数的值就是function的返回值
13.3 task
Task声明格式:
声明task时,形式参数可以指明数据类型和传输方向,如下:
方向有下面四种:
当没有指明方向时,默认方向是input。一旦一个参数指明了是某个方向,后面没有指明方向的参数的方向和这个参数的方向保持一致。举例:
a/b的方向是input,u/v的方向是output。
当没有指明数据类型时,默认数据类型是logic。一旦一个参数指明了是某个数据类型,后面没有指明数据类型的参数的数据类型和这个参数的数据类型保持一致。
Task的参数还可以是数组,举例:
13.3.1 static和automatic task
在module、interface、program、package里定义的task默认是static。
把task变成automatic有以下两种方式:
-声明时加上关键字automatic
-如果module、interface、program、package是automatic的,那里面定义的task也是automatic的
在class里定义的task总是automatic的。
可以在automatic task里声明一部分变量是static的,另外也可以在static task里声明一部分变量是automatic的。
13.3.2 task的内存使用和并发激活
Static task里面的变量是所有task实例共享的,而automatic task里面的变量对于不同task实例都是相互独立的。因此,automatic task里的变量在该task之外是不能调用的,它们的生命周期只存在于该task内部。
13.4 function
Function使用时有一些限制:
-function里面不能出现时间控制语句,包括:#、##、@、fork-join、fork-join_any、wait、wait_order、expect
-function里不能调用task
声明语法如下:
Function如果有返回值,默认是logic类型,function也可以没有返回值。
声明举例:
方向和task一样也有四种:
当没有指明方向时,默认方向是input。一旦一个参数指明了是某个方向,后面没有指明方向的参数的方向和这个参数的方向保持一致。
当没有指明数据类型时,默认数据类型是logic。一旦一个参数指明了是某个数据类型,后面没有指明数据类型的参数的数据类型和这个参数的数据类型保持一致。
function的参数还可以是数组。
在事件表达式、过程连续赋值表达式、非过程语句表达式中,不能调用带有output、inout、ref参数的function,但是可以调用带有const ref参数的function。
Function里也可以不写任何语句,此时返回的就是与函数同名的变量的值。
13.4.1 返回值和void function
Function隐式声明了一个变量,该变量与function的名字相同。Function的返回值可以通过两种形式返回,一种就是通过return语句返回,另一种就是通过将值赋给与函数同名的变量。举例:
Function的返回值可以是结构体或者union。
Function的返回值也可以是void,表示没有返回值。
如果function有返回值,但是表达式中不使用返回值,那么可以用void进行强制转换,如下:
13.4.2 static和automatic function
在module、interface、program、package里定义的function默认是static的,因此在所有function实例中这些function里的变量是共享的,里面的变量也是static的。
可以通过下面两种方式将function变成automatic的:
-在function声明时加上关键字automatic
-如果module、interface、program、package是automatic的,那么里面的function默认也是automatic的
在class里面定义的function总是automatic的。
Static function里面的变量默认是static的,但是也可以显式声明为automatic的,同理,automatic function里面的变量默认是automatic的,但是也可以显式声明为static的。
13.4.3 常量函数
常量函数有以下约束:
-常量函数不能有output、inout、ref参数
-void function不是常量函数
-DPI import函数不是常量函数
-常量函数在return语句后面不能包含事件调度语句
-常量函数不能有任何fork语句
-常量函数不能有hierarchical引用
-常量函数中调用的函数也必须是常量函数
-常量表达式中可以调用的系统function包括:$bits和数组查询系统函数,但是调用其他系统function是非法的
-常量函数中调用系统task将被忽略
-常量函数可以引用在package或者$unit里定义的参数
-如果常量函数里的形参受到defparam语句的影响,那么返回值是不确定的,这可能造成不可预期的后果
-generate块里不能声明常量函数
-常量函数的参数可以有默认值,但参数必须都是常量表达式
常量函数的调用发生在elaboration阶段。
13.4.4 函数调用产生的后台进程
因为function是无延迟的,所以调用function的进程是立即返回的。那么,在function里面可以出现的语句包括:非阻塞赋值、event触发、clocking驱动、fork join_none。
13.5 子程序调用和参数传递
子程序的调用可以通过位置或者名字传参,参数也可以设置默认值。
13.5.1 按值传递
按值传递的方式是调用子程序进行传参的默认方式。参数在子例程中会被复制,子例程中操作的对象也是复制后的对象,因此,如果在子例程中对参数进行了修改,不影响子例程外参数的值。考虑到内存问题,如果参数占用内存很大,建议将该参数声明为一个共享的参数,而不要通过传参的方式交给子例程处理,否则,复制操作会使得内存占用很大。
13.5.2 按引用传递
按引用传递指的是在参数声明时加上ref关键字,那么就不会进行复制操作,子例程里使用的参数就是子例程外声明的参数。Static的function或者task不允许出现ref传递参数。因此,子例程中如果修改了该参数,也会影响子例程外该参数的值。
按引用传递的参数只能是下面几种:
-变量
-class property
-unpacked结构体的成员
-unpacked数组的成员
Nets或者net的bit-select/part-select不允许出现在ref参数中。
如果想要ref 参数不被子例程修改,那么可以加上const关键字,此时该参数就会变成一个只读变量,强行修改参数的值会报编译错误,如下:
13.5.3 参数的默认值
格式如下:
调用子例程时,声明了默认值的参数可以不传递实参,但是没有声明默认值的参数必须传递实参。举例:
上面j/data都声明了默认值,那么调用时有以下几种情况:
13.5.4 按名字绑定参数
Sv支持按照位置或者名字绑定参数,举例:
如果在调用子例程时既使用了按位置绑定参数的方式,又使用了按名字绑定参数的方式,那么前者一定要放在后者前面,如下:
13.5.5 可选的参数列表
如果function不需要传参,那么()是可以省略的。
13.6 import function和export function
可以通过DPI的当时导入C语言里定义的函数,同样也可以导出sv的函数到C语言环境使用,参考第35章DPI的介绍。
13.7 task和function的名字
Task名字和function名字的解析具体见第23章。
13.8 参数化的task和function
参数化task和function要结合类的静态方法来实现。举例:
上面的例子中,参数化function的实现用到了参数类的参数DECODE_W,使得参数的传递位宽能够得到灵活的控制。