Flexible interface that supports scaler or arrays

Hi all,

Thanks in advance for any suggestions.
I’m presenting a conceptual example showing how I’d like to make an interface re-usable when connecting to a DUT with different data types.

I’m trying to create a reusable testbench interface that connects to a DUT.
The DUT has a data signal that is say: [7:0] data[y]
Another DUT may have data signal that’s not in an array: [7:0] data

my interface is parameterized like this as (re-use code that I don’t want to change because it resides in a re-use repository):
interfacename #(XDIM=1), YDIM=1); // DIM values are passed in

logic [7:0] my_data[y]


In my testbench top I connect the interface to the dut:

module top_tb;

parameter XDIM=2;
parameter YDIM=3;

interfacename #(.XDIM(XDIM), .YDIM(YDIM) interface_inst; // I control this file and pass in parameters to match the dut I’m connecting to

dut dut_instance
(

.data(interface_inst.my_data),

)


I want to use this concept regardless of what DUT I connect to.
I’d do this by adjusting XDIM/YDIM to match the dut.

This works until my DUT does not use an array for it’s data and I get a type mismatch error.
e.g., ‘data’ gets connected to my_data[y]
I can’t even get around by changing X and Y dimensions to [1][1] which would be a single element.

Question: Is there a clever way to make this connection? e.g., could I make an alias called my_data that is aliased to an array when the dut is an array, but a non-array when it’s not.

I’m sure this doesn’t work, but conceptually, I’m looking for something like this:

logic [7:0] non_array_data;
logic [7:0] array_data[y];

e.g., if (XDIM==1 and YDIM==1)
alias my_data = non_array_data;
else
alias my_data = array_data;

The only thing I can think of doing is a set of ifdefines to select my signal assignments, but it cumbersome because I have a lot of other for-loops in other sections of the code to process data and my ifdef code blocks would get rather large. I also have other sized arrays to contend with.

Thanks for any suggestions on how I might work around this issue.
Brian

In reply to jnbkeller:

It is difficult to help without knowing how you expect my_data to be used.

I might suggest looking at bit-stream casting(section 6.24.3 in the 1800-2017 LRM) or the streaming operators (section 11.4.14) to reformat the data as needed.

In reply to dave_59:

Also consider using an interface type parameter instead.

interface foo_if
#(
   type atom_t = bit [ 7 : 0 ]
);
//...

  localparam int ATOM_DIMENSIONS = $dimensions( atom_t );  
  localparam int ATOM_ELEMENT_SIZE = $size( atom_t, ATOM_DIMENSIONS );
  localparam int TOTAL_ELEMENTS =  $bits( atom_t ) / ATOM_ELEMENT_SIZE;

  atom_t my_data;
//...
endinterface

The default “atom_t” works with a single dimensional (8-bit scaler). But, the interface can support different sized scalers, as was as n-dimensional arrays (of varying types) - all controlled by the parameter type passed in from the parent interface instantiation. i.e.

module top;
  typedef bit [ 2 : 0 ][ 7 : 0 ] my_pixel_t;
  foo_if #( .atom_t( my_pixel_t ) ) rgb_pixel_if();
endmodule

I use these quite a lot - however for ease, I tend to require atom_t to be a packed typed. I’ve even used packed structures in the parameter type override.

In reply to Mark Curry:

Hi Mark,

Thanks so much for this info; I was unaware of the $dimensions function; this will be very helpful.

Brian