Tuesday, June 11, 2013

SystemVerilog Constraint Gotcha

I found another one (I guess I still need to order that book). In using the UVM, I have some sequences randomizing other sub sequences. I really want it to work like this (simplified, non-UVM) example:

class Foo;
   rand int bar;
   function void display();
      $display("bar: %0d", bar);
   endfunction 
endclass

class Bar;
   int bar;
   function void body();
      Foo foo = new();
      bar = 3;
      foo.display();
      assert(foo.randomize() with {bar == this.bar;});
      foo.display();
   endfunction 
endclass

module top;
   Bar bar;
   initial begin 
      bar = new();
      bar.body();
   end 
endmodule 

See the problem there? Here's what prints out when you run the above:

bar: 0
bar: -1647275392

foo.bar is not constrained to be 3 like you might expect. That's because this.bar refers to bar that is a member of class Foo, not bar that's a member of class Bar. As far as I can tell, there is no way to refer to bar that is a member of Bar in the constraint. I guess Foo could have a reference back up to Bar, but that's really awkward. Has anyone else run into this? How do you deal with it?

UPDATE: Thank you to Mihai Oncica for pointing out that the local keyword with the scope resolution operator can be used to solve this problem. Here is the now working code example:

class Foo;
   rand int bar;
   function void display();
      $display("bar: %0d", bar);
   endfunction 
endclass

class Bar;
   int bar;
   function void body();
      Foo foo = new();
      bar = 3;
      foo.display();
      assert(foo.randomize() with {bar == local::bar;});
      foo.display();
   endfunction 
endclass

module top;
   Bar bar;
   initial begin 
      bar = new();
      bar.body();
   end 
endmodule 

And here is the result:

bar: 0
bar: 3

5 comments:

Mihai Oncica said...

Hello Bryan,

I stumbled your blog when I was trying to refresh my memory about disable fork/block_name. You showed some good examples.

Now regarding your question, a simple fix is to use local:: namespace.
For example:
assert(foo.randomize() with {bar == local::bar;});

Bryan said...

Thank you! I've never seen the local keyword used like that, but when I use it in my example it seems to work.

Gordon McGregor said...

Yup - the resolution namespace for 'with' is somewhat backwards from what you'd expect. It defaults to the destination, not the source, unlike oh everything else in the world.

The 'local' resolution was a recent hack/fix for that issue in the SV spec.

Anonymous said...

What do you do if 'bar' is a function or task argument?

Bryan said...

Anonymous, are you referring to Foo::bar or Bar::bar?

I assumed you meant Bar::bar, so I removed the int bar declaration and created a simple function:

function int bar();
return 3;
endfuction

And then changed the randomize to:

assert(foo.randomize() with {bar == local::bar();});

And it worked the same as when bar was an int. Is that what you were talking about?