Randomization with function

Hi, everyone!

i cannot randomize my object, if i use function in constrainsts:

class A;
  rand int a;
  rand int b;
  
  function static int foo(int i);
    $display("call foo() with a: %0d, b: %0d", a, b);
    
    return i == 0;
  endfunction
  
  constraint b_c {    
    b inside {[0:5]};
  };
  constraint a_c {   
    a == foo(b);
    a inside {[1:5]};
  };
  
endclass

A a = new();

module tb;
  initial begin
    void'(a.randomize());
    
    $display("Result: %p", a);
    
    $finish();
  end
endmodule

i receive following message:

call foo() with a: 0, b: 3

testbench.sv(25): randomize() failed due to conflicts between the following constraints:

testbench.sv(15): a_c { (a == this.foo(b)); }

testbench.sv(16): a_c { (a inside { [1:5] }); }

Where:

a = 0 /* random */

this.foo(b) = 0

looks like solver has made one run with a = 0, b=3 and then stopped

if i do not use function, like this

class A;
  rand int a;
  rand int b;
  
  function static int foo(int i);
    $display("call foo() with a: %0d, b: %0d", a, b);
    
    return (i == 0);
  endfunction
  
  constraint b_c {    
    b inside {[0:5]};
  };
  constraint a_c {   
    if (b==0) a == 1;
    else a == 0;
    a inside {[1:5]};
  };
  
endclass

A a = new();

module tb;
  initial begin
    void'(a.randomize());
    
    $display("Result: %p", a);
    
    $finish();
  end
endmodule

solver works correctly:

Result: '{a:1, b:0}

** Note: $finish : testbench.sv(30)

can anyone explain, why function are not working with randomization?
I use Questa 2020.1

In reply to boryah:

Hi, everyone!
i cannot randomize my object, if i use function in constrainsts:

class A;
rand int a;
rand int b;
function static int foo(int i);
$display("call foo() with a: %0d, b: %0d", a, b);
return i == 0;
endfunction
constraint b_c {    
b inside {[0:5]};
};
constraint a_c {   
a == foo(b);
a inside {[1:5]};
};
endclass
A a = new();
module tb;
initial begin
void'(a.randomize());
$display("Result: %p", a);
$finish();
end
endmodule

i receive following message:
looks like solver has made one run with a = 0, b=3 and then stopped
if i do not use function, like this

class A;
rand int a;
rand int b;
function static int foo(int i);
$display("call foo() with a: %0d, b: %0d", a, b);
return (i == 0);
endfunction
constraint b_c {    
b inside {[0:5]};
};
constraint a_c {   
if (b==0) a == 1;
else a == 0;
a inside {[1:5]};
};
endclass
A a = new();
module tb;
initial begin
void'(a.randomize());
$display("Result: %p", a);
$finish();
end
endmodule

solver works correctly:
can anyone explain, why function are not working with randomization?
I use Questa 2020.1

You may need to read the LRM section 18.5.12 Functions in constraints, where there is a mention about:
*“Functions that appear in constraint expressions should be automatic (or preserve no state information)
and have no side effects.”

*

However I believe your issue is due to the ordering that is establish when using functions in constraints, basically “b” gets solved first as “3” then foo(3) = 0, which creates the conflict of a == 0 and a inside {[1:5]}

"Random variables used as function arguments shall establish an implicit variable ordering or
priority. Constraints that include only variables with higher priority are solved before other, lower
priority constraints. Random variables solved as part of a higher priority set of constraints become
state variables to the remaining set of constraints.
For example:

class B;
rand int x, y;
constraint C { x <= F(y); }
constraint D { y inside { 2, 4, 8 } ; }
endclass

forces y to be solved before x. Thus, constraint D is solved separately before constraint C, which uses
the values of y and F(y) as state variables. In SystemVerilog, the behavior for variable ordering
implied by function arguments differs from the behavior for ordering specified using the
“solve…before…” constraint; function argument variable ordering subdivides the solution space
thereby changing it. Because constraints on higher priority variables are solved without considering
lower priority constraints at all, this subdivision can cause the overall constraints to fail. Within each
prioritized set of constraints, cyclical (randc) variables are solved first.

"

BTW is always good practice to test if randomize was successful or not in your code, for example:

if(!a.randomize()) $fatal(1, "Randomize Failed");

HTH,
-R

In reply to rgarcia07:

Thank you for your reply!
yes, indeed, function should be automatic. i tried to implement it as automatic at first, but i faced same result, ten tried to change it to static just to see what happens, and copied that code into this topic, sorry about that.
Anyway, the behaviour is the same, whether i use automatic or static function.

The problem here is tthat Vendor runs function only once during constraints solving, and i dont understand yet, how to solve this

In reply to boryah:

I believe your problem has nothing to do with the tool, I tried your code on 3 simulators all failing due to the same conflict, as stated by the LRM random variables as arguments in functions gets solved first in your case you got b = 3 → foo(3) = 0 which makes a = 0 and in your constraint you are defining a inside {[1:5]} which contradicts the result, maybe you can add some guard or explain a bit further what you want to achieve so people can suggest a different approach.

HTH,
-R

In reply to boryah:

Function in constraints are used (evaluating from left to right) only to specify comparisons or specific states the variable should have. This means that you can include a checking mechanism or prints in there without any problem. Nevertheless, your case is not consistent in terms of solving since you are explicitly saying I wanna A to be 0 and in [1:5].


class A;
  rand int a;
  rand int b;
 
  function int foo(int ai,int bi);
    $display("call foo() with a: %0d, b: %0d", ai, bi);
    return 1;
  endfunction
 
  constraint b_c {    
    b inside {[0:5]};
  };
  constraint a_c {   
    foo(a,b) == 1;
    a inside {[1:5]};
  };
  
  // Use post Randomize alternatively to print out
  function void post_randomize();
    $display("Post Randomize call with a: %0d, b: %0d", a, b);
  endfunction
endclass
 
A a = new();
 
module tb;
  initial begin
    repeat(5) begin
    	void'(a.randomize());
    end
    $finish();
  end
endmodule

// Result
call foo() with a: 1, b: 0
post Randomize call foo() with a: 1, b: 0
call foo() with a: 5, b: 1
post Randomize call foo() with a: 5, b: 1
call foo() with a: 2, b: 0
post Randomize call foo() with a: 2, b: 0
call foo() with a: 1, b: 4
post Randomize call foo() with a: 1, b: 4
call foo() with a: 3, b: 3
post Randomize call foo() with a: 3, b: 3
$finish called from file “testbench.sv”, line 31.
$finish at simulation time 0

In reply to Rsignori92:

print is not what function does in this case. What it does, it returns 1 if input is 0, and 0 if input is not 1. Thus, it does specify the way constraints should be solved.

In reply to boryah:

This is exactly what I said regarding functions in constraint:

Function in constraints are used (evaluating from left to right) only to specify comparisons or specific states the variable should have. This means that you can include a checking mechanism or prints in there without any problem. Nevertheless, your case is not consistent in terms of solving since you are explicitly saying I wanna A to be 0 and in [1:5].

But the returning values (since is checking A) cannot be assigned to A so in this case for me function is mainly printing. If you wanna considering a checking mechanism then the approach can still be the same using post randomize unless you wanna the return value to be a state of a randomized variable → not A (as i said).


class A;
  rand int a;
  rand int b;
  rand bit c;
  
  function bit foo(int ai,int bi);
    $display("call foo() with a: %0d, b: %0d", ai, bi);
    return (bi != 0);
  endfunction
 
  constraint b_c {    
    b inside {[0:5]};
  };
  constraint a_c {   
    c == foo(a,b);
    a inside {[1:5]};
  };
  
  // Use post Randomize alternatively to print out
  function void post_randomize();
    $display("post Randomize call foo() with a: %0d, b: %0d", a, b);
    if(c)
      $display("b is not 0");
    else
      $display("b is 0");
  endfunction
endclass
 
A a = new();
 
module tb;
  initial begin
    repeat(5) begin
    	void'(a.randomize());
    end
    $finish();
  end
endmodule

Regards