The short answer: SystemVerilog keeps Verilog’s rule that functions run in zero time and tasks may consume time, and adds the return statement, void functions, default and named arguments, pass-by-reference with ref and const ref, ANSI-style typed arguments (including structs, arrays and classes) and automatic storage by default in classes. Use ref when a task must see a variable change while it’s running, or to avoid copying large arrays.
The basics of tasks and functions are covered in my Verilog tasks and functions post. Here I focus on what SystemVerilog adds, because those additions are exactly what testbench code relies on.
- What’s new
- return and void functions
- Default and named arguments
- Pass by reference: ref and const ref
- Static vs automatic
- Tasks and functions in RTL
Table of Contents
Part 1: What’s new

Part 2: return and void functions
function automatic int max3(int a, int b, int c);
if (a >= b && a >= c) return a;
return (b >= c) ? b : c;
endfunction
function void print_pkt(packet p); // no return value
$display("len=%0d", p.len);
endfunction
void'(some_function()); // discard a return value explicitly
Part 3: Default and named arguments
task automatic send(input logic [7:0] data,
input int delay = 0,
input bit last = 1'b1);
...
endtask
send(8'hA5); // delay = 0, last = 1
send(.data(8'h3C), .last(1'b0)); // named, any order
Part 4: Pass by reference: ref and const ref
// waits until a flag set by another thread becomes true
task automatic wait_flag(ref bit flag);
wait (flag);
endtask
// no copy of a large array, and the callee can't modify it
function automatic int sum(const ref int data[]);
foreach (data[i]) sum += data[i];
endfunction
- With
input, the argument is copied at the call, so later changes aren’t seen. Withref, the task sees every change immediately. - ref arguments are only allowed in automatic tasks and functions.
const refis the efficient way to pass big arrays or queues read-only.
Part 5: Static vs automatic
Module-level tasks and functions are still static by default, as in Verilog. Methods inside classes are automatic by default. My habit: write automatic on every module-level task and function in testbenches, so parallel calls don’t share variables by accident.
Part 6: Tasks and functions in RTL
Functions remain synthesisable when their body is: they’re useful in always_comb blocks and packages for things like parity, encoding and saturating arithmetic. Tasks in RTL are rare. Keep testbench tasks (with @, # or wait) in testbench files or classes.
Practise this on VLSI Forge
I built VLSI Forge so you can write RTL in your browser, run it on a real simulator and check every signal in the waveform. Free, nothing to install.
Design Verification problems · RTL Design problems
Previous in my SystemVerilog series: SystemVerilog for RTL design. Next: Interfaces, modports and clocking blocks.
FAQ
What is a void function in SystemVerilog?
A function that returns nothing. It still executes in zero time, so it can be called from other functions, unlike a task.
What does ref mean in a SystemVerilog argument?
Pass by reference: the subroutine works on the caller’s variable directly, so changes are visible both ways immediately.
Why must ref arguments be in automatic subroutines?
Because each call needs its own reference; static subroutines share a single set of variables between calls.
Can I pass a class object to a function?
Yes. Class handles are passed by value, but the handle points to the same object, so the function can change the object’s properties.
