Search code examples
verilogsystem-veriloguvm

"A variable index into the for generate block is illegal" error


I created a group tasks using "generate" and was trying to call them using a for loop.

I have used generate block to create multiple instances of module, so I thought I could use it similarly to create multiple tasks. I plan to have no arguments for the tasks, and always call the tasks in a loop. But the number of tasks to be called is dependent on a parameter (instead of "4" in the example below, it will be a parameter).

Here is an example of what I was trying to do:

  task display;
    fork
      other_tasks();
      for(int i=0; i<4; i++) begin
        create_task[i].print_i;
      end
//      create_task[0].print_i;
//      create_task[1].print_i;
//      create_task[2].print_i;
//      create_task[3].print_i;
    join
  endtask

  generate
    for(genvar i=0; i<4; i++) begin : create_task
      task print_i();
        $display("%t, %m, Message #%0d", $time, i);
        #1;
      endtask
    end : create_task
  endgenerate

The code gives me no error if I call the tasks separately (commented lines) instead of using "for". However, I would prefer to do this using a loop because the number of tasks I need to call may vary. What would be the correct way of doing this?


Solution

  • Based on the the limited requirements you provided, the simplest solution is triggering a single event that calls all the tasks.

     event print;
     task display;
        fork
          other_tasks();
          ->print;
        join
      endtask
    
      generate
        for(genvar i=0; i<4; i++) begin : create_task
          task print_i();
            $display("%t, %m, Message #%0d", $time, i);
            #1;
          endtask
        always @print print_i;
        end : create_task
      endgenerate
    

    If you want to only call some of the tasks, you could create an array of events, and trigger the array indexed by a variable.

     event print[4];
     task display;
        fork
          other_tasks();
          ->print[some_index];
        join
      endtask
    
      generate
        for(genvar i=0; i<4; i++) begin : create_task
          task print_i();
            $display("%t, %m, Message #%0d", $time, i);
            #1;
          endtask
        always @print[i] print_i;
        end : create_task
      endgenerate
    

    If you ever need to pass arguments to the task, there are ways of using interface classes instead of using events.

    interface class abstract_print;
      pure virtual task printer(input int arg);
    endclass
    
    abstract_print list[4];
    
     task display;
        fork
          other_tasks();
          list[some_index].print(some_arg);
        join
      endtask
    
      for(genvar i=0; i<4; i++) begin : create_task
          task print_i(int arg);
            $display("%t, %m, Message #%0d", $time, i);
            #1;
          endtask
        class concrete_print implements abstract_print;
           function new;
             list[i] = this;
           endfunction
           task print(int arg);
              print_i(arg);
           endtask
        endclass
        concrete_print p = new;
        
      end : create_task