设计者常需要在程序的多个不同地方实现同样的功能,这就有必要将这些公共的部分提取出来,将其组成子程序,然后在有需要的地方调用这些子程序。
一、任何和函数的区别
函数 | 任务 |
---|---|
函数能调用另一个函数,但不能调用另一个任务 | 任务能调用另一个任务,也能调用另一个函数 |
函数总是在仿真时刻0就开始执行 | 任务可以在非零仿真时刻进行 |
函数一定不能包含任何延迟、事件或者时序控制声明语句 | 任务可以包含延迟、事件或者时序控制声明语句 |
函数至少有一个输入变量,函数可以有多个输入变量 | 任务可以没有或者有多个输入、输出和双向变量 |
函数只能返回一个值,函数不能有输出或者双向变量 | 任务不返回任何值,任务可以通过输出或双向变量传递多个值 |
二、任务
任务使用关键字task和endtask进行声明,如果子程序满足以下任意一个条件,则必须使用任务而不能使用函数:
- 子程序中包含有延迟、时序或者事件控制结构
- 没有输出或者输出变量数目大于1
- 没有输入变量
1.任务的声明和调用
module operation;
...
...
parameter delay = 10;
reg [15:0] A, B;
reg [15:0] AB_AND, AB_OR, AB_XOR;
always @(A or B)
begin
//启动bitwise_oper任务,该任务提供两个输入变量A和B
//有三个输出变量AB_AND, AB_OR, AB_XOR
bitwise_oper(AB_AND, AB_OR, AB_XOR, A, B);
end
...
...
//定义bitwise_oper任务
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
...
endmodule
2.自动(可重入)任务
任务在本质上是静态的,任务重的所有声明项的地址空间是静态分配的,同时并发执行的多个任务共享这些存储区。因此,如果这个任务在模块中的两个地方被同时调用,则这两个任务调用将对同一块地址空间进行操作,那么结果很有可能是错的。
这时候我们可以在task前面添加关键字automatic就能避免。这样的任务也被称为自动任务。
每次调用的时候,在动态任务重声明的所有模块项的存储空间都是动态分配的。
module top
reg [15:0] cd_xor, ef_xor;
reg [15:0] c, d, e, f;
task automatic bitwise_xor;
output [15:0] ab_xor;
input [15:0]a, b;
begin
#delay ab_and = a & b;
ab_or = a | b;
ab_xor = a ^ b;
end
end task
//下面两个always块将会在clk上升沿的时候同时调用bitwise_xor任务。
//因为该任务是可以重入的,所以并发的同时调用能正常地运行
always @(posedge clk)
bitwise_xor(ef_xor, e, f);
always @(posedge clk)
bitwise_xor(cd_xor, c, d);
endmodule
三、函数
verilog使用关键字function和endfunction来进行函数声明。对于一个子程序来说,如果下面的所有条件全部成立,则可以用函数来完成:
- 在子程序内不含有延迟、时序或者控制结构
- 子程序只有一个返回值
- 至少有一个输入变量
- 没有输出或者双向变量
- 不含有非阻塞赋值语句
1.函数的声明和调用
//定义一个模块,其中包含能计算偶校验位的函数calc_parity
module parity;
...
reg [31:0] addr;
reg parity;
//每当地址值发生变化,计算新的偶校验位
always @(addr)
begin
parity = calc_parity(addr);
$display("Parity calculated = %b", calc_parity(addr));
end
...
...
//定义偶校验位计算函数
function calc_parity;
input [31:0]address;
begin
calc_parity = ^address;
end
endfunction
...
...
endmodule
2.自动(递归)函数
verilog中的函数时不能进行递归调用的,原因同上,因为是对同一块地址空间进行操作,那么计算结果将是不确定的。因此我们需要加上automatic,那么该函数将成为自动的或可递归的,即仿真器每一次函数调用都动态分配新的地址空间,每个函数调用对各自的地址空间进行操作。
module Top;
function automatic integer factorial;
input [31:0] oper;
integer i;
begin
if(operand >= 2)
factorial = factorial(oper - 1) * oper;
else
factorial = 1;
end
endfunction
integer result;
initial
begin
result = factorial(4);
$display("Factorial of 4 is %0d", result);
end
endmodule
3.常量函数
常量函数实际上就是一个带有某些限制的常规verilog函数。