Thesis:
Xilinx XST reverses the direction of vectors after concatenating these.
I have a SATAController and a PicoBlaze soft core CPU. This CPU uses a register interface + cross-clocking to read/write test data. The CPU writes to six 8-bit registers, which store a 24-bit offset and a 24-bit length field for a read request. These address and length fields are expanded to 48 bits (keyword: LBA-48 addressing mode).
How does it look like?
-- register values after cross clocking with double FFs
signal sync_Offset : T_SLV_24; -- std_logic_vector(23 downto 0)
signal sync_Length : T_SLV_24;
-- output signals for LBA-48 addressing
signal Address_LB : T_SLV_48; -- std_logic_vector(47 downto 0)
signal BlockCount_LB : T_SLV_48;
-- [...]
begin
-- [...]
-- SLL 7 -> granularity for logical blocks is 1 MiB
Address_LB <= resize(sync_Offset & "0000000", Address_LB'length);
BlockCount_LB <= resize(sync_Length & "0000000", BlockCount_LB'length);
Normally one would guess that resize results inAddress_LB == "0000_0000_0000_0000_0" & sync_Offset & "000_0000"
BlockCount_LB == "0000_0000_0000_0000_0" & sync_Length & "000_0000"
But, it's not. After three hours of debugging I found out that the synthesis result is as following:Address_LB = "0000_000" & sync_Offset & "0_0000_0000_0000_0000"
BlockCount_LB = "0000_000" & sync_Length & "0_0000_0000_0000_0000"
So what happens?
The signals sync_*
are descending vectors. After concatenation with operator &
the vector is in ascending direction, which causes resize to align sync_* at the other boundary of the vector.
So I searched the web for this phenomenon and I couldn't find anything. I also searched through std_logic_1164 but the concatenation operator isn't explicitly defined for std_logic(_vector).
So here are my questions:
- Is this a language feature or tool specific?
(I used Xilinx XST for these tests.) - Where is
&
defined? - What are the resulting vector directions after concatenating two:
- equal directed vectors or two
- reverse directed vectors?
Solutions:
- One could use an intermediate signal of known direction.
- One can write a function to force a certain direction. See function
descend(..)
.
Fixed example from above:
Address_LB <= resize(descend(sync_Offset & "0000000"), Address_LB'length);
BlockCount_LB <= resize(descend(sync_Length & "0000000"), BlockCount_LB'length);
Appendix - Function Declarations:
subtype T_SLV_24 is STD_LOGIC_VECTOR(23 downto 0);
subtype T_SLV_48 is STD_LOGIC_VECTOR(47 downto 0);
resize function:
-- Resizes the vector to the specified length. The adjustment is make on
-- on the 'high end of the vector. The 'low index remains as in the argument.
-- If the result vector is larger, the extension uses the provided fill value
-- (default: '0').
-- Use the resize functions of the numeric_std package for value-preserving
-- resizes of the signed and unsigned data types.
function resize(vec : std_logic_vector; length : natural; fill : std_logic := '0') return std_logic_vector is
constant high2b : natural := vec'low+length-1;
constant highcp : natural := imin(vec'high, high2b);
variable res_up : std_logic_vector(vec'low to high2b);
variable res_dn : std_logic_vector(high2b downto vec'low);
begin
if vec'ascending then
res_up := (others => fill);
res_up(vec'low to highcp) := vec(vec'low to highcp);
return res_up;
else
res_dn := (others => fill);
res_dn(highcp downto vec'low) := vec(highcp downto vec'low);
return res_dn;
end if;
end function;
function imin(arg1 : integer; arg2 : integer) return integer is
begin
if arg1 < arg2 then return arg1; end if;
return arg2;
end function;
function descend(vec : std_logic_vector) return std_logic_vector is
variable res : std_logic_vector(vec'high downto vec'low);
begin
res := vec;
return res;
end function;
Edit 1:
Involved packages:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library PoC;
use PoC.utils.all; -- here are resize, imin and descend declared
T_SLV_<n>
stands for "type; std_logic_vector; n bits -> (n-1 downto 0)"
T_SLV_24
and ofAddress_LB
? – Josh