文章目录
在sv的class中定义的方法分为两种:.function 和 task。下面将介绍它们的特点和声明方式,以及它们之间的异同。
一、function
1.1 function的特点
- function中不可以内置耗时语句,被调用时是立即返回的,即不消耗仿真时间;
- 函数至少要有一个输入变量,可以无返回值或一个返回值;
- 函数参数列表中的变量缺省数据类型为logic类型;
- 函数参数参数列表中的变量如果没有规定反向,默认是 input方向;
- 如果函数有返回值,函数的返回值类型需要在函数定义时指定,且只能通过return返回一个值,或者通过赋值给与函数同名的内部变量(用的很少);
- 如果函数没有返回值,要在函数定义时指定为 void ;
- 函数不可以调用task,因为task可能含有耗时语句,而函数不支持耗时语句。
1.2 function的定义
① 参数方向
参数方向可以是:
- input
- output
- inout
- ref ,即引用,后面会介绍使用方法
// input int x ;
// input int y;
function logic [15:0] myfunc1(int x, int y);
...
endfunction
function logic [15:0] myfunc2;
input int x;
input int y;
...
endfunction
//返回的参数类型logic [15:0]
function logic [15:0] myfunc3(int a, int b, output logic [15:0] u, v);
...
endfunction
② 返回值
有返回值时,function定义如下:
//方式一:通过将一个值赋给与函数同名的内部变量;
//这种用的很少,在SV绿皮书中介绍深拷贝章节就是用的这种做法
function [15:0] myfunc1 (input [7:0] x,y);
myfunc1 = x * y - 1; //
endfunction
//方式二:用return返回
function [15:0] myfunc2 (input [7:0] x,y);
return x * y - 1; // 用return返回值
endfunction
//方式三:用ref
function void my_func3(ref [7:0]out,input [7:0] x,y);
out = x * y - 1;
endfunction
无返回值时,function定义如下:
myprint(a);
function void myprint (int a);
...
endfunction
如果不想要函数的返回值,还可以使用强制转换的方式,将一个有返回值的函数丢弃其返回值,如下:
void'($cast(tr,h));
//正常情况下,如果动态转换cast失败与成功,都会有返回值的。
//当我们不关心这个返回值时,就可以采用强制转换为void形式
③ 静态和动态function
可以参考我之前的文章:静态和动态一文。
二、task
2.1 task的特点
- task 可以内置耗时语句,也就是当调用task时,这个结果可以不是立即返回的;
- task 既可以调用function,也可以调用task;
- task不可以用 return 返回值;
- task 可以在参数列表中定义参数方向,以此来返回值,可以返回一个或多个值;
- task的参数列表中的参数,如果不规定方向,默认是input;
- 缺省的参数数据类型是 logic类型;
2.2 task 的定义
① 参数方向
参数方向可以是:
- input
- output
- inout
- ref ,即引用,后面会介绍使用方法
如下,是两种带方向定义的task;
//input a,b ;默认是input方向
//output logic [15:0] u,
//output logic [15:0] v;
task mytask1(a, b, output logic [15:0] u, v);
#10ns;
endtask
task mytask2;
input a;
input b;
output logic [15:0] u;
output logic v;
...
endtask
注意:虽然参数的默认方向是input,但如果你有些参数定义了方向,为了便于阅读和防止出错,那最好就把所有参数的方向都定义一下。
实例:
module traffic_lights;
logic clock, red, green;
parameter on = 1, off = 0;
parameter red_tics = 350, green_tics = 200;
initial red = off;
initial green = off;
always begin // waveform for the clock
#100 clock = 0;
#100 clock = 1;
end
always begin
red = on; // 打开红灯
light(red, red_tics); // 等待red_tics周期后,关闭红灯
green = on; // 打开绿灯
light(green, green_tics); // 等待green_tics周期后,关闭绿灯
end
//首先会等到tics周期的时钟,然后会将传递进来相应颜色的灯关闭
task light (output color, input [31:0] tics);
repeat (tics) @ (posedge clock);
color = off; //关闭相应颜色的灯
endtask: light
endmodule: traffic_lights
② ref的使用
以下是直接将数组传递到task中。这种方式会直接将这个a,b数组复制到堆栈中,然后再传递到方法中。
//input [3:0][7:0] a
//input [3:0][7:0] b [3:0]
//output [3:0][7:0] y[1:0]
task mytask3(input [3:0][7:0] a, b[3:0], output [3:0][7:0] y[1:0]);
...
endtask
如果数组占用内存比较小,倒还无所谓。如果是一个很大的数组,那就会影响内存了。这时候就可以使用 ref 来传递数组。ref的特点是:
- ref是变量(但不能是net)的引用,它的值是该变量最后一次赋值的值。
- 但是如果将一个变量连接到多个ref端口,就可能产生竞争的问题,因为多个模块的端口都有可能更新同一个变量;
- 参数列表中如果加了ref,就不能再对齐限定方向,如ref input [7:0] arr[ ] ,这是非法的形式。
task automatic show ( const ref byte data [] );
for ( int j = 0; j < data.size ; j++ )
$display( data[j] ); // data can be read but not written
endtask
使用ref的优点:
- 参数传递的方式是引用,而不是复制。如果不用ref,直接引用数组,会被复制在堆栈区,影响性能;
- 在任务中修改变量的结果对调用它的函数随时可见;
- const ref 可以使引用的对象在子程序中不被改变;
③ 静态和动态task
关于静态和动态的可以先看我的博客:静态和动态一文。
- 在module, interface, program 和 package的 task 默认是static的。
- 如果一个task被声明为static,那么在整个仿真过程中,这个task都是可以被其他可见的,会一直存在。
- 如果一个task是static/automatic 的,那么其内所有的成员变量也是static/automatic ;
- 如果想在module内声明一个automatic的task,需要显示的加上automatic修饰,如下:
task automatic my_auto_task(x,output y);
...
endtask
④参数的缺省值
有的时候为了在原有方法中想要添加更多的控制,参数类表中的数据可能会很多,但是为了方便我们在调用时,不用给每个参数都传递一个值,这时就可以给参数列表中的参数设置默认值的方式,如下:
// j和data有默认值,K没有设置默认值
task read(int j = 0, int k, int data = 1 );
...
endtask
read( , 5 ); // is equivalent to read( 0, 5, 1 );
read( 2, 5 ); // is equivalent to read( 2, 5, 1 );
read( , 5, ); // is equivalent to read( 0, 5, 1 );
read( , 5, 7 ); // is equivalent to read( 0, 5, 7 );
read( 1, 5, 2 ); // is equivalent to read( 1, 5, 2 );
read( ); // 错误声明; k 没有设置默认值
read( 1, , 7 ); // 错误声明; k 没有设置默认值
三、function 和 task 的区别
可以直接看我之前的博客:function 和 task 的区别。