disable fork terminates every process spawned by the calling process, and the process class lets you target one specific process handle. The plain disable statement is a third option: it terminates a currently-executing block or task by name — but it works on a completely different kind of scope than either of those, which makes it easy to misuse without realizing it.
7 min read | Intermediate Level
What You'll Learn
- What
disabledoes, and the two things it can target: a named block or a task - How
disable's scope differs fundamentally fromdisable fork's scope - Why disabling a named block or task can affect completely unrelated processes that happen to share the same name
- How
disablecan be used as an early exit from within a task
What Does disable Do?
disable terminates a currently-executing named block or task. If that block or task isn't currently running, disable simply has no effect — it isn't an error, it's a no-op.
Syntax
disable block_name;
disable task_name;
Parameters:
block_name— the label given to abegin/end(orfork/join) block, written asbegin : block_nametask_name— the name of a task
A Basic Example: Disabling One Named Block
Here, three threads run in parallel: a timeout, a background monitor, and a thread that reacts to an early event. When the event fires, it cancels only the timeout by name — the monitor keeps running, completely unaffected:
module tb;
initial begin
fork
begin : timeout_blk
#100 $display ("[%0t] Timeout reached", $time);
end
begin : monitor_blk
forever #10 $display ("[%0t] Monitoring...", $time);
end
begin
#30 $display ("[%0t] Event occurred", $time);
disable timeout_blk; // cancel only the timeout, leave monitor_blk running
end
join_none
#60 $finish;
end
endmodule
timeout_blk is disabled at 30ns, well before it ever reaches its 100ns delay, so its message never prints. monitor_blk keeps running, printing every 10ns right up until the test ends at 60ns — it's never touched, because disable only targets the block named in the statement.
Quick Check
Q: If disable fork; had been used instead of disable timeout_blk; in the example above, what would happen to monitor_blk?
Show Answer
It would be terminated too. disable fork kills every process spawned by the calling process, with no way to spare just one of them — that's exactly the distinction a named disable is for.
Why the Scope Is Different From disable fork
disable fork works on a dynamic scope: it only terminates processes that were actually forked by the calling process, based on the runtime parent-child relationship between them. Named disable works on a completely different, static (lexical) scope instead: it terminates every currently-active execution of that name, anywhere in the design, regardless of which process spawned it or how the two are related at runtime.
This makes named disable genuinely dangerous if the same block label or task name is reused — for example, inside a class method that multiple object instances run concurrently. If two unrelated instances both happen to be executing a block called proc_blk at the same time, calling disable proc_blk; from either one terminates both instances' executions, not just the one that called it. The relationship that matters here is "same name," not "same object" or "same process tree."
Where Is Named disable Used?
Named disable is the right tool when you need to cancel one specific piece of background work — like a single timeout — without disturbing other unrelated monitors or checkers running in the same process, exactly as in the timeout example above. disable fork would be the wrong choice there, since it can't spare anything.
disable as an Early Exit From a Task
A task can also call disable on its own name to exit early:
task check_value (int val);
if (val < 0) begin
$display ("Invalid value, exiting early");
disable check_value;
end
$display ("Processing value: %0d", val);
endtask
This isn't quite the same as a return statement, even though both exit a task early. In everyday use, prefer return for exiting a task from within itself — it's the more idiomatic choice. Named disable is generally reserved for terminating a task or block from outside it, which a plain return can't do at all.
Common Beginner Mistakes
Mistake #1: Assuming disable only affects the instance you're thinking of
As the warning above explains, named disable terminates every currently-active execution of that name, not just the one specific instance or process you had in mind. Why? This is especially dangerous with class methods or module code that gets reused across multiple concurrently-running instances — the same block name in each instance is still just one name as far as disable is concerned.
Mistake #2: Confusing disable with disable fork
One works on a static, name-based scope; the other works on a dynamic, parent-child scope. Why? Picking the wrong one either terminates far more than intended (an unrelated process that happens to share a name) or far less (leaving unrelated background threads spawned by the same process still running when you meant to clear all of them).
Mistake #3: Assuming disable errors out on a block that isn't running
disable on a block or task that isn't currently executing simply does nothing — no error, no warning. Why? This can mask a real bug: if you expect a specific block to be disabled and it silently wasn't (because it had already finished, or never started), nothing in the simulation will tell you that on its own.
Practice Exercise
Challenge: Cancel Only One of Two Named Blocks
Extend the timeout/monitor example from earlier with a second independent named block, backup_blk, that prints a message after 50ns. At 30ns, cancel only timeout_blk — both monitor_blk and backup_blk should keep running untouched.
Hint
Give the new block its own distinct label, and fork it alongside the others — disabling one name by itself has no effect on differently-named blocks.
Solution
module tb;
initial begin
fork
begin : timeout_blk
#100 $display ("[%0t] Timeout reached", $time);
end
begin : monitor_blk
forever #10 $display ("[%0t] Monitoring...", $time);
end
begin : backup_blk
#50 $display ("[%0t] Backup check complete", $time);
end
begin
#30 $display ("[%0t] Event occurred", $time);
disable timeout_blk;
end
join_none
#60 $finish;
end
endmodule
Only timeout_blk is named in the disable statement, so it's the only one affected — backup_blk still prints at 50ns, and monitor_blk keeps ticking every 10ns for the rest of the test, exactly as before.
Summary
In this tutorial, you learned:
disableterminates a currently-executing named block or task; it does nothing if that name isn't currently active- Its scope is static (lexical): it affects every currently-active execution of that name, anywhere, not just one specific process or instance
- This is fundamentally different from
disable fork's scope, which is dynamic and limited to the calling process's own descendants - Reusing a block label or task name across multiple concurrently-running instances makes named
disablerisky, since it can terminate unrelated executions that happen to share the same name - A task can call
disableon itself as an early exit, thoughreturnis the more idiomatic choice for that specific case