Reduced markdown conversion of https://www.ics.uci.edu/~jmoorkan/vhdlref/vhdl.html
Each VHDL design unit comprises an entity
declaration and one or
more architecture
s. Each architecture defines a different
implementation or model of a given design unit. The entity definition
defines the inputs to, and outputs from the module, and any generic
parameters used by the different implementations of the module.
entity <name> is
port( <port definition list> ); -- input/output signal ports
generic( <generic list> ); -- optional generic list
end <name>;
Port declaration format: <port name>: <mode> <data type>;
-
in
: can be read but not updated within the module, carrying information into the module. (Anin
-port cannot appear on the left hand side of a signal assignment.) -
out
: can be updated but not read within the module, carrying information out of the module. (Anout
-port cannot appear on the right hand side of a signal assigment.) -
buffer
: likewise carries information out of a module, but can be both updated and read within the module. -
inout
: is bidirectional and can be both read and updated, with multiple update sources possible.
NOTE: A
buffer
is strictly an output port, i.e. can only be driven from within the module, whileinout
is truly bidirectional with drivers both within and external to the module.
entity counter is
port (
Incr, Load, Clock : in std_ulogic;
Carry : out std_ulogic;
Data_Out : buffer std_ulogic_vector(7 downto 0);
Data_In : in std_ulogic_vector(7 downto 0));
end counter;
Generics allow static information to be communicated to a block from its environment for all architectures of a design unit. These include timing information (setup, hold, delay times), part sizes, and other parameters.
entity and_gate is
port(
a,b : in std_ulogic;
c : out std_ulogic);
generic (gate_delay: time := 5ns);
end and_gate;
An architecture
defines one particular implementation of a design unit,
at some desired level of abstraction.
architecture <name> of <entity> is
<declarations>
begin
<concurrent statements>
end;
Declarations include data types, constants, signals, files, components, attributes, subprograms, and other information to be used in the implementation description. Concurrent statements describe a design unit at one or more levels of modeling abstraction, including dataflow, structure, and/or behavior.
- Behavioral Model: No structure or technology implied. Usually written in sequential, procedural style.
- Dataflow Model: All datapaths shown, plus all control signals.
- Structural Model: Interconnection of components.
A VHDL package contains subprograms, constant definitions, and/or type definitions to be used throughout one or more design units. Each package comprises a declaration section, in which the available (i.e. exportable) subprograms, constants, and types are declared, and a package body, in which the subprogram implementations are defined, along with any internally-used constants and types. The declaration section represents the portion of the package that is visible to the user of that package. The actual implementations of subroutines in the package are typically not of interest to the users of those subroutines.
package <name> is
... exported constant declarations
... exported type declarations
... exported subprogram declarations
end <name>;
package ee530 is
constant maxint: integer := 16#ffff#;
type arith_mode_type is (signed, unsigned);
function minimum(constant a, b : in integer) return integer;
end ee530;
package body <name> is
... exported subprogram bodies
... other internally-used declarations
end <name>;
package body ee530 is
function minimum (constant a,b : integer) return integer is
variable c : integer; -- local variable
begin
if a < b then
c := a; -- a is min
else
c := b; -- b is min
end if;
return c; -- return min value
end;
end ee530;
To make all items of a package "visible" to a design unit, precede the
desired design unit with a use
statement:
use <library name>.<package name>.all
A use
statement may precede the declaration of any entity or
architecture which is to utilize items from the package. If the use
statement precedes the entity declaration, the package is also visible
to the architecture.
Compile user-developed packages in your current working library. To make it visible:
use <package name>.all;
Note:
std
andwork
(your current working library) are the two default libraries. The VHDLlibrary
statement is needed to make theieee
library and/or additional libraries visible.
library <lib name>; -- make library visible
use <lib name>.<pkg name>.all; -- make package visible
To make TEXTIO visible: use std.textio.all;
This package contained in the ieee library supports multi-valued logic signals with type declarations and functions. To make visible:
library ieee; -- VHDL Library stmt
use ieee.std_logic_1164.all;
Identifiers in VHDL must begin with a letter, and may comprise any
combination of letters, digits, and underscores. Note that VHDL
internally converts all characters to UPPER CASE
.
Memory1, Adder_Module, Bus_16_Bit
Numeric contants can be defined, and can be of any base (default is decimal). Numbers may include embedded underscores to improve readability.
Format: base#digits#
(base must be a decimal number)
16#9fba# (hexadecimal)
2#1111_1101_1011# (binary)
16#f.1f#E+2 (floating-point, exponent is decimal)
Bit vector constants are are specified as literal strings.
x"ffe" (12-bit hexadecimal value)
o"777" (9-bit octal value)
b"1111_1101_1101" (12-bit binary value)
Expressions in VHDL are similar to those of most high-level languages. Data elements must be of the type, or subtypes of the same base type. Operators include the following:
- Logical:
and
,or
,nand
,nor
,xor
,not
(for boolean or bit ops) - Relational:
=
,/=
,<
,<=
,>
,>=
- Arithmetic:
+
,-
,*
,/
,mod
,rem
,**
(exponential),abs
- Concatenate:
&
a <= b nand c;
d := g1 * g2 / 3;
Bus_16 <= Bus1_8 & Bus2_8;
Each VHDL objects must be classified as being of a specific data type. VHDL includes a number of predefined data types, and allows users to define custom data types as needed.
bit
:0
,1
boolean
:TRUE
,FALSE
integer
:-(231) to +(231 - 1)
(SUN Limit)natural
:0 to integer'high
(subtype of integer)positive
:1 to integer'high
(subtype of integer)character
: ASCII characters (eg. 'A
')time
(include units; eg. 10ns, 20us)
std_ulogic
:U
,X
,1
,0
,Z
,W
,H
,L
,-
std_logic
: resolvedstd_ulogic
valuesX01
: subtype {X
,0
,1
} ofstd_ulogic
X01Z
: subtype {X
,0
,1
,Z
} ofstd_ulogic
UX01
: subtype {U
,X
,0
,1
} ofstd_ulogic
UX01Z
: subtype {U
,X
,0
,1
,Z
} ofstd_ulogic
bit_vector
:array (natural range <>) of bit
string
:array (natural range <>) of char
text
: file ofstring
(From package: ieee.std_logic_1164.all)
std_ulogic_vector
:array (natural range <>) of std_ulogic
std_logic_vector
:array (natural range <>) of std_logic
signal dbus: bit_vector(15 downto 0);
dbus (7 downto 4) <= "0000"; (4-bit slice of dbus)
signal cnt: std_ulogic_vector(1 to 3);
variable message: string(0 to 20);
An enumerated data type can be created by explicitely listing all possible values.
type opcodes is (add, sub, jump, call); -- Type with 4 values
signal instruc : opcodes; -- Signal of this type
...
if instruc = add then -- test for value 'add'
...
Custom data types can include arrays, constrained and unconstrained, and record structures.
- Constrained array: Upper and lower indexes are specified.
- Unconstrained array: Indexes are specified when a signal or variable of that type is declared.
- Subtype: A selected subset of values of a given type. Elements of different subtypes having the same base type may be combined in expressions (elements of different types cannot). Subtypes can be used to detect out-of-range values during simulation.
An alias
defines an alternate name for a signal or part of a signal.
Aliases are often used to refer to selected slices of a bit_vector.
signal instruction: bit_vector(31 downto 0);
alias opcode: bit_vector(6 downto 0) is instruction(31 downto 25);
...
opcode <= "1010101"; -- Set the opcode part of an instruction code
A constant associates a value to a symbol of a given data type. The use of constants may improve the readability of VHDL code and reduce the likelihood of making errors. The declaration syntax is:
constant Vcc : signal := '1'; -- logic 1 constant
constant zero4 : bit_vector(0 to 3) := ('0','0','0','0');
A variable is declared within a blocks, process, procedure, or function, and is updated immediately when an assignment statement is executed. A variable can be of any scalar or aggregate data type, and is utilized primarily in behavioral descriptions. It can optionally be assigned initial values (done only once prior to simulation). The declaration syntax is:
process
variable count : integer := 0;
variable rega : bit_vector(7 downto 0);
begin
...
count := 7; -- assign values to variables
rega := x"01";
...
end;
A signal is an object with a history of values (related to event times, i.e. times at which the signal value changes).
Signals are declared via signal declaration statements or entity port definitions, and may be of any data type. The declaration syntax is:
signal clock : bit;
signal GND : bit := '0';
signal databus : std_ulogic_vector(15 downto 0);
signal addrbus : std_logic_vector(0 to 31);
Each signal has one or more drivers which determine the value and timing of changes to the signal. Each driver is a queue of events which indicate when and to what value a signal is to be changed. Each signal assignment results in the corresponding event queue being modified to schedule the new event.
- signal line x
10ns 0
Driver of
20ns 1
signal x
- Event Values
- Times
NOTE: If no delay is specified, the signal event is scheduled for one infinitessimally-small
delta
delay from the current time. The signal change will occur in the next simulation cycle.
(Assume current time is T)
clock <= not clock after 10ns; -- change at T + 10ns
databus <= mem1 and mem2 after delay; -- change at T + delay
x <= '1'; -- change to '1' at time T + "delta";
Element delay models may be specified as either inertial or transport. Inertial delay is the default, and should be used in most cases.
- Inertial delay: The addition to an event queue of an event scheduled at time T automatically cancels any events in the queue scheduled to occur prior to time T, i.e. any event shorter than the delay time is suppressed.
- Transport delay: Each new event is simply inserted into the event queue, i.e. behavior is that of a delay line. The keyword transport is used to indicate transport delays.
B <= A after 5ns; -- inertial delay
C <= transport A after 5 ns; -- transport delay
5______15 17_________30
A _______| |_| |_____________
____________________
B ___________| |_________ (Inertial Delay)
_______ __________
C ___________| |_| |_________ (Transport Delay)
10 20 22 35
Where there are multiple drivers for one signal, a resolution function must be provided to determine the value to be assigned to the signal from the values supplied by the multiple drivers. This allows simulation of buses with multiple sources/drivers.
NOTE: The
std_logic
andstd_logic_vector
types from the ieee library have predefined resolution functions:
signal data_line: std_logic;
begin
block1:
data_line <= '1'; -- one driver
...
block2:
data_line <= 'Z'; -- 2nd driver
The resolved value is 1
since 1
overrides a Z
(floating)
value. If the two values had been 1
and 0
, the resolved value
would have been X
, indicating an unknown result.
Concurrent statements are included within architecture definitions and within block statements, representing concurrent behavior within the modelled design unit. These statements are executed in an asynchronous manner, with no defined order, modeling the behavior of independent hardware elements within a system.
A signal assignment statement represents a process that assigns values to signals. It has three basic formats.
A <= B;
A <= B when <condition1> else
C when <condition2> else
D when <condition3> else E;
with <expression> select A <=
B when choice1,
C when choice2,
D when choice3,
E when others;
For each of the above, waveforms (time-value pairs) can also be specified.
A <= B after 10ns when condition1 else
C after 12ns when condition2 else
D after 11ns;
-- 4-input multiplexer (Choice is a 2-bit vector)
with Choice select Out <=
In0 after 2ns when "00",
In1 after 2ns when "01",
In2 after 2ns when "10",
In3 after 2ns when "11";
-- 2-to-4 decoder (Y = 4-bit and A = 2-bit vectors)
Y <= "0001" after 2ns when A = "00" else
"0010" after 2ns when A = "01" else
"0100" after 2ns when A = "10" else
"1000" after 2ns;
-- Tri-state driver: (Y is logic4; X is bit_vector)
Y <= '0' after 1ns when En = '1' and X = '0' else
'1' after 1ns when En = '1' and X = '1' else
'Z' after 1ns;
-- A is a 16-bit vector
A <= (others => '0'); -- set all bits of A to '0'
The keyword others
in the last example indicates that all elements
of A not explicitly listed are to be set to 0
.
An independent sequential process represents the behavior of some portion of a design. The body of a process is a list of sequential statements.
label: process (sensitivity list)
<local declarations>
begin
<sequential statements>
end process label;
DFF: process (clock)
begin
if clock = '1' then
Q <= D after 5ns;
QN <= not D after 5ns;
end if;
end process DFF;
The sequential statements in the process are executed in order,
commencing with the beginning of simulation. After the last statement of
a process has been executed, the process is repeated from the first
statement, and continues to repeat until suspended. If the optional
sensitivity list is given, a wait on ...
statement is inserted
after the last sequential statement, causing the process to be suspended
at that point until there is an event on one of the signals in the list,
at which time processing resumes with the first statement in the
process.
An externally defined procedure/subroutine can be invoked, with
parameters passed to it as necessary. This serves the same function and
behaves in the same manner as a process
statement, with any signals
in the passed parameters forming a sensitivity list.
ReadMemory (DataIn, DataOut, RW, Clk); -- (where the ReadMemory procedure is defined elsewhere)
Instantiates (i.e. create instances of) predefined components within a design architecture. Each such component is first declared in the declaration section of that architecture, and then instantiated one or more times in the body of the architecture.
- In the declaration section: list the component declaration and one or more configuration specifications.
- The configuration specification identifies specific architecture(s) to be used for each instance of the component. (There may be multiple architectures for a given component.)
Each instance of a declared component is listed, an instance name assigned, and actual signals connected to its ports as follows:
<instance name>: <component name> port map (port list);
The port list may be in either of two formats:
- (1) Positional association: signals are connected to ports in the
order listed in the component declaration.
- Example:
A1: adder port map (v,w,x,y,z)
(v,w, and y must be of typebit_vector
, y and z of typebit
)
- Example:
- (2) Named association: each signal-to-port connection is listed
explicitly as
signal => port
.
A1: adder port map(a => v, b => w, s => y, cin => x, cout => z);
(The signal ordering is not important in this format)
A concurrent assertion statement checks a condition (occurrence of an event) and issues a report if the condition is not true. This can be used to check for timing violations, illegal conditions, etc. An optional severity level can be reported to indicate the nature of the detected condition.
assert (clear /= '1') or (preset /= '1')
report "Both preset and clear are set!"
severity warning;
A generate statement is an iterative or conditional elaboration of a portion of a description. This provides a compact way to represent what would ordinarily be a group of statements.
Generate a 4-bit full adder from 1-bit full_adder
stages:
-- Note that a label is required here
add_label: for i in 4 downto 1 generate
FA: full_adder port map(C(i-1), A(i), B(i), C(i), Sum(i));
end generate;
The resulting code would look like:
FA4: full_adder port map(C(3), A(4), B(4), C(4), Sum(4));
FA3: full_adder port map(C(2), A(3), B(3), C(3), Sum(3));
FA2: full_adder port map(C(1), A(2), B(2), C(2), Sum(2));
FA1: full_adder port map(C(0), A(1), B(1), C(1), Sum(1));
Sequential statements are used to define algorithms to express the behavior of a design entity. These statements appear in process statements and in subprograms (procedures and functions).
Suspends process/subprogram execution until a signal changes, a condition becomes true, or a defined time period has elapsed. Combinations of these can also be used.
wait [on <signal> {, <signal>}]
[until condition]
[for time expression]
Suspend execution until one of the two conditions becomes true, or for
25ns
, whichever occurs first.
wait until clock = '1' or enable /= '1' for 25ns;
Assign a waveform to one signal driver (edit the event queue).
A <= B after 10ns;
C <= A after 10ns; -- value of C is current A value
Update a process/procedure/function variable with an expression. The update takes affect immediately.
A := B and C;
D := A; -- value of D is new A value
Invoke an externally-defined subprogram in the same manner as a concurrent procedure call.
Standard if...then
and case
constructs can be used for selective
operations.
if <condition> then
<sequence of statements>
elsif <condition> then
<sequence of statements>
else
<sequence of statements>
end if;
NOTE:
elsif
andelse
clauses are optional.
case <expression> is
when <choice> => <sequence of statements>
when <choice> => <sequence of statements>
...
when others => <sequence of statements>
end case;
NOTE: case choices can be expressions or ranges.
Sequences of statements can be repeated some number of times under the control of while or for constructs.
<label>: while <condition> loop
<sequence of statements>
end loop <label>;
<label>: for <loop variable> in range loop
<sequence of statements>
end loop <label>;
NOTE: the label is optional.
- Loop termination statements: allow termination of one iteration, loop, or procedure.
next [when condition];
: end current loop iterationexit [when condition];
: exit innermost loop entirelyreturn expression;
: exit from subprogram
NOTES:
- The next/exit condition clause is optional.
- The return expression is used for functions.
- 8. Sequential assertion - same format as a concurrent assertion.
A procedure is a subprogram that is passed parameters and may return values via a parameter list.
procedure <name> (
signal clk : in vlbit;
constant d : in vlbit;
signal data : out vlbit) is
<local variable declarations>
begin
<sequence of statements>
end <proc name>;
Procedure call: <name>(clk1, d1, dout);
A function is a subprogram that is passed parameters and returns a single value. Unlike procedures, functions are primarily used in expressions.
-- Convert bit_vector to IEEE std_logic_vector format
-- (attributes LENGTH and RANGE are described below)
function bv2slv (b : bit_vector) return std_logic_vector is
variable result : std_logic_vector(b'LENGTH-1 downto 0);
begin
for i in result'RANGE loop
case b(i) is
when '0' => result(i) := '0';
when '1' => result(i) := '1';
end case;
end loop;
return result;
end;
-- Convert bit_vector to unsigned (natural) value
function b2n (B : bit_vector) return Natural is
variable S : bit_vector(B'Length - 1 downto 0) := B;
variable N : Natural := 0;
begin
for i in S'Right to S'Left loop
if S(i) = '1' then
N := N + (2**i);
end if;
end loop;
return N;
end;
signal databus : vector4(15 downto 0);
signal internal : bit_vector(15 downto 0);
variable x : integer;
...
databus <= bv2slv(internal);
x := b2n(internal);
Data conversion between ieee types and bit
/bit_vector
(functions in
ieee.std_logic_1164
)
function | from | to |
---|---|---|
To_bit(sul) |
std_ulogic |
bit |
To_bitvector(sulv) |
std_ulogic_vector* /std_logic_vector/ |
bit_vector |
To_StdULogic(b) |
bit |
std_ulogic |
To_StdLogicVector(bv) |
bit_vector or std_ulogic_vector |
std_logic_vector |
To_StdULogicVector(bv) |
bit_vector or std_logic_vector |
std_ulogic_vector |
To_X01(v) |
bit , std_ulogic , or std_logic |
X01 |
To_X01Z(v) |
bit , std_ulogic , or std_logic |
X01Z |
To_UX01(v) |
bit , std_ulogic , or std_logic |
UX01 |
rising_edge(s)
-true
if rising edge on signal s (std_ulogic
)falling_edge(s)
-true
if falling edge on signal s (std_ulogic
)
An object attribute returns information about a signal or data type.
S'DELAYED(T)
: value of S delayed by T time unitsS'STABLE(T)
: true if no event on S over last T time unitsS'QUIET(T)
: true if S quiet for T time unitsS'LAST_VALUE
: value of S prior to latest changeS'LAST_EVENT
: time at which S last changedS'LAST_ACTIVE
: time at which S last activeS'EVENT
: true if an event has occurred on S in current cycleS'ACTIVE
: true if signal S is active in the current cycleS'TRANSACTION
: bit value which toggles each time signal S changes
if (clock'STABLE(0ns)) then -- change in clock?
... -- action if no clock edge
else
... -- action on edge of clock
end if;
if clock'EVENT and clock = '1' then
Q <= D after 5ns; -- set Q to D on rising edge of clock
end if;
T'BASE
: base type of TT'LEFT
: left bound of data type TT'RIGHT
: right boundT'HIGH
: upper bound (may differ from left bound)T'LOW
: lower bound
T'POS(x)
: position number of value of x of type TT'VAL(x)
: value of type T whose position number is xT'SUCC(x)
: value of type T whose position is x+1T'PRED(x)
: value of type T whose position is x-1T'LEFTOF(x)
: value of type T whose position is left of xT'RIGHTOF(x)
: value of type T whose position is right of x
A'LEFT(N)
: left bound of indexA'RIGHT(N)
: right bound of indexA'HIGH(N)
: upper bound of indexA'LOW(N)
: lower bound of indexA'LENGTH(N)
: number of values in range of indexA'RANGE(N)
: range:A'LEFT to A'RIGHT
A'REVERSE_RANGE(N)
: rangeA'LEFT downto A'RIGHT
NOTE: For multi-dimensional array, Nth index must be indicated in the attribute specifier. N may be omitted for a one-dimensional array.
for i in (<data bus>'RANGE) loop
...
for i in (d'LEFT(1) to d'RIGHT(1)) loop
...