task和function说明语句分别用来定义任务和函数,利用任务和函数可以把一个很大的程序模块分解成较小的任务和函数便于理解和调试。输入、输出和总线信号的值可以传入、传出任务和函数。任务和函数往往还是大的程序模块中在不同地点多次用到的相同的程序段。使用task和function语句可以简化程序的结构,使程序明白易懂,是编写较大型模块的基本功。
1.task和function说明语句的不同点:
任务和函数有些不同,主要有四点:
(1)函数只能与主模块共用同一个仿真时间单位,而任务可以定义自己的仿真时间单位。
(2)函数不能启动任务,而任务能启动其他任务和函数。
(3)函数至少要有一个输入变量,而任务可以没有或有多个任务类型的变量。
(4)函数返回一个值,而任务则不返回值。
函数的目的是通过返回一个值来响应输入信号的值,任务却能支持多种目的,能计算多个结果值,这些结果值只能通过被调用的任务的输出或总线端口送出。Verilog HDL模块使用函数时是把它当作表达式中的操作符,这个操作的结果值就是这个函数的返回值。
例如,定义一任务或函数对一个16位的字进行操作让高字节与低字节互换,把它变为另一个字(假定这个任务或函数名为:switch_bytes)。
任务返回的新字是通过输出端口的变量,因此16位字字节互换任务的调用源码是:
switch_bytes(old_word,new_word);
任务switch_bytes把输入old_word字的高、低字节互换放入new_word端口输出。
而函数返回的新字是通过函数本身的返回值。因此16位字字节呼唤函数的调用源码是:
new_word = switch_bytes(old_word);
2.task说明语句:
如果传给任务的变量值和任务完成后接收结果的变量已定义,就可以用一条语句启动任务,任务完成后控制就返回启动过程。如任务内部有定时控制,则启动的时间可以与控制返回的时间不同。任务可以启动其他任务,其他任务又可以启动别的任务,可以启动的任务数是没有限制的。不管有多少任务启动,只有当所有的启动任务完成以后,控制才能返回。
(1)任务的定义:
定义任务的语法为:
task <任务名>
<端口及数据类型声明语句>
<语句1>
<语句2>
·
·
·
<语句n>
endtask
这些声明语句的语法与模块定义中的对应声明语句的语法是一致的。
(2)任务的调用及变量的传递
启动任务并传递输入、输出变量的声明语句的语法为:
任务的调用:
<任务名>(端口1,端口2,···,端口n);
【例1】怎样定义任务和调用任务
task my_task;
input a,b;
inout c;
output d,e;
<语句> //执行任务工作相应的语句
c = foo1; //赋初始值
d = foo2; //对任务的输出变量赋值
e = foo3;
endtask
【例2】描述红绿黄交通灯行为的Verilog模块,其中使用了任务
module triffic_lights;
reg clock,red,amber,green;
parameter on = 1,off = 0,red_tics = 350;
amber_tics = 30,green_tics = 200;
//交通灯初始化
initial red = off;
initial amber = off;
initial green = off;
//交通灯控制时序
always
begin
red = on; //开红灯
light(red,red_tics); //调用等待任务
green = on; //开绿灯
light(green,green_tics); //等待
amber = on; //开黄灯
light(amber,amber_tics); //等待
end
//定义交通灯开启时间的任务
task light;
output color;
input[31:0]tics;
begin
repeat(tics)
@(posedge clock); //等待tics个时钟的上升沿
color = off;
end
endtask
//产生时钟脉冲的always块
always
begin
#100 clock = 0;
#100 clock = 1;
end
endmodule
这个例子描述了一个简单的交通灯的时序控制,并且该交通灯有它自己的时钟产生器。在这里,该模块只是一个行为模块,不能综合成电路网表。
3.function说明语句:
函数的目的是返回一个用于表达式的值。
(1)定义函数的语法:
function <返回值的类型或范围> (函数名)
<端口说明语句>
<变量类型说明语句> begin
<语句>
...
end
endfunction
<返回值的类型或范围>这一项是可选项,如缺省则返回值为一位寄存器类型数据.
【例2】
function [7:0] getbyte;
input [15:0] address;
begin
<说明语句> //从地址字节中提取低字节的程序
getbyte = result_expression; //把结果赋予函数的返回字节
end
endfunction
(2)函数的返回的值:
函数的定义蕴含声明了与函数同名的、函数内部的寄存器。如在函数的声明语句中<返回值的类型或范围>为默认,则这个寄存器是一位;否则是与函数定义中<返回值的类型或范围>一致的寄存器。函数的定义把函数返回值所赋值寄存器的名称初始化为与函数同名的内部变量。上面的例2说明这个概念:getbyte被赋予的值就是函数的返回值。
(3)函数的调用:
函数的调用是通过将函数作为表达式中的操作数来实现的,其调用格式为:
<函数名> (<表达式>,···,<表达式>)
其中函数名作为确认符。
两次调用函数getbyte,把两次调用产生的值进行位拼接运算,以生成一个字。
word = control?{getbyte(msbyte),getbyte(lsbyte)}:0;
(4)函数的使用规则:
与任务相比函数的使用有较多的约束,函数的使用规则为:
- 函数的定义不能包含有任何的时间控制语句,即任何用#、@或wait来标识的语句。
- 函数不能启动任务。
- 定义函数时至少要有一个输入参数。
- 在函数的定义中必须有一条赋值语句给函数中的一个内部变量赋以函数的结果值,该内部变量。具有和函数名相同的名字。
(5)举例说明:
下面,定义一个可进行阶乘运算的名为factorial函数,该函数返回一个32位的寄存器类型的值,还可后向调用自身,并且打印出部分结果值。
【例3】阶乘函数的定义和调用:
module tryfact;
//函数的定义------------------------------
function [31:0] factorial;
input [3:0] operand;
reg [3:0] index;
begin
factorial = 1; //0的阶乘为1,1的阶乘也为1
for(index = 2; index <= operand; index = index +1) //for循环语句
factorial = index * factorial;
end
endfunction
//函数的测试---------------------------------------------------------
reg [31:0] result;
reg [3:0] n;
initial
begin
result = 1;
for(n=2;n<=9;n=n+1) //循环语句
begin
$display("Partial result n= %d result= %d",n,result)
result = n*factorial(n)/((n*2)+1);
end
$display("Finalresult = %d",result); //打印出结果
end
endmodule //模块结束