在 FPGA 设计中,合理使用 function 和 task 不仅能让代码变简洁,还能极大提升模块复用性与可读性。function 可用于组合逻辑运算和常数计算,而task 则能执行多步操作甚至包含延时语句。合理使用两者,能让复杂设计结构化、调试更高效。
一、function 说明
function 声明如下:
function <返回值的类型或范围> 函数名;
<端口说明语句> // input xxx
<变量类型说明语句> // reg yyy
begin
<语句>
end
endfunction
函数的调用是通过将函数作为表达式中的操作数来实现的。函数一般是可以综合的,使用时注意以下规则: ① function 用于常数计算或描述组合逻辑,不能包含时间控制语句,函数仿真时间为 0,不能使用非阻塞赋值; ② function 只有一个返回值,返回值被赋予和函数名同名的变量,当返回值的范围不定义时,默认为一位寄存器类型数据; ③ function<端口说明语句>中只能有 input 参数,不能有 output/inout,而且至少需要有一个 input 参数; ④ function 定义只能在模块中完成,不能出现在过程块中 (initial,always); ⑤ function 调用时参数列表的顺序必须与函数定义声明时输入的顺序相同; ⑥ function 调用可以在过程块中出现,也可以在 assign 赋值语句中出现,函数调用只能作为赋值语句的右端操作数。 ⑦ function 可以调用其他函数,也可以调用该函数本身,但不能调用任务; ⑧ function 不允许使用 disable 终止语句。
二、function 示例
下面是一个调用 1 位全加器function实现四位全加器的例子。
module functions_1 (A, B, CIN, S, COUT);
input [3:0] A, B;
input CIN;
output [3:0] S;
output COUT;
wire [1:0] S0, S1, S2, S3;
function signed [1:0] ADD;
input A, B, CIN;
reg S, COUT;
begin
S = A ^ B ^ CIN;
COUT = (A&B) | (A&CIN) | (B&CIN);
ADD = {COUT, S};
end
endfunction
assign S0 = ADD(A[0], B[0], CIN),
S1 = ADD(A[1], B[1], S0[1]),
S2 = ADD(A[2], B[2], S1[1]),
S3 = ADD(A[3], B[3], S2[1]),
S = {S3[0], S2[0], S1[0], S0[0]},
COUT = S3[1];
endmodule
如果不用调用函数的形式,直接写四位全加器的加法,代码、仿真、综合电路如下,可见与调用函数的形式结果是一模一样的。
module functions_1 (A, B, CIN, S, COUT);
input [3:0] A, B;
input CIN;
output [3:0] S;
output COUT;
assign {COUT,S}=A+B+CIN;
endmodule
下面是在 Xilinx 的许多源码中出现的一个计算数值位宽的 function。
三、task 说明
declaration:
task <任务名>;
<端口及数据类型声明语句>
<语句 1>
<语句 2>
...
<语句 n>
endtask
调用如下:
<任务名>(端口 1, 端口 2,...,端口 n);
① task 常用于调试,或对硬件进行行为描述,可以包含时序控制(#延迟,@, wait 等)(此时无法被综合),所以任务常用于 testbench 测试模块; ② task 可以没有或可以有一个或多个 input、output 和 inout 端口,这暗示了任务可能没有返回值; ③ task 除了在任务中定义变量,还能够引用任务定义所在模块中声明的任何变量; ④ task 定义结构内不允许出现过程块(initial 或 always 过程块); ⑤ task 调用的参数列表顺序与任务 I/O 端口变量的声明次序需要相同; ⑥ task 调用语句是一条语句,可以出现在 always 或者 initial 语句中; ⑦ task 可以调用其他任务或函数,也可以调用该任务本身; ⑧ 关键字 disable 可以用来禁止任务的执行。
四、task 示例
下面是一个调用 1 位全加器task实现四位全加器的例子。
module tasks_1 (A, B, CIN, S, COUT);
input [3:0] A, B;
input CIN;
output reg [3:0] S;
output reg COUT;
reg [1:0] S0, S1, S2, S3;
task ADD;
input A, B, CIN;
output 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


