top of page

Layering via Reusable Randomization Policy Classes and application in power management system


A while back, a good friend and colleague shared a presentation with me called 'I Didn't Know Constraints Could Do That' by John Dickol. When I first read it, I didn't understand nothing (also after second read) However, the code provided and its structure caught my attention. After reading it more thoroughly, I concluded that the most effective approach to enhance my understanding would be to create my own example using terms from my area of expertise, which is power management.


following are resources I used for building my example

If you find this subject intriguing and wish to gain a deeper understanding of this experiment, it is strongly recommended that you engage with John's presentation and paper. John's explanations regarding this subject are outstanding.

 

A power management system, often referred to as PMS, is a specialized technology or framework designed to efficiently regulate and distribute electrical power within a given system or network. Its primary purpose is to ensure that power resources are utilized optimally, balancing the demand for electricity with the available supply. In the event of unexpected power fluctuations, shortages, or failures, the power management system swiftly intervenes to mitigate potential damage.


The depicted system comprises eight power consumers under the supervision of a controller. Each consumer has the ability to request power and may potentially encounter failures, which are reported to the controller. A manager has established a priority table for each port and determined the allocated power for the controller. The primary responsibility of the controller is to continually observe power requests and failure statuses for each port. When any of these parameters change, the controller is tasked with making informed decisions, factoring in the priority table and the power allocations predetermined by the manager.




For my experiment, I aim to generate dynamic scenarios that involve randomizing all system parameters. As suggested in the paper, my goal is to establish reusable policy classes and incorporate or remove them from the randomization process as needed. A test scenario could be for example

  1. For the initial set of 4 ports, ensure there is at least one port short failure and one over current failure present. Ports [7:4] are designated as no failure.

  2. For the initial set of 4 ports, all priorities should be high or at least equal to high, while for ports [7:4], priorities should be lower than high.

  3. The power allocation to the chip should surpass the power requests of the initial 4 ports but should still be under the cumulative power requests of all the ports.


 

The system's inheritance tree appears as follows:


 

System types:


// No failure, Open circuit, Over current, Short circuit

typedef enum int {NOFL, OPEN, OVRC, SHRT} failure_type;


typedef enum int {LOW, MEDIUM, HIGH, CRITICAL, HIGHEST} priority_type;

typedef enum int //watts

{

CONS_10=10,

CONS_20=20,

CONS_30=30,

CONS_40=40,

CONS_50=50

} power_request_type;


 

Description and code snippet for manager class

  • manager class

    • prio[8] - priority type for each one of the ports

    • ports[8] - power device

    • pwr_alloc - power allocation for the system

    • policy[$] - A queue of policy classes, with each one representing distinct requirements for the random constraint solver.


class pwr_mng_chip; // Properties int num_ports; rand pwr_mng_port ports[]; rand priority_type prio[]; rand int chip_pwr_alloc; rand rand_policy_base #(pwr_mng_chip) chip_power_policies[$]; // Up to maximum power allocated for default case constraint max_chip_pwr_alloc_c { chip_pwr_alloc inside {[0: num_ports * 50]}; } ... // Set this handle for all policy classes function void pre_randomize(); foreach (chip_power_policies[idx]) chip_power_policies[idx].set_item(this); endfunction

...

endclass


 

Description and code snippet for port class


  • port class

    • pwr_req - port power requested type

    • failure - port failure type


class pwr_mng_port;

...

rand power_request_type pwr_req; rand failure_type failure; // No failure is the default condition constraint failre_c{ soft failure==NOFL; } ...

endclass

 

Below is the description of system scenarios and how policy classes guide the randomization engine in generating these scenarios.


Scenario 1 : Confirm that the system accurately follows channel priority when activating power channels in response to requests for power exceeding the current system capacity.

ports should not initiate and failure mode.


// Ports power request must be higher than chip power allocation

class over_pwr_req_pcy extends rand_policy_base #(pwr_mng_chip); constraint over_pwr_req_c { item.ports.sum(port) with (int'(port.pwr_req)) > item.chip_pwr_alloc ; } endclass


// Test instansite manager and policy class and randomize the chip

class sys_test extends uvm_test;

...

pwr_mng_chip chip; over_pwr_req_pcy over_pwr_req;


task run_phase (uvm_phase phase);


// Construct the manager chip with NUM_PORTS = 8 chip = new(.num_ports(`NUM_PORTS));


// Construct policy class

over_pwr_req=new;


// Set policy class to manager class policies queue

chip.chip_power_policies = {over_pwr_req};


// Randomize manager class and display results if (!chip.randomize()) `uvm_error(get_name(), "Failed to randomize sys power policies") $display(chip.convert2string());

...

endtask


The outcome for the previously described scenario, with a specific seed value, is as follows:


----------------------

chip_pwr_alloc : 292

----------------------

ports_pwr_req : 310

---------------------------------------------------

| port name | power_request | fault_type | priority

---------------------------------------------------

| port_0 | 30 | NOFL | HIGH

| port_1 | 20 | NOFL | HIGH

| port_2 | 50 | NOFL | LOW

| port_3 | 50 | NOFL | LOW

| port_4 | 20 | NOFL | CRITICAL

| port_5 | 50 | NOFL | HIGHEST

| port_6 | 40 | NOFL | CRITICAL

| port_7 | 50 | NOFL | CRITICAL

---------------------------------------------------


As the ports request sum up to 310 watts while the power allocation for the entire chip remains at 292 watts, the controller is now faced with the task of determining which ports to activate based on the priority algorithm.

 

Scenario 2 :


Among the ports, a minimum of 2 and maximum of 4 should exhibit a failure mode, while those in non-failure mode should maintain their power requests unchanged


Set to a number greater than or equal to the minimum and less than or equal to the maximum number of failures.

class min_num_of_failures_pcy extends rand_policy_base #(pwr_mng_chip); int min_failures; int max_failures; randc bit [(`NUM_PORTS-1):0] selected_ports; constraint min_num_failures_c { $countones(selected_ports) inside {[min_failures:max_failures]}; foreach(selected_ports[idx]) { if (selected_ports[idx]) { item.ports[idx].failure != NOFL; } } } ... endclass


In the test class


min_num_of_failures_pcy min_num_of_failures;

...

// Remove policy classes that were previously assigned.

chip.chip_power_policies.delete();


// Don't change chip power allocation chip.chip_pwr_alloc.rand_mode(OFF);


// Don't change port priorities

chip.prio.rand_mode(OFF);


// Don't change ports power request

foreach (chip.ports[idx]) chip.ports[idx].pwr_req.rand_mode(OFF);


// Between 2 to 4 failures to port controller

min_num_of_failures=new(2,4);


// Set policy class to manager class policies queue

chip.chip_power_policies = {min_num_of_failures};


// Randomize manager class and display results if (!chip.randomize()) `uvm_error(get_name(), "Failed to randomize sys power policies")

$display(chip.convert2string());


The outcome for the previously described scenario, with a specific seed value, is as follows:

----------------------

chip_pwr_alloc : 292

----------------------

ports_pwr_req : 310

---------------------------------------------------

| port name | power_request | fault_type | priority

---------------------------------------------------

| port_0 | 30 | NOFL | HIGH

| port_1 | 20 | NOFL | HIGH

| port_2 | 50 | OVRC | LOW

| port_3 | 50 | NOFL | LOW

| port_4 | 20 | NOFL | CRITICAL

| port_5 | 50 | OVRC | HIGHEST

| port_6 | 40 | SHRT | CRITICAL

| port_7 | 50 | OPEN | CRITICAL

---------------------------------------------------


From the results, it is evident that priorities, chip power allocation, and requested power for ports remain unchanged. However, for this particular seed, four ports exhibit one of the failure modes.

 

Scenario 3 :

  • For the initial 4 ports, introduce a minimum of 1 SHORT failure and 1 OVERC failure. [7:4] ports are NOF.

  • For the initial 4 ports, all priorities should be higher or equal to HIGH, for [7:4] lower than HIGH.

  • The allocation of power to the chip should exceed the power requests of the initial 4 ports but remain below the power requests of all ports

Observe that this class is an extension of the previously declared policy discussed in scenario 2.


// Set failure mode to 4 initial ports

class location_failure_pcy extends min_num_of_failures_pcy; constraint initial_4_ports { selected_ports[7:4] == 0; } constraint failure_4_ports { foreach(selected_ports[idx]) { if (selected_ports[idx]) { item.ports[idx].failure inside {OVRC, SHRT}; } } } function new; super.new(1,4); endfunction endclass


// Set priorities to ports

class location_priority_pcy extends rand_policy_base #(pwr_mng_chip); constraint priority_location { foreach (item.prio[idx]) { if (idx<4) { item.prio[idx] inside {[HIGH:HIGHEST]}; } else { item.prio[idx] inside {[LOW:MEDIUM]}; } } } endclass


// Chip power allocation must be higher than ports with index equal [0,1,2,3]

class location_pwr_req_pcy extends rand_policy_base #(pwr_mng_chip); constraint location_pwr_req { item.ports.sum(port) with (int'(port.pwr_req * (port.index < 4))) < item.chip_pwr_alloc; } endclass


in test class

...

location_failure=new; location_priority=new; location_pwr_req=new; chip.chip_power_policies = {location_failure, location_priority, over_pwr_req, location_pwr_req}; if (!chip.randomize()) `uvm_error(get_name(), "Failed to randomize sys power policies") $display(chip.convert2string());


The outcome for the previously described scenario, with a specific seed value, is as follows:

----------------------

chip_pwr_alloc : 184

----------------------

ports_pwr_req : 310

---------------------------------------------------

| port name | power_request | fault_type | priority

---------------------------------------------------

| port_0 | 30 | NOFL | CRITICAL

| port_1 | 20 | SHRT | CRITICAL

| port_2 | 50 | NOFL | CRITICAL

| port_3 | 50 | OVRC | HIGHEST

| port_4 | 20 | NOFL | MEDIUM

| port_5 | 50 | NOFL | MEDIUM

| port_6 | 40 | NOFL | LOW

| port_7 | 50 | NOFL | LOW

---------------------------------------------------


Analyzing the results, we observe that the power request for ports [3:0] is 150 watts, while the chip's power allocation is 184 watts, satisfying requirement number 3. Furthermore, the priority levels of the initial four ports are set as 'highest' or 'equal to HIGH,' meeting requirement number 2. Additionally, ports 1 and 3 exhibit SHORT and OVER current conditions, satisfying requirement number 1.

 

In this article about layering via reusable policy classes, we discuss a way to use reusable policy classes in PMS applications. This method has been tested and is highly flexible. It allows us to change requirements as our tests run, making it easy to simulate real-life situations and see how the device being tested reacts. Later I also want to look at the idea of mixing different policy classes randomly in the same queue without causing contraindication in randomization.


The code can be found here

3 Comments


Guest
Sep 20, 2023

I didn't think you would be able to put heterogeneous class items into a single queue, but I guess since they are all derived from the same parent class it works?

Like
Shimon
Nov 21, 2023
Replying to

Yes you are correct, this is polymorphism.

Like
bottom of page