UVM Clock agent to create parameterized number of clock, either of which can during course of the simulation

Hi All,
I am trying to create a UVM clock UVC which can generate N#of clocks based on the parameter we set. I generate these clocks in the interface which receives the frequency information from the driver. I am using a “generate” to repeat the always statement based on the set parameter. However, I am unable to change the frequency of these clocks during the course of the simulation. I understand that I cannot use “freq” which is real number in the sensitivity list of always statement inside the generate block. How can I handle this scenario? I am OK with moving away from generate block and using any other method to generate parameterized number of clocks; however, I definitely want to have a capability of changing the clock frequency in the middle of the simulation(through sequence).
Any help would be appreciated.

//Interface


  logic  [CLK_NUM-1:0]  clk_out                   ;
  logic  [CLK_NUM-1:0]  enable                    ;
  int                   freq[CLK_NUM]             ;
  int                   duty[CLK_NUM]             ;
  int                   phase[CLK_NUM]            ;
  real                  freq_Ghz[CLK_NUM-1:0]     ;     //Variable to store Ghz converted frequency
  real                  Tperiod [CLK_NUM-1:0]     ;     //Timeperiod 
  real                  half_Tperiod[CLK_NUM-1:0] ;     //half the Time period 
  real                  temp_freq[CLK_NUM-1:0]    ;     //Temp storage for the frequency 
  logic [CLK_NUM-1:0]   update_freq;

  initial begin 
     enable [CLK_NUM-1:0] =0;
     clk_out[CLK_NUM-1:0] =0;
  end 

  genvar c;

  generate 
    for( c = 0 ;  c < CLK_NUM ; c++) begin

      always @(posedge enable[c] or negedge enable[c]) begin
          $display ("I am in the block %t",$time);
          if(enable[c] == 1 ) begin
            freq_Ghz[c]                = freq[c] * 0.001;
            Tperiod[c]                 = 1.0/freq_Ghz[c]; 
            half_Tperiod[c]            = Tperiod[c]/2.0;
            
            forever begin 
              #(half_Tperiod[c]) clk_out[c] = ~clk_out[c];
            end
          end
          else begin
             clk_out[c]   =0;     
          end
      end    
    end
  endgenerate  

In reply to Subhash.Sangam:

It look s alittle bit confusing to me. I do not see where you are getting the values of your variables from? Does it come through a seq_item? I’d synchronize on an Event if you want to change your frequency.

I set it from driver.

task clk_driver::get_and_drive();

  `uvm_info(get_type_name(), "Reset dropped", UVM_LOW)
  forever begin
    clk_seq_item req;             
    seq_item_port.get_next_item(req);
    // Drive the data item
    `uvm_info(get_type_name(), $sformatf("Driving master_transaction :\n%s",req.sprint()), UVM_LOW)
    vif.enable = req.enable;      //Enabling 
    vif.freq   = req.freq  ;
    vif.duty   = req.duty  ;
    vif.phase  = req.phase ;
    
    seq_item_port.item_done();
  end


endtask : get_and_drive

In reply to Subhash.Sangam:

OK, I see you are passing the seq_item data to the virtual interface.
I’d trigger an Event before calling item_done();
Then you can synchronize in your interface on this event.

Thanks for the reply. However, I am not sure if it will work. I tried something very similar to trigger always block. It did not work. The problem is calling always block (which has forever # delay element) inside the “generate”. My wild guess is Generate being synthesizable code doesn’t allow that.

In reply to Subhash.Sangam:
Your problem is you have a
forever
loop inside an
always
block - it has nothing to do with
generate
. Once you get into the
forever
, you never get out of it to recompute the period.

What you need to do is separate the clock generation from the period calculation.

 for(genvar c = 0 ;  c < CLK_NUM ; c++) begin : gen_clocks
 
      always_comb begin
            freq_Ghz[c]                = freq[c] * 0.001;
            Tperiod[c]                 = 1.0/freq_Ghz[c]; 
            half_Tperiod[c]            = Tperiod[c]/2.0;
           
     end

     always begin
              clk_out[c] = 0;
              wait (enable[c])
              #(half_Tperiod[c]);
              clk_out[c] = 1;
              #(half_Tperiod[c]);
            end
    end : gen_clocks

you are missing duty and phase that will need to be considered for both blocks

Thanks Dave. Works perfect.
It was intentional that I left out duty cycle and phase to avoid complexity of the code and show the intended part.

Great help!