Repository: VUnit/vunit (language: python)

Issue:
Unaligned accesses to AXI read and write slaves wrongly fail 4kbyte boundary check
When doing unaligned accesses around a 4k boundary, 4k boundary check wrongly fail in AXI read and write slaves.

Example:
**Bus width:** 32 bits
**Start address:** 3841
**Burst length, beats:** 64 beats
**Burst length, bytes:** 255

When doing unaligned write accesses, by in this case starting from 3841, the first byte in the first beat will have it's strobe bit set to zero, i.e not writing that byte, which is expected. 

However, the access is still 64 beats long, as expected, but the `check_4kbyte_boundary` method in `axi_slave_private_pkg` will count the end address of the access from the start address of the burst, not the aligned address.
This will incorrectly assume that the burst ends at address 3841 + 4 * 64 - 1 = 4096, and not at address 4095 (which is what will happen when writing), but the access is still 64 beats long, one byte is just strobed out in the first beat.

A suggested patch here is to count from the starting address which is aligned.
```vhdl
procedure check_4kbyte_boundary(burst : axi_burst_t) is
  variable first_address, last_address : integer;
  variable first_page, last_page : integer;
begin
  first_address := burst.address - (burst.address mod data_size); -- Aligned
  last_address := first_address  + burst.size*burst.length - 1;

  first_page := first_address / 4096;
  last_page := last_address / 4096;

  if first_page /= last_page then
    fail("Crossing 4KByte boundary. First page = "
         & integer'image(first_page) & " (" & to_string(first_address) & "/4096)"
         & ", last page = "
         & integer'image(last_page) & " (" & to_string(last_address) & "/4096)");
  end if;
end procedure;
```


Discussion / hints:

Fix patch:
diff --git a/vunit/vhdl/verification_components/src/axi_slave_private_pkg.vhd b/vunit/vhdl/verification_components/src/axi_slave_private_pkg.vhd
index 64ef4af9c..603984ed9 100644
--- a/vunit/vhdl/verification_components/src/axi_slave_private_pkg.vhd
+++ b/vunit/vhdl/verification_components/src/axi_slave_private_pkg.vhd
@@ -438,7 +438,7 @@ package body axi_slave_private_pkg is
       variable first_page, last_page : integer;
     begin
       first_address := burst.address - (burst.address mod data_size); -- Aligned
-      last_address := burst.address + burst.size*burst.length - 1;
+      last_address := first_address + burst.size*burst.length - 1;
 
       first_page := first_address / 4096;
       last_page := last_address / 4096;


Test patch:
diff --git a/vunit/vhdl/verification_components/test/tb_axi_write_slave.vhd b/vunit/vhdl/verification_components/test/tb_axi_write_slave.vhd
index e99c547ec..1298d822f 100644
--- a/vunit/vhdl/verification_components/test/tb_axi_write_slave.vhd
+++ b/vunit/vhdl/verification_components/test/tb_axi_write_slave.vhd
@@ -353,6 +353,14 @@ begin
       buf := allocate(memory, length(data), permissions => no_access);
       transfer(x"2", buf, log_size, data);
 
+    elsif run("Test unaligned write around 4kbyte boundary") then
+      -- Do one beat write at unaligned address starting around 4kB boundary
+      log_size := log_data_size;
+      buf := allocate(memory, 4096 - 2**log_size + 1, permissions => no_access);
+      random_integer_vector_ptr(rnd, data, 2**log_size , 0, 255);
+      buf := allocate(memory, length(data), permissions => no_access); -- Unaligned address
+      transfer(x"2", buf, log_size, data);
+
     elsif run("Test error on missing tlast fixed") then
       mock(axi_slave_logger, failure);