Hierarchical path using array

 `define **NOC_PATH** Top.Dut.NoC
 
 `define **MOD1** apb
 
 `define MOD2 ram
 
 ...
   
 `define ERR_MOD0 \
   if (rst_qj & `**NOC_PATH**.`**MOD0**.Rst & !is_csr0_inj) begin \
     ... error injection logic
   end \

Now, to avoid having long list of defines - I want to store list of modules names in a string array.

string MOD_NAMES[] = { "axi", "apb", "ram", ... };

However, I’m struggling to use MOD_NAMES[0] (or similar) to dynamically create the hierarchical path like I did with the defines.

I have tried few ways at my end - but couldn’t get expected results. Any suggestions please ?

You cannot use strings to create references to identifiers.
You can create list of class objects that refer to a hierarchical path, and put those objects in a list indexed by a string.

package probe_pkg;
  interface class abstract_probe;
    pure virtual function logic get_Rst;
  endclass
  abstract_probe list[string];
endpackage
  
interface probe #(string name) (input logic p);
  import probe_pkg::*;
  class concrete_probe implements abstract_probe;;
    function new(string name);
      list[name]=this;
    endfunction
    virtual function logic get_Rst();
      return p;
    endfunction
  endclass
  concrete_probe h=new(name);
endinterface

module top;
  
  dut dut();
  
  `define probe(path,name)  bind path.name probe#(`"name`") prb(Rst);
  
  `probe(dut,apb1)
  `probe(dut,ram1)
  `probe(dut,ram2)
  `probe(top,dut)
  
  initial foreach(probe_pkg::list[i])
    $display("%s.Rst is %b",i, probe_pkg::list[i].get_Rst());
endmodule

module dut;
  wire Rst=1;
  apb apb1();
  ram ram1();
  ram ram2();
endmodule

module apb;
  wire Rst=0;
endmodule
module ram;
  wire Rst;
endmodule

See my DVCon paper for more details on how this works.

1 Like
`define NOC_PATH l1_inst

package probe_pkg;
  interface class abstract_probe;
    pure virtual function logic get_Rst;
  endclass
  abstract_probe list[string];
endpackage
  
interface probe #(string name) (input logic p);
  import probe_pkg::*;
  class concrete_probe implements abstract_probe;;
    function new(string name);
      list[name]=this;
    endfunction
    virtual function logic get_Rst();
      return p;
    endfunction
  endclass
  concrete_probe h=new(name);
endinterface

module top;
  level1 l1_inst(); 
  
  initial begin
    l1_inst.l2_inst.t4();
        
    `define probe(path,name) bind path.name probe#(`"name`") prb(rst);
    
    `probe(`NOC_PATH,l2_inst);
    
    if (probe_pkg::list[0].rst)
      $display ("rst is set");
    
  end
endmodule

module level1;
  level2 l2_inst();
endmodule


module level2;
  int rst;

  task t4();
    rst = 1; 
  endtask
endmodule

Thanks, Dave for the suggestion. I created this example which depicts more closer behaviour of my TB.
But it is resulting in below error - Could you please help.

Error-[SE] Syntax error
  Following verilog source has syntax error :
  "testbench.sv", 40 (expanding macro): token is 'bind'
      `probe(`NOC_PATH,array[0]); // manually passing 0 here, but in actual it
  will be a random index
                                ^
  SystemVerilog  keyword 'bind' is not expected to be used in this context.

#0, probe(path=l1_inst.l2_inst, name=array[0]) : "testbench.sv":38
full expansion of macro (probe), error at line 1
=>bind l1_inst.l2_inst.array[0] probe#("array[0]") prb(a);
1 error

The bind construct cannot be placed inside procedural code. Move the `probe macro references outside.

Also, my example declared list as an associative array–you cannot access list[0]. If you don’t care how you access elements in list, use a queue instead

Thanks a lot @dave_59

Hi @dave_59 , Follow-up - I have observed that the declared queue.push() is storing module name as per design instantiation sequence and not as per `probe calling.
I have mentioned in the end of the code comment that why the same sequence of full_path and queue is important for me.

I could think of below 2 ways, but don’t know to do code :-

  1. Is there a way to get elements in probe_pkg::queue as per `probe calling ?
  2. foreach (probe_pkg::queue[i])
    full_path [i] = {“dut.”, “noc.”, here , “.addr”}; // Is there a way to get module name stored in probe_name here? // this way full_path and queue element sequence will match

Please help with any suggestions.

EDA Code Link - EDA Playground

module dut;
  noc noc();
endmodule

module noc;
  rom rom3();
  rom rom2();
  rom rom1();
  rom rom();
  ram ram();
  // probe_pkg::queue is getting filled as per this sequence
endmodule

module ram;
  int rst = 1;
  int addr = 2;
  
endmodule

module rom;
  int rst = 1;
  int addr = 4;
endmodule

`define NOC_PATH dut.noc

package probe_pkg;
  interface class abstract_probe;
    pure virtual function int get_Rst;
  endclass
  abstract_probe queue[$];
endpackage

interface probe #(string name) (input int p);
  import probe_pkg::*;
  class concrete_probe implements abstract_probe;
    string probe_name;
    function new(string name);
      probe_name = name;
      queue.push_back(this);
    endfunction
    virtual function int get_Rst();
      return p;
    endfunction
  endclass
  concrete_probe h = new(name);
endinterface

module tb;

  dut dut();
  int idx = 2;
  int path_val;

  `define probe(path, name) bind path.name probe#(`"name`") prb(rst);
  
  `probe(`NOC_PATH, rom);
  `probe(`NOC_PATH, rom1);
  `probe(`NOC_PATH, rom3);
  `probe(`NOC_PATH, ram);
  `probe(`NOC_PATH, rom2);
  
  string names[] = {"rom", "rom1", "rom3", "ram", "rom2"}; // same sequence as `probe calling
  string full_path [];
  
  initial begin
    full_path = new [names.size()];
    
    foreach (names[i])
      full_path [i] = {"dut.", "noc.", names[i], ".addr"}; //creating path to node register
    
    //foreach (probe_pkg::queue[i])
      // full_path [i] = {"dut.", "noc.", ---- , ".addr"}; // Is there a way to get module name here ?
    
    foreach (probe_pkg::queue[i]) $display ("probe_pkg::queue [%0d] = %0p", i, probe_pkg::queue[i]);
    
    /*
    if (probe_pkg::queue[idx].get_Rst())
     assert(uvm_hdl_read(full_path[idx], path_val));
     // inject error on the node register (addr)
     // Here idx will be a random integer - that's why sequence of queue[idx] & full_path[idx] is important.
     // Otherwise Reset will be checked for different module and Error will be injected on different module.
    */
  end
endmodule
----------- Output --- Not as per `probe() ----- 
probe_pkg::queue [0] = '{probe_name:"rom3"}
probe_pkg::queue [1] = '{probe_name:"rom2"}
probe_pkg::queue [2] = '{probe_name:"rom1"}
probe_pkg::queue [3] = '{probe_name:"rom"}
probe_pkg::queue [4] = '{probe_name:"ram"}