Assigning different clock for a interface array

I want to assign for an array of interfaces with different sample clock. for example
//////////////

type_rx_if rx_vif[3:0] (clk1) ;
type_rx_if rx_vif[7:4] (clk2) ;
//////////////
but this report compile error. then I tried to assign the clock to each interface.
/////////////////
type_rx_if rx_vif[7:0] () ;
assign rx_vif[0].clk =clk1 ;

assign rx_vif[4].clk =clk2 ;

////////////////
this code pass the compile, but in simulation, it seems to change on the same clock edge of clk1.

is there any solution to assign different sample clock for the element in interface array? or I have to define it as 2 separated arrays?

I think the compiler error is because you can’t declare the same identifier twice, even if each declares an array of the same type with mutually exclusive index ranges.

But the following might work:

type_rx_if rx_vif[7:0] ({4{clk2}},{4{clk1}}) ;

type_rx_if rx_vif[7:0] ({{4{clk2}},{4{clk1}}}) ;

this assignment also passed the compile and works the same as my individual assign, all data event is on the same clk1 edge. maybe there is other bug in my testbench, or did you really tried this usage of SV ?

In reply to jie:

Are clk1 and clk2 related?

You should be able to come up with a very small testcase to prove that this works.

my interface definition is like this, I think it’s correct, since I ran the tb with 1 clock without bugs.

interface type_rx_if(input bit clk);

wire …

clocking mck @(posedge clk);
input …

endclocking: mck

modport master(clocking mck);

clk1 and clk2 are not related,

I just worked the tb out with only clk1, now I want to change the clock domain of some interface and the corresponding drv, mon, I don’t want to modify the code of drv and mon. so I try to only modify the instantiation of the interface on the top level. is there some potential reasons that will cause bug with this. I just checked the waveform, the clock in the interface is correctly connected, but the signals in the drv and mon still change on the same clock.

In reply to dave_59:

I tried this code without uvm, it works fine, the data signal change on each clock assigned to the interfaces, is it possible that uvm_config_db did something to the virtual interface so that the signals in drv and mon does not perform as I expected?
//////////

interface type_rx_if(input bit clk);

wire  done; 	
   clocking mck @(posedge clk);
      inout  done;
   endclocking: mck
   
   modport master(clocking mck);
  
endinterface


module test ();
reg  reset, clk, clk_sfp ;

  type_rx_if rx_vif[3:0] ({clk_sfp,{3{clk}}});
  

    initial begin
    clk <= 1'b0;
	   clk_sfp <= 1'b0;
	   #200;
	   rx_vif[3].mck.done <= 1'b0;
	   rx_vif[0].mck.done <= 1'b0;
	   #200;
	   rx_vif[3].mck.done <= 1'b1;
	   rx_vif[0].mck.done <= 1'b1;	   
    end

  always
    #62.5 clk = ~clk;
  always
  	#40 clk_sfp = ~clk_sfp;


endmodule

In reply to jie:

OK, modify your example to use a virtual interface, and use the uvm_config_db to get the virtual interfaces. Most likely there is a problem with how you are setting the uvm_config_db, which until now, you had never mentioned.

In reply to dave_59:

Hi, I tried it with virtual if, but it still works on individual clocks. I suppose there is deep bug with my testbench. I post my code here:

`timescale 1ns / 1ps

  import uvm_pkg::*;
`include "uvm_macros.svh"

interface type_rx_if(input bit clk);

wire  done; 
wire  start; 	
   clocking mck @(posedge clk);
      inout  done;
      output  start;
   endclocking: mck
   
   modport master(clocking mck);
  
endinterface

class test_case1 extends uvm_test;
  `uvm_component_utils(test_case1)
  virtual type_rx_if sigs[3:0];
	
  function new(string name = "test_case1", 
    uvm_component parent=null);
    super.new(name,parent);
  endfunction : new

  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
      for (int i=0; i < 4; i=i+1) begin
         if (!uvm_config_db#(virtual type_rx_if)::get(this, "", {"rx_vif_", i}, sigs[i])) begin
            `uvm_fatal("DRV/NOVIF", "No virtual interface specified for this driver instance")
         end
					end					
  endfunction : build_phase

   virtual protected task run_phase(uvm_phase phase);
      phase.raise_objection(this, "running ");
      super.run_phase(phase);
		 #200;
	   this.sigs[0].mck.start <= 1'b0;
	   this.sigs[3].mck.start <= 1'b0;
	   #200;
	   this.sigs[0].mck.start <= 1'b1;
	   this.sigs[3].mck.start <= 1'b1;   
	   phase.drop_objection(this, " done");
endtask: run_phase
endclass : test_case1

module test_top ();  
reg  reset, clk, clk_sfp ;
  type_rx_if rx_vif[3:0] ({clk_sfp,{3{clk}}});
   
    genvar i;
  generate 
	  for (i=0; i < 4; i=i+1) begin
	  		  initial
			  begin
   	   uvm_config_db#(virtual type_rx_if)::set(uvm_root::get(), "*", {"rx_vif_", i}, rx_vif[i]);
    end
    end
 endgenerate
 
     initial begin
     run_test();
   end

    initial begin
    clk <= 1'b0;
	   clk_sfp <= 1'b0;
	   #200;
	   rx_vif[3].mck.done <= 1'b0;
	   rx_vif[0].mck.done <= 1'b0;
	   #200;
	   rx_vif[3].mck.done <= 1'b1;
	   rx_vif[0].mck.done <= 1'b1;	   
    end
  always
    #62.5 clk = ~clk;
  always
  	#40 clk_sfp = ~clk_sfp;
endmodule

in my TB , I created 8 agents, I want to use 4 with clk1,4 with clk2, and instantiate the vif in the drv and mon as here in the test, but I can’t imagine what is the difference between my TB and this code. is it because of the 8 mons and drvs are array of the same class? or is there any other possible bug? anyhow, I think the interface related code only exist in drv, mon, interface definition and TB top, I did not use config to pass it down.

In reply to jie:

There is a problem with the expression {“rx_vif_”, i} in these two statements:

if (!uvm_config_db#(virtual type_rx_if)::get(this, "", {"rx_vif_", i}, sigs [ i ])) begin
uvm_config_db#(virtual type_rx_if)::set(uvm_root::get(), "*", {"rx_vif_", i}, rx_vif [ i ]);

Since i is an int, not a string , the concatenation is treated as an integral concatenation creating an 88-bit value “rx_vif_” . This integral expression gets converted back to a string, but the first character in the expression truncates the string. Even when the last character goes to 1,2,3,… etc., the argument to the get/set method only sees “rx_vif_”.
Technically, this implicit conversion from an integral to a string type is illegal and the compiler should have flagged it as an error. However, because of bug compatibility that vendors are sometimes forced to implement, most simulators will not flag this error by default.

The correct way to write this expression is $sformatf(“rx_vif_%0d”,i)

In reply to dave_59:

I modified my TB according to your advice, but the result is the same, all clocks change on clk1.

finally I found out the reason for that. but I don’t know whether it’s a potential bug of UVM or I misused it.

The story is. I have a interface to config the registers of the dut. I have another 4 interfaces for packet transaction。
if I use all these with 1 clock, everything is ok.
if I use 2 packet ifcs and the register ifc with clk1, the other 2 ifcs with clk2, the transaction with clk1 work fine, but the transaction with clk2 is not working. since I used a virtual sequence which start reg sequence working on clk1 and afterwards packet sequence working on both clk1 and clk2. the code look like this.
//////////

virtual task body();

uvm_do_on(my_norm_reg_seq, p_sequencer.reg_sqr); //on clk1 repeat(sent_cnt) begin fork uvm_do_on(incr_send_pkt_seq_0, p_sequencer.eth_sqr_0); //drvs on clk1
uvm_do_on(incr_send_pkt_seq_1, p_sequencer.eth_sqr_1); //drvs on clk1 uvm_do_on(incr_send_pkt_seq_2, p_sequencer.eth_sqr_2); //drvs on clk2
uvm_do_on(incr_send_pkt_seq_3, p_sequencer.eth_sqr_3); //drvs on clk2 join end uvm_do_on(my_check_reg_seq, p_sequencer.reg_sqr); //on clk1

endtask : body

///////////

if I comment out the norm_reg_seq line without configure the register on clk1, the TB will send packet on both clk1 and clk2 in each port correctly. I doubt if the UVM can switch the clock of the sequence automatically. or is there anything I should tell UVM to do this?