|
1 Then each test could define its own version of this constraint to generate its own flavors of stimulus. External constraints have several advantages over in-line constraints. They can be put in a file and thus reused between tests. An external constraint applies to all instances of the class, while an in-line constraint only affects the single call to randomize.
// packet.sv
class Packet;
rand bit [7:0] length;
rand bit [7:0] payload[];
constraint c_valid {length > 0;
payload.size == length;}
constraint c_external;
endclass
Example 6-31 Program defining external constraint
// test.sv
program test;
constraint Packet::c_external {length == 1;}
...
endprogram
2 Don’t use signed numbers in random constraints unless you really want signed values.
class SignedVars;
rand byte pkt1_len, pk2_len;
constraint total_len {
pkt1_len + pk2_len == 64;
}
endclass
class Vars32;
rand logic [31:0] pkt1_len, pk2_len; // unsigned type
constraint total_len {
pkt1_len + pk2_len == 64;
}
endclass
3 class dyn_size;
rand reg [31:0] d[];
constraint d_size {d.size inside {[1:10]}; }// make the array smaller and faster
endclass
4 传输1到8次,每次的传输的总和小于1024
The problem here is that in verilog, the sum of many 8-bit values
is computed using an 8-bit result.
数组全部为符号的八位操做不行,因为为有符号数操作
数组全部为无符号八位数也不行,因为verilog结果也是八位的,见上面的英文解释
数组全部为32位的无符号也不行,因为可能wrap around,结果正好小于了(溢出的不考虑)
数组全部为10位的无符号数也不好,很可能第1位经常为1,所以结果经常大于256
The only practical way to constrain a dynamic array or queue is with foreach.
solution:
class good_sum5;
rand uint len[];
constraint c_len {foreach (len[i])
len[i] inside {[1:255]};
len.sum < 1024;
len.size inside {[1:8]};}
5 Generating an array of unique values
Your may be tempted to use a constraint with nested foreach specifying
a[i] != a[j]. The SystemVerilog solver expands out these equations, so
an array of 30 values creates almost 1000 constraints.
// Generate a random array of unique values
class UniqueArray;
int max_array_size, max_value;
rand bit [7:0] a[]; // Array of unique values
constraint c_size {a.size inside {[1:max_array_size]};}
function new(int max_array_size=2, max_value=1);
this.max_array_size = max_array_size;
if (max_value < max_array_size)
this.max_value = max_array_size;
else
this.max_value = max_value;
endfunction
// At this point array is allocated, fill w/unique vals
function void post_randomize;
RandcRange rr = new(max_value);
foreach (a[i]) begin
assert (rr.randomize());
a[i] = rr.value;
end
endfunction
function void display;
$write("Size: %3d:", a.size());
foreach (a[i]) $write("%4d", a[i]);
$display;
endfunction
endclass
In my opinion, you just use one randc variable in [1:max_size],that's ok.
6 With randsequence you describe the grammar of the transaction
initial begin
for (int i=0; i<15; i++) begin
randsequence (stream)
stream : cfg_read := 1 |
io_read := 2 |
mem_read := 5;
cfg_read : { cfg_read_task; } |
{ cfg_read_task; } cfg_read;
mem_read : { mem_read_task; } |
{ mem_read_task; } mem_read;
io_read : { io_read_task; } |
{ io_read_task; } io_read;
endsequence
end // for
end
task cfg_read_task;
...
endtask
A stream can be either cfg_read, io_read, or mem_read. The random sequence engine randomly picks one. A cfg_read can be either a single call to the cfg_read_task, or a call to the task followed by another cfg_read.
The code to generate the sequence is separate and a very different style. from the classes with data and constraints used by the sequence.
More seriously, if you want to modify a sequence, perhaps to add a new
branch or action, you have to modify the original sequence code.
The last form. of generating random sequences is to randomize an entire
array of objects. You can create constraints that refer to the previous and next
objects in the array,
Combining sequences in VMM
7 initial begin
int len;
randcase//can be used in the procedual block
1: len = $urandom_range(0, 2); // 10%: 0, 1, or 2
8: len = $urandom_range(3, 5); // 80%: 3, 4, or 5
1: len = $urandom_range(6, 7); // 10%: 6 or 7
endcase
$display("len=%0d", len);
end
class LenDist;
rand int len;
constraint c
{len dist {[0:2] := 1, [3:5] := 8, [6:7] := 1}; }//just in the constraint block
endclass
LenDist lenD;
initial begin
lenD = new;
assert (lenD.randomize());
$display("Chose len=%0d", lenD.len);
end
Code using randcase is more difficult to override and modify than random
constraints. The only way to modify the random results is to rewrite the
code or use variable weights
8 Be careful using randcase, as it does not leave any tracks behind. For
example, you could use it to decide whether or not to inject an error in a transaction. The problem is that the downstream transactors and scoreboard need to know of this choice. The best way to inform. them would be to use a variable in the transaction or environment.
9 initial begin
// Level 1
randcase
one_write_wt: do_one_write();
one_read_wt: do_one_read();
seq_write_wt: do_seq_write();
seq_read_wt: do_seq_read();
endcase
end
// Level 2
task do_one_write;
randcase
mem_write_wt: do_mem_write();
io_write_wt: do_io_write();
cfg_write_wt: do_cfg_write();
endcase
endtask
task do_one_read;
randcase
mem_read_wt: do_mem_read();
io_read_wt: do_io_read();
cfg_read_wt: do_cfg_read();
endcase
endtask
10 Every object and thread have its own PRNG and unique seed. When a new
object or thread is started, its PRNG is seeded from its parent’s PRNG.
11