I am reading Stuart Sutherland's RTL Modelling with System Verilog and up to this point have had no trouble understanding. However, unless I missed it, it seems he has gone way too quickly over functions and tasks in SystemVerilog. I am therefore left with (at least) two questions:

(1) My understanding is that (unless ref is used), SystemVerilog functions are pass-by-value. Is the same true of tasks?

(2) What do the input and output keywords do for function arguments? Consider the snippet below. There is no return (void), so shouldn't I conclude (given (1) regarding pass-by-value) that this function cannot affect things outside? I ask because in the flow of discussion about the snippet, no such comment is made, and so it seems like I should conclude that any formal argument marked with output will be such that it affects things "outside" the function. Is this right?

parameter N = 32;
function automatic void sum_to_endpoint_f (output [N—1:0] result,
input [$clog2(N)-1:0] endpoint,
input [N—1:0] data_array [64] // look-up-table array 
  result = data_array[0];
  if (endpoint == 0) return; // exit the function early 
  for (int i=1; i<=63; i++) begin
    result = result + data_array[i];
    if (i == endpoint) return; // exit the function early 

2 Answers 2


(1) Yes, (unless ref is used), SystemVerilog tasks are pass-by-value.

Refer to IEEE Std 1800-2017, section 13.3 Tasks:

input // copy value in at beginning

(2) A function input behaves as above: "copy value in at beginning". The input is not affected outside the function. The output will update the variable assigned when the function is called.

If this is how you call the function:

sum_to_endpoint_f(.result(myresult), ...);

then, myresult outside the function will be assigned to the final value of result inside the function.

output // copy value out at end

  • \$\begingroup\$ Thanks very much for your answer. I guess then I don't understand in what sense this can all be called pass-by-value then. Are you saying that input really leads to pass by value whereas output is a sort of pass by reference (in the sense that what happens to the signal inside the function affects the outside)? \$\endgroup\$
    – EE18
    Feb 3 at 16:13
  • \$\begingroup\$ @EE18: You're welcome. I updated the answer. To get a better feel, run some simulations with actual code. If you get unexpected results, you can post a new question. \$\endgroup\$
    – toolic
    Feb 3 at 16:32

In SystemVerilog, task and function arguments have the same semantics and are independent of a function returning a value or not. This was not the same in legacy Verilog where functions were required to have at least one input and return a value (there was no void return). A task never returns a value, may consume time, so it can't be used in an expression like a non-void function.

Most other programming language don't distinguish between tasks and functions; they are just routines. And all routines pass their arguments by values. If you need to modify an actual argument, you pass a pointer to the argument.

There are no pointers in SystemVerilog. You specify the direction you want values to pass argument values using input, output, or inout qualifiers. Input arguments get copied by value upon entry task/function and output arguments get copied upon exit. Inouts get copied both upon entry and exit.

There are only two main use cases where you would want to use a ref argument. One is where you have a very large array and only plan to access some of the elements. This saves the copying time. The other is in a time consuming task where you need the argument's actual value to keep syncing while the task is active. (e.g. passing a clock to a task and waiting for its posedge inside the task.

  • \$\begingroup\$ Beautiful, thank you so much for this very helpful answer! In terms of the distinction then between tasks and (void) functions, is the only difference that (void) functions syntactically enforce that no time be consumed or are there other differences? \$\endgroup\$
    – EE18
    Feb 3 at 23:33
  • \$\begingroup\$ There is one other difference I can think of when called from within a synthesizable always_comb block. The implicit sensitivity includes the expansion of a function (void or non-void). It does not do this for a task. \$\endgroup\$
    – dave_59
    Feb 4 at 4:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.