10. Tasks and functions(任务和函数)

101 篇文章 11 订阅

10. Tasks and functions

任务和函数提供了从描述中的几个不同位置执行通用过程的能力。它们还提供了一种将大型过程分解为小型过程的方法,以便于阅读和调试源代码描述。本条款讨论了任务和函数之间的差异,描述了如何定义和调用任务和函数,并给出了每个任务和函数的示例

10.1任务和函数之间的区别
以下规则将任务与功能区分开来:

——函数应在一个仿真时间单位内执行;任务可以包含时间控制语句。
——函数无法启用任务;任务可以启用其他任务和函数。
——函数应至少有一个输入类型参数,并且不应有inout类型参数;任务可以有零个或多个任何类型的参数。
——函数应返回单个值;任务不应返回值。

        函数的目的是通过返回单个值来响应输入值。一个任务可以支持多个目标,并可以计算多个结果值。但是,只有output或inout类型的参数将任务调用的结果值传回。函数用作表达式中的操作数;该操作数的值是函数返回的值。

10.2Tasks and task enabling(任务和任务启用)
        应通过定义要传递给任务的参数值和接收结果的变量的语句启用任务。任务完成后,控制权应传回启用过程。因此,如果任务内部有计时控件,则启用任务的时间可能与返回控件的时间不同。一个任务可以启用其他任务,这反过来又可以启用其他的任务,而不限制启用的任务数。无论启用了多少任务,在所有启用的任务完成之前,控制都不应返回。

10.2.1 Task declarations

语法10-1给出了定义任务的语法。

task_declaration ::= (From A.2.7)
    task [ automatic ] task_identifier ; 
        { task_item_declaration } 
        statement_or_null 
    endtask 
    
    | task [ automatic ] task_identifier ( [ task_port_list ] ) ; 
        { block_item_declaration } 
        statement_or_null 
      endtask  
    task_item_declaration ::= 
        block_item_declaration 
        | { attribute_instance }   tf_ input_declaration ; 
        | { attribute_instance }   tf_output_declaration ; 
        | { attribute_instance }   tf_inout_declaration ; 
    task_port_list ::= 
        task_port_item { , task_port_item } 
    task_port_item ::= 
        { attribute_instance } tf_input_declaration 
        | { attribute_instance } tf_output_declaration 
        | { attribute_instance } tf_inout_declaration 
    tf_input_declaration  ::=
        input [ reg ] [ signed ] [ range ] list_of_port_identifiers 
        | input task_port_type list_of_port_identifiers
    tf_output_declaration ::=
        output [ reg ] [ signed ] [ range ] list_of_port_identifiers 
        | output task_port_type list_of_port_identifiers
    tf_inout_declaration  ::=
        inout [ reg ] [ signed ] [ range ] list_of_port_identifiers 
        | inout task_port_type list_of_port_identifiers
    task_port_type ::=
        integer | real | realtime | time
    block_item_declaration ::= (From A.2.8)
        {attribute_instance} reg [signed] [range] list_of_block_variable_identifiers ;  
        | { attribute_instance } integer list_of_block_variable_identifiers ;  
        | { attribute_instance } time list_of_block_variable_identifiers ;  
        | { attribute_instance } real list_of_block_real_identifiers ;  
        | { attribute_instance } realtime list_of_block_real_identifiers ;  
        | { attribute_instance } event_declaration  
        | { attribute_instance } local_parameter_declaration ;
        | { attribute_instance } parameter_declaration ;
    list_of_block_variable_identifiers ::=
        block_variable_type { , block_variable_type }
    list_of_block_real_identifiers ::=
        block_real_type { , block_real_type }
    block_variable_type ::=
        variable_identifier { dimension }
    block_real_type ::=
        real_identifier { dimension }

语法10-1-任务声明的语法

有两种可选的任务声明语法。
第一个语法应以关键字task开头,然后是可选的关键字automatic,然后是任务名称和分号,最后是关键字endtask。
关键字automatic声明一个可重入的自动任务,所有任务声明都为每个并发任务条目动态分配。任务项声明可以指定以下内容:

——输入参数

——输出参数

——inout参数

——所有可以在过程块中声明的数据类型

        第二个语法应以关键字task开头,后跟任务名称和括号task_port_list。task_port_list应由零个或多个逗号分隔的task_port项组成。右括号后应有分号。任务正文应跟随关键字endtask。

        没有可选关键字automatic的任务是静态任务,所有声明的项都是静态分配的。这些项目应在同时执行的任务的所有用途中共享。带有可选关键字automatic的任务是自动任务。自动任务中声明的所有项都为每次调用动态分配。层次结构引用无法访问自动任务项。自动任务可以通过使用其分层名称来调用。

10.2.2 Task enabling and argument passing(任务启用和参数传递)

任务启用语句应以括号中逗号分隔的表达式列表形式传递参数。任务启用语句的形式语法在语法10-2中给出。

task_enable ::= (From A.6.9)
    hierarchical_task_identifier [ ( expression { , expression } ) ] ;

语法10-2-任务启用语句的语法

        如果任务定义没有参数,则不应在任务启用语句中提供参数列表。否则,表达式的有序列表应与任务定义中参数列表的长度和顺序相匹配。空表达式不能用作任务启用语句中的参数。如果任务中的参数被声明为输入,那么相应的表达式可以是任何表达式。参数列表中表达式的求值顺序未定义。如果参数声明为输出或inout,则表达式应限制为在过程赋值左侧有效的表达式(见9.2)。
以下项目满足此要求:

——寄存器、整数、实数、实数时间、时间变量(reg, integer, real, realtime, and time variables)

——内存引用(Memory references)

——寄存器,整数和时间变量的拼接(Concatenations of reg, integer, and time variables)

——内存引用的拼接(Concatenations of memory references)

——寄存器,整数和时间变量的位选或者部分选择(Bit-selects and part-selects of reg, integer, and time variables)

        任务启用语句的执行应将启用语句中列出的表达式的输入值传递给任务中指定的参数。任务返回的执行应将任务输出和inout类型参数的值传递给任务启用语句中的相应变量。任务的所有参数应通过值而不是引用(即指向值的指针)传递。

例如:示例1—以下示例说明了具有五个参数的任务定义的基本结构:

Example1:
task my_task; 
    input a, b; 
    inout c; 
    output d, e; 
        begin   
            . . .      //执行任务工作的语句
            . . .
            c = foo1; //初始化结果寄存器的赋值
            d = foo2;
            e = foo3;
        end
endtask 

或者使用任务声明的第二种形式,任务可以定义如下:

task my_task (input a, b, inout c, output d, e); 
    begin
        . . .   //执行任务工作的语句
                
        . . .
        c = foo1; // //初始化结果寄存器的赋值
        d = foo2;
        e = foo3;
    end
endtask

以下语句启用该任务:my_task (v, w, x, y, z);

        任务启用参数(v、w、x、y和z)对应于任务定义的参数(a、b、c、d和e)。在任务启用时,输入和输出类型参数(a、b和c)接收在v、w和x中传递的值。因此,任务启用调用的执行有效地导致以下分配:

a = v;   b = w;   c = x;

作为任务处理的一部分,my_task的任务定义应将计算的结果值放入c、d和e中。任务完成后,将执行以下分配以将计算值返回到调用进程:

x = c;  y = d;  z = e;

10.2.3Task memory usage and concurrent activation(任务内存使用和并发激活)
        一个任务可以同时启用多次。自动任务的所有变量应在每次并发任务调用时复制,以存储特定于该调用的状态。静态任务的所有变量都应是静态的,因为模块实例中每个声明的局部变量都应对应一个变量,而不管任务的并发激活次数如何。然而,模块不同实例中的静态任务应具有彼此独立的存储。静态任务中声明的变量,包括输入、输出和inout类型参数,应在调用之间保留其值。应将其初始化为4.2.2中所述的默认初始化值。自动任务中声明的变量(包括输出类型参数)应在执行进入其范围时初始化为默认初始化值。输入和输出类型参数应初始化为从与任务启用语句中列出的这些参数对应的表达式传递的值。

        由于在自动任务中声明的变量在任务调用结束时被释放,因此它们不应用于在该点之后可能引用它们的某些构造中:

—不得使用非阻塞赋值或程序连续赋值来赋值。
—程序性连续赋值或程序性强制声明不得引用它们。

—非阻塞赋值的分配内事件控制中不得引用它们。
—不得使用$monitor和$dumpvars等系统任务跟踪它们。

10.310.3 Disabling of named blocks and tasks(禁用命名块和任务)
        disable语句提供终止与并发活动过程相关联的活动的能力,同时保持Verilog HDL过程描述的结构化性质。disable语句提供了一种机制,用于在任务执行其所有语句之前终止任务、中断循环语句或跳过语句以继续循环语句的另一次迭代。它对于处理硬件中断和全局重置等异常情况非常有用。disable语句的语法形式如语法10-3所示。

disable_statement ::= (From A.6.5)
    disable hierarchical_task_identifier ; 
    | disable hierarchical_block_identifier ; 

语法10-3-disable语句的语法

        任何形式的disable语句都应终止任务或命名块的活动。应在块后的语句或任务启用语句后恢复执行。命名块或任务中启用的所有活动也应终止。如果任务启用语句是嵌套的(即,一个任务启用另一个任务,另一个启用另一任务),那么禁用链中的任务将禁用链中向下的所有任务。如果一个任务被多次启用,则禁用该任务应禁用该任务的所有激活。如果任务被禁用,则不会指定任务可以启动的以下活动的结果:

—输出和inout参数的结果
—计划但未执行的非阻塞赋值
—程序性连续赋值(赋值和force 语句)

        disable语句可以在块和任务中使用,以禁用包含disable语句的特定块或任务。disable语句可以用于禁用函数中的命名块,但不能用于禁用函数。如果函数中的disable语句禁用调用该函数的块或任务,则该行为未定义。对于任务的所有并发执行,禁用自动任务或自动任务内的块与常规任务相同。
例如:示例1—此示例说明了块如何禁用自身。

示例1:
begin : block_name
    rega = regb;
    disable block_name;
    regc = rega; // 这个赋值语句将永远不会执行

示例2-此示例显示了在命名块中使用的disable语句,其方式类似于forward goto。disable语句之后执行的下一条语句是命名块之后的语句。

示例2:
begin : block_name
    ...
    ...
    if (a == 0)
        disable block_name;
        ...
   end  // end of named block
    // continue with code following named block
    ...

示例3-此示例显示disable语句用作任务的早期返回。然而,使用disable语句禁用自身的任务并不是编程语言中return语句的简写。

示例3:
task proc_a; 
    begin
        ...
        ...
    if (a == 0)
        disable proc_a; // return if true
    ...
    ...
    end
endtask

示例4-此示例显示了disable语句的使用方式与C编程语言中的两个语句continue和break相同。该示例说明了允许命名块执行的控制代码,直到循环计数器达到n次迭代,或者直到变量a设置为值b。命名的块中断包含执行到a==b的代码,此时禁用中断;语句终止该块的执行。命名块continue包含为for循环的每次迭代执行的代码。每次此代码执行disable continue时;continue块终止,语句执行传递到for循环的下一次迭代。对于continue块的每次迭代,如果(a!=0),则执行一组语句。如果(a!=b),则执行另一组语句。

示例4:
begin :break
    for(i=0 ;i<n; i=i+1) begin :continue
        @clk
            if(a==0)  //"continue" loop
                disable continue;
            statements
            statements
        @clk
            if(a==b) //"break" from loop
                disable break;
             statement
             statement
        end
    end

示例5—此示例显示了当reset事件发生时,用于同时禁用时序控制序列和任务动作的禁用语句。
该示例显示了一个fork-join块,其中有一个命名的顺序块(event_expr)和一个等待事件reset发生的disable语句。顺序块和等待reset并行执行。event_expr块等待事件ev1的一次出现和事件trig的三次出现。当这四个事件发生后,再加上d个时间单位的延迟,任务操作就会执行。当事件reset发生时,无论顺序块中的事件如何,fork-join块都会终止,包括任务操作。

示例5:
fork
    begin : event_expr
        @ev1;
        repeat (3) @trig;
        #d action (areg, breg);
    end
    @reset disable event_expr;
join

例6—这个示例是可再触发单稳态的行为描述。命名的事件retrig重新启动单稳态时间段。如果retrig在250个时间单位内继续发生,则q将保持为1。

示例6:
always begin : monostable
    #250 q=0;
    end
always @retrig begin
    disable monostable;
    q=1;
end

10.4函数和函数调用
函数的目的是返回要在表达式中使用的值。本条款的其余部分解释了如何定义和使用函数。
10.4.1函数声明
语法10-4给出了定义函数的语法。

function_declaration ::= (From A.2.6)
    function [ automatic ] [ function_range_or_type ] 
        function_identifier ; 
        function_item_declaration { function_item_declaration } 
        function_statement 
    endfunction 

    | function [ automatic ] [ function_range_or_type ] 
        function_identifier ( function_port_list ) ; 
        { block_item_declaration } 
    function_statement 
    endfunction
function_item_declaration ::= 
    block_item_declaration 
    | { attribute_instance } tf_input_declaration ; 
function_port_list ::= 
    { attribute_instance } tf_input_declaration 
    { , { attribute_instance }tf_input_declaration }
     tf_input_declaration  ::=
        input [ reg ] [ signed ] [ range ] list_of_port_identifiers 
        | input  task_port_type list_of_port_identifiers
function_range_or_type ::= 
      [ signed ] [ range ] 
    | integer 
    | real 
    | realtime 
    | time  
block_item_declaration ::= (From A.2.8)
    { attribute_instance } reg [ signed ] [ range ]  list_of_block_variable_identifiers ;                  
    | { attribute_instance } integer list_of_block_variable_identifiers ;  
    | { attribute_instance } time list_of_block_variable_identifiers ;  
    | { attribute_instance } real list_of_block_real_identifiers ;  
    | { attribute_instance } realtime list_of_block_real_identifiers ;  
    | { attribute_instance } event_declaration  
    | { attribute_instance } local_parameter_declaration ;
    | { attribute_instance } parameter_declaration ;
list_of_block_variable_identifiers ::=
    block_variable_type { , block_variable_type }
list_of_block_real_identifiers ::=
    block_real_type { , block_real_type }
block_variable_type ::=
    variable_identifier { dimension }
block_real_type ::=
    real_identifier { dimension }

语法10-4-函数声明语

        函数定义应该以关键字function开始,然后是可选关键字automatic,接下来是从函数返回的可选项function_range_or_type,接下来是函数的名字,在接下来既可以是分号或者花括号包含的端口列表然后紧接着是一个分号,最后以关键字endfunction结束。

        使用function_range_or_type是可选的,没有function_range_or_type的函数将函数返回值默认设置为标量。如果使用,function_range_or_type应该说明函数返回值是real,整数型,time,realtime,或者是位宽为[n:m]比特的向量(可选的)。

函数至少应该有一个输入声明;

        关键字automatic声明一个可重入的自动函数,所有函数声明都为每个并发函数调用动态分配。自动函数中的变量不能通过层次化引用来访问。自动函数可以通过使用其层次化名称来调用。

        函数输入应声明为两种方式之一。第一个方法应具有函数名称,后跟分号。分号之后,应跟随一个或多个可选地与块项声明混合的输入声明。在函数项声明之后,应该有一个行为语句,然后是endfunction关键字。第二个方法应该有函数名,后跟一个左括号和一个或多个输入声明,用逗号分隔。在所有输入声明之后,应该有一个右括号和一个分号。分号之后,应该有零个或多个块项声明,后面是行为语句,然后是endfunction关键字。

例如:以下示例使用范围说明定义了一个名为getbyte的函数:

function [7:0] getbyte; 
    input [15:0] address; 
        begin
            // code to extract low-order byte from addressed word
         . . .
        getbyte = result_expression;
    end
endfunction

或者使用函数声明的第二种形式,函数可以定义如下:

function [7:0] getbyte (input [15:0] address); 
    begin
    // code to extract low-order byte from addressed word 
        . . .
        getbyte = result_expression;
    end
endfunction

10.4.2从函数返回值
        函数定义应隐式声明函数内部的变量,其名称与函数相同。此变量默认为1位寄存器型变量,或与函数声明中指定的类型相同。函数定义通过将函数结果分配给与函数同名的内部变量来初始化函数的返回值。在声明函数的作用域中声明另一个与函数同名的对象是非法的。在函数内部,有一个带有函数名称的隐含变量,可以在函数内的表达式中使用。因此,在函数范围内声明另一个与函数同名的对象也是非法的。
10.4.1示例中的以下一行说明了这一概念:

getbyte = result_expression;

10.4.3函数调用(Calling a function)

        函数调用是表达式中的操作数。函数调用具有语法10-5中给出的语法。

function_call ::= (From A.8.2)
    hierarchical_function_identifier{ attribute_instance } ( expression { , expression } )

语法10-5-函数调用语法

函数调用参数的求值顺序未定义。
例如:下面的示例通过拼接对函数getbyte(在10.4.1中定义)的两次调用的结果来创建一个单词:

word = control ? {getbyte(msbyte), getbyte(lsbyte)}:0;

10.4.4函数规则
函数比任务更有限。以下规则管理它们的使用:

a)函数定义不应包含任何时间控制的语句,也就是任何包含#,@或者wait的语句;

b)函数不应该使能tasks;

c)一个函数定义应该至少包含一个input参数;

d)一个函数定义不应该有任何声明为output或者inout的参数;

e)一个函数不应该有任何非阻塞赋值或者过程连续赋值语句;

f)一个函数不应该有任何事件触发;

例如:这个例子定义了称作factorial的函数,它返回一个整数值。这个factorial函数被迭代调用并且打印结果;

module tryfact;
// define the function
function automatic integer factorial; input [31:0] operand;
integer i;
if (operand >= 2)
    factorial = factorial (operand - 1) * operand;
else
factorial = 1;
endfunction
// test the function integer result; integer n;
initial begin
    for (n = 0; n <= 7; n = n+1) begin
        result = factorial(n);
        $display("%0d factorial=%0d", n, result);
        end
    end
endfunction

仿真结果如下:

0 factorial=1
1 factorial=1
2 factorial=2
3 factorial=6
4 factorial=24 
5 factorial=120 
6 factorial=720 
7 factorial=5040

10.4.5 Use of constant functions

        常量函数调用用于支持在细化时构建复杂的值计算(参见12.8)。常量函数调用应为调用模块本地常量函数的函数调用,其中函数的参数为常量表达式。常量函数是正常Verilog函数的子集,应满足以下约束:

—它们不应包含层次引用。

—常量函数内调用的任何函数都应是当前模块本地的常量函数。

—调用constant_expression中允许的任何系统函数都是合法的(见第5条)。对其他系统功能的调用是非法的。

—在常量函数中的所有系统函数都会被忽略。

—函数中使用的所有参数值应在调用常量函数调用之前定义(即,在常量函数调用的求值中使用的任何参数都构成在原始常量函数调用位置使用该参数)。

—所有不是参数或函数的标识符应在当前函数作为本地声明。

—如果它们使用任何直接或间接受defparam语句影响的参数值(参见12.2.1),则结果是未定义的。这可能会产生错误,或者常量函数可能会返回不确定的值。

—它们不得在生成块内声明(见12.4)。

—在任何需要常量表达式的上下文中,它们本身都不应使用常量函数。

        常量函数调用在细化时进行评估。它们的执行对在模拟时或在细化时对函数的多次调用中使用的变量的初始值没有影响。在每一种情况下,变量都会像正常模拟一样被初始化。
例如:本例定义了一个名为clogb2的函数,该函数返回一个整数,其值为日志基数2的上限。

module ram_model (address, write, chip_select, data);
    parameter data_width = 8;
    parameter ram_depth = 256;
    localparam addr_width = clogb2(ram_depth); 
    input [addr_width - 1:0] address;
    input write, chip_select;
    inout [data_width - 1:0] data;
      //define the clogb2 function 
    function integer clogb2;
        input [31:0] value; 
            begin
                   value = value - 1;
                    for (clogb2 = 0; value > 0;  clogb2 = clogb2 + 1)
                        value = value >> 1;
            end
    endfunction

分配了参数的ram_model的实例如下:

ram_model #(32,421) ram_a0(a_addr,a_wr,a_cs,a_data); 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值