Uploaded by papayagabanana

Introduction to VHDL

advertisement
ECE385
DIGITAL SYSTEMS LABORATORY
Introduction to VHDL
What is VHDL:
 VHSIC Hardware Description Language
 VHSIC - Very High Speed Integrated Circuits
 Follows the structure of ADA programming Language
 Originally intended as a Simulation Language for very large systems
 Very Strongly Typed Language, for example, bit vector “0011” and integer ‘3’ are
not easily interchangeable. VHDL is not case sensitive.
Uses 9 Signal Values (IEEE standard):
 A Signal Value must be enclosed in single quotes
 ‘0’ -- Forcing 0
 ‘1’ -- Forcing 1
 ‘X’ -- Forcing Unknown
 ‘-’ -- Don’t Care
 ‘Z’ -- High Impedance
 ‘U’ -- Uninitialized
 ‘L’ -- Weak 0
 ‘H’ -- Weak 1
Data Objects:
VHDL provides different data objects, such as, signal, variable, and constant.
 Signals can be thought of as representing wires in the circuit. A signal holds the
current and future values of an object.
 Declaring signals:
 signal <signal_name>: <signal_type>
 signal <signal_name>: std_logic;
 signal count: std_logic;
 signal <signal_name>: std_logic_vector(<upper_bound> downto
<lower_bound>);
 signal vector_A: std_logic_vector(7 downto 0);

Variables can be assigned a single value of a specific data type. Variables are used for
computations within processes, just like in conventional programming languages.
 Declaring variables:
 variable <variable_name>: <variable_type>
 variable var1: std_logic;
 variable var2: std_logic_vector(7 downto 0);
 variable var3: integer range 0 to 8;


Constants can be any valid data type. Constants are only allowed to be declared and
initialized at the beginning of the simulation.
 Declaring constants:
 constant <constant_name>: <constant_type>
 constant max: integer:= 25;
Difference between signals and variables: Signals can be assigned different values at
different points in time so a signal can have multiple values. Signals are scheduled to
receive certain values at certain points in time by the simulator, and thus signal
objects must maintain a history of values. Variables, on the other hand, can only be
assigned one value at any point in time. Variables are assigned value during the
execution of the assignment statement. Also, you can trace signals in simulation
waveforms but not variables.
Data Types:
You will generally be using signals and variables of type std_logic and std_logic_vector,
but VHDL also has other standard data types that you can use, such as, integer, boolean
etc. Single bit values are enclosed in single quotes and bit vectors are enclosed in double
quotes. The data types are defined in STANDARD package libraries provided according
to the IEEE standard system.
Operators:
Operators can be used in expressions involving signals, variables, or constant object
types. Here are some of the useful operators:
 Logical: not, and, or, nand, nor, xor
 Relational: =, /= (inequality), <, <=, >, >=
 Addition: +,  Concatenation: & (used to combine bits)
 Shift: sll (logical left shift), srl, sla (arithmetic left shift), sra
Note: VHDL also has some other operators that are not mentioned here since all
operators are not supported by all synthesis tools. Older versions of VHDL may not
include some of the operators. For example, xnor is not supported in VHDL’87, but is
supported in VHDL’93. Shift operators are also only supported in VHDL’93.
Converting Between Data Types:
 CONV_STD_LOGIC_VECTOR(integer, bits) – Converts an integer to a standard
logic vector.
 Example: CONV_STD_LOGIC_VECTOR(2, 3) will produce a standard logic
vector of “010”.

CONV_INTEGER(std_logic_vector) – Converts a standard logic vector to an integer.
 Example: CONV_INTEGER(“010”) will produce an integer value of 2.
Entities and Architectures:
 Design Entity
Entity is the primary design unit in VHDL. Multiple entities may be combined to
form a larger design. An entity-architecture pair provides design entity description.
Entity declaration provides the external interface to the design entity. It contains pinout description, interface description, input-output port definitions etc.
Architecture describes the behavior of an entity or its structure (gates, wires, etc.)
using VHDL constructs.
Example:
-- Always include necessary Libraries
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
-- Entity declaration.
-- x and y are the inputs and z is the output of the entity called ‘Example’
-entity Example is
Port ( x : in std_logic;
y : in std_logic;
z : out std_logic);
end Example;
-- Architecture definition.
-- The behavior of the entity, Behavioral (can be any name), describes
-- the function of the design entity.
-architecture Behavioral of Example is
begin
z <= x or y;
-- output z will be the logical or of x and y
end Behavioral;
Concurrency in VHDL:
 Concurrent Signal Assignments (CSA)
 All statements in a VHDL description are executed concurrently unless specified
within a process.
 Concurrency is useful in describing combinational logic circuits.
 A concurrent statement is evaluated when any of its arguments change its value.

A process executes only on specified triggers
 A process declaration includes a sensitivity list.
 A process executes only when one of the arguments in the sensitivity list changes.
 Processes are useful in describing sequential circuits and state transition diagrams.
Signals and Variables:
Signal assignments take effect during the next simulation cycle and after exiting a
process if put in a process construct. If it is desired that the values change immediately,
then variables should be used. Signals are declared before ‘begin’ in architecture
definition and variables are declared before ‘begin’ in process definition.
Example: Using Signals and Variables
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity sig_var_example is
Port ( a : in std_logic;
b : in std_logic;
c : in std_logic;
out_sig : out std_logic;
out_var : out std_logic);
end sig_var_example;
architecture Behavioral of sig_var_example is
signal sig1, sig2 : std_logic;
-- Signals are declared here
begin
sig_proc: process (a, b, c) is
begin
sig1 <= b or c;
sig2 <= sig1 and a;
out_sig <= sig1 nand sig2;
end process;
-- Output based on signals
var_proc: process (a, b, c) is
variable var1, var2 : std_logic; -- Notice where variables are declared.
begin
var1 := b or c;
-- Notice the assignment operator
var2 := var1 and a;
out_var <= var1 nand var2;
-- Output based on variables
end process;
end Behavioral;


In this case, the values of sig1 and sig2 will not change until the next simulation
cycle after the process exits. out_sig will be computed using the old values of sig1
and sig2.
out_var will be calculated from the current values of a, b, and c since var1 and
var2 will reflect the results immediately (rather than during the next simulation
cycle).
Behavioral Simulation: Notice the difference between out_sig and out_var
If-Then-Else and If-Then-Elsif Statements:
 An if statement executes a block of sequential statements upon matching certain
condition(s).
 It can also include an else component or one or more elsif (not ‘elseif’ or ‘else if’,
no ‘e’ in else) components.
 If-then-elsif construct forces a priority order in the logic. I.e. the first statement
will be evaluated first and then the following statements in order.
 If statements are only allowed within a process. The statements are executed
sequentially if a conditional match is detected.
 To avoid inferred latches, all outputs should be assigned values on all execution
paths.
 Keep common statements outside of the if-then-else or if-then-elsif statements.
Example: Inference
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity if_example is
Port ( x : in std_logic;
y : in std_logic;
z : in std_logic;
sel : in std_logic;
w : out std_logic);
end if_example;
architecture Behavioral of if_example is
begin
example inference: process(x, y, z, sel) --Create a process
variable s1, s2: std_logic;
begin
if (sel = '1') then
s1 := x and y;
s2 := s1 xor z;
w <= s1 and s2; --Since w gets a value only conditionally,
-- a latch is inferred.
end if;
end process;
end Behavioral;

To avoid the inferred latch, assign a default value to w outside the if-then
statement. For example, you can add "w <= '0';" before the if-then statement. You
can also add the same statement in the else part using an if-then-else statement.
Case Statement:





Equivalent to nested set of if-then-elsif constructs but produces less logic since it
does not force priority order.
It identifies mutually exclusive blocks of code.
Since all possible values of select inputs must be covered, use the others clause.
Case statements have to be inside a process.
Case statements can be used to describe multiplexors.
Example: Case Statement
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity case_example is
Port ( a : in std_logic;
b : in std_logic;
sel : in std_logic;
c : out std_logic);
end case_example;
architecture Behavioral of case_example is
begin
case_process: process(a, b, sel) is
begin
case sel is
when '0' =>
c <= a;
when '1' =>
c <= b;
when others =>
c <= '0';
end case;
--Use the ‘others’ clause
--Don’t forget the ‘end case’
end process;
end Behavioral;
When-Else and Select Statements:
A When-else statement is similar to an if-then-else statement. It forces a priority structure
and conditions are evaluated sequentially.
A Select statement is similar to a case Statement. It requires all possible input
combinations to be covered by mutually exclusive conditions. Since it does not force a
priority structure, the synthesis tools can produce better-optimized logic than with whenelse statements.
Unlike if-then-else and case statements, when-else and select statements do not have to be
inside a process. See the examples below with when-else and select statements.
Example: 4-to-1 Multiplexer
Din
sel
4
2
ENTITY
mux
ARCHITECTURE
Design 1:
entity mux is
port (sel: in std_logic_vector(1 downto 0);
Din: in std_logic_vector (3 downto 0);
Dout: out std_logic);
end entity;
Dout
architecture my_mux_behavior of mux is
begin
Dout <= Din(3) when sel=“11” else -- first evaluate this
Din(2) when sel=“10” else -- next evaluate this
Din(1) when sel=“01” else -- then evaluate this
Din(0) when sel=“00” else -- then evaluate this
‘X’;
-- if all fails then X
end my_mux_behavior;
Design 2: (Better than Design 1 since produces less logic)
entity mux is
port (sel: in std_logic_vector(1 downto 0);
Din: in std_logic_vector (3 downto 0);
Dout: out std_logic);
end entity;
architecture my_mux_behavior of mux is
begin
with sel select
-- there is no specific order under which conditions are evaluated
Dout <= Din(3) when “11”,
Din(2) when “10”,
Din(1) when “01”,
Din(0) when “00”,
‘X’ when others; --“default case” must be included
end my_mux_behavior;
Designing Latches and Flip-Flops:
Flip-Flops and other clocked devices can only be synthesized inside a process.
Example: Positive-Edge Triggered D-Flip-Flop with Asynchronous Reset
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity d_ff is
Port (clk : in std_logic;
reset : in std_logic;
d : in std_logic;
q : out std_logic);
end d_ff;
architecture Behavioral of d_ff is
begin
out_Q: process(clk, reset, d) is --Create a process
begin
if (reset = '1') then
q <= '0';
elsif (rising_edge(clk)) then --Check for rising_edge of clock
q <= d;
end if;
end process;
end Behavioral;
Creating Hierarchical Design Using Components:
As mentioned earlier, you can create larger designs using entities as the components that
make up the design. One entity can instantiate another entities as components of it. One
entity can use different entities as components as well as instantiate multiple copies of the
same entity.
Example: Creating a 4-bit adder using a full-adder component
Design Entity Full-Adder
entity full_adder is
port (x, y, z : in std_logic;
s, c : out std_logic);
end entity;
architecture Behavioral of full_adder is
begin
s <= x xor y xor z;
c <= (x and y) or (y and z) or (x and z);
end Behavioral;
Use component full_adder to create a 4-bit adder:
ADDER4
A3 B3
c_out
x y
FA3
c s z
S3
A2 B2
A1 B1
x y
c2 cFA2 z
s
x y
c1 cFA1 z
s
S2
A0 B0
x y
c0 FA0
c s z
S1
c_in
S0
Design Entity ADDER4 that uses multiple full-adder components
entity ADDER4 is
port (A,B : in std_logic_vector (3 downto 0);
S : out std_logic_vector (3 downto 0);
c_in : in std_logic;
c_out : out std_logic);
end entity;
architecture structural of ADDER4 is -- omit “is” for older simulators
--Declare component that will be used in architecture definition
component full_adder is
port(x,y,z: in std_logic; s,c: out std_logic); -- reproduce the entity description
end component full_adder; -- omit name “full_adder” for older simulators
signal c0,c1,c2: std_logic; -- internal carries in the the 4-bit adder
begin -- this illustrates how to instantiate and connect components
FA0: full_adder port map(x =>A(0), y =>B(0), z =>c_in, s =>S(0), c =>c0);
FA1: full_adder port map(x =>A(1), y =>B(1), z =>c0, s =>S(1), c =>c1);
FA2: full_adder port map(x =>A(2), y =>B(2), z =>c1, s =>S(2), c =>c2);
FA3: full_adder port map(x =>A(3), y =>B(3), z =>c2, s =>S(3), c =>c_out);
end structural ADDER4;
More Examples:
Example: Sequential Circuit
enable
up_down
asynch_clr
clk
8-bit Up-Down Counter
Q(7)Q(6) . . . . . Q(1)Q(0)
entity up_down_counter is
port (clk, enable, up_down : in std_logic;
asynch_clr: in std_logic;
Q: out std_logic_vector(7 downto 0));
end entity;
architecture counter_behavior of up_dn_counter is
signal count: std_logic_vector(7 downto 0);
-- count is an internal signal to this process
Begin
process(clk, asynch_reset)
-- sensitivity list
begin
if (asynch_reset=‘1’) then
count <= “00000000”;
elsif (clk’event and clk=‘1’) then
-- rising edge
if (enable=‘1’) then
if (up_down=‘1’) then
count <= count+”00000001”;
else
count <= count-”00000001”;
end if;
end if;
-- ‘end if’ is not permitted here for ‘elsif’
end if;
end process;
Q <= count;
end counter_behavior;
NOTE: We cannot use “Q <= Q + 1” since Q is defined as output only. If we want to use
“Q <= Q + 1” then we can declare Q as type ‘buf’ instead of ‘out’.
e.g. Q: buf std_logic_vector(7 downto 0)
-
Notice ‘elsif’, not ‘elseif’ or ‘else if’
Notice ‘end if’, not ‘endif’
Use ‘rising_edge(clk)’ rather than ‘clk’event and clk=’1’’ for true rising_edge
detection.
Example: 4-Bit Shift Register
D
Shift_In
Load
Shift_En
Clk
4
4-Bit Register
“reg_4”
4
Data_Out
Shift_Out
entity reg_4 is
Port (Shift_In, Load, Shift_En, Clk : in std_logic;
D : in std_logic_vector(3 downto 0);
Shift_Out : out std_logic;
Data_Out : out std_logic_vector(3 downto 0));
end reg_4;
architecture Behavioral of reg_4 is
signal reg_value: std_logic_vector(3 downto 0);
begin
operate_reg: process (Load, Shift_En, Clk, Shift_In)
begin
if (rising_edge(Clk)) then
if (Shift_En = '1') then
reg_value <= Shift_In & reg_value(3 downto 1);
-- operator “&” concatenates two bit-fields
elsif (Load = '1') then
reg_value <= D;
else
reg_value <= reg_value;
end if;
end if;
end process;
Data_Out <= reg_value;
Shift_Out <= reg_value(0);
end Behavioral;
State Machine Design:
Example: Control Unit
Reset
LoadA
LoadB
Execute
Clk
Control
Shift_En
Ld_A
Ld_B
entity control is
Port ( Reset, LoadA, LoadB, Execute : in std_logic;
Clk : in std_logic;
Shift_En, Ld_A, Ld_B : out std_logic);
end control;
architecture Behavioral of control is
--declare A, B, ..., F of type cntrl_state
--User defined type “cntrl_state” has 6 symbolic values.
type cntrl_state is (A, B, C, D, E, F);
--declare signals state and next_state of type cntrl_state
signal state, next_state : cntrl_state;
begin
control_reg: process (Reset, Clk, LoadA, LoadB)
begin
if (Reset = '1') then
state <= A;
elsif (rising_edge(Clk)) then
state <= next_state;
end if;
end process;
--Assign 'next_state' based on 'state' and 'Execute'
get_next_state: process (Execute, state)
begin
case state is
when A =>
if (Execute = '1') then
next_state <= B;
else
next_state <= A;
end if;
when B =>
next_state <= C;
when C =>
next_state <= D;
when D =>
next_state <= E;
when E =>
next_state <= F;
--wait at state F until 'Execute' = 0
when F =>
if (Execute = '0') then
next_state <= A;
else
next_state <= F;
end if;
-- “when others =>” default case is not needed here since there are
-- only six values for “state” and we have exhausted them all.
end case;
end process;
--Assign outputs based on 'state'
get_cntrl_out: process (LoadA, LoadB, state)
begin
case state is
when A =>
--Only load value in register(s) when in state A
Ld_A <= LoadA;
Ld_B <= LoadB’
Shift_En <= '0';
--No Load or Shift when in state F
when F =>
Ld_A <= '0';
Ld_B <= '0';
Shift_En <= '0';
when others =>
--This is used for states B, C, D, and E
Ld_A <= '0';
Ld_B <= '0';
Shift_En <= '1';
end case;
end process;
end Behavioral;
Other Notes/Hints:
 A design that simulates is not guaranteed to synthesize. Simulation tools allow
simulating designs that may not be physically possible to implement.
 It is always a good idea to assign values to signals/outputs in all possible cases. That
way we can avoid inferred latches. If we do not specify the value of a signal for some
input combination, then the design will have inferred latch(es) because it will try to
hold the old value of the signal.
 Keep in mind that VHDL statements are usually executed concurrently and not
sequentially.
 When a value is assigned to a signal, it does not take affect until the next
simulation cycle.
 Include all inputs that can change the outputs in the sensitivity list of a process to
avoid different circuit behavior from simulation and synthesis.
 You can manipulate when the process executes by including/excluding certain
inputs from the sensitivity list, but that will only work for the simulation model. If
the synthesized design is combinational, then the logic will respond to events on
any inputs, not just the inputs in the sensitivity list.
 VHDL allows delay/wait statements, but that is only guaranteed during simulation.
The synthesized design may not have similar delays.
 For example: x <= y after 5 ns;
 This is allowed, but synthesis behavior is not guaranteed.
 VHDL compilers generally allow while-loop statement, but synthesis tools generally
don’t support it. Don’t use it in a design that needs to be synthesized.
 Do not specify initial values in signal declarations. Synthesis compilers ignore initial
values. Initial values can be assigned explicitly under a controlling signal (e.g. reset).
 Assign ‘don’t care’ values to signals when possible for default cases. It may allow the
synthesis compiler to produce a better design.
 Don’t use don’t care symbols in comparisons.
 If you use integer type signals, specify value-ranges in the declaration as default sizes
can produce a rather large design.
 Case statements produce less logic than if-then-else statements since if-then-else
statements produce priority logic also.
 You cannot have a wait statement in a process unless it is the first statement in the
process and the only wait statement in the process. The process will also need to have
an empty sensitivity list.
 Do not assign value to the same signal via multiple statements that can execute
simultaneously.
 Write your code for synthesis rather than just simulation, as you will need to
synthesize your designs for all experiments involving VHDL.
References:
 Yalamanchili, Sudhakar. Introductory VHDL – From Simulation to Synthesis.
New Jersey: Prentice-Hall, 2001.
 Hamblen, James and Furman, Michael. Rapid Prototyping of Digital Systems – A
Tutorial Approach, Second Edition. Kluwer Academic Publishers, 2001.
Download