Learn VHDL syntax
Objectives:
signal
, variable
, constant
, or port
) must have its type defined when it is declared.bit
type.Though VHDL has a limited number of “buld-in” data types, libraries in VHDL extend this number.
Scalar data types in VHDL represent single values. These includes:
BIT
std_logic
architecture Behavioral of mux is
signal A, B, SEL, Z : bit;
begin
Z <= A when (SEL = '1') else B;
end Behavioral;
A
,B
, SEL
, and Z
declared with the bit data type. It executes a simple multiplexer behavior.
Boolean
Both the data type declarations are taken from library STD
and the package Standard
. These are predefined and implicit for every VHDL model and, therefore, no explicit library declaration is ever required.
std_ulogic
std_ulogic
was developed from the Multi-Value Logic (MVD) system. It provides a detailed hardware modeling option
The u
in std_ulogic
means unresolved.
This indicates that a function, at some point after the initial compilation, returns to a resolve state.
std_ulogic
takes values as shown in the example here:
U
means uninitializedX
means forcing Unknown0
means forcing Zero1
means forcing OneZ
means high impedanceW
weak unknownL
weak zeroH
weak one-
don’t care conditionIt supports different signal strengths, don’t care conditions, and tristate drivers.
It is defined in the package std_logic_1164
of IEEE
.
std_ulogic
std_logic
is the resolved form of std_ulogic
and is more commonly used.
It takes the same nine values as std_ulogic
.
The table shown here is called the resolution table used for the std_logic
data type. This table resolves the question of which value is to be assigned when multiple values are assigned to the same signal.
Both the std_logic
and std_ulogic
types are actually enumerated types and their values must always be capitalized
Both the std_logic
and std_ulogic
data types have same set of values. The difference is implementation.
If a user wants to drive two or more signals to a common output
if this common output has the std_ulogic
data type, then this issue throws an error and will not be resolved. std_ulogic
does provide a built-in means of error checking for inadvertent multiple drivers.
in the case of the std_logic
type, it provides some form of a resolution function for the multiple drivers issue.
architecture rtl of example is
signal OUT_1: std_ulogic;
signal A, B, C, RES_OUT: std_logic;
begin
OUT_1 <= A;
OUT_1 <= B;
OUT_1 <= C;
RES_OUT <= A;
RES_OUT <= B;
RES_OUT <= C;
end architecture;
For example, the OUT_1 signal will give an error here, whereas the RES_OUT signal with the std_logic
type will make it through compile but will not often make it through implementation due to multiple drivers.
How do we resolve the multiple drivers issue?
It can be resolved by using the tristate buffer modeling technique.
std logic
typeAtthe board level, these signals are combined via open-drain or open-collector outputs-in which case, std_logic
weak high (H) and weak low (1) could be used to model
signal A, B, C, RES_OUT: std_logic;
RES_OUT <= A when EN0 = '1' else 'Z';
RES_OUT <= B when EN1 = '1' else 'Z';
RES_OUT <= C when EN2 = '1' else 'Z';
The syntax and an example of the type integer is shown here
Syntax
type integer is range ...
Example
signal A: integer range 0 to 7;
signal B: integer range 15 downto 0;
Syntax
type real is range ...
Example
type CAPACITY is range -25.0 to 25.0;
signal SIG_1: CAPACITY := 3.0;
+, -, *, /, mod, rem
) can be performed on integer
and real
Since VHDL is a strongly typed language by its nature, assigning one type to another is illegal. Performing this change requires a conversion mechanism.
The conversion processes in VHDL are:
std_logic_vector
=> signed
and unsigned
types.std_logic_vector
and signed
or unsigned
can be used as long as the original and destination signals have the same bit width.For example:
signal ex1 : std_logic_vector(3 downto 0);
signal ex2 : signed(3 downto 0);
signal ex3 : unsigned(3 downto 0);
ex2 <= signed(ex1);
ex3 <= unsigned(ex1);
ex1 <= std_logic_vector(ex2);
ex2 <= signed(ex1)
The first example type casts a signal of std_logic_vector
type (ex1) to signed
type and stores it in ex2 which is of signed
type as well.ex3 <= unsigned(ex1)
Similarly, the second example type casts a signal of std_logic_vector
type (ex1) to unsigned
typeex1 <= std_logic_vector(ex2)
Third example type casts a signal of singed type (ex2) to std_logic_vector
type.signed
and unsigned
=> integer
.integer
to signed/unsigned
includes a specification of the intended bit width.signal ex1: signed(3 downto 0);
signal ex2: integer;
ex1 <= to_signed (ex2, ex1'length);
ex2 <= to_integer(ex1);
ex1 <= to_signed (ex2, ex1'length)
The first example here converts an integer (ex2) to signed type using to_signed
conversion function, available in numeric_std
package and stores the result in ex1
which is of singed
type.ex2 <= to_integer(ex1)
Similarly, the to_integer
conversion function converts a signed
type to integer
type in the second example.Since VHDL is a strongly typed language by its nature, assigning one type to another is illegal. Performing this change requires a conversion mechanism.
The conversion processes in VHDL are:
std_logic_vector
=> signed
and unsigned
types.std_logic_vector
and signed
or unsigned
can be used as long as the original and destination signals have the same bit width.For example:
signal ex1 : std_logic_vector(3 downto 0);
signal ex2 : signed(3 downto 0);
signal ex3 : unsigned(3 downto 0);
ex2 <= signed(ex1);
ex3 <= unsigned(ex1);
ex1 <= std_logic_vector(ex2);
ex2 <= signed(ex1)
The first example type casts a signal of std_logic_vector
type (ex1) to signed
type and stores it in ex2 which is of signed
type as well.ex3 <= unsigned(ex1)
Similarly, the second example type casts a signal of std_logic_vector
type (ex1) to unsigned
typeex1 <= std_logic_vector(ex2)
Third example type casts a signal of singed type (ex2) to std_logic_vector
type.signed
and unsigned
=> integer
.integer
to signed/unsigned
includes a specification of the intended bit width.signal ex1: signed(3 downto 0);
signal ex2: integer;
ex1 <= to_signed (ex2, ex1'length);
ex2 <= to_integer(ex1);
ex1 <= to_signed (ex2, ex1'length)
The first example here converts an integer (ex2) to signed type using to_signed
conversion function, available in numeric_std
package and stores the result in ex1
which is of singed
type.ex2 <= to_integer(ex1)
Similarly, the to_integer
conversion function converts a signed
type to integer
type in the second example.Common conversions required in VHDL are
std_logic_vector
to integer
integer
to std_logic_vector
To convert from std logic_vector to integer:
integer_value <= to_integer( unsigned(slv_value));
To convert from integer to std_logic_vector:
slv_value <= std_logic_vector(to_unsigned( integer_value, n ));
integer
, real
, bit
, etc.type mem_array is array (integer range 0 to 1023) of std_logic_vector(15 downto 0);
The example shown here creates a new type called mem_array
. Once created, this mem array becomes a full-blown type that a new signal or variable may be defined as. In this example, the created type is an array of std_logic_vector
with length of 16 bits. The array has 1024 elements.
subtype <new subtype name> is <type or subtype name>;
subtype ROM_MEMORY_RANGE is integer range 0 to 255;
The syntax always has a pre-defined type as subtype only refines the use of a previously defined type or subtype by further limiting its scope.
The example shown here creats a subtype ROM_MEMORY_RANGE
that limits the integer range (by default, a minimum of 32 bits) to 8 bits.
type character is (nul, sol, stx, ...);
constant MY_CHAR : character := 'Q';
The constant MY_CHAR
is of the character type and has been assigned a value equal to ‘Q.
postive range<>
type string is array (positive range <>) of character;
constant msg: string (1 to 10) := "setup time";
A constant msg
of string type with the range equal to ten characters. It has been assigned a value of “setup time”.
mass
, length
, time
etc.time
is the only pre-defined physical typetype time is range -2147483647 to 2147483647
units
fs;
ps = 1000 fs;
ns = 1000 ps;
us = 1000 ns;
ms = 1000 us;
...
end units;
time
, defined in IEEE, is shown onscreen with its range.constant TPD : time := 3 ns;
...
Z <= A after TPD;
In the example here, a constant TPD
is of the type time and has been initialized with a 3ns value.
The syntax for this enumerated type is for this enumerated type is given onscreen:
type <new type name> is (<list of items>);
type MY STATE is (RST, LOAD, FETCH, STORE, SHIFT);
...
signal STATE, NEXT STATE: MY STATE;
...
case (STATE) is
when LOAD =>
if COND_A and COND B then
NEXT STATE <= FETCH;
else
NEXT_STATE <= STORE;
...
In this example,
MY_STATE
is of the enumerated typeDifferent synthesis tools may apply a different encoding scheme based on technology-specific optimization algorithms or other proprietary factors.
type rx_states is (IDLE, START, DATA, PARITY, STOP);
type tx_states is (IDLE, START, DATA, PARITY, STOP);
signal rx_states: rx states := IDLE;
signal tx_states: tx states := IDLE;
Consider an example of a UART transmitter and receiver. Here, rx_states
and tx_states
are of the enumerated type.
For ease of understanding, the states list in rx_states
can be considered as rx_states.IDLE, rx_States.START etc. Similarly, the states in tx_states
can be considered as tx_states.IDLE, tx_states.START, etc