1、function和task简介
Systemverilog和Verilog中的 function 和 task有一些区别。
1.1 verilog中
- task可以消耗时间,而function不能消耗时间。
- function不能包含一些消耗时间的语句,例如:#100n; @ ; wait()等
- fucntion不能调用task
- verilog中的function必须要有返回值,并且返回值必须被使用,例如用到赋值语句中。
- task没有返回值
1.2 SystemVerilog中
- task可以消耗时间,而function不能消耗时间。
- function不能包含一些消耗时间的语句,例如:#100n; @ ; wait()等
- function可以在特殊情况下调用task(只能在fork … join_none语句生成的线程中调用)
- function可以声明为void函数,从而没有返回值。non-void函数的返回值也不是必须要使用,如果不用返回值可以使用void进行结果转换:如下。(对于VCS仿真器,不采用下面的处理也可以)
- task没有返回值
//声明为void函数,没有返回值
function void print_state();
...
endfunction
//对于non-void函数,不使用其返回值时使用void进行结果转换
void'($fscanf(file,"%d",i));
2、function和task的声明
- 通过关键字 task xxx; … endtask 声明 task
- 通过关键字 function xxx; … endfunction 声明 function
- 一般情况下,不带参数的子程序在定义或调用时并不需要带空括号()
//第一种声明:输入输出参数在function内部声明
function test1(); //此时()可以省略
input bit [3:0] a;
output bit [7:0] b;
b=a+1;
endfunction : test1
//第二种声明:输入输出参数在()中声明
function test2(input bit[3:0] a, output bit[7:0] b);
b=a+2;
endfunction
//第三种声明:输入输出在()中声明,但是参数的类型在function内部声明
function test3(input a, output b);
bit[3:0] a;
bit[7:0] b;
bit[3:0] c=1;
b=a+c+3;
endfunction
- task的声明与function一样,以一个例子进行说明
//task的声明与function一样,以一个例子进行说明
task task_test4(input bit[3:0] a, output bit[7:0] b);
b=a+4;
endtask
2.1 调用
test1(a, result_b); 调用的实际过程为,变量a的值被copy给 test1函数的形参a,test1函数的形参b通过计算(b=a+1)得到,然后b的值被copy给变量result_b
bit [3:0] a=0;
bit [7:0] result_b, result_c;
test1(a,result_b);
$display("%d",result_b); // 1
test2(a,result_b);
$display("%d",result_b); // 2
test3(a,result_b);
$display("%d",result_b); // 4
task_test4(a,result_b);
$display("%d",result_b); // 4
2.2 参数
2.1.1 参数的方向
- 参数共有四种方向:input,output,inout,ref
- 参数缺省的类型和方向分别为: input 和 logic,如果参数前面的参数有定义,则跟随前面参数的类型和方向
task TT(a, output bit[15:0] b,c);
//a的类型为 input logic
//b的类型为 output bit[15:0]
//c的类型为 output bit[15:0]
2.1.2 ref方向类型
- 在verilog中,对参数的处理方式很简单,在子程序的开头把input或inout的值复制给本地变量,在子程序退出时,则复制output或inout的值。除了标量以外,没有任何把存储器传递给verilog子程序的方法;
- 在SystemVerilog中,参数的传递方式可以指定为引用(ref)而不是复制。
- ref是对变量(不能是net)的引用(相当于C语言中的指针),它的值是该变量最后一次赋的值。如果将一个变量连接到多个ref端口,就有可能产生竞争,因为多个模块的端口更新的是同一个变量。
- SystemVerilog允许不带ref进行数组的传递,这时数组会被复制到堆栈区中,这种操作的代价很高,除非是对特别小的数组。
- systemverilog的语言参考手册规定了ref参数只能用于带自动存储的子程序中(说明:这句话来源于绿皮书,在SV LRM 3.1a中没有找到,但是在最新的《SystemVerilog IEEE 1800-2017》中有写。然而我在VCS2016试着用static task 也没有报错,暂不清楚原因)
- ref参数在任务里可以修改变量而且修改结果对调用他的函数随时可见
- 不希望子程序改变数组的值,可以使用const ref来修饰
2.1.3 参数的缺省值
- 可以为参数指定一个缺省值,如果在调用时不指明参数,则使用缺省值。
3、 function的返回值
- void function没有返回值
- non-void function都有返回值,注意:output、inout、ref这些参数改变了变量,但这不算返回值
- 如果有return语句,则函数返回return后的结果
- 如果没有return语句,则返回与函数同名的内部变量
3.1 返回值的引用
- 直接将函数调用语句作为右式赋值给左边的变量即可
//以上面的两个function为例进行引用函数的返回值说明
initial begin
logic [7:0] a,b;
logic [15:0] result;
a=2;
b=3;
$display(myfunc1(a,b)); //输出 5
result = myfunc1(a,b);
$display(result); //输出 5
a=3;
b=4;
$display(myfunc2(a,b)); //输出 11
result = myfunc2(a,b);
$display(result); //输出 11
end
3.2 函数返回值的类型
- 默认为logic类型,也可以自己声明类型。myfunc1和myfunc2的返回值类型就是 logic [15:0]
- 如果什么都没声明,则返回值为 logic 类型的标量
function myfunc3();
endfunction
//没有给myfunc3进行赋值,所以logic类型默认值为1'bx
$display("%b",myfunc3()); //输出 x
3.3 task中return语句
- task中的return语句可以让task立刻结束,而不用执行到task的最后一句
//如果len小于0,那么task打印后遇到return语句立刻返回,不会执行其他代码
task load_array(int len, ref int array[]);
if(len < 0) begin
$display("bad len");
return;
//其他代码
endtask