Verification access to hierarchical signals without using hierarchical instance names

In reply to JoelG:
You use the “%m” format specifier to get the name of the instance you are bound into. You can build a list of instances your are bound into using an associative array. And interfaces have no such restrictions about always blocks.

module node;
  logic x;
endmodule

interface node_tracker;
  initial top.list[$sformatf("%m")]=node.nt;
  always @(node.x) $display("%m x has been set %d",node.x);
  function void setx();
    node.x = $urandom_range(1);
  endfunction
endinterface

module top;
  
  node n[5][5]();
  
  bind node node_tracker nt();
  
  virtual node_tracker list[string];
  
  initial begin
    #10
    foreach(list[s]) list[s].setx;
  end
endmodule

Here is a similar abstract/concrete class based approach taken from my DVCon 2012 paper

package tracker;
virtual class abstract;
  static abstract list[string];
  function new(string name);
    list[name] = this;
  endfunction
  pure virtual function void setx;
endclass
endpackage    

module node;
  logic x;
endmodule

module node_tracker;
  always @(node.x) $display("%m x has been set %d",node.x);
  class concrete extends tracker::abstract;
    function new(string name);
      super.new(name);
    endfunction
    function void setx();
      node.x = $urandom_range(1);
    endfunction
  endclass
  concrete c = new($sformatf("%m"));
endmodule

module top;
  import tracker::*;
  node n[5][5]();
  
  bind node node_tracker nt();
  
  initial begin
    foreach(abstract::list[s]) abstract::list[s].setx;
  end
  
endmodule