Create different handle of class in the macro based on number of times macro is called

My requirement is to develop macro which takes cmd list and wait
for each response and other features too. We decided to have macro. so below
is code(not complete but enough to show my problem).

Code => fork join in macro issue - EDA Playground

my problem is when i call my_macro in fork join with two different cmd list.due to single handle construction every time, last update override the 1st thread updates.

Is it possible to create different object every time i call macro so that i have two cmd list for each thread of fork.

i tried to create the array of handle but its difficult to get index of each macro.

I tried to mimic this problem in below code please let me know if you need any other update.
PS : I am using OVM methodology

Mimic code is avaliable in fork join in macro issue - EDA Playground

OUTPUT if you see the output, only cmd2** are getting print. cmd1**is getting overwritten.

I know below with below code that is excepted output, I am tring to find the solution for my problem.

I would not use a macro, at least you haven’t explained why a task or function would not work.

One basic problem with your macro is that it is just text expansion. So even though you have two lines of text in your fork join, it expands to

   fork
       utils_h = new(); 
       utils_h.cmd_queue = {"cmd1","cmd11","cmd111"};
       utils_h = new();
       utils_h.cmd_queue = {"cmd1","cmd11","cmd111"};
   join

Since these 4 statements could be executed in any order, you are lucky you didn’t get a null handle reference error. Putting begin/end around the statements as part of the macro will fix this, but you still have the problem of two threads writing to the same variable, utils_h. Also, there is no point in using a fork unless the statements within it have some time consuming process associated with them. It’s not really clear your reason for adding the commands in parallel if it’s the execution of command that need to be in parallel.

What you probably want is a queue of util classes. Instead of your macro, have a function that adds constructs a single util class, and the commands to the queue inside that object. Then either fork off processes for each set of commands.

class utils_helper_c;
  
  string cmd_queue[$];
  
  function new(string name = "utils_h");
    $display("creating %s object",name);
  endfunction : new
endclass : utils_helper_c

class test;
   utils_helper_c utils_q[$];
   function void add_cmds(string cmds[$]);
      utils_helper_c utils_h = new();
      utils_h.cmd_queue = cmds;
      utils_q.push_back(utils_h);
   endfunction : add_cmds
   task run();
      $display("running run task...");
      add_cmds({"cmd1","cmd11","cmd111"});
      add_cmds({"cmd2","cmd22","cmd222"});
      while(utils_q.size() !=0)  // or repeat wait(util_q.size() !=0) 
	fork
	   utils_helper_c utils_h = utils_q.pop_front();
	   foreach(utils_h.cmd_queue[i])
	     #5
		$display("%t printing cmds %s",$time,utils_h.cmd_queue[i]);
	 join_none
   endtask : run
   
endclass : test

module top;
  
  test t;
  initial begin
     t = new();
     t.run();
  end
   
endmodule : top

In reply to dave_59:

@dave_59,
This macro is used to take the list of cmds and wait for each cmd resp i.e cmd_seq.get_resp and if that cmd is completed we push that cmd into cmd completed queue. one of feature that user can pass the count that for how many cmds it should wait.
e.g. my_macro(cmd_list,10) here cmd_list has 20cmds and they already driven from driver.Now once i received response of 10 cmds i can move further in my testcase util this time I would block the testcase at that point.

why Forked, there can be another scenario where we can have two types of cmds
e.g.

fork 
begin
 my_macro(read_cmd_list,10)
end
begin
 my_macro(write_cmd_list,5)
end

in this case, raed and write cmd list have 20 cmds each and after 10 read and 5 write response i can move further in my testcase.

But with current implementation, only the last thread update the utils_h.cmd_list.

This is because one class handle and when 2nd thread start it update the handle pointer.

what might be another solution if we don’t use Macro. This utility is for user who doesn’t have much knowledge of Implementation,where they just have to call one macro with proper arguments. adding more steps like add_cmds function would be burden on them.

In reply to Vinay Jain:

I don’t see the burden to the user in learning a function/task name and its required arguments versus learning a macro name and its required arguments. From the user’s point of view, it is just a name and a set of arguments. In either case they need to understand the functionality that one line of code gives them. They should not care whether the underlying code is a macro or a method call.

There are some things you can only do with a macro, like build identifier names or strings, but so far you have shown me nothing that requires you to use a macro.

You have also not shown us how commands are sent and responses received.