10. Tasks and functions
Frm: IEEE Std 1364™-2001, IEEE Standard Verilog® Hardware Description Language
10. Tasks and functions
Tasks and functions provide the ability to execute common procedures from several different places in a description. They also provide a means of breaking up large procedures into smaller ones to make it easier to read and debug the source descriptions. This clause discusses the differences between tasks and functions, describes how to define and invoke tasks and functions, and presents examples of each.
10.1 Distinctions between tasks and functions
The following rules distinguish tasks from functions:
— A function shall execute in one simulation time unit; a task can contain time-controlling statements.
— A function cannot enable a task; a task can enable other tasks and functions.
— A function shall have at least one input type argument and shall not have an output or inout type
argument; a task can have zero or more arguments of any type.
— A function shall return a single value; a task shall not return a value.
The purpose of a function is to respond to an input value by returning a single value. A task can support multiple goals and can calculate multiple result values. However, only the output or inout type arguments pass result values back from the invocation of a task. A function is used as an operand in an expression; the value of that operand is the value returned by the function.
Example:
Either a task or a function can be defined to switch bytes in a 16-bit word. The task would return the switched word in an output argument, so the source code to enable a task called switch_bytes could look like the following example:
switch_bytes (old_word, new_word);
The task switch_bytes would take the bytes in old_word, reverse their order, and place the reversed bytes in new_word.
A word-switching function would return the switched word as the return value of the function. Thus, the function call for the function switch_bytes could look like the following example:
new_word = switch_bytes (old_word);
10.2 Tasks and task enabling
A task shall be enabled from a statement that defines the argument values to be passed to the task and the variables that receive the results. Control shall be passed back to the enabling process after the task has completed. Thus, if a task has timing controls inside it, then the time of enabling a task can be different from the time at which the control is returned. A task can enable other tasks, which in turn can enable still other tasks—with no limit on the number of tasks enabled. Regardless of how many tasks have been enabled, control shall not return until all enabled tasks have completed.
10.2.1 Task declarations
The syntax for defining tasks is given in Syntax 10-1.
task_declaration ::= (From Annex A - A.2.7)
task [ automatic ] task_identifier ;
{ task_item_declaration }
statement
endtask
| task [ automatic ] task_identifier ( task_port_list ) ;
{ block_item_declaration }
statement
endtask
Syntax 10-1—Syntax for task declaration
There are two alternate task declaration syntaxes.
The first syntax shall begin with the keyword task, followed by the optional keyword automatic, followed by a name for the task and a semicolon, and ending with the keyword endtask. The keyword automatic declares an automatic task that is reentrant with all the task declarations allocated dynamically for each concurrent task entry. Task item declarations can specify the following:
— Input arguments
— Output arguments
— Inout arguments
— All data types that can be declared in a procedural block
The second syntax shall begin with the keyword task, followed by a name for the task and a parenthesis enclosed task_port_list. The task_port_list shall consist of zero or more comma separated task_port_items. There shall be a semicolon after the close parenthesis. The task body shall follow and then the keyword endtask.
In both syntaxes, the port declarations shall have the same syntax as defined by the tf_input_declaration, tf_output_declaration and tf_inout_declaration, as detailed in Syntax 10-1 above. Tasks without the optional keyword automatic are static tasks, with all declared items being statically allocated. These items shall be shared across all uses of the task executing concurrently. Task with the optional keyword automatic are automatic tasks. All items declared inside automatic tasks are allocated dynamically for each invocation. Automatic task items can not be accessed by hierarchical references. Automatic tasks can be invoked through use of their hierarchical name.
10.2.2 Task enabling and argument passing
The task enabling statement shall pass arguments as a comma-separated list of expressions enclosed in parentheses. The formal syntax of the task enabling statement is given in Syntax 10-2.
task_enable ::= (From Annex A - A.6.9)
hierarchical_task_identifier [ ( expression { , expression } ) ] ;
Syntax 10-2—Syntax of the task enabling statement
The list of arguments for a task enabling statement shall be optional. If the list of arguments is provided, the list shall be an ordered list of expressions that has to match the order of the list of arguments in the task definition.
If an argument in the task is declared as an input, then the corresponding expression can be any expression. The order of evaluation of the expressions in the argument list is undefined. If the argument is declared as an output or an inout, then the expression shall be restricted to an expression that is valid on the left-hand side of a procedural assignment (see 9.2). The following items satisfy this requirement:
— reg, integer, real, realtime, and time variables
— Memory references
— Concatenations of reg, integer, real, realtime and time variables
— Concatenations of memory references
— Bit-selects and part-selects of reg, integer, and time variables
The execution of the task enabling statement shall pass input values from the expressions listed in the enabling statement to the arguments specified within the task. Execution of the return from the task shall pass values from the task output and inout type arguments to the corresponding variables in the task enabling statement. All arguments to the task shall be passed by value rather than by reference (that is, a pointer to the value).
Example:
Example 1—The following example illustrates the basic structure of a task definition with five arguments.
task my_task; input a, b; inout c; output d, e; begin . . . // statements that perform the work of the task . . . c = foo1; // the assignments that initialize result regs d = foo2; e = foo3; end endtask
Or using the second form of a task declaration, the task could be defined as:
task my_task (input a, b, inout c, output d, e); begin . . . // statements that perform the work of the task . . . c = foo1; // the assignments that initialize result regs d = foo2; e = foo3; end endtask
The following statement enables the task:
my_task (v, w, x, y, z);
The task enabling arguments (v, w, x, y, and z) correspond to the arguments (a, b, c, d, and e) defined by the task. At task enabling time, the input and inout type arguments (a, b, and c) receive the values passed in v, w, and x. Thus, execution of the task enabling call effectively causes the following assignments:
a = v; b = w; c = x;
As part of the processing of the task, the task definition for my_task shall place the computed result values into c, d, and e. When the task completes, the following assignments to return the computed values to the calling process are performed:
x = c; y = d; z = e;
Example 2—The following example illustrates the use of tasks by describing a traffic light sequencer:
module traffic_lights; reg clock, red, amber, green; parameter on = 1, off = 0, red_tics = 350, amber_tics = 30, green_tics = 200; // initialize colors. initial red = off; initial amber = off; initial green = off; always begin // sequence to control the lights. red = on; // turn red light on light(red, red_tics); // and wait. green = on; // turn green light on light(green, green_tics); // and wait. amber = on; // turn amber light on light(amber, amber_tics); // and wait. end // task to wait for ’tics’ positive edge clocks // before turning ’color’ light off. task light; output color; input [31:0] tics; begin repeat (tics) @ (posedge clock); color = off; // turn light off. end endtask always begin // waveform for the clock. #100 clock = 0; #100 clock = 1; end endmodule // traffic_lights.