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 disable does, and the two things it can target: a named block or a task
  • How disable's scope differs fundamentally from disable fork's scope
  • Why disabling a named block or task can affect completely unrelated processes that happen to share the same name
  • How disable can 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 a begin/end (or fork/join) block, written as begin : block_name
  • task_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:

  • disable terminates 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 disable risky, since it can terminate unrelated executions that happen to share the same name
  • A task can call disable on itself as an early exit, though return is the more idiomatic choice for that specific case
Did you find what you were looking for?

Quiz

Test Your Knowledge

1 of 5 Score: 0 / 5