VHDL type conversions

I have been learning VHDL for simulation and synthesis lately. Coming from a Verilog background, one of the biggest differences between Verilog and VHDL is the latter’s type system. VHDL is strongly typed, even compared to C or C++. Every signal has a type, and the compiler will tolerate very little ambiguity between types. It is fair to say you will get nowhere with VHDL until you are comfortable working with them.

Types

VHDL supports many types, but you can get by with a small number of them for logic synthesis, including std_logic, std_logic_vector, signed, unsigned, integer. The types are defined by IEEE 1164, implemented in the std_logic_1164 library. While we’re on the subject, you will also want to use the numeric_std and possibly std_logic_unsigned libraries for helper functions.

Example header:

library IEEE;
use IEEE.std_logic_1164.all;      -- for logic types
use IEEE.numeric_std.all;         -- for signed/unsigned arithmetic
use IEEE.std_logic_unsigned.all;  -- for std_logic_vector arithmetic

I will briefly describe the types:

std_logic is a single multi-valued logic bit. Most often, it will be driven either as ‘0’ (logic 0/false) or ‘1’ (logic 1/true). It supports 1-bit logical operations.

Example declaration and initialization:

signal s0 : std_logic := '1';

std_logic_vector, signed, unsigned are all bit vectors, or fixed size arrays of std_logic bits. They should be thought of primarily as bit vectors, and not numbers in the usual sense. A limited number of arithmetic functions are available, and the operands must be explicitly cast or converted into the same type.

Example declaration and initialization (note double quotes):

signal v1: std_logic_vector(3 downto 0) := "1011";  -- binary 1011
signal u1: unsigned(3 downto 0) := "1011";          -- decimal 11 (unsigned)
signal s1: signed(3 downto 0) := "1011";            -- decimal -5 (signed 2's complement)

std_logic_vector supports bitwise logical operations. It will also support unsigned arithmetic with the ieee.std_logic_unsigned package. signed and unsigned support arithmetic with numeric_std.

The integer is a different animal. It is a number. Unless constrained, it will be in the full range defined by the implementation (currently signed 32-bit). The integer will need to be constrained for the integer to be converted to a smaller number of wires. Integers are more intuitive to work with internally, but all interfaces should be implemented using one of the vector types (preferably std_logic_vector).

Example:

signal i1: integer range 0 to 15; -- 4 bits

Type Conversions

In general, one of the array types can be cast to another type of the same size:

u1 <= unsigned(v1);          -- unsigned cast
s1 <= signed(u1);            -- signed cast
v1 <= std_logic_vector(s1);  -- std_logic_vector cast

A conversion function (to_x) must be used to convert between integers and signed/unsigned types. Two operations, a conversion and a cast, are required to convert between integer and std_logic_vector. Note that unlike the cast, the conversion function takes a second argument, length, to constrain the integer to a fixed number of bits.

Example:

u1 <= to_unsigned(i1, 4);    -- unsigned conversion
s1 <= to_signed(i1, 4);      -- signed conversion
v1 <= std_logic_vector(u1);  -- std_logic_vector cast

i1 <= to_integer(s1);        -- integer conversion

The takeaway from this is that for logic synthesis, I find it useful to think of VHDL bottom-up as a symbolic representation of discrete logic (ports and wires) rather than top-down as a logical implementation of the high-level design.

This entry was posted in Undifferentiated Goo and tagged . Bookmark the permalink.

Comments are closed.