04-25-2014 06:50 AM
I want to implement an AXI4 burst capable slave interface. When using the generator from "Create and Package IP" the logic for generating the wrap signal looks like this:
aw_wrap_en <= '1' when (((axi_awaddr AND CONV_STD_LOGIC_VECTOR(aw_wrap_size,C_S_AXI_ADDR_WIDTH)) XOR CONV_STD_LOGIC_VECTOR(aw_wrap_size,C_S_AXI_ADDR_WIDTH)) = low) else '0';
and this is the same as
aw_wrap_en <= '1' when (((axi_awaddr AND CONV_STD_LOGIC_VECTOR(aw_wrap_size,C_S_AXI_ADDR_WIDTH))) = CONV_STD_LOGIC_VECTOR(aw_wrap_size,C_S_AXI_ADDR_WIDTH)) else '0';
which looks strange to me. So that means that expression is true if the address masked by aw_wrap_size equals aw_wrap_size. When looking at the ARM specification for AXI4 (ARM IHI 0022E ID 022613, p. A3-49) it says:
Wrap_Boundary = (INT(Start_Address / (Number_Bytes * Burst_Length)) * (Number_Bytes * Burst_Length)
aw_wrap_size is defined in the template as
aw_wrap_size <= (C_S_AXI_DATA_WIDTH/8 * CONV_INTEGER(S_AXI_AWLEN));
where I would expect it to be dependend on S_AXI_AWSIZE.
But let's assume that C_S_AXI_DATA_WIDTH/8 = 2^S_AXI_AWSIZE it still doesn't make a lot of sense to me:
According to the AXI4 specification this would result in a upper burst wrap boundary of:
Upper_Wrap_Boundary = (Start_Address / (2^S_AXI_AWSIZE * (S_AXI_AWLEN + 1))) * (2^S_AXI_AWSIZE * (S_AXI_AWLEN + 1)) + (2^S_AXI_AWSIZE * (S_AXI_AWLEN + 1)) Upper_Wrap_Boundary = 108
but when I put these numbers into the aw_wrap_en expression it would result in the expression to resolve to true although the wrap boundary wasn't reached.
The upper wrapping boundary has to be a multiple of (2^S_AXI_AWSIZE + (S_AXI_AWLEN + 1)) so I would need an algorithm which checks if the current address is:
address(aligned) rem (2^S_AXI_AWSIZE + (S_AXI_AWLEN + 1)) = 0
So how can this be checked without a divider?
And there are other places in the code I think are confusing:
constant low : std_logic_vector (C_S_AXI_ADDR_WIDTH - 1 downto 0) := "000000";
why not use
constant low : std_logic_vector (C_S_AXI_ADDR_WIDTH - 1 downto 0) := (others => '0');
instead, so that it would work with arbitrary address widths?
And another construct which looks strange to me:
constant ADDR_LSB : integer := (C_S_AXI_DATA_WIDTH/32)+ 1;
as this only works for 16, 32 and 64 bits. This is pseudo code which would work for every 2^N data width:
constant ADDR_LSB : integer := log2(C_S_AXI_DATA_WIDTH/8)
I've attached my testbench if you want to play with the wrap generation logic yourself.
To make a long story short: Is the Xilinx provided implementation correct and are my assumptions wrong? If the Xilinx provided solution is wrong does anyone have a working solution?
05-13-2014 03:18 AM
This problem was confirmed by the Xilinx support and should be fixed in a later version of Vivado. Unfortunately I don't know for which release the error will be fixed.