top of page

Clarifying solve before

Only after reading this article did I realize that I do not understand at all the subject known as variable ordering also referred to as solve before (section 18.5.10 in LRM).


Here's a quote by Dave Rich from the article.

"The solve before constraint is one of the most misunderstood constructs in SystemVerilog. It is not really a constraint in that it has no effect on the available solution set. It only effects the distribution in the choice of values among the available solutions."


 

In my opinion, the most effective approach to mastering this subject involves creating an example, experimenting with it, and observing how simulation outcomes evolve when constraints are altered.


In the given illustration, I've defined two enum variables: instr_group_t, which signifies instruction groups within the CPU instruction set, and instr_t, which denotes the actual instructions.


Within my example, I've included three distinct groups.

  • DATA_PROC representing standard data-processing instructions

  • SHIFT representing shift instructions

  • PACK representing packing and unpacking instructions


The instructions associate with each group are presented in the following table

DATA_PROC

SHIFT

PACK

ADC

ASR

SXTB

ADD

LSL

SXTH

ADDR

LSR

UXTB

AND

BIC

During each randomization cycle, the simulator will select an instruction group and pair it with an instruction from the set of instructions associated with that particular group.


The example will demonstrate how the distribution of selected pairs changes in response to the number of instructions linked to a group and the addition of a "solve before" constraint block. The code can be found here

 

Let's start with defining the enums


typedef enum int {DATA_PROC, SHIFT, PACK} instr_group_t;

typedef enum int { /*DATA PROCESS*/ ADC, ADD, ADR, AND, BIC, CMP, /*SHIFT*/ ASR, LSL, LSR, /*PACK_UNPACK*/ SXTB, SXTH, UXTB } instr_t;


Every variable definition and constraint will reside within the instr_item class.


class instr_item extends uvm_object;


Within that class, two random variables were defined, one for the instruction group and another for the instruction itself.


rand instr_group_t instr_group; rand instr_t instr;


Three instruction queues were declared and initialized, this will enable us to easily

modify selection sets when we intend to alter the distribution of selected pairs.


instr_t data_proc_q[$] = {ADC, ADD, ADR}; instr_t shift_q[$] = {ASR, LSL, LSR}; instr_t pack_q[$] = {SXTB, SXTH, UXTB};



Additionally, we have defined four static variables to keep track of the engine selection during each randomization cycle.


static int data_proc_cnt; static int shift_cnt; static int pack_cnt; static int total_cnt;


The constraint block is quite straightforward: it involves selecting an instruction group and an instruction from the set of instructions associated with that group.


constraint inster_c { // Choose instruction from group if (instr_group==DATA_PROC) { instr inside {data_proc_q}; } else if (instr_group==SHIFT) { instr inside {shift_q}; } else { instr inside {pack_q}; } }


In the post_randomize method the results are going to be counted


function void post_randomize(); if (instr inside {data_proc_q}) begin data_proc_cnt++; total_cnt++; end else if (instr inside {shift_q}) begin shift_cnt++; total_cnt++; end else begin pack_cnt++; total_cnt++; end endfunction : post_randomize


 

For the case presented above, lets calculate the probability for each pair.

Group

Instruction

Probability per instruction

Probability per group

DATA_PROC

ADC

1/9

DATA_PROC

ADD

1/9

DATA_PROC

ADR

1/9

1/3

SHIFT

ASR

1/9

SHIFT

LSL

1/9

SHIFT

LSR

1/9

1/3

PACK

SXTB

1/9

PACK

SXTH

1/9

PACK

UXTB

1/9

1/3

TOTAL

9

1

1

Here are the outcomes following 1200 randomization cycles. As anticipated, we can observe that each group has occurred one-third of the time.


 Total number of data process instructions: 388, percent from total:    32.000
 Total number of shift        instructions: 390, percent from total:    32.000
 Total number of pack unpack  instructions: 422, percent from total:    35.000 


 

Let's modify the data_proc_q queue to hold six values instead of three.


instr_t data_proc_q[$] = {ADC, ADD, ADR, AND, BIC, CMP};


For the new case, lets calculate the probability for each pair.

Group

Instruction

Probability per instruction

Probability per group

DATA_PROC

ADC

1/12

DATA_PROC

ADD

1/12

DATA_PROC

ADR

1/12

DATA_PROC

AND

1/12

DATA_PROC

BIC

1/12

DATA_PROC

CMP

1/12

6/12

SHIFT

ASR

1/12

SHIFT

LSL

1/12

SHIFT

LSR

1/12

3/12

PACK

SXTB

1/12

PACK

SXTH

1/12

PACK

UXTB

1/12

3/12

TOTAL

12

1

1

These are the results after 1200 randomization cycles, in line with our expectations: the DATA_PROC group was selected 50% of the time, while SHIFT and PACK were each chosen 25% of the time.


Total number of data process instructions: 588, percent from total:    49.000
 Total number of shift        instructions: 307, percent from total:    25.000
 Total number of pack unpack  instructions: 305, percent from total:    25.000


The "solve before" constraint comes in handy if we aim to equalize the probabilities of group selection.


In the scenario where we require equal group selection probability, let's compute the probability for each pair.


​Group

​Instruction

​Probability per instruction

​Probability per group

DATA_PROC

1/18

DATA_PROC

1/18

DATA_PROC

1/18

DATA_PROC

1/18

DATA_PROC

1/18

DATA_PROC

1/18

1/3

SHIFT

1/9

SHIFT

1/9

SHIFT

1/9

1/3

PACK

1/9

PACK

1/9

PACK

1/9

1/3

TOTAL

12

1

1


The altered constraint block, now incorporating the "solve before" constraint, appears as follows:


constraint inster_c { // Choose instruction from group if (instr_group==DATA_PROC) { instr inside {data_proc_q}; } else if (instr_group==SHIFT) { instr inside {shift_q}; } else { instr inside {pack_q}; } // Apply variable ordering solve instr_group before instr; }



Total number of data process instructions: 388, percent from total:    32.000
 Total number of shift        instructions: 390, percent from total:    32.000
 Total number of pack unpack  instructions: 422, percent from total:    35.000

After doing the math, you can notice that the chances of each group being selected are now the same.

3 Comments


Guest
Nov 06, 2023

I have faced this issue during a project execution, where one variable(a) had 2 values and other var(b) had 1000 values. if a==0 -> b==0;. (a=0,b=0) combination had probability of 1/1001 only. I could find this only when i analyzed the post randomize logs.

Like
Shimon
Nov 21, 2023
Replying to

Sorry for late response. Let me investigate this.

Like

Guest
Sep 05, 2023

Good Luck Shimon, it looks Amazing

Like
bottom of page