From 9f06dbd5e1f7f3bf92426a3ba9aae251db739441 Mon Sep 17 00:00:00 2001 From: Andreas Kurth Date: Tue, 28 Apr 2020 10:13:22 +0200 Subject: [PATCH 1/7] AXI_BUS_DV: Assert burst on one page --- src/axi_intf.sv | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/axi_intf.sv b/src/axi_intf.sv index c0257f21c..273e9baae 100644 --- a/src/axi_intf.sv +++ b/src/axi_intf.sv @@ -249,6 +249,16 @@ interface AXI_BUS_DV #( assert property (@(posedge clk_i) ( r_valid && ! r_ready |=> $stable(r_last))); assert property (@(posedge clk_i) ( r_valid && ! r_ready |=> $stable(r_user))); assert property (@(posedge clk_i) ( r_valid && ! r_ready |=> r_valid)); + // Address-Channel Assertions: The address of the last beat of a burst must be on the same 4 KiB + // page as the address of the first beat of a burst. Bus is always word-aligned, word < page. + assert property (@(posedge clk_i) aw_valid |-> (aw_burst != axi_pkg::BURST_INCR) || ( + axi_pkg::beat_addr(aw_addr, aw_size, aw_len, aw_burst, 0) >> 12 == // lowest beat address + axi_pkg::beat_addr(aw_addr, aw_size, aw_len, aw_burst, aw_len) >> 12 // highest beat address + )) else $error("AW burst crossing 4 KiB page boundary detected, which is illegal!"); + assert property (@(posedge clk_i) ar_valid |-> (aw_burst != axi_pkg::BURST_INCR) || ( + axi_pkg::beat_addr(ar_addr, ar_size, ar_len, ar_burst, 0) >> 12 == // lowest beat address + axi_pkg::beat_addr(ar_addr, ar_size, ar_len, ar_burst, ar_len) >> 12 // highest beat address + )) else $error("AR burst crossing 4 KiB page boundary detected, which is illegal!"); `endif // pragma translate_on From 0548f74f19c792b3a8309bf16335eb1e3f531477 Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Fri, 2 Sep 2022 16:44:56 +0200 Subject: [PATCH 2/7] axi_test: update rand_master 4KiB boundary check --- src/axi_test.sv | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/src/axi_test.sv b/src/axi_test.sv index eadadcaff..ad42d2174 100644 --- a/src/axi_test.sv +++ b/src/axi_test.sv @@ -905,14 +905,9 @@ package axi_test; addr + len <= mem_region.addr_end; }; assert(rand_success); - if (ax_beat.ax_burst == axi_pkg::BURST_FIXED) begin - if (((addr + 2**ax_beat.ax_size) & PFN_MASK) == (addr & PFN_MASK)) begin - break; - end - end else begin // BURST_INCR - if (((addr + 2**ax_beat.ax_size * (ax_beat.ax_len + 1)) & PFN_MASK) == (addr & PFN_MASK)) begin - break; - end + if (axi_pkg::beat_addr(addr, ax_beat.ax_size, ax_beat.ax_len, ax_beat.ax_burst, 0) >> 12 == + axi_pkg::beat_addr(addr, ax_beat.ax_size, ax_beat.ax_len, ax_beat.ax_burst, ax_beat.ax_len) >> 12) begin + break; end end end else begin @@ -937,14 +932,9 @@ package axi_test; addr + ((len + 1) << size) <= mem_region.addr_end; }; assert(rand_success); - if (ax_beat.ax_burst == axi_pkg::BURST_FIXED) begin - if (((addr + 2**ax_beat.ax_size) & PFN_MASK) == (addr & PFN_MASK)) begin - break; - end - end else begin // BURST_INCR, BURST_WRAP - if (((addr + 2**ax_beat.ax_size * (ax_beat.ax_len + 1)) & PFN_MASK) == (addr & PFN_MASK)) begin - break; - end + if (axi_pkg::beat_addr(addr, ax_beat.ax_size, ax_beat.ax_len, ax_beat.ax_burst, 0) >> 12 == + axi_pkg::beat_addr(addr, ax_beat.ax_size, ax_beat.ax_len, ax_beat.ax_burst, ax_beat.ax_len) >> 12) begin + break; end end end From 67de5356c5348016c90712228b6d249583f9126b Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Fri, 2 Sep 2022 17:27:59 +0200 Subject: [PATCH 3/7] axi_test: update rand_master 4KiB boundary check for atops --- src/axi_test.sv | 105 ++++++++++++++++++++++++++---------------------- 1 file changed, 56 insertions(+), 49 deletions(-) diff --git a/src/axi_test.sv b/src/axi_test.sv index ad42d2174..eecbc91d1 100644 --- a/src/axi_test.sv +++ b/src/axi_test.sv @@ -958,62 +958,69 @@ package axi_test; beat.ax_atop[5:4] = 2'b00; end if (beat.ax_atop[5:4] != 2'b00) begin // ATOP - // Determine `ax_atop`. - if (beat.ax_atop[5:4] == axi_pkg::ATOP_ATOMICSTORE || - beat.ax_atop[5:4] == axi_pkg::ATOP_ATOMICLOAD) begin - // Endianness - beat.ax_atop[3] = $random(); - // Atomic operation - beat.ax_atop[2:0] = $random(); - end else begin // Atomic{Swap,Compare} - beat.ax_atop[3:1] = '0; - beat.ax_atop[0] = $random(); - end - // Determine `ax_size` and `ax_len`. - if (2**beat.ax_size < AXI_STRB_WIDTH) begin - // Transaction does *not* occupy full data bus, so we must send just one beat. [E1.1.3] - beat.ax_len = '0; - end else begin - automatic int unsigned bytes; - if (beat.ax_atop == axi_pkg::ATOP_ATOMICCMP) begin - // Total data transferred in burst can be 2, 4, 8, 16, or 32 B. - automatic int unsigned log_bytes; - rand_success = std::randomize(log_bytes) with { - log_bytes > 0; 2**log_bytes <= 32; - }; assert(rand_success); - bytes = 2**log_bytes; + forever begin + // Determine `ax_atop`. + if (beat.ax_atop[5:4] == axi_pkg::ATOP_ATOMICSTORE || + beat.ax_atop[5:4] == axi_pkg::ATOP_ATOMICLOAD) begin + // Endianness + beat.ax_atop[3] = $random(); + // Atomic operation + beat.ax_atop[2:0] = $random(); + end else begin // Atomic{Swap,Compare} + beat.ax_atop[3:1] = '0; + beat.ax_atop[0] = $random(); + end + // Determine `ax_size` and `ax_len`. + if (2**beat.ax_size < AXI_STRB_WIDTH) begin + // Transaction does *not* occupy full data bus, so we must send just one beat. [E1.1.3] + beat.ax_len = '0; end else begin - // Total data transferred in burst can be 1, 2, 4, or 8 B. - if (AXI_STRB_WIDTH >= 8) begin - bytes = AXI_STRB_WIDTH; - end else begin + automatic int unsigned bytes; + if (beat.ax_atop == axi_pkg::ATOP_ATOMICCMP) begin + // Total data transferred in burst can be 2, 4, 8, 16, or 32 B. automatic int unsigned log_bytes; - rand_success = std::randomize(log_bytes); assert(rand_success); - log_bytes = log_bytes % (4 - $clog2(AXI_STRB_WIDTH)) - $clog2(AXI_STRB_WIDTH); + rand_success = std::randomize(log_bytes) with { + log_bytes > 0; 2**log_bytes <= 32; + }; assert(rand_success); bytes = 2**log_bytes; + end else begin + // Total data transferred in burst can be 1, 2, 4, or 8 B. + if (AXI_STRB_WIDTH >= 8) begin + bytes = AXI_STRB_WIDTH; + end else begin + automatic int unsigned log_bytes; + rand_success = std::randomize(log_bytes); assert(rand_success); + log_bytes = log_bytes % (4 - $clog2(AXI_STRB_WIDTH)) - $clog2(AXI_STRB_WIDTH); + bytes = 2**log_bytes; + end end + beat.ax_len = bytes / AXI_STRB_WIDTH - 1; end - beat.ax_len = bytes / AXI_STRB_WIDTH - 1; - end - // Determine `ax_addr` and `ax_burst`. - if (beat.ax_atop == axi_pkg::ATOP_ATOMICCMP) begin - // The address must be aligned to half the outbound data size. [E1.1.3] - beat.ax_addr = beat.ax_addr & ~((1'b1 << beat.ax_size) - 1); - // If the address is aligned to the total size of outgoing data, the burst type must be - // INCR. Otherwise, it must be WRAP. [E1.1.3] - beat.ax_burst = (beat.ax_addr % ((beat.ax_len+1) * 2**beat.ax_size) == 0) ? - axi_pkg::BURST_INCR : axi_pkg::BURST_WRAP; - // If we are not allowed to emit WRAP bursts, align the address to the total size of - // outgoing data and fall back to INCR. - if (beat.ax_burst == axi_pkg::BURST_WRAP && !AXI_BURST_WRAP) begin - beat.ax_addr -= (beat.ax_addr % ((beat.ax_len+1) * 2**beat.ax_size)); + // Determine `ax_addr` and `ax_burst`. + if (beat.ax_atop == axi_pkg::ATOP_ATOMICCMP) begin + // The address must be aligned to half the outbound data size. [E1.1.3] + beat.ax_addr = beat.ax_addr & ~((1'b1 << beat.ax_size) - 1); + // If the address is aligned to the total size of outgoing data, the burst type must be + // INCR. Otherwise, it must be WRAP. [E1.1.3] + beat.ax_burst = (beat.ax_addr % ((beat.ax_len+1) * 2**beat.ax_size) == 0) ? + axi_pkg::BURST_INCR : axi_pkg::BURST_WRAP; + // If we are not allowed to emit WRAP bursts, align the address to the total size of + // outgoing data and fall back to INCR. + if (beat.ax_burst == axi_pkg::BURST_WRAP && !AXI_BURST_WRAP) begin + beat.ax_addr -= (beat.ax_addr % ((beat.ax_len+1) * 2**beat.ax_size)); + beat.ax_burst = axi_pkg::BURST_INCR; + end + end else begin + // The address must be aligned to the data size. [E1.1.3] + beat.ax_addr = beat.ax_addr & ~((1'b1 << (beat.ax_size+1)) - 1); + // Only INCR allowed. beat.ax_burst = axi_pkg::BURST_INCR; end - end else begin - // The address must be aligned to the data size. [E1.1.3] - beat.ax_addr = beat.ax_addr & ~((1'b1 << (beat.ax_size+1)) - 1); - // Only INCR allowed. - beat.ax_burst = axi_pkg::BURST_INCR; + // Make sure that the burst does not cross a 4KiB boundary. + if (axi_pkg::beat_addr(beat.ax_addr, beat.ax_size, beat.ax_len, beat.ax_burst, 0) >> 12 == + axi_pkg::beat_addr(beat.ax_addr, beat.ax_size, beat.ax_len, beat.ax_burst, beat.ax_len) >> 12) begin + break; + end end end endtask From 352285363dd47a4086557c5ed5e72264cd53b66b Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Fri, 2 Sep 2022 18:25:56 +0200 Subject: [PATCH 4/7] tb_axi_sim_mem: Fix for 4KiB boundary checking --- test/tb_axi_sim_mem.sv | 40 +++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/test/tb_axi_sim_mem.sv b/test/tb_axi_sim_mem.sv index dda3de6a9..76ddb03f8 100644 --- a/test/tb_axi_sim_mem.sv +++ b/test/tb_axi_sim_mem.sv @@ -99,18 +99,25 @@ module tb_axi_sim_mem #( drv.reset_master(); wait (rst_n); // AW + forever begin `ifdef XSIM - // std::randomize(aw_beat) may behave differently to aw_beat.randomize() wrt. limited ranges - // Keeping alternate implementation for XSIM only - rand_success = std::randomize(aw_beat); assert (rand_success); + // std::randomize(aw_beat) may behave differently to aw_beat.randomize() wrt. limited ranges + // Keeping alternate implementation for XSIM only + rand_success = std::randomize(aw_beat); assert (rand_success); `else - rand_success = aw_beat.randomize(); assert (rand_success); + rand_success = aw_beat.randomize(); assert (rand_success); `endif - aw_beat.ax_addr >>= $clog2(StrbWidth); // align address with data width - aw_beat.ax_addr <<= $clog2(StrbWidth); - aw_beat.ax_len = $urandom(); - aw_beat.ax_size = $clog2(StrbWidth); - aw_beat.ax_burst = axi_pkg::BURST_INCR; + aw_beat.ax_addr >>= $clog2(StrbWidth); // align address with data width + aw_beat.ax_addr <<= $clog2(StrbWidth); + aw_beat.ax_len = $urandom(); + aw_beat.ax_size = $clog2(StrbWidth); + aw_beat.ax_burst = axi_pkg::BURST_INCR; + // Make sure that the burst does not cross a 4KiB boundary. + if (axi_pkg::beat_addr(aw_beat.ax_addr, aw_beat.ax_size, aw_beat.ax_len, aw_beat.ax_burst, 0) >> 12 == + axi_pkg::beat_addr(aw_beat.ax_addr, aw_beat.ax_size, aw_beat.ax_len, aw_beat.ax_burst, aw_beat.ax_len) >> 12) begin + break; + end + end drv.send_aw(aw_beat); // W beats for (int unsigned i = 0; i <= aw_beat.ax_len; i++) begin @@ -132,10 +139,17 @@ module tb_axi_sim_mem #( drv.recv_b(b_beat); assert(b_beat.b_resp == axi_pkg::RESP_OKAY); // AR - ar_beat.ax_addr = aw_beat.ax_addr; - ar_beat.ax_len = aw_beat.ax_len; - ar_beat.ax_size = aw_beat.ax_size; - ar_beat.ax_burst = aw_beat.ax_burst; + forever begin + ar_beat.ax_addr = aw_beat.ax_addr; + ar_beat.ax_len = aw_beat.ax_len; + ar_beat.ax_size = aw_beat.ax_size; + ar_beat.ax_burst = aw_beat.ax_burst; + // Make sure that the burst does not cross a 4KiB boundary. + if (axi_pkg::beat_addr(ar_beat.ax_addr, ar_beat.ax_size, ar_beat.ax_len, ar_beat.ax_burst, 0) >> 12 == + axi_pkg::beat_addr(ar_beat.ax_addr, ar_beat.ax_size, ar_beat.ax_len, ar_beat.ax_burst, ar_beat.ax_len) >> 12) begin + break; + end + end drv.send_ar(ar_beat); // R beats for (int unsigned i = 0; i <= ar_beat.ax_len; i++) begin From e1f5b280786a9b362600391a67ad5ae4b24a2805 Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Fri, 2 Sep 2022 18:25:42 +0200 Subject: [PATCH 5/7] axi_test: fix random_master AR 4KiB boundary checking --- src/axi_test.sv | 55 ++++++++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/src/axi_test.sv b/src/axi_test.sv index eecbc91d1..10caf777b 100644 --- a/src/axi_test.sv +++ b/src/axi_test.sv @@ -1026,30 +1026,37 @@ package axi_test; endtask function void rand_excl_ar(inout ax_beat_t ar_beat); - ar_beat.ax_lock = $random(); - if (ar_beat.ax_lock) begin - automatic logic rand_success; - automatic int unsigned n_bytes; - automatic size_t size; - automatic addr_t addr_mask; - // In an exclusive burst, the number of bytes to be transferred must be a power of 2, i.e., - // 1, 2, 4, 8, 16, 32, 64, or 128 bytes, and the burst length must not exceed 16 transfers. - static int unsigned ul = (AXI_STRB_WIDTH < 8) ? 4 + $clog2(AXI_STRB_WIDTH) : 7; - rand_success = std::randomize(n_bytes) with { - n_bytes >= 1; - n_bytes <= ul; - }; assert(rand_success); - n_bytes = 2**n_bytes; - rand_success = std::randomize(size) with { - size >= 0; - 2**size <= n_bytes; - 2**size <= AXI_STRB_WIDTH; - n_bytes / 2**size <= 16; - }; assert(rand_success); - ar_beat.ax_size = size; - ar_beat.ax_len = n_bytes / 2**size; - // The address must be aligned to the total number of bytes in the burst. - ar_beat.ax_addr = ar_beat.ax_addr & ~(n_bytes-1); + forever begin + ar_beat.ax_lock = $random(); + if (ar_beat.ax_lock) begin + automatic logic rand_success; + automatic int unsigned n_bytes; + automatic size_t size; + automatic addr_t addr_mask; + // In an exclusive burst, the number of bytes to be transferred must be a power of 2, i.e., + // 1, 2, 4, 8, 16, 32, 64, or 128 bytes, and the burst length must not exceed 16 transfers. + static int unsigned ul = (AXI_STRB_WIDTH < 8) ? 4 + $clog2(AXI_STRB_WIDTH) : 7; + rand_success = std::randomize(n_bytes) with { + n_bytes >= 1; + n_bytes <= ul; + }; assert(rand_success); + n_bytes = 2**n_bytes; + rand_success = std::randomize(size) with { + size >= 0; + 2**size <= n_bytes; + 2**size <= AXI_STRB_WIDTH; + n_bytes / 2**size <= 16; + }; assert(rand_success); + ar_beat.ax_size = size; + ar_beat.ax_len = n_bytes / 2**size; + // The address must be aligned to the total number of bytes in the burst. + ar_beat.ax_addr = ar_beat.ax_addr & ~(n_bytes-1); + end + // Make sure that the burst does not cross a 4KiB boundary. + if (axi_pkg::beat_addr(ar_beat.ax_addr, ar_beat.ax_size, ar_beat.ax_len, ar_beat.ax_burst, 0) >> 12 == + axi_pkg::beat_addr(ar_beat.ax_addr, ar_beat.ax_size, ar_beat.ax_len, ar_beat.ax_burst, ar_beat.ax_len) >> 12) begin + break; + end end endfunction From 840b6ce354e74afe34561edf71786a1e5ed57fbf Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Fri, 19 Jul 2024 12:00:30 +0200 Subject: [PATCH 6/7] Re-randomize beat to ensure 4KiB-safe Ax is found --- src/axi_test.sv | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/axi_test.sv b/src/axi_test.sv index 10caf777b..ce8638343 100644 --- a/src/axi_test.sv +++ b/src/axi_test.sv @@ -951,14 +951,14 @@ package axi_test; task rand_atop_burst(inout ax_beat_t beat); automatic logic rand_success; - beat.ax_atop[5:4] = $random(); - if (beat.ax_atop[5:4] != 2'b00 && !AXI_BURST_INCR) begin - // We can emit ATOPs only if INCR bursts are allowed. - $warning("ATOP suppressed because INCR bursts are disabled!"); - beat.ax_atop[5:4] = 2'b00; - end - if (beat.ax_atop[5:4] != 2'b00) begin // ATOP - forever begin + forever begin + beat.ax_atop[5:4] = $random(); + if (beat.ax_atop[5:4] != 2'b00 && !AXI_BURST_INCR) begin + // We can emit ATOPs only if INCR bursts are allowed. + $warning("ATOP suppressed because INCR bursts are disabled!"); + beat.ax_atop[5:4] = 2'b00; + end + if (beat.ax_atop[5:4] != 2'b00) begin // ATOP // Determine `ax_atop`. if (beat.ax_atop[5:4] == axi_pkg::ATOP_ATOMICSTORE || beat.ax_atop[5:4] == axi_pkg::ATOP_ATOMICLOAD) begin @@ -1020,6 +1020,8 @@ package axi_test; if (axi_pkg::beat_addr(beat.ax_addr, beat.ax_size, beat.ax_len, beat.ax_burst, 0) >> 12 == axi_pkg::beat_addr(beat.ax_addr, beat.ax_size, beat.ax_len, beat.ax_burst, beat.ax_len) >> 12) begin break; + end else begin + beat = new_rand_burst(1'b0); end end end @@ -1056,6 +1058,8 @@ package axi_test; if (axi_pkg::beat_addr(ar_beat.ax_addr, ar_beat.ax_size, ar_beat.ax_len, ar_beat.ax_burst, 0) >> 12 == axi_pkg::beat_addr(ar_beat.ax_addr, ar_beat.ax_size, ar_beat.ax_len, ar_beat.ax_burst, ar_beat.ax_len) >> 12) begin break; + end else begin + ar_beat = new_rand_burst(1'b1); end end endfunction From ca4695052484b235a0f0b2106fadaee26cfdd5ef Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Thu, 25 Jul 2024 11:21:54 +0200 Subject: [PATCH 7/7] update CHANGELOG --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4419e29d1..0db60adee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,12 +9,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Added - `axi_sim_mem`: Increase number of request ports, add multiport interface variant. - `axi_bus_compare`: Optionally consider AXI `size` field to only compare used data. +- `AXI_BUS_DV`: Add property checking that bursts do not cross 4KiB page boundaries. ### Fixed - `axi_bus_compare`: Fix mismatch detection. - `axi_to_detailed_mem`: Only respond with `exokay` if `lock` was set on the request. Bump `common_cells` for `mem_to_banks` fix. - `axi_dw_downsizer`: Fix `i_forward_b_beats_queue` underflow. +- `axi_test`: Ensure random requests do not cross 4KiB page boundaries. ## 0.39.3 - 2024-05-08 ### Added