zoukankan      html  css  js  c++  java
  • 【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)任务和函数都包含在设计层次中,可以通过层次名对它们进行调用。

  • 相关阅读:
    mybatis模糊查询语句
    Java中解压文件名有中文的rar包出现乱码问题的解决
    tomcat服务器开启gzip功能的方法
    asp.net 操作word
    asp.net webservice 返回json数据乱码解决方法
    阿里云服务器mysql修改编码问题
    阿里云服务器问题:访问
    EasyUI 在aspx页面显示高度不正常解决办法
    C# 或 JQuery导出Excel
    如何分离数据库 (SQL Server Management Studio)
  • 原文地址:https://www.cnblogs.com/streetlive/p/12643347.html
Copyright © 2011-2022 走看看