Need help with understanding which data structure is good to implement this case

i Have a requirement to implement a cache in DV environment.
i have three caches having different data width and hence LRU width and valid bits width.

one way i thought was to have a associative array of struct which would be easy to implement and use as well since the addresses are going to be sparse. and then using dynamic array of associative array.
somewhat like this

struct{
bit [63:0] data,
bit [2:0] LRU, 
bit[7:0] valid
} cache_struct;
cache_struct cache[num_caches][addr];

but then since the width varies i am not sure how to take care of that in dynamic array of cache. result of this i have to write seperate cache manipulation tasks fro each cache.
right now i have defines two different structures each having distinct width and (no of cache line in each cache varies so lru width and valid width so does my single cache with different names so i cant iterate through array.
right now it is like

struct{
bit [127:0] data,
bit [1:0] LRU, 
bit[8:0] valid
} cache1;
cache1 cacheB[addr];

struct{
bit [63:0] data,
bit [2:0] LRU, 
bit[7:0] valid
} cache0;
cache0 cacheA[addr];

is there a better way of implementing this like parameterized way
please let me know.
thank you

Let’s say there was such a thing as a parameterized struct. That wouldn’t help you iterate through an array because arrays need elements all of the same type.

You could create a parameterized class instead of a struct, but they need a common unparameterized base class to be put into the same array

interface class cache_base;
  pure virtual function int get_data_width;
endclass
class cache_entry#(int DATA_WIDTH, LRU_WIDTH) implements cache_base;
  bit [DATA_WIDTH-1:0] data;
  bit [LRU_WIDTH-1:0] LRU;
  bit[DATA_WIDTH/8-1:0] valid;
  function int get_data_width();
    return DATA_WIDTH;
  endfunction
endclass


cache_entry #(128,2) cache1;
cache_entry #(64,3)  cache0;
cache_base cache[bit [31:0]];

...
begin
    cache1 = new;
    cache[0] = cache1;
    cache1 = new;
    cache[10] = cache1;
    cache0 = new;
    cache[20] = cache0;
    cache0 = new;
    cache[30] = cache1;
    foreach(cache[i]) $display("cache[%3d].DATA_WIDTH = %d",i,cache.get_data_width);
end

If you don’t want to go the class object route, you could create a struct with each field having its maximum width, and add other fields containing the actual width:

struct{
bit [511:0] data,
bit [7:0] LRU, 
bit[63:0] valid,
int DATA_WIDTH
} cache_struct;

right now i have implemented something similar to second approach but i am curious to understand what interface class does and how the assignment works you mentioned.
cache[0] = cache1;
do you have any pointers

Thank you.

An interface class is like an abstract virtual class containing nothing but pure virtual methods. Virtual classes are never constructed directly; they must be extended with their virtual methods overridden ( a concrete class that can be constructed).

An abstract interface class gets implemented into a concrete class with implements instead of extends. The key benefit is that a concrete class can implement multiple interface classes as well as extend a single base class.

i think that makes sense! thank you for the response.