Verilog · Chapter 15.4 · Tasks & Functions
Re-entrant Tasks & Functions in Verilog — Recursion & Concurrency
A task or function is re-entrant if it can be safely entered again while a previous call is still in progress, that is, called recursively when it calls itself or concurrently when the same routine runs from several parallel processes at once. Reentrancy requires that each invocation have its own storage, which is exactly what the automatic keyword provides. This makes two patterns possible. Recursion is where a function calls itself with a smaller problem, such as a factorial or a tree traversal, and each level must keep its own locals. Concurrent calls are where the same task runs from multiple fork threads or always blocks at once, and each thread's call must stay independent. This page explains re-entrancy, the automatic requirement, recursive functions, and concurrent task calls.
Foundation11 min readVerilogReentrantRecursionautomaticConcurrency
Chapter 15 · Section 15.4 · Tasks & Functions
1. The Engineering Problem
A recursive function, or a task called from several places at once, must be re-entrant — safe to enter again while a prior call is still running — which requires per-call storage:
A re-entrant task/function can be safely called recursively or concurrently, which requires
automaticstorage (per-call). A recursive or concurrently-calledstaticroutine corrupts its locals.
This page drills re-entrancy with automatic.
2. Mental Model — Each Active Call Needs Its Own Storage
3. Recursion — A Function Calling Itself
Recursion requires automatic so each level keeps its own locals:
// factorial — recursive, needs automatic:
function automatic integer factorial;
input integer n;
if (n <= 1) factorial = 1;
else factorial = n * factorial(n - 1); // calls itself
endfunction
// usage (elaboration-time constant, e.g. for a parameter):
localparam DEPTH = factorial(4); // = 24Each recursive call of factorial is active while it waits for the deeper call to return, so each needs its own n — which automatic provides. A static factorial would share one n across all levels and compute garbage. Recursion is used for compile-time computations (factorials, logs) and recursive structure generation.
4. Concurrency — The Same Routine in Parallel
Concurrent calls require automatic so the parallel invocations stay independent:
task automatic drive_channel; // automatic → reentrant
input integer ch;
input [7:0] data;
reg [7:0] local_buf; // per-call (independent across channels)
begin
local_buf = data;
@(posedge clk);
bus[ch] = local_buf;
end
endtask
// called concurrently for several channels:
initial fork
drive_channel(0, 8'hA1);
drive_channel(1, 8'hB2);
drive_channel(2, 8'hC3);
joinThe three drive_channel calls run concurrently (from the fork...join), each with its own local_buf because the task is automatic. A static version would share local_buf, and the channels would corrupt each other. Concurrent, reentrant tasks are common in testbenches that drive several interfaces in parallel.
Visual A — reentrancy needs automatic
Reentrancy — recursion and concurrency need automatic
data flow5. Common Mistakes
- Recursion without
automatic— shared static storage corrupts the levels (§3, DebugLab 1). - Concurrent calls without
automatic— parallel invocations clobber (§4, 15.3). - Assuming static is reentrant — it is not; only
automaticis (§2).
6. Debugging Lab
One reentrancy debug post-mortem
7. Interview Q&A
8. Exercises
Exercise 1 — Make it recursive
Write a recursive automatic function that computes 2^n for a constant n.
Exercise 2 — Why automatic?
Explain why a static recursive function corrupts its state but an automatic one does not.
Exercise 3 — Concurrent task
A task is called from three parallel fork threads and gives intertwined results. What declaration fixes it?
9. Summary
Re-entrant tasks and functions are safe to call recursively or concurrently:
- Reentrancy requires
automatic— per-call storage so overlapping calls are independent. - Recursion — a self-calling function needs each level's own storage (
automatic). - Concurrency — the same task in parallel processes needs per-call storage (
automatic). staticis not re-entrant — shared storage corrupts overlapping calls.
The last sub-topic covers advanced patterns: Chapter 15.5 Tasks & Functions Advanced drills synthesis rules, passing arrays, and combining tasks/functions.
Related Tutorials
- Static & Automatic Behaviour — Chapter 15.3; the storage axis reentrancy builds on.
- Function — Chapter 15.1; recursive functions.
- Task — Chapter 15.2; concurrent tasks.
- Tasks & Functions — Chapter 15 overview; where static, automatic, and re-entrant behaviour fit in the chapter.
- Sequential & Parallel Blocks — Chapter 14.2; the
fork...joinconcurrency requiringautomatic.