【FPGA篇章五】FPGA函数任务:对讲解函数与任务专题展开详解

欢迎大家关注我的微信公众账号,支持程序媛写出更多优秀的文章

 

任务和函数也属于过程块,多用于仿真文件设计中,使用两者的目的有所区别:

函数(function):对输入的值执行一些处理,返回一个新的值。

    因此至少有一个input类型的参数,不能有inout或output类型的参数。

    函数在一个仿真时间单位内执行完毕,因此不能包含任务、不能使用非阻塞赋值。

    使用上都是把函数作为表达式中的一个操作数。

任务(task):其实作用与module差不多,只是能在过程块中调用,实现的功能比函数更加广泛。

    任务可以包含时序控制语句,也可以调用其它任务和函数;

    可以使用任意类型的参数,也可以没有参数。

将一个数据按位取反后得到一个新的数据,

将这个功能用任务的形式实现,调用时形式如下:

switch_bytess(old_word, new_word);

将这个功能用函数的形式实现,调用时形式如下:

new_word = switch_bytess(old_word);

接下来分别介绍任务和函数的一些用法,再给出Verilog支持的系统任务和系统函数。

1 函数(function)

函数用作表达式中的一个操作数。一个函数的声明架构如下:

 1 function  返回值位宽或类型说明 函数名;
 2     端口声明;
 3     局部变量定义;
 4     //函数主体
 5         begin
 6          语句1;
 7           .....
 8          语句n;
 9       end  
10 endfunction

下面是一个function的例子:

 1 module  alu(a,b,product,result);
 2    input[1:0] a,b;
 3    output[3:0] product,result;
 4    wire[1:0]  a,b;
 5    reg[3:0] product,result;
 6    reg [7:0] all_result;
 7 
 8    always@(a,b)
 9      begin    //注意调用函数的方法与任务不同;       
10        all_result =  cal(a,b);
11        product = all_result[7:4];
12        result = all_result[3:0]; 
13      end
14 
15 //定义函数cal
16     function  [70]    cal;
17       input[10]   a;
18       input[10]   b;
19       reg[30]   temp;
20 
21        begin 
22           product = a*b;
23           temp = a*a;
24           result = temp-b; 
25           cal = {product,result};
26         end
27     endfunction
28 
29 endmodule    

函数的默认返回值是一个标量,可以使用“[范围或类型]”将返回值设置为real、integer、time、realtime或矢量。

定义函数时,函数内部会隐式地定义一个和函数名相同的数据。函数内部可以直接使用这个数据,这个数据的值便是函数的返回值。

递归函数

verilog中的函数是不能进行递归调用的;设计模块中若某函数在两个不同的地方被同时并发调用,由于这两个调用同时对同一块地址空问进行操作,那么计算结果将是不确定的。

若在函数声明时使用了关键字automatic,那么该函数将成为自动的或可递归的.

下例说明如何定义自动函数,来完成阶乘运算:

 1  //用函数的递归调用定义阶乘计算
 2  module   top;
 3     ..................
 4 
 5     //定义自动(递归)函数
 6      function automatic integer  factorial;
 7          input [31 : 0] oper;
 8          begin
 9           if (oper >= 2)
10               factorial = factorial( oper - 1) * oper;//递归调用
11           else
12               factorial=1:
13         end
14     endfunction  
15 
16 integer result;
17    initial
18      begin
19             result = factorial (4);
20      end
21 endmodule

2 任务(task)

任务(task)定义与调用的格式分别如下:

1 //任务定义的格式为:
2 task  任务名 ;
3     端口及数据类型声明语句;
4     其他语句;
5 endtask
6 
7 //任务调用的格式为:
8      任务名 (端口1,端口2,……)

比如下面是定义一个任务的例子:

 1 //使用任务描述运算单元
 2 module  alu(a,b,result);
 3     input[1:0]  a,b;
 4     output[3:0]  result;
 5     wire[1:0]  a,b;
 6     reg[3:0]  result;
 7 
 8     always@(a, b)
 9        begin
10             //按照任务中定义的端口顺序调用任务
11             cal(a,b, result);
12       end
13 
14 //定义任务cal
15 task cal;
16     //任务端口列表
17     input[1:0] a;
18     input[1:0] b;
19     output[3:0] result;
20     //内部定义局部变量(必须reg型)
21     reg[3:0]  temp;
22     begin 
23          temp = a*a;
24          result = temp-b; 
25     end
26 endtask
27 endmodule 

任务的定义和module差不多,只不过端口列表中的参数可以是任意数据类型

使用“disable+任务名”可以提前终止任务的执行。

自动任务

任务在本质上是静态的,任务中的所有声明项的地址空间是静态分配的。因此,如果这个任务在模块中的两个地方被同时调用,则这两个任务调用将对同一块地址空间进行操作。操作的结果很有可能是错误的。

为了避免这个问题,Verilog通过在task关键字前面添加关键字automatic,使任务成为可重入的,这样声明的任务也称为自动任务,每次调用时,在动态任务中声明的所有模块项的存储空间都是动态分配的,每个调用都对各自独立的地址空间进行操作。这样,每个任务调用只对自己所拥有的独立变量副本进行操作.因此可以得到正确的执行结果。

 1 module auto_task;
 2     reg [4:0]  cd_add, ef_add;
 3     reg [4:0] c, d , e, f;
 4     reg clk1,clk2;
 5    parameter delay=1;
 6     
 7 initial 
 8    begin
 9       clk1=0;     clk2=0;
10       c=3;d=5;e=7;f=4;
11       #20 c=2;d=4;e=8;f=10;
12       #20 $stop;
13    end
14 initial forever #4 clk1=~clk1;
15 initial forever #5 clk2=~clk2;
16 
17 task automatic adder; // 任务定义
18         output [4: 0] ab_adder;
19         input [4: 0] a, b;
20         begin
21             #delay ab_adder = a + b;
22          end
23 endtask
24 
25 always @(posedge clk1)
26           adder(ef_add,e,f);
27 
28 always @ (posedge clk2)
29           adder(cd_add, c, d);
30 
31 endmodule

3 任务task与function的区别

不同点:

任务 task 函数 function
通常用于调试,或对硬件进行行为描述 通常用于计算,或描述组合逻辑
可以包含时序控制(#延迟,@, wait) 不能包含任何延迟;函数仿真时间为0
可以有 input,output,和inout参数 只含有input参数并由函数名返回一个结果
可以调用其他任务或函数 可以调用其他函数,但不能调用任务

共同点:

  1)任务和函数必须在module内调用

  2)在任务和函数中不能声明wire

  3)所有输入输出都是局部寄存器

  4)任务函数执行完成后才返回结果

4 小结
(1)任务和函数都用来对设计中多处使用的公共代码进行定义,使用任务和函数可以将模块分割成许多个可独立管理的子单元,增强了模块的可读性和可维护性。

(2)任务可以有任意多个输入、输入/输出(inout)和输出变量。在任务中可以使用延迟、事件和时序控制结构,在任务中可以调用其他的任务和函数;

(3)自动任务使用关键字automatic进行定义,它的每一次调用都对不同的地址空间进行操作。因此,在被多次并发调用时仍然可以获得正确的结果;

(4)函数只能有一个返回值,并且至少要有一个输入变量。在函数中不能使用延迟、事件和时序控制结构。在函数可以调用其它函数,但不能调用任务;

(5)当声明函数时,Verilog仿真器都会隐含地声明一个同名的寄存器变量,函数的返回值通过这个寄存器传递回调用处;

(6)递归函数使用关键字automatic进行定义,递归函数每一次调用都拥有不同的地址空间。因此对这种函数的递归调用和并发调用可以得到正确的结果;

(7)任务和函数都包含在设计层次中,可以通过层次名对它们进行调用。

原文地址:https://www.cnblogs.com/streetlive/p/12643347.html