Advice for simplifying constraint

I am trying to write constraint for generating sequence for a given say ‘n’ AXI transactions with same burst size and burst length. However consecutive generated AXI transaction addresses needs to be sequential only i.e.
transaction1 start address should follow transaction0 end address …
transaction2 start address should follow transaction1 end address …

transactionN start address should follow transactionN-1 end address …

To achieve same I have written following, which does work correctly. But with higher values of ‘n’ is causing either simulation running long or constraint solver hanging.


class tr_gen;
  rand bit[1:0]  bsz; 
  rand bit[31:0] s_addr[$];
  rand bit[31:0] e_addr[$];
  rand bit[2:0]  bl;
  bit[31:0] addr_4KB_msk = ~((32'('h1) << 12)-1);

  rand bit[5:0] num;

  constraint c_tr_n {
    s_addr.size() == (num+1); // number of transactions
    e_addr.size() == s_addr.size();
  }

  constraint c_s_addrs {
    foreach(s_addr[it]) if(it>0) {
      s_addr[it] == s_addr[it-1]+((bl+1)*(2**bsz)); // consecutive start addr = addr + total num bytes in transasction
    }
  }
  constraint c_e_addrs {
    foreach(e_addr[it]) if(it>0) {
      e_addr[it] == e_addr[it-1]+((bl+1)*(2**bsz)-1); // end addr = start addr + total num bytes in transaction - 1
    }
  }

  constraint c_4kb_boundary {
    foreach(s_addr[it]) {
      (s_addr[it] & addr_4KB_msk) == (e_addr[it] & addr_4KB_msk); // for every transaction ensure 4kb boundary
    }
  } 

  constraint c_s_addr_byte_align {
    foreach(s_addr[it]) {
      bsz == 1 -> s_addr[it][0:0] = 0; // 2 byte
      bsz == 2 -> s_addr[it][1:0] = 0; // 4 byte
      bsz == 3 -> s_addr[it][2:0] = 0; // 8 byte
    }
  }

endclass

module try;

initial begin
  tr_gen tr;
  tr = new();
  void'(tr.randomize() with { 
   num == 5;
  });

  // use tr 
end

endmodule


For higher values of num, simulator is hanging. Any suggestions to make it more optimized or simulator friendly?

In reply to bhupeshpaliwal:

I was able to get a result for num == 63, which is the largest possible value, for all simulators on EDAPlayground. I noticed you had some typos (= instead of ==), so maybe you did not copy the example correctly and missed something. Or maybe you have hit a tool specific issue. Some comments for optimizations:

  1. Use dynamic arrays instead of queues [$] for quicker access unless you plan on pushing or popping values.
  2. You only need to check s_addr[0] for byte alignment.
  3. You could calculate bsz, bl, and the array sizes in pre_randomize to simplify the constraint equations.
  4. You could calculate e_addr in post_randomize to simplify the constraint equations.

In reply to dave_59:
Thanks Dave, without above suggestions, especially using dynamic array instead of queues has improved performance a lot.
Also since end address we were only using to calculate for 4kb boundary, so removed it altogether.
However considering for me num value is in range of close to more than 'd3000+ so still seeing some hangs on max values.

One trivial query:

You could calculate bsz, bl, and the array sizes in pre_randomize to simplify the constraint equations.
By this you meant that in pre_randomize() following needs to be done :




class tr_gen;
  rand bit[1:0]  bsz; 
  rand bit[31:0] s_addr[];

  rand bit[2:0]  bl;
  bit[31:0] addr_4KB_msk = ~((32'('h1) << 12)-1);
 
  rand bit[5:0] num;
 
  constraint c_tr_n {
    s_addr.size() == (num+1); // number of transactions
  }
 
  constraint c_s_addrs {
    foreach(s_addr[it]) if(it>0) {
      s_addr[it] == s_addr[it-1]+((bl+1)*(2**bsz)); // consecutive start addr = addr + total num bytes in transasction
    }
  }
 
  constraint c_4kb_boundary {
    foreach(s_addr[it]) {
      (s_addr[it] & addr_4KB_msk) == (s_addr[it]+((bl+1)*(2**bsz)-1)) & addr_4KB_msk); // for every transaction ensure 4kb boundary
    }
  } 

  // constraint only s_addr[0] 
  constraint c_s_addr_byte_align {
    bsz == 1 -> s_addr[0][0:0] == 0; // 2 byte
    bsz == 2 -> s_addr[0][1:0] == 0; // 4 byte
    bsz == 3 -> s_addr[0][2:0] == 0; // 8 byte
  }

  function void pre_randomize();
    randomize(bsz,bl); // randomize
    bsz.rand_mode(0); // no further randomize required for bsz
    bl.rand_mode(0); // no further randomize required for bl

    // num is passed as inline constraint will reflect here ?
    s_addr = new [num+1];
    c_tr_n.constraint_mode(0); // no further constraint required
  endfunction

endclass
 
module try;
 
initial begin
  tr_gen tr;
  tr = new();
  void'(tr.randomize() with { 
   num == 5;
  });
 
  // use tr 
end
 
endmodule


In reply to bhupeshpaliwal:

You could do that although it’s probably easier declaring bsz and bl as non-rand, then calling std::randomize(bsz,bl). That way you don’t have to deal with rand_mode/constraint_mode.

In reply to dave_59:
Thanks Dave for your kind guidance.