The question of disable

Hi all,

Why does ‘im alive’ still get printed when I run the code below?

class test01;
  task kill_proc();
    fork
      begin
        #10ns;
        $display("%0t, im alive", $time());
      end        
      begin
        #5ns;
        disable module01.blk;
      end
    join
  endtask
endclass

module module01;
    initial begin: blk
        test01 t = new();
        t.kill_proc();
    end
endmodule

BR

I believe you may have run into a tool-specific issue. After moving the class definition inside the module, the “im alive” no longer appears, and all tools behave correctly.

There might be some ambiguity in allowing hierarchical references outside a module. Another tool gives an error message. In general, I would avoid disabling named blocks as they cannot be targeted to specific instances of a process.

Thank you Dave.

Sorry, I have a few more questions.

Q1:

module module01;
  initial begin: blk01
      automatic int a = 1000;
  end

  initial begin: blk02
      automatic int a = 100;
      $display("%0d", blk02.a); // 100
      // $display("%0d", blk01.a); // compilation error
  end
endmodule

I understand that blk02.a is a hierarchical reference, so the compiler should resolve it at compile time. But since a within blk02 has a automatic lifetime, it should be created during the simulation phase, I would expect a compilation error. Yet the code successfully prints 100.

Additionally, using blk01.a results in a compilation error, as expected. Why is that?

Q2:

class test01;
  task kill_proc();
    fork
      begin: blk
        #10ns;
        $display("%0t, im alive", $time());
      end        
      begin
        #5ns;
        disable kill_proc.blk;
      end
    join
  endtask
endclass

module module01;
    initial begin
        test01 t = new();
        t.kill_proc();
    end
endmodule

When I changed the code as above, kill_proc.blk is also a hierarchical reference and is also resolved during the elaboration phase. However, the class object is dynamically created during the simulation phase. In theory, it should cause a compilation error, but it actually works.

I’ve been thinking about this problem for a while. I checked the LRM but still couldn’t find a satisfying explanation for why this works. Could you please shed some light on this?

BR

I was wondering — for Q2, is kill_proc.blk resolved as a member select, equivalent to this.kill_proc.blk?

But I still don’t fully understand Q1.

There are a few different concepts getting mixed together here:

  1. The scope where a name is declared
  2. Whether that name can be referenced hierarchically
  3. The lifetime of the object represented by that name
  4. For disable, which active executions are affected

static and automatic affect lifetime. They do not by themselves define the lexical scope of the declaration.

Q1

The key point is that blk02.a is being referenced from within blk02 itself.

The variable a has automatic lifetime, meaning its storage is created when execution enters the block activation and destroyed when execution leaves that activation. But the declaration of a is still known statically from the source code. The compiler can resolve the name blk02.a because it is a qualified reference to a declaration in the current named block scope.

So this is legal:

$display("%0d", blk02.a);

inside blk02, because it is still within the locally declared scope where a exists as a declared name.

By contrast, this is illegal:

$display("%0d", blk01.a);

from inside blk02, because it attempts to reach into a differently named block and access an automatic variable declared there. An automatic variable may be referenced hierarchically only from within its locally declared scope, or from scopes nested inside that scope. It is not available for arbitrary external hierarchical access from a sibling or enclosing scope.

So the issue is not that automatic variables cannot have qualified names. They can. The issue is where the reference is made from.

Q2

The class object is dynamically created, and each call to kill_proc() creates a runtime activation of that task. But the class, task, and named block declarations are all statically known from the source code.

So kill_proc.blk names the block declaration blk inside the task declaration kill_proc. The fact that the object is created dynamically does not prevent the compiler from resolving the name of the declaration.

However, disable kill_proc.blk; does not mean “disable only the blk in this particular object instance” or “disable only my local sibling branch.” It disables all currently active executions of the named block blk declared inside kill_proc.

That distinction matters if kill_proc() is re-entered or called concurrently. Multiple active task calls could each have an active execution of blk, and a named-block disable can terminate all of them.

So:

disable kill_proc.blk;

names a statically declared block, and at runtime disables all active executions of that named block.

If the intent is only to terminate the other branch of the current fork created by the current process, then disable fork is usually the more appropriate construct:

That disables the child processes of the current fork context rather than all active executions of a named block declaration.

Thank you very much for your detailed reply, it has been a great help to me. :grinning_face: