函数是零时间执行结构。与任务不同,函数具有确保它们返回而不暂停启用它们的进程的限制。因此,函数不能包含任何耗时的语句。从这个角度来看,一个函数不能有以下运算符:
#, ##, @, fork..join, fork..join_any, wait, wait_order or expect.
函数允许不阻塞的语句,例如,允许非阻塞分配、命名事件触发器、fork_join_none。
由于函数中不允许时间控制,因此无论这些任务是否包含时间控制语句,它都无法调用任务。
最简单的语法形式:
function <automatic | static> <data_type_or_implicit_or_void> function_name function_body endfunction
返回类型可以是显式的 data_type 或“void”。例如:
function logic [2:0] getStatus (int a, int b); …. endfunction
返回类型可以是显式的 data_type 或“void”。 那么如果是隐式,它返回什么?
例如:
function logic [2:0] getStatus (int a, int b); …. endfunction
也可以写成(将参数定义在了函数体中):
function logic [2:0] getStatus; input int a; input int b; …. endfunction
我们看到函数声明默认为正式方向“输入”。一旦给出了方向,随后的形式默认为相同的方向。例如:
function logic [2:0] getParity (int a, int b, output logic [2:0] u, v);
在这个例子中,“a”和“b”是输入,而“u”和“v”是输出。这样的形式参数与task的参数相同。
每个形式参数都有一个可以显式指定或从前一个参数继承的数据类型。默认数据类型是“logic”。
如果函数体没有任何内容: 函数返回与函数同名的隐式变量的当前
示例:
module mTask( ); int data, address; function int func1 (inout int dataIO, input int addressIN ); addressIN = addressIN + 1; dataIO = dataIO + 1; $display($stime,,,"\t addressIN = %h dataIO=%0d", addressIN, dataIO); endfunction initial begin address = 0; data = 9; #1; $display($stime,,, "CALL func1"); void '(func1 ( data, address)); //function call as a statement end endmodule
Simulation log:
1 CALL func1
1 addressIN=00000001 dataIO=10
V C S S i m u l a t i o n R e p o r t
示例:
module mFunc( ); int data, address; int newval; int add; function int func1 (inout int dataIO, input int addressIN ); func1 = addressIN + dataIO; //return value assigned to function name //OR //return addressIN + dataIO; //return value specified using 'return' statement endfunction initial begin address = 1; data = 9; add= 10; #1; newval = add + func1 ( data, address); $display($stime,,, "newval = %0d",newval); end endmodule
Simulation log:
1 newval = 20
V C S S i m u l a t i o n R e p o r t
就像“automatic”task 一样,也可以有“automatic”function。“automatic”与“static”的规则和工作方式与任务的规则和工作方式几乎相同。
默认情况下,函数是“静态的”。必须在函数声明中使用关键字“automatic”以使其automatic。请注意,就像任务一样,在类中定义的函数始终是automatic的。静态意味着为函数的所有变量分配一次内存(对于例程本地的参数或变量没有调用堆栈)并为函数的每次调用共享。
可以在“静态”函数中将局部变量声明为“自动”,或者在“自动”函数中声明为“静态”。
示例:
module funcAuto; integer result; function automatic integer factorial (input [31:0] operand); if (operand > 1) factorial = factorial (operand - 1) * operand; else factorial = 1; endfunction: factorial initial begin for (int n = 0; n <= 7; n++) begin result = factorial(n); $display("%0d factorial=%0d", n, result); end end endmodule
Simulation log:
Before calling function : a=2 result=0
After calling function : a=2 result=70
SystemVerilog 允许通过两种方式将参数传递给任务和函数:通过值或通过引用。参数可以按名称或位置绑定(就像模块实例化那样)。还可以为任务或函数参数提供默认值。
通过引用传递的参数不会复制到任务/功能区;相反,对原始参数的引用被传递给子例程。然后子例程可以通过引用访问参数数据。
通常,声明为输入的任务或函数的参数在进入例程时按值复制,而声明为输出的参数在从例程返回时按值复制。 Inout 参数被复制两次,一次是在输入时,一次是在从例程返回时。
用 ref 声明的参数。不是复制的,而是对调用例程时使用的实际参数的引用。
function/task ( ref type argument);
由于任务/函数中的参数指向(引用)原始参数,因此对子例程中参数的任何更改都将在外部可见。通过引用传递的优点之一是在处理大型数组时。通过值传递,将整个数组复制到子例程区域,而通过引用传递,只传递对数组的引用。这有利于提高性能
如果不希望在任务/函数中更改“ref”参数,则将其声明为“const ref”。任何更改“const ref”参数值的尝试都将导致编译错误。
module top; int a, result; initial begin a = $urandom % 6; $display ("Before calling function : a=%0d result=%0d", a, result); result = mult(a); $display ("After calling function : a=%0d result=%0d", a, result); end function int mult(ref int a); a = a + 5; return a * 10; endfunction endmodule
Simulation log:
Before calling function : a=2 result=0
After calling function : a=7 result=70
合法的引用传递是包括:变量、类属性、解压缩(unpack)结构的成员或解压缩数组的元素。但是通过引用传递nets是不合法的。