Skip to content

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 automatic storage (per-call). A recursive or concurrently-called static routine 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:

recursion.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   // 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);     // = 24

Each 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:

concurrency.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   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);
   join

The 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 flow
Reentrancy — recursion and concurrency need automaticrecursion(self-call)each level activeconcurrency(parallel calls)each call active→ requireautomaticper-call storagecalls independentre-entrant
Both recursion (a function calling itself, each level still active) and concurrency (the same task called from parallel processes) leave multiple calls active at once. Each active call needs its own storage, which automatic provides — making the routine re-entrant. A static routine shares storage and corrupts overlapping calls.

5. Common Mistakes

  1. Recursion without automatic — shared static storage corrupts the levels (§3, DebugLab 1).
  2. Concurrent calls without automatic — parallel invocations clobber (§4, 15.3).
  3. Assuming static is reentrant — it is not; only automatic is (§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).
  • static is 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.