Abstract Classes and constraints?

In reply to Mark Curry:
Ok, after spending (too much) time playing with randomization constraints for the last week or so, I’m back with followups. I just can’t get things to work. I’ve reduced things down to a small(ish) example. The example does NOT contain any abstract classes, but should show where I’m having difficulties.

I first declare a parameterized class


  class foo_c #( type T = bit [ 15 : 0 ] );
    typedef foo_c #( T ) foo_ic_q [ $ ];
    rand T private_obj;
    rand foo_ic_q children;

As shown, these objects contain a private_obj, and a queue of a number of children. We use these objects throughout our testbenches for modeling our golden code and driving the DUTs.
Next, I’ve added some stuff to help us add constraints to these objects:


    typedef bit [ 31 : 0 ] limits_t[ $ ]; // First index is which constraint (we may apply more than one),
    typedef int sum_indices_t[ $ ][ $ ];  // First index is which constraint.
                                          // Second index is an index into which child member the constraint relates to
    sum_indices_t sum_indices;
    limits_t      limits;

Now we add a do_sum() functions - which sums child member private_objects (with a qualifier)

    function bit [ 31 : 0 ] do_sum( input foo_ic_q children_arg = {}, int index = 0 ); 
      bit [ 31 : 0 ] rets;
      rets = 0;
      if( ( sum_indices.size() > index ) && ( children_arg.size() != 0 ) )
      begin
        for( int i = 0; i < sum_indices[ index ].size(); i++ )
	begin  
	  int this_child_index;
	  this_child_index = sum_indices[ index ][ i ]; 
	  if( children_arg.size() > this_child_index )
	    rets = rets + children_arg[ this_child_index ].private_obj;
	end
      end
      return( rets );
    endfunction

Note, I coded this method to actual explicitly pass the child objects as function arguments, instead of just operating on the (local) class member. My thoughts here was this would enable the constraint solver to “see” the other rand members.
Now a constraint:

    constraint children_less_than 
    {  
      foreach( sum_indices[ which_constraint ] )
      (
        ( limits.size() > which_constraint ) -> ( do_sum( this.children, which_constraint ) < limits[ which_constraint ] )
      ); 
    }

Now finally, a test that puts it all together:

  typedef foo_c #( bit [ 15 : 0 ] ) foo_spec;

  foo_spec parent, child0, child1;

  initial
  begin
    parent = new();
    child0 = new();
    child1 = new();
    parent.children.push_back( child0 );
    parent.children.push_back( child1 );
    if( parent.randomize() )
      $display( "Randomize() returned child0 = 0x%0x, child1 = 0x%0x, sum = 0x%0x.", child0.private_obj, child1.private_obj, child0.private_obj + child1.private_obj );
    else
      $display( "Randomize() Failed" );

This (unconstrained) example works great. The solver is randomizing parent, and according to spec, following the rand objects of children, and randomizing them too. All’s great! Until I try and add constraints (my original problem):

    parent.sum_indices.push_back( { 0, 1 } ); // child0+child1 
    parent.limits.push_back( 32'd100 );      // ( child0+child1 ) < 100
    if( parent.randomize() )
      $display( "Randomize() returned child0 = 0x%0x, child1 = 0x%0x, sum = 0x%0x.", child0.private_obj, child1.private_obj, child0.private_obj + child1.private_obj );
    else
      $display( "Randomize() Failed" );

This fails randomize(). I can get it to occasionally pass randomize() by playing with the limits. I’ve got a feeling I understand what’s happening (the children are being independently randomized() before the parent). But at this point, I’m unsure of the best way to proceed. I can’t think of any clean ways of constraining my variables.