VHDL Math Tricks
Introduction
VHDL is a strongly typed language. Success in VHDL depends on understanding the types and overloaded operators provided by the standard and numeric packages.
These pages gives a short tutorial on:
Common VHDL Types
TYPE |
Value |
Origin |
std_ulogic |
'U', 'X', '0', '1', 'Z', 'W', 'L', 'H', '-' |
std_logic_1164 |
std_ulogic_vector |
array of std_ulogic |
std_logic_1164 |
std_logic |
resolved std_ulogic |
std_logic_1164 |
std_logic_vector |
array of std_logic |
std_logic_1164 |
unsigned |
array of std_logic |
numeric_std, std_logic_arith |
signed |
array of std_logic |
numeric_std, std_logic_arith |
boolean |
true, false |
standard |
character |
191 / 256 characters |
standard |
string |
array of character |
standard |
integer |
-(2^31 -1) to (2^31 - 1) |
standard |
real |
-1.0E38 to 1.0E38 |
standard |
time |
1 fs to 1 hr |
standard |
|
Packages for Numeric Operations
- numeric_std -- IEEE standard
- Defines types signed, unsigned
- Defines arithmetic, comparison, and logic operators for these types
- std_logic_arith -- Synopsys, a defacto industry standard
- Defines types signed, unsigned
- Defines arithmetic, and comparison operators for these types
- std_logic_unsigned -- Synopsys, a defacto industry standard
- Defines arithmetic and comparison operators for std_logic_vector
Recommendation:
Use numeric_std for new designs
Ok to use std_logic_unsigned with numeric_std |
Using IEEE Numeric_Std
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
|
Using Synopsys Std_Logic_Arith
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
|
Use numeric_std or std_logic_arith, but never both.
Recommendation, if you use Synopsys Packages:
Use std_logic_arith for numeric operations
Use std_logic_unsigned only for counters and testbenches
Don't use the package std_logic_signed. |
Recommendation:
Use numeric_std for new designs
|
Unsigned and Signed Types
- Used to represent numeric values:
TYPE |
Value |
Notes |
unsigned |
0 to 2N - 1 |
|
signed |
- 2(N-1) to 2(N-1) - 1 |
2's Complement number |
- Usage similar to std_logic_vector:
signal A_unsigned : unsigned(3 downto 0);
signal B_signed : signed (3 downto 0);
signal C_slv : std_logic_vector (3 downto 0);
. . .
A_unsigned <= "1111"; -- = 15 decimal
B_signed <= "1111"; -- = -1 decimal
C_slv <= "1111"; -- = 15 decimal only if using std_logic_unsigned |
- Type definitions identical to std_logic_vector
type UNSIGNED is array (natural range <>) of std_logic;
type SIGNED is array (natural range <>) of std_logic; |
- How are the types distinguished from each other?
- How do these generate unsigned and signed arithmetic?
- For each operator, a unique function is called
function "+" (L, R: signed) return signed;
function "+" (L, R: unsigned) return unsigned; |
- This feature is called Operator Overloading:
- An operator symbol or subprogram name can be used more than once as long as calls are differentiable.
Overloading Basics
- Simplified view of overloading provided by VHDL packages
Operator | Left | Right | Result |
Logic | TypeA | TypeA | TypeA |
Numeric | Array | Array | Array1 |
| Array | Integer | Array1 |
| Integer | Array | Array1 |
Notes: |
Array = unsigned, signed, std_logic_vector2 |
TypeA = boolean, std_logic, std_ulogic, bit_vector |
std_logic_vector, std_ulogic_vector, |
signed3, unsigned3 |
Array and TypeA types used in an expression must be the same. |
1) for comparison operators the result is boolean |
2) only for std_logic_unsigned. |
3) only for numeric_std and not std_logic_arith |
- For a detailed view of VHDL's overloading, get the VHDL Types and Operators Quick Reference card at: http://www.SynthWorks.com/papers
Overloading Examples
Signal A_uv, B_uv, C_uv, D_uv, E_uv : unsigned(7 downto 0);
Signal R_sv, S_sv, T_sv, U_sv, V_sv : signed(7 downto 0);
Signal J_slv, K_slv, L_slv : std_logic_vector(7 downto 0);
signal Y_sv : signed(8 downto 0);
...
-- Permitted
A_uv <= B_uv + C_uv ; -- Unsigned + Unsigned = Unsigned
D_uv <= B_uv + 1 ; -- Unsigned + Integer = Unsigned
E_uv <= 1 + C_uv; -- Integer + Unsigned = Unsigned
R_sv <= S_sv + T_sv ; -- Signed + Signed = Signed
U_sv <= S_sv + 1 ; -- Signed + Integer = Signed
V_sv <= 1 + T_sv; -- Integer + Signed = Signed
J_slv <= K_slv + L_slv ; -- if using std_logic_unsigned
-- Illegal Cannot mix different array types
-- Solution presented later in type conversions
-- Y_sv <= A_uv - B_uv ; -- want signed result |
Strong Typing Implications
- Size and type of target (left) = size and type of expression (right)
- Each operation returns a result that has a specific size based on rules of the operation. The table below summarizes these rules.
Operation Size of Y = Size of Expression
Y <= "10101010"; number of digits in literal
Y <= X"AA"; 4 * (number of digits)
Y <= A; A'Length = Length of array A
Y <= A and B; A'Length = B'Length
W <= A > B; Boolean
Y <= A + B; Maximum (A'Length, B'Length)
Y <= A + 10; A'Length
V <= A * B; A'Length + B'Length |
Some think VHDL is difficult because of strong typing
Master the above simple rules and it is easy
|
Strong Typing Implications
signal A8, B8, Result8 : unsigned(7 downto 0);
signal Result9 : unsigned(8 downto 0);
signal Result7 : unsigned(6 downto 0);
...
-- Simple Addition, no carry out
Result8 <= A8 + B8;
-- Carry Out in result
Result9 <= ('0' & A8) + ('0' & B8);
-- For smaller result, slice input arrays
Result7 <= A8(6 downto 0) + B8(6 downto 0); |
Strong Typing = Strong Error Checking Built into the Compiler
This means less debugging.
Without VHDL, you better have a good testbench and lots of time to catch your errors.
|
Type Conversions
- VHDL is dependent on overloaded operators and conversions
- What conversion functions are needed?
- Signed & Unsigned (elements) <=> Std_Logic
- Signed & Unsigned <=> Std_Logic_Vector
- Signed & Unsigned <=> Integer
- Std_Logic_vector <=> Integer
- VHDL Built-In Conversions
- Automatic Type Conversion
- Conversion by Type Casting
- Conversion functions located in Numeric_Std
Automatic Type Conversion: Unsigned, Signed <=> Std_Logic
- Two types convert automatically when both are subtypes of the same type
subtype std_logic is resolved std_ulogic; |
- Converting between std_ulogic and std_logic is automatic
- Elements of Signed, Unsigned, and std_logic_vector = std_logic
- Elements of these types convert automatically to std_ulogic or std_logic
Legal
Assignments
|
A_sl <= J_uv(0);
B_sul <= K_sv(7);
L_uv(0) <= C_sl;
M_slv(2) <= N_sv(2); |
Implication:
|
Y_sl <= A_sl and B_sul and
J_uv(2) and K_sv(7) and M_slv(2); |
Type Casting: Unsigned, Signed <=> Std_Logic_Vector
- Use type casting to convert equal sized arrays when:
- Elements have a common base type (i.e. std_logic)
- Indices have a common base type (i.e. Integer)
- Unsigned, Signed <=> Std_Logic_Vector
A_slv <= std_logic_vector( B_uv );
C_slv <= std_logic_vector( D_sv );
G_uv <= unsigned( H_slv );
J_sv <= signed( K_slv ); |
- Motivation, Unsigned - Unsigned = Signed?
signal X_uv, Y_uv : unsigned (6 downto 0);
signal Z_sv : signed (7 downto 0);
. . .
Z_sv <= signed('0' & X_uv) -signed('0' & Y_uv); |
Numeric_Std Conversions: Unsigned, Signed <=> Integer
- Converting to and from integer requires a conversion function.
- Unsigned, Signed => Integer
Unsigned_int <= TO_INTEGER ( A_uv );
Signed_int <= TO_INTEGER ( B_sv ); |
- Integer => Unsigned, Signed
C_uv <= TO_UNSIGNED ( Unsigned_int, 8 );
D_sv <= TO_SIGNED ( Signed_int, 8 ); |
Array
width = 8
|
- Motivation (indexing an array of an array):
Data_slv <= ROM( TO_INTEGER( Addr_uv) ); |
signal A_uv, C_uv : unsigned (7 downto 0);
signal Unsigned_int : integer range 0 to 255 ;
signal B_sv, D_sv : signed( 7 downto 0) ;
signal Signed_int : integer range -128 to 127; |
Std_Logic_Arith Conversions: Unsigned, Signed <=> Integer
- Converting to and from integer requires a conversion function.
- Unsigned, Signed => Integer
Unsigned_int <= Conv_INTEGER ( A_uv );
Signed_int <= Conv_INTEGER ( B_sv ); |
- Integer => Unsigned, Signed
C_uv <= Conv_UNSIGNED ( Unsigned_int, 8 );
D_sv <= Conv_SIGNED ( Signed_int, 8 );
Array width = 8 |
- Motivation (indexing an array of an array):
Data_slv <= ROM( Conv_INTEGER( Addr_uv) ); |
signal A_uv, C_uv : unsigned (7 downto 0);
signal Unsigned_int : integer range 0 to 255;
signal B_sv, D_sv : signed( 7 downto 0);
signal Signed_int : integer range -128 to 127; |
Std_Logic_Vector <=> Integer
Converting between std_logic_vector and integer is a two step process:
Unsigned_int <= to_integer( unsigned( A_slv ));
Signed_int <= to_integer( signed( B_slv ));
.Numeric_Std: Std_Logic_Vector => Integer
.
Numeric_Std: Integer => Std_Logic_Vector
C_slv <= std_logic_vector( to_unsigned( Unsigned_int, 8 ));
D_slv <= std_logic_vector( to_signed( Signed_int, 8 ));
signal A_slv, C_slv : std_logic_vector (7 downto 0) ;
signal Unsigned_int : integer range 0 to 255 ;
signal B_slv, D_slv : std_logic_vector( 7 downto 0) ;
signal Signed_int : integer range -128 to 127;
Ambiguous Expressions
.
An expression / statement is ambiguous if more than one operator
symbol or subprogram can match its arguments.
.
Std_Logic_Arith defines the following two functions:
function "+" (L, R: SIGNED) return SIGNED;
function "+" (L: SIGNED; R: UNSIGNED) return SIGNED;
.
The following expression is ambiguous and an error:
Z_sv <= A_sv + "1010" ; Is "1010" Signed or Unsigned
"1010" = -6 or 10
.
Issues typically only arise when using literals.
.
How do we solve this problem?
Std_Logic_Arith: Ambiguous Expressions
.
VHDL type qualifier (type_name') is a mechanism that specifies the type
of an operand or return value of a subprogram (or operator).
Z_sv <= A_sv + signed'("1010") ;
-- Z_sv <= A_sv + signed("1010") ;
.Leaving out the ' is an error:
Effects all numeric
operators in
std_logic_arith
.
Without ', it is type casting. Use type casting for:
Z_sv <= A_sv + signed(B_slv) ;
.
Recommended solution, use integer:
Z_sv <= A_sv - 6 ;
Addition Operators
Addition Operators: + -
.
Arrays with Arrays:
Add_uv <= A_uv + B_uv ;
Sub_uv <= C_uv - D_uv ;
Size of result =
Size of largest array operand
Size of Add = maximum(A, B)
Shorter array gets extended.
.
Arrays with Integers:
Inc_uv <= Base_uv + 1 ;
Y_uv <= A_uv + 45 ;
Caution: Integers must fit into an
array the same size as the result.
Extra MSB digits are lost
A must be at least 6 bits
By convention the left most bit is the MSB
Use Integers with Care
.
Synthesis tools create a 32-bit wide resources for unconstrained integers
signal Y_int, A_int, B_int : integer ;
. . .
Y_int <= A_int + B_int ;
.
Do not use unconstrained integers for synthesis
.
Specify a range with integers:
signal A_int, B_int: integer range -8 to 7;
signal Y_int : integer range -16 to 15 ;
. . .
Y_int <= A_int + B_int ;
.
Recommendation: Use integers only as constants or literals
Y_uv <= A_uv + 17 ;
Comparison Operators
Comparison Operators: = /= > >= < <=
.
Comparison operators return type boolean
.
Std_Logic is our basic type for design.
.
How do we convert from boolean to std_logic?
.
AGeB <= '1' when (A_uv >= B_uv) else '0';
AEq15 <= '1' when (A_uv = "1111" ) else '0';
Arrays with Arrays:
.
Arrays with Integers (special part of arithmetic packages):
DEq15 <= '1' when (D_uv = 15 ) else '0';
Input arrays are extended to be the same lengthResult = Boolean
Multiplication and Division
Multiplication Operators: * / mod rem
.
Array Multiplication
signal A_uv, B_uv : unsigned( 7 downto 0) ;
signal Z_uv : unsigned(15 downto 0) ;
. . .
Z_uv <= A_uv * B_uv; Size of result =
Sum of the two input arrays
.
Array with Integer (only numeric_std)
Z_uv <= A_uv * 2 ;
Note: "/ mod rem" not well supported by synthesis tools.
Size of result =
2 * size of array input
Adder with Carry Out SynthWorks
Unsigned Algorithm: Unsigned Code:
-------------------
'0', A(3:0)
+ '0', B(3:0)
-------------------
CarryOut, Result(3:0)
Y5 <=
('0' & A) + ('0' & B);
Y <= Y5(3 downto 0) ;
Co <= Y5(4) ;
signal A, B, Y : unsigned(3 downto 0);
signal Y5 : unsigned(4 downto 0) ;
signal Co : std_logic ;
Adder with Carry In
signal A, B, Y : unsigned(3 downto 0);
signal Y5 : unsigned(4 downto 0) ;
signal CarryIn : std_logic ;
Desired Result:
Algorithm
A(3:0) + B(3:0) + CarryIn
-------------------
A(3:0), '1'
+ B(3:0), CarryIn
-------------------
Result(4:1), Unused
Example: Carry = 0
Carry = 1
--------
0010, 1
0001, 0
--------
0011, 1
--------
0010, 1
0001, 1
--------
0100, 0
Result
Code:
Y5 <= (A & '1') + (B & CarryIn);
Y <= Y5(4 downto 1) ;
ALU Functions
.
ALU1: .
Three implementations
OpSel Function
00 A + B
01 C + D
10 E + F
11 G + H
.
Tool Driven Resource Sharing
.
Code Driven Resource Sharing
.
Defeating Resource Sharing
.
Since OpSel can select only one addition at a time, the operators are mutually exclusive.
Possible Solutions to ALU 1
As Specified:
Optimal results:
A
A
CB
E
G
D
Z
OpSel
i0
i1
o
sel
i2
i3
i0
i1
o
sel
i2
i3
Z
E
F
B
D
G
F
OpSel
H
H
OpSel
.
This transformation of operators is called Resource Sharing
i0
i1 O
sel
i2
i3
ALU 1: Tool Driven
ToolDrvnProc : process (OpSel,A,B,C,D,E,F,G,H)
begin
case OpSel is
when "00" => Z <= A + B ;
when "01" => Z <= C + D ;
when "10" => Z <= E + F ;
when "11" => Z <= G + H ;
when others => Z <= (others => 'X') ;
end case ;
end process ; -- ToolDrvnProc
.
Important: to ensure resource sharing, operators must be coded in the
same process, and same code (case or if) structure.
.
Any potential issues with this?
ALU 1: Code Driven
X <= Mux4(OpSel, A, C, E, G) ;
Y <= Mux4(OpSel, B, D, F, H) ;
Z <= X + Y ;
.
Best Synthesis, use for:
.
Sharing arithmetic operators
.
Sharing comparison operators
.
Sharing complex function calls
.
Resource sharing often is not possible when using third party
arithmetic logic.
Lewis
Copyright © 2003 SynthWorks Design Inc. All Rights Reserved. MAPLD 2003 P29
SynthWorks
ALU 1: Defeating Resource Sharing *
.
Bad Code will defeat Resource Sharing.
BadAluProc: process (OpSel, A, B, C, D, E, F, G, H)
begin
if (OpSel = "00") then Z <= A + B; end if;
if (OpSel = "01") then Z <= C + D; end if;
if (OpSel = "10") then Z <= E + F; end if;
if (OpSel = "11") then Z <= G + H; end if;
end process ;
Uses "end if", rather than "elsif"
.
* Not Recommended,
synthesis tool may create a separate resource for each adder.
Defeating Resource Sharing
.
When does this happen?
case StateReg is
when S1 =>
if (in1 = '1') then
Z <= A + B ;
. . .
end if ;
when S2 =>
if (in2 = '1') then
Z <= C + D ;
. . .
end if ;
. . .
when Sn =>
. . .
when others =>
.
Separate statemachines and
resources
Statemach : process(...)
begin
-- generate function
-- select logic (OpSel)
end process ;
Resources : process(...)
begin
-- code:
-- arithmetic operators
-- comparison operators
end process ;
|
|