Using parameter array in for loop to construct parameterized object

I have a set of parameters, and wish to construct parameterized objects using these parameters. Ideally the following code should work:

class B;
  virtual function void who();
  endfunction 
endclass

class C#(int p) extends B;
  function void who();
    $display("I am C#(%0d)",p);
  endfunction
endclass

module m;
  parameter N=3;
  parameter integer len[N]={5,6,7};
  
  initial begin
    B c_array[3];
    for(int i=0;i<N;i++) begin
      C#(len[i]) myC=new;
      c_array[i]=myC;
      myC.who();
    end
  end
endmodule

This complains i is not a compile time constant.

Any other way of achieving the same result? My goal is to write generic code that works for any given parameters.

The following works, but is hard to maintain if someone uses a parameter that hasn’t been used before:

module m;
  parameter N=3;
  parameter integer len[N]={5,6,7};
  
  initial begin
    B c_array[3];
    for(int i=0;i<N;i++) begin
      case(len[i])
        5: begin
          C#(5) myC=new;
          c_array[i]=myC;
        end
        6: begin
          C#(6) myC=new;
          c_array[i]=myC;
        end
        7: begin
          C#(7) myC=new;
          c_array[i]=myC;
        end
      endcase     
      c_array[i].who();
    end
  end
endmodule

In reply to NiLu:

Use a generate block

module m;
  parameter N=3;
  parameter integer len[N]={5,6,7};
    
   B c_array[3];
   for(genvar i=0;i<N;i++) begin
      
      initial begin
	 automatic C#(len[i]) myC=new;
	 c_array[i]=myC;
	 myC.who();
      end
  end
endmodule

One problem you may encounter is that if another initial block needs to read c_array at time 0, you have a race condition. A way of removing that race is to make the c_array initialization happen before time 0 as a static initialization:

virtual class B; // made virtual so that no one is allowed to construct it
   pure virtual function void who(); 
   static B c_array[$];
   function new();
      c_array.push_back(this);
   endfunction 
endclass
 
class C#(int p) extends B;
  function void who();
    $display("I am C#(%0d)",p);
  endfunction
endclass
 
module m;
  parameter N=3;
  parameter integer len[N]={5,6,7};
    
   for(genvar i=0;i<N;i++) begin      
	 C#(len[i]) myC=new;
   end
   initial
     foreach (B::c_array[i]) B::c_array[i].who();
endmodule

In reply to dave_59:

Thanks for the generate suggestion. I assume the race condition you mentioned isn’t really related to this? I could/would have the same issue with my case statement solution?

Now the real problem is that the objects I want to create are UVM agents inside a UVM env. I think the generate wont work there? I assume I could create the agents just like the interfaces on the ‘static module’ side of the testbench and pass handles in the config db. But this kind of (further) breaks the top-down UVM construction like it is the case with the interfaces.

In reply to NiLu:
Yes, the race conditional is another issue whenever you have multiple initial blocks reading and writing the same variables at time 0.

You are also correct that you can’t use a generate block inside a class. But what you can do instead of constructing an array of agents inside the generate block, you use the factory to construct an array of proxies for the agent using uvm_object wrapper. It would look something like

package my_stuff;
class my_agent#(int p) extends uvm_agent;
...
endclass
uvm_object_wrapper c_array[int];
class C#(int p) extends uvm_component_registry#(my_agent#(p));
 function new;
  c_array[p] = this;
 endfunction
endclass
endpackage
module top;
  parameter N=3;
  parameter integer len[N]={5,6,7};
 
   for(genvar i=0;i<N;i++) begin      
	 my_stuff::C#(len[i]) myC=new;
   end
   initial run_test();
endmodule

Then when you go to construct your agents in the env, you can do this with i as a variable

c_array[i].create_component("name",parent);

In reply to dave_59:

Thanks, I need to look deeper into this code/concept, but I think I get the idea. Does it also work if some of the len parameters are equal? E.g.

parameter integer len[N]={5,6,7,5,5,5}

In reply to NiLu:
It should work if some of the parameters are equal. One of the benefits of using a proxy classes is they are lightweight stand-ins for the actual class. It doesn’t matter that you will create some of these classes multiple times - they will just get thrown away when they get assigned to the same element in the associative array.