1
votes

I have declared following interface:

interface data_x #(parameter g_DataWidth = 8)
   (input ckrs_t ClkRs_ix);
   logic [g_DataWidth-1:0] data;
   bit         enable;
   ckrs_t ClkRs;

   always_comb begin
      ClkRs = ClkRs_ix;
   end
endinterface

The interface has data bus and the data enable, and it is as well associated with the clock and reset signal, which is a typedef ckrs_t.

I have a module, which accepts as an argument array of those interfaces:

module fourmclinks
   (...
    data_x packet_ox[NUMBER_OF_GBT_LINKS-1:0],
    data_x packet_ix[NUMBER_OF_GBT_LINKS-1:0],
    ...
    );

The problem I have is, that I need to declare in top-level entity an array of those data_x interfaces, but each time use different ClkRs_ix input clock. (It is used in the gbts, where each receiver has its own clock and reset signal).

I tried many things, including this one:

   ckrs_t txclock_x;
   assign txclock_x.clk = GbtTxFrameClk40MHz_k;
   assign txclock_x.reset = GbtReset_r;
   data_x #(.g_DataWidth(g_FrameSize)) packet_ox[NUMBER_OF_GBT_LINKS-1:0](.ClkRs_ix(txclock_x));

   data_x #(.g_DataWidth(g_FrameSize)) packet_ix[NUMBER_OF_GBT_LINKS-1:0]();
   genvar                 linkiface;
   generate
      for(linkiface=1; linkiface < NUMBER_OF_GBT_LINKS+1; linkiface++) begin : linkgenerator
     assign packet_ix[linkiface-1].ClkRs_ix.clk =
                             GbtRxFrameClk40Mhz_kb4[linkiface];
     assign packet_ix[linkiface-1].ClkRs_ix.reset = GbtReset_r;
     assign packet_ix[linkiface-1].enable = 0;
     assign packet_ix[linkiface-1].data = RxDataAppSfpGbtUserData_4b80[linkiface];
      end
   endgenerate

Hence making empty/virtual/unassigned/... interface array declaration, and then in generate loop assign correct signals to it. This simulates, but quartus does not compile it claiming

value cannot be assigned to input "ClkRs_ix".

How to correctly generate array of interfaces, each having different input connection? Please help

1
I would expect your code to work but did not investigate it further. Since it does not work, I suggest to move your interface instance inside the generate 'for' loop and forget about the instance array. It is going to be cleaner in any case. - Serge

1 Answers

0
votes

I'm bit smarter now, so here is the solution to the problem. But first issues:

  • it is not possible just to remove 'input' direction from the port declaration in the data_x interface declaration above. If this is done, one has to then manually assign clock and reset lines for every instance of the data_x object. This is indeed possible, but one loses all the beauty of having the clock and reset automatically assigned during the instantiation of the interface

  • it is not possible either in this particular case to make a virtual interface, and connect the signals in the for loop. Root cause of this is the presence of always_comb, which takes in the input reset/clock and assigns it to the internal signals. So this assignment, together with manual assignment of reset and clock in the top-level entity results in driving those signals from two sources, which Quartus will not digest

So the only possible way, which I found is following:

Declare the data_x interface to generate the always_comb on demand:

interface data_x #(
          parameter g_hasClock = 1,
          parameter g_DataWidth = 8)
   (
    input ckrs_t ClkRs_ix
    );

   logic [g_DataWidth-1:0] data;
   bit         enable;
   ckrs_t ClkRs;

   generate
      if(g_hasClock) begin
     always_comb begin
        ClkRs = ClkRs_ix;
     end
      end
   endgenerate

endinterface // data_x

Instantiate the interface with unbound ClkRs_ix. Note the usage of g_hasClock, which instantiates the data_x interface without always_comb block, hence Quartus stops complaining about multiple drivers:

   data_x #(.g_DataWidth(g_FrameSize),
        .g_hasClock(0)) packet_ix[NUMBER_OF_GBT_LINKS-1:0]();

And then generate interface with different clocks:

   genvar                 linkiface;
   generate
      for(linkiface=1; linkiface < NUMBER_OF_GBT_LINKS+1; linkiface++)
   begin : linkgenerator

     assign packet_ix[linkiface-1].ClkRs.clk = GbtRxFrameClk40Mhz_kb4[linkiface];
     assign packet_ix[linkiface-1].ClkRs.reset = GbtReset_r;
     assign packet_ix[linkiface-1].enable = 0;
     assign packet_ix[linkiface-1].data = RxDataAppSfpGbtUserData_4b80[linkiface];
      end
   endgenerate

This works. It is not so nice because we have to do it manually. Just for sake of completeness: if the clocks for all interfaces is the same, all that code above boils down to this snippet:

  ckrs_t txclock_x, rxclock_x;
   assign txclock_x.clk = GbtTxFrameClk40MHz_k;
   assign txclock_x.reset = GbtReset_r;
   data_x #(.g_DataWidth(g_FrameSize)) packet_ox[NUMBER_OF_GBT_LINKS-1:0](.ClkRs_ix(txclock_x));

I'm sure this is not the best solution ever, but it is compilable and gives the result needed.