Protocol specs state legality as tables, and each table row becomes one constraint line with a spec citation. Legality lives in one block in one class, so a spec revision diffs exactly one block.
Spec row | Constraint line |
|---|---|
Size within bus width | size <= log2(bus bytes), so size <= 2 on a 32-bit bus |
Align to transfer size | addr % (1 << size) == 0 |
WRAP length 2, 4, 8, 16 | (btype == WRAP) turns on the length set |
No 4KB crossing | page offset plus bytes bounded by 4096 |
Exclusive: power-of-2 bytes up to 128, aligned to the total | gated size and alignment rules |
These are full-AXI burst rules: single-beat AXI4-Lite has no bursts, lengths, or wrap modes. The worked example below is therefore AXI4-style burst legality.
The next listing is not run in this course's checks. It is classes only, with no top module.
// not-run: classes only, no top module
typedef enum bit [1:0] {FIXED = 2'd0, INCR = 2'd1, WRAP = 2'd2} btype_e;
class axi_txn;
rand bit [31:0] addr;
rand bit [2:0] size;
rand bit [4:0] len_p1;
rand btype_e btype;
rand bit [2:0] opc;
rand bit excl;
constraint valid_c {
size <= 3'd2;
addr % (32'd1 << size) == 0;
len_p1 inside {[1:16]};
(btype == WRAP) -> len_p1 inside {2, 4, 8, 16};
// WRAP start aligns to one transfer (the line above), not to the
// whole burst: a start inside the wrap window is what makes it wrap.
(32'(addr[11:0]) + (32'(len_p1) << size)) <= 32'd4096;
!(opc inside {3'h6, 3'h7});
excl -> ((32'(len_p1) << size) inside {1, 2, 4, 8, 16, 32, 64, 128});
excl -> (addr % (32'(len_p1) << size) == 0);
}
endclass
class axi_corner_txn extends axi_txn;
constraint corner_bias_c {
soft addr dist { [32'h0000_0000:32'h0000_0FFF] :/ 3,
[32'hFFFF_F000:32'hFFFF_FFFF] :/ 3,
[32'h0000_1000:32'hFFFF_EFFF] :/ 4 };
soft len_p1 dist { 16 := 3, [1:15] :/ 7 };
}
endclassEvery shift widens before shifting: a 5-bit length shifted in 5 bits truncates, so the cast to 32 bits comes first. Corner pressure stays out of valid_c in its own soft block over whole variables, never over a part select.
Constrain in natural units such as beats and bytes, and convert wire encodings at the driver.
Legal, likely, and measured stay separate: valid_c, soft bias or policy, and a monitor covergroup.
A procedural re-check of each rule at bring-up catches flipped relationals in seconds.
The next listing is not run in this course's checks. It is classes only, with no top module.
// not-run: classes only, no top module
typedef enum bit [1:0] {ERR_NONE, ERR_CRC, ERR_LEN} ekind_e;
class eth_txn;
rand bit [13:0] length;
rand bit [13:0] actual_len;
bit [31:0] crc; // computed in post_randomize, so not rand
rand bit inj;
rand ekind_e kind;
bit [31:0] payload_hash;
int unsigned err_pct = 0;
constraint rate_c {
inj dist { 1 := err_pct, 0 := (100 - err_pct) };
(inj == 0) -> kind == ERR_NONE;
(inj == 1) -> kind != ERR_NONE;
}
constraint len_ok_c {
(kind != ERR_LEN) -> (length == actual_len);
actual_len inside {[64:1518]};
}
constraint len_bad_c {
(kind == ERR_LEN) -> (length != actual_len && length inside {[64:1518]});
}
function void post_randomize();
crc = compute_crc(actual_len);
payload_hash = compute_crc(actual_len);
if (kind == ERR_CRC) crc = crc ^ 32'h0000_0001;
endfunction
function bit [31:0] compute_crc(bit [13:0] n);
return {18'h0, n} * 32'h9E37_79B9;
endfunction
endclass
class eth_mon_txn;
bit dut_flagged;
bit [31:0] payload_hash;
endclassField violations use gated constraints so the frame is wrong in exactly one dimension and plausible elsewhere. Computed-field violations corrupt the golden value in post_randomize with a single-bit flip. Flags travel in the transaction so the scoreboard expects rejection instead of excusing anything.
One flipped bit targets the CRC checker precisely. Garbage may collide with a valid code or trip unrelated decode paths.
A real failure of the DUT check, counted on the negative path. Injected flags demand rejection, never exemption.
A legality checker and a CRC compare stand in for the DUT's receive check. The testbench sends one clean frame, one legal WRAP that starts mid-window, and two frames each wrong in one dimension, and compares the checker's flag with the expected verdict.
module tb_legal_violation;
reg [31:0] addr;
reg [2:0] size;
reg [4:0] len_p1;
reg [1:0] btype;
reg [31:0] off;
reg [31:0] beats;
reg ok;
reg [31:0] golden;
reg expect_reject;
reg dut_flag;
integer problems;
task automatic check_legal;
begin
off = addr[11:0];
beats = len_p1;
ok = 1;
if (size > 2) ok = 0;
if ((addr % (32'd1 << size)) != 0) ok = 0;
if (len_p1 < 1 || len_p1 > 16) ok = 0;
if (btype == 2 && !(len_p1 == 2 || len_p1 == 4 || len_p1 == 8 || len_p1 == 16)) ok = 0;
if ((off + (beats << size)) > 4096) ok = 0;
end
endtask
// Receiver stand-in: flags an illegal shape or a CRC that misses the golden value.
task automatic receive(input [31:0] crc_in);
begin
check_legal;
dut_flag = !ok || (crc_in != golden);
if (dut_flag !== expect_reject) problems = problems + 1;
end
endtask
initial begin
problems = 0;
golden = 32'd100 * 32'h9E37_79B9;
// clean INCR frame
addr = 32'h1000; size = 2; len_p1 = 4; btype = 1;
expect_reject = 0; receive(golden);
// legal WRAP that starts mid-window, so it really wraps
addr = 32'h2004; size = 2; len_p1 = 4; btype = 2;
expect_reject = 0; receive(golden);
// wrong in one dimension: WRAP length 3
addr = 32'h2000; size = 2; len_p1 = 3; btype = 2;
expect_reject = 1; receive(golden);
// wrong in the other dimension: legal shape, one CRC bit flipped
addr = 32'h1000; size = 2; len_p1 = 4; btype = 1;
expect_reject = 1; receive(golden ^ 32'h0000_0001);
if (problems == 0) $display("PASS: clean and mid-window WRAP accepted, bad length and bad CRC rejected");
else $fatal(1, "FAIL: found %0d unexpected verdicts", problems);
$finish;
end
endmodule