Each VHDL design unit comprises an "entity" declaration and one or more "architectures". 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;
The mode of a port defines the directions of the singals on that pirt, and is one of: in, out, buffer, or inout.
entity counter is
port (Incr, Load, Clock: in bit;
Carry: out bit;
Data_Out: buffer bit_vector(7 downto 0);
Data_In: in bit_vector(7 downto 0));
end fulladder;
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 bit;
c: out bit);
generic (gate_delay: time := 5ns);
end and_gate;
architecture arch_name of entity_name is
... declarations ...
begin
... concurrent statements ...
end
Declarations include data types, constants, variables, 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.
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 package_name is
... exported constant declarations
... exported type declarations
... exported subprogram declarations
end package_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 package_name is
... exported subprogram bodies
... other internally-used declarations
end package_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;
use library_name.package_name.allA "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.
use package_name.all;
Note: 'std' and 'work' (your current working library)
are the two default libraries. The VHDL 'library' statement is needed to
make the 'ieee' library and/or additional libraries visible.
library lib_name; -- make library visible use lib_name.pkg_name.all; -- make package visible
library ieee; -- VHDL Library stmt
use ieee.std_logic_1164.all;
library mgc_portable; -- Special Mentor Graphics Library use mgc_portable.qsim_logic.all; -- Quicksim portable data types
Memory1, Adder_Module, Bus_16_Bit
16#9fba# (hexadecimal)
2#1111_1101_1011# (binary)
16#f.1f#E+2 (floating-point, exponent is decimal)
Bit String Literals
Bit vector constants are are specified as literal strings.
Examples
x"ffe" (12-bit hexadecimal value)
o"777" (9-bit octal value)
b"1111_1101_1101" (12-bit binary value)
Arithmetic and Logical Expressions
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:
a <= b nand c; d := g1 * g2 / 3; Bus_16 <= Bus1_8 & Bus2_8;
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);
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'
...
type word is array (0 to 15) of bit;
type memory is array (integer range <>) of bit_vector(0 to 7); -- a type which is an arbitrary-sized array of 8-bit vectors variable memory256: memory(0 to 255); -- a 256-byte memory array variable stack: memory(15 downto 0); -- a 16-byte memory array
subtype byte_signed is integer range -128 to 127; subtype byte_unsigned is integer range 0 to 255;
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
constant Vcc: signal:= '1'; --logic 1 constant
constant zero4: bit_vector(0 to 3) := ('0','0','0','0');
process
variable count: integer := 0;
variable rega: bit_vector(7 downto 0);
begin
...
count := 7; -- assign values to variables
rega := x"01";
...
end;
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.
10ns '0' Driver of
20ns '1' signal x
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.
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.
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 and std_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.
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'.
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.
label: block (guard expression)
... local declarations ...
begin
... concurrent statements ...
end block label;
If a guard expression is given, "guarded" a boolean variable GUARD is automatically defined and set to the boolean value of the guard expression. GUARD can then be tested within the block, to perform selected signal assignments or other statements only when the guard condition evaluates to TRUE.
-- D Latch: Transfer D input to Q output when Enable = '1'
block (Enable = '1')
begin
Q <= guarded D after 5ns;
end block;
-- D Flip-flop: Transfer D to Q on falling edge of Clock
block (Clock'EVENT and Clock = '0')
begin
Q <= guarded D after 5ns;
end block;
-- Tristate driver with input B and output A
block (Enable = '1')
begin
A <= B when GUARD = '1' else 'Z';
end block;
In the last example, B is assigned to signal A only when GUARD is true, which implies Enable = '1'.
ReadMemory (DataIn, DataOut, RW, Clk); (where the ReadMemory procedure is defined elsewhere)
The "component declaration" defines the component interface, which corresponds to the component's entity declaration. This allows the VHDL compiler to check signal compatibilities.
component adder
port(a,b: in bit_vector(7 downto 0);
s: out bit_vector(7 downto 0);
cin: in bit;
cout: out bit);
end component;
for ALL: comp1 use entity work.comp1 (equations);
for ADDER1: adder use entity work.adder (equations);
for ADDER2: adder use entity work.adder (dataflow);
In all three examples, the prefix work. indicates that the
current working library contains the indicated component models.
In the first example, architecture equations of entity
comp1 is used for all instances of comp1. In the
other examples, architecture equations is to be used for
instance ADDER1
of component adder, and architecture dataflow
is to be used for instance ADDER2 of component adder.
The port list may be in either of two formats:
A1: adder port map(a=>v, b=>w, s=>y, cin->x, cout->z);
(The signal ordering is not important in this format)
architecture r1 of register is
component jkff
port(J,K,CLK: in bit;
Q,QN: out bit);
end component;
for ALL: jkff use entity work.jkff (equations);
-- Use architecture equations of entity jkff
for all instances
component dff
port(D,CLK: in bit;
Q,QN: out bit);
end component;
for DFF1: dff use entity work.dff (equations);
for DFF2: dff use entity work.dff (circuit);
--Use different architectures of dff for instances
DFF1 and DFF2
begin
JKFF1: jkff port map (j1,k1,clk,q1,qn1);
JKFF2: jkff port map (j2,k1,clk,q2,qn2);
DFF1: dff port map (d1,clk,q4,qn4);
DFF2: dff port map (d2,clk,q5,qn5);
end.
Concurrent assertion
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.
Syntax:
assert (clear /= '1') or (preset /= '1')
report "Both preset and clear are set!"
severity warning;
Generate statement
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.
Example
Generate a 4-bit full adder from 1-bit full_adder stages:
for i in 4 downto 1 generate
FA: full_adder port map(C(i-1), A(i), B(i), C(i), Sum(i));
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
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).
Wait statement
- 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.
Syntax:
wait [on signal_name {,signal_name}]
[until condition]
[for time expression]
Example
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;
Signal assignment statement
Assign a waveform to one signal driver (edit the event queue).
Example
A <= B after 10ns;
C <= A after 10ns; -- value of C is current A value
Variable assignment statement
Update a process/procedure/function variable with an expression.
The update takes affect immediately.
Example
A := B and C;
D := A; -- value of D is new A value
Procedure call
Invoke an externally-defined subprogram in the same manner
as a concurrent procedure call.
Conditional Statements
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 and else clauses are optional.
case expression is
when choices => sequence of statements
when choices => sequence of statements
...
when others => sequence of statements
end case;
NOTE: case choices can be expressions or ranges.
Loop statements
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
iteration
exit [when condition]; -- exit innermost loop entirely
return expression; -- exit from subprogram
NOTES: 1. The next/exit condition clause is optional.
2. The return expression is used for functions.
procedure proc_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: proc_name(clk1, d1, dout);
-- 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")
a + b, a - b, a * b, a / b, a mod b, a rem b
Logical operations between all signal types and vectors of signal types in the "ieee" library.
and, or, nand, nor, xor, xnor, not
Shift/rotate left/right logical/arithmetic operators:
sll, srl, sra, rll, rrl
Ex. a := x sll 2; -- "shift left logical" bit_vector x by 2 bits
Relational operations: =,/=,<,>,<=,>=
Type conversion:
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;
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
...
file Prog: text is in "file_name"; --text file "file_name" variable L: line; -- read lines from file to L