函数 | 任务 |
函数能调用另一个函数,但是不能调用任务 | 任务能调用另一个任务,也能调用另一个函数 |
函数总是在仿真时刻0就开始执行 | 任务可以在非零时刻执行 |
函数一定不能包含任何延迟、事件或者时序控制语句 | 任务可以包含任何延迟、事件或者时序控制语句 |
函数至少有一个输入变量 | 任务可以没有或者有多个输入、输出、双向变量 |
函数只能返回一个值,函数不能有输出或者双向变量 | 任务不返回任何值,任务可以通过输出或者双向变量传递多个值 |
任务:
当:
- 子程序包含延迟、事件或者时序控制语句结构
- 没有输出或者输出变量数目大于1
- 没有输入变量
则必须使用任务。
自动(可重入)任务:
任务本质上是静态的,任务中的所有声明项的地址空间是静态分配的,同时并发执行的多个任务共享这些存储区。因此如果这个任务在两个地方同时调用,则这两个任务将对同一块地址区间进行操作。为了避免这个问题,Verilog通过在task关键字前面添加automatic关键字,使任务为可重入的,存储空间动态分配,每个调用都有独立的地址空间。
例如:
module top;
reg [15:0] cd_xor, ef_xor;
reg [15:0] c, d, e, f;
task bitwise_xor;
output [15:0] ab_xor;
input [15:0] a,b;
begin
ab_xor = a^b;
end
endtask
always @(posedge clk or negedge rstn) begin
bitwise_xor(ef_xor, e, f);
end
always @(posedge clk or negedge rstn) begin
bitwise_xor(cd_xor, c, d);
end
...
-
endmodule
函数:
当:
- 在子程序内不包含延迟、时序或者控制结构
- 子程序只有一个返回值
- 至少有一个输入变量
- 没有输出或者双向变量
- 不含有非阻塞赋值语句
可以使用函数。
函数的调用通过指明函数名及其输入变量来进行。当声明函数的时候,隐含的声明了一个与函数名同名的寄存器类型变量。函数的输出结果也将通过该寄存器被传递回来。
例如:
module parity;
...
reg [31:0] addr;
reg parity;
always@(addr)
begin
parity = calc_parity(addr);
$display("Parity calculated = %b", calc_parity(addr));
end
functioin calc_parity;
input [31:0] address;
begin
calc_parity = ^address;
end
endfunction
...
endmodule
在函数第一次调用时,它的返回值被用来设置寄存器变量parity,在第二次调用时,它的返回值直接使用系统任务$display进行显示。从这一点可以看出,函数的返回值被用在函数调用的地方。
【calc_parity函数隐含的定义了1bit的calc_parity寄存器变量,若要返回多bit变量,则:functioin [N:0] calc_parity;】
自动(递归)函数:
有 自动任务,同理就有自动函数。也是使用关键字 automatic。动态的分配存储空间。