verilog语言中任务和函数的区别

任务和函数有助于简化程序,有点类似与Fortran语言的subroutine和function。

任务和函数的共同点:

1.任务和函数必须在模块内定义,其作用范围仅适用于该模块,可以在模块内多次调用。
2.任务和函数中可以声明局部变量,如寄存器,时间,整数,实数和事件,但是不能声明线网类型的变量。
3.任务和函数中只能使用行为级语句,但是不能包含always和initial块,设计者可以在always和initial块中调用任务和函数。

任务和函数的不同点:

函数能调用另一个函数,但是不能调用任务 任务可以调用另一个任务,也可以调用函数
函数总是在仿真时刻0开始                  任务可以在非零时刻开始执行
函数一定不能包含任何延迟,事件或者时序控制声明语句     任务可以包含延迟,事件或者时序控制声明语句
函数至少要有一个输入变量,也可以有多个输入变量       任务可以没有或者有多个输入,输出,输入输出变量
函数只能返回一个值,函数不能有输出或者双向变量       任务不返回任何值,或者返回多个输出或双向变量值
由上述的特点决定:函数用于替代纯组合逻辑的verilog代码,而任务可以代替verilog的任何代码。

任务

任务使用关键字task和endtask来进行声明,如果子程序满足下面任何一个条件,则必须使用任务而不能使用函数。
1.子程序中包含有延迟,时序或者事件控制结构
2.没有输出或者输出变量超过一个
3.没有输入变量

例:

module operation;
parameter delay=10;
reg [15:0] A,B, AB_AND,AB_OR,AB_XOR;
always @(A or B)
begin
	bitwise_ope(AB_AND,AB_OR,AB_XOR,A,B);
end
task bitwise_oper;
output [15:0] ab_and,ab_or,ab_xor;
input [15:0] a,b;
begin
	#delay ab_and=a&b;
ab_or=a|b;
ab_xor=a^b;
end
endtask
always @(posedge clk)
	bitwise_xor(ef_xor,e,f);
always @(posedege clk2)
	bitwise_xor(cd_xor,c,d)
task autumatic bitwise_xor;
output ab_xor;
input a,b;
begin
	ab_xor=a^b;
end
endtask

endmodule

自动(可重入)任务:verilog任务中所有声明的变量地址空间都是静态分配的,因此如果在一个模块中多次调用任务时,可能会造成地址空间的冲突,为了避免这个问题,verilog通过在task关键字后面添加automatic使任务称为可重入的,这时在调用任务时,会自动给任务声明变量分配动态地址空间,这样有效避免了地址空间的冲突。

有下列六点需要注意:
(1)在第一行“task”语句中不能列出端口名称;
(2)任务的输入、输出端口和双向端口数量不受限制,甚至可以没有输入、输出以及双向端口。
(3)在任务定义的描述语句中,可以使用出现不可综合操作符语句(使用最为频繁的就是延迟控制语句) ,但这样会造成该任务不可综合。
(4)在任务中可以调用其他的任务或函数,也可以调用自身。
(5)在任务定义结构内不能出现 initial和 always过程块。
(6)在任务定义中可以出现“disable 中止语句” ,将中断正在执行的任务,但其是不可综合的。当任务被中断后,程序流程将返回到调用任务的地方继续向下执行。
虽然任务中不能出现 initial 语句和 always 语句语句, 但任务调用语句可以在 initial 语句和 always 语句中使用
其语法形式如下: task_id[(端口1, 端口 2, …, 端口 N)]; 其中 task_id是要调用的任务名,端口 1、端口 2,…是参数列表。参数列表给出传入任务的数据(进入任务的输入端)和接收返回结果的变量(从任务的输出端接收返回结果) 。

在调用任务时,需要注意以下几点:
(1)任务调用语句只能出现在过程块内;
(2)任务调用语句和一条普通的行为描述语句的处理方法一致;
(3)当被调用输入、输出或双向端口时,任务调用语句必须包含端口名列表,且信号
端口顺序和类型必须和任务定义结构中的顺序和类型一致。需要说明的是,任务的输出端口
必须和寄存器类型的数据变量对应。
(4)可综合任务只能实现组合逻辑,也就是说调用可综合任务的时间为“0” 。而在面向仿真的任务中可以带有时序控制,如时延,因此面向仿真的任务的调用时间不为“0” 。

函数

函数使用关键字function和endfunction定义,对于子程序,如果满足下述所有条件则可以用函数来完成:
1.在子程序中不含有延迟时序或者控制结构
2.子程序只有一个返回值
3.至少有一个输入变量
4.没有输出或者双向变量
5.不含有非阻塞赋值语句

例:

module parity;
reg [31:0] addr;
reg parity;
always @(addr)
begin
	parity=calc_parity(addr);
end
function calc_parity;
input [31:0] addr;
begin
	calc_parity=^addr;
end
endfunction
endmodule

跟任务调用一样,在模块中如果调用多次函数,也会碰到地址冲突的问题,因此也引入automatic关键字来对函数可重用性声明。没有进行可重用性声明的函数不可以多次或者递归调用,进行了可重用性声明的函数可以递归调用。
常量函数和带符号函数(函数声明时加signed关键字说明)

module ram;
parameter RAM_DEPTH=256;
input [clogb2(RAM_DEPTH)-1:0] addr;//clogb2函数返回值为8
function integer clogb2(input integer depth);
begin
	for(clogb2=0; depth>0;clogb2=clogb2+1)
	depth=depth>1;
end
endfunction
endmodule

函数的目的是返回一个用于表达式的值。
一、定义函数的语法

function<返回值的类型或范围>(函数名)<端口说明语句>
<变量类型说明语句>
begin
	<语句>
end
endfunction

注意:<返回值的类型或范围>是可选项,如缺省则返回值为一位寄存器类型数据。
二、从函数返回的值:函数的定义蕴含声明了与函数同名的、函数内部的寄存器。
三、函数的调用:函数的调用是通过将函数作为表达式中的操作数来实现的。其调用格式如下:
<函数名>(<表达式><,<表达式>>*)
其中函数名作为确认符。
四、函数的使用规则:与任务相比较,函数的使用有较多的约束。
1、函数的定义不能包含有任何的时间控制语句,即#,@或wait来标识的语句;
2、函数不能启动任务;
3、定义函数时至少要有一个输入参量;
4、在函数的定义中必须有一条赋值语句给函数中的一个内部变量赋以函数的结果值,该内部变量具有和函数名相同的名字。

发布了36 篇原创文章 · 获赞 5 · 访问量 2379

猜你喜欢

转载自blog.csdn.net/qq_36248682/article/details/105527857