任务、函数以及void函数
verilog中 task(任务)可以有消耗时间的语句,比如#100的时延,function(函数)中不能带有@(posedge clock)、wait(ready)的阻塞语句,也不能调用task,function必须有返回值,且返回值必须被使用。
SV中允许函数调用任务,但只能在fork..........jon_none语句生成的线程调用。
function void print_state (.......);
$display("@ %0t:state = %s",$time,cur_state.name());
endfunction
void函数常用于没有函数返回值。
当任务和函数不带参数时,可以省去空括号()。
建议对所有子程序参数的声明带上类型和方向,如下所示:
task arb( input logic[31:0] a,
input logic[7:0] b,
output bit[7:0] c
);
如上所示,在SV中参数传递的方式可以为引用而不是复制,下面为ref参数类型:
task arc( ref logic[31:0] a,
const ref logic[7:0] b[10],
output bit[7:0] c
);
如上所示,ref参数和const ref参数,向子程序传递参数因尽量使用ref,使用const ref参数则不允许子程序改变数组的值,使用ref参数时,当在任务里修改变量而且修改变量对调用它的函数随时可见,即任务中ref参数出现了修改,不需要task全部运行完,外部调用程序也可以发现输出的ref参数发生了变化。
采用名字进行参数传递,如下:
task arf( input int a = 1,
input int b = 2,
input int c = 3,
input int d = 4,
);
$display("a =%d,b=%d,c=%d,d=%d",a,b,c,d);
endtask
arf(); //使用abcd的默认值 1 2 3 4
arf(.b(10)) //指定b为10,,其余默认值 1 10 3 4
arf(,10,.d(22)) //110 3 22
子程序的返回
在task中使用return语句返回值。
函数返回一个数组,第一种方式:定义一个数组类型,在函数的声明中使用该类型,如下所示:
typedef int fix_array10[10]; //定义一个数组
fix_array10 f10;
function fix_array10 init(int st);
foreach (init[i])
init[i] = i + st;
endfunction
initial begin
f10 = init(10);
end
函数init创建了一个数组,当数组过大可能出现性能的问题。
第二种方式通过引用来进行数组参数的传递。
function void init(ref int f(10),int st);
foreach (f[i])
f[i] = i + st;
endfunction
int f[10];
initial begin
init(f,10)
end
自动存储
SV中在模块(module)和program中的子程序前加入automatic,如下所示:
program automatic test;
task asd(input logic[31:0]a,input b,output logic d);
.............
endtask
..............
endprogram
加了automatic因此可以对task同时进行多次的调用,否则打二次调用参数会覆盖。
时间值
$timeformat的四个参数为:时间标度(-9代表钠秒,-12代表皮秒),小数点后的数据精度,时间值之后的后缀字符串,显示数值的最小精度。
$timeformat(-12,3,"ns",5);
#2.5ns $display("%t",$realtime); //2.500ns
$time返回根据模块时间精度要求的进行舍入的整数。
$realtime返回一个带小数的完整实数。