Escolar Documentos
Profissional Documentos
Cultura Documentos
Dr T D Binnie 2010
Contents
1
1a. 1b.
Introduction
Background..................................................................................................................... 7 Key Features.................................................................................................................. 8
2. 3.
3a. 3b
9 13
4.
15
Entities ......................................................................................................................... 15 Architectures.............................................................................................................. 18 A Complete VHDL (behavioural) Example ............................................................ 21 Concurrent and Sequential Statements ............................................................... 23 Further examples of entity-architecture pairs ................................................ 24 Structural VHDL........................................................................................................ 25
5.
5a. 5b. 5c.
27
6.
6a. 6b. 6c. 6d.
Concurrent Statements
31
Simple Concurrent Signal Assignments. ................................................................ 31 Conditional Signal Assignments.............................................................................. 31 Selected Signal Assignments ................................................................................ 37 Tristate Output ......................................................................................................... 39
7.
7a. 7b. 7c. 7d.
Sequential Modelling
41
8.
8a. 8b
55
9. 10.
59 63
10a. Component declaration ............................................................................................. 64 10b. Component Instantiation and Interconnections ................................................. 65
11.
11a. 11b. 11c
71
Introduction
1a. Background
VHDL is a language that was developed during the 1970s to describe and model complex digital circuits and systems. VHDL is an acronym: the V stands for VHSIC (Very High Speed Integrated Circuit) and the HDL stands for Hardware Description Language. It is only recently that manufacturers of programmable logic devices have produced software which allows the VHDL descriptive code to be synthesised into to an actual digital circuit. This translation from software code into hardware is called digital synthesis. Synthesis is the process which translates VHDL, which is just abstract code, into a digital circuit. Designers must be aware that VHDL is primarily a descriptive language and not all VHDL code can be synthesised. In this book we will concentrate on VHDL for synthesis. Although VHDL looks similar to conventional programming languages like C or Pascal, there are some important differences. A hardware description language is inherently parallel, i.e. commands, which correspond to logic gates, are executed (computed) in parallel, as soon as a new input arrives. A HDL program emulates the behaviour of a physical digital system. It can also be used to describe digital systems as hierarchy of logic components.
architecture RTL of mux_gate is begin process (SEL,A,B,C,D,E) begin case SEL is when "000" => SIG <= A; when "001" => SIG <= B; when "010" => SIG <= C; when "011" => SIG <= D; when others => SIG <= E; end case; end process; end RTL;
Synthesis
This book was written as a very simple introduction to VHDL for students who want to get quickly to circuit design and synthesis. Some knowledge of digital electronics is assumed. The next section shows how VHDL can be used to simply define connectivity between components or as an abstract description language. In Section 3, the structure of a VHDL module is introduced. This is followed in Section 4 by a quick overview of VHDL coding using concurrent and sequential statements. Sections 5 gives an important introduction to Data Objects - signals variables and constants - in VHDL. More detailed VHDL examples and a discussion of concurrent and sequential modelling follow in Sections 6 and 7. Section 8 gives examples of coding sequential state machines. Section 9 has tested examples of synthesisable code for a range of digital components. Section 10 gives examples of port mapping and structural code. Lastly Section 11 gives details of VHDL testbenches.
1b.
<= is the signal assignment operator. e.g. A <= B; means that signal B is assigned the value of signal A; Signal assignments are concurrent statements. The order in which assignment statements are executed cannot be fixed. When a signal on the right changes its value then the statement is executed. Thus an event on one signal will lead to an event on another. Sequential events on the other hand are described using a process statement. Within a process, the statements are executed sequentially. All signal values in a process are updated at the same time. Comment Lines start with two dashes: -- comments VHDL is not case sensitive: 'ABCD' is the same as 'abcd'.
2.
Example 2: Behavioural VHDL These two pieces of VHDL code would actually synthesise the same circuit, shown in Figure 2.
A B C
OR AND F
A behavioural description specifies the relationship between the input and output signals. This could be an algorithmic description or a less abstract Boolean expression. The above example is a very simple circuit. The code is a behavioural level description of a logic operation. This type of description is at the highest level of abstraction from gate level. This code contains no timing or structural information.
The structural level of description, on the other hand, describes a system as a collection of gates and components that are interconnected to perform a desired function. A structural description is like a circuit net list and can be compared to a schematic of interconnected logic gates.
signal X: bit; begin Gate1: OR_comp port map (A, B, X); Gate2: AND_comp port map (C, X, F); end;
Example 3 is a structural level description of the logic circuit in Figure 2. This type of description is at the lowest level of abstraction. This code simply defines the connections between basic logic gates or other components. VHDL allows the description of digital systems both at the behavioural level and the structural level or a mix of both. Behavioural levels description can be further divided into two styles: algorithmic and dataflow (also termed Register Transfer Level - RTL). The algorithmic description is specified in terms of computational steps, whereas the dataflow-RTL representation describes how data moves between registers though a system. RTL is used predominantly in sequential systems.
logic
reg
logic
reg
10
process(clock) begin if rising_edge (clock) then if (C =1 and (A=1 or B=1)) then F <="00000001" else F <= "10000000"; end if; end if; end process;
Example 4: Register Transfer Level VHDL Example 4 is a simple RTL representation of a circuit. This description contains functional and timing information. The logic for the register F is clearly defined; the output will change on the clock edge. At the RTL level, a description details the behavioural of a sequential design at the clock-cycle level.
More Abstract
Behavioural Architecture
Model gives timing and some detail of internal architecture but does not use sub components Model describes the components and connections to make the functionality
More Detailed
Structural Architecture
These notes are about writing VHDL for the synthesis of digital circuitry. To do this successfully, we write behavioural code at RTL level. When designing larger circuits with VHDL it is important to consider hierarchy and coding style. Using vendor advised coding styles will produce a more effective and efficient implementation.
11
12
3.
VHDL Module
Libraries Entity
( Interface: Input and Output Ports )
Architecture
( Combinational Description and Sequential Processes, Subprograms )
13
3b
VHDL Keywords
abs access after alias all and architecture array assert attribute begin block body buffer bus case component configuration constant disconnect
downto else elsif and entity exit file for function generate generic group guarded if impure in inertial inout is
label library linkage literal loop map mod nand new next nor null of on open others out package
port postponed procedure process pure range record register reject rem report return ror rol select severity shared signal sla sll sra
srl subtype then to transport type units unaffected until use variable wait when while with xor xnor
Notes
VHDL uses reserved keywords which cannot be used as signal names or identifiers. Keywords and user-defined identifiers are case insensitive. Lines with comments start with two adjacent hyphens (--) and are ignored by the compiler. VHDL also ignores line breaks and extra spaces. VHDL is a strongly typed language which means that the type of signals always has to be declared.
14
4.
An entity always starts with the keyword entity, followed by its NAME and the keyword is. Next are the port declarations using the keyword port. An entity declaration always ends with the keyword end, optionally followed by the name of the entity. The signal_names consists of a comma separated list of one or more user-selected identifiers that specify external interface signals. Followed by the signal mode - a reserved word to indicate the signal direction: in, out or inout. Then a built-in signal type: bit, std_logic etc.
Notes
Xilinx ISE Tools have an HDL Design Wizard (which pops up when a new HDL module is to be written.) This wizard writes the VHDL entity declaration for the designer when the user enters the number and name of input and output ports. The component entity declaration then appears in the new VHDL script. It is still important to understand the entity declaration as it may need to be edited later if changes to the design are made.
15
For the example of Figure 2 above, the entity declaration looks as follows.
The entity name is Logic and the entity has three input ports: A, B and C and one output port: F. Notice the use and placement of semicolons. Note that VHDL is not case sensitive.
Each port has to have a mode (in, out or inout) and a defined type. In this case, the std_logic type. This is the preferred type of digital signals. The simplest type is the bit type that can only have the values '1' and '0', The std_logic type signals can have nine values. As we will appreciate later, this is important to describe a digital hardware system accurately. The std_logic type is defined in the std_logic_1164 package within in the IEEE library.
16
Here are a few other examples of entity declarations: An example of the entity declaration of a D flip-flop with set and reset inputs is
entity dff_sr is port (D, CLK, S, R: in std_logic; Q, Qnot: out std_logic); end dff_sr;
An example of the entity declaration for a 4 bit counter with enable, reset and clock inputs entity count4 is port ( reset: in std_logic; enable: in std_logic; clock: in std_logic; count: inout std_logic_vector (3 downto 0) ); end count4;
Four-to-one multiplexer of which each input is an 8-bit word and is a std_logic_vector type.
entity mux4_to_1 is port ( In0,In1,In2,In3: in std_logic_vector(7 downto 0); Out1: out std_logic_vector(7 downto 0));
end mux4_to_1;
17
4b.
Architectures
The architecture body specifies how the circuit operates and how it is structured. A circuit can be specified in a variety of ways, such as behavioural or structural, or a combination of the both. The form of an architecture body is shown below:
architecture architecture_name of NAME_OF_ENTITY is -- Declarations -- components declarations -- internal signal declarations -- type declarations begin -- VHDL statements; end architecture_name;
The architecture body for a simple car alarm circuit, described at the behavioural level, is given below,
architecture behavioural of Buzzer is begin WARNING <= (not DOOR and IGNITION) or (not SBELT and IGNITION); end behavioural;
Example 4: Buzzer Circuit Architecture The header line of the architecture body defines the architecture name, e.g. behavioural, and associates it with the entity, BUZZER. Note, the name: behavioural is not a reserved word. The main body of the architecture starts with the keyword, begin and gives the Boolean expression of the function.
18
Importantly, the ' <= ' symbol represents an assignment operator. This assigns the value of the expression on the right to the signal on the left. It does not mean 'equal to'. It cannot be reversed. The architecture body ends with an end keyword followed by the architecture name. More examples are shown in the next section.
Not all signals needs be ports of the module. Internal signals can be declared in the architectures before the begin.
DOOR
B1 WARNING
B2
IGNITION
SEATBELT
Here is a car alarm circuit with internal nodes labelled as signals: B1 and B2.
architecture behavioral of Buzzer is signal B1, B2: std_logic; begin B1 <= not door and ignition; B2 <= not seatbelt and ignition; warning <= B1 or B2; end behavioral;
19
4c.
The VHDL design entity for the circuit shown above circuit will not compile without access to a library. A VHDL package is a file in a library that contains declarations (not the definition) of commonly used objects, data types, component declarations, signals, procedures and functions that can be shared among different VHDL designs. The std_logic type is defined in the ieee.std_logic_1164 package in the ieee library. In order to use the std_logic one needs to specify the library and package. This must be done at the beginning of the VHDL file, before the entity declaration, using the library and the use keywords as follows:
Notes
The Xilinx HDL design wizard will automatically insert the available package and library lines for the VHDL module. You can write your own package to access commonly used components, and access it using a statement such as:
use work.my_package.all;
The package and the component definitions must be compiled into the local work directory.
20
4d.
library ieee; use ieee.std_logic_1164.all; entity AND2 is port (in1, in2: in std_logic; out1: out std_logic); end AND2; architecture behavioral_2 of AND2 is begin out1 <= in1 and in2; end behavioral_2;
Example 6: AND Gate This is a complete piece of VHDL code which can be compiled and synthesised and downloaded on to an FPGA. The behavioural logic in the code is written using the logical and operator. The basic logic functions for the bit type are generic but logic functions for the std_logic type are defined in the ieee library.
Notes
21
A further example of a two-input XNOR gate is shown below. library ieee; use ieee.std_logic_1164.all; entity name
entity XNOR2 is port (A, B: in std_logic; Z: out std_logic); end XNOR2; architecture behavioural of XNOR2 is -- signal declaration (of internal signals X, Y) signal X, Y: std_logic; begin X <= A and B; Y <= (not A) and (not B); Z <= X or Y; end behavioural; architecture name
Notes
The statements in the body of the architecture make use of logic operators. The allowed logic operators are: and, or, nand, nor, xor, xnor and not. In addition, other types of operators are allowed including relational, shift, and arithmetic operators.
22
4e.
It is worth noting that the signal assignments in the above examples are concurrent statements. This means that the statements are executed only when one or more of the signals on the right hand side change their value (i.e. an event occurs on one of the signals). For instance, when the input A changes, the internal signals X and Y change values that in turn causes the last statement to update the output Z. There may be a propagation delay associated with this change. The logic described is asynchronous combinational logic. Digital systems are basically datadriven and an event which occurs on one signal will lead to an event on another signal, etc. The execution of the statements is determined by the flow of signal values. Not the order in which they are written (The order in which these statements are written does not matter!) This is in sharp contrast to conventional, software programs that execute the statements in a sequential or procedural manner. The programs you have looked at already are simple logic statements. It is also straight forward to use a conditional signal assignment in concurrent VHDL. To do this we use the when else selection statement. In the example shown on the following page, you can see that the output Y is assigned the value of 1 depending on the values of inputs A, B and C.
Where sequential operation is required in a VHDL module then a process statement must be used within the architecture. The process is a piece of code within the architecture where the order of execution of the statements is sequential. This is the most common form of logic design and is covered in detail in the next section. There is an example following which implements the same logic as the concurrent logic above it but it is written using the if then else selection statement.
23
4f
Example 8: Concurrent VHDL: when-else Sequential Selection Statement: if then - else library ieee; use ieee.std_logic_1164.all; entity logic2 is port (A : in std_logic; B: in std_logic; C: in std_logic; Y: out std_logic); end logic2; architecture behavioral of logic2 is begin process (A,B,C) begin if (A='0' and B = '0') then Y <= '1'; elsif C = '1' then Y <= '1'; else Y <= '0'; end if; end process; end behavioral;
24
4g.
Structural VHDL
Structural VHDL is just a netlist of connection and is usually at the top level of a VHDL design. It can replace a schematic. In fact Xilinx ISE toolscan generate a schematic from any structural description. Here is a simple example of structural VHDL code. It is representative of a netlist connecting basic logic components.
architecture structural of BUZZER2 is -- Component declarations component AND2 port (in1, in2: in std_logic;out1: out std_logic); end component; component OR2 port (in1, in2: in std_logic;out1: out std_logic); end component; component NOT1 port (in1: in std_logic;out1: out std_logic); end component; -- declaration of signals used to interconnect gates signal DOOR_NOT, SEATBELT_NOT, B1, B2: std_logic; begin -- Component instantiations statements U0: NOT1 port map (DOOR, DOOR_NOT); U1: NOT1 port map (SEATBELT, SEATBELT_NOT); U2: AND2 port map (IGNITION, DOOR_NOT, B1); U3: AND2 port map (IGNITION, SEATBELT_NOT, B2); U4: OR2 port map (B1, B2, WARNING); end structural;
component declarations
component connections
Example 10: Structural VHDL Following the header is the component declaration part that gives the components (in this case: gates) that are going to be used in the description of the circuits. In our example, we use a two- input AND gate, two-input OR gate and an inverter. The function of the components is defined in another vhdl module. The components can be stored in one of the packages that are referred to in the header of the file. The declarations for the components give the inputs (in1, in2) and the output (out1). Next, the internal nets (signal names) are defined. In our example these signals are called DOOR_NOT, SEATBELT_NOT, B1, B2.
25
The statements after the begin keyword gives the instantiations of the components and describes how these are interconnected. Each line starts with an instance name (e.g. U0) followed by a colon and a component name and the keyword port map. This keyword defines how the components are connected. In the example above, this is done through positional association. DOOR corresponds to the input, in1 of the NOT1 gate and DOOR_NOT to the output. Similarly, for the AND2 gate where the first two signals (IGNITION and DOOR_NOT) correspond to the inputs in1 and in2, respectively, and the signal B1 to the output out1.
26
5.
5a.
Constants
A constant can have a single value of a given type and cannot be changed during the simulation. A constant is declared as follows, constant list_of_name_of_constant: type [ := initial value] ; where the initial value is optional. Constants can be declared at the start of an architecture and can then be used anywhere within the architecture. Constants declared within a process can only be used inside that specific process.
constant state1: std_logic vector := "001"; constant rise_time: time:= 2 ns; constant data_bus_width: integer:= 16;
27
5b.
Variables
Variables can only be used inside a process. A variable can have a single value, as with a constant, but a variable can be updated using a variable assignment statement. The variable is updated without any delay as soon as the statement is executed. The variable declaration is as follows:
For example: variable test: boolean :=FALSE; variable result: integer range 0 to 256 :=16; variable count: bit_vector (7 downto 0);
The variable result, in the example above, is an integer that has a range from 0 to 256 with initial value of 16 at the start of the simulation. A variable can be updated using a variable assignment statement such as variable_name := expression;
Note: Care must be taken when variables are used in code which is to be synthesised. Variables are updated earlier than signals. This means that the synthesised circuit will be different to one which uses signals.
28
5c.
Signals
Signals are declared with the following statement: signal list_of_signal_names: type [ := initial value] ; For example: signal sum, carry: std_logic; signal address: integer :=0; signal data_bus: std_logic_vector (0 downto 7); signal temp: integer range 0 to 100;
Signals are updated when their signal assignment statement is executed, after a certain delay, as shown below, sum <= ( A xor B ); One can also specify multiple waveforms using multiple events as illustrated below, signal wavefrm : std_logic; wavefrm <= '0', '1' after 5ns, '0' after 10ns, '1' after 20 ns; Time values in signal assignment statements are not synthesisable but can be used in test benches. Signal initial values are also ignored in synthesis. It is important to understand the difference between variables and signals, particularly how it relates to when their value changes: A variable changes instantaneously when the variable assignment is executed, whereas a signal changes a delay after the assignment expression is evaluated. If no delay is specified, the signal will change after a 'delta delay'. Signals used in a process have implicit memory. A process is a queue of statements in time order. This is significant as it means that when a signal is assigned a value it only takes this value at some later point in time - even if there is no stated delay. This zero time delay is called delta delay. The actual delay will be determined by the implementation logic. In a process all signal values in the process are updated at the end of the process whereas variable values are updated instantaneously.
Note: If you are new to VHDL, in the first instance, use signals only.
29
30
6.
Concurrent Statements
In this section, we will use concurrent statements to describe behaviour. This method is usually called data flow modelling. The data flow modelling describes a circuit in terms of its function and the flow of data, between registers, through the circuit. Concurrent signal assignments are event triggered and executed as soon as an event on one of the signals occurs.
6a.
Sum <= (A xor B) xor Cin; Carry <= (A and B); Z <= (not X) or Y after 2 ns;
As soon as an event occurs on one of the signals, the expression will be evaluated. The type of the target signal has to be the same as the type of the value of the expression. Statements like "after 2ns" can be used for modelling but cannot be synthesised.
6b.
signal <= expression when condition else expression when condition else expression; The target signal will receive the value of the first expression whose Boolean condition is TRUE. If no condition is found to be TRUE, the target signal will receive the value of the final expression. If more than one condition is true, the value of the first condition that is TRUE will be assigned.
31
S0 S1
A B C D
MUX output
input ABCD
SELECT 0 0 0 1 1 0 1 1
output A B C D
entity MUX_4a is port (S1, S0, A, B, C, D: in std_logic; Z: out std_logic); end MUX_4a; architecture concurrent of MUX_4a is begin Z <= A when S1='0' and S0='0' else B when S1='0' and S0='1' else C when S1='1' and S0='0' else D; end concurrent;
Example 12: 4-to-1 Multiplexor using when-else The conditional signal assignment will be re-evaluated as soon as any of the signals in the conditions or expression change. The when-else construct is useful to express logic function in the form of a truth table.
32
An example of the same multiplexer as above is given below in a more compact form. entity MUX_4b is port (A, B, C, D: in std_logic; SEL: in std_logic_vector (1 downto 0); Z: out std_logic); end MUX_4b; architecture concurrent of MUX_4b is begin Z <= A when SEL = "00" else B when SEL = "01" else C when SEL = "10" else D; end concurrent;
Example 13: 4-to-1 Bus Multiplexor The condition in a selection statement can be a compound condition as shown in the next example of a four-to-one bus multiplexor. A bus in VHDL is, as we have seen, described as a vector. The width of the bus (the number of signals) is defined in the brackets after the port or signal declaration. As binary numbers start with the Most Significant Bit (MSB) the vectors are usually written (X downto 0) where X is the value of the bus width minus 1. (downto is a key word.)
library ieee; use ieee.std_logic_1164.all; entity MUX_4c is port(in0, in1, in2, in3 : in std_logic_vector(7 downto 0); s0, s1 : in std_logic; z : out std_logic_vector(7 downto 0)); end MUX_4c; architecture imp of MUX_4c is begin z <= in0 when (s0 = 0 and s1 = 0) else in1 when (s0 = 1 and s1 = 0) else in2 when (s0 = 0 and s1 = 1) else in3 when (s0 = 1 and s1 = 1) else "XXXXXXXX"; end imp;
33
Notes
The when-else statement does not have semicolons after the 'else'. The numerical values of vectors are contained within double quotes. The concurrent construct is simpler than the sequential if-then-else construct within a process statement.
34
Here is the truth table of a 8 Bit Priority Encoder. The input is an 8 bit bus, illustrated here as bit B7 downto B0. The output, F, a 3 bit bus, simply gives the position of the '1' on the input vector as a binary number: 8 bit INPUT B7 B6 B5 B4 B3 B2 B1 B0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 B7 B6 B5 B4 B3 B2 B1 B0 Priority Encoder 3 bit OUTPUT 2 0 0 F2 F1 F0 0 0 1 1 1 1
2
2 0 0 1 1 0 0 1 1
2 0 1 0 1 0 1 0 1
VHDL code for the priority encoder is shown below: library ieee; use ieee.std_logic_1164.all; entity priority is port ( B : in std_logic_vector(7 downto 0); F : out std_logic_vector(2 downto 0)); end priority; architecture imp of priority is begin F <= "000" when B(0) = 1 else "001" when B(1) = 1 else "010" when B(2) = 1 else "011" when B(3) = 1 else "100" when B(4) = 1 else "101" when B(5) = 1 else "110" when B(6) = 1 else "111" when B(7) = 1 else "---"; -- output is a "dont care" end imp;
35
The last example of the conditional selection statement when-else shows how easy it is to use VHDL to carry out simple arithmetic operations.
library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; entity addsub is port ( A, B : in std_logic_vector(7 downto 0); ADSUB : in std_logic; RES : out std_logic_vector(7 downto 0)); end addsub; architecture imp of addsub is begin RES <= A + B when ADSUB = 1 else A - B; end imp;
Note that for this arithmetic operation to work on a std_logic_vector, the library ieee.std_logic_unsigned.all; must be included.
36
6c.
with choice_expression select target_name <= expression when choice, target_name <= expression when choice, target_name <= expression when choice;
The target is a signal that will receive the value of an expression whose choice includes the value of the choice expression. The expression selected is the first with a matching choice. The choice can be static (e.g. 7) or a range (e.g. 2 to 5). The following rules must be followed for the choices:
entity MUX_4d is port (A, B, C, D: in std_logic; SEL: in std_logic_vector(1 downto 0); Z: out std_logic); end MUX_4d; architecture concur of MUX_4d is begin with SEL select Z <= A when "00", B when "01", C when "10", D when "11";
end concur;
37
The next example shows an arithmetic unit which uses with-select-when to select the arithmetic operations on bus inputs A and B chosen by the select input, sel. sel 3
8 A Arith Unit 8 B 8 F
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity arith_unit is port (A, B: in std_logic_vector(7 downto 0); sel: in std_logic_vector(2 downto 0); carry_in: in std_logic; F: out std_logic_vector(7 downto 0 )); end arith_unit; architecture Behavioral of arith_unit is begin with sel(2 downto 0) select F <= A when "000", A + 1 when "001", A - 1 when "010", B when "011", B + 1 when "100", B - 1 when "101", A + B when "110", A + B + carry_in when "111", "XXXXXXXX" when others; end Behavioral;
38
6d.
Tristate Output
This next example shows how to implement a tristate buffer using VHDL. Tristate buffers are simple devices which facilitate connection to a common bus. The buffer output can be set to high impedance when a connection is not required.
entity tristate is port (input, enable: in std_logic; output: out std_logic); end tristate; architecture concur of tristate is begin output <= input when enable = '1'; else 'Z'; end concur;
39
40
7.
Sequential Modelling
Concurrent modelling is easier to write but more prone to error in operation as the timing is not prescribed. In the above examples of concurrent modelling a when-else statement is used as a conditional statement. In most complex digital systems the timing of data transfer and computational events is critical to correct operation. To describe and model these systems we use the process construct and write behavioural - RTL VHDL to define sequential systems.
7a.
Process
The basis for sequential modelling is the process construct. As you will see, the process construct allows us to model complex digital systems, in particular sequential circuits. A process statement is the main construct in behavioural modelling. It allows you to use sequential statements to describe the behaviour of a system in time. The syntax for a process statement is label: process (sensitivity_list) is process_declarations begin -- sequential statements; end process label;
The statements for sequential modelling within a process are: signal assignments variable assignments if else elsif end if statements case when statement wait statement null statement loop statement next statement exit statement procedure call
41
A process is called once at the start of simulation, and thereafter when any of the signals in the sensitivity list changes. Simple conditional statements in a process infer combinatorial logic but conditional statements which are sensitive to a clock or statements which read an output (feedback) infer flip-flops or registers - sequential logic. In a sequential circuit the output depends not only on the present inputs but on the previous state of the circuit. This means that a sequential circuit must contain some sort of memory.
k present state
next state
Memory
Memory elements are implied when VHDL is written in a process where the assignment of signal values are dependent on a clock edge. In other words, for any coding where the value of a signal has to be stored or held, e.g. using wait or if-else, a storage element is inferred and the synthesis tool will implement a memory element. Variables and constants that are used inside a process have to be defined in the process declarations part before the keyword begin. The keyword begin indicates the start of the computational part of the process. The statements are executed sequentially. Note that variable assignments inside a process are executed immediately and denoted by the ":=" operator. This is in contrast to signal assignments denoted by "<=" and which changes occur after a delay. As a result, changes made to variables will be available immediately to all subsequent statements within the same process.
42
The simplest sequential circuit and the basic building block and storage element of sequential logic circuits is the D-type Flip Flop.
DFF
D 0
Q 0 1 0 1
Q+ 0 0 1 1
clock
The output Q changes to the value of the input D on the (usually rising) edge of the clock pulse. Q+ is called the "next state" of the flip flop. The characteristic equation of the flip flop is thus Q+ = D.
An example of a process architecture is shown here. A positive edgetriggered D flip-flop is shown. library ieee; use ieee.std_logic_1164.all; entity DFF_1 is port (CLK, D : in std_logic; Q : out std_logic); end DFF_1; architecture BEHAV_DFF of DFF_1 is begin DFF_PROCESS: process (CLK) begin if (CLK'event and CLK = '1') then Q <= D; end if; end process; end BEHAV_DFF;
Example 21: D Flip-flop using if-else On the positive (rising) edge of the clock, Q is assigned the value of D. The process will on run when there is a change in the value of the clock.
43
A process is declared within an architecture and is itself a concurrent statement. However, the statements inside a process are executed sequentially. The sensitivity list is a set of signals to which the process is sensitive. Any change in the value of the signals in the sensitivity list will cause immediate execution of the process. The single comma ' means attribute. 'event is an attribute of CLOCK. The expression (CLK'event and CLK = '1') checks for a positive clock edge.
library ieee; use ieee.std_logic_1164.all; entity DFF_1 is port (CLK, D : in std_logic; Q : out std_logic); end DFF_1; architecture BEHAV_DFF of DFF_1 is begin DFF_PROCESS: process begin wait until rising_edge(clk); Q <= D; end process; end BEHAV_DFF;
Example 22: D Flip-flop using wait If the sensitivity list is not specified, a process has to include one wait statement as the first line of the process to make sure that the process will halt. Notice that one cannot include both a sensitivity list and a wait statement. Library: std_logic_1164 also contains the "rising_edge" and "falling_edge" attributes which can also be used for synchronous timing. A process statement that is sensitive to the clock or waits for the clock edge infers edge sensitive memory and will usually synthesise into a flip flop. For negative or falling edge you can use (CLK'event and CLK = '0') A process that is level sensitive only will infer a latch not a flip-flop. Dtype flip flops general have asynchronous Reset (Q to '0' ) inputs. To add this to our code we simply add a few more lines.
44
library ieee; use ieee.std_logic_1164.all; entity DFF_1 is port (CLK, D, Reset : in std_logic; Q : out std_logic); end DFF_1; architecture BEHAV of DFF_1 is begin process (CLK) begin if Reset = '1' then Q <= '1' ; elsif (CLK'event and CLK = '1') then Q <= D; end if; end process; end BEHAV;
Example 23: D Flip-flop with asynchronous reset If we take our earlier BUZZER combinational example:
architecture behavioral of BUZZER is begin WARNING <= (not DOOR and IGNITION) or (not SBELT and IGNITION); end behavioral; T Then a synthesis tool will generate the following circuit: DOOR
IGNITION
WARNING
45
If we make the signal assignment dependent on a clock edge within a process, then the assignment of the output will only update on the rising clock edge.
architecture BEHAV_SEQ of SEQ_1 is signal F; std_logic; begin process (CLK) begin if (CLK'event and CLK = '1') WARNING <= (not DOOR and IGNITION) or (not SBELT and IGNITION); else 0; end if; end process; end BEHAV_SEQ;
IGNITION
WARNING
SEATBELT
CLOCK
This illustration shows how we can write RTL style VHDL with computational elements clocked in sequence through a logic system.
46
7b.
Example 25 MUX using if-else in a process Note that although this code uses if-else inside a process it does not describe a sequential circuit and the synthesis tools would infer a simple multiplexor.
47
if-elsif and case statements (see below) are commonly used for the implementation of sequential arithmetic and functional blocks in processes. Here is an example of a simple counter with enable and reset. Synthesis tools pick up statements like: count <= count + 1; to synthesise counters efficiently. 4 UpCounter count 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111 Clock count
en reset
library IEEE; use IEEE.std_logic_1164.all; use IEEE.std_logic_unsigned.all; use IEEE.std_logic_arith.all; entity upcounter is port ( reset, clock, en: in STD_LOGIC; count: inout STD_LOGIC_VECTOR (3 downto 0) ); end upcounter; architecture arch of upcounter is begin process (clock, reset. en) begin if reset='1' then count <= "0000"; elsif clock'event and clock='1' then if en ='1' then count <= count + 1; end if; end if; end process; end arch;
Notes To use arithmetic functions on std_logic signals, such as: count <= count + 1; the library ieee.std_logic_unsigned must be used. Signal count must be made inout as it has to read its own value in the same line else an internal signal can be used.
48
D7 clock Q7
D6
D5
D4
D3
D2
D1
D0 reset load
Q6
Q0
library ieee; use ieee.std_logic_1164.all; entity regn is generic (n: integer := 8); port ( D: in std_logic_vector (n-1 downto 0); clock, reset, enable : in std_logic; Q : out std_logic_vector( n-1 downto 0 )); end regn; architecture Behaviour of regn is begin process(clock, reset, enable) begin if reset=0 then Q <= ( others => 0); elsif (clockevent and clock=1) then if enable = 1 then Q <= D; else null; end if; end if; end process; end Behaviour;
Example 27: Generic Register Here is an 8 bit generic shift register. The declaration of the generic parameter, n, in the entity allows the width of the register to be controlled by one parameter. To set the data to a value the line: Q <= ( others => 0); is used. (others =>'0') simply means assign all the values in the output bus to '0'.
49
Shift Register Q clock dir enable serial_in n data_ out Here we have an 8 bit shift register. Very useful sequential building block. Constructed out of 1-bit registers. Used to perform either parallel to serial data conversion or serial to parallel data conversion.
Figure 16: Shift Register library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity Shift_Reg is port( enable: in STD_LOGIC; serial_in: in STD_LOGIC; dir: in STD_LOGIC; clock: in STD_LOGIC; data_out: out STD_LOGIC_VECTOR (7 downto 0)); end Shift_Reg; architecture Behavioral of Shift_Reg is signal reg: std_logic_vector(7 downto 0); begin process (CLOCK, enable,dir) begin if enable ='1' then if clock'event and clock='1' then if dir = '0' then reg(7 downto 0) <= '0' & reg(7 downto 1) ; else reg(7 downto 0) <= reg(6 downto 0) & serial_in; end if; end if; end if; end process; data_out <= reg; end Behavioral;
Example 28: 8-Bit Shift Register The bits in this register can be shifted to the right or left depending on the value of the control input: dir. Note the use of the concatenate operator: &. This is used to join two vectors together.
50
7c.
Case statement
The case statement executes one of several sequences of statements, based on the value of a single expression. The syntax is as follows, case expression is when choices => sequential statements; when choices => sequential statements; when others => sequential statements; end case; The expression must evaluate to an enumerated type of a onedimensional array, such as a std_logic_vector. The case statement evaluates the expression and compares the value to each of the choices. The when clause corresponding to the matching choice will have its statements executed. The following rules must be adhered to: no two choices can overlap (i.e. each choice can be covered only once) if the "when others" choice is not present, all possible values of the expression must be covered by the set of choices. entity MUX_4f is port ( SEL: in std_logic_vector(2 downto 1); A, B, C, D: in std_logic; Z: out std_logic); end MUX_4f; architecture behav of MUX_4f is begin process (SEL, A, B, C, D) begin case SEL is when "00" => Z <= A; when "01" => Z <= B; when "10" => Z <= C; when "11" => Z <= D; when others => Z <= 'X'; end case; end process; end behav;
Example 29: MUX using case statement The "when others" covers the cases when SEL = "0X", "0Z", "XZ", "UX", etc. It should be noted that these combinational circuits can be expressed in other ways, using concurrent statements such as the with select construct. Since the case statement is a sequential statement, nested case statements are possible.
51
architecture behv of ALU is begin process(A, B, opcode) begin if clk'event and clk = '1' then if reset ='1' then Accumulator <= (others => 'Z'); else case opcode is when "000" => Accumulator <= (others => '0'); when "001" => Accumulator <= A; when "010" => Accumulator <= A + 1; when "011" => Accumulator <= A - 1; when "100" =>Accumulator <= A + B; when "101" => Accumulator <= A and B; when "110" =>Accumulator <= A or B; when "111" => Accumulator <= (others => '1'); when others => Accumulator <= (others => 'X'); end case; end if; end if; end process; end behv;
52
7d.
Multiple Processes
The following example illustrates the use of more than one process. One process can use signals that will trigger another process when events on the signals in its sensitivity list occur. This example is a Full Adder, composed of two Half Adders: S_ha = (A B) and C_ha = AB For the Full Adder: Sum = (A B) Cin = S_ha Cin Cout = (A B) Cin + AB = S_ha.Cin + C_ha
P1 ints1 P2 Sum Half Adder2 ints3
A B
Half Adder1
ints2
Cout
Cin
library ieee; use ieee.std_logic_1164.all; entity Full_Add is port (A, B, Cin : in std_logic; Sum, Cout : out std_logic); end FULL_ADDER; architecture behav of Full_Add is signal ints1, ints2, ints3: std_logic; begin Proc1: process (A, B) begin ints1<= A xor B; ints2<= A and B; end process Pro1; Proc2: process (int1, int2, Cin) begin Sum <= ints1 xor Cin; ints3 <= ints1 and Cin; Cout <= ints2 or int3; end process Pro2; end behav;
53
54
8.
The VHDL file for the sequence detector is given on the next page. Note the use on an enumerated data type: Streg_type. This restricts the allowed state values of Streg to S1, S2, S3, S4. The next state is conditional only on the present state which is the value of Streg. The four possible states are accessed via the value of Streg within a case statement and the transition from each state is implemented using ifelsif-else statements. Alternatively the states assignment can be defined directly using the constant statement.
architecture arch of fsm is constant S1 : std_logic_vector(2 downto 0) := 000; constant S2 : std_logic_vector(2 downto 0) := 001; constant S3 : std_logic_vector(2 downto 0) := 011; constant S4 : std_logic_vector(2 downto 0) := 010; begin
55
library ieee; use ieee.std_logic_1164.all; entity seq_det is port (CLK, RST, Din: in STD_LOGIC; Z: out STD_LOGIC); end;
architecture arch of seq_det is type Streg_type is ( S1, S2, S3, S4 ); signal Streg: Streg_type; begin process (CLK) begin if CLK'event and CLK = '1' then if RST='1' then Streg <= S1; else case Streg is when S1 => if Din='0' then Streg <= S1; elsif Din='1' then Streg <= S2; end if; when S2 => if Din='1' then Streg <= S2; elsif Din='0' then Streg <= S3; end if; when S23 => if Din='1' then Streg <= S4; elsif Din='0' then Streg <= S1; end if; when S4 => if Din='0' then Streg <= S3; elsif Din='1' then Streg <= S2; end if; when others => null; end case; end if; end if; end process; -- signal assignment statements for combinatorial outputs Z <= '1' when (Streg = S4) else '0'; end arch;
56
8b
architecture process 1 case statement for state transitions process 2 Clock timing Sensitivity, Reset
begin -- the synchronous state transition process process (clk, reset) begin if (reset = 0) then present_state <= state0; elsif (clkevent and clk = 1) then present_state <= next_state; end if ; end process; -- state transition on clock edge
57
process (present_state, Din) -- start transitions begin case present_state is when state0 => if (Din = 0) then next_state <= state1; else next_state <= state0; end if ; when state1 => if (Din = 1) then next_state <= state2; else next_state <= state1; end if ; when state2 => if (Din = 1) then next_state <= state3; else next_state <= state1; end if ; when state3 => if (Din = 1) then next_state <= state0; else next_state <= state1; end if ; when others => next_state <= state0; end case; end process; end arch;
58
9.
library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; entity rom_32x4 is port ( Clk : in std_logic; en : in std_logic; -- Read enable addr : in std_logic_vector (4 downto 0); data : out std_logic_vector(3 downto 0)); end rom_32x4; architecture imp of rom_32x4 is subtype word is std_logic_vector (3 downto 0); type rom_type is array (31 downto 0) of word; constant ROM : rom_type := ("1001", "0010", "0011", "0100", "0101", "0110", "0111", "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111", "0001", "0010", "0011", "0100", "0101", "0110", "0111", "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111", "0000", "0010"); begin process (Clk) begin if (Clkevent and Clk = 1) then if (en = 1) then data <= ROM(conv_integer(addr)); end if; end if; end process; end imp;
This is an example of a 32 x 4 bit Read Only Memory. The memory contents are defined by the value of the constants, and are hardwired on implementation. Note that the index of the address must be an integer. The use of a subtype is optional. The ROM code can be synthesised, and would be implemented by logic blocks.
59
library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; entity ram_16x8 is port ( Clk : in std_logic; we : in std_logic; -- Write enable en : in std_logic; -- Read enable addr : in std_logic_vector(4 downto 0); di : in std_logic_vector(7 downto 0); -- Data in do : out std_logic_vector(7 downto 0)); -- Data out end ram_16x8; architecture impl of ram_16x8 is type ram_type is array (15 downto 0) of std_logic_vector (7 downto 0); signal RAM : ram_type; begin process (Clk) begin if (Clkevent and Clk = 1) then if (en = 1) then if (we = 1) then RAM(conv_integer(addr)) <= di; do <= di; else do <= RAM(conv_integer(addr)); end if; end if; end if; end process; end impl;
This is an example of a 16 x 8 bit read-write RAM. Note that the index of the address must be an integer, thus conversion functions must be used on the address vector. The RAM code can be synthesised, and would be implemented in Block RAM.
60
library ieee; use ieee.std_logic_1164.all; entity shiftreg is port ( clock : in std_logic; serial_in: in std_logic; serial_out : out std_logic); end shiftreg; architecture impl of shiftreg is signal temp : std_logic_vector(7 downto 0); begin process (clock) begin if (Clkevent and Clk = 1) then for i in 0 to 6 loop temp(i + 1) <= temp(i); end loop; tmp(0) <= serial_in; end if; end process; serial_out <= temp(7); end impl;
Example 38: Shift Register using loop statement This is an example of an 8 bit right shift register, coded using a loop statement. Loop statements can only be used in a process. (Outside a process the generate statement can be used for a similar operation.) The loop statement is a succinct method for the description of repetitive structures. The number of iterations must be defined. The loop is expanded at compile time.
61
entity Count_1s is generic (width: integer := 8); port (datain: in std_logic_vector(width - 1 downto 0); count: out std_logic_vector(3 downto 0)); end Count_1s; architecture algorithm of Count_1s is signal temp: std_logic_vector(3 downto 0) ; begin process(datain) variable temp : unsigned(3 downto 0); begin temp := "0000"; for i in 0 to (width-1) loop if datain(i) = '1' then temp := temp + '1'; else null; end if; end loop; count <= std_logic_vector (temp); end process; end algorithm;
Example 39: One's Counter This is an example of one's counter using a loop statement. The algorithm will return the number of '1's in a binary number. e.g. "01011011" would return 5. The code requires the use of a variable in the loop statement as a signal would not update in each iteration. Note the width of the input vector is controlled by a generic parameter.
62
10.
Structural Modelling
Structural modelling was described briefly in the section Structural Modelling in Section 3: "Basic Structure of a VHDL file". Structural modelling describes a circuit in terms of components and its interconnection. There is no behavioural representation. Each component has to predefined (e.g. in the architecture or in a package) and can be described as structural, a behavioural or dataflow model. At the lowest hierarchy each component is described as a behavioural model, using the basic logic operators defined in VHDL. In general, structural modelling is very good to describe the top level of complex digital systems, through a set of components in a hierarchical fashion. A structural description can best be compared to a schematic block diagram that can be described by the components and the interconnections. VHDL provides a formal way to do this by: Declaring a list of components being used, Declaring signals which define the nets that interconnect , Labelling multiple instances of the same component so that each instance is uniquely defined.
The components and signals are declared within the architecture body, architecture architecture_name of NAME_OF_ENTITY is component declarations signal declarations begin component instantiation and connections : end architecture_name;
63
10a.
Component declaration
Before components can be instantiated they need to be declared in the architecture declaration section or they can be declared in a separate vhd file called a package. The component declaration consists of the component name and the interface (ports). The syntax is as follows: component component_name is port ( port_signal_names: mode type; port_signal_names: mode type; port_signal_names: mode type); end component component_name The component name refers to either the name of an entity defined in a library or an entity explicitly defined in the VHDL file (see example of the four bit adder). The list of interface ports gives the name, mode and type of each port, similarly as is done in the entity declaration. A few examples of component declarations follow: component OR2 port (in1, in2: in std_logic; out1: out std_logic); end component; component PROC port (CLK, RST, RW, STP: in std_logic; ADDRBUS: out std_logic_vector (31 downto 0); DATA: inout integer range 0 to 1024); end component; component FULLADDER port(a, b, c: in std_logic; sum, carry: out std_logic); end component;
As mentioned earlier, the component declaration has to be done either in the architecture body or in a package. If the component is declared in a package, one does not have to declare it again in the architecture body as long as one uses the library and use clause to allow access to the package.
64
10b.
component NAND2 port (in1, in2: in std_logic; out1: out std_logic); end component; signal int1, int2: std_logic; architecture struct of EXAMPLE is U1: NAND2 port map (A,B,int1); U2: NAND2 port map (in2=>C, in2=>D, out1=>int2); U3: NAND3 port map (in1=>int1, int2, Z); end
65
A further complete hierarchical structural example of a One's Counter is given below and in the following pages:
A2 0 0 0 0 1 1 1 1
A1 0 0 1 1 0 0 1 1
A0 0 1 0 1 0 1 0 1
C2 0 0 0 1 0 1 1 1
C1 0 1 1 0 1 0 0 1
count_1s
sub_1
sub_2
and2
or3
and3
or4
inv
66
use work.all; entity count_1s is port (A: in BIT_VECTOR(2 downto 0); B: out BIT_VECTOR(1 downto 0)); end count_1s; architecture struct of count_1s is component sub_1 port (X: in BIT_VECTOR(2 downto 0); Z: out BIT); end component; component sub_2 port (X: in BIT_VECTOR(2 downto 0); Z: out BIT); end component; begin COMPONENT_1: sub_1 port map (A,B(1)); COMPONENT_2: sub_2 port map (A,B(0)); end struct;
use work.all; entity sub_1 is port (X: in BIT_VECTOR(2 downto 0); Z: out BIT); end sub_1; architecture andor of sub_1 is component AND2 port (Ip1,Ip2: in BIT; Op: out BIT); end component; component OR3 port (Ip1,Ip2,pI3: in BIT; Op: out BIT); end component; signal A1,A2,A3: BIT; begin D1: AND2 port map (X(0),X(1),A1); D2: AND2 port map (X(0),X(2),A2); D3: AND2 port map (X(1),X(2),A3); D4: OR3 port map (A1,A2,A3,Z); end andor;
67
use work.all; entity sub_2 is port(X: in BIT_VECTOR(2 downto 0); Z: out BIT); end sub_2; architecture andorinv of sub_2 is component AND3 port(Ip1,Ip2,Ip3: in BIT;Op: out BIT); end component; component OR4 port(Ip1,Ip2,Ip3,Ip4: in BIT;Op: out BIT); end component; component INV port(Ip: in BIT;Op: out BIT); end component; signal T0,T1,T2,A1,A2,A3,A4: BIT; begin D5: INV port map(X(0),T0); D6: INV port map(X(1),NT1); D7: INV port map(X(2),T2); D8: AND3 port map(X(2),T1,T0,A1); D9: AND3 port map(T2,T1,X(0),A2); D10: AND3 port map(X(2),X(1),X(0),A3); D11: AND3 port map(T2,X(1),T0,A4); D12: OR4 port map(A1,A2,A3,A4,Z); end andorinv;
68
entity AND2 is port (Ip1,Ip2: in BIT; Op: out BIT); end AND2; architecture BEHAV of AND2 is begin Op <= Ip1 and Ip2; end BEHAV; entity OR3 is port (Ip1,Ip2,Ip3: in BIT; Op: out BIT); end OR3; architecture BEHAV of OR3 is begin Op <= Ip1 or Ip2 or Ip3; end BEHAV; entity AND3 is port(Ip1,Ip2,Ip3: in BIT;Op: out BIT); end AND3; architecture BEHAV of AND3 is begin Op<=Ip1 and Ip2 and Ip3; end BEHAV; entity OR4 is port(Ip1,Ip2,Ip3,pI4: in BIT;Op: out BIT); end OR4; architecture BEHAV of OR4 is begin Op<=Ip1 or Ip2 or Ip3 or Ip4; end BEHAV; entity INV is port(Ip: in BIT;Op: out BIT); end INV; architecture BEHAV of INV is begin Op<= not Ip; end BEHAV;
69
70
11.
designer. The tool then runs ModelSim in the background and gives a graphical output confirming results or showing errors. The test bench waveform is only recognised by Xilinx software. The more general way to write a test bench is using VHDL. The VHDL code generated by HDL Bencher can be viewed in ISE by selecting: View Behavioural Testbench. Remember when writing any test bench to check all input combinations for complete verification of DUT operation.
11a.
Introduction
After writing the VHDL code for a certain system and before processing the design, however, we should take the time to verify that the code actually does what it is intended to do, by running a simulation. VHDL can be used as a powerful test stimulus language. VHDL code which is written to stimulate and verify a VHDL Design is called a Test bench. Test benches are VHDL descriptions of circuit stimuli and corresponding expected outputs that verify the behaviour of a circuit over time. They should be an integral part of any VHDL project and should be created together with other description of the system. The easiest way to understand the concept of a test bench is to think of it as a virtual circuit tester. This tester, which you will describe in VHDL, applies stimulus to your design description and (optionally) verifies that the simulated circuit does what is intended to do. After creating one or more test benches as part of your design specification, you will need to use a simulator to apply the test bench to your design as it was originally written. This simulation will ensure that the design operates as expected. This type of simulation is called functional simulation, which will uncover most logical errors in the design. A Test Bench is VHDL code which includes the Design Under Test ( DUT sometimes called UUT) as a structural element within the code. The VHDL test bench has no ports. The basic structural element of a Test bench is shown below.
71
72
entity (empty)
architecture
DUT: component declaration internal signal declarations begin DUT: Port Map
end arch
73
11b.
Testbench Structure
When writing test benches, you will most likely use a broader range of language features. The simplest test benches are those that apply some sequence of inputs to the Device Under Test (DUT) so that its operation can be observed in simulation. Such a test bench must consist of a declaration for a test component. In what follows, we will discuss the main structure and features of this test component: Empty entity declaration. The entity declaration of the test component does not include an interface (port) list. Local signals. The architecture of the test component has declarations for local signals. These signals are used to apply inputs to the DUT and observe the behaviour or the output during simulation. DUT instantiation. The architecture for the test bench should use the structural level of abstraction (in the form of port mapping statements) to connect the low-level (previously top level) design description to the other parts of the test bench (including signals and components). It is worth noticing that to the simulator, there is no distinction between those parts of the design that are being tested and the test bench itself. Process statement. To apply stimulus to the design, the test bench will probably be written using one or more sequential processes. A process that is intended for testing will normally have no sensitivity list. Instead it will have a series of signal assignments and wait statements. Signal assignments wait statements. The wait statements provide a specific amount of delay between each new combination of inputs for the DUT to stabilize between the assignments of test inputs. This process statement will be used to apply a sequence of input values (the actual stimulus) to a low-level circuit and (if desired) check the state ot that circuit's outputs at various points in time. Transport statements. There are two ways to write stimulus vectors: using wait statement (as just explained) or using transport statements. Transport-based test benches are smaller and easier to read than waitbased test-benches, but wait-based test benches are easier to understand when simulation is single stepped for debugging. Assert statements. Assert statements are used to verify that the DUT is operating correctly for each combination of inputs. In case of fallacy operation, the text you have specified in the optional report statement clause is displayed on your simulator's window.
74
Multiple processes. Separate processes running in the background can be used to generate some useful signals like clocks. Loop statement. You will probably use VHDL's looping features to simplify the description of repetitive stimulus (such as the system clock). Loops can be used to apply inputs and monitor outputs over potentially long periods of time. Using file I/O. Storing the test data in files can reduce the time required to add or modify test data. In this case, the test bench does not have to be recompiled when test stimulus is added or modified. You should plan to create test benches that are re-usable, by developing a master test bench that reads test data from a file. You may also need to write the simulation results to a disk file for later analysis.
75
11c
USE IEEE.STD_LOGIC_ARITH.ALL; USE IEEE.STD_LOGIC_UNSIGNED.ALL; ENTITY testbench IS END testbench; ARCHITECTURE testbench_arch OF testbench IS COMPONENT warning PORT ( Ignition : In std_logic; SBelt : In std_logic; Door : In std_logic; Warning : Out std_logic ); END COMPONENT; SIGNAL Ignition : std_logic; SIGNAL SBelt : std_logic; SIGNAL Door : std_logic; SIGNAL Warning : std_logic; BEGIN UUT : warning PORT MAP ( Ignition => Ignition, SBelt => SBelt, Door => Door, Warning => Warning PROCESS BEGIN -- -------------------Ignition <= transport '0'; SBelt <= transport '0'; Door <= transport '0'; -- -------------------WAIT FOR 200 ns; -- Time=200 ns Ignition <= transport '1'; -- -------------------WAIT FOR 100 ns; -- Time=300 ns SBelt <= transport '1'; -- -------------------WAIT FOR 100 ns; -- Time=400 ns Door <= transport '1'; -- -------------------WAIT FOR 100 ns; -- Time=500 ns Ignition <= transport '0'; -- -------------------END PROCESS; END testbench_arch; Example 44: VHDL Test Bench
);
76
PROCEDURE CHECK_Warning ( next_Warning : std_logic; TX_TIME : INTEGER ) IS VARIABLE TX_LOC : LINE; BEGIN IF (Warning /= next_Warning) THEN STD.TEXTIO.write(TX_LOC,string'("Error at time=")); STD.TEXTIO.write(TX_LOC, TX_TIME); STD.TEXTIO.write(TX_LOC,string'("ns Warning=")); END IF; END; Example 45: Simplified Check procedure PROCESS BEGIN -- -------------------Ignition <= transport '0'; SBelt <= transport '0'; Door <= transport '0'; -- -------------------WAIT FOR 200 ns; -- Time=200 ns Ignition <= transport '1'; CHECK_Warning('1',200); -- -------------------WAIT FOR 100 ns; -- Time=300 ns SBelt <= transport '1'; CHECK_Warning('0',300); -- -------------------WAIT FOR 100 ns; -- Time=400 ns Door <= transport '1'; CHECK_Warning('0',400); -- -------------------WAIT FOR 100 ns; -- Time=500 ns Ignition <= transport '0'; CHECK_Warning('1',500); -- -------------------WAIT FOR 100 ns; -- Time=600 ns SBelt <= transport '0'; CHECK_Warning('0',600); -- -------------------WAIT FOR 100 ns; -- Time=700 ns Door <= transport '0'; CHECK_Warning('1',700); -- -------------------WAIT FOR 150 ns; -- Time=850 ns -- -------------------END PROCESS
77
78
79
80
81
82