目录
1. 函数与任务的不同
2. task说明语句
2.1 task定义
2.2 任务的调用及变量的传递
3. function说明语句
3.1定义函数的语法
参考文档:Xilinx UG 901
task语句和function语句是verilog设计中分别用于定义任务和函数的,通过定义task和function,能够将本来很大的程序模块分解为较小的任何函数,这样更便于理解和调试。在设计中,可能在一些大的程序中经常会有某个程序段在不同的地点被反复用到,通过定义task和function进行调用要节省代码量,简化程序的结构,也方便理解。
先宏观地了解一下函数和任务的异同点,然后带着疑问去学习会有方向。
任务和函数的共同点:
1.任务和函数必须在模块内定义,其作用范围仅适用于该模块,可以在模块内多次调用。
2.任务和函数中可以声明局部变量,如寄存器,时间,整数,实数和事件,但是不能声明线网类型的变量。
3.任务和函数中只能使用行为级语句,但是不能包含always和initial块,设计者可以在always和initial块中调用任务和函数。
任务与函数的不同点:
从上面描述的特点,可以看出:函数用于替代纯组合逻辑的verilog代码,而任务可以代替verilog的任何代码
【注意】:函数的目的是通过返回一个函数值来响应输入信号的值,而任务的目的有很多种,能计算多个结果值,这些结果值只能通过被调用的任务的输出信号输出或总线端口送出。Verilog HDL模块使用函数时,是将其当作表达式种的操作符(认真体会啊,操作符的话,就会就结果) ,这个操作执行后的结果就是函数的返回值,该返回值可以用于其他操作。
任务使用关键字task和endtask来进行声明,定义任务的语法如下:
task<任务名>
<端口及数据类型声明语句>
<语句1>
<语句2>
…………
<语句n>
endtask
在任务内的各种语句,其语法与Vierlilog 的module块中用法一致。
任务的调用:
<任务名> (端口1,端口2,…..,端口n);
下面举例子说明怎么定义任务与调用任务:
任务定义:
task my_task;
input a,b; //可以有输入
inout c;
output d,e; //可以有输出
<语句>
….
c = fool; //
d = foo2; //对输出变量赋值
e = foo3;
endtask
任务调用:
my_task(v,w,x);
任务调用变量(v,w,x)和任务定义的I/O变量(a,b,c)之间是一一对应的,当任务启动时,由v和w传入的变量赋给了a和b,而当任务完成之后的输出又通过c赋给x。
如果子程序满足下面任何一个条件,则必须使用任务而不能使用函数。
EX:
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
关键字 automatic
自动(可重入)任务:verilog任务中所有声明的变量地址空间都是静态分配的,因此如果在一个模块中多次调用任务时,可能会造成地址空间的冲突,为了避免这个问题,verilog通过在task关键字后面添加automatic使任务称为可重入的,这时在调用任务时,会自动给任务声明变量分配动态地址空间,这样有效避免了地址空间的冲突。
前文就介绍了,调用函数的目的就是需要返回一个用于其他表达式的值
函数使用关键字function和endfunction定义
function<返回值的类型或范围>(函数名); //返回值的类型可选,默认返回值为一位寄存器类型数据
<端口说明语句>
<变量类型说明语句>
begin
<语句>
………
end
endfunction
【注意】:<返回值的类型或范围>这一项是可选项,如缺省则返回值为一位寄存器类型数据
对于子程序,如果满足下述所有条件则可以用函数来完成:
1.在子程序中不含有延迟时序或者控制结构
2.子程序只有一个返回值
3.至少有一个输入变量
4.没有输出或者双向变量
5.不含有非阻塞赋值语句
下面举一个实际程序的例子,来说明一下function函数的实际定义与如何调用。
Ex1:
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关键字来对函数可重用性声明。没有进行可重用性声明的函数不可以多次或者递归调用,进行了可重用性声明的函数可以递归调用。
练习:用两种不同的方法设计一个功能相同的模块,完成4个8位2进制输入数据的冒泡排序。第一种,用纯组合逻辑实现;第二种,假设8位数据按照时钟节拍串行输入,要求时钟触发任务的执行,每个时钟周期完成一次数据交换的操作。
//----------------- 第一种 ------------------
module sort4(ra,rb,rc,rd,a,b,c,d);
output[7:0] ra,rb,rc,rd;
input[7:0] a,b,c,d ;
reg[7:0] ra,rb,rc,rd ;
reg[7:0] va,vb,vc,vd ;
always @ (a or b or c or d)
begin
{va,vb,vc,vd}={a,b,c,d};
change(va,vb);
change(vb,vc);
change(vc,vd);
change(va,vb);
change(vb,vc);
change(va,vb);
{ra,rb,rc,rd}={va,vb,vc,vd};
end
task change; //make a task of comparing
inout[7:0] x,y;
reg[7:0] tmp;
if(x>y)
begin
tmp = x ;
x = y ;
y = tmp;
end
endtask
endmodule
//----------------- 第二种 ------------------
module sort4(clk,reset,ra,rb,rc,rd,a);
output[7:0] ra,rb,rc,rd;
input[7:0] a;
input clk,reset;
reg[7:0] ra,rb,rc,rd;
reg[7:0] va,vb,vc,vd;
always @ (posedge clk)
begin
if(!reset)
begin
va<=0;
vb<=0;
vc<=0;
vd<=0;
end
else
va<=a;
end
always @ (posedge clk)
begin
change(va,vb) ;
change(vb,vc) ;
change(vc,vd) ;
change(va,vb) ;
change(vb,vc) ;
change(va,vb) ;
{ra,rb,rc,rd}={va,vb,vc,vd};
end
task change; //make a task of comparing
inout[7:0] x,y; //
reg[7:0] tmp;
if(x>y)
begin
tmp = x ;
x = y ;
y = tmp ;
end
endtask
endmodule