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

3 Responses

    1. 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
  1. It might be the case with old version of simulator. I tried this on vcs 2020 version and got error msg as below:

    Error-[CSTR-UMC] Constraint: unsupported method call
    testbench.sv, 17
    $unit, “this.get_a()”
    Method constraint_container::get_a contains a construct not supported in
    constraint functions: reference to a rand variable ‘a’ at line 7 of
    testbench.sv.
    Rewrite the method to avoid unsupported constructs.

    1 error

Leave a Reply

Your email address will not be published. Required fields are marked *

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

Subscribe to our newsletter

Do you want to be up to date with our latest articles?