I don’t like magic numbers or redundant variables in the code. They make it unclear and unreadable. Fortunately, VHDL gives many various options to eliminate such parts. One of them are predefined attributes. VHDL delivers many groups of attributes, which are useful in many situations. Some of them work only with signals, other with specific data types etc., but in general, rule of using an attribute is always the same:
object‘attribute
First, very popular, attribute which I am going to focus on, is attribute length. I use it mostly in three situations:
- when a type is defined in another package
- when there is an unconstrained array
- when there is an unconstrained function or procedure.
Length attribute is generally used when the length of the object is not directly given or is unconstrained. Creating unconstrained arrays or functions allows keeping the code flexible, cause it can be adjusted to any length of the object, not just to one. Using length for unconstrained objects is not limited to arrays or functions, but can work also with i.e. ports.
Below I gave some examples to show how to use length attribute.
Type is defined in another package
Package
PACKAGE pkg IS CONSTANT c_TestVec :STD_LOGIC_VECTOR(12-1 DOWNTO 0) := x"100"; TYPE TypeA IS ARRAY (1 TO 3) OF UNSIGNED(16-1 DOWNTO 0); END PACKAGE pkg;
Entity
ENTITY AttLenExtDef IS PORT ( clk : IN STD_LOGIC; rst : IN STD_LOGIC; Aarr : IN TypeA; Barr : IN TypeA; Yarr : OUT TypeA; Avec : IN STD_LOGIC_VECTOR(c_TestVec'LENGTH-1 DOWNTO 0); Yvec : OUT STD_LOGIC_VECTOR(c_TestVec'LENGTH-1 DOWNTO 0) ); END AttLenExtDef;
Type TypeA does not need any parameter. It is already defined in the package.
To define lengths of Avec and Yvec vectors, attribute length was used. Now their lengths are corelated with constant c_TestVec. Change of the constant will change the port.
Architecture
calc : PROCESS(clk) BEGIN IF rising_edge(clk) THEN IF rst = '1' THEN Yarr <= (OTHERS => (OTHERS => '0')); Yvec <= (OTHERS => '0'); ELSE FOR i IN 1 TO TypeA'LENGTH LOOP Yarr(i) <= Aarr(i) + Barr(i); END LOOP; Yvec <= Avec; END IF; END IF; END PROCESS;
Attribute length is used in for loop. One loop assigns values to every vector in the array Yarr. Size of the array is not an issue, it will work for every size.
Unconstrained array
Package
PACKAGE pkg IS TYPE TypeB IS ARRAY (NATURAL RANGE <>) OF UNSIGNED(16-1 DOWNTO 0); END PACKAGE pkg;
Entity
ENTITY AttLenArr IS PORT ( clk : IN STD_LOGIC; rst : IN STD_LOGIC; Aarr : IN TypeB; Barr : IN TypeB; Yarr : OUT TypeB ); END AttLenArr;
TypeB is not fully constrained. Entity AttLenArr does not know the length of the array. It will be constrained one level above, where AttlenArr is instantiated (in my case that is testbench).
Architecture
calc : PROCESS(clk) BEGIN IF rising_edge(clk) THEN IF rst = '1' THEN FOR i IN 1 TO Yarr'LENGTH LOOP Yarr(i) <= (OTHERS => '0'); END LOOP; ELSE FOR i IN 1 TO Yarr'LENGTH LOOP Yarr(i) <= Aarr(i) + Barr(i); END LOOP; END IF; END IF; END PROCESS;
Length can be every size and for every value, that process will do the same job. It is independent on the length of the array.
Testbench with AttLenArr instantiation
SIGNAL Aarr : TypeB(4 DOWNTO 1); SIGNAL Barr : TypeB(4 DOWNTO 1); SIGNAL Yarr : TypeB(4 DOWNTO 1); ... ... ... UUT : AttLenArr PORT MAP ( clk => clk, rst => rst, Aarr => Aarr, Barr => Barr, Yarr => Yarr );
Testbench defines length of the signals and use them as inputs to entity AttLenArr. Change the length in declaration, will cause the change of the signals’ length in AttLenArr entity.
Unconstrained function or procedure
Entity
ENTITY AttLenFun IS PORT ( A : IN UNSIGNED(8-1 DOWNTO 0); B : IN UNSIGNED(8-1 DOWNTO 0); C : IN UNSIGNED(8-1 DOWNTO 0); Y : OUT UNSIGNED(8-1 DOWNTO 0) ); END AttLenFun;
Here package is not used. Entity defines length of all signals.
Architecture
ARCHITECTURE rtl OF AttLenFun IS -- a, b, c - the same length FUNCTION Add3 (a, b, c : UNSIGNED) RETURN UNSIGNED IS VARIABLE y :UNSIGNED(a'LENGTH-1 DOWNTO 0); BEGIN y := a + b + c; RETURN y; END Add3; BEGIN Y <= Add3(A, B, C); END ARCHITECTURE;
In architecture, there is a function, which adds 3 values. Function does not use any constant lengths. It will be synthesised for lengths of the arguments. That kind of function can be used in many places. It should be noted that, just for example purposes, all arguments to the function have the same length.
*** *** ***
All source codes used in that post you can find on gitlab.com.
*** *** ***