Write constraints on elements of a dynamic array of classes

Hello everyone,

I was trying to write constraints on class variables in a top level class and the problem is the class handles themselves are dynamic arrays. To present my problem I put together a stripped off version of my code by referring to other posts, especially the one located here: Constraint for object dynamic array

Here is the sample code to represent my issue:

class cpu_core_class;
    rand int core_count;
    constraint c_core_count {
        core_count inside {[32'd1:32'd16]};
    }
    function new();
    endfunction: new
endclass: cpu_core_class

class memory_controller_class;
    rand int controller_count;
    constraint c_controller_count {
        controller_count inside {[32'd1:32'd16]};
    }
    function new();
    endfunction: new
endclass: memory_controller_class

class hardware_config_class;
    rand cpu_core_class cpu_cores;
    rand memory_controller_class memory_controllers;
    function new();
        cpu_cores = new();
        memory_controllers = new();
    endfunction: new
endclass: hardware_config_class

class cache_size_class;
    rand int size_kb;
    constraint c_size_kb {
        size_kb inside {[32'd16:32'd1048576]};
    }
    function new();
    endfunction: new
endclass: cache_size_class

class params_memory_class;
    rand cache_size_class cache_size;
    function new();
        cache_size = new();
    endfunction: new
endclass: params_memory_class

class params_cpu_class;
    function new();
    endfunction: new
endclass: params_cpu_class

class params_class;
    rand hardware_config_class hardware;
    rand params_memory_class memory[];
    rand params_cpu_class cpu[];
    local const int unsigned MAX_UNITS = 32;
    function new();
        hardware = new();
        memory = new[MAX_UNITS];
        cpu = new[MAX_UNITS];
    endfunction: new
    constraint c_max_memory_bandwidth{
        (memory.sum() with ($clog2(item.cache_size.size_kb))) + ($clog2(hardware.cpu_cores.core_count)) <= 20;
    }
   
    constraint c_hardware{
        cpu.size() == hardware.cpu_cores.core_count;
        memory.size() == hardware.memory_controllers.controller_count;
    }
    function void pre_randomize();
        foreach(cpu[i]) begin
            cpu[i] = new;
            memory[i] = new;
        end
    endfunction: pre_randomize
endclass: params_class


module random();
  params_class params;

  initial begin
    params = new();

    if(!params.randomize()) begin
      $error("Randomization Failed after turning off structural params randomization");
    end else begin
      $display("CPU Size: %0d", params.cpu.size());
      $display("Memory Size: %0d", params.memory.size());
    end
  end

endmodule

The above code is also located in the eda playground link here :

I see that I get exactly what I want for few seeds but for other seeds, the above code is hitting constraint resolution failure (indicating that for the seeds that dint fail, it was just a coincidence)

Here is a sample error:

# testbench.sv(82): randomize() failed due to conflicts between the following constraints:
# testbench.sv(31): memory[1].cache_size.c_size_kb { (memory[1].cache_size.size_kb inside { [16:1048576] }); }
# testbench.sv(31): memory[0].cache_size.c_size_kb { (memory[0].cache_size.size_kb inside { [16:1048576] }); }
# testbench.sv(31): memory[2].cache_size.c_size_kb { (memory[2].cache_size.size_kb inside { [16:1048576] }); }
# testbench.sv(31): memory[3].cache_size.c_size_kb { (memory[3].cache_size.size_kb inside { [16:1048576] }); }
# testbench.sv(31): memory[4].cache_size.c_size_kb { (memory[4].cache_size.size_kb inside { [16:1048576] }); }
# testbench.sv(31): memory[5].cache_size.c_size_kb { (memory[5].cache_size.size_kb inside { [16:1048576] }); }
# testbench.sv(60): c_max_memory_bandwidth { ((($clog2(memory[0].cache_size.size_kb) + $clog2(memory[1].cache_size.size_kb) + $clog2(memory[2].cache_size.size_kb) + $clog2(memory[3].cache_size.size_kb) + $clog2(memory[4].cache_size.size_kb) + $clog2(memory[5].cache_size.size_kb) + $clog2(memory[6].cache_size.size_kb) + $clog2(memory[7].cache_size.size_kb)) + $clog2(hardware.cpu_cores.core_count)) <= 20); }
# Given:
# bit signed [31:0] memory[1].cache_size.size_kb
# bit signed [31:0] memory[0].cache_size.size_kb
# bit signed [31:0] memory[2].cache_size.size_kb
# bit signed [31:0] memory[3].cache_size.size_kb
# bit signed [31:0] memory[4].cache_size.size_kb
# bit signed [31:0] memory[5].cache_size.size_kb
# bit signed [31:0] memory[6].cache_size.size_kb
# bit signed [31:0] memory[7].cache_size.size_kb
# bit signed [31:0] hardware.cpu_cores.core_count

I was expecting the .size() to be calculated such that the constraint c_max_memory_bandwidth is satisfied. But looks like that is not the case here.

Can someone please suggest what is going on or a workaround for this issue?

Thanks and regards,

I was able to strip down the example code even more and still reproduce the above issue.

class params_memory_class;
    rand int size_kb;
    constraint c_size_kb {
        size_kb inside {[32'd16:32'd1048576]};
    }
    function new();
    endfunction: new
endclass: params_memory_class

class params_class;
    rand params_memory_class memory[];
    rand int controller_count;
    constraint c_controller_count {
        controller_count inside {[32'd1:32'd16]};
    }
    
    local const int unsigned MAX_UNITS = 32;
    function new();
      	memory = new[MAX_UNITS];
    endfunction: new
    
    constraint c_max_memory_bandwidth2{
      (memory.sum() with ($clog2(item.size_kb))) <= 20;
    }
   
    constraint c_hardware{
        memory.size() == controller_count;
    }
    function void pre_randomize();
      foreach(memory[i]) begin
            memory[i] = new;
        end
    endfunction: pre_randomize
endclass: params_class


module random();
  params_class params;

  initial begin
    params = new();

    if(!params.randomize()) begin
      $error("Randomization Failed after turning off structural params randomization");
    end else begin
      $display("Memory Size: %0d", params.memory.size());
    end
  end

endmodule

Also located here: EDA Playground

It appears that the size of the dynamic array is resolved first and then the solver is trying to solve for size_kb and if a seed picks the size to be <= 5, the solver can find a solution for the constraint, else it fails. So I need to see how to force the solver to solve for size as well, if the constraint cannot be resolved.