任务和函数,可以把一个很大的程序模块分解成许多较小的任务和函数便于理解和调试。在编写Testbench时用的较多,而在写可综合的代码时要少用。
1 task详细说明
1.1 任务定义
task <任务名> (
<端口及数据类型声明>
);
<语句1>;
<语句2>;
<语句3>;
...
endtask
在定义一个task时,必须注意以下几点:
- 任务定义结构不能出现在任何一个过程块内;
- 一个task可以没有输入/输出端口,当然也可以有;
- 一个task可以没有返回值,也可以通过输出端口或双向端口返回一个或多个值;
- 除任务参数外,task还能够访问调用任务的模块中定义的任何变量;
- 在任务定义的描述语句中,可以出现不可综合操作符合语句,但这样会造成任务不可综合;
- 在任务定义结构中不可出现initial和always语句;
1.2 任务调用
<任务名> (端口1, 端口2, 端口3, ..., 端口n);
在调用task时,必须注意一下几点:
- task调用是过程性语句,因此只能出现在always过程块和initial过程块中,调用task的输入与输出参数必须是寄存器类型的;
- task调用语句中的列表必须与任务定义时的输入、输出和双向端口参数说明的顺序相匹配。
- 在调用task时,参数要按值传递,而不能按地址传递(和其他语言的不同);
- 在一个task中,可也直接访问上一级调用模块中的任何寄存器;
- 可以使用循环中断控制语句disable来中断任务执行,在task被中断后,程序流程将返回到调用task调用的地方继续向下执行。
- 可综合任务只能实现组合逻辑,也就是说调用可综合任务的时间为0。
2 function详细说明
2.1 函数定义
function <返回值类型和范围> (函数名);
<端口定义>
begin
<语句1>;
...
end
endfunction
函数定义例子:
function [7:0] getbyte; // 函数名,也是返回寄存器
input [15:0] address; // 端口定义
begin
<说明语句> //从地址字中提取低字节的程序
getbyte = result_expression;
end
endfunction
定义function时,要注意以下几点:
- function定义结构不能出现在任意一个过程块(always块或者initial块)的内部;
- function定义不能包括有任何时间控制语句,即任何用#,@或wait来标识的语句;
- 定义function时至少要有一个输入端口,不能包含输出端口;
- 函数不能启动任务;
- 定义function时,在function内部隐式地将函数名声明成一个寄存器变量,就是函数有且仅有一个返回值即函数名。
如果没有指定的返回值的宽度,function将缺省返回1位二进制数。
2.2 函数调用
<函数名> (<输入表达式1>, ..., <输入表达式n>);
function的调用既可以出现在过程块中又可以出现在assign连续赋值语句之中;另外,function定义中声明的所有局部变量寄存器都是静态的,即function中的局部寄存器在function的多个调用之间保持他们的值。
3 区别
- 任务种中可以使用#、@或wait等时间控制语句,函数不可以;
- 函数不能启动任务,而任务可以启动其他任务和函数;
- 函数至少要有一个输入变量,不能包含输出变量,任务可以没有或有多个任何类型的变量;
- 函数有且只有一个返回值,任务没有返回值,但可以有输出端口(寄存器),也可以没有;
- 任务可以访问调用它的模块中的任何变量,函数不可以;
4 深入理解
- task相当于一个过程,因此是不能调用always、initial这样的过程块的,当然也不能调用module。
- task语句是可综合的,但是只能实现组合逻辑,如果包含了时间控制语句,比如#、@、wait等,就不能综合了。这样可综合的task就相当于把一些组合逻辑封装起来,达到简化代码的作用。