Constraining WStrb

Hi All,
I have a 128-bit AXI interface which supports Sparse Writes. Although as per AXI protocol each bit of WStrb corresponds to 1 byte of write data bus, in our case WStrb operates on DWord ( 32-bit ) granularity.
So essentially each 4-bit WStrb would be 4’h0 or 4’hF.
An additional requirement is that "strobes can’t be discontinuous meaning that we can’t have WStrb as 16’hF0F0" i.e we can’ have a 0 between 2 F’s
Eg: 16’hF0F0 would be illegal whereas 16’h0FF0 , 16’hFFF0 would be legal

Here is my attempt

task write_data_axi(input longint my_addr, input bit [127:0] data_in[],input bit[3:0] awlen,output bit[3:0] DWord_strobe[],input bit rand_addr = 1);

  bit [3:0]   Dword_strb[];

  if($test$plusargs("SPARSE_WRITES")) begin
       
     Dword_strb = new[4*data_in.size()]; // For 128-bit Wdata there would be 4 DWords, For 2 beats ( each of 128-bit WData ) there would be 8 DWords

      if( !( std::randomize(Dword_strb) with { foreach(Dword_strb[i]) Dword_strb[i] inside { 4'h0 , 4'hF }; } )  )
          `uvm_warning(get_type_name(),$sformatf("Randomization failed for Dword_strb"))
      else
          `uvm_info(get_type_name(),$sformatf("For Sparse Writes,using DWord Strobe as %0p",Dword_strb),UVM_LOW)

     DWord_strobe = Dword_strb; 

  end
  .......................
endtask

The above in-line constraint only constraints each nibble of WStrb to either 4’h0 or 4’hF.
I am seeking suggestions to constraint as per “strobes can’t be discontinuous”

One possible solution is that I have a queue ‘valid_q’ ( each element is 16-bit wide ) of all valid combinations and I constraint

  foreach(Dword_strb[i])
 {     if( i % 4 == 0 )
     {
        Dword_strb[i+:4]  inside { valid_q } ; 
     }
 }

Any possible alternative solution without using non-random array ?
( Curious if there is a possible solution using iterative constraint like sum()/and() )

class non_sparse_strb;

rand bit  w_strb[];
rand int  valid_bytes;
rand int offset_start;

constraint c_strb { 
                   w_strb.size() == 16;
                   valid_bytes inside {[2:8]};
                   offset_start inside{[2:8]};
                   offset_start + valid_bytes <= 16;
                  }

function void post_randomize();
  foreach(w_strb[i]) w_strb[i]=0;
   $display("\n#########################################33");
 $display("\n valid bytes = %0d , offset = %0d", valid_bytes, offset_start);
  for(int i = offset_start ; i < offset_start+valid_bytes ;i++)
    w_strb[i] = 1;
endfunction

function void print();
  foreach(w_strb[i]) $write("%0d ",w_strb[i]);
endfunction
endclass

module tb;
 non_sparse_strb nstrb;

initial begin
 nstrb = new();
repeat(5) begin
 nstrb.randomize();
 nstrb.print();
end 
end
endmodule

class non_sparse_strb models a non-sparse strobe signal (w_strb) that marks valid portions of dynamically sized data_in. The constraints ensure data_in scales between 128*1 and 128*8 bits while maintaining proper mapping to w_strb. valid_bytes controls how many dwords (32-bit chunks) are valid, and offset_start defines their starting position within bounds. The post_randomize function resets w_strb before correctly marking valid positions.


class non_sparse_strb;

rand bit  w_strb[];
randc int  valid_bytes;
rand int offset_start;
rand bit [127:0] data_in[];
    
constraint c_strb {  
  data_in.size() inside {[1:8]};  
  w_strb.size() == 4 * data_in.size();  
  valid_bytes inside {[1:w_strb.size()]};  
  offset_start inside {[0:w_strb.size() - valid_bytes]};  
}

function void post_randomize();  
  w_strb = '{default: 0};  
  for (int i = offset_start; i < offset_start + valid_bytes; i++)  
    w_strb[i] = 1;  
endfunction  

function void print();
  foreach(w_strb[i]) $write("%0d ",w_strb[i]);
  $display("\n valid bytes = %0d , offset = %0d strb.size = %0d data_in.size() = %0d", valid_bytes, offset_start,  w_strb.size(),data_in.size());
endfunction
endclass

module tb;
 non_sparse_strb nstrb;

initial begin
 nstrb = new();
  repeat(16) begin
   nstrb.randomize();
   nstrb.print();
  end 
end

endmodule