Gotcha: Function Calls in SystemVerilog Constraints

SystemVerilog allows to call functions inside constraints, although, as I found out, it is a sensitive topic.
Here is an example:

class constraint_container;
  rand int unsigned a, b, c;

  function int unsigned get_a();
    return a;
  endfunction

  function int unsigned value_of(int unsigned value);
    return value;
  endfunction

  constraint a_constraint {
    a == 5;
    // I expect "b" to be equal to "a", but, surprise, surprise...
    b == get_a();
    // I expect "c" will be equal to "a"
    c == value_of(a);
  }
endclass

module top;
  initial begin
    automatic constraint_container cc_inst = new();
    void'(cc_inst.randomize());
    $display($sformatf("a: %0d, b: %0d, c: %0d", cc_inst.a, cc_inst.b, cc_inst.c));
  end
endmodule

At first glance one would guess that all three fields will be 5, but one will get instead:

OUTPUT:
a: 5, b: 0, c: 5

Gotcha: regardless of the seed, b will always be equal to 0!
It looks like get_a() is evaluated before the generation, when a is 0.

The closest thing I could find as an explanation for this behavior was in SystemVerilog IEEE 1800-2012 standard, chapter “18.5.12 Functions in constraints”:

Functions shall be called before constraints are solved, and their return values shall be treated as state variables.
Function calls in active constraints are executed an unspecified number of times (at least once) in an unspecified order.

The conclusion is that functions used by constraints should compute the result based ONLY on function’s arguments and NOT on class members.


Comments

Tudor Timi March 12th, 2015 22:12:20

It’s the same for e as well. Cadence has a note saying that functions used in constraints shouldn’t use any class members/global variables.


    Cristian Slav March 13th, 2015 10:20:42

    Hi Tudor,

    I could not find such a note. Can you please point me to a specific chapter?

    I just gave it a try in ‘e’ language using this code:

    struct constraint_container {
       a : uint;
       b : uint;
       c : uint;
    
       get_a() : uint is {
          return a;
       };
    
       value_of(value : uint) : uint is {
          return value;
       };
    
       keep a == 5;
       keep b == get_a();
       keep c == value_of(a);
    };
    
    extend sys {
       run() is also {
          var cc_inst : constraint_container;
          gen cc_inst;
          messagef(LOW, "a: %0d, b: %0d, c: %0d", cc_inst.a, cc_inst.b, cc_inst.c);
       };
    };

    The output is this:

    OUTPUT:
    a: 5, b: 5, c: 5

Leave a Comment:

Your comment will be visible after approval.

(will not be published)

This site uses Akismet to reduce spam. Learn how your comment data is processed.