task 和 function 说明语句分别用来定义任务和函数,利用任务和函数可以把函数模块分成许多小的任务和函数便于理解和调试。任务和函数往往还是大的程序模块在不同地点多次用到的相同的程序段。输入、输出和总线信号的数据可以传入、传出任务和函数。
task 和 function 的不同:
1)函数只能与主模块共用同一个仿真的时间单位,而任务可以自己定义自己的仿真时间单位。
2)函数不能启动任务,但是可以调用其它函数,但是任务可以调用其他函数和任务;
3)函数至少要有一个输入变量,而任务可以没有或者有多个任何类型的变量。
4)函数返回一个值,而任务不返回任何值。
函数的目的值通过一个返回值对输入的信号进行响应。而任务可以支持多种目的,能计算多个结果值,这些值只能通过任务的输出端口或者总线端口输出。
A) task说明语句
如果传给任务的变量和任务完成后接受结果的变量已经定义,就可以用一条语句启动任务,任务完成以后控制就传回启动过程。如果任务内部有定时设置,则启动的时间可以与控制返回的时间不同。
1)任务的定义;
task <任务名>;
<端口及数据类型声明语句>
<语句1>
...
<语句n>
endtask
2)任务的调用以及变量的传递:
任务定义;
task my_task;
input a,b;
inout c;
output d,e;
.... //执行任务的相应语句
c=foo1; //对任务的变量赋初始值
b=foo2;
e=foo3;
endtask
任务的调用: my_task(v,w,x,y,z)
任务调用变量(v,w,x,y,z)和任务定义的I/O变量(a,b,c,d,e)是一一对应的。任务启动时,v,w和x的值给了a b c,结束时c,d,e的值返回给x,y,z。
B)function说明语句
函数的目的是返回一个用于表达式的值。定义函数的语法:
function <返回值的类型或范围>函数名;
<端口说明语句>
<变量类型说明语句>
begin
<语句>
...
end
endfunction
在这里,<返回值的类型或范围>可以不定义,如果默认则代表一位寄存器类型数据。
1)函数返回的值:函数的定义蕴含声明了一个与函数同名的,函数内部的寄存器,其位数与定义的相同;
2)函数的调用:函数的调用是通过将函数作为表达式中的操作数来实现的。例:word=control ? {getbyte(msbyte),getbyte(lsbyte)} : 0;
3)函数的使用规则:
a)函数的定义不能包含任何的时间控制语句,及任何用# ,@,或wait来标识的语句。
b)函数不能启动任务,定义函数至少输入一个输入参量;
c)函数的定义中必须有一条赋值语句给函数中的一个内部变量赋以函数的值,该内部变量具有和函数名一样的名字。
d)函数的输入变脸不能像模块的端口那样列在函数名后面的括弧内,在声明输入时把这些输入端口列出即可。函数不能被禁用。
在上例中,函数的定义也可以采用C语言的风格定义,如:function cal_paruty(input [31:0] address);
Verilog中的函数是不能进行递归调用的。在设计模块中如果某个函数在两个不同位置同时并发被调用,由于这两个调用同时对同一地址进行操作,那么计算结果将是不定值。但是可以在函数声明时使用automatic关键字,那么在调用时,仿真器将为函数分配不同的地址。每个函数调用各自的地址。因此:自动函数中声明的局部变量不能通过层次名进行访问,而自动函数本身可以通过层次名进行调用。
例:factorial=factorial(n-1)*n;
关于函数和任务的小结:
1)任务和函数都是用来对设计中多处使用的公共代码进行定义,使用任务和函数增强模块的可读性和可维护性。
2)可重入任务用关键词automatic定义,他的每一次调用都对不同的地址进行操作。因此可以多次并发调用时,也可得到正确的结果。
3)函数只能返回一个值,并且至少需要一个输入变量;在函数中不能使用延迟、事件和时序控制结构,但是可以调用其他函数,不能调用任务。
4)任务可以具有任意个输入、输入输出/输出、输出变量,在任务中可以使用延时、事件和时序控制结构,可以自定义时钟,可以调用其他函数和任务。
5)递归函数必须使用automatic关键词进行定义,递归函数的每一次调用都调用不同的地址空间,因此这种函数的递归调用和并发调用可以得到正确结果。