目录
一.task和function说明语句的区别
二.任务(task)
1.任务定义
2.任务调用及变量传递
三.函数(function)
1.函数定义语法
2.函数返回值
3.函数调用
4.函数使用规则
一.task和function说明语句的区别
task和function说明语句分别用来定义任务和函数。利用任务和函数可以把一个很大程序模块分解为许多较小的任务和函数便于理解和调试。输入、输出和总线信号的值可以传入或传出任务和函数。
任务和函数往往是大程序模块中在不同地点多次用到的相同程序端。任务和函数有些不同,主要的不同有以下4点:
- 函数只能与主模块共同用一个仿真时间单位,而任务可以定义自己的仿真时间单位
- 函数不能启动任务,而任务能启动其它任务和函数
- 函数至少要有一个输入变量,而任务可以没有或有多个任何类型的变量
- 函数返回一个值,而任务则不返回值
函数的目的是通过返回一个值来响应输入信号的值。任务确能支持多种目的,能计算多个结果值,这些结果值只能通过被调用的任务的输出或总线端口送出。Verilog模块使用函数时是把它当作表达式中的操作符,这个操作的结果值就是这个函数的返回值。
二.任务(task)
如果传给任务的变量值和任务完成后接收结果的变量已定义,就可以用一条语句启动任务。任务完成以后控制就传回启动过程。如任务内部有定时控制,则启动的时间可以与控制返回的时间不同。
任务可以启动其它任务,其它任务又可以启动别的任务,可以启动的任务数是没有限制的,不管有多少任务启动,只有当所有的启动任务完成以后,控制才能返回。
任务就是一段封装在“task-endtask”之间的程序。任务是通过调用来执行的,而且只有在调用时才执行,如果定义了任务,但是在整个过程中都没有调用它,那么这个任务是不会执行的。调用某个任务时可能需要它处理某些数据并返回操作结果,所以任务应当有接收数据的输入端和返回数据的输出端。另外,任务可以彼此调用,而且任务内还可以调用函数。
1.任务定义
定义语法为:
- 任务:
- task <任务名>;
- <端口及数据类型声明语句>
- <语句1>
- <语句2>
- ...
- <语句n>
- endtask
2.任务调用及变量传递
调用任务并传递输入/输出变量的声明语句的语法如下:
<任务名> (端口1,端口2,...,端口n);
任务定义如下所示:
- task my_task;
- input a,b;
- input c;
- output d,e;
- ...
- <语句> //执行任务工作相应的语句
- ...
- c = foo1; //赋初始值
- d = 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。
- module traffic_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(color,tics); //定义交通灯开启时间的任务
- output color;
- input [31:0] tics;
- begin
- repeat(tics) @(posedge clock); //等待tics个时钟的上升沿
- color = off;
- end
- endtask
- always begin //产生时钟脉冲的always块
- #100 clock = 0;
- #100 clock = 1;
- end
- endmodule
三.函数(function)
函数的目的是返回一个用于表达式的值。
1.函数定义语法
- function <返回值的类型或范围> (函数名)
- <端口说明语句>
- <变量类型说明语句> begin
- <语句
- ...
- end
- endfunction
注意:<返回值的类型或范围>这一项是可选项,如缺省则返回值为一位寄存器类型数据。例如:
- function [7:0] getbyte;
- input [15:0] address;
- begin
- <说明语句> //从地址字节中提取低字节的程序
- getbyte = result_expression; //把结果赋予函数的返回字节
- end
- endfunction
2.函数返回值
函数的定义蕴含声明了与函数同名的、函数内部的寄存器。如在函数的声明语句中<返回值的类型或范围>为缺省,则这个寄存器是一位;否则是与函数定义中<返回值的类型或范围>一致的寄存器。
函数的定义把函数返回值赋值寄存器的名称初始化为与函数同名的内部变量。上面的例子说明这个概念:getbyte被赋予的值就是函数的返回值。
3.函数调用
函数的调用是通过将函数作为表达式中的操作数来实现的,其调用格式为:
<函数名> (<表达式><,<表达式>>*)
其中函数名作为确认符。例如通过对两次调用函数getbyte的结果进行位拼接运算来生成一个字。
word = control?{getbyte(msbyte),getbyte(lsbyte)}:0;
4.函数使用规则
与任务相比函数的使用有较多的约束,规则为:
- 函数的定义不能包含有任何的时间控制语句,即任何用#、@或wait来标识的语句
- 函数不能启动任务
- 定义函数时至少要有一个输入参数
- 在函数的定义中必须有一条赋值语句给函数中的一个内部变量赋以函数的结果值,该内部变量具有和函数名相同的名字
5.实例详解
下面定义一个可进行阶乘运算的名为factorial函数,该函数返回一个32位的寄存器类型的值,该函数可后向调用自身,并且打印出部分结果值。
- module tryfact; //模块开始
- //函数的定义------------------------------
- function [31:0] factorial; //函数开始
- input [3:0] operand; //函数入口
- reg [3:0] index; // 函数的寄存器
- begin
- factorial = operand?1:0; //若operand全为0,则为0
- for(index = 2; index <= operand; index = index +1) //循环语句
- 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 //模块结束
注:
1.函数(function)
函数在一个仿真时间单位内执行完毕,因此不能包含任务、不能使用非阻塞赋值。使用上都是把函数作为表达式中的一个操作数。
2.任务(task)
任务可以包含时序控制语句,例如延迟语句,这样会造成该任务不可综合,在任务定义结构内不能出现initial和always过程块,但任务调用语句可以在initial语句和always语句中使用。也可以调用其它任务和函数;可以使用任意类型的参数,也可以没有参数。可综合任务只能实现组合逻辑,也就是说调用可综合任务的时间为“0”。而在面向仿真的任务中可以带有时序控制,如时延,因此面向仿真的任务的调用时间不为“0”。
不同点:
任务 task | 函数 function |
通常用于调试,或对硬件进行行为描述 | 通常用于计算,或描述组合逻辑 |
可以包含时序控制(#延迟,@, wait) | 不能包含任何延迟;函数仿真时间为0 |
可以有 input,output,和inout参数 | 只含有input参数并由函数名返回一个结果 |
可以调用其他任务或函数 | 可以调用其他函数,但不能调用任务 |
共同点:
1)任务和函数必须在module内调用
2)在任务和函数中不能声明wire
3)所有输入输出都是局部寄存器
4)任务函数执行完成后才返回结果
1.任务定义格式:
任务定义的形式如下:
task task_id;
[declaration]
procedural_statement
endtask
其中,关键词 task 和 endtask 将它们之间的内容标志成一个任务定义,task 标志着一个任务定义结构的开始;task_id 是任务名;可选项 declaration 是端口声明语句和变量声明语句,任务接收输入值和返回输出值就是通过此处声明的端口进行的;procedural_statement是一段用来完成这个任务操作的过程语句,如果过程语句多于一条,应将其放在语句块内;endtask 为任务定义结构体结束标志。下面给出一个任务定义的实例。
task task_demo; //任务定义结构开头,命名为 task_demo
input [7:0] x,y; //输入端口说明
output [7:0] tmp; //输出端口说明
if(x>y) //给出任务定义的描述语句
tmp = x;
else
tmp = y;
endtask
上述代码定义了一个名为“task_demo”的任务,求取两个数的最大值。在定义任务时,
有下列六点需要注意:
(1)在第一行“task”语句中不能列出端口名称;
(2)任务的输入、输出端口和双向端口数量不受限制,甚至可以没有输入、输出以及双向端口。
(3)在任务定义的描述语句中,可以使用出现不可综合操作符合语句(使用最为频繁的就是延迟控制语句) ,但这样会造成该任务不可综合。
(4)在任务中可以调用其他的任务或函数,也可以调用自身。
(5)在任务定义结构内不能出现 initial和 always过程块。
(6)在任务定义中可以出现“disable 中止语句” ,将中断正在执行的任务,但其是不可综合的。当任务被中断后,程序流程将返回到调用任务的地方继续向下执行。
2.任务调用
虽然任务中不能出现 initial 语句和 always 语句语句, 但任务调用语句可以在 initial 语句和 always 语句中使用,其语法形式如下: task_id[(端口1, 端口 2, ........, 端口 N)]; 其中 task_id是要调用的任务名,端口 1、端口 2,…是参数列表。参数列表给出传入任务的数据(进入任务的输入端)和接收返回结果的变量(从任务的输出端接收返回结果) 。
任务调用语句中,参数列表的顺序必须与任务定义中的端口声明顺序相同。任务调用语句是过程性语句,所以任务调用中接收返回数据的变量必须是寄存器类型。下面给出一个任务调用实例。
例:通过 Verilog HDL 的任务调用实现一个 4 比特全加器。
module EXAMPLE (A, B, CIN, S, COUT);
input [3:0] A, B;
input CIN;
output [3:0] S;
output COUT;
reg [3:0] S;
reg COUT;
reg [1:0] S0, S1, S2, S3;
task ADD;
input A, B, CIN;
output [1:0] C;
reg [1:0] C;
reg S, COUT;
begin
S = A ^ B ^ CIN;
COUT = (A&B) | (A&CIN) | (B&CIN);
C = {COUT, S};
end
endtask
always @(A or B or CIN) begin
ADD (A[0], B[0], CIN, S0);
ADD (A[1], B[1], S0[1], S1);
ADD (A[2], B[2], S1[1], S2);
ADD (A[3], B[3], S2[1], S3);
S = {S3[0], S2[0], S1[0], S0[0]};
COUT = S3[1];
end
endmodule
在调用任务时,需要注意以下几点:
(1)任务调用语句只能出现在过程块内;
(2)任务调用语句和一条普通的行为描述语句的处理方法一致;
(3)当被调用输入、输出或双向端口时,任务调用语句必须包含端口名列表,且信号端口顺序和类型必须和任务定义结构中的顺序和类型一致。需要说明的是,任务的输出端口必须和寄存器类型的数据变量对应。
(4)可综合任务只能实现组合逻辑,也就是说调用可综合任务的时间为“0” 。而在面向仿真的任务中可以带有时序控制,如时延,因此面向仿真的任务的调用时间不为“0” 。
今天就聊到这里,各位,加油。
任务和函数主要有以下一些不同:
(1)函数只能与主模块共用一个仿真单位时间,而任务可以定义自己的仿真时间单位。
(2)函数不能启动任务,可以启动其它函数。而任务可以启动其他任务和函数。
(3)函数至少有一个输入变量(不能有inout或out)等,而任务可以没有或有任意个(输入或输出变量)。
(4)函数返回一个值,而任务不返回值。
函数的目的是通过返回一个值来响应输入信号的值。任务却能支持多种目的,能计算多个结果值,这些值只能通过被调用的任务的输出或总线端口送出。
任务不可综合,函数可综合。
函数的使用规则:
(1)函数的定义不能包含有任何的时间控制语句,即任何用#,@,wait来标识的语句。
(2)函数不能启动任务。
(3)定义函数至少有一个输入变量。
(4)在函数的定义中必须有一条赋值语句给函数中的一个内部变量赋以函数的结果值,该内部变量具有和函数名相同的名字。
1、函数和任务都要用begin和end。
如:task light;
output color; //声明端口顺序,任务调用时也要按这个顺序对应
input [31:0] tics;
begin
#(10*tics) color=off; //函数体只有一句,begin和end可有可无
end
endtask
task light;
output color;
input [31:0] tics;
begin
#(10*tics); color=off; //函数体有两句(以上),begin和end必须有
end
endtask
2、上述任务可写成这样,把端口声明写成这样。
task light(output color,input [31:0] tics);
begin
#(10*tics); color=off; //函数体有两句(以上),begin和end必须有
end
endtask