Sei sulla pagina 1di 314

Advanced Electronic Design Automation

for elektroda people


Advanced Electronic Design Automation

Chapter 1 - Introduction to Electronic Design Automation & SPICE


Concepts

Chapter Information

This chapter contains the following sections:

PART 1 Introduction to EDA PART 2 SPICE Concepts

1.1 The need for EDA 1.7 The role of circuit simulators
1.8 A brief history of SPICE
1.2 Hardware description languages 1.9 Circuit components
1.10 Coding a circuit for simulation
1.3 The design process 1.11 Simulating a circuit

1.4 Semi-custom design tools 1.12 Types of analysis


1.12.1 DC analysis
1.4.1 Design entry
Self assessment questions
1.4.2 Design verification
SPICE Exercise 1
1.4.3 Design layout
Further study

1.5 Full custom design tools


1.12.2 AC analysis
1.5.1 Design entry
Self assessment questions
1.5.2 Design layout
SPICE Exercise 2
1.5.3 Design verification
Further study

1.6 Low and high level tools


1.12.3 Transient analysis
Self assessment questions
Self assessment questions
Further study
SPICE Exercise 3
Further study

1.13 Obtaining results


1.13.1 DC convergence problems
1.13.2 transient analysis problems
PART 1 Introduction to Electronic Design Automation

1.1 The Need for Electronic Design Automation

The early integrated circuits were simple designs comprising a small number of transistors and limited interconnect. Design
took place at the layout level by manually constructing the artwork for each layer using black tape on transparent sheets.
These were then photographed to produce the masks for fabrication.

As circuit densities began to increase it became apparent that some degree of automation would be needed to manage the
ever increasing complexity inherent in the design process.

Three distinct groups of CAD tools emerged to assist in the management of this complexity.

Design Entry Tools

Intel’s first microprocessor was drawn on paper sheets laid out on the floor of a rented aircraft hanger! An interesting
approach for a relatively modest design by today’s standards but inadequate for later developments in technology. Design
entry tools enable a design to be partitioned into a number of manageable blocks that define a hierarchy from system level
down to component level.

Design Verification Tools

Implicit in all circuits of whatever complexity is the potential for design errors. Design verification tools enable the functionality,
performance and testability of a design to be evaluated prior to fabrication. This is essential in ensuring that the design will
work first time and will remain robust and resilient in service under all operating conditions.

Design Layout Tools

Defining circuit structures at the layout level is particularly complex. A detailed knowledge of device topologies and associated
process technologies is required. Design layout tools incorporate a set of process related design rules to ensure that the
required structures can be successfully manufactured within the limits of the intended process technology.

1.2 Hardware Description Languages

Hardware Description Languages

Prior to the advent of CAD tools with graphic capabilities designers were restricted to specifying a design in a textual manner
using a Hardware Description Language (HDL). This can be thought of as a parts list identifying the components required for
the circuit and a wiring list defining the component connections.

Example – Single Bit Half Adder


A typical description for this circuit in a HDL could be:

The CIRCUIT header identifies a circuit named HALFADD with inputs/outputs A, B, SUM and CARRY.

The parts list defines three components INV, AND2 and OR2 with their respective pin names.

The wiring list defines each gate by its name, type and wires to be connected to its pins.

CAD tool vendors provided their own proprietary HDL for use with a particular technology and although there are still
examples of this type of HDL around in some of the older packages, schematic capture techniques, when introduced, quickly
became more popular.

Recently however HDLs have enjoyed a resurgence due to the introduction of behavioural languages such as VHDL and
Verilog. Whilst both languages have the capability of defining a circuit at the component level (referred to as a Structural
description) their real power comes from their ability to synthesise a circuit from a technology independent functional
description.

Example – Single Bit Half Adder circuit described functionally in VHDL


The entity block is essentially providing the same information as the circuit header in the previous example.

The architecture block, however, contains no information about the logic gates or connections that comprise the circuit.
Instead it uses logical operators to define the required functionality.

There has always been a debate about schematic capture versus HDL. Experienced users of an HDL would claim it to be a
faster method of specifying a design.

For example the definition of a 20 bit shift register by schematic capture would require the placement of 20 flip flops in the
circuit. An HDL with a loop construct could achieve the same result in a couple of lines of text.

Schematic capture packages, however, have a shorter learning curve than HDLs and design specification errors such as
wrong components, incorrect wiring etc. are generally easier to spot.

1.3 The Design Process

Two routes through the design process can be identified - one for semi-custom design and one for full custom design.
Although the methodology involves design entry, verification and layout for both design styles the specific tools required and
their place in the process will be technology dependent.

1.4 Semi-Custom Design Tools

1.4.1 Design Entry

Schematic and HDL Capture


Schematic capture packages enable the
designer to define a circuit and its interconnect
using circuit symbols associated with the
available components in the cell library for a
particular technology. HDL capture defines the
circuit symbols and interconnect using a textual
language.

Netlist Generation
A design specified either schematically or
textually needs to be compiled into a netlist for
input to a simulator or layout tool. Historically
each CAD vendor provided their own format for
the netlist. More recently, however, a number of
standard formats have emerged so that designs
can be transferred between design systems.
One of the most popular is EDIF (Electronic Data
Interchange Format). If the design contains
analogue components then the netlist is often in
a form suitable for input to a circuit simulator
such as SPICE.

1.4.2 Design Verification

Simulation
Simulators enable the operation of the circuit
constructed during design entry to be analysed.

If the design contains only analogue components


then a circuit simulator such as SPICE is
required. SPICE operates at the transistor level
solving a series of equations that define the
component voltages and currents. Complex
mathematical models for transistors are required
to accurately represent their physical
characteristics. A set of device parameters
supplied by the foundry enables these models to
be customised for each process technology.
Circuit simulators can consume large amounts of
computing power, do not always converge to a
result and are best suited to the circuits of
individual components rather than complete
designs.

Designs containing only digital components require a logic simulator. This type of simulator operates at the logic element level
using models that define the function and propagation delay for each logic component. As with circuit simulators these models
are supplied by the manufacturer for a particular device or process. The simulator also requires a comprehensive set of user-
generated test patterns to define the input signal states and timing so that the design can be verified in all its intended
operating modes.

Mixed signal designs containing both analogue and digital components require a mixed mode simulator. One approach is to
use a circuit simulator and model the digital components at a functional level. Another approach is to use a logic simulator and
model the analogue components at a functional level. A more accurate approach is to use a circuit simulator for the analogue
components linked to a logic simulator for the digital components.

Simulation results at this stage in the process can include an estimate of the loaded switching delays of the components but
not the additional delays incurred by the interconnect. This is available only when the layout stage is complete.

Timing Verification
Timing verifiers and analysers enable the propagation delays through each branch of the circuit to be determined. This is
particularly useful in cases where the design has to meet a stringent time specification and excessive delays need to be
reduced. Checks for snigs, glitches and timing violations on flip flops are often also included.

Fault Simulation
Fault simulators check that possible circuit faults arising out of manufacturing defects can be detected at the outputs by a
suitably designed set of test patterns applied at the inputs. The technique involves running a normal (fault free) simulation and
capturing the state of the outputs for each input pattern. The simulator is then run in fault mode systematically setting each
node in the circuit to a permanent logic 1 (stuck at 1) or logic 0 (stuck at 0) and the outputs re-captured. The two sets of
results are compared. A difference in output observed for a given fault equates to a detectable fault.

A fault dictionary of detectable and non-detectable faults is thus compiled, enabling the designer to refine the test patterns
until maximum fault cover is achieved. Test patterns can be generated by hand or automatically by a test pattern generator
which analyses the construction of the circuit and derives suitable input signals.

1.4.3 Design Layout

Placement
Placement involves the allocation of cells on the silicon in optimum proximity so that the resultant routing is kept as short as
possible. For digital circuits this process is implemented automatically although some design systems provide a layout editor
to enable the designer to make manual adjustments. For analogue and mixed signal circuits the allocation of the analogue
cells has to be done manually using the layout editor.

Routing
Routing involves the specification of the interconnect between the cells. Again for digital circuits this is accomplished
automatically. For analogue and mixed signal circuits the routing of the analogue cells is implemented manually using a
routing editor. Some design systems incorporate a routing verifier that checks any manually defined routing against the
interconnect defined on the schematic during design entry.

Upon completion of the layout process additional timing information relating to the interconnect delays is back annotated into
the simulation database to enable a post layout simulation to be performed. Results from this give the most accurate
representation of the performance of the device when manufactured.

Manufacturing Preparation
The manufacturing process is technology dependent. For programmable logic devices the output of the design system
provides programming data to configure the device. For gate array, analogue array and standard cell devices the design
system provides the necessary artwork required by the foundry for mask definition. This information will be specified in a
standard format, such as CIL (Caltech Intermediate Format), for interfacing with the foundry equipment.

1.5 Full Custom Design Tools

1.5.1 Design Entry

Cell Definition
In a full custom process each component (digital
or analogue) has to be defined as a transistor
level cell. Typically this can be accomplished in
one of three ways. Working at the physical level
the cell can be defined as a set of polygons that
define the individual masks required to construct
the cell layout. Alternatively the circuit can be
expressed as a series of lines or sticks that are
later automatically fleshed out to their polygon
equivalents. A third approach is to define the cell
as a conventional transistor circuit diagram from
which is automatically synthesised the required
layout information.

1.5.2 Design Layout

Cell Placement
The cells defined in the design entry stage are
assembled into suitable positions on the silicon
using a layout editor. The designer has complete
freedom to place a cell anywhere on the chip
although typical full custom layouts tend to follow
those of gate array and standard cell topologies
with cells arranged in rows separated by routing
channels.

Cell Routing
The layout editor also permits the connection of
cells together by defining the required metal
interconnect. Particular attention must be paid to
track widths and spacings so that the design
rules for the chosen process are not violated.

1.5.3 Design Verification

Simulation
During cell construction each cell must be
simulated to verify correct functional operation
and to extract appropriate timing information for
later use when the complete design is simulated.
This is normally accomplished using a circuit
simulator such as SPICE.
When cells are combined during the cell layout
stage the developing design can be simulated
with a logic simulator to ensure correct functional
operation. The simulator will require models for
each of the cells in the design to specify the
timing and functional information extracted by
the circuit simulator during the cell definition
stage.

Design Rule Check


Before releasing the artwork for manufacture the
layout must be checked against the process
design rules to identify any violations. The
design rules specify a set of minimum sizes,
spacings etc. for the features on each mask in
the process and for the interaction of these
features when the masks are overlaid.

Manufacturing Preparation
In a full custom process the design takes place
at the layout level so the mask data is directly
available for conversion to a suitable standard
format.

1.6 Low and High Level Tools

Each EDA tool in an ASIC design system requires time and effort on the part of the designer to achieve a workable level of
operating competence. Some tools are relatively simple to use, others require considerable additional skill, knowledge and
expertise to operate successfully. We might classify the former as low level and the latter as high level.

The automated parts of the process such as placement and routing together with design entry by schematic capture for
example could be classified as low level.

Conversely the use of circuit simulators requires a knowledge of how components are modelled and analysed, design entry
and synthesis using VHDL requires competence in the use of the language whilst an understanding of fault simulators
provides the key to achieving maximum testability for a design. This set of tools we could classify as high level.
This module considers the high level tools in greater detail as a necessary pre-requisite for utilising the design systems
featured in later digital and analogue design modules.

Further Study

More information on EDA tools not covered specifically in this module can be found in the following sections of Application
Specific Integrated Circuits by M.J.S. Smith.

Schematic Entry & Netlist Generators Section 9.1


Logic Simulators Sections 13.4 &13.7
Timing Analysers Section 13.7
Placers Section 16.2
Routers Section 17.2
Design Rule Checkers Section 17.4.2
Mask Preparation Section 17.4.3.

PART 2 SPICE Concepts

1.7 The Role of Circuit Simulators

For many electrical and electronic engineers the idea of simulating a circuit will be a totally new concept. Significant benefits,
however, can result from the introduction of the technique to many aspects of the design, development and test process.

The Design Engineer is offered the opportunity to verify the correctness of the design and optimise its performance prior to the
construction of the prototype. It is generally much easier to adjust a model of the design as it exists on a CAD system rather
than modify the actual hardware. The resultant savings in time and money iterating towards a perfectly working circuit can
therefore be quite considerable.

The Development Engineer can experiment with variants to the design more quickly and cost effectively than by constructing
each hardware equivalent.
The Test Engineer can analyse the behaviour of the circuit, assess its resilience to changes in operating conditions and
formulate an appropriate testing strategy.

Many engineers have found simulation a very useful tool in developing an understanding of electrical and electronic principles.
Every aspect of a circuit’s operation is accurately portrayed and unexpected effects are often revealed, much to the surprise
of the designer!

Circuit simulators are essential in the integrated circuit industry. The requirement to achieve a right-first-time design is
paramount in saving time and money. In a semi custom methodology we see them being used to simulate analogue library
components both in mixed signal and analogue arrays. In a full custom methodology they are indispensable in verifying the
correct functionality of both the analogue and digital cells constructed by the designer.

A note of caution, however, should be sounded in relation to the use of circuit simulators for analogue IC designs. Digital
components and circuits are very predictable in their operation and logic simulators can be expected to accurately predict the
operation and performance of the design when manufactured. Analogue circuits are less predictable and circuit simulators will
not capture every possible effect that can occur on the silicon, particularly when digital components are also present. For this
reason it is wise to plan for at least one iteration at the foundry in order to correct any unexpected manufacturing effects.

1.8 A Brief History of SPICE

SPICE is an acronym for Simulation Program with Integrated Circuit Emphasis and was inspired by the need to accurately
model devices used in integrated circuit design. It has now become the standard computer program for electrical and
electronic simulation. The majority of commercial packages are based on SPICE2 version G6 from the University of California
at Berkeley although development has now progressed to SPICE3. The increased utilisation of PCs has led to the production
of PSPICE, a widely available PC version distributed by the MicroSim Corporation whilst HSPICE from Meta-Software has
been popular for workstations and is now also available for the PC.

1.9 Circuit Components

SPICE will simulate a circuit containing any of the following circuit components:

● Resistors
● Capacitors – linear and non-linear
● Inductors – linear and non-linear
● Mutual Inductors
● Voltage and Current Sources
● Dependent Voltage and Current Sources – linear and non-linear
● Transmission Lines
● Diodes – Junction and Schottky
● Bipolar Transistors – NPN and PNP
● Junction and MOSFETS – N channel and P channel

Voltage sources can be of the following types:

● Sinusoidal
● Pulse
● Exponential
● Frequency Modulated
● Piecewise Linear

The latter type enables irregular waveforms such as sawtooth and triangular etc. to be defined by dividing the waveform into
sections and specifying each section individually.

1.10 Coding a Circuit for Simulation

Traditionally, circuits to be simulated with SPICE have been coded as text files that define component types, values and
connectivity, analysis requirements and display options.

Example:

A typical SPICE input file, often referred to as a Spice Input Deck, for the above circuit would be as follows. Lines beginning
with a full stop imply control information rather than circuit information.
Notes

● The file must begin with a title line.


● Comments can be inserted if preceded by an asterisk.
● Component names are defined with a mandatory initial letter (e.g. V for voltage sources, R for resistors etc.) followed
by any other combination of letters or numbers. Signal sources often require additional information (e.g. SIN (0 250M
50K) defines a sine wave with 0 volts dc offset, 250 mV peak amplitude (SPICE operates on peak values) and a
frequency of 50 KHz.
● Component connections are defined by the circuit nodes connected to their terminals.
● Component values are defined with a value and a scaling factor (e.g. M for milli, K for kilo etc.).
● Analysis requirements are defined with a keyword and appropriate parameters (e.g. TRAN 2U 10M defines a transient
analysis lasting 10 milliseconds with values calculated every 2 microseconds).
● Display options define the type of output (e.g. PRINT for a tabular listing, PLOT for a graphical plot) and the nodes to
be displayed (e.g. V(2,0) – the voltage between nodes 2 and 0, I(R1) – the current through R1).
● The file must terminate with the END statement.

1.11 Simulating a Circuit

Most current versions of SPICE will enable a circuit to be defined using a schematic capture package. The user will be
required to specify component values and types of analysis but instance names and node numbers are automatically
assigned by the software. In some systems the circuit is then translated into the equivalent input deck prior to simulation;
others such as CADENCE input directly to the simulator.

The simulation process occurs in three stages:

1. Create the input deck with a text editor or the schematic with a schematic capture package.
2. Submit the circuit to the SPICE simulator.
3. Display the output results.

Results can be tabulated numerically in columns, one column for each of the specified signals to be output.

Early versions of SPICE offered a very crude method of plotting results using essentially a text terminal to display the points in
a graph as asterisks. Modern versions use sophisticated graphics packages to display and manipulate the results.

Design systems offering a schematic entry facility will also provide a series of menus to enable the analysis options to be
defined and the signals for display to be selected.

1.12 Types of Analysis

There are three basic types of analysis that SPICE can perform on a circuit, namely DC, AC and Transient. Selecting the most
suitable is dependent on the nature and application of the circuit, the components used and the requirements of the design
engineer. Analysis requirements and associated parameters are defined in the input deck by using appropriate control
statements.

1.12.1 DC Analysis

In DC analysis the values of all the dc voltages and currents in the circuit are calculated in response to the applied voltage
and current sources.

There are four options available:

Operating (Bias) Point Analysis

Determines the stable operating point of the circuit and calculates the corresponding dc voltages and currents
at that point.

Example Control Statement

.OP

This command instructs SPICE to display the voltages at all nodes in the circuit, the currents through all
components and their power consumption.

Transfer Curves Analysis

Calculates the dc voltages and currents in a circuit as a voltage/current source is varied over a specified range.
The values of voltages/currents at specified nodes in the circuit can be plotted as a function of the varied
voltage or current.

Example Control Statement

.DC ISOURCE 0M 10M 0.5M

Sweep the dc current source ISOURCE from 0 mA to 10 mA in steps of 0.5mA

Small Signal Transfer Function Analysis

Calculates the gain between a specified output voltage/current and an input voltage/current source and the
corresponding input and output resistances.

Example Control Statement

.TF V(2) VIN


Calculate the gain between the voltage at node 2 and input voltage source VIN (i.e.V(2)/VIN)
Calculate the input resistance at VIN
Calculate the output resistance at V(2)

Sensitivity Analysis (not available in SPICE3)

Calculates the sensitivity of specified voltages/currents to variations in circuit component values. The
sensitivity is calculated by:

1) Increasing each circuit element by a unit value (e.g. volt, amp, ohm etc.) – Element Sensitivity
2) Increasing each circuit element by 1% - Normalised Sensitivity

Example Control Statement

.SENS V(1,2) IBIAS

Calculate the sensitivity of the voltage between nodes 1 and 2 and the current through current source IBIAS

Temperature Analysis

Re-calculates the voltages and currents at the specified temperatures. The default temperature is 27oC.

Example Control Statement

.TEMP -55 25 125

Repeat the simulation at temperatures of -55oC, 25oC and 125oC

SPICE Exercise 1

If you have not previously studied the module Microelectronics Technologies and Applications, this is likely to be the first time
you will have attempted to access Cadence remotely.

First you need to install MetaFrame on your PC; click here for instructions.

Read the essential information about remote access, linked from here.

Both the above links are also present on the Information pages. If you are in any doubt, please contact the AMI Office, or
Ian Charlson.

Now run SPICE Exercise 1 to verify your results from SAQs 1 and 2.
Further study

Read chapter 4 of The Spice Book by A. Vladimirescu for an in-depth description of DC Analysis.

1.12.2 AC Analysis

In AC analysis the frequency of an ac input signal source is varied and the response of the ac voltages and currents in the
circuit calculated.

There are four options available:

AC Frequency Sweep Analysis

Calculates the ac voltages and currents in a circuit as the frequency of an input voltage source is varied over a
specified range. The values of voltages/currents at specified nodes in the circuit can be plotted as a function of
the varied frequency.

The frequency sweep can be linear (equal intervals between the points) or in decades (points distributed
logarithmically in decades with the end frequency in each decade 10 times that of the start) or in octaves
(points distributed logarithmically in octaves with the end frequency in each octave twice that of the start).

Example Control Statement

.AC DEC 10 100 10MEG

Sweep the frequency of the sinusoidal input voltage source between 100 Hz and 10MEG Hz logarithmically in
decades with 10 points per decade.

Noise Analysis

Calculates the noise values in the circuit components as the frequency of an input voltage source is varied
over a specified range. The noise values of voltages at specified nodes in the circuit can be plotted as a
function of the varied frequency.

Example Control Statement

.NOISE V(3) VIN 10

Calculate the noise values at node 3 and input voltage source VIN. Provide a noise summary every 10 points
in the frequency sweep.

Distortion Analysis

Calculates the harmonic distortion generated in a specified load resistor as a result of applying one or more
sinusoidal input sources.

Example Control Statement


.DISTO RL

Calculate the harmonic distortion in load resistor RL.

Poles and Zeros Analysis

Extracts the values of poles and zeros in a circuit from the frequency plot.

Example Control Statement

.PZ 2 0 4 0 VOL POL

Calculate the poles (POL) in a two port circuit with input between node 2 and 0 and output between node 4 and
0 and an input voltage source (VOL).

Attempt SAQ 3

THEN

Run the SPICE Exercise 2 to verify your results.

Further study

Read Chapter 5 of The Spice Book by A. Vladimirescu for an in-depth description of AC Analysis.

1.12.3 Transient Analysis

In Transient Analysis a specified signal source is applied to the circuit and the response of the voltages and currents in the
circuit is calculated over a given period of time.

There are two options:

Time Domain Analysis

A time dependent signal eg. pulse, exponential, sinusoidal, piecewise linear, single frequency FM, is applied to
the circuit. The voltages/currents at specified nodes in the circuit are plotted as a function of time.

Example Control Statement

.TRAN 100U 15M


Sweep time from 0 to 15 milliseconds calculating values every 100 microseconds.

Fourier Analysis

Calculates the harmonic content (magnitude and phase) of specified voltages and currents.

Example Control Statement

.FOUR 20K V(1)

Perform a fourier analysis on the voltage at node 1 for an applied waveform with a 20K Hz fundamental.

Attempt SAQ 4

THEN

Run SPICE Exercise 3 to verify your results

Further study

Read Chapter 6 of The Spice Book by A. Vladimirescu for an in-depth description of AC Analysis.

1.13 Obtaining Results

A characteristic of circuit simulators is that occasionally their algorithms fail to iterate towards a result. Users of SPICE in
particular will be familiar with the two most feared error messages at the end of an unsuccessful run:

NO CONVERGENCE IN DC ANALYSIS – Indicating a failure of an algorithm to find a DC solution.

INTERNAL TIME STEP TOO SMALL IN TRANSIENT ANALYSIS – Indicating that an algorithm has repeatedly reduced its
time step in order to find a solution and has reached the limit.

The following are some of the reasons why errors such as these can occur.

1.13.1 DC Convergence Problems


Circuit Specification

A common mistake in coding or entering a circuit schematic is to incorrectly specify component values or connections. Failure
to provide a ground connection, omitting to connect a component to a node, specifying a component without a scaling factor
(eg. a capacitor set equal to 10 rather than 10P will be interpreted as 10 Farads!) are all common errors.

Initial Node Voltages

SPICE assumes that all node voltages are initially set to zero before the simulation. The nature of the circuit may be such that
a solution cannot be found from this initial condition. Two commands are available that allow the user to specify non-zero
values on some or all of the nodes.

.NODESET V(2) = 0.5 V(3) = 1.25

This will set the initial voltage at node 2 to 0.5 volts and at node 3 to 1.25 volts.
These values are only used as initial estimates to assist the algorithm and will be replaced by the calculated values if a
successful solution is obtained.

.IC V(1) = 3.5 V(6) = 1.2

This will set the initial voltage at node 1 to 3.5 volts and at node 6 to 1.2 volts.
Unlike the NODESET command these values will be used all the way through the simulation. The IC command is most useful
when implementing a transient analysis.

Iteration Limit

SPICE uses a default iteration limit of 100 to obtain a solution. This can be raised by the user by specifying the OPTIONS
command with the parameter ITL1.

.OPTIONS ITL1 = 600

This will raise the number of iterations to 600.

Should this approach not succeed then a technique known as Source Ramping can be employed. This involves using the
ITL6 parameter to step up all voltage and current sources in the circuit from 0 to their stated values, performing a specified
number of iterations at each step.

.OPTIONS ITL6 = 20

This ramps up the voltage/current sources with 20 iterations per step

1.13.2 Transient Analysis Problems

Model Parameters

SPICE provides default parameters for its semiconductor models. These are ideal parameters (eg. a diode with zero leakage
current) and can often cause an algorithm to fail. A complete set of parameters should be specified for the devices being
used. If they are standard components these parameters are often provided in a library accompanying the simulator, if they
are components in an IC design the parameters will be supplied by the foundry.

Time Step Iterations

The number of iterations performed at a time step can be increased from the default value of 101 by using the ITL4 parameter
with the OPTIONS command.
.OPTIONS ITL4 = 50

The time step iterations are increased to 50

Oscillator Startup

Circuits containing oscillators are notoriously difficult to get started. Most designs rely on a step function from the power
supply at switch on. This effect can be simulated by defining the power supply voltage source as a pulse with no period (ie. a
step function).

Further study

See chapter 10 of The Spice Book by A. Vladimirescu for detailed advice on convergence problems.
Advanced Electronic Design Automation

Chapter 2 - Semiconductor Modelling in SPICE

Chapter Information

The chapter contains the following sections:

2.1 Representing semiconductor 2.5 The junction field effect


devices transistor model
2.2 Coding semiconductor devices Further study
2.3 The diode model
Further study 2.6 The metal oxide semiconductor
Self Assessment Questions field effect transistor model
SPICE Exercise 4 Further study
Self Assessment Questions
2.4 The bipolar transistor model Spice Exercise 6
Further study
Self Assessment Questions
SPICE Exercise 5

This chapter describes the features and facilities within SPICE for analysing electrical and electronic circuits
containing active components. Semiconductor modelling techniques are discussed accompanied by appropriate
examples executed on CADENCE.

2.1 Representing Semiconductor Devices


SPICE contains a number of built-in models for representing semiconductor devices. These models are used to
calculate values of voltages and currents for the devices from a series of appropriate equations that reference
process parameters such as saturation current, forward resistance, junction capacitances etc.

Each model has a defined name and a set of default parameters that typically relate to idealised conditions. Any or all
of these default parameters can be replaced with specific parameters to customise the model for a particular device
or process.

These specific parameters are often obtained from manufacturer’s data sheets for standard components or from the
foundry for ASICs. Additional information relating to device geometry can also be incorporated.

The following model types are currently available:

Device Type Model Name

Diode
NPN Bipolar D
Transistor NPN
PNP Bipolar PNP
Transistor NJF
N – Channel PJF
Junction FET NMOS
P – Channel PMOS
Junction FET Diffused Resistor*
N - Channel Diffused
MOSFET Capacitor*
P – Channel Uniformly
MOSFET Distributed RC*
R N – Channel
C MESFET*
URC P – Channel
NMF MESFET*
PMF

* SPICE3 only

2.2 Coding Semiconductor Devices

In textual SPICE representations each semiconductor device is allocated a user defined instance name which, like
passive components, must begin with a mandatory initial letter and a user defined model name. The instance name is
necessary to identify a particular component amongst many of the same type in a circuit. The model name enables a
component to be linked to a particular model and its associated parameters.
The relevant section of coding for the
above circuit would be:

Q1 1 2 3 QMOD
D1 3 0 DMOD
.MODEL QMOD NPN
.MODEL DMOD D

Here we define a bipolar transistor with an instance name Q1 (Q is the mandatory initial letter for bipolar devices) with
its collector, base and emitter terminals connected to nodes 1 2 and 3 respectively. The terminal ordering in the
statement is defined as Collector – Base – Emitter. A user defined model name of QMOD has been assigned to the
device.

Similarly a diode with an instance name of D1 (D is the mandatory initial letter for diodes) is connected with its anode
to node 3 and its cathode to node 0. Again the terminal ordering is significant, anode coming before cathode. A user
defined model name of DMOD has been assigned.

The two MODEL statements associate the user defined model names with the default SPICE models NPN for n type
bipolar transistors and D for diodes.

In this example none of the default model parameters have been re-specified. This is easily accomplished, however,
by appending the required parameter names and values to the MODEL statements.

Example

.MODEL QMOD NPN IS = 0.5F BF = 200 VAF = 10


.MODEL DMOD D IS = 1P

Here the transistor default parameters IS, BF and VAF (Saturation current, Current gain and Early voltage) have been
re-specified with the values 0.5 x 10-15 amps, 200 and 10 volts respectively. The remaining default parameter values
will be used for the simulation.

Similarly the diode default parameter IS (Saturation current) has been re-specified with the value 1 x 10-12 amps.

For schematic entry the system will allocate an instance name but the user is required to define a model name.

2.3 The Diode Model


Diodes are modelled using the generalised diode equation:

ID = IS(eqVd/NkT – 1)

where

IS is the saturation current


N is the emission coefficient
T is the temperature in degrees Kelvin
q is the electron charge equal to 1.6 x 10 -19 C
k is Boltzmann’s constant equal to 1.38 x 10 -22 JK -1
Vd is the voltage drop across the device

In addition to IS there are a number of other parameters such as forward resistance, junction potential, breakdown
voltage etc. that relate to the model.

Further study

Section 3.2 of The Spice Book by A. Vladimirescu provides further information on the diode model and its associated
parameters.

2.4 The Bipolar Transistor Model

The DC operating conditions of the device are modelled by the transport version of the Ebers-Moll model which
represents the transistor as two back-to-back pn junctions with their associated diode equations:

ICC = IS(eqVbe/NF.kT – 1) for the forward biased diode


ICE = IS(eqVbc/NR.kT – 1) for the reverse biased diode

The dynamic operating conditions are modelled by 5 charges. Two are associated with the currents ICC and ICE
whilst the remaining three represent the charges in the depletion regions of the base-emitter, base-collector and
collector-substrate junctions.

Further study

Section 3.3 of The Spice Book by A. Vladimirescu provides further information on the bipolar transistor model and its
associated parameters. The section also contains a useful exercise in obtaining specific model parameters from a
manufacturer’s data sheet.

2.5 The Junction Field Effect Transistor Model (JFET)

Junction FETs are coded using instance names that have a mandatory initial letter J. The ordering of the terminals is
Drain – Gate – Source.

Example:
J1 1 2 0 MODJ
.MODEL MODJ NJF VTO
= -2 RD = 50

Here we define an n channel JFET with drain, gate and source terminals connected to nodes 1, 2 and 0 respectively
and a user defined model name of MODJ.

The model name is associated with the default SPICE model NJF. Specific parameters have been provided for the
Threshold Voltage (VTO = 2 volts) and the Drain Resistance (RD = 50 Ohms).

The DC operating conditions are defined by the general Drain/Source current equation. There are two variants
depending on the relative magnitudes of the Drain/Source (VDS) and Gate/Source (VGS) voltages.

where

BETA is the transconductance


VTO is the threshold voltage
LAMBDA is the channel length modulation

Dynamic operation is modelled by two charges associated with the Gate – Drain and Gate – Source junctions.

Further study

Section 3.4 of The Spice Book by A. Vladimirescu provides further information on the junction field effect transistor
model and its associated parameters.
2.6 The Metal Oxide Semiconductor Field Effect Transistor Model (MOSFET)

MOSFETs are coded using instance names that have a mandatory initial letter M. The ordering of the terminals is
Drain – Gate – Source – Substrate (bulk)

Example:

M1 1 2 0 0 MODN
.MODEL MODN
NMOS VTO = 1.5 RS
= 20

Here we define an n-channel MOS device with drain, gate, source and substrate terminals connected to nodes 1, 2, 0
and 0 respectively.

The model name is associated with the default SPICE model NMOS. Specific parameters have been provided for the
Threshold Voltage (VTO = 1.5 volts) and the Source Resistance (RS = 50 Ohms).

Additionally for MOS devices there is the capability to include values for the device geometry. The following
parameters can be appended to the component declaration:

L Channel Length
W Channel Width
AD Drain Diffusion Area
AS Source Diffusion Area
PD Drain Diffusion Perimeter
PS Source Diffusion
NRD Perimeter
NRS Number of Squares for
Drain Diffusion
Number of Squares for
Source Diffusion

Example:

M1 1 2 0 0 MODN L = 2.5U W = 5U

defines an n channel MOSFET with a channel length of 2.5 microns and channel width of 5
microns
The DC operating conditions are defined by the general Drain/Source current equation. There are two variants
depending on the relative magnitudes of the Drain/Source (VDS) and Gate/Source (VGS) voltages.

with KP as the transconductance


W = channel width
Leff = effective channel length (corrected for the lateral diffusion of the drain and source)
VTH = Threshold Voltage

Dynamic operation of the device is modelled by charges associated with the gate – oxide – semiconductor interface
and by charges associated with the drain and source diffusions.

Further study

Section 3.5 of The Spice Book by A. Vladimirescu provides further information on the MOSFET module and its
associated parameters.
Advanced Electronic Design Automation
Chapter 3 - Fault Simulation and Test Pattern Generation

Chapter Information

The chapter contains the following sections:

3.1 The Need for Testing 3.6 Test pattern Generation


Self Assessment Question
3.2 Role of the Fault Simulator Fault Simulation Exercise 1

3.3 Fault Simulator Operation 3.7 Fault Simulation of Sequential


Circuits
Self Assessment Question
3.4 Fault Simulation in the
Fault Simulation Exercise 2
Design Process

3.8 Behavioural Fault Simulation


3.5 Basic Testing Concepts
3.5.1 The SA0 and SA1 Fault Model
3.5.2 Controllabilty and Observability
Self Assessment Question
3.5.3 Fault Categories
3.5.4 Fault Simulation

This chapter introduces the concepts of ASIC testing. The utilisation of fault simulators both in assessing the
testability of designs and in formulating test pattern sets is discussed. Practical exercises using the Verifault
fault simulator within CADENCE are also included.

3.1 The Need for Testing

The modern integrated circuit is a complex device manufactured by a complicated fabrication process. At any
stage in the process physical defects can occur in the silicon which can render the resultant device useless.

Transistor construction can be flawed, metal tracks can be shorted or open circuit, die can be damaged during
wafer sawing or packaging and human error or mechanical failure can result in a processing step being
incorrectly implemented. For this reason ICs are tested twice during manufacture.

The first test occurs at the completion of fabrication. Each die is subjected to a Wafer Test during which a series
of test patterns are applied to the input bond pads by wafer probes and the resulting signals on the output bond
pads captured for analysis.

A second test occurs when the devices have been packaged, often using the same set of test patterns.

Customers may also test the devices prior to and after assembly into a product.

Failure to detect even a small percentage of faults can be costly. The designer must therefore supply a
comprehensive set of test patterns capable of identifying every possible fault that can occur in the device.

3.2 The Role of the Fault Simulator

An initial set of patterns would typically be those developed during the simulation phase of the design process.
These patterns, however, are primarily intended to test for design errors and will not necessarily detect all
manufacturing defects.

A fault simulator enables the designer to assess the efficiency of this initial set and assists in the development of
additional patterns such that all possible faults can be detected. The resulting effectiveness of a test pattern set
is referred to as the Fault Cover and is expressed as a percentage of the detected faults with respect to the
detectable faults. The goal is therefore to achieve a 100% fault cover.

Often a circuit may be required to function in such a way that makes it impossible to detect certain faults with
any test pattern. In these circumstances it is necessary to incorporate additional test circuitry for the purpose of
achieving maximum fault cover. This is known as Design for Testability. A good fault simulator will also identify
these undetectable faults and assist the designer in the development of appropriate test logic.

It is also possible to automate to a certain extent the production of a test pattern set using an Automatic Test
Pattern Generator which analyses the faults that can occur in the circuit logic and generates the appropriate
patterns. These patterns can then be applied to the circuit by the fault simulator and the resulting fault cover
determined.
3.3 Fault Simulator Operation

There are a number of fault simulator algorithms. All operate on the principle of performing a normal simulation
(the good circuit) and capturing the outputs. The circuit is then re-simulated with a series of faults injected (the
faulty circuit) and the outputs compared.

Serial Fault Simulation involves simulating the good circuit, capturing all the output states for each test pattern
then simulating the faulty circuit one fault at a time. The process is slow and impractical for large circuits.

Parallel Fault Simulation simulates the good circuit then simulates many faulty circuits simultaneously storing
the results of an individual output in the bits of the computer word in memory. One bit is used for the good circuit
and the remaining bits for the faulty circuits. Other words are used for other outputs. This technique is
considerably faster than serial fault simulation.

Concurrent Fault Simulation operates on the principle that not all of the circuit needs to be simulated in order to
detect a particular fault. As before, the good circuit is simulated then only the appropriate part of the faulty circuit
for each fault. The simulation algorithm is more complicated but the resulting speed of execution is much faster.
The Verifault fault simulator uses the concurrent technique.

There are also a number of Simulation Engines or Hardware Accelerators on the market that exploit the
performance advantages of using dedicated hardware rather than software to perform parallel fault simulation.

3.4 Fault Simulation in the Design Process

The necessity to consider testing as an essential element in the design activity has led to the integration of fault
simulators within the design process.

A circuit will have been entered either schematically by a schematic capture package or structurally by a
hardware description language such as VHDL or Verilog. Fault simulators can only operate at the structural
level (although some like Verifault are capable of propagating faults through behavioural descriptions). Circuits
initially defined at the behavioural level must first be synthesised to a particular target technology.

The circuit will then be functionally simulated using a set of test vectors designed to verify the circuit in all its
operating modes.

A correctly functioning circuit can then be fault simulated using the same test vector file developed during
functional simulation. It is usually necessary to incorporate additional commands into this file to specify the type
and nature of faults to be injected and strobe information relating to the points in time when outputs can be
inspected.

The result of the fault simulation is a Fault Dictionary detailing the faults that can be detected in the circuit and a
Faults List which reports on the faults actually detected/undetected by the test patterns.
Typically this first pass could result in fault covers of around 60% being attained. The information in the fault list
relating to the undetected faults can be used to enhance the test patterns and the process repeated until full
fault cover is achieved. This may also involve modifications to the design to improve testability.

3.5 Basic Testing Concepts

The range of defects likely to be introduced during the fabrication process is immensely varied. A fault model
that could handle all possibilities would be extremely complex and unworkable. Fortunately experience has
shown that whatever the cause of the fault the effect is likely to be an input/output pin (port) or a wire (net) or a
component pin (terminal) either stuck permanently at a logic 0 (SA0) or stuck permanently at a logic 1 (SA1). All
fault simulators operate on variations of this basic fault model.

3.5.1 The SA0 and SA1 Fault Model

Consider the following circuit in which the AND gate output is stuck at 0.

We can define two truth tables, one for the circuit working correctly and one for the circuit with the fault.

Good Circuit Faulty Circuit

A B C F A B C F

0 0 0 1 0 0 0 1

0 0 1 0 0 0 1 0

0 1 0 1 0 1 0 1

0 1 1 0 0 1 1 0

1 0 0 1 1 0 0 1

1 0 1 0 1 0 1 0

1 1 0 1 1 1 0 1
1 1 1 1 1 1 1 0

We observe that the input pattern A=1, B=1, C=1 produces a difference at the output between the expected
value in a good circuit and the actual value obtained in the faulty circuit. A tester operating on this circuit will
know the expected value, measure the actual value, compare the two and detect the fault.

3.5.2 Controllabilty and Observability

Test patterns can only be applied at the input pins to integrated circuits and faults observed at the output pins.

Therefore when determining a suitable combination of input values for a test pattern it is necessary to consider
both how a particular fault can be set up from the inputs (Controllability) and seen at the outputs (Observability).

The previous example shows that values on A and B that would produce a logic 0 on the AND gate output or
values of C that would force the OR gate output to logic 1 are ineffective in detecting the fault. We conclude
therefore that in order to detect the fault it is necessary to attempt to set the faulty node to a logic 1 (i.e. the
opposite logic value to the stuck-at fault) by setting A=1, B=1 and to ensure that the fault is not obstructed from
being seen at the output by setting C=1. The process of clearing an observable route to an appropriate output is
referred to as Sensitising a Path.

3.5.3 Fault Simulation

Four types of fault are typically identified by fault simulators

● Detected
● Undetected
● Potentially Detected
● Untestable

Detected faults are those that can be detected and have been detected by one or more test patterns. These
faults are flagged when a value observed on the faulty circuit is different to that of the good circuit.

Undetected faults are those that can be detected but have not been detected by any of the test patterns.
These faults are flagged when a value observed on the faulty circuit is the same as that of the good circuit.

Potentially Detected faults are those that can be detected but only might have been detected. These faults are
flagged when a value observed on the faulty circuit is indeterminate (X) whilst that of the good circuit is 0 or 1.

Example:
In a good circuit the RESET signal would be capable of initialising the flip flop to a known state and therefore
defining a known value on F. In the faulty circuit, however, a SA1 fault on the RESET would prevent the flip flop
from being initialised correctly and the resulting output would therefore be unknown (i.e. either a 0 or a 1).
Whether or not the fault is detected would depend on this unknown value being the same or different to that
defined for the good circuit. We cannot say for certain that the fault is detected but it may be so we classify it as
being Potentially Detected.

These type of faults are included in the analysis because usually as testing proceeds one or more test patterns
will eventually reveal a discrepancy between the good and bad circuits and the fault will be detected.

Untestable faults occur on nets that cannot be controlled or nets that cannot be observed

Example:

A fault on the flip flop NQ output cannot be detected since it cannot be observed at any of the circuit outputs.
Similarly a fault on the input of the AND gate tied to VDD to define a logic 1 cannot be detected since it cannot
be controlled by the simulator to the required logic values.
The Verifault fault simulator used in CADENCE will identify untestable faults in circuits and determine
detectable, undetectable and potentially detectable faults according to the following model.

Where Z = High
Impedance
X=
Unknown
L = 0 OR Z
H = 1 OR Z
D=
Detected
U=
Undetected
P=
Potentially
Detected

3.5.4 Fault Simulation

Typically in logic circuits a particular test pattern in a set will detect a number of faults.

Example:

To test for a
SA0 on F
we need to
set A=1
and B=1.
To test for a
SA0 on A
we also
need to set
A=1 and
B=1.

We say that
these two
faults are
Equivalent
Faults such
that if the
fault
simulator
can detect
one of them
it can also
detect the
other.

Additionally
a fault that
can occur
on a net will
be the
same fault
as that on
the logic
element
terminal to
which it is
connected
or to the
circuit port
which it
drives or is
driven by.

We can
therefore
collapse all
possible
faults on
ports, nets
and
terminals to
a small set
of Prime
Faults to
which all
other faults
are
equivalents.
3.6 Test Pattern Generation

For logic circuits with any degree of complexity it would be uneconomical both in terms of redundancy and
testing time to construct a test pattern set that consisted of all possible input signal combinations even though
this would give the best chance of achieving full fault cover.

Consider the following adder circuit constructed internally from 8 cascaded full adders :-

A comprehensive test for


correct functionality would
require 256 values on A
and 256 values on B each
with both values of CARRY
IN giving a total of 256 x
256 x 2 = 131,072
combinations.

In reality a functional
simulation would specify
only a small fraction of
these values. The designer
would carefully choose a set
of patterns to test the circuit
in all its possible operating
modes.

Typical patterns would


include a range of positive
and negative numbers with
and without the CARRY IN
signal, bit patterns with
alternate 1s and 0s to check
that each full adder had
been wired correctly, worst case carry conditions to check that a CARRY IN will ripple successfully from the
least significant to the most significant stage etc.

Applying this functional set of test patterns to a fault simulator could be expected to deliver a reasonable degree
of fault cover (typically around 60%). The simulator will produce a faults list that will identify the type and nature
of the undetected faults. The designer can then use this information to devise additional test patterns and re-
simulate. This process will be repeated until maximum fault cover is achieved.
3.7 Fault Simulation of Sequential Circuits

In addition to the logic faults already considered for combinational circuits, sequential circuits are also
susceptible to Parametric faults which can effect voltage and current levels and switching speeds. They arise
from improper fabrication or the normal aging process often accelerated by extremes of temperature, humidity
or mechanical vibration. The result can be timing variations and consequent circuit failure. Typical faults likely to
occur are Race Conditions where two or more signals change simultaneously and Hazards where a signal
momentarily changes value (a Glitch).

In determining a suitable set of test patterns for sequential circuits, particular attention has to be paid to the
order in which test vectors are applied.

Example:

The circuit defines a SET-RESET flip flop and its associated truth table.

Testing for each of the NAND gate inputs SA1 using the SET/RESET input values sequenced as in the truth
table produces the following results on the Q output.

INPUTS OUTPUT

___ ______ ___ _ _____


Good Circuit Q SA1
SET RESET SET SA1 Q SA1 RESET SA1

0 0 1 0 1 1 1
0 1 1 0 1 1 1
1 0 0 0 0 0 1
1 1 0 0 0 1 1
This sequence of patterns is effective in detecting all faults except where in all input combinations the
good circuit and the faulty circuit outputs are identical.

Running the input sequence in reverse produces the following results

INPUTS OUTPUT

___ ______ ___ _ ______


SET RESET SET SA1 Q SA1 RESET SA1
Good Circuit Q SA1

1 1 X X 0 1 X
1 0 0 0 0 0 X
0 1 1 0 1 1 1
0 0 1 0 1 1 1

Now some of the outputs are indeterminate as a result of the ordering of the sequence. For example the first
combination of inputs would normally retain the current value of Q but that value has not been defined.

These indeterminate values may ultimately be classed as potentially detected faults, but with only the

fault actually detected the overall fault cover is lower than before.

A further complication can arise if the input pattern 0,0 is followed by the pattern 1,1. The first pattern can force
both outputs to logic 1, feeding this value back to the NAND gate inputs. Under these conditions the second
pattern will force the outputs to logic 0, feeding this value back to switch the outputs back to logic 1 and so on.
The circuit therefore oscillates at a frequency determined by the propagation delay of the gates. A fault simulator
will identify this type of fault as an Oscillation Fault.

The application of automatic test generators for sequential circuits has proved difficult to achieve and for this
reason a high degree of designer involvement in specifying test patterns is required.

3.8 Behavioural Fault Simulation

Fault simulators can only analyse circuits described at the structural level. This implies that any circuit defined
behaviourally in VHDL or Verilog must first be synthesised to the required target technology before running the
simulator.

Simulators like Verifault also provide the capability to propagate faults through behavioural components in
structural circuits. This is useful when attempting to analyse the testability of the structural parts of such circuits.

A full and complete fault simulation is not possible, however, until all components are in structural form.
Further Study

Sections 14.3 to 14.5 of Application Specific Integrated Circuits by M. Smith provide a detailed analysis of
testing principles and fault simulation techniques.
Advanced Electronic Design Automation

Chapter 4 - Introduction to VHDL

Chapter Information

The chapter contains the following sections:

4.3 Levels of Abstraction Supported by VHDL


4.1 Introduction and Historical Development of
MOS Switch Level
VHDL Logic Gate Level
Introduction Register Transfer Level
Links to VHDL-related Web Sites Chip Level
Historical Development of VHDL System Level

4.2 The Scope and Role of VHDL in the Design 4.4 Benefits and Implications of using VHDL
Process Features and Benefits of VHDL
Overview of the VHDL Design Flow Implications of using VHDL

4.1 Introduction and Historical Development of VHDL

Introduction

Previous chapters of this module have dealt with circuit-level design and simulation techniques using the SPICE language. This language is
based on the use of complex and highly accurate mathematical models of analogue and mixed-signal components linked together by a
textual description of the circuit (a netlist). The following chapters of this module are an introduction to the realm of high-level digital design
using a Hardware Description Language (HDL). Such a language is used to augment or replace schematic-based design entry by describing
the behaviour, structure and dataflow of a digital system in terms of the high-level constructs commonly found in general purpose software
languages. Unlike most general purpose languages, however, an HDL has built-in support for concurrency, this being an essential
requirement for the modelling of hardware, which is inherently concurrent. The use of an HDL and associated tools, such as simulators and
synthesizers, gives the designer the freedom to explore digital systems design at a high level of abstraction while improving both the quality
and productivity of the design process by removing the burden of detailed gate-level design.

Hardware Description Languages have been used in the design community for many years. Languages such as PALASM and ABEL have
supported Programmable Logic design with great success and continue to do so. A common limitation of all PLD languages is that they tend
to provide features which are closely tied to the underlying technology of the devices being developed and as such have certain limitations
which prevent them from being used in behavioural modelling and design of complex digital systems. An example of such a limitation is the
inability of PLD-based languages to handle timing behaviour. This precludes such languages for use in modelling of real hardware.

Other HDLs, not specifically designed for PLD development, have also enjoyed wide usage in the past. Examples are HILO and ELLA. One
fundamental limitation of such languages is that they have not been standardised by an internationally recognised organisation and as such
remain essentially proprietary in nature. Another HDL, Verilog-HDL, is the subject of Chapter 9 of this module. For background information on
Verilog-HDL, take a look at the Doulos web page, in particular the link to Designer's Guide to Verilog.

This module is concerned with a Hardware Description Language which is not specifically aimed at any particular implementation technology,
supports the design of all types of digital system described at any level of abstraction and is defined by an internationally recognised
standard.

The language, known by the


acronym VHDL, is defined by
IEEE Standard 1076-1993 and
provides a comprehensive set of
constructs for all levels of design
representation ranging from
primitive MOS switch level up to
system level. The following quote
is extracted from the VHDL
Language Reference Manual, an
IEEE Design Automation
Standards Committee document
which completely defines both the
syntax and the semantics of the
language:

The VHSIC Hardware Description


Language (VHDL) is a formal
notation intended for use in all
phases of the creation of
electronic systems. Because it is
both machine readable and
human readable, it supports the
development, verification,
synthesis, and testing of
hardware designs; the
communication of hardware
design data; and the maintenance, modification, and procurement of hardware

The above statement indicates that VHDL is a multi-faceted language and is used in all phases of the hardware development process from
documentation and specification to implementation and testing.

Q . What do the letters VHDL stand for?

A . Very High Speed Integrated Circuit Hardware Description Language

It is easy to confuse VHDL with the other widely used HDL known as Verilog-HDL since they appear to share the same acronym. However
the acronym VHDL is deliberately not used for Verilog-HDL in order to avoid confusion. The two languages are used for similar purposes but
they look quite different.

Because of the diversity and range of applications VHDL supports, it is consequently a relatively large language with many different
constructs, operators and types. This in itself can present the new user of VHDL with a daunting prospect that of having to learn and become
familiar with all the different aspects of the language. In practice the situation is not nearly as bad as might be perceived, since users of the
language tend to be concerned with only one or two areas of application such as RTL simulation and Synthesis for example.

Most engineers use VHDL for designing and creating hardware. This requires the user to become familiar with that part of the language which
is applicable to Synthesis (the automatic creating of a gate-level circuit from a behavioural description). This constitutes a significant subset of
the full language definition. However, all users of VHDL need to have a basic knowledge of most of the language in order to exploit it in other
application areas such as test bench creation and component modelling.

Other web-based resources associated with this module

In addition to the main body of material presented in these web pages and the links to outside sources of information, this module provides a
number of other specially developed resource files which will be referred to when appropriate. The resource files are described below:

● VHDL Language Syntax Guide - This file contains a complete definition of the syntax of VHDL-1993 in hypertext-linked Bachus-Naur
format.
● VHDL Quick Reference Guide - This file contains a brief summary of the main features of the VHDL language.
● Examples of VHDL Descriptions - This file contains numerous example VHDL descriptions grouped according to function.
The above links refer to material which has been specifically designed for this module; there are a multitude of other web sites concerned with
various aspects of VHDL and its application. Some of these sites contain little information of any real use. However, there are many others
which contain useful information and links to other sites of interest. For a general introduction to VHDL, visit the Doulos web site and read
through the material entitled A Hardware Engineer's Guide to VHDL, in particular the sub-section entitled VHDL Backgrounder. Another basic
VHDL tutorial can be found at the Green Mountain web site.

Links to a selection of VHDL-related web sites are given below. Some of these will be referenced in the forthcoming chapters when
appropriate.

Links to VHDL-related Web Sites

● Integrated circuit, VLSI, system design, VHDL, schematic capture, simulation, layout, test and DFT,
wafer fabrication, mask packaging, mask assembly.
Try EDAC - http://www.edac.org/

VHDL-UK is the UK independent VHDL users forum. It aims to provide information and
discussion on all aspects of VHDL and Design Automation. VHDL-UK produces a regular
VHDL-UK update newsletter and organises user-oriented conferences in addition to promoting
electronic information exchange. The site contains many useful links to other VHDL
resources.

The Free Model Foundation is dedicated to the furtherance of standard modelling practices
Free Modelling Foundation within the electrical engineering community. In particular it supports the use of VHDL,
Verilog, and IBIS modelling languages.

The Library of Parameterised Modules offers designers a means to achieve technology-


independent design entry without sacrificing efficiency. The library can be used with
Library of Parameterised Modules schematic, Verilog or VHDL design entry and is supported by most major EDA vendors. As
the LPM expands in scope and support it will become the standard method of design entry
and synthesis over the next 5 to 20 years.

Cypress Semiconductors' main business is the manufacture of a variety of silicon


Cypress Semiconductors' VHDL integrated circuits including PLDs, CPLDs and FPGAs. They have a great deal of expertise
link page in VHDL modelling and publish models of their devices on the web. In addition, they have
produced a low-cost VHDL compiler for their range of programmable devices.

Doulos is one of the main VHDL/Verilog training companies in the UK. They offer a wide
Doulos variety of services in addition to training, such as design consultancy and modelling. Their
excellent web site contains a range of useful tutorial material and links.

An HTML short-form data sheet describing the features of the Cadence VHDL Simulator.
Cadence Leapfrog Data Sheet
This tool will be used later in this module as part of the hands-on exercise.

The Hamburg VHDL archive intends to provide a collection of free public domain or
Hamburg VHDL Archive
shareware VHDL tools, models and documentation.

The aim is to provide free access to VHDL source code and related material. They have
ERM/PHASE Team WWW Server installed a database that you may access via the WWW. The database has four different
parts that can only be read and two databases for contributions from users.

VHDL International (VI) is an organization dedicated to promoting the Very High Speed
VHDL International Integrated Circuit (VHSIC) Hardware Description Language (VHDL) as a standard
worldwide language for the design and description of electronic systems.

Historical Development of VHDL

Book reference: section 1.2

The VHDL language was born out of the need to be able to specify highly complex systems in a coherent and unambiguous manner at all
levels of design abstraction. This means that the same language can be used to describe a design in terms of low-level primitive elements,
such as logic gates, as well as complex parts such as microprocessors and peripherals. Beyond this, the language can be used to describe a
complete system comprising many printed circuit boards. Going back to the late 1970s and early 1980s such a language did not exist and
other non-standard languages were being used for various aspects of the design process, but none of these had the diverse range and
descriptive power of what was to become VHDL. As summarised in the table below, the Very High Speed Integrated Circuit initiative spawned
the need for a language which would support and enhance the development of complex integrated circuits and systems for use in the defence
industry.

The adoption and standardisation of the language by the IEEE effectively sealed its fate as one of the most significant developments in the
area of design and verification, opening it up to a worldwide audience of potential users and tool developers. The standardisation of VHDL is
an ongoing process and several additional utilities have been standardised for use with the language such as Standard Logic (Std 1164),
Numeric_std and Numeric_bit (Std 1076.3). In recent years, a great amount of interest has been focused on the development of extensions to
the language to handle analogue and mixed-signal designs and a standard will shortly be released defining VHDL-AMS. More information on
VHD-AMS can be found at http://www.vhdl-ams.com/.

1980 United States Government launches the Very High Speed Integrated Circuit (VHSIC) initiative with a view to enhancing
the operating performance and circuit density of VLSI technology in order to produce the next generation of VLSI Chips.
The design tools and languages available at the time were found to be inadequate for the task of describing and
verifying large complex systems, being largely based around gate-level descriptions of digital systems. In addition, the
use of a range of diverse and incompatible design languages and systems created problems when design data had to
be exchanged between government agencies and sub-contractors.

The above problems led to the need for a standard HDL which could be computer readable and executable.

1983 United States Government issues a request for a proposal to develop a Hardware Description Language which would
support the VHSIC programme. The language, to be known as VHSIC-HDL, would have two principal goals. Firstly, the
language would have to be capable of describing complex hardware systems in a totally unambiguous manner at
various levels of abstraction and secondly, the language would have to be defined as a standard so that all
organisations involved in the programme would be forced to specify designs using the same language. A parallel
requirement was the need to implement a set of tools for use with the language to enable both simulation and synthesis.
The requirement that all sub-contractors had to specify designs in VHDL meant that the procurement agency could
independently verify the performance and functionality of a design prior to awarding a contract.

A team of industrialists comprising Intermetrics, IBM and Texas Instruments successfully bid to develop the language
VHDL.

1985 Version 7.2 of the language released for public review.

1987 Following the public review, which resulted in a number of revisions and changes, the language was adopted by the
IEEE and ratified as IEEE standard 1076-1987, IEEE Standard VHDL. This effectively placed VHDL into the public
domain, the VHDL Language Reference Manual fully defines the syntax and semantics of the language. All software
tool developers were required to implement the language as defined by the LRM.

1993 As part of a five-yearly on-going review of the language, VHDL has been revised and updated to result in the current
standard: IEEE Standard 1076-1993, IEEE Standard VHDL. The majority of simulation and synthesis tools are
compatible with both the 1987 and the 1993 definitions of the language.

The 1993 update to standard VHDL resulted in a number of additions and enhancements to the language. These range
from the cosmetic changes which result in all statement bracketing being consistent to more significant changes such as
direct entity instantiation, new built-in logical operators and file handling. (The latter is incompatible with the 1987
version of VHDL).

Read Chapter 1 of the recommended textbook (VHDL for Logic Synthesis by A. Rushton)

4.2 The Scope and Role of VHDL in the Design Process

The users of VHDL in the engineering community can be divided into two broad categories as illustrated below:
In support of the requirement that VHDL should be used in all phases of the design cycle, a significant number of users of the language are
engaged in modelling existing hardware for the purposes of simulation and verification. This application of VHDL requires the full capabilities
of the language in order to create accurate timing models and sophisticated simulation test benches.

A significant number of users are generating real hardware from Register Transfer Level VHDL descriptions. These designs are destined to
become FPGAs, CPLDs or ASICs using Logic Synthesis/Compilation tools. This group makes use of a recognised subset of the language for
describing logic behaviour in a manner which is suitable for such design automation tools.

There is, of course, a large amount of overlap between the two groups of users illustrated above, in terms of both the extent to which the
language elements are used, and the types of descriptions generated. Those involved with describing hardware for synthesis will often use
the language to create a simulation testbench in order to verify the correct functionality of their design. This will require a broader knowledge
of VHDL than that which is needed for RTL descriptions alone.

This module is concerned with the automated design of electronic systems and therefore it focuses on those aspects of the VHDL language
which are used in the development of RTL type descriptions for logic synthesis.

Overview of the VHDL Design Flow

For many years designers have used schematic based tools for capturing a design, making use of libraries of symbols representing the
available cells for a given technology. Like HDLs, schematics can be organised hierarchically such that sub-circuits are hidden inside symbols
which are placed on a schematic at a higher level in the hierarchy. It is generally accepted that there will always be an important role for
schematic-based design entry. This is due to the fact that a schematic representation will always have an immediate visual impact. As a
means to communicating structural information it is second to none.

Schematics have one fundamental limitation however: their inability to convey behavioural information in a totally unambiguous manner. A
good example of this is seen when one compares the logic diagram of a finite state machine design with its corresponding behavioural VHDL
description (see below). The function of the state machine is extremely difficult to establish from the logic diagram. However, the behavioural
VHDL description provides a clear indication of the operation of the state machine.
Partial Schematic and VHDL Representation of a FSM

Combining Schematics and VHDL

Many state-of-the-art design tools support design capture using both schematics and VHDL. This combines the visual impact of a block
diagram with the descriptive power of VHDL. In addition, simulation can be performed on systems comprising VHDL behavioural models and
standard schematic parts by connecting them together on a schematic diagram. The simulation netlist is structured such that the gate-level
simulation engine handles the schematic parts, while the VHDL simulator handles the VHDL models in a mixed simulation environment. Once
a design containing VHDL models has been verified to be functionally correct, the next step is to synthesise the VHDL descriptions into gate-
level netlists suitable for the implementation technology. Schematic representations of the synthesised circuits can be generated for viewing
purposes, but such schematics should not be edited using the schematic editor.

VHDL Design Flow

The flow diagram depicts a


typical VHDL design flow. The
starting point in any design
flow is a written specification of
the system being designed.
This should be as
comprehensive and
unambiguous as possible,
clearly setting out the
requirements of the system.

Having established the


specification for the system,
the next step is to create a
system-level VHDL model for
the design. This will form part
of the documentation for the
design and will also provide a
means to verify the
functionality using a VHDL
simulator. At this point we are
not too concerned about
writing VHDL code which is
synthesisable, the primary
objective of the system-level
description is to produce a
description which implements
the requirements set out in the
written specification. As shown
in the flow diagram below, the
system-level model will
constitute a number of VHDL
source files. These are ASCII
text files containing the source
description of the design. A
VHDL design can be
contained in any number of
source files and there is no
correlation between design
units and source files.

The system-level VHDL model


is subject to an iterative
process of modification as
changes are introduced in
order to correct errors and
ambiguities revealed by
simulation. At this initial stage
it is common for the designer
to produce a simulation test
bench for the design. This is a
VHDL description of a test
environment, within which the
design is placed, to provide
input stimulation and response
analysis. The test bench can
be used throughout the VHDL
design flow for verification
purposes. This is due to the
fact that the various
representations of the design
will share a common interface
specification (entity), even
though the internal details
(architecture) may be
significantly different.

Having satisfied ourselves that the system-level description is correct, the next step involves rewriting the VHDL description in the Register
Transfer Level style prior to synthesis. This approach is consistent with the top-down design methodology used in many design flows and
results in a description which contains more details concerning the way in which the design is to be implemented in hardware, ie. major
registers and functional units can be identified. This step is not as difficult as one might assume, once a decision has been made as to how to
implement the basic algorithmic behaviour of the design. It is then a case of mapping the required functionality onto standard sequential and
combinational behaviours. Simulation is carried out in order to verify that the RTL version of the design is consistent with the system-level
model created previously. One benefit of using VHDL is that the same simulator can be used for all style of descriptions throughout the design
flow.

The process of synthesis follows RTL simulation. This involves a separate software tool (it may share the same VHDL analyser as the
simulator) known as a Synthesis Tool. The process of synthesis will be covered in detail later in this module. In summary, synthesis involves
analysing the RTL description, synthesizing the RTL constructs into Boolean Equations, optimisation of the Boolean equations and finally
mapping of the logic onto the resources provided by the technology being targetted. The synthesis tool will generate a number of output files,
one of which will be a VHDL gate-level netlist version of the original design which is provided for simulation purposes.

The VHDL netlist file produced by the synthesis tool is equivalent to the low-level circuit which will be implemented in the target technology. It
is very important to verify that this detailed circuit correctly implements the functionality of the RTL description from which it was derived. This
is not to suggest that the logic synthesis tool may have bugs in it, but it is possible that the synthesis tool has interpreted the RTL description
in a manner that has resulted in a circuit which was not the desired implementation.

One final step, not shown in the flow diagram below, is timing simulation. The implementation tools for the target technology will produce
parasitic and fanout loading time delay information for the design. This information can be back annotated into the gate-level VHDL netlist,
allowing timing simulation to be carried out. This may be a very important step if the design is sensitive to implementation-dependent time
delays.

4.3 Levels of Abstraction Supported by VHDL

Design Abstraction represents a strategy for hiding detail and managing complexity. The higher the level of abstraction, the less detailed and
further away from the real physical implementation of a design the representation is. Given the complexity of contemporary designs,
abstraction is an essential part of the design process.

Closely related to abstraction is Hierarchy. This involves partitioning and organising designs into blocks having a well defined interface and
function. Blocks can be nested inside of other blocks. In this case the parent block sees only the interface to the child block, the internal
details of the child block are hidden from view. Blocks can be nested to any depth - in other words there can be any number of hierarchical
levels in a design. At the top level, a design will be described as a structure containing a number of blocks and these blocks will in turn
contain other blocks and so on. At the bottom of the hierarchy, the design will usually be described in terms of behavioural elements which do
not contain references to lower level blocks. As shown in the image below, different levels of abstraction can be used freely at any level within
the hierarchy.
The VHDL language
supports both abstraction
and hierarchy. Abstraction
is supported by means of
the rich diversity of
available statements and
constructs. These allow a
variety of different
descriptive styles to be
used. Hierarchy is
supported by means of
Instantiation, which refers
to the process of creating
an occurrence of a Design
Entity inside of another
design entity. Such
occurrences are referred
to as Components. The
image below shows how
design entities can be
instantiated within a
hierarchical structure.
Communication is via
signals connected
between the ports of the
design entities.

The VHDL language was


developed with the
intention of it being applied
to all phases of the design
cycle. For digital systems
in particular, there are well
defined levels of
representation. These are
listed below in bottom-up
fashion and expanded
upon in the relevant sub-
section

● MOS Switch Level


● Logic Gate Level
● Register Transfer Level
● Chip Level
● System Level

The levels given above are indicative and therefore do not need to be adhered to rigorously. In general, a VHDL description may involve the
simultaneous use of several different styles. A common scenario used for design verification involves the use of a so-called testbench
containing an RTL description of an FPGA design along with behavioural models of other chips and system elements.

MOS Switch Level

VHDL does not have inbuilt capabilities for circuit-level simulation in the
continuous time domain. However, it can be used to simulate digital circuits
made up from individual MOS switches. Each individual MOS switch is modelled
as an event-driven bi-directional switch. The use of VHDL for switch-level
simulation is relatively rare, due to the complexity of the switch model and the
consequential degradation in simulation performance.

Logic Gate Level

VHDL provides many features for the support of gate-level simulation, the
most useful being the Boolean operators (AND, OR, XOR etc.) which
correspond directly to the gate-level primitives. In addition to basic logic
gates, this level includes other low-level primitives such as flip-flops and
latches. VHDL descriptions written at this level are referred to as netlists
or wirelists due to the fact that they consist of a list of components and
interconnections. As such they are inherently difficult to read and debug.
VHDL netlists are often generated automatically from a schematic or as
part of the output of a synthesis tool.

Most PLD, FPGA and ASIC vendors have developed libraries of gate-level cells coded in VHDL for use with simulation tools.
These libraries are usually VITAL (link) compliant. This means that the cells have been modelled using a standard set of
guidelines and support facilities (the VITAL packages) in order to provide very accurate and consistent timing simulation. Gate-
level simulation is often performed during the latter stages of the design process after the design has been synthesised from a
higher level description. Typically a design will contain tens or hundreds of thousands of gates and therefore simulation
performance is an important issue. Most modern VHDL simulators have been 'accelerated' for gate-level simulation using the
VITAL standard. This means that the underlying functions used by the simulator have been hard coded into the simulation
kernel.
Register Transfer Level

Register Transfer Level (RTL) is by far the most


commonly used style of VHDL description. At this
level, systems are described in terms of
combinational and sequential functions at the
behavioural level. The functions are often
described using individual processes and
interconnected using signals to form a dataflow.
Typical functions included in an RTL description
are Registers, Counters and State Machines
along with combinational functions such as
Multiplexors, Arithmetic Units and Decoders. The
RTL style may include some structural information
via the dataflow. However, the individual
functional blocks are invariably described using
behavioural constructs rather than instantiated
gates and flip-flops. Many designs are made up
from registers interspersed with combinational
functions and together these form the datapath.
Transfers between registers and operations
carried out by combinational functions are
controlled by the controller or control path which is
usually a behavioural description of a Finite State
Machine.

The RTL level of description is possibly the most


useful of all the alternative levels and is
particularly relevant to those people involved with
producing real hardware using VHDL, ie. users of
logic synthesis, since this level is the accepted
style for designs which are intended for synthesis.
The majority of VHDL users are targetting
synthesis tools and therefore it is probably true to
say that the majority of VHDL descriptions are
written in the RTL style.

Chip Level

The high-level behavioural constructs available in the VHDL


language make it the ideal language for describing whole
integrated circuits. Chip-level VHDL descriptions fall into two
general categories - those intended for synthesis and those
intended for simulation only. The first category of descriptions
are made up from a collection of RTL descriptions forming a top-
level structure and as the design is intended for synthesis it will
not contain any detailed timing information other than basic
clocking relationships. The chip being described will invariably
be an FPGA or cell-based ASIC.

The second category of chip-level descriptions are those


intended for simulation purposes only. These are primarily
behavioural models whose purpose is to accurately simulate the
behaviour of the chip being modelled at the pin level. The
internal organisation of the model is not important. In order to
achieve this, the models generally include detailed timing
behaviour, extracted from the manufacturer's data sheets,
concerning the behaviour of the device interface pins under
most conditions. The power and range of the VHDL language
can result in very compact models of complex chips which
would be impractical to model at the gate level.

Many IC manufacturers are producing accurate VHDL models


for their standard devices (see, for example, Cypress
Semiconductors). Some of these models are freely available,
while others are only obtainable for a fee. Models are available
for a large range of devices covering simple gates to full-blown
microprocessors. The primary purpose of these models is to
enable a designer to perform a realistic simulation of a full
system which may include RTL descriptions of programmable
parts, memories, standard 'glue logic' and perhaps a
microcontroller. A term often used to describe this aspect of the
use of VHDL is 'System-level Simulation'.

System Level

As the above image suggests, 'system-level' may


involve describing a complete system comprising
multiple PCBs connected to a backplane bus,
although it should not be assumed that a system
level description must contain a vast amount of
structural information. Some entire systems can be
described in a very concise manner using the high-
level behavioural constructs provided by VHDL. In
addition to the models of the hardware
components making up a system, it is often
necessary to model the bus systems or networks
used to interconnect them. For example a VHDL
description of a computer interface card may be
extensively verified using another VHDL model of
the computer system bus and the two would be
connected together to form what is often termed a
VHDL Testbench.

The types and operators built-in to the VHDL


language allow a limited amount of mixed signal
simulation to be carried out. In particular, Analogue-
to-Digital and Digital-to-Analogue converters can
be described behaviourally, thereby facilitating the
verification of systems which involve an interface
to the real world.

With the impending arrival of the analogue


extensions to VHDL (VHDL-AMS) it will soon be
possible to describe any type of system comprising
event-driven and continuous-time elements, which
may be electrical or mechanical in nature.

4.4 Benefits and Implications of using VHDL

Useful link: Read through the Benefits of using VHDL section of the Hardware Engineer's Guide to using VHDL at the Doulos web site.

The hardware description language VHDL represents a significant addition to the digital system designer's toolbox. The language has many
features and benefits, along with some implications to the user. The following tables summarise the features, benefits and implications of the
VHDL design paradigm:

Features and Benefits of VHDL

Feature/Benefit Comments
Computer readable and The VHDL language has all of the capabilities of other general purpose high level
executable high level language languages in addition to being highly descriptive in nature. The wide ranging constructs
are very intuitive and can be used to model discrete time systems at various levels of
abstraction in a totally unambiguous manner.

Verbose language The lack of abbreviations in the language promotes readability. Constructs are structured
in a consistent and clear manner which is easy to work with.

Rich variety of high level constructs Most of the constructs required for digital system modelling are built into the language.
and operators Others are provided by standardised packages.

Built-in handling of All statements within the architecture body are effectively concurrent processes.
concurrent processes Simulators must be capable of scheduling the processes in order to reproduce the
semantics of the statements. All hardware events are communicated between processes
via signal events.

Built-in implementation independent The library and use context clauses allow easy access to libraries of predefined
support for libraries components, types and functions. The operating system dependent features of library
management are abstracted out of the language.

Full support for hierarchy The basic unit of hardware, the design entity, can be instantiated within another design
entity as a so-called Component. This allows a design to be described in terms of a
structural hierarchy. The traditional method of placing occurrences of one design entity
inside of another requires a component declaration. A shorthand method of instantiation
is provided in VHDL -1993, namely direct instantiation. Like most general purpose high
level languages. VHDL also provides support for sub-programs called functions and
procedures. These can be used to devolve a large sequential algorithm into a
manageable hierarchy well defined modules.

Flexible binding mechanism In a hierarchical description, all component references must be bound to design entities
in order for simulation to be carried out. Specific bindings between components and
entity architecture pairs is achieved using a Configuration Declaration.

Information hiding and sharing Types, functions and procedures etc. can be grouped together under one name in the
form of a Package. This supports shared design data and team design.

Powerful set of Attributes The use of attributes to extract information from a type or object allows for very concise
descriptions.

Standard/public language Being non-proprietary, the language is widely supported and designs are portable to
alternative design environments.

Simulation,description
One language supports the entire design cycle.
and synthesis language

Availability and portability of models There are numerous sources of VHDL models, some of which are free. Most silicon
vendors provide accurate low-level simulation models to support gate-level timing
simulation. Many companies provide behavioural models of their off-the-shelf
components for use in system simulation.

Promotes communication
A standard language allows communication between designers and tool sets.
between designers

Increased designer productivity The wide availability and ease of use of logic synthesis tools eliminates the need to enter
designs schematically at the gate level, thus drastically improving designer productivity.

High level design Design description is at the RTL/data flow level. This frees the designer to concentrate
on the concept of the design rather than the low level detail.

Improved quality Automated translation and optimisation of the gate level network allows a designer to
quickly explore alternative structures or algorithms. Hence the final results will be high
quality.

Consistency between levels Automatic synthesis guarantees that the pre- and post-synthesis networks are
of abstraction equivalent.

Reduction of layout design Technology-specific aspects of ASIC design, such as layout generation, are handled
expertise requirement automatically. This eliminates the possibility of layout errors.

Facilitates design sharing The readability, portability and technology independence of VHDL descriptions makes
and re-use them amenable to re-use. Many modelling companies are now in the business of selling
off-the-shelf VHDL descriptions in the form of intellectual property.
Implications of using VHDL

Implication Comments

Language-based methodology Designers who are used to the traditional design methods based around schematic capture
will require training in the use of new design processes tools and skills.

Future proof The potential lifetime of a design is extended beyond the lifetime of the underlying
hardware technology. Protects against technology changes.

Shorter development times Verify design concept before deciding on hardware. May also facilitate concurrent
engineering.

Awareness of synthesis pitfalls While automated netlist generation frees designer from the drudgery of low level detail,
he/she must have a thorough understanding of the limitations of the synthesis process.
Rubbish in equals rubbish out!

Widens hardware options Ease of retargetting PLD, CPLD, FPGA and ASIC technologies.

Textual descriptions Textual descriptions replace/augment schematics. This represents a significant change in
the design environment which may take some time to get accustomed to.

Software skills Adds software skills to hardware knowledge.

More simulation Promotes the use of more simulation during the development phase, allowing a designer to
explore a wider set of alternative solutions to a given problem. Simulation may be
perceived as 'non-productive' by management, but the long term gains will justify the
simulation overhead.

Increased awareness of testability Adopting a consistent style of coding (required by synthesis tools) with the ability to make
modifications quickly will encourage the designer to consider including additional testing
hardware in the design. The availability of automatic 'Test Coverage' tools for VHDL
designs will also promote Design for Test.
Advanced Electronic Design Automation

Chapter 5 - Elements of VHDL

Chapter Information

The chapter contains the following sections:

5.4 Sequential Processes


Using Variables
5.1 The Design Entity
A simple example - Two-to-one Multiplexer
Primaries and Secondaries 5.5 Built-in Types and Operators
The VHDL Network Model
5.6 VHDL Styles of Description
5.2 Signals and Concurrent Statements
The Concurrent Signal Assignment

5.3 The Simulation Cycle


Row-by-row Description of the Simulation Table

5.1 The Design Entity

The Hardware Description Language VHDL is a textual language for describing the structure, behaviour or data flow of a digital
system. A VHDL design consists of any number of ASCII text files, known as design files, containing descriptions of the basic building
blocks of the design known as design units. There is no fixed relationship between design files and design units and a single design file
may contain any number of design units or perhaps just a single design unit.

Design units are divided into two categories - primary and secondary. A combination of one of each of these two types, referred to as
the entity declaration and the architecture body, go together to form the basic hardware building block of a VHDL description - the
design entity.
The design entity represents a block of hardware with a well defined interface and a well defined function. The entity declaration,
among other things, specifies the name of the design and its external interface, the latter in terms of signal ports. The entity declaration
does not contain details of the function of the design. The internal behaviour or structure of an entity describes its function and how the
signals in its external interface (declared in the entity) interact with the outside world. This is the purpose of the architecture body.

A simple example - Two-to-One Multiplexer

The image below shows a simple example of a design entity for a two-to-one multiplexer. The gate-level circuit diagram is shown on
the left while the equivalent VHDL description is shown on the right. The VHDL text has been coloured to highlight the different parts of
the description such that all reserved words are blue, names are shown in grey, port signals are black, internal signals are dark
green, labels are purple and types are red. Despite the use of different cases, the language is not case sensitive, upper and lower
case characters can be freely mixed (apart from when they are used for character literals). It is good practice to try to maintain a
consistent style (at least within the same text file).

The upper text block contains the entity declaration for 'mux2to1', the main purpose of which is to declare the ports of the design entity.
The ports represent the interface signals which facilitate communication between the architecture body and the external environment.
They have a mode which dictates the direction of signal flow, and a type which dictates the values that can be assigned to the ports
(type 'bit' can take on values '0' and '1' only). In this example there are three inputs A, B and SEL which are all of the same built-in
VHDL type known as 'bit', and one output, also of type 'bit', named Y.

The lower text block on the right hand side of the above image is the architecture body part of the design entity. In this case the
architecture is written in the so-called dataflow style which makes use of individual concurrent signal assignment statements linked
together by signals. Each statement is equivalent to one logic gate in the schematic (this is not a requirement of the language, one
statement could be used in place of the four used here). The labels next to each statement (G1, G2, G3 and G4) correspond with
those used in the schematic. The architecture statements have access to the port signals (A, B, SEL and Y) as well as internal signals
(W1, W2 and W3), the latter shown in dark green. The mode of the port signals restricts where they can appear in a concurrent signal
assignment statement, input ports can only appear on the right hand side (input) of the signal assignment operator '<=' while output
ports can only appear on the left hand side of the assignment operator (target). Internal signals are local to the architecture and as
such cannot be accessed from outside the bounds of the architecture body. They have no mode and therefore can appear on either
side of the concurrent signal assignment statement.

The above example illustrates the basic structure of a VHDL description; we will return to this example when we consider the way in
which VHDL descriptions execute during a simulation run, later in this chapter.

Primaries and Secondaries


The entity declaration is a primary unit which defines a unique interface and therefore there can only be one entity of a given name.
The architecture body is a secondary unit which is paired with an entity to form a design entity. There can exist any number of
architectures associated with a single entity, each one having a different name. Each alternative architecture may describe a different
type of implementation of the entity's behaviour. The interface, as defined by the entity, is always the same.

The VHDL Network Model

The two-to-one multiplexer example uses a single design entity to describe a hardware function in terms of basic VHDL statements. In
general, VHDL descriptions can comprise any number of design-entities arranged in a hierarchical network, as illustrated by the image
below. Design entities which contain other design entities are sometimes referred to as structural descriptions and the lower level
design entities are known as components. Communication between the instantiated design entities is via signals connecting the ports
of two or more design entities together. There is no limit to the number of levels used in a hierarchical description and different styles of
description can be used at any level in the structure. At the lowest level in the hierarchy, there must exist behavioural descriptions,
written in terms of the native VHDL statements. These are required for simulation.

The top level design entity may correspond to the top level view of the design being described, or alternatively it may describe a so-
called testbench. A testbench is a VHDL structural description containing a component instantiation of the top-level design entity along
with other components or processes which form a complete simulation test environment for the top-level design. The fundamental
benefit of testbenches is that they make use of the VHDL language to both generate test stimulus patterns and analyse responses.
This makes the entire design and test environment extremely portable. In addition, VHDL contains some very powerful features for use
in the creation and diagnosis of test data. Testbenches will be covered in more detail in Chapter 6.
Read sections 3.1, 3.2 and 3.5 of Chapter 3 of the recommended textbook (VHDL for Logic Synthesis by A. Rushton).

5.2 Signals and Concurrent Statements

At the very heart of the VHDL language lies the fundamental unit of execution referred to as a process. All statements which can
appear within an architecture are effectively processes. An individual process will have a set of input signals to which the process
responds (or is sensitive to), and will drive certain output signals. One very important property of all architecture statements
(processes) is that they execute concurrently.

There is one particular concurrent statement which is known as the process statement; this statement is used to encapsulate a group
of sequential statements which form an algorithm. The process statement will be introduced in section 5.4 Sequential Processes.

The Concurrent Signal Assignment

The concurrent signal assignment statement is one of the most commonly used statements for describing logic using VHDL. The
image below shows a simple example of a concurrent signal assignment statement describing an inverter. The statement is equivalent
to a process having an input signal 'a' and an output signal (target) 'q'. The process is activated by an event (a change in a signal from
one value to a different value) on signal 'a'. Whenever an event occurs on 'a', the statement executes and a new value is scheduled for
the target 'q'. The new value of 'q', which is the result of evaluating the expression 'not a', does not appear on 'q' immediately, but after
a notional delay of one delta. This notional delay is equivalent to a time period of zero seconds, it is used to allow the simulation tool to
synchronise the execution of multiple concurrent processes.
When the input signal to the inverter 'a' is stable, the
statement is said to be suspended. It is effectively waiting for
the next event to occur on 'a'. In this manner the statement
describes the behaviour of a logical inverter having zero delay.

If it was required to describe an inverter having a delay of


10ns, then the expression 'not a' would be followed by a delay
clause which would have the effect of delaying the
assignment of the new value to 'q' by 10ns.

q <= not a after 10ns;

It should be noted that, while the above delay would be seen


in the simulation waveforms, a logic synthesis tool would
ignore it. This is due to the fact that a synthesis tool does not
know how to create a circuit to provide a precise delay of
10ns.

It is far more common for an architecture to contain multiple concurrent statements, as shown in the image below. This design entity
represents an inverter made up from three individual inverters connected in series. The three statements labelled g1, g2 and g3 are
order independent, ie. they can appear in any order. The execution of the statements is controlled by events on the signals linking
them together. The model starts in a stable condition corresponding to 'a' at logic-1 and 'z' at logic-0. An event on 'a' (change from '1' to
'0') triggers statement g1 to execute; this schedules a '1' for signal 'b' after one delta delay. At a time equal to (20ns + 1 delta), signal 'b'
goes to logic-1 causing the statement labelled g2 to execute. The execution of g2 causes a '0' to be scheduled for signal 'c' one delta
delay later, at a time of (20ns + 2 delta). Finally, at (20ns + 2 delta) signal 'c' goes to logic-0, triggering statement g3. The execution of
g3 results in a 0-to-1 event on the output 'z', at a time of (20ns + 3 delta). Since 'z' is an output port, there are no further executions of
statements within the architecture (until the next event on 'a'); the 'z' output may trigger other processes to execute in other design
entities which have 'z' as an input.

The delta delay mechanism can be useful in revealing circuits which contain potential hazards, as shown by the example below. All
assignments having no delay clause have an effective delay of one delta. When the input signal 'a' changes from '0' to '1', the event
triggers both statements g1 and g2. The result is that the output 'z' is scheduled to change to '1' after 1 delta and the internal signal 'b'
is scheduled to change to '0' after one delta. After one delta (ie. at the next simulation cycle), 'z' goes to logic-1 and statement g2 is
triggered by the event on 'b'. The result is that 'z' is scheduled to return to '0' after a further delta. As shown by the waveforms below,
the output 'z' has a 1-delta glitch on it, reflecting the behaviour of the real circuit. (Such a short pulse is very difficult to detect on the
simulation waveforms since it has a width of zero!)
The above examples illustrate the use of simple logical expressions in concurrent signal assignment statements. In addition to
representing individual logic gates as above, a logical expression can be used to describe more complex structures in a single
statement. The following example shows how a single expression can describe a 4-bit equality comparator. This example also
demonstrates the use of vectored ports. Both the 'a' and 'b' inputs are 4-bit signals, hence they are declared as Bit_Vectors rather than
Bits. In the architecture, the individual bits are compared using the built-in XNOR operator, the results are then logically ANDed to form
the output 'aeqb'.

The above style of description is relatively low level - it does not make full use of the statements provided by the VHDL language.
There are two very powerful concurrent signal assignment statements - the conditional signal assignment and the selected signal
assignment - both can be used to describe logic without the use of low level logical operators. The above 4-bit equality comparator is
repeated below, but this time the conditional signal assignment statement is used rather than the logical operators.

entity comp4 is port(


a, b: in bit_vector(3 downto 0);
aeqb: out bit);
end comp4;

architecture cond_ass of comp4 is


begin
s1 : aeqb <= '1' when (a = b) else '0';
end cond_ass;

The above description makes use of one concurrent signal assignment statement (labelled s1) which is sensitive to events on input
ports 'a' and 'b' (statement s1 is in fact sensitive to every individual bit of bit_vectors 'a' and 'b'). Whenever an event occurs on signals
'a' or 'b', the logical expression (a = b) is evaluated and if found to be true (ie. each bit of 'a' matches the corresponding bit of 'b'), the
target signal is driven to a '1'. If the condition (a = b) is false, the output 'aeqb' is driven to '0'. The equality operator is one of a set of
comparison operators which is valid for bit_vector type operands, the other comparison operators, such as 'greater than' and 'less
than', can also be used. Take a look at the example description of a 4-bit magnitude comparator contained in the VHDL Examples
resource.

The conditional signal assignment statement is a very powerful statement for describing digital logic in a concise manner. The simple
concurrent signal assignment (ie. one that unconditionally assigns an expression to a target) is effectively a sub-statement of the
conditional signal assignment, since the 'when....else' part is optional. This last point is clearly defined by the IEEE language syntax
definition for a conditional signal assignment which is repeated below for convenience.

conditional_signal_assignment ::=

target <= options conditional_waveforms ;

The conditional_waveforms part of the statement comprises an optional and repeatable part enclosed in curly braces and an optional
part enclosed in square brackets. Therefore it could simply consist of a waveform.

conditional_waveforms ::=

{ waveform when condition else } waveform [ when condition ]

The waveform comprises at least one waveform_element plus any number of additional waveform_elements separated by commas.
This allows complex patterns to be assigned to a target signal. Each waveform_element consists of a value and an optional time delay.
For a simple signal assignment there will be just one waveform_element and this will consist of a value_expression with an optional
time delay clause.

waveform ::=

waveform_element {, waveform_element } | unaffected

The value_expression may be any legal expression which produces a result which has the same type as the target. In the previous
examples we have seen the use of expressions made up from signals and Boolean operators to describe digital logic. In fact the
value_expression could be a simple literal value such as '1' or X"AA" (hexadecimal notation).

waveform_element ::=

value_expression [ after time_expression ] | null [ after time_expression ]

Read sections 3.4 and 3.6 - 3.10 of Chapter 3 of the recommended textbook (Rushton).

5.3 The Simulation Cycle

This section looks in more detail at the way in which a VHDL description of a logic network executes during a simulation run. The
discussion is based around the example used in the first section of this chapter concerning the Design Entity. The circuit diagram of
the 2-to-1 multiplexer is shown in the image below.
The above logic circuit can be described directly in terms of concurrent signal assignment statements as shown below. We will
consider the details of this design from the simulation viewpoint, assuming that the circuit is operating as a stand alone design entity.
The top level input ports - A, B and SEL, will be forced to certain values during the simulation period. This would be carried out by
typing commands at the simulator command prompt.

All VHDL descriptions can be thought of as a collection of concurrent processes which communicate using signals. This example
consists of four such processes in the form of concurrent signal assignment statements labelled G1, G2, G3 and G4. This section
describes the low level interaction of these processes as the simulation proceeds.

entity mux2to1 is
port(A, B, SEL : in bit; Y : out bit);
end mux2to1;

architecture gates of mux2to1 is


signal W1, W2, W3 : bit;
begin
G1 : W1 <= not SEL;
G2 : W2 <= A and SEL;
G3 : W3 <= W1 and B;
G4 : Y <= W2 or W3;
end gates;

The simulation of the above model is described using the 'Simulation Table' shown below. The table details the sequence of events
which take place as the simulation proceeds, the left hand column contains the row number which will be used to identify individual
rows. Next to the row number column are the 'time' and 'delta' columns. These contain the value of the total simulation time and the
number of deltas which have elapsed respectively. The delta column resets to zero each time a new input event occurs. This is due to
the fact that the delta delays correspond to the propagation of the signal events through each stage of the logic. Since each concurrent
signal assignment statement in the example description is a zero-delay assignment, each one introduces an effective delay of one
delta.

The 'Signals' column lists the value of each signal (including ports) as the simulation time advances and the 'Execute' column contains
the label corresponding to any statement which is active (executed) during the current simulation cycle. An architecture statement will
execute whenever an event occurs on any signal appearing on the right hand side of the assignment operator (<=).

The 'Drivers' column keeps track of the projected value of each signal as simulation proceeds. In a VHDL description, all concurrent
signal assignments create what is termed a driver for the signal being assigned to. Signals which are of type 'bit' are allowed to have
only one driver which contains the projected waveform for the signal being assigned to (shown in blue). When a signal assignment
statement is executed, the driver for the target signal is updated, but the new value is not placed on the signal until the specified delay
has elapsed, or after one delta if the assignment does not involve a delay. Hence, in the simulation table shown below, the values of
the signal drivers are transferred across to the 'Signals' on the next row, ie. after a delay of one delta.

The execution of the statements (or processes) is governed by the signal events which take place during simulation. These are
highlighted in red in the simulation table. An event occurs when the value of a signal changes from 0-to-1 or 1-to-0.
Row-by-row Description of the Simulation Table

Row number 1 - corresponds to time zero, all uninitialised signals are given the default value of '0' (the left-hand element in the type
definition of type 'bit'). The VHDL language requires that all processes execute once at the beginning of the simulation, irrespective of
sensitivity. This is due to the fact that the history of the signals is not known. All statements G1, G2, G3 and G4 are executed using
input values of '0' and the resulting drivers are shown in the right hand columns. The only signal to have changed is internal signal W1
(not SEL). The driver contains a '1' indicating that this signal will become a '1' after a delay of one delta.

Row number 2 - the values of the drivers from row 1, for signals W1, W2, W3 and Y, are transferred to the signal columns and the
number of deltas is incremented to one. Signal W1 has become a '1' and hence an event has occurred on W1 (highlighted). The
primary input signals A, B and SEL have not changed at this point. The event on signal W1 triggers statement G3 to execute, since W1
appears on the right hand side of the statement. The execution of the statement 'W3 <= W1 and B;' uses the current values of W1 and
B, ie. those inserted in row 2. The result is '0' and therefore the drivers columns remain unchanged.

Row number 3 - the values of the drivers match those of the corresponding signals in row 2, indicating that there are no further events
to propagate. The '-' entries show that there is 'nothing left to do' in row 3.

Row number 4 - primary input B is forced to logic-1 thereby creating an event on B. The event on B triggers statement G3 to execute
which causes the driver for signal W3 to be updated to '1' since both B and W1 are at '1' in row 4. Note that the delta column has been
reset to zero to indicate the start of a new set of propagations.

Row number 5 - The drivers from row 4 are transferred to the signals in row 5 causing W3 to become a '1'. The delta column indicates
one delta has elapsed. The event on W3 triggers statement G4 (Y <= W2 or W3;) and since W3 is a '1', the driver for Y is updated to
'1'.

Row number 6 - The drivers from row 5 are transferred to the signals in row 6 causing the Y output port to become '1'. The delta
column is incremented by 1 to become 2. The Y output port has an event on it, but this event does not cause any further activity within
the confines of the design entity 'mux2to1'. If the the 'mux2to1' design entity had been part of a larger design, then the event on Y may
have caused other processes to execute in other parts of the system.

Row number 7 - this row is similar to row 3 in that there are no further events to propagate and the simulation engine has 'nothing left
to do'.
Row number 8 - primary input SEL is forced to logic-1. The event on SEL causes statements G1 and G2 to execute, since it appears
on the right hand side of both. The result is that a '0' is added to the drivers for W1 and W2. Therefore W1 is scheduled to change
value whereas W2 is scheduled to have a transaction (the driver is updated with a value which matches its current value). Again, as
before, the delta column is reset to 0.

Row number 9 - The drivers from row 8 are transferred to the signals in row 9 causing W1 to go to '0'. Delta is incremented to 1 to
indicate that one simulation cycle has elapsed. The event on W1 triggers statement G3 to execute (W3 <= W1 and B;) and since W1
has gone to '0', the driver for W3 is also updated with the value '0'.

Row numbers 10 and 11- in row 10 , W3 is updated with it's driver causing a 1-to-0 event on W3. This triggers the execution of
statement G4 which predicts a value of '0' for the output Y. In row 11, the drivers from row 10 are transferred to the corresponding
signals and the resulting event on output Y has no further effect. The original event on the SEL input has taken 3 deltas to propagate
through to the Y output, corresponding to three stages of logic (inverter, AND gate and OR gate). The simulation time remains at 20ns
to reflect the fact that the logic has zero delays.

Row number 12 - all events resulting from the change in the SEL input have been processed and the simulator is waiting for the next
input event to occur.

Row numbers 13, 14 and 15 - At 30ns, input A is forced to logic-1. This event is propagated through two stages of logic (AND-OR)
resulting in Y going to '1' at 30ns + 2 delta.

Row number 16 - All events have been dealt with and the circuit is stable.

Summary

The above table and associated notes provide a detailed account of how a VHDL simulator responds to events and keeps track of
signals using drivers and delta delays. In general, to make use of VHDL for the purposes of generating hardware, it may not be
necessary to fully understand the underlying mechanisms being used by the simulator. However, for those engineers who are involved
in producing simulation models, and to a certain extent those who create test benches for simulation, an appreciation of the semantics
of the language is an essential pre-requisite to producing efficient and elegant models.

Read section 3.3 of Chapter 3 of the recommended textbook (Rushton).

5.4 Sequential Processes

The statements enclosed within the architecture body of a design entity are concurrent statements and all the statements are in a state
of suspension or activity depending on the signal events taking place within the model. The architecture statements work in much the
same way as the component parts of a hardware system. There is one particular concurrent statement which shares the same name
as the basic unit of execution in a VHDL description - this is known as the process statement. The process statement embodies a
sequence of statements taken from the special set of VHDL statements known as sequential statements. These statements are very
similar to those used in other general purpose high level languages (if..then, case, loop etc.) and they are used to describe hardware in
terms of an algorithm.

An architecture can contain any number of totally independent process statements, each one containing a unique sequential algorithm.
All the process statements execute concurrently, in the same manner as other architecture statements.

The VHDL language supports both concurrency and sequentiality and it is the interaction of these two domains which is perhaps one
of the most difficult aspects of the language to understand. There are a number of standard templates which are commonly used to
describe hardware using sequential processes. Once these have been fully understood, the subtleties of process execution may be put
to one side. The availability of sequential processes provides the designer with a very powerful descriptive tool since many types of
complex hardware systems can be described in a concise manner using a sequence of high-level statements.

The process statement can take one of two basic forms:

● Process statement with sensitivity list


● Process statement with wait statements

The two forms given above are incompatible, a process which uses a sensitivity list cannot contain wait statements, and vice versa.
This limitation is deliberately imposed to avoid ambiguity - it does not constitute a limitation of the language.

The first form of the process statement is often used to describe combinational logic (although not exclusively). The image below
illustrates the basic syntax and flow of execution. The sensitivity list contains a list of signal names to which the process is sensitive
(sig1, sig2, sig3..), events on one or more of these signals cause the entire process to execute, starting with the sequential statement
following the keword begin and ending with the statement preceding end process (see green arrow). The signals enclosed within the
sensitivity list act exactly like those which appear on the right hand side of a concurrent signal assignment statement, ie. they trigger
the execution of the process. Once the process has executed, it returns to the top statement (grey arrow) and goes into a state of
suspension while waiting for the next event to occur on one of the input signals. This corresponds to the top of the process indicated by
the red arrow. A process can be in one of two states - either suspended (waiting for a signal event) or active (responding to a signal
event). A process never terminates.

Example: An n-input AND Gate

1 entity and_n is

2 generic(n : positive := 8); --no. of inputs


3 port(A : in bit_vector((n-1) downto 0);
4 F : out bit);
5 end and_n;

6 architecture using_loop of and_n is


7 begin
8 process(A)
9 variable TEMP_F : bit;

10 begin
11 TEMP_F := '1';
12 for i in A'range loop
13 TEMP_F := TEMP_F and A(i);
14 end loop;
15 F <= TEMP_F;
16 end process;
17 end using_loop;
This example introduces several features of the VHDL language which will be mentioned briefly here in order to concentrate on the
process statement. The description is that of a Logical-AND gate having a parameterised number of input ports. This is a common
feature of VHDL descriptions which is designed to be flexible and therefore re-usable. The number of inputs is defined by the value of
the generic port 'n', the type of which is positive, since the number of inputs cannot be 0 or negative. The generic port is given a
default value of 8. This is required if the description is to be simulatable and synthesisable, since the VHDL analyser must be able to
determine the width of all signals. (The default value can be overidden by an alternative value when the design entity is used as a
component in another design entity).

On lines 3 and 4, the input port 'A' is declared as a bit_vector having 'n' bits arranged in descending order (the preferred direction) and
the output port 'F' is declared as a single bit. The architecture begins on line 6 and contains effectively one statement, the process
statement spanning lines 8 to 16 inclusive. The process is sensitive to an event on any bit of the input port A. When such an event
occurs, the process executes the statements on lines 11 through to 15 in the order that they appear.

The process makes use of a locally declared variable, named 'TEMP_F' (line 9), to hold the intermediate result of a 2-input AND
operation. This is a typical example of how a local variable is used within a process. The scope of the variable is limited to the bounds
of the process. A synthesis tool would interpret the variable as a set of internal signals interconnecting the AND gates. Each time the
process executes, the variable is initialised to '1' prior to entering the loop statement. The loop iterates through the range of the input
port ((n-1) down to 0), logically ANDing TEMP_F with each bit of the input port 'A' so that, on exit from the loop, TEMP_F is the AND of
all of the bits of A.

The loop statement is a very useful sequential statement and is commonly used to perform a repetitive or iterative operation. The loop
counter 'i' (line 12) is implicitly declared by the loop statement and acts as a constant within the bounds of the loop. The attributerange
is used to extract the limits of iteration for the loop from the input port A. The expression "A'range" is equivalent to '((n-1) down to 0)'.

The last statement in the process (line 15) is a sequential signal assignment. This statement assigns the value of the variable TEMP_F
to the output port F which updates the driver for F such that it will take on the value of TEMP_F after one delta delay. The entire
process executes during one simulation cycle, any signal assignments updating the corresponding drivers for the signals being
assigned. Having executed, the process suspends at the top of the process, at line 10, until another event occurs on A.

The above example of an n-input AND Gate shows a typical application of the process statement. It would be very difficult to create
such a gate using a concurrent signal assignment statement. The execution of the process statement is identical to an equivalent
concurrent signal assignment statement in that it responds to events on an input signal and updates the driver for the output signal.
Another example showing the use of processes to describe combinational logic can be found in the VHDL Example Resource - Two-to-
four decoder.

Processes with Wait Statements

The above image shows one particular form of a process containing a wait statement. The process shown above shares the same
simulation behaviour as the process which uses a sensitivity list shown in the previous image. The wait statement is one of the most
important statements contained in the VHDL language; its basic function is to suspend a process. The wait statement shown above
provides the same functionality as the sensitivity list even though the process appears to be suspended at the bottom of the statement
part rather than at the top. This is due to the fact that both positions are effectively the same when considering suspension. The reason
for having the wait statement at the bottom of the process rather than at the top is to ensure that the process executes at least once -
at the beginning of a simulation run - to match the behaviour of a sensitivity-list process.

Example: A 2-input Exclusive-NOR Gate


1 entity x_nor is
2 port (
3 in1 : in bit ;
4 in2 : in bit ;
5 out1 : out bit) ;
6 end x_nor;

7 architecture behaviour of x_nor is


8 begin
9 process
10 begin
11 if in1 = in2 then
12 out1 <= '1' after 10 ns;
13 else
14 out1 <= '0' after 10 ns;
15 end if;
16 wait on in1, in2;
17 end process;
18 end behaviour;

This example makes use of a process to describe the behaviour of a 2-input Exclusive NOR gate (a 2-bit identity comparator). The
process has no sensitivity list (line 9) due to the presence of a wait statement (line 16). At the start of a simulation run the process will
execute, irrespective of the states of the input signals 'in1' and 'in2'. Given that both inputs will take on their default initial value of '0',
the output will be scheduled to become a '1' after a delay of 10ns. The process will then suspend at line 16 until an event occurs on
one or more of the inputs. Suppose input 'in1' is forced to '1' at 30ns, this event will cause the process to resume execution from the
top and the if statement Boolean condition will be false. The output signal driver will be updated with the value '0' after a time delay of
10ns, hence 'out1' will go low at 40ns.

The two example VHDL descriptions used in this section contain just one process to describe the behaviour of the logic. In general, an
architecture can contain any number of processes, each process being an independent sequential program. The execution of
processes is often determined by signal events, either via a sensitivity list or a wait statement. There are some situations where a
process must suspend and resume execution without having to wait for signal events. Such processes make use of another variation
of the wait statement, discussion of which will be deferred until a later chapter.

Using Variables

The final example in this section reinforces the use of a variable to hold an intermediate value within a process. A common modelling
error is to use a signal to hold an intermediate value within a process rather than a variable. Although it is not fundamentally incorrect
to use a signal, it must be remembered that a signal does not get updated immediately following an assignment to it. At least one delta
must elapse before the driver of the signal updates the signal itself which is equivalent to reaching the end of a process (assuming the
process contains no wait statements), ie. the point at which the process suspends and waits for the next signal event.
Generally, it is far safer (and more efficient) to
use a variable to hold an intermediate value
within a process. The diagram to the right shows
a four-input multiplexer and the corresponding
entity declaration.

The first attempt at an architecture body for the


multiplexer is shown below. As the name of the
architecture suggests, the modeller has
incorrectly used an internal signal (muxval) to
hold the integer equivalent of the binary input
address. The multiplexer is described in terms of
a single process which is sensitive to all of the
inputs, an event on one or more of these inputs
causing the process to execute from beginning to
end, causing the driver for the output 'q' to be
updated. The author of this architecture has
mistakenly assumed that the signal 'muxval', after
having been initialised to zero, will have a value
between 0 and 3 (depending on the values of 'a'
and 'b') prior to being used in the case statement.
The basic problem is that the signal 'muxval' does
not get updated within the process, so the
assignment 'muxval <= 0;' does not change the
value of the signal 'muxval', just its driver. On
entry to the first if..then statement, the signal
'muxval' is still equal to the initial default value of
an integer type object (-MAXINT). The first if..then
statement tests the value of input 'a' and adds 1 to 'muxval' if 'a' is equal to '1'. Since 'muxval' is a signal, only the driver for 'muxval' is
updated (to -MAXINT + 1). The second if..then tests the 'b' input, and because of the cumulative nature of signal assignments within a
process, the driver for 'muxval' could be overwritten by the value (-MAXINT + 2).

The value of the signal 'muxval' on entry to the case..when statement will not be what the modeller expected. Instead of being between
0 and 3 (ie. the decimal equivalent of b&a), it will still be equal to the default initial value for an integer (-MAXINT). The value of
'muxval' will not change until the process suspends, ie. after the case..when statement, by which time it is too late. In addition, the new
value assigned to 'muxval' at the end of the process is likely to be incorrect, due to the fact that each successive assignment
overwrites the previous one.
The solution to the problems described above is to replace the internal signal declaration with a process variable declaration, as shown
in the 'better' architecture below. The variable 'muxval' must be declared within the process statement (variables cannot be declared
within an architecture). The fundamental difference between a variable and a signal is that a variable has no time dimension and
therefore no driver; assignments to a variable take immediate effect. The value of the variable 'muxval' will lie between 0 and 3 prior to
the case..statement, where it is used to choose between one of the four inputs and assign the correct one to the output 'q'.

One final modification, which would result in a more efficient description for synthesis purposes, is to constrain the range of possible
values which can be assigned to the variable 'muxval'. Given the architecture body shown below, a synthesis tool may allocate a 32-bit
signal to the object 'muxval' since all integer type objects are 32-bits in length. This can be avoided by appending the declaration of the
variable with a range constraint, as shown below:

variable muxval : natural range 0 to 3;

Constraining the range of objects in this manner is generally a good idea for synthesis and simulation, since a simulator will flag an
error if a constrained object is assigned a value outside the allowed range.

Read sections 8.1 - 8.5 of Chapter 8 of the recommended textbook (Rushton).


5.5 Built-in Types and Operators

The VHDL language provides the designer with a comprehensive set of built-in types which can be used in a variety of modelling
situations. Like most other high-level languages, it supports the use of the two common classes of object known as variable and
constant. Constants can be declared and used just about anywhere in a VHDL description. In hardware terms a constant is equivalent
to a hardwired value, or a Read Only Memory. Variables are generally restricted to being used to hold intermediate values within a
process or sub-program; a process variable is interpreted by a synthesis tool as a local signal.

The most important class of object supported by VHDL is the signal, the purpose of which is to model the physical interconnections
that form the communication channels in an electronic system. We have already seen some examples of signal objects in use, such as
entity ports and architecture internal signals, the latter being used to link together processes within an architecture body. The previous
examples have also made use of a limited number of the built-in types provided by VHDL, such as type 'bit' - the basic built-in logical
type, and 'integer' - the basic discrete numeric type.

The built-in VHDL types are defined in a special primary unit known as package standard which is implicitly used by all VHDL design
entities. This package defines a number of types, some of which are implementation dependent. The table shown below presents a
summary of the standard types and their typical usage.

Type Class Application Synthesis

The result of a comparison operation, rarely


Boolean enumeration type Synthesises to a single wire
used as a logical type

bit enumeration type Basic 2-state logical type Synthesises to a single wire

Commonly used in literal form for messages,


character enumeration type Rarely used
strings and other types

Can make use of arithmetic operations,


Synthesises to a bus having sufficient wires to
integer discrete numeric type represents two's complement values in
represent the range of values
arithmetic circuits

Can make use of arithmetic operations,


Synthesises to a bus having sufficient wires to
natural discrete numeric type represents unsigned values in arithmetic
represent the range of values
circuits and generics

Can make use of arithmetic operations,


Synthesises to a bus having sufficient wires to
positive discrete numeric type represents unsigned values in arithmetic
represent the range of values
circuits and generics

Can be used to represent analogue values in


real floating-point type Not synthesisable
simulation of discrete-time systems

time physical type Used in delay clauses for simulation Not synthesisable

Commonly used in literal form for messages,


string array of character Rarely used
strings and other types

Used for uni-directional buses of wires and


bit_vector array of bit Synthesises to a bus of wires
registers

VHDL Standard Types and Uses

In addition to the standard types listed above, there is an additional set of types which have been standardised by the IEEE, known
collectively as std_logic. The definition of the std_logic types and associated operators are contained in the IEEE standard package
std_logic_1164. This package is universally supported by simulation and synthesis tools and normally resides in a library named IEEE.

The basic single-bit logical type defined by package std_logic_1164, known as std_logic, was designed to overcome the shortcomings
of VHDL built-in type bit. An object of type bit can only take on one of two values ('0' and '1'), whereas std_logic type objects can be
assigned any one of nine values. In addition to '0' and '1' these include high impedance ('Z'), unknown ('X') and unitialised ('U'). Note
that type std_logic uses the same character literals as the built-in type bit, to represent logic-0 and logic-1.

The type std_logic and associated support functions and packages were developed in order to provide the design community with an
industry standard type, which could be used in realistic VHDL simulations of hardware systems that involve three-state logic and
resistive pull-up circuitry. Of the nine values defined in package 'std_logic_1164' for type std_ulogic, only three have any interpretation
for logic synthesis - '0', '1' and 'Z' (logic-0, logic-1 and high impedance). The remaining six metalogical values are provided for
simulation purposes only and as such are generally ignored by synthesis tools.

The table below lists the types defined by the IEEE std_logic_1164 package.

Type Class Application Synthesis

Unresolved base type, signals of type std_ulogic can have


std_ulogic enumeration type Synthesises to a single wire
only one driver. Seldom used

Resolved sub-type of above, the industry standard single-bit


std_logic sub-type of std_ulogic Synthesises to a single wire
logical type. Can have multiple drivers

std_ulogic_vector array of std_ulogic Uni-directional buses and registers. Seldom used Synthesises to a bus of wires

std_logic_vector array of std_logic Used for bi-directional three-state buses and registers Synthesises to a bus of wires

IEEE Standard Logic Types

Of the four types listed in the table shown above, types std_logic and std_logic_vector are by far the most commonly used. Signals
of these types are resolved which means that more than one signal assignment statement can assign to a single std_logic type signal.
In other words the signal can be allowed to have multiple drivers (each signal assignment statement creates a driver). The value
assigned to a std_logic signal is calculated using the values of all of its drivers by the so-called resolution function. This function
(named resolved) will determine the most sensible value for the signal each time any one or more of the signal assignment statements
driving the signal executes. The resolution function is defined in the IEEE package 'std_logic_1164' and, because most simulators
have been optimised for its use, it does not have an adverse affect on simulation peformance and is completely transparent to the
user.

The image shown below illustrates the use of the resolved type std_logic. A single bus wire named 'tribus' is driven by three concurrent
signal assignment statements, each one describing a three-state buffer. The signal 'tribus' has multiple drivers and therefore it must be
a resolved type (using type 'bit' rather than 'std_logic' would result in the analyser error message 'unresolved object has multiple
drivers'). Each time an event occurs on any of the buffer input or enable signals, the drivers for 'tribus' are updated and the resolution
function associated with type std_logic is executed to determine the correct value to assign to the signal.
Note the inclusion of the library and use clauses preceding the entity declaration in the VHDL listing above. These are always
required when std_logic types are being used. In addition, the buffer enable signals 'aen', 'ben' and 'cen' need not have been declared
as type std_logic since they should only ever be logic-0 or logic-1 (type bit would suffice). In general, it is good practice however, to
use type std_logic (std_logic_vector) for all signals in a design description, regardless of whether all nine possible values are being
used. This simplifies the modelling process and avoids the need to convert between different types within the description.

The following table illustrates some of the possible values which could exist in the circuit shown in the image above. The left hand six
columns show the values of the buffer input and enable signals while the right hand two columns show the values of the drivers for
signal 'tribus' and the value of the signal itself, respectively. The first three rows show alternative situations for the circuit signals,
resulting in signal 'tribus' being assigned a correct value. The last row illustrates a conflict situation where two buffers are enabled at
the same time while driving opposing values - the result is that 'tribus' is forced to an unknown state.

a b c aen ben cen drivers tribus

1 0 0 1 0 0 (1,Z,Z) 1

0 1 0 0 0 1 (Z,Z,0) 0

1 0 1 0 0 0 (Z,Z,Z) Z

0 1 1 1 0 1 (0,Z,1) X

Typical Signals and Drivers for 'tribus_demo'

Both the built-in and std_logic types can be combined with operators to form expressions. The VHDL language is a strongly-typed
language and therefore many of the freedoms enjoyed by programmers using other languages are not available when using VHDL.
The types of the arguments must be consistent with the operators being used in an expression and the overall type of the expression
must be an exact match with the type of the target being assigned. The following table lists, in descending order of precedence, the
operators supported by VHDL.

Operator Class** Supported Synthesisable Types Comment/Synthesis Constraints

** Miscellaneous Integer (and sub-types) Exponentiation - exponent must be constant value of 2

abs Miscellaneous Integer (and sub-types) Absolute value

Bit, Boolean and Bit_vector (std_logic and


not Miscellaneous Logical inversion
std_logic_vector)*

* Multiplying Integer (and sub-types) Multiplication

Division - right hand operand must be a constant power of


/ Multiplying Integer (and sub-types)
2

Modulo arithmetic - right hand operand must be a constant


mod Multiplying Integer (and sub-types)
power of 2

Remainder after Division - right hand operand must be a


rem Multiplying Integer (and sub-types)
constant power of 2

+ Sign Integer (and sub-types) Plus sign

- Sign Integer (and sub-types) Negate

+ Adding Integer (and sub-types) Add operator

- Adding Integer (and sub-types) Subtract operator

Bit and Bit_vector (std_logic and Concatenation - allows merging of signals and buses to
& Adding
std_logic_vector)* form larger buses

sll Shifting Bit_vector Shift left logical - right hand operand must be a constant

srl Shifting Bit_vector Shift right logical - right hand operand must be a constant
Shift left arithmetic - right hand operand must be a
sla Shifting Bit_vector
constant

Shift right arithmetic - right hand operand must be a


sra Shifting Bit_vector
constant

rol Shifting Bit_vector Rotate left - right hand operand must be a constant

ror Shifting Bit_vector Rotate right - right hand operand must be a constant

= Relational All types Equality

/= Relational All types Inequality

< Relational All types Less than

<= Relational All types Less than or equal

> Relational All types Greater than

>= Relational All types Greater than or equal

Bit, Boolean and Bit_vector (std_logic and


and Logical Logical-AND
std_logic_vector)*

Bit, Boolean and Bit_vector (std_logic and


or Logical Logical-OR
std_logic_vector)*

Bit, Boolean and Bit_vector (std_logic and


nand Logical The NOT of Logical-AND
std_logic_vector)*

Bit, Boolean and Bit_vector (std_logic and


nor Logical The NOT of Logical-OR
std_logic_vector)*

Bit, Boolean and Bit_vector (std_logic and


xor Logical Exclusive-OR
std_logic_vector)*

Bit, Boolean and Bit_vector (std_logic and


xnor Logical The NOT of Exclusive-OR
std_logic_vector)*

*the logical operators for std_logic and std_logic_vector are contained in the IEEE std_logic_1164 package.
**operators of the same class have equal precedence, in an expression they are processed left-to-right.

VHDL Standard Operators in Order of Precedence

The logical operators have the same precedence. They are not allowed to be mixed in the same expression without parentheses.
Generally, it is good practice to use parentheses in logical expressions to indicate different levels of logic. The non-inverting operators
such as or and xor can be chained to form multi-input circuits. However, the inverting operators nor and nand must be treated as
binary operators and grouped in pairs using parentheses.

Examples:

w1 <= a and b or c; illegal expression using mixed operators

w1 <= (a and b) or c; parentheses make expression legal

w2 <= x nand y nand z; illegal expression, inverting operators cannot be chained

w2 <= x nand (y nand z); parentheses make expression legal

w3 <= n xor m xor p xor q; legal expression for a 4-input xor gate

w4 <= a sll 4; shift the vector 'a' 4 positions to the left


w5 <= x srl 2 sll 3; illegal expression, shift operators cannot be chained

w6 <= a & "0000"; concatenation of vector 'a' with literal "0000"

The last example illustrates the use of the concatenation operator. Assuming 'a' is a bit_vector of length n, the expression 'a & "0000"'
will be a bit_vector of length n+4 with the rightmost 4 bits set to zero. In effect, 'a' has been shifted left by 4 bit positions.

Types Bit and Boolean

The built-in logical types Bit and Boolean are enumeration types. Type bit uses character literals '0' and '1' whereas type Boolean uses
named literals - true and false.

Type Bit has no relationship with type Boolean. In other words true(false) is not equivalent to '1'('0'). For example, the following
conditional signal assignment statement is valid only if the signal 'sel' is of type Boolean:

y <= a WHEN sel ELSE b; correct if signal ‘sel’ is Boolean

If the signal 'sel' was a bit-type signal, it would have to be explicitly tested, as shown below:

y <= a WHEN sel = '1' ELSE b; correct if signal ‘sel’ is Bit

The logical operators (and, or, xor etc.) can be used with both bit-type objects and Boolean-type objects, but in a given expression, the
types must match :

y <= a and b; valid if y, a and b are all of type Bit or Boolean

Summary

This section introduced the rich variety of types and operators supported by the VHDL language and associated standard packages.
Only a brief introduction to some of the types and operators has been given here, others will be described when they are used in later
chapters. For more information refer to the VHDL Quick Reference and chapters 4 and 5 of the recommended text book.

Read sections 4.1 to 4.6 of Chapter 4 of the recommended textbook (Rushton).


Read sections 5.1 to 5.4 and skim remaining sections of Chapter 5.

5.6 VHDL Styles of Description

This section makes use of a simple hardware example to illustrate the alternative styles of description provided by the VHDL language.
The gate-level logic circuit for a 2-input exclusive-OR module is shown in the image below. The circuit comprises five simple logic
gates interconnected using four internal wires.

There are three alternative styles of description:

● Register Transfer Level (or Dataflow)


● Behavioural
● Structural
The entity declaration for the exclusive-OR gate is shown below, along with the so-called Register-Transfer-Level (or Dataflow) style of
architecture body. The 'RTL' style makes use of concurrent signal assignment statements to describe the logic in terms of a dataflow,
in this example a single statement describes the module using the xor operator. This style of description contains some structural
information implied by the concurrent statements themselves.

The behavioural style makes use of a process statement, as shown below. In this case the exclusive-OR operation is implemented by
an if..then statement which compares the two inputs and drives the output to logic-1 if they are different. This style of description is
very powerful when used to describe systems. However, it contains no structural information regarding the design.

The image below shows the Structural VHDL description of the exclusive-OR module. This type of description is sometimes referred to
as a netlist (a group of components interconnected by nets). There is a clear correspondence between the circuit diagram of the
exclusive-OR module and the VHDL description given below. This particular version of the module requires three additional design-
entities - the and_gate, or_gate and inverter which are declared as components within the architecture declarative region and
instantiated within the statement part of the architecture. The port map defines the connectivity of the components. In this case named
association has been used to explicitly match port names with signal names.
There are no fixed rules concerning the use of the above styles and a considerable amount of overlap exists between them. In general,
a VHDL description can combine the use of any of the styles. Typically the top level of a hierarchical description will be structural while
the lowest level will consist of behavioural simulation models.
Advanced Electronic Design Automation

Chapter 6 - Describing Digital Systems Using VHDL

Chapter Information

This chapter contains the following sections:

6.1 Register Transfer Level Design Using VHDL

6.2 IEEE Standard Logic and Supporting Packages

6.3 Describing Combinational and Sequential Logic

6.1 Register Transfer Level Design Using VHDL

The hardware description language VHDL supports a wide range of design styles or abstractions; the commonly accepted set of alternative design
abstractions are described in Chapter 4. Of these, the so-called Register Transfer Level is the most widely used. This level of description is always
used when the basic objective is to implement a hardware design by means of logic synthesis. The basis of RTL design is to view a digital system
as a collection of storage elements (registers) interspersed with functional blocks such as arithmetic and data routing circuits, the latter being
essentially combinatorial in nature. Once it is established how to describe such elements using VHDL, the designer can combine them to form
descriptions of complex digital systems which can then be targeted to a variety of hardware technologies.

Whilst logic synthesis tools are capable of automatically creating optimum gate-level circuits from RTL-level VHDL descriptions, it remains the job
of the designer to create the RTL description in the first place. Luckily for the designer, this represents the creative and potentially most challenging
part of the design process. Logic Synthesis tools are currently only capable of accepting VHDL descriptions written in the RTL style. Such
descriptions contain a significant amount of information concerning the way in which a design is to be implemented both structurally and
algorithmically. The designer is still responsible for exploring the considerable design space in the search for a cost-efficient solution to the
particular problem.

RTL Design Example - An 8-Bit Unsigned Binary Multiplier

In order to illustrate the use of the RTL style of description in VHDL, we will consider a simple example design - an 8-bit unsigned binary multiplier.
The multiplier accepts two parallel 8-bit inputs and produces a 16-bit product; all values are interpreted as unsigned. Clearly such a design could
be implemented in a variety of different ways and the way in which the design is described in VHDL will ultimately affect both the structure and
algorithm used in the hardware.

The figure below shows the multiplier in block form:


A behavioural description for the multiplier is given in Listing 6.1, the entity (lines 4 to 8) declares the name and ports of the design and is
preceded by a library clause and two use clauses.

● line 1 - makes the IEEE library name visible to the design entity
● line 2 - makes the contents of ‘IEEE’ package ‘std_logic_1164’ available to the design entity allowing the use of ‘std_logic’ for signals and
ports
● line 3 - makes the contents of package ‘std_logic_arith’ available to the design entity. This provides mathematical operators for use with
std_logic_vectors, interpreting them as unsigned binary numbers

The use of package ‘std_logic_arith’ is required in order to use the multiplication (*) operator within the architecture on line 11, since no such
operator exists within the definition of the language itself.

(Note - An alternative to using the Cadence package 'Std_Logic_Arith' is the IEEE standard package 'Numeric_Std'. This package defines, among
other things, the types 'Signed' and 'Unsigned' - these are based on the 'Std_Logic_Vector' type and allow either signed or unsigned arithmetic
operations to be described. See Section 6.2 below for more details)

Listing 6.1: Behavioural Description of an 8-bit Multiplier

1 library ieee;
2 use ieee.std_logic_1164.all;
3 use ieee.std_logic_arith.all;
4 entity mul8x8 is
5 port(multiplier : in std_logic_vector(7 downto 0);
6 multiplicand: in std_logic_vector(7 downto 0);
7 product: out std_logic_vector(15 downto 0));
8 end mul8x8;

9 architecture simple of mul8x8 is


10 begin
11 product <= multiplier * multiplicand;
12 end simple;

The elementary description given in listing 6.1 reveals nothing about the way in which the multiplication is to be carried out. A Logic Synthesis tool
would either reject the description completely or otherwise produce a gate-level circuit which may be inefficient and heavily dependent on the tool.
The description suggests that the design is to be implemented in the form of a purely combinational circuit. However, such a circuit may be too
complex. An alternative method for performing multiplication is to use a sequence of shift and add operations which requires a far simpler circuit
comprising both combinational and sequential elements. If such an implementation is required, the VHDL description given in listing 6.1 has to be
modified to reflect the algorithm being used.

The first step towards the RTL shift-and-add multiplier description is to create a behavioural description which implements the shift-and-add
algorithm. The following table illustrates the multiplication process using the shift-and-add sequence. The table contents correspond to forming the
product of the following two numbers :

Multiplicand = 110010002 (200)


Multiplier = 110000112 (195)

Each bit of the multiplier is tested in turn, and if equal to logic-1, the multiplicand is added to the upper half of the partial product (bits 15 down to
8); the sixteenth bit is required to hold any carry generated from the addition of the two 8-bit numbers. The partial product is shifted right by one bit
position after each test/add operation and zeros inserted from the left.
From the above table the final result is:

Product = 10011000010110002 (39000)

Listing 6.2 shows the VHDL version of the algorithm illustrated by the above table. The entity declaration is repeated in lines 1 to 8 and is identical
to the entity declaration given in listing 6.1. The use of the package ‘std_logic_arith’ is again required, but in this case to support the use of the ‘+’
operator on line 18. This operator adds together two std_logic_vectors.
The use of the ‘+’ and ‘*’ operators in the above listings is an example of operator overloading . The operator symbols are identical to the built-in
VHDL addition and multiplication operators. However, the VHDL analyser establishes that the overloaded functions (supplied in package
std_logic_arith) are to be used, rather than the built-in operators, by scrutinizing the operand types.

The architecture (lines 10 to 24) makes use of a single sequential process which is sensitive to events on the input ports ‘multiplier’ and
‘multiplicand’.

Listing 6.2: Algorithmic Description of Multiplier

1 library ieee;
2 use ieee.std_logic_1164.all;
3 use ieee.std_logic_arith.all;
4 entity mul8x8 is
5 port(multiplier : in std_logic_vector(7 downto 0);
6 multiplicand: in std_logic_vector(7 downto 0);
7 product: out std_logic_vector(15 downto 0));
8 end mul8x8;
9
10 architecture shift_add of mul8x8 is
11 begin
12 process(multiplier, multiplicand)
13 variable pp: std_logic_vector(16 downto 0);
14 begin
15 pp := (others => ‘0’);
16 for k in 0 to 7 loop
17 if multiplier(k) = ‘1’ then
18 pp(16 downto 8) := (‘0’ & pp(15 downto 8)) +
18c (‘0’ & multiplicand);
19 endif;
20 pp := ‘0’ & pp(16 downto 1);
21 endloop;
22 product <= pp(15 downto 0);
23 end process;
24 end shift_add;

Referring to listing 6.2, line 13 declares a local process variable ‘pp’ to hold the partial product. This is initialised to zero each time a new product is
to be calculated. The loop statement spanning lines 16 to 21 implements the shift-add algorithm by testing each bit of the multiplier in turn, starting
with bit-0. If ‘multiplier(k)’ is equal to logic-1, the most significant 9 bits of the partial product variable are updated with the sum of the multiplicand
(extended to 9-bits) and the upper half of the partial product itself (lines 18 and 18c).
The upper half of the partial product and the multiplicand are both extended to 9-bits using the concatenation operator &, which is used to combine
two array type objects to form a longer array type object.
The expression (‘0’ & multiplicand) forms a 9-bit std_logic_vector having a leading zero as shown below:

‘0’ MD7 MD6 MD5 MD4 MD3 MD2 MD11 MD1

The concatenation is required here to match the number of bits in the source expressions to that of the target.
The concatenation operator is used again on line 20, but this time to perform a 1-bit right shift operation (this is a common use of the & operator).
The expression (‘0’ & pp(16 downto 1)) is a 17-bit word comprising a logic-0 in the most significant bit position followed by what were previously
the 16 most significant bits of the partial product, ie. the whole word shifted 1-bit right.

The loop statement iterates through each bit of the multiplier until the final product is held in variable ‘pp’. On line 22 the lower order 16-bits of ‘pp’
are assigned to the output port signal (bit-16 is the carry bit). The process then suspends until the next event occurs on one of the input numbers.

Although the description of the multiplier given in listing 6.2 clearly defines the algorithm to be used to perform the computation, it remains
combinational. In its present form the description would synthesize to a complex arrangement of adders somewhat similar to the basic array
multiplier. Assuming that the iteration performed by the loop statement is to be implemented by sequential logic, the hardware resources required
to form the data-path portion of the multiplier can be deduced from the statements in the body of the loop. Clearly a binary adder and shifter are
required along with some registers to hold values during the computation process.

The block diagram shown below represents the hardware structure of the shift-add multiplier. As shown, the design consists of three registers, a
gated adder and a controller block, the latter providing control signals to the registers during multiplication (it will be seen later that the controller is
essentially a counter).

The multiplier and partial product registers (mrreg and ppreg) are capable of performing a 1-bit right shift operation in addition to supporting parallel
load and clear operations. The multiplicand register (mdreg) is a straightforward parallel load register, its purpose being to hold the multiplicand
during the multiplication process. The parallel outputs of the multiplicand register are AND-gated with bit-0 of the multiplier register, which
corresponds to the if..then statement on lines 17 to 19 of listing 6.2.

The binary adder has two 8-bit inputs and produces a 9-bit result which is input to the partial product register. This register acts as both an
accumulator and a shifter. During accumulation, only the top 9-bits are loaded from the adder, the lower 8-bits are refreshed via feedback. When
the partial product is shifted right (listing 6.2, line 20), all bits of the partial product register are affected.
The Controller block, shown in the above diagram, forces the data-path to carry out the correct sequence of actions as defined by the algorithm
described by listing 6.2. The Shift-Add multiplier is entirely synchronous, and as such, makes use of a common clock signal (not shown on the
block diagram) to clock all of the sequential elements. Each register has two or three control signals which are set up during each clock cycle. For
example, the multiplier register uses the following signals:

clr_mr : clear multiplier register


load_mr : parallel load multiplier register
shift_mr : shift right multiplier register

At the start of the multiplication process the multiplier and multiplicand registers are loaded and the partial product register is cleared. During the
subsequent 16 clock cycles the partial product register is alternately loaded and shifted (the multiplier register is shifted every other clock cycle)
until the final product is formed. The complete multiplication takes a total of 17 clock cycles.

The Register Transfer Level description of the shift-add multiplier is shown in listing 6.3. The entity declaration (lines 1 to 9) now includes an
additional ‘clock’ input port, otherwise it is identical to the previous listings. The architecture body declarative part (lines 12 to 23) contains several
signal declarations, lines 13 and 16 declare std_logic_vector type signals which correspond to multiplier, multiplicand and partial product registers.
The declarations on lines 13 and 16 do not contain any indication that the signals represent register values. This is inferred by the way in which the
signals are assigned to, later in the description.

Lines 14 and 15 declare intermediate signals associated with the adder and lines 18, 19 and 20 declare the single-bit register control signals
shown on the block diagram.

Finally, on line 22 a signal named ‘mulstate’ is declared. This represents the state of the counter which forms the heart of the controller. This signal
is of type natural and constrained to have a range of 0 to 16. The range constraint is very important for synthesis purposes since it results in a 5-bit
word being used for mulstate rather than the default 32-bit.

The architecture statement part begins on line 24 and comprises three sequential processes (lines 25, 69 and 77). The first sequential process
(lines 25 to 59) describes the data-path registers ‘mdreg’, ‘mrreg’ and ‘ppreg’ which correspond to the three shaded areas of text enclosing
if..then..elsif statements. In each shaded portion of text, the signal corresponding to the register output is assigned values according to the states
of the register control signals. The key to what makes the signals ‘mrreg’, ‘mdreg’ and ‘ppreg’ behave as registers is the statement on line 26:
26 wait until rising_edge(clock);

The above statement causes the process to suspend until a logic-0 to logic-1 transition occurs on the clock input which happens on a continuous
basis as the process loops around. All outputs which are assigned within the process are therefore synchronised to the rising edges of the clock
and as such behave like the outputs of a clocked register. (See section 6.3 for a more detailed description of clocked processes).

Listing 6.3: RTL description of Multiplier

1 library ieee;
2 use ieee.std_logic_1164.all;
3 use ieee.std_logic_arith.all;
4 entity mul8x8 is
5 port(multiplier: in std_logic_vector(7 downto 0);
6 multiplicand: in std_logic_vector(7 downto 0);
7 product: out std_logic_vector(15 downto 0);
8 clock: in std_logic);
9 end mul8x8;
10
11 architecture structural of mul8x8 is
12
13 signal mdreg, mrreg : std_logic_vector(7 downto 0);
14 signal adderin : std_logic_vector(7 downto 0);
15 signal adderout : std_logic_vector(8 downto 0);
16 signal ppreg : std_logic_vector(16 downto 0);
17
18 signal clr_mr ,load_mr ,shift_mr: std_logic;
19 signal clr_md ,load_md : std_logic;
20 signal clr_pp ,load_pp ,shift_pp: std_logic;
21
22 signal mulstate : natural range 0 to 16;
23
24 begin

25 registers: process begin


26 waituntil rising_edge(clock);
27
28 --register to hold multiplicand during multiplication
29 if clr_md = '1' then
30 mdreg <= (others => '0');
31 elsif load_md = '1' then
32 mdreg <= multiplicand;
33 else
34 null;
35 end if;
36
37 --register/shifter to hold multiplier
38 if clr_mr = '1' then
39 mrreg <= (others => '0');
40 elsif load_mr = '1' then
41 mrreg <= multiplier;
42 elsif shift_mr = '1' then
43 mrreg <= ‘0’ & mrreg(7 downto 1);
44 else
45 null;
46 end if;
47
48 --register/shifter accumulates partial product values
49 if clr_pp = '1' then
50 ppreg <= (others => '0');
51 elsif load_pp = '1' then
52 ppreg(16 downto 8) <= adderout; --add to top half
53 elsif shift_pp = '1' then
54 ppreg <= ‘0’ & ppreg(16 downto 1);
55 else
56 null;
57 end if;
58
59 end process;
60
61 --adder
62 adderin <= mdreg when mrreg(0) = ‘1’ else (others => ‘0’);
63 adderout <= ('0' & ppreg(15 downto 8)) + ('0' & adderin);
64
65 --connect ppreg to product output
66 product <= ppreg(15 downto 0);
67
68 --multiplier state counter
69 mul_state: process begin
70 wait until rising_edge(clock);
71 if mulstate < 16 then mulstate <= mulstate + 1;
72 else mulstate <= 0;
73 end if;
74 end process;
75
76 --assign control signal values based on state
77 state_decoder: process(mulstate)
78 begin
79 --assign defaults, all registers refresh
80 clr_mr <= '0';
81 load_mr <= '0';
82 shift_mr <= '0';
83 clr_md <= '0';
84 load_md <= '0';
85 clr_pp <= '0';
86 load_pp <= '0';
87 shift_pp <= '0';
88 if mulstate = 0 then
89 load_mr <= '1';
90 load_md <= '1';
91 clr_pp <= '1';
92 elsif mulstate mod 2 = 0 then --mulstate = 2,4,6,8 ....
93 shift_mr <= '1';
94 shift_pp <= '1';
95 else --mulstate = 1,3,5,7......
96 load_pp <= '1';
97 end if;
98 end process;
99
100 end structural;

The gated-adder shown in the block diagram of the multiplier is described using two concurrent signal assignment statements on lines 62 and 63.
The statement on line 62 effectively ANDs each bit of the multiplicand register output with bit-0 of the multiplier, while line 63 describes an 8-bit
adder with carry output, feeding the upper 9 bits of the product register.

The controller logic for the multiplier is divided into two processes (lines 69 and 77) - one describing a 17-state counter and the other decoding the
states of the counter into the required control signals. The state counter spans lines 69 to 74, it counts repetitively from 0 to 16, changing state on
the rising edges of the clock signal. The state decoder process (lines 77 to 98) describes combinational logic which is apparent from the lack of a
wait statement (or if rising_edge(clock).... clause). It is triggered by events on the controller state signal ‘mulstate’.

Inspection of the if..then.. statement on lines 88 to 97 shows that the registers are shifted during even numbered states and loaded during the odd
numbered states, the exception being state number 0. State number 0 is the reset state of the multiplier. In this state the multiplicand and multiplier
registers are loaded with values from the multiplier ports and the partial product register is cleared to zero.

Read Chapter 2 of the recommended textbook (Rushton)

Summary

This section has presented the basic ideas concerning Register Transfer Level design using VHDL. The multiplier example design illustrates the
alternative ways of describing hardware, starting with a purely behavioural model and progressing to a fully synthesisable RTL description based
on an intermediate algorithmic description.

6.2 IEEE Standard Logic and Supporting Packages

Book Reference: Read Section 4.8 and Chapter 7


As demonstrated by the examples contained in the previous section and chapters, the industry standard logical type ‘std_logic’ is widely used in
VHDL descriptions to represent a single-bit signal, and the corresponding array type - ‘std_logic_vector’ is used for multi-bit signals such as buses
and registers. The IEEE standard package ‘std_logic_1164’ contains definitions of the above logical types along with a full set of logical operators
for both single and multi-bit objects.

A very useful set of Quick Reference Cards summarising the VHDL language syntax and associated IEEE packages is available for download from
the Qualis web site. (You will need to register first).

Design entities that make use of std_logic include a library and use clause before the entity declaration as shown below:

library ieee;

use ieee.std_logic_1164.all;

entity ……………

Including the above clauses allows the designer to declare signals and ports of type std_logic and std_logic_vector (variables and constants can
also be declared). It is common practice to declare Std_logic_vectors with a descending range, as shown by the following example :

signal myreg: std_logic_vector(7 downto 0);

The above declaration creates an array type signal object (a binary word), the left hand element of which is normally interpreted as the most
significant bit (myreg(7)). The signal can be represented schematically as shown below:

myreg

7 6 5 4 3 2 1 0

Each element of the above array can take on any one of the nine values provided by the basic std_logic type - 'U', 'X', '0', '1', 'Z', 'W', 'L', 'H', '-' . Of
these nine values, only forcing-1 (‘1’), forcing-0 (‘0’) and high impedance (‘Z’) are recognised by logic synthesis tools, the remaining metalogical
values are used in simulation only.
All objects of type std_logic and std_logic_vector are given an initial value of ‘U’ (Uninitialised) at the beginning of a simulation run, unless they are
explicitly set to some other value as illustrated below:

signal myreg: std_logic_vector(7 downto 0) := "00001111";

The signal ‘myreg’ above would have been set to the default value "UUUUUUUU" if it had not been initialised as shown.

Objects of type std_logic_vector can be assigned literal values using bit-string literals which are character strings representing logical values. The
following example shows how the above signal ‘myreg’ can be assigned a value:

myreg <= "10010110";

The individual bit values are assigned to each of the corresponding elements of the array as shown below:

myreg(7) <= ‘1’


myreg(6) <= ‘0’
myreg(5) <= ‘0’
myreg(4) <= ‘1’
myreg(3) <= ‘0’
myreg(2) <= ‘1’
myreg(1) <= ‘1’
myreg(0) <= ‘0’

Standard logic also supports the use of hexadecimal and octal notations to represent strings of ‘1’s and ‘0’s. The above assignment could have
been written in the following form:

myreg <= X"96";

The metalogical value ‘Z’ represents the high impedance condition which is commonly used to represent three-state logic. The following concurrent
statement would be interpreted by a logic synthesis tool as an octal three-state buffer :

DATAOUT <= Data when RDREQ = '0' else "ZZZZZZZZ";

The above statement requires that both ‘DATAOUT’ and ‘data’ are 8-bit objects; a more flexible way of assigning the same value to a multi-bit
target is to use an aggregate:

DATAOUT <= Data when RDREQ = '0' else (others => ‘Z’);

The keyword ‘others’ in the above statement effectively means all of the bits of DATAOUT. This form of the literal places no restriction on the
length of the target. The expression ‘(others => ‘Z’)’ is known as an aggregate. For more details see Section 4.10 of the module textbook
(Rushton).

Slices and the Concatenation Operator

Objects of type std_logic_vector are arrays of std_logic elements. Such objects are commonly used to represent binary words in a digital system.
As mentioned above, it is important to declare the range of such objects in descending order (15 downto 0). One important reason for doing so is
that the comparison operators (such as >, < , >= etc.) work on the elements of the array from left to right, regardless of how the range has been
defined. The above applies to both the built-in comparison operators, which are used with bit_vectors, and those defined in the IEEE arithmetic
support package ‘std_logic_arith’.

Given the following signal assignments to two std_logic_vectors:

a <= "10010110";

b <= "01111111";

The following expressions illustrate the operation of the comparison operators:

if (a < b) then…… expression is FALSE

if (a > b) then…… expression is TRUE


if (a = b) then…… expression is FALSE

if (a /= b) then…… expression is TRUE

Std_logic_vectors of a given length can be ‘sliced’ into sub-vectors by appending the name of the object with the required range; individual bits of a
std_logic_vector can be referenced by means of a single index, as shown below:

signal a, b: std_logic_vector(7 downto 0);

signal c: std_logic_vector(3 downto 0);

…………………

a <= b; No need to specify range when referencing all bits

c <= a(7 downto 4); Upper 4-bits of ‘a’ assigned to ‘c’

b(3 downto 0) <= c; Target is a slice

a(5 downto 2) <= b(4 downto 1); Source and target are slices

c(3) <= a(0); Individual elements are std_logics

The concatenation operator (&) is used to join one-dimensional array objects together to form larger arrays. Individual std_logic elements can also
be joined to std_logic_vectors using this operator. The concatenation operator is often used to shuffle the bits of a word and is particularly useful
for performing left and right shift operations on std_logic_vectors, since the built-in shift operators (sll, srl ,sla etc.) are only applicable to
bit_vectors.

The following statement copies the 4-bits of signal ‘c’ to both halves of the 8-bit word ‘a', as defined above:

a <= c & c;

Individual bit(std_logic) elements can be concatenated to form bit_vectors(std_logic_vectors). This is used in the following design entity which
describes a 3-input majority voter unit:

1 ENTITY maj IS
2 PORT(a,b,c: IN BIT; m: OUT BIT);
3 END maj;
--Dataflow style architecture
4 ARCHITECTURE concurrent OF maj IS
5 BEGIN
6 --selected signal assignment statement (concurrent)
7 WITH a & b & c SELECT
8 m <= '1' WHEN "110"|"101"|"011"|"111",'0' WHENOTHERS;
9 END concurrent;

The multiplier example used in the previous section makes use of the & operator to perform right shift operations and word extension. The first
statement below uses the & operator to add a leading ‘0’ to the 8-bit operands of the addition in order to match the length of the operands to the
result.

pp(16 downto 8) := (‘0’ & pp(15 downto 8)) +


(‘0’ & multiplicand);

The statement below shifts the bits of variable ‘pp’ one bit position to the right as shown

pp := ‘0’ & pp(16 downto 1);

‘pp’ before execution of the above statement:

pp
16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0

‘pp’ after execution of the above statement:

pp

'0' 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1

Shifting left is achieved by tagging zero(s) onto the end of the source slice :

pp := pp(13 downto 0) & “000”; shifts left by 3-bits

When shifting std_logic_vector type objects, care should be taken to ensure the correct result when the words represent signed two’s complement
values. When a right shift is performed on a two’s complement number (divide by 2), the sign bit must be copied into the high order bit positions to
maintain the correct sign and value. The following line of VHDL declares an 8-bit signal and initialises it to the decimal value -64:

signal tcreg: std_logic_vector(7 downto 0) := "11000000";

The following statement shifts ‘tcreg’ right by one bit with sign extension, the result being “11100000”, ie. -32, assuming the word is a two’s
complement value.

tcreg <= tcreg(7) & tcreg(7 downto 1);

Logical Operators

Package ‘std_logic_1164’ defines a full set of logical operators for use with both std_logic and std_logic_vector type operands. All word operations
are performed on a bit-by-bit basis.

The package makes use of look-up tables to predict the result of a logical operation, given the possibility of nine different input values on each
individual std_logic. For example, if one of the operands of a logical-and operation was equal to ‘Z’, the result would be ‘unknown’ regardless of the
other operand, ie. ‘X’.

The logical operations provided by package std_logic_1164 are listed in Section 5.5 of Chapter 5 and are repeated below for convenience.

Logical Operators defined by Package Std_logic_1164

not and or nand nor xor xnor

As shown above, the logical operators for std_logic have the same names as the built-in bit-type operators which is another example of
overloading . The VHDL analyser automatically selects the correct function definition by inspection of the operand types.

Edge Detection Functions

Two extremely useful functions are included within the std_logic_1164 package, the purpose of these functions being to detect when a std_logic
(or std_ulogic) signal undergoes either a rising or falling transition. The functions, known as ‘rising_edge’ and ‘falling_edge’ find widespread
application in the description of synchronous sequential logic systems.

The function declarations are shown below and are contained in the package declaration. This primary design unit may be thought of as a header
file containing declarations of types, constants and function prototypes. The description of the actual functions is contained within the package
body.

FUNCTION rising_edge (SIGNAL s: std_ulogic) RETURN BOOLEAN;

FUNCTION falling_edge (SIGNAL s: std_ulogic) RETURN BOOLEAN;

The box shown below shows the VHDL code for the function ‘rising_edge’. The function accepts a signal of type std_ulogic or std_logic (std_logic
is a sub-type of std_ulogic) as an input parameter, returning a Boolean result which is true if the signal has undergone a logic-0 to logic-1
transition. The Boolean condition enclosed within the parentheses after the keyword return on line 3 uses the built-in attributes ‘EVENT' and
‘LAST_VALUE' to ensure that only a valid rising edge has occurred.

1 FUNCTION rising_edge (SIGNAL s : std_ulogic) RETURN BOOLEAN IS


2 BEGIN
3 RETURN (s'EVENT AND (To_X01(s) = '1') AND
4 (To_X01(s'LAST_VALUE) = '0'));
5 END;

The use of the above function was illustrated in the example contained in the previous section (see listing 6.3, line 26). The following statement is
often used in a process which describes synchronous sequential logic.

wait until rising_edge(clock);

Package Numeric_Std

(Read Chapter 7 of Rushton for a detailed description of Numeric_Std)

The VHDL language provides a rich variety of arithmetic operators. However, these are limited to being used with only integer and real operands
(See table of operators in Chapter 5). The widespread adoption of the IEEE std_logic type for digital signals has led to the development of support
packages providing arithmetic, comparison and conversion functions for use with std_logic and std_logic_vector based types.

Several alternative Std_Logic arithmetic support packages have been developed by companies such as Synopsis and Cadence, these have been
primarily for use with their corresponding proprietary tool sets. An example is Cadence's version of the Std_Logic_Arith package, this is
compatible with the Cadence Leapfrog VHDL Simulator, but at the time of writing is not supported by Cadence's VHDL synthesis tool (currently
Ambit).

Certain CAD-vendor packages have been adopted by the IEEE as standards, due to their widespread support and usage in the VHDL community.
Examples are the following Synopsis packages:

Std_Logic_Unsigned Std_Logic_Signed

In recent years, the IEEE has produced a standard package in an attempt to provide all of the best features of the previous non-standard
packages. This more recent package also enjoys wide support by simulation and synthesis tools - the package is formally known as Numeric_Std
Standard VHDL Synthesis Package 1076.3 or just Numeric_Std for short.

Design entities that make use of the Numeric_Std package need to include an additional use clause following the ‘std_logic_1164’ use clause,
both appearing before the entity declaration, as shown below:

library ieee;
use ieee.std_logic_1164.all;
use ieee.Numeric_Std.all;
entity ……………

The design file containing the VHDL text for package ‘Numeric_Std ’ comprises two design units - a primary unit (the package declaration) and a
secondary unit (the package body). The file can be accessed for viewing in read-only mode from the Cadence Leapfrog Notebook window by
firstly selecting the IEEE library, in order to make it the current library, and then double-clicking on the ‘Numeric_Std’ icon in the library browser
area. The text editor will open, allowing you to view the file.

Including the above library and use clauses prior to an entity declaration makes all of the types, functions and operators available for use by the
corresponding design entity (note that both the ‘std_logic_1164’ and ‘Numeric_Std’ use clauses must be included).

At the heart of the Numeric_Std package is the definition of two new types for use in arithmetic operations, as shown below (NB - VHDL is not case
sensitive).

type Unsigned is array (Natural range <>) of Std_Logic;


type Signed is array (Natural range <>) of Std_Logic;

The above type declarations are very similar to the declaration of the Std_Logic_Vector type, however as their names suggest, the type 'Unsigned'
is used to represent unsigned quantities (positive only) while the type 'Signed' is used to represent signed 2's complement (positive and negative)
quantities.
The base-type of the Unsigned and Signed array types is Std_Logic, so these 'new' types are closely related to the Std_Logic_Vector type defined
within Std_Logic_1164.

Signals, Constants and Variables of type Unsigned and Signed are declared and used in much the same way as Std_Logic_Vectors, their main
difference is how they are interpreted in arithmetic operations.
The following shows how 8-bit unsigned and signed signals are declared.

signal Abus : Unsigned(7 downto 0);


signal Bbus : Signed(7 downto 0);

The signals Abus and Bbus differ in the range of values they can represent, as illustrated by the following tables.

Abus (7 downto 0) Numerical value


7 6 5 4 3 2 1 0
0 1 1 1 1 1 1 1 +127
0 1 1 1 1 1 1 0 +126
0 0 0 0 0 0 1 0 +2
0 0 0 0 0 0 0 1 +1
0 0 0 0 0 0 0 0 0
1 1 1 1 1 1 1 1 +255
1 1 1 1 1 1 1 0 +254
1 0 0 0 0 0 0 1 +129
1 0 0 0 0 0 0 0 +128

The range of 8-bit unsigned values

Abus (7 downto 0) Numerical value


7 6 5 4 3 2 1 0
0 1 1 1 1 1 1 1 +127
0 1 1 1 1 1 1 0 +126
0 0 0 0 0 0 1 0 +2
0 0 0 0 0 0 0 1 +1
0 0 0 0 0 0 0 0 0
1 1 1 1 1 1 1 1 -1
1 1 1 1 1 1 1 0 -2
1 0 0 0 0 0 0 1 -127
1 0 0 0 0 0 0 0 -128

The range of 8-bit signed values

The first table shows that an 8-bit unsigned number has the range 0 to 255, the total number of values is always 2n, i.e. 256 (n is the number of
bits). The second table shows how the 2's complement system splits the 256 possible values into positive and negative ranges, where the most
negative value is always one greater than the most positive value.

The second table highlights bit-7 as the Sign Bit - 0 means the number is positive, and 1 means the number is negative. In effect, bit-7 has a
weighting of minus 128.

The availability of separate signed and unsigned types allows the same operators and function names to be applied to operands of the two types,
this operator overloading is a common feature of the VHDL language. Generally however, it is not possible to mix signed and unsigned objects in
the same expression without the need for type conversion, this is due to VHDL's rigorous type checking.

To avoid problems, it is usually a good idea to decide which of the two types is best suited to the modeling situation and attempt to stick to that
type - signed or unsigned. If mixing types cannot be avoided then the Numeric_Std package provides numerous type conversion functions to solve
any conflicts.

Table 6.1 below summarises the main arithmetic and comparison operators provided by Numeric_Std.

The left-hand column shows the types allowed for the left-hand operand, and the third and fourth columns indicate the possible types of the right
hand operand and result, respectively. The second column shows the symbol for the operator, these are identical to those already built into the
VHDL language.
Table 6.1: Arithmetic and comparison operators supported by package Numeric_Std

Overloaded Operators
Left Op Right Result
abs Signed Signed
- Signed Signed
Unsigned +, -, *, /, rem, mod Unsigned Unsigned
Signed +, -, *,/ , rem, mod Signed Signed
Unsigned +, -, *,/ , rem, mod Natural Unsigned
Signed +, -, *,/ , rem, mod Integer Signed
Unsigned <, >, <=, >=, =, /= Unsigned Boolean
Signed <, >, <=, >=, =, /= Signed Boolean
Unsigned <, >, <=, >=, =, /= Natural Boolean
Signed <, >, <=, >=, =, /= Integer Boolean

As shown in table 6.1, the built-in types Natural and Integer can be used in arithmetic and comparison operations, where the Natural sub-type
corresponds to Unsigned type and Integer corresponds to the type Signed. Also note that the types Signed and Unsigned cannot be mixed within a
given operation (without type conversion).

The arithmetic operations are very useful when describing arithmetic circuits such as binary adders and multipliers (see Listing 6.1). They are also
extensively used when describing binary counters in VHDL, as listing 6.4 serves to illustrate:

Listing 6.4: 8-Bit Binary Up Counter using Numeric_Std‘+’ operator

1 LIBRARY ieee;
2 USE ieee.Std_logic_1164.ALL;
3 USE ieee.Numeric_Std.ALL;
4 ENTITY cntr8bit IS
5 PORT(clock, reset, enable: IN Std_logic;
6 count: OUT Std_logic_vector(7 DOWNTO 0));
7 END cntr8bit;
8 ARCHITECTURE using_numeric OF cntr8bit IS
9 SIGNAL count_int : Unsigned(7 DOWNTO 0);
10 BEGIN
11 PROCESS
12 BEGIN
13 WAIT UNTIL rising_edge(clock);
14 IF reset = '1' THEN
15 count_int <= (OTHERS => '0');
16 ELSIF enable = '1' THEN
17 count_int <= count_int + 1;
18 ELSE
19 NULL;
20 END IF;
21 END PROCESS;
22 count <= Std_Logic_Vector(count_int);
23 END using_numeric;

Listing 6.4 is the VHDL description for an 8-bit binary up counter. The counter is clocked synchronously and is capable of being reset and disabled,
both of which also occur on the rising edge of the clock input. This form of description, comprising a process containing a single wait statement,
constitutes a very typical style of counter description. Most logic synthesis tools will accept a counter described in this manner. The key line in the
present context is line 17, where the overloaded ‘+’ operator is used to add the Natural value 1 to the Unsigned 'count_int' signal in order to
increment the counter.

The property of the ‘+’ operator of limiting the length of the result to be same as the length of the longest operand ensures that the counter
overflows in the correct manner. When the value of the signal ‘count_int’ reaches "11111111", the next clock edge will result in it becoming
"00000000", in just the same way as the real hardware.
One or two other points to notice about Listing 6.4:

● The ‘counter’ register is an internal signal ‘count_int’ which avoids having to make the ‘count’ port bidirectional (INOUT or BUFFER).
● If both ‘reset’ and ‘enable’ are logic-0 the if..then..else leads to the null statement, which means essentially ‘do nothing’. This means that
the value of ‘count_int’ is to remain the same, ie. the counter is disabled.
● All counter actions (increment, reset and enable/disable) are synchronised to the ‘clock’ input due to the fact that the process is
synchronised to the clock by the wait statement on line 13.

The internal Unsigned signal 'count_int' is converted to Std_Logic_Vector on line 22 before being assigned to the output port 'count'. This is
necessary even though the two types are closely related.

Table 6.1 also lists the comparison operators supplied in package Numeric_Std. These are repeated below for convenience.

Left Op Right Result


Unsigned <, >, <=, >=, =, /= Unsigned Boolean
Signed <, >, <=, >=, =, /= Signed Boolean
Unsigned <, >, <=, >=, =, /= Natural Boolean
Signed <, >, <=, >=, =, /= Integer Boolean

As shown, these allow a comparison to be made between an Unsigned/Signed vector and a natural/integer value, or between a pair of signed or
unsigned vectors. All comparisons return a boolean (True/False) result.

It is important to keep in mind that the comparison is based upon the sign of the two operands, hence an Unsigned vector is compared with a
Natural number whereas a Signed vector is compared with an Integer value. (For example it wouldn't make sense to compare an Unsigned vector
with a negative integer)

These operators are primarily intended to enhance the readability of VHDL descriptions.

The use of the concatenation (&) operator to perform shifting of std_logic_vectors was described earlier in this chapter. An alternative, and perhaps
more readable, approach is to make use of the shift functions provided in package Numeric_Std. These are listed in table 6.2 below.

Table 6.2 : Shift functions supported by package Numeric_Std

Function Returns
Shift_Left(Unsigned, Natural) Unsigned
Shift_Right(Unsigned, Natural) Unsigned
Shift_Left(Signed, Natural) Signed
Shift_Right(Signed, Natural) Signed
Rotate_Left(Unsigned, Natural) Unsigned
Rotate_Right(Unsigned, Natural) Unsigned
Rotate_Left(Signed, Natural) Signed
Rotate_Right(Signed, Natural) Signed

The shifting and rotating operations all require a second Natural-type argument to specify the number of bit positions to shift by, this must be a
compile time constant to be compatible with synthesis.
All the operations listed in Table 6.2 are overloaded for Signed and Unsigned values, the results of which may differ depending on the bit pattern of
the first argument. Generally zeros are shifted in for SHIFT_LEFT operations, whereas SHIFT_RIGHT operations preserve the sign-bit (left-most
bit) when the operand is Signed.
The following examples illustrate this last point.

signal tcreg : Unsigned(7 downto 0) := "11000000";


…………………
tcreg <= Shift_Right(tcreg, 3); --tcreg = "00011000"

signal tcreg : Signed(7 downto 0) := "11000000";


…………………
tcreg <= Shift_Right(tcreg, 3); --tcreg = "11111000"

Table 6.3 shows the type conversion functions defined in package Numeric_Std.
These functions allow two-way conversion between std_logic_vectors and signed/unsigned vectors as well as conversion between integer values
and signed/unsigned vectors.
The close relationship between Std_Logic_Vector and Signed/Unsigned means that it is only necessary to precede the object to be converted with
the name of the required type in the manner of type casting.
The 'To_Unsigned/To_Signed' functions require an additional argument to specify the number of bits to use in the returned result, this must be
sufficient to accommodate the value of the first argument.
Table 6.3 Type conversion functions supported by package Numeric_Std

From To Function
Unsigned,
Signed Signed(from)
Std_Logic_Vector
Signed,
Unsigned Unsigned(from)
Std_Logic_Vector
Unsigned, Signed Std_Logic_Vector Std_Logic_Vector(from)
Unsigned, Signed Integer To_Integer(from)
Natural Unsigned To_Unsigned(from, size)
Integer Signed To_Signed(from, size)

The example shown in listing 6.5 illustrates how some of the type conversion/casting functions are used in the model of a memory device.

The ROM input address is converted into an integer value on line 20, the value is then used as an index into the memory array on line 22. (The
contents of the memory array are stored as integers for efficient simulation).

On line 22, the following expression converts the data being read from the memory array into a suitable form for the output port.

Std_Logic_Vector(To_Unsigned(rom_values(index),8))

The description of the ROM given in listing 6.5 uses a combinational process that is triggered by events on any of the input signals. The memory is
modelled by a constant array of integers which are preset using an aggregate (line 11). The process (lines 13 to 24) reads a value from the array
and converts it to an Unsigned vector of length 8 (before casting to a Std_Logic_Vector) when both 'csbar' and 'oebar' are logic-0, otherwise the
output data lines are set to high impedance. The use of integers to represent memory contents uses less memory during simulation, compared to
using an array of std_logic_vectors or Unsigned vectors.

Listing 6.5: A 16-word, 8-bit wide Read Only Memory

1 LIBRARY ieee;
2 USE ieee.std_logic_1164.ALL;
3 USE ieee.Numeric_Std.ALL;
4 ENTITY rom16x8 IS
5 PORT(address: IN Unsigned(3 DOWNTO 0);
6 csbar, oebar: IN Std_Logic;
7 data : OUT Std_Logic_Vector(7 DOWNTO 0));
8 END rom16x8;

9 ARCHITECTURE version1 OF rom16x8 IS


10 SUBTYPE Byte IS Natural RANGE 0 TO 255;
10a TYPE rom_array IS ARRAY (0 TO 15) OF Byte;
11 CONSTANT rom_values : rom_array := (1,2,3,4,5,6,7,8,9,
10,11,12,13,14,15,16);
12 BEGIN
13 PROCESS(address, csbar, oebar)
14 VARIABLE index: Natural RANGE 0 TO 15 := 0;
15 BEGIN
16 IF (csbar = '1' OR oebar = '1') THEN
17 data <= "ZZZZZZZZ";
18 ELSE
19 --convert address to an integer
20 index := To_Integer(address);
21 --read data and assign to output data lines
22 data <= Std_logic_Vector(To_Unsigned(rom_values(index),8));
23 END IF;
24 END PROCESS;
25 END version1;

Logical Operators for types Signed and Unsigned


Package 'std_logic_1164' defines the full set of logical operators for use with both std_logic and std_logic_vector type operands. The Numeric_Std
package re-defines (overloads) these operators (shown below) for the types Signed and Unsigned, such that they perform the exact same function
as their std_logic_vector counterparts.
It is worth remembering that the individual elements of a Signed or Unsigned vector are in fact Std_Logic enumeration type objects, and so the
logical operations are already defined for these within package Std_Logic_1164.

Logical Operators defined in Numeric_Std


not and or nand nor xor xnor

Read Section 4.8 and skim Chapters 6 and 7 of the recommended textbook (Rushton)

6.3 Describing Combinational and Sequential Logic

The previous sections and chapters have presented a number of examples of both sequential and combinational designs described using VHDL.
This section will provide more detail concerning the accepted styles used for describing such systems. In general, the examples and templates
presented in this section are suitable for both simulation and synthesis purposes.

Describing Combinational Logic using Concurrent Signal Assignment Statements

The architecture body of a design entity can contain any number of concurrent signal assignment statements. These come in three varieties:

● Simple Concurrent Signal Assignment


● Conditional Signal Assignment
● Selected Signal Assignment

The simplest way to describe gate-level logic is to use the first form of the statement. In this case the target signal is assigned a source expression
which makes use of the built-in (or std_logic_1164 supported) logical operators. Examples of this sort of description can be found in the VHDL
Examples resource file, a selection of which is given below:

● Quad NAND gate

--uses 1993 std VHDL


library IEEE;
use IEEE.Std_logic_1164.all;
entity HCT00 is
port(A1, B1, A2, B2, A3, B3, A4, B4 : in std_logic;
Y1, Y2, Y3, Y4 : out std_logic);
end HCT00;

architecture VER1 of HCT00 is


begin

Y1 <= A1 nand B1 after 10 ns;


Y2 <= A2 nand B2 after 10 ns;
Y3 <= A3 nand B3 after 10 ns;
Y4 <= A4 nand B4 after 10 ns;

end VER1;

● Quad OR gate
--uses 1993 std VHDL
library IEEE;
use IEEE.Std_logic_1164.all;
entity HCT32 is
port(A1, B1, A2, B2, A3, B3, A4, B4 : in std_logic;
Y1, Y2, Y3, Y4 : out std_logic);
end HCT32;

architecture VER1 of HCT32 is


begin

Y1 <= A1 or B1 after 10 ns;


Y2 <= A2 or B2 after 10 ns;
Y3 <= A3 or B3 after 10 ns;
Y4 <= A4 or B4 after 10 ns;

end VER1;
● Hamming Encoder

All of the above examples make use of the logical operators to explicitly describe the function of the design at a relatively low level. In some cases
this may be undesirable or inefficient, and therefore the two remaining forms of the concurrent signal assignment statement (conditional and
selected) may be used to create more compact forms of description.

The basic distinction between the conditional and selected signal assignment statements is best understood by comparing their equivalent
sequential statements:

Conditional Signal Assignment => If..then..else statement

Selected Signal Assignment => case..when statement

The conditional signal assignment statement tests a set of Boolean conditions in strict order of priority, whereas the selected signal assignment
statement gives equal priority to all possible values of the test expression. This fundamental difference leads to different logic structures when the
two forms of statement are synthesised. The following list contains links to illustrative examples in the VHDL Examples resource file:

● Multiplexer description using a Selected Signal Assignment


● Binary Coded Decimal to 7-Segment Decoder using Selected Signal Assignment
● Majority Voter using Selected Signal Assignment
● Multiplexer description using a Conditional Signal Assignment
● Address Decoder using Conditional Signal Assignment
● Priority Encoder using Conditional Signal Assignment

Concurrent signal assignment statements are generally not used to describe sequential logic, although it is entirely possible to do so. For example,
a basic D-Type Flip-Flop can be described using a Conditional Signal Assignment.

Most logic synthesis tools require that sequential logic circuits are described using a process statement and this is by far the most common
approach. This topic will be covered in detail later in this section.

Describing Combinational Logic using a Process

A process can be used to describe combinational logic in the form of a sequential algorithm. This allows the use of the full set of sequential
statements to describe the behaviour of the logic. There are two very important points regarding the use of processes, which must be kept in mind
when creating descriptions of combinational logic:

1. Only one signal driver is allowed per signal


2. The output signal(s) must be assigned a value for every possible input combination

The first point refers to the fact that multiple drivers to a signal are not recognised within processes. If a signal is assigned more than once within a
process, the later assignment tends to overwrite the earlier assignments. In general, the signals being driven by a process (the outputs) should be
assigned to by one statement and this statement will usually be situated near to the end of the process. If intermediate values need to be
calculated, then variables should be used.

The second point is linked to one of the basic properties of a signal object, ie. a signal has implicit memory. When a signal is assigned a value by a
process (or any concurrent statement), a driver is created to hold the current and projected value for that signal. If the same process executes
again, but this time the signal is not assigned a value, it retains the value it was given by the previous assignment.

So, in order to avoid creating a logic circuit which has memory properties (combinational feedback), a process must always assign a value to its
outputs.
The image shown below represents a basic template for a VHDL process describing combinational logic. The keyword process is followed by a
sensitivity list containing all of the signals which act as inputs to the process. The process should not read any signal (ie. the signal should not
appear on the right hand side of a statement) which does not appear in the sensitivity list. While it is perfectly legal to read a signal not included in
the process sensitivity list, a logic synthesis tool would flag this as an error. As shown in the image below, the ‘outputs’ of the process should be
assigned near to the end of the process and only once per execution of the process. One way of ensuring that all outputs are assigned a value
every time a process executes is to assign them default values at the top of the process. The statements following the default assignments can
overwrite these values if necessary.

Listing 6.6, shown below, is taken from the RTL description of the Shift-Add Multiplier contained in the previous section (listing 6.3). The process
describes a combinational decoder which uses the integer signal ‘mulstate’ as an input and produces a number of control outputs which depend on
the current value of ‘mulstate’. Lines 4 to 11 assign default values for the process outputs. This is followed by an if..then..elsif..else statement
which assigns logic-1 to certain outputs for certain values of ‘mulstate’. The assignments made on lines 13-15, 17,18 and 20 overwrite the
previously assigned default values, any signal which is not assigned to by the if..then statement is assigned logic-0, thus ensuring that the
description is purely combinatorial.

Listing 6.6: Combinational process describing a decoder

1 state_decoder: process(mulstate)
2 begin
3 --assign defaults, all registers refresh
4 clr_mr <= '0';
5 load_mr <= '0';
6 shift_mr <= '0';
7 clr_md <= '0';
8 load_md <= '0';
9 clr_pp <= '0';
10 load_pp <= '0';
11 shift_pp <= '0';
12 if mulstate = 0 then
13 load_mr <= '1';
14 load_md <= '1';
15 clr_pp <= '1';
16 elsif mulstate mod 2 = 0 then--mulstate = 2,4,6,8 ....
17 shift_mr <= '1';
18 shift_pp <= '1';
19 else --mulstate = 1,3,5,7......
20 load_pp <= '1';
21 end if;
22 end process;
Latch Inference

The retentive property of VHDL signal objects can result in unwanted feedback being included in a circuit which is synthesised from a VHDL
process containing incomplete assignments. Most synthesis tools will detect such a situation and issue a warning suggesting that the model
designer may not have intended to infer latches in the design. An alternative scenario may be that the designer intended latches to be placed in the
design and the use of an incomplete assignment has been used to deliberately ‘infer’ latches. Listing 6.7 illustrates a simple example of the latter,
the incomplete if..then statement being used to infer a transparent latch. When the input signal ‘enable’ is equal to logic-1, events on ‘datain’ are
fed through onto the process output ‘q’ (the latch is transparent). When ‘enable’ is taken from logic-1 to logic-0, the process executes and the
(enable = ‘1’) condition is now false. No assignment is made to output ‘q’ and therefore it retains its current value. A synthesis tool interprets this
description as a latch which uses feedback to maintain the stored value when ‘enable’ is zero. Such a latch is shown in the image below listing 6.7.

Listing 6.7: Incomplete if..then statement inferred latch

process(enable, datain)
begin
if enable = ‘1’ then
q <= datain;
--when en -> '0' ,q retains value
end if;
end process;

The statement(s) enclosed within the if..then statement shown in listing 6.7 is a simple signal assignment. In general, any number of statements
describing combinational logic can be enclosed within the outermost if..then statement, the resulting synthesised circuit will be the corresponding
combinational circuit with latched outputs.

An example - 16-line to 4-line Multiplexer

The following listing (Listing 6.8) show a design entity for a multiplexer using a complete if..then statement. This is followed by listing 6.9, which
shows an alternative architecture for the multiplexer making use of a case..when statement.

Listing 6.8 describes the multiplexer using a single if..then statement enclosed within a process which is sensitive to all of the inputs. The first
three combinations of the select input ‘s’ are tested in turn and the output is assigned the selected input, the final else clause (line 18) covering the
‘s = "11"’ case. Even though the if..then statement prioritises each test in sequence, a synthesis tool would almost certainly generate a
straightforward 16- to 4-line multiplexer from the description of listing 6.8. This is due to the fact that all meaningful values for ‘s’ have been
mentioned, the remaining 77 possible values are not relevant (a std_logic_vector(1 downto 0) has 92 possible values, ie. 81).

Listing 6.9 shows the same design described using a case..when statement. The important difference here is that a case statement must
elaborate all possible values for the select expression which is achieved by including a final ‘when others =>‘ clause on line 19a to pick up all the
metalogical combinations of ‘s’ and assign a ‘forcing don’t know’ (‘X’) output value in response. Notice the alternative use of the others keyword on
lines 19a and 19b. On line 19a it refers to all of the combinations of ‘s’ not mentioned in previous when clauses, while on line 19b it refers to all of
the bits of the output ‘x’.

Listing 6.8: Multiplexer using If..then statement


1 library ieee;
2 use ieee.std_logic_1164.all;

3 entity mux is port(


4 a, b, c, d: in std_logic_vector(3 downto 0);
5 s: in std_logic_vector(1 downto 0);
6 x: out std_logic_vector(3 downto 0));
7 end mux;

8 architecture using_if of mux is


9 begin
10 mux4_1: process (a, b, c, d, s)
11 begin
12 if s = "00" then
13 x <= a;
14 elsif s = "01" then
15 x <= b;
16 elsif s = "10" then
17 x <= c;
18 else
19 x <= d;
20 end if;
21 end process mux4_1;
22 end using_if;

Listing 6.9: Multiplexer using case..when statement

8 architecture using_case of mux is


9 begin
10 mux4_1: process (a, b, c, d, s)
11 begin
11a case s is
12 when "00" =>
13 x <= a;
14 when "01" =>
15 x <= b;
16 when "10" =>
17 x <= c;
18 when "11" =>
19 x <= d;
19a when others =>
19b x <= (others => ‘X’);
20 end case;
21 end process mux4_1;
22 end using_case;

Describing Sequential Logic using a Process

The process statement is by far the most widely used VHDL construct for describing sequential logic such as registers, counters and state
machines. All of these systems are assumed to have a synchronous clock input which forces the circuit to transition from one state to another
(usually on the rising edge).

Occasionally, it may be necessary to include asynchronous sequential logic in a design and although it is possible to describe such logic
behaviourally, most commercial logic synthesis tools are incapable of handling such descriptions correctly (they tend to minimise out any
redundant logic included to avoid hazards !). To ensure successful synthesis, the asynchronous sequential logic portions of a design should be
described in a structural form, using primitive logic cells which are available in the target technology. These are effectively ignored by the synthesis
process and the gate-level circuits reproduced in net-list form.
The image shown below illustrates the basic template for a process describing synchronous sequential logic. The key differences between this
template and the combinational logic version are the absence of the sensitivity list and the inclusion of a single wait statement at the top of the
process. The purpose of the latter is to suspend the process until a logic-0 to logic-1 transition occurs on the clock input (falling_edge(clock) could
be used if negative edge triggering is required); hence the process is synchronised to the clock. The statements following the wait statement
describe the logic supplying the inputs to the sequential elements of the circuit. Any output signals which are assigned to within the process are the
outputs of clocked storage elements.

Most logic synthesis tools accept descriptions written in the style shown in the image below; occasionally however a slightly different template is
required which uses the following condition in the wait until statement:

wait until clock’event and clock = ‘1’;

Yet another variation is :

wait until clock = ‘1’;

The above variations are all interpreted by the synthesis tool in the same way. However, if the synthesis tool supports the use of IEEE Std_logic, it
should allow the use of the ‘rising_edge’ function.

The process template shown in the image above describes a sequential logic system in which all actions are synchronous. If asynchronous control
signals are being used (such as a reset or parallel load input), the template as it stands is unsuitable and must be modified. This will be covered
later in this section.

Registers

The above image shows an 8-bit register with synchronous ‘clear’ and ‘load’ inputs, the VHDL description of the register is shown in listing 6.10.
The register is described using a single synchronous process which triggers on the rising edges of the clock input. The process encloses an
if..then statement (lines 13 to 17) which describes the logic feeding the storage elements of the register. The ‘clear’ input has a higher priority than
the parallel ‘load’ input and if both control inputs are negated (logic-0) the register outputs remain unchanged.
Listing 6.10: Synchronous Loadable Register

1 library ieee;
2 use ieee.std_logic_1164.all;
3 entity reg8 is
4 port(clock, clear, load: in std_logic;
5 d: in std_logic_vector(7 downto 0);
6 q: out std_logic_vector(7 downto 0));
7 end reg8;
8 architecture v1 of reg8 is
9 begin
10 reg_proc: process
11 begin
12 wait until rising_edge(clock);
13 if clear = '1' then
14 q <= (others => '0');
15 elsifload = '1' then
16 q <= d;
17 end if;
18 end process;
19 end v1;

The detailed logic circuit for one bit of the above register is shown in the image below. The circuit corresponds to that which would be generated by
a logic synthesis tool, the contents of the process statement results in the need for a positive edge triggered D-type Flip-flop with its ‘D’ input
connected to a pair of cascaded 2-to1 multiplexers.

When the ‘c’ input of a multiplexer is at logic-1, the lower data input (s2) is selected and fed through to the D output. Therefore, when the ‘clear’
input is at logic-1, the right hand multiplexer feeds a logic-0 into the Flip-flop regardless of the state of ‘load’. The left hand multiplexer is connected
to the Flip-flop when ‘clear’ is logic-0. The value of ‘load’ then determines the Flip-flop input. When both ‘clear’ and ‘load’ are logic-0, the feedback
path between the Flip-flop ‘Q’ output and its ‘D’ input is complete, allowing the stored value to be maintained.
Registers with Asynchronous Reset

In some cases it is necessary to provide a global asynchronous reset input to force all storage elements into a known state. This input operates
independently of the synchronous clock. Listing 6.11 shows the required format, the wait until.. statement has been removed and the process
made sensitive to both the clock and reset inputs (a wait statement is incompatible with a sensitivity list). Any event on either of the two ‘input’
signals will cause the if..then statement (lines 3 to 7) to execute. If ‘reset’ has gone to logic-1, the asynchronous reset takes place, regardless of
the state of the clock. The system remains in the reset state as long as ‘reset’ remains at logic-1. If ‘reset’ is logic-0 and a rising edge occurs on
‘clock’, the process will execute and the elsif clause (line 5) will be true, allowing the synchronous action to take place (load, count etc..).

The choice of whether to use a synchronous or an asynchronous reset mechanism often depends on the target hardware. If the hardware supports
an asynchronous reset, the synthesis tool will automatically make use of it when mapping a description similar to listing 6.11.

Listing 6.11: Asynchronous Reset

1 process(clock, reset)
2 begin
3 if reset = ‘1’ then
4 ..asynchronous reset action
5 elsif rising_edge(clock) then
6 ...flip-flops plus logic
7 end if;
8 end process;

Counters

Synchronous counters are described in a similar manner to registers, the logic feeding the storage elements will be either an incrementer or a
decrementer, depending on the required direction of counting. Listing 6.12 shows an example description for an 8-bit Synchronous Down Counter
with Parallel Load.

The counter design entity makes use of the ‘Numeric_Std’ package, hence the inclusion of the use clause on line 3. This allows the use of the
overloaded ‘-’ operator on line 19 to decrement the count value. An additional output port named ‘tc’ is provided to indicate when the counter has
reached the count of zero. The conditional signal assignment statement on line 24 generates ‘tc’, the comparison with the natural number 0 is
allowed due to the use of Numeric_Std.

In order to avoid the need for a buffer or inout mode output, the design makes use of an internal signal named ‘count’ to hold the current value of
the counter. This signal corresponds to a register. The use of an internal signal means that ‘count’ can appear on both sides of a statement; the
actual output of the design entity ‘q’ is simply connected to ‘count’ on line 27.

Finally, the wait until.. statement on line 15 indicates that the parallel load operation is synchronous. When ‘load’ is taken to logic-1 the parallel
data is loaded into ‘count’ on the next rising edge of the clock input.

Listing 6.12: An 8-Bit Loadable Down Counter


1 library ieee;
2 use ieee.std_logic_1164.all;
3 use ieee.Numeric_Std.all;
4 entity pldcntr8 is
5 port (clk, load : in std_logic;
6 datain : in Unsigned(7 downto 0);
7 q: out Unsigned(7 downto 0);
8 tc : out std_logic);
9 end pldcntr8;

10 architecture v1 of pldcntr8 is

--internal register to hold count value


11 signal count: Unsigned(7 downto 0);

12 begin
13 count_proc : process
14 begin
15 wait until rising_edge(clk);
16 if load = '1' then
17 count <= datain;
18 else
19 count <= count - 1;
20 end if;
21 end process;
22
23 --terminal count logic
24 tc <= '1' when count = 0 else '0';
25
26 --connect counter register to output port
27 q <= count;
29
30 end v1;

Universal Counter/Register

The following listing (6.13) represents an extremely useful design entity - a universal register/counter. This description makes use of many of the
language elements discussed previously (and one or two new ones) to describe a register which can operate in a number of different modes. The
design has the added benefit of being scaleable, ie. a generic is used to set the length of the register. It is declared on line 5 and given a default
value of 8. Once a generic parameter has been declared, it can be used as a constant value anywhere in the description. For example the ‘datain’
and ‘dataout’ ports have lengths which depend on the generic ‘n’.

Some additional points regarding listing 6.13 are given below:

● The case statement (lines 18 to 33) provides an efficient way of selecting the mode of the register. The final others (line 31) clause is
required to cover all unspecified cases of ‘mode’.
● The 'Numeric_std’ supported operators ‘+’ and ‘-’ provide a concise way of incrementing and decrementing the counter (lines 24 and 26).
● Shifting is performed using the concatenation operator (&) on lines 28 and 30.
A combinational process (lines 36 to 45) makes use of a loop to test each bit of the register in order to determine if it contains zero. The
built-in attribute range is used to set the limits of the loop to the range of the register array ((n-1) downto 0). The exit statement (line 42)
terminates the loop as soon as a logic-1 is found in the register.

Listing 6.13: Universal Register/Counter

1 library ieee;
2 use ieee.std_logic_1164.all;
3 use ieee.Numeric_std.all;
4 entity unicntr is
5 generic (n: positive := 8);
6 port (clock, serinl, serinr : in std_logic;
7 mode : in std_logic_vector(2 downto 0);
8 datain : in Unsigned((n-1) downto 0);
9 dataout : out Unsigned((n-1) downto 0);
10 termcnt : out std_logic);
11 end unicntr;

12 architecture v1 of unicntr is

13 signal int_reg: Unsigned((n-1) downto 0);

14 begin

15 main_proc: process
16 begin
17 wait until rising_edge(clock);
18 case mode is
19 when "000" => --reset to zero
20 int_reg <= (others => '0');
21 when "001" => --parallel load
22 int_reg <= datain;
23 when "010" => --increment
24 int_reg <= int_reg + 1;
25 when "011" => --decrement
26 int_reg <= int_reg - 1;
27 when "100" => --shift left
28 int_reg <= int_reg((n-2) downto 0) & serinl;
29 when "101" => --shift right
30 int_reg <= serinr & int_reg((n-1) downto 1);
31 when others => --refresh
32 null;
33 end case;
34 end process;
35 --detect when internal register contains all zeros
36 det_zero: process(int_reg)
37 begin
38 termcnt <= '1';
39 for i in int_reg'rangeloop
40 if int_reg(i) = '1' then
41 termcnt <= '0';
42 exit;
43 endif;
44 endloop;
45 end process;

46 dataout <= int_reg; --connect register to port

47 end v1;

State Machines

The VHDL language is widely used to describe synchronous Finite State Machines (FSM). These sequential circuits are found in many digital
integrated circuits and systems where they are generally used to implement control sequences. The ability to describe such systems in a
behavioural fashion, coupled with the availability of automatic synthesis tools, has considerably improved the Finite State Machine design process.

A state machine comprises two main parts, as shown in the image below:

● State Register (Sequential Logic)


● Next State and Output Logic (Combinational Logic)

The state register consists of ‘k’ flip-flops, where 2k must be greater than or equal to the number of states in the design. The ‘next state and output
logic’ block is driven by the flip-flop outputs (present state) and the external Inputs. It uses these to generate the inputs to the flip-flops (next state)
and the state machine Outputs.

The block diagram shown in the image above represents the most general form of a FSM, known as the Mealy type. Variations on this structure,
and their corresponding VHDL descriptions will be presented in a later chapter.

The design of a state machine usually starts with some form of written or verbal specification which has then to be converted into a formal
representation referred to as an Algorithmic State Machine (ASM) diagram.

An example ASM diagram (or state diagram) is shown in the image below. It makes use of blue boxes to represent the states, green diamonds to
indicate transition conditions, and purple oblongs to show conditional outputs.

The state machine moves from one state to another (state transition) in time with the active clock edge and the transition path is determined by the
state of the control input ‘x’. In a Mealy state machine the outputs depend on both the present state and the control inputs. For example, in state
‘s0’ the output ‘z’ is active (logic-1) if the control input ‘x’ is high, otherwise ‘z’ is logic-0.

(A state machine in which the outputs depend on the value of the present state only is known as a Moore type FSM).
The VHDL behavioural description of the above ASM diagram is shown in listing 6.14. This description uses the built-in type ‘bit’ for port signals.
However, the state of the machine is defined in symbolic (or abstract) form using a type declaration on line 6. This makes the description far more
readable and easier to debug than if the state was represented by a binary word. A logic synthesis tool will automatically assign a unique binary
code to each state symbol, when converting the behavioural description into a gate-level circuit. The ‘present_state’ and ‘next_state’ signal objects
declared on line 7 can take on any one of the four symbolic values defined on line 6, having been assigned the type ‘state_type’.

The architecture body makes use of two processes, one describing combinational logic (line 18), and the other, sequential logic (line 11). This
structure is consistent with the block diagram of a Mealy state machine shown above.

The state register process (lines 11 to 15) suspends until an event occurs on the clock and the clock becomes a logic-1, in other words, the clock
undergoes a rising edge transition. When this happens the value of the next state signal is assigned to the present state (Q <= D). This section of
description corresponds to a 2-bit positive edge triggered register. The logic feedback process is combinational, it is driven by events on the
external input ‘x’ and the ‘present_state’. A case statement is used to elaborate each possible state with nested if..then statements determining the
values of the ‘next_state’ and external output ‘z’.

The case statement, beginning on line 20, does not require a final ‘when others => ‘ clause. This is due to the fact that all possible values of the
case expression ‘present_state’ have been elaborated (s0, s1, s2 and s3) and the number of states is a power of two. Often the number of states
declared in the ‘state_type’ type declaration does not equal a power of two, and therefore there will be additional states which could occur in the
synthesised circuit (since the state will be represented by a bit_vector or std_logic_vector). In this case, it is good practice to include a ‘when
others =>’ clause as the final limb of the case statement to ensure that the state machine cannot get trapped in an unused state.

Listing 6.14: Classic 2-Process FSM

1 entity fsm is
2 port(clock, x: in bit; z: out bit);
3 end fsm;
4 -------------------------------------------------
5 architecture behaviour of fsm is
6 type state_type is (s0,s1,s2,s3);
7 signal present_state, next_state: state_type;
8 begin
9
10 --state register process

11 state_reg: process
12 begin
13 waituntil clock'event and clock = '1';
14 present_state <= next_state;
15 end process;
16
17 --combinational logic feedback process
18 fb_logic: process(present_state,x)
19 begin
20 case present_state is
21 when s0 =>
22 if x = '0' then
23 z <= '0';
24 next_state <= s0;
25 else
26 z <= '1';
27 next_state <= s2;
28 endif;

29 when s1 =>
30 if x = '0' then
31 z <= '0';
32 next_state <= s0;
33 else
34 z <= '0';
35 next_state <= s2;
36 endif;

37 when s2 =>
38 if x = '0' then
39 z <= '1';
40 next_state <= s2;
41 else
42 z <= '0';
43 next_state <= s3;
44 endif;

45 when s3 =>
46 if x = '0' then
47 z <= '0';
48 next_state <= s3;
49 else
50 z <= '1';
51 next_state <= s1;
52 endif;
53 endcase;
54 end process;
55
56 end behaviour;

Read Sections 8.6 and 8.7, read Chapter 9 and Sections 12.1 and 12.2 of the recommended textbook (Rushton).
Advanced Electronic Design Automation
Chapter 7 - Hierarchy, Regularity and an Introduction to Test Benches

Chapter Information

This chapter contains the following sections:

7.1 Creating Hierarchical VHDL Designs - Components and Libraries


7.2 Scalable Design using Generics
7.3 Design Verification using Test Benches
Self Assessment Questions

7.1 Creating Hierarchical VHDL Designs - Components and Libraries

An essential part of the design of digital systems is the management of complexity. In order to comprehend the overall structure and behaviour of a complex system, it must be
viewed at a series of levels.

For many years designers have used schematic-based tools to create symbols to represent underlying circuits. These symbols are then instantiated in a higher level circuit which may
in turn be represented by a symbol on the top-level system block diagram. Organising schematic designs into symbols and schematics, in a hierarchical structure, as illustrated by the
image below, attempts to manage the inherent complexity of the design by hiding the details within blocks. Once a symbol has been created for a particular schematic, it can be
instantiated any number of times, and at different levels in the hierarchy.

Often a hierarchical schematic is converted into a so-called flat net-list, describing the design at the lowest level, in terms of the primitives supported by the target technology. This
type of description is often used as an input to the physical layout tools which implement the design in hardware. The VHDL language is capable of describing designs in both
hierarchical-structure and flat-net-list forms. The latter is commonly used to perform post-layout timing simulation in conjunction with accurate low level timing models of the target
technology primitive elements. (See VITAL libraries).

There are two basic mechanisms used in VHDL to support hierarchy:

● Component Instantiation
● Direct Instantiation

Both of the above achieve the same thing, ie. create an instantiation, or occurrence, of one design entity within another design entity. However, the second mechanism was
introduced in the 1993 version of the language, and as such, may not be supported by some VHDL Simulators and Synthesis tools. (Cadence Leapfrog does not support direct
instantiation)

A simple example - 4-Bit Synchronous Binary Counter using JK Flip-flops

We will illustrate the use of hierarchy in VHDL using a rather trivial example, a 4-bit counter made up from JK flip-flops. It is unlikely that the language would be used in this manner to
describe such a circuit. However, it does serve to illustrate the basic constructs and syntax involved in describing structures. The underlying circuit diagram of the counter is shown
below, in figure 7.1.

The counter makes use of two types of behavioural component, namely the JK FlipFlop and 2-
input AND gate. These components are wired together to form the counter circuit and hence they
occupy the lowest level in the hierarchy (for simulation purposes, the lowest level is always the
behavioural level). Figure 7.2 depicts the hierarchical organisation of the design.

An elementary behavioural description of a JK Flip-flop is given in listing 7.1. The architecture


body makes use of a single process which executes each time the ‘clock’ input undergoes a logic-
0 to logic-1 transition (line 12).

The if..then..elsif..else statement (lines 13 to 21) tests for each combination of the ‘j’ and ‘k’ inputs
in turn, assigning an appropriate value to the local variable ‘state’. The ‘state’ variable is then
used to update the values of the ‘q’ and ‘qbar’ flip-flop outputs on lines 23 and 24.

The other primitive element used by the counter, a 2-input AND gate, is shown in listing 7.2. The
gate is modelled using a simple concurrent signal assignment statement, on line 6 of listing 7.2.

Listing 7.1: JK Flip Flop

1 entity jkff is
2 port(clock, j, k: in bit; q, qbar: buffer bit);
3 end jkff;

4 architecture using_process of jkff is


5 begin

6 --sequential process to model jk flip-flop


7 process
8 --declare a local variable to hold ff state
9 variable state: bit:= '0';

10 begin

11 --synchronise process to rising edge of clock


12 waituntil (clock'event and clock = '1');

13 if (j = '1' and k = '1') then --toggle


14 state:= not state;
15 elsif (j = '0' and k = '1') then --reset
16 state:= '0';
17 elsif (j = '1' and k = '0') then --set
18 state:= '1';
19 else --no change
20 state:= state;
21 end if;

22 --assign values to output signals


23 q <= state after 5 ns;
24 qbar <= not state after 5 ns;

25 end process;
26
27 end using_process;

Listing 7.2: 2-input AND gate

1 entity and_gate is
2 port(a, b : in bit; f: out bit);
3 end and_gate;

4 architecture simple of and_gate is


5 begin
6 f <= a and b after 2 ns;
7 end simple;

Having created the two design entities listed above, the next step involves analysing the
corresponding source descriptions using a VHDL Analyser. This analysis (or compilation) process
results in the creation of what are commonly termed library units located in the so-called working
library. The work library normally corresponds to a folder of the same name located below the folder
containing the VHDL source files. The exact format of the compiled design units depends heavily on
the VHDL Simulator being used. Typically , the higher performance simulators convert the VHDL
source descriptions directly into executable machine code modules which are subsequently linked
together during the elaboration phase prior to simulation.

The image shown to the right illustrates the idea of the ‘project’ and ‘work’ folders, the latter
corresponding to the ‘work’ library containing compiled design units.

The ‘work’ library has a special property which is not applicable to other libraries. Whereas the library
‘ieee’ and it’s contents must be made visible using a library and use clause, library ‘work’ is implicitly
made available to all design units, rather as if an invisible ‘library work;’ clause preceded every
design entity.

However, in order to instantiate a design entity within another design entity, the lower level design
entity must be declared in the form of a component. The component declaration is placed in the
architecture-declarations region of the higher level design entity, it creates a socket into which a
design entity may be placed.

component entity_name
port(list_of_ports);
end component;

Although not strictly required, it is common practice to use the same name and ports for the
component, and the entity to which it will be bound. Listing 7.3 shows the structural description of the
4-bit binary counter, the component declarations are highlighted starting at lines 5 and 8. The
component declarations serve to indicate that a lower level design entity is being made use of, they
do not specify which entity architecture pair will be ‘plugged into’ the ‘socket’.
The architecture statement part (lines 14 to 20) contains a set of component instantiation statements
which must be labelled in order to distinguish instances of the same component. The component
name (usually the entity name) is followed by the so-called port map specifying how the component
is connected up to the local environment. The connections can be mapped in one of two ways:

● Positional association - the local signals are mapped by position according to the order
defined by the component declaration. (Used in listing 7.3)
● Named association - the association operator ‘=>’ is used to link the formal ports of the
component to the actual local signals. The associations can be listed in any order.

It makes sense to only map signal objects to the ports of a component. These can be locally declared signals, such as ‘s1’ and ‘s2', or ports such as ‘count(0)’. If an upper level port is
being mapped to a component instance port, they must have compatible modes. For example, it would be wrong to try and connect an input port such as ‘clock’ to the output port ‘q’
of a JK Flip-flop.
This last point is further illustrated by the need to make the ‘count’ output port of the ‘mod16_cntr’ entity a buffer port, in order to match the mode of the ‘q’ outputs of the JK Flip-flops.

Referring back to the circuit diagram of the 4-bit counter, it can be seen that the ‘qbar’ outputs of the JK Flip-flops are open circuit. In the VHDL language, this corresponds to using
the reserved word open in the port mapping. (Input ports can also be left ‘open’, they will generally take on a value corresponding to the left-hand element of the port’s type
declaration - ie. a ‘bit’ type port would default to ‘0’, whereas a ‘std_logic’ type port would have the value ‘U’).
Listing 7.3: Structural Description of the 4-bit Counter using local declarations

1 entity mod16_cntr is
2 port(clock: in bit; count: buffer bit_vector(0 to 3));
3 end mod16_cntr;

4 architecture net_list of mod16_cntr is


5 component jkff
6 port(clock, j, k: in bit; q, qbar: buffer bit);
7 end component;
8 component and_gate
9 port(a, b: in bit; f: out bit);
10 end component;
11 signal tied_high: bit:= '1';
12 signal s1,s2: bit;

13 begin

14 a1: and_gate port map (count(0),count(1),s1);


15 a2: and_gate port map (s1, count(2), s2);
16 jk1: jkff port map
17 (clock,tied_high,tied_high,count(0),open);
18 jk2: jkff port map (clock,count(0),count(0),count(1),open);
19 jk3: jkff port map (clock,s1,s1,count(2),open);
20 jk4: jkff port map (clock,s2,s2,count(3),open);
21 end net_list;

The Default Binding

When a hierarchical VHDL design is loaded for simulation, all instantiated components need to be bound to entity architecture pairs. Most commercial simulators support a
mechanism known as Default Binding in which the instantiated component is bound to the last design entity compiled into the library having the same name as the component. In the
case of the 4-bit counter example discussed above, there is only one JK Flip-flop entity contained in library work, and therefore it is this library unit which will be loaded into the
simulator.

The VHDL language supports the use of multiple architectures for a given entity which is extremely useful in the context of logic synthesis, since the output of such a tool is often a
VHDL gate-level net-list having the same entity declaration as the original RTL description:

RTL Description --> work.myentity(behaviour)


Gate-level net-list --> work.myentity(post_synth)

In the above, the library units are referred to by the convention ‘library_name.entity_name(architecture_name)’.

If we assume that the component ‘myentity’ is instantiated within a design, it is essential to be able to select which of the two alternative architectures to load prior to a simulation run.
This is the function performed by the VHDL construct known as the Configuration.

There are two types of Configuration:

● Configuration Specification - An architecture declarative item


● Configuration Declaration - A Primary Library Unit

The first variation on the configuration allows a component instance (or instances) to be bound to a specific entity-architecture pair by specifying the binding within the architecture
which instantiates the design entities. In the case of the 4-bit counter example, the configuration specifications would take the following form:

Listing 7.3, insert line 12a:

for all: jkff use entity work.jkff(using_process);

Listing 7.3, insert line 12b:

for all: and_gate use entity work.and_gate(simple);

In the above, the keyword all is used to select all occurrences (or labels) of the component having the names ‘jkff’ and ‘and_gate’. The default binding mechanism often means that
the two lines given above are redundant if only one entity-architecture pair exists in the library for a given component. However, under certain circumstances it may be necessary to
explicitly select a particular design entity to use for a component.

Using a Package

The need to declare all components within the architecture declarative part, prior to instantiating them in the architecture statement part, is rather cumbersome, particularly if the
design makes use of many different components. In addition, it is generally more convenient if the component declarations for a large library of components, such as a FPGA vendor
library, are put in a single place away from the design entities that make use of them.

The VHDL language provides a construct for grouping a number of elements under one common name - the package. Listing 7.4 shows an example of a package declaration
containing the two component declarations used by the current example. These are included along with a common signal declaration under the package name ‘jkpack’.

The package declaration is a primary design unit and as such can be analysed and placed in the working library (or a different library if required). The declarations contained in a
package can be made available to a design entity by attaching the package to the design entity by means of a use clause. If the package is situated in the library named ‘work’, then
only a use clause is required, since all design entities have access to library work by default.

Listing 7.4: Package Declaration

1 package jkpack is

2 signal tied_high : bit:= '1';

3 component jkff
4 port(clock, j, k: in bit; q, qbar: buffer bit);
5 end component;

6 component and_gate
7 port(a, b: in bit; f: out bit);
8 end component;

9 end jkpack;

Listing 7.5 is a repeat of the 4-bit binary counter description, but this time the entity declaration is preceded by a use clause (line 1) making the contents of the package ‘jkpack’
available to the ‘mod16_cntr’ design entity. The keyword ‘all’, on line 1 of listing 7.5, indicates that all of the declarations contained within the package are being made available.
The inclusion of line 1 in listing 7.5 means that the architecture declarative part (line 6) does not need to explicitly declare the two components ‘jkff’ and ‘and_gate’ and the global
signal ‘tied_high’. In a sense, these declarations are effectively included via the package and use clause, and the architecture statement part can make reference to them in exactly
the same manner as before.

The package declaration provides a number of useful facilities:

● Long-winded declarations can be hidden away in a common place


● A package can be used by any number of designs, promoting design sharing and reuse.
● A vendor can provide low-level simulation models using a consistent format

In general a package does not need to be compiled into library ‘work’, it may be beneficial to have separate libraries containing different groups of design units. For example, a
package named ‘actelprims’, containing a library of primitive cell declarations to support the design of Actel FPGAs, may be analysed into a library named ‘actellib’. For a design
entity to instantiate components held in this library, it must be preceded by the following clauses:

library actellib;
use actellib.actelprims.all;

The first of the two lines shown above makes the library name ‘actellib’ visible to the design entity. The second line makes all of the component declarations contained in the package
‘actelprims’ available to the design entity, along with their corresponding design entities (the latter are required for simulation).

The logical name ‘actellib’ corresponds to a directory or folder situated on the host computer file system containing the compiled design units.

Listing 7.5: Structural Description of the 4-bit Counter using package ‘jkpack’
1 use work.jkpack.all;
2 entity mod16_cntr is
3 port(clock: inbit; count: buffer bit_vector(0 to 3));
4 end mod16_cntr;

5 architecture net_list of mod16_cntr is

6 signal s1,s2: bit;

7 begin

8 a1: and_gate port map (count(0),count(1),s1);


9 a2: and_gate port map (s1, count(2), s2);
10 jk1: jkff port map
11 (clock,tied_high,tied_high,count(0),open);
12 jk2: jkff port map (clock,count(0),count(0),count(1),open);
13 jk3: jkff port map (clock,s1,s1,count(2),open);
14 jk4: jkff port map (clock,s2,s2,count(3),open);

15 end net_list;

Read Chapter 10, Sections 10.1 to 10.9 of the recommended textbook (Rushton).
7.2 Scalable Design using Generics

We have already seen a number of examples which make use of generic ports in the previous chapters. This section will describe their use in the context of creating scalable (or
parameterisable) designs. A scalable design consists of a design which can be stretched or shrunk to any required size, in one or more dimensions. Since the majority of digital
systems involve the processing of binary encoded words, a very common requirement is to be able to handle words of different lengths - counters, registers, arithmetic units,
decoders, multiplexers and buffers can all be made scalable, ie. capable of handling different word lengths.

The ability to create scalable hardware descriptions in VHDL relies on another construct which is often used in conjunction with generics - the generate statement. This concurrent
statement is used to replicate other concurrent statements using a mechanism similar to a for..loop. The combination of the generic parameter and the generate statement constitutes
a powerful tool for creating designs which can be reused.

An n-bit Adder

As a first illustration of scalable design, the image below shows a logic circuit possessing inherent regularity of structure - the ripple carry adder. The image shows a 4-bit adder.
However, the VHDL description which follows is capable of adding words of any length. The circuit comprises a set of Full Adder modules, the carry-out of each one feeding the carry-
in of the next, with the exception of the least-significant and most-significant stages. Every Full Adder, apart from the outermost pair, is connected to its neighbouring Full Adders in
exactly the same way. This constitutes an ideal topology for the generate statement. The first and last stages represent the boundary conditions and these irregularities are handled
by the ‘if condition generate’ form of the generate statement.

The logic circuit for an individual Full Adder stage is shown in the image below.

Listing 7.6 shows the source description for Scalable Ripple Carry Adder. The entity declaration includes a generic port clause on line 2, specifying a positive integer ‘n’ and assigning
it a default value of 3. The number ‘n’, once declared and initialised, can be used anywhere within the design entity as a constant value. It is used to set the word size of the adder on
lines 3 and 5, where the ‘(n+1)-bit’ input and output ports are declared.

For simulation purposes the generic port is often given a default value as shown in listing 7.6. However, this can be overridden by an alternative value when the design entity is
instantiated as a component, by means of a ‘generic map’:
u1: addn generic map(7) port map(a, b, cin, cout, ovfl, s);

The above component instantiation statement creates an 8-bit adder.

The architecture body of the scalable adder declares a local signal on line 8 to hold the carries generated by each stage of the adder. The statement part of the architecture
comprises an outermost generate statement enclosing two conditional generate statements. The outermost generate statement (bounded by lines 10 and 24) uses the ‘range’
attribute to iterate through each stage of the adder (0 to n), the indexing variable, ‘i’, is implicitly declared by the statement itself. The first conditional generate statement (lines 11 to
16) tests for the condition i = 0, which corresponds to the least significant stage of the adder. This stage is the only one which makes use of the ‘carry_in’ port in the carry and sum
equations. The two concurrent signal assignment statements on lines 12 and 13 are generated once only for i=0.

The second nested generate statement (lines 17 to 23), generates full adder stages 1 to n (n /= 0). These stages are all interconnected in the same way, the (i-1)th carry is used to
produce both the ith sum and ith carry signals.

Listing 7.6: A Scalable Ripple-Carry Adder

1 entity addn is
2 generic(n : positive:= 3); --no. of bits less one
3 port(addend, augend : in bit_vector(0 to n);
4 carry_in: in bit; carry_out, overflow: out bit;
5 sum : out bit_vector(0 to n));
6 end addn;

7 architecture generated of addn is


8 signal carries: bit_vector(0 to n);
9 begin
10 addgen: for i in addend'range generate
11 lsadder: if i = 0 generate
12 sum(i) <= addend(i) xor augend(i) xor carry_in;
13 carries(i) <= (addend(i) and augend(i)) or
14 (addend(i) and carry_in) or
15 (carry_in and augend(i));
16 end generate;
17 otheradder : if i /= 0 generate
18 sum(i) <= addend(i) xor augend(i)
19 xor carries(i-1);
20 carries(i) <= (addend(i) and augend(i)) or
21 (addend(i) and carries(i-1)) or
22 (carries(i-1) and augend(i));
23 end generate;
24 end generate;
25 carry_out <= carries(n);
26 overflow <= carries(n-1) xor carries(n);
27 end generated;

The combination of outer and inner


generate statements produces a total
of ‘n+1’ sum equations and the same
number of carry equations. The
corresponding concurrent statements
are developed during the simulator
initialisation or elaboration phase.
Often a VHDL simulator/synthesiser
will label each individual concurrent
statement developed from a generate
statement using the notation
‘generate_statement_label__index_no’.
In the case of the present example, this
would result in a structure as shown in
the image to the left.
Any Size Binary Counter

The second example, demonstrating the use of the generate statement, involves duplicating component instantiations in order to create a scalable binary counter. A synchronous
binary counter can be constructed by cascading T-type Flip-flops and 2-input AND-gates. The image below shows a 5-bit example.

As shown, each flip-flop has its T-input connected to the output of a 2-input AND-gate, with the exception of the least significant stage (its T-input is tied high). The AND-gates form a
sort of carry-chain, feeding pulses to each flip-flop, thereby causing them to toggle at the correct points in time. Synchronous counters of any length can be created from this simple
structure. As the length of the counter increases however, the maximum count rate decreases due to accumulated gate delays.

The VHDL description of the generated binary counter is given in listing 7.7.

The design entity spanning lines 0 to 19 is a T-type flip-flop with asynchronous clear input. This behavioural module is used as a component in the counter description beginning on
line 20.

The ‘bigcntr’ design entity combines the use of a generic port defining the length of the counter (size) with the use of the generate statement to produce multiple instantiations of the T-
type flip-flop component and associated AND gates. The result is a counter of length equal to ‘size’.

This design can be used as a benchmark to compare the performance of different commercial VHDL simulators. Specifying a counter size of 32-bits and running the simulation for
several thousand clock cycles presents a considerable event processing load to a simulator.

Listing 7.7: Scalable Synchronous Binary Counter using T-Type Flip-flops

0 library ieee;
1 use ieee.std_logic_1164.all;
2 entity tff is
3 port(clk, t, clear : in std_logic; q: buffer std_logic);
4 end tff;

5 architecture v1 of tff is
6 begin
7 process(clear, clk)
8 begin
9 if clear = '1' then
10 q <= '0';
11 elsif rising_edge(clk) then
12 if t = '1' then
13 q <= not q;
14 else
15 null;
16 end if;
17 end if;
18 end process;
19 end v1;

20 library ieee;
21 use ieee.std_logic_1164.all;
22 entity bigcntr is
23 generic(size : positive:= 32);
24 port(clk, clear : in std_logic;
25 q: buffer std_logic_vector((size-1) downto 0));
26 end bigcntr;

27 architecture v1 of bigcntr is

28 component tff is
29 port(clk, t, clear: in std_logic;
30 q: buffer std_logic);
31 end component;

32 signal tin: std_logic_vector((size-1) downto 0);

33 begin

34 genttf: for i in (size-1) downto 0 generate


35 ttype: tff port map (clk, tin(i), clear, q(i));
36 end generate;
37 genand: for i in 0 to (size-1) generate
38 t0: if i = 0 generate
39 tin(i) <= '1';
40 end generate;
41 t1_size: if i > 0 generate
42 tin(i) <= q(i-1) and tin(i-1);
43 end generate;
44 end generate;

45 end v1;

Read Chapter 10, Sections 10.10 and 10.11 from the recommended textbook (Rushton).

7.3 Design Verification using Test Benches

The range of constructs and types provided by the VHDL language make it extremely useful for the construction of so-called test benches. A test bench is a self-contained VHDL
description containing an instantiation of the design under test, along with components and/or processes to perform design verification. Typically, a test bench will include a clock
source (if the design is synchronous), a test pattern generator, and perhaps a response analyser.

The fundamental advantages of using the VHDL language to create a test environment are as follows:

● It removes the need to learn a simulation-platform-specific command language.


● The designer is forced to consider the test problem from the outset.
● The design and accompanying test environment are fully portable.

Listing 7.8, shown below, illustrates the general structure of a rudimentary VHDL test bench description. The entity declaration (lines 1 to 4) contains no port or generic clauses, since
the test bench represents the very top of the design hierarchy, and as such, will not be instantiated within another design.

The architecture declares a component corresponding to the actual design being tested, along with a pattern generator and a set of local signals to interconnect them. The pattern
generator can take a wide variety of forms, ranging from a simple sequential process, assigning locally declared test vectors at fixed intervals, to a highly sophisticated model
accessing test data from standard format ASCII files.

In order to ensure consistency, it may be useful to verify a design both before and after synthesis using the same test bench. For this purpose, a test bench may include a
configuration specification within the architecture declarations part, to explicitly select the appropriate version of the design for simulation.

Lines 13 and 14 of listing 7.8 show two alternative configuration specifications for pre- and post-synthesis, line 14 assumes that the synthesised net-list description has the same
name as the original design.
Listing 7.8: General form of a Test Bench Design Entity

1 library ieee;
2 use ieee.std_logic_1164.all;
3 entity testbench is --no ports !!
4 end testbench;

5 architecture general of testbench is

6 component design
7 port(list_of_ports);
8 end component;

9 component pattern_generator
10 port(list_of_ports);
11 end component;

12 --configuration specification to select design architecture


13 for dut: design useentity work.design(behaviour);
14 OR for dut: design use entity work.design(netlist);

15 --declare local signals to connect dut and pattern_generator


16 signal list_of_signals : std_logic;

17 begin

18 --optional clock generator


19 process
20 begin
21 clock <= '0', '1' after 50 ns;
22 wait for 100 ns;
23 end process;

24 --instantiate test pattern generator


25 patgen1: pattern_generator port map (clock, …………);

26 --instantiate device under test


27 dut: design port map (clock,………………);

28 end general;

Having declared the design-under-test and pattern generator components, the architecture statement part instantiates them, connecting the outputs of the pattern generator to the
inputs of the design. In the absence of a response analyser, the design outputs are connected to local signals in order that they may be viewed during the simulation run.

It is common for the design to be a synchronous sequential system, in which case the pattern generator is required to output patterns in time with the system clock. Listing 7.8
illustrates how a repetitive clock signal may be generated by a process, on lines 20 to 23.

Example - Testing a Pattern Detector FSM

The following example demonstrates how a test bench may be put together to test the operation of a Finite State Machine. The function of the FSM is to detect, on a serial input data
stream ‘serin’, the pattern “01111110”. Whenever the pattern is detected, an output named ‘match’ is asserted for one clock period.

The image below shows the Algorithmic State Machine diagram for the FSM (taken from Chapter 6 SAQ no. 14), and listing 7.9 contains the equivalent VHDL description.
Listing 7.9: Pattern Detector FSM

1 library ieee;
2 use ieee.std_logic_1164.all;
3 entity patdet is
4 port(clock, serin, reset: in std_logic;
5 match : out std_logic);
6 end patdet;

7 architecture v1 of patdet is

8 type state_type is (s0, s1, s2, s3, s4, s5, s6, s7, s8);
9 signal pstate, nstate : state_type;
10
11 begin

12 --state register
13 process(clock, reset) begin
14 if reset = '1' then
15 pstate <= s0;
16 elsif rising_edge(clock) then
17 pstate <= nstate;
18 end if;
19 end process;
20 --next state logic
21 process(serin, pstate)
22 begin
23 case pstate is
24 when s0 =>
25 if serin = '0' then
26 nstate <= s0;
27 else
28 nstate <= s1;
29 end if;
30 when s1 =>
31 if serin = '0' then
32 nstate <= s0;
33 else
34 nstate <= s2;
35 end if;
36 when s2 =>
37 if serin = '0' then
38 nstate <= s0;
39 else
40 nstate <= s3;
41 end if;
42 when s3 =>
43 ifserin = '0' then
44 nstate <= s0;
45 else
46 nstate <= s4;
47 end if;
48 when s4 =>
49 if serin = '0' then
50 nstate <= s0;
51 else
52 nstate <= s5;
53 end if;
54 when s5 =>
55 if serin = '0' then
56 nstate <= s0;
57 else
58 nstate <= s6;
59 end if;
60 when s6 =>
61 if serin = '1' then
62 nstate <= s8;
63 else
64 nstate <= s7;
65 end if;
66 when s7 =>
67 if serin = '0' then
68 nstate <= s0;
69 else
70 nstate <= s8;
71 end if;
72 when s8 =>
73 if serin = '0' then
74 nstate <= s0;
75 else
76 nstate <= s8;
77 end if;
78 endcase;
79 end process;

80 --generate output
81 match <= '1' when pstate = s7 else '0';

82 end v1;

Testing the pattern detector FSM involves applying a set of random patterns to the serial data input while observing the output. It is important to verify that the FSM is capable of
ignoring all other patterns, in addition to being able to detect the unique string “01111110”.

A very compact circuit, capable of generating large amounts of test data, is the Pseudo-Random Bit Sequence Generator (PRBSG). A 9-bit PRBSG, suitable for testing the Pattern
detector FSM, is shown in the image below. The circuit cycles through 511 states and produces the “01111110” string only twice during each complete sequence.

The serial output of the PRBSG, ‘prbs’, will be connected to the ‘serin’ input of the Pattern Detector FSM. Both circuits will be initialised by a common ‘reset’ signal, and clocked using
a common ‘clock’ signal. This will ensure that the PRBSG and Pattern Detector FSM are synchronised to each other.
Listing 7.10 contains the VHDL description of the PRBSG. The model uses generic ports to select the length and feedback taps of the shift register. These are set to default values
corresponding to the circuit diagram shown in the image above. The shift register and feedback circuitry are described using a single conditional signal assignment statement on lines
14 to 16. It should be noted that this form of description would not be recognised by most synthesis tools. However, this is of no consequence, since the model is not intended for
synthesis. (Some simulators may have a problem with lines 14 to 15 also).

Listing 7.10: Pseudo-random Bit Sequence Generator

1 library ieee;
2 use ieee.std_logic_1164.all;
3 entity prbsgen is
4 generic(length: positive:= 8;
5 tap1: positive:= 8;
6 tap2: positive:= 4);
7 port(clk, reset : in std_logic; prbs: out std_logic);
8 end prbsgen;

9 architecture v3 of prbsgen is

10 --create a shift register


11 signal prreg: std_logic_vector(length downto 0);

12 begin

13 --signal assignment shifts register and feeds in xor value


14 prreg <= (0 => '1', others => '0') when reset = '1' else
15 (prreg((length - 1) downto 0) & (prreg(tap1) xor
16 prreg(tap2))) when rising_edge(clk) else prreg;

17 --connect msb of register to output


18 prbs <= prreg(length);

19 end v3;

The FSM and random pattern generator are combined, along with a free-running clock generator, to form the pattern detector test bench, shown in diagrammatic form below. The
blue box represents the design under test, and as such the only part of the description intended for synthesis. The corresponding VHDL description of the test bench is given in listing
7.11.
Listing 7.11: Pattern Detector FSM Test Bench

1 library ieee;
2 use ieee.std_logic_1164.all;
3 entity patdetbench is
4 end patdetbench;

5 --defining architecture for functional simulation


6 architecture precomp of patdetbench is

7 component prbsgen
8 port(clk, reset: in std_logic; prbs: out std_logic);
9 end component;

10 component patdet
11 port(clock, serin, reset: in std_logic;
12 match: out std_logic);
13 end component;

14 --configure patdet to be functional model


15 for patdet1: patdet use entity work.patdet(v1);

16 --declare local signals to connect modules


17 signal clock, reset, pattern, match: std_logic;

18 begin

19 --clock generator
20 process
21 begin
22 clock <= '0', '1' after 50 ns;
23 wait for 100 ns;
24 end process;

25 --instantiate test pattern generator


26 patgen1: prbsgen port map (clock, reset, pattern);

27 --instantiate device under test


28 patdet1: patdet port map (clock, pattern, reset, match);

29 end precomp;

Once all of the design files have been analysed and the corresponding design units loaded into library ‘work’, the simulation of the ‘patdetbench’ test bench design entity can proceed.

Following a reset operation, the model is run for 511 clock cycles in order to apply one complete pseudo random sequence to the pattern detector serial input. A close-up view of the
resulting test bench waveforms is shown in the image below. As shown, the pattern detector has correctly picked up the two occurrences of the “01111110” string.

After synthesising the ‘patdet.vhd’ design file, the equivalent gate-level net-list produced by the synthesis tool can be re-simulated using the same test bench. After analysing the
synthesised net-list, the configuration specification contained in the test bench design entity (line 15 of listing 7.11), would be modified to that shown below:
for patdet1: patdet use entity work.patdet(netlist);

where ‘netlist’ is the name of the post-synthesis VHDL architecture.

Having generated a gate-level net-list for the design, the next step would normally involve the use of a technology-specific place-and-route physical design tool to generate a layout
for the design. The exact nature of this process depends heavily on the chosen target technology, but a common overhead introduced by the place-and-route process is time delays.
Most FPGA and ASIC physical design tools produce a standard format delay file (SDF file) containing delay information for all of the nets associated with the design. This file can be
loaded into the simulator and the delays appended to the gate-level description.

The process described above is known as Back Annotation. It allows a VHDL simulator to be used for the purpose of accurate timing simulation after the synthesis and physical
layout steps have taken place, providing the designer with additional information concerning the performance of the design.

Read Chapter 13, Sections 13.1 to 13.6 from the recommended textbook (Rushton).
Advanced Electronic Design Automation

Chapter 8 - VHDL for Synthesis

Chapter Information

The chapter contains the following sections:

8.5 Combinatorial Examples


8.1 Benefits of Logic Synthesis
8.6 Infering Latches
8.2 Logic Synthesis Design Flow
8.7 Sequential Examples
8.3 VHDL Language Support
8.8 Finite State Machines
8.4 Language Guidelines for
Synthesis
8.9 Cadence Synthesis Introductory

8.1 Benefits of Logic Synthesis

Logic Synthesis represents an important activity in today's digital design environment. Modern Synthesis tools are
reliable, robust and produce high-quality optimised circuits from Register Transfer Level HDL designs.

Logic Synthesis tools play a central role in 'Digital Electronic Design Automation', a loose definition of logic synthesis is
as follows:

'The automated process of converting a RTL/Data-flow model of a digital-system into a gate-level circuit'

The above process is depicted by the image below:

Synthesis: Conversion from HDL to gates

Most, if not all, digital designers make use of logic synthesis in the design of complex FPGAs and ASICs. In today's
aggressive microelectronics marketplace, market windows are shorter and consequently designs must be produced
within a shorter time-scale, while still maintaining high quality.

Synthesis frees the designer from the drudgery of manual gate-level design, allowing him/her to design at a higher
level. The designer can concentrate their efforts on the more important aspects of the design, such as architectural
design and algorithm development. Some of the other benefits of Logic Synthesis are listed below:

● Increased designer productivity - designs can be created and verified more rapidly at the Register Transfer Level (RTL)
since a few lines of RTL may be the equivalent of several hundred gates.

● High-level design entry - The high-level constructs available in HDLs, combined with modern graphical entry tools allow the
designer to experiment with alternative designs more easily.

● Improved quality - State-of-the-art synthesis tools are highly optimised and reliable, producing quality circuits which meet the
design criteria in all respects.

● Consistency between levels of hierarchy - Automating the transfer to gate-level implementation guarantees that a design
remains functionally identical when moving from one level to another. Manual design can introduce errors and inconsistencies
when moving from one level to another.

● Reduction of layout design expertise requirement - Whilst silicon layout design is still crucial to the design of individually
optimised cells, most IC design layouts are produced by automated tools using advanced placement and routing algorithms
and standard cell libraries.

● Technology independence - The choice of the so-called 'Target Technology' can be deferred to a much later stage of the
design process when the initial design is a RTL HDL description. Synthesis allows the designer to experiment with alternative
hardware technologies (PLD, CPLD, FPGA, ASIC….) before making a final commitment.

● Facilitates design re-use and sharing - The standardisation of HDLs means that designs written using such languages can
be ported from one design environment to another without major changes being necessary. Commonly required Sub-systems
and functional blocks can be shared amongst design teams and between organisations when in HDL form. The use of generics
and parameters means that designs can be made scalable - i.e. the same HDL code can be used to create circuits of varying
sizes without requiring major changes to the code.

A word of caution!

Despite all of the positive things one can say about logic synthesis, it is not a panacea. Designers making use of EDA
tools must always be aware of the limitations of such tools, in particular it must be remembered that logic synthesis
tools do not create or design logic that is not already implied by the HDL description.
This means that if the RTL design is in some way flawed, then the synthesis process will yield a highly optimal, but
equally flawed, output gate-level circuit.

Synthesis tools, like all automated tools, must be used with caution - Rubbish in leads to rubbish out!

The following basic facts apply to the vast majority of logic synthesis tools:

● Registers are implied by synchronizing assignments to clocks - the inclusion of the statement 'wait until rising_edge(CLK);' at
the beginning of a process means that all signals assigned to within the process will end up, at the gate-level, being the outputs
of positive-edge triggered flip-flops.

● Synthesis optimizes combinatorial logic - Combinatorial logic can be described using concurrent or sequential assignments in a
HDL, without explicitly using logical operators. Logic Synthesis tools produce the Boolean equations that are inferred by these
statements and then map these equations to gate-level circuits that are optimized to suite the target technology.

● Synthesis does not add, delete or move registers - All the registers contained in the gate-level output net-list correlate directly
with those implied by the HDL code.

8.2 Logic Synthesis Design Flow

The flow diagram shown below illustrates the basic flow of activity involved with Logic Synthesis.
Synthesis Flow

The process starts with the creation of the Register Transfer Level VHDL source files that describe the functionality of
the hardware. Often, a designer will also create a Test-Bench (written in VHDL) to aid in the verification of the design.

The RTL source files must be written using only those constructs that are supported by synthesis - details of what can
and can't be included in the RTL source is covered in the next section - 'VHDL Language Support'.
Having created the RTL source file and test-bench, it is always essential to perform thorough simulations of the design
prior to synthesis, in order to verify that the functionality of the design is correct.

As previously stated, a logic synthesis tool has no way of checking for the correctness of a design in terms of
functionality, hence the need for extensive pre-synthesis simulation. At this stage in the process, any technology-
dependent time delays are not known, and therefore the simulation waveforms are 'ideal' in the sense that they do not
exhibit delays.

Once the designer has established that the sources have been fully verified it is time to perform synthesis. For most
tools the first step is to simply add those VHDL source files associated with the design to a synthesis 'project'.

It is important to bear in mind that only the design descriptions can be synthesised, the test-bench sources should not
be added to the project since these have been created for the purpose of simulation only.

All synthesis tools allow the designer to specify a set of so-called 'user constraints' to the design prior to synthesis.
These are used to control the manner in which the synthesis tool creates the hardware implementation of the RTL
source descriptions.

Examples of user constraints are as follows:

● Clocks - most synthesis tools allow the user to specify the target clock frequency (and perhaps duty cycle) for
the design. The image below shows the dialogue box used in Synopsis FPGA Express to set the required clock

● Path delays from inputs to outputs - As show below, the desired delay between inputs and outputs can be
specified prior to synthesis. This can be done for all ports or just specified for certain critical I/Os.

● Ports - A fundamental requirement for many designs is the placement of the I/O signals on the target device
(often needed to match the layout of a printed circuit board). This so-called 'Pad Location' constraint binds a
particular I/O signal with a particular physical pin on the target chip. The synthesis tool will attempt to route the
top-level I/O signals to the required pin locations specified by the user.
● State Encoding - The majority of digital designs include one or more Finite State Machines (FSM). In the VHDL
language the 'state' of an FSM is often specified in terms of an abstract enumeration type such as:

type State_type is (RES, S0, S1, S2, S3);

During synthesis, the abstract names specified by the type declaration above are mapped into binary codes stored in
flip-flops at the gate level using one of a number of alternative encoding methods as shown in the Synopsis Synthesis
Options dialogue box below.

Having specified the RTL sources along with the user constraints, the actual process of synthesis can commence. As
shown in the flow diagram above, this consists of three stages (shown in blue).

Firstly, the RTL source statements are converted into Boolean equations and references to flip-flops, the latter are
usually D-type. At this point the design representation is still somewhat independent of the target technology.

The second and third steps may be considered as one process, involving translation, mapping and optimisation of the
logic equations as they are converted into gate-level circuits. Some optimisation may be achieved by removing
redundant logic and attempting to share resources wherever possible. Some factorisation of Boolean equations may be
necessary to map the logic onto multi-level gate circuits. The 'Synthesis Technology Library' contains details of the gate-
level resources available in the chosen technology, each alternative type of device and manufacturer will provide a
separate technology library for a given synthesis tool.

The output of the synthesis process (the grey box shown in the flow diagram) is a gate-level net-list, a text file
containing a list of circuit components and interconnections. The format of the net-list may adhere to one of several
'standards' such as EDIF (Electronic Design Interchange Format), VHDL or XNF (Xilinx Netlist Format).

The so-called 'gate-level sign-off' step represents the point at which the design may be committed to actual hardware,
whether that is an ASIC, CPLD or FPGA. In order to ensure that the design will ultimately work correctly, it is the
designer's responsibility to perform post-synthesis simulation.

Post-synthesis simulation is perhaps just as important as pre-synthesis simulation, in that it is very important to check
that the output of the synthesis tool remains consistent with the original design specification. Often a design is intended
to run a clock frequency that approaches the limits imposed by the chosen technology, in this situation it is extremely
important to perform post-synthesis and post-layout timing simulation. The latter includes time delay information
extracted from the physical 'place and route' tools in the form of a Standard Delay Format (SDF) file.

In order to provide designers with a consistent and accurate means to perform timing simulation, the IEEE have
produced a standard known as VITAL (VHDL Initiative Toward ASIC Libraries) which lays down a framework for
defining gate-level primitives and their corresponding timing behaviour. The vast majority of Silicon vendors have
produced VITAL libraries to support timing simulation of their parts, the details of which are largely transparent to the
designer.

The design entities contained within a VITAL compliant library are capable of performing detailed timing checks during
a simulation run and report any problems via the 'message' window if they arise. If timing problems do arise, they may
be rectified prior to sign-off by a combination of approaches that may include one or more of the following:

1. Tighten up the synthesis timing constraints


2. Review the choice of target technology - a faster chip may be needed
3. Review the original RTL source design - there may be asynchronous logic in the design which could be removed

Most of these approaches require re-running one or more of the steps involved in the synthesis process until the
problem is removed.

8.3 VHDL Language Support

Supported VHDL Language Constructs

The following list is representative of most synthesis tools in terms of which language constructs are supported. Expect
to see some minor variations between one tool and another - if in doubt, read the documentation.

● Entity, Architecture and Package design units.

● Function and Procedure sub-programs

● IEEE Libraries - Std_Logic_1164, Std_Logic_Unsigned, Std_Logic_Signed, Numeric_Std and Numeric_Bit

● Ports of mode in, out, inout and buffer

● Signals, Constants and Variables (the latter should be restricted to sub-programs and processes)

● Composite types - Arrays and Records

● Integer and subtypes Natural and Positive (Integer types should have a range constraint unless a 32-bit word
is required)

● User defined enumeration types (eg. type State_type is (s0, s1, s2, s3);).

● Operators - +, - , *, /, **, mod, rem, abs, not, =, /=, <, >, <=, >=, and, or, nand, nor, xor, xnor, sll, srl, sla, sra,
rol, ror, & . (Notes: /, mod and rem are usually supported for compile-time constants or when the right-hand
argument is a power of 2. The shifting operators are usually supported for compile-time constant shift values)

● Sequential statements - signal and variable assignments, wait, if, case, loop, for, while, return, null, function
and procedure call. (Note: only a single wait statement is allowed in a process)

● Concurrent statements - signal assignment, process, block, component instantiation, sub-program call,
generate.

● Generic ports in entities.

● Predefined attributes - 'range, 'event

● Aggregates and others clause.

Unsupported VHDL Language Constructs

The following list is representative of most synthesis tools in terms of which language constructs are not supported,
inclusion of these constructs will normally result in an error message. As with supported constructs, expect to see some
minor variations between one tool and another - if in doubt, read the documentation.

● Access and File types - the former are similar to C's pointers and files have no direct correspondence to
hardware.

● Register and Bus kind signals - very rarely used VHDL constructs.

● Guarded blocks - as above, rarely used.

● Next and Exit loop control statements - A synthesis tools creates logic from a loop by 'unwinding' the loop into a
series of iterations, often resulting in iterative circuits. Prematurely terminating a loop prevents this unwinding
process.

● Objects of type Real - floating point numbers cannot be mapped to hardware and therefore are not supported.

● User defined resolution functions - prior to IEEE Standard 1164, designers made up their own multi-valued logic
systems and resolution functions to support technology related aspects of simulation. None of these custom
solutions is standard, and therefore none are supported by synthesis.

Ignored Constructs

The following constructs will not generally flag an error message, however they will be ignored by most synthesis tools.

● Assert and report statements - these are included in a design to send messages to the 'console' window
relaying information about what is happening during a simulation. As such, they have nothing to do with the
hardware of the design.

● After clause - this is used to specify inertial and transport hardware delays in a design, or alternatively in a test-
bench to produce clocks and other control waveforms. Synthesis tools have no way of creating a specific delay
time, unless it is created by means of counting clock pulses in hardware. Delays may be included in the pre-
synthesis RTL design, however they will be ignored during the synthesis process.

8.4 Language Guidelines for Synthesis

One of the main hurdles presented to a new user of VHDL is distinguishing between the simulate-able and synthesise-
able parts of the language. VHDL is a rich and powerful language, providing a broad range of constructs, all of which
can be 'executed' or simulated.
When it comes to producing hardware however, many of the language features that are so useful in creating for
example a test-bench, cannot be mapped to hardware.

Section 8.3 provided a representative summary of the VHDL language constructs in terms of what can and can't be
synthesised, the lists presented in section 8.3 summarise what is commonly covered and excluded by most modern
tools.

The following guidelines should also be observed when creating VHDL descriptions for synthesis:

● Always Simulate before Synthesising - Any logical and functional errors contained in the RTL design will be
faithfully implemented by the synthesis process. It is highly recommended that the design be comprehensively
simulated prior to synthesis, in order to eliminate such errors. A test-bench written in VHDL is the preferred
method to provide the stimulus (and perhaps analyse the responses) during simulation.

● Always Simulate after Place-and-Route - This step is especially important when targeting cell-based ASIC
technologies (essentially full-custom fabrication) due to the high re-working costs involved. Most physical design
tools will produce a timing-accurate VHDL net-list output that will allow accurate and worst-case timing
simulation to be carried out prior to committing the design to Silicon.

● Avoid asynchronous logic - Synthesis tools are optimised to produce synchronous sequential logic circuits
that make use of a single clock. Most synthesis tools will optimise out the extra logic necessary to make
asynchronous state machines work reliably. Also, asynchronous logic circuits may rely on technology-
dependent propagation delays for correct operation, and therefore present a problem to the algorithms used in
synthesis. If an asynchronous state machine cannot be avoided then it must be coded in VHDL as a net-list of
technology primitives and the synthesis tool must be constrained to disable optimisation for this section of the
code.

● Processes - All processes must have either a sensitivity list containing all inputs or one wait statement (usually
the first statement within the process body). Processes that describe synchronous sequential logic must be
triggered by a single clock (using the transition supported by the target technology). Initialisation of sequential
logic may be asynchronous or synchronous, depending on the target hardware and the requirements of the
design. For asynchronous operations, the set/reset inputs must be included in a sensitivity list at the beginning
of the process, along with the synchronous clock.

● Avoid combinatorial loops - For combinatorial processes it is essential to make sure that all outputs are
assigned values for every possible combination of inputs, otherwise the synthesis process may produce
unwanted latching logic. The easiest way to achieve the above is to set all outputs of a process to default values
at the top of the process, subsequently overriding these values if necessary.

● Assignments and Variables - The signal assignment operator is <= and the variable assignment operator is
:=. Variables can be used in sub-programs (functions and procedures) and processes. When used in processes,
a variable may be mapped to a storage element if it used to hold the contents of a register or the state of a
FSM.
Alternatively, a variable may be used to hold an intermediate value within a combinatorial process - in this case
the variable will either be optimised out or mapped to a hardware signal.

The use of the 'don't care' and 'don't know' ('-' and 'X') in assignments is interpreted differently when performing
simulation and synthesis. For simulation, the appearance of an 'X' indicates that the signal is an unknown output, and
usually means that there is a problem within the design (commonly due to not initialising properly). The 'don't care'
literal (-) is used to indicate that for a certain set of input conditions the value of an output can be either 0 or 1.

During simulation, don't care values appear as expected, reproduced faithfully in the simulation results.

The situation for synthesis is different to simulation - both 'don't care' and 'don't know' values ('-' and 'X') are handled in
the same manner. Synthesis tools interpret the two meta-logical states as meaning 'don't care', and will therefore
attempt to exploit these states to minimise logic wherever possible.

● Data types - Unconstrained Integer objects are 32-bit quantities by default. A synthesis tool will insert a 32-bit
register or bus wherever it encounters an unconstrained Integer. For this reason, it is usually necessary to
constrain the range of an Integer (or Natural/Positive) object when it is declared so that the minimum number of
bits is used in its hardware representation.

● Enumeration types - these are commonly used to represent the states of an FSM or the operation codes of a
processor. Synthesis tools map enumeration types to a set of Std_logic_vectors having sufficient bits to
represent the set of enumerated values. If the number of enumeration values is not an exact power of 2, there
will be more possible states in the synthesised circuit than defined by the enumeration type in the RTL code.
These extra states are effectively unused states and the possibility always exists that the synthesised circuit
may get 'locked into' one of these unused states. This problem is best avoided by having an overriding reset
mechanism, such as an asynchronous reset.

8.5 Combinatorial Examples

The process statement is the most general construct in the VHDL language for describing hardware. The VHDL source
listing given below shows the general template for describing combinatorial logic using a process.

As shown, the process usually has a sensitivity list containing all of the inputs to the block of logic. Whilst it is perfectly
legal VHDL to omit certain signals from this list (even though the missing inputs are read by the process), most
synthesis tools will issue a warning if missing inputs are detected. Another compelling reason to include all inputs is that
there will be a mismatch between pre- and post-synthesis simulation results if inputs are missing from the sensitivity list.

Template for a combinatorial process

The two blocks of text shown shaded above describe what must be done to avoid inferring latches in a combinatorial
logic process, either strategy may be used and a thorough description of this aspect of combinatorial processes is given
in the next section.

In addition to using a process, a designer may use concurrent signal assignment statements to describe combinatorial
logic.

The following VHDL listing shows how a conditional signal assignment statement can be used to describe logic, the
design-entity 'mux(archmux)' uses a 3-bit select input 's' to choose between 4 inputs 'a, b, c and d'.
The conditional signal assignment statement assigns decreasing priority to the conditions corresponding to the order
they appear in the statement.
In the present example, the conditions s = "000", s = "101" and s = "110" choose between the inputs 'a', 'b' and 'c', with
input 'a' having the highest priority.
The output 'x' is assigned the value of the input 'd' if the select input 's' is any of the possible codes not mentioned
above.

Design-entity 'mux(archmux)'

The logic diagram shown below is the synthesised circuit for the above design-entity. The three AND gates are included
to detect the three conditions specified in the conditional signal assignment statement, the output of each AND gate
enables one of the data inputs (d) to pass through the data selector by asserting the corresponding enable (e) input.
The OR gate detects when none of these conditions is true and assigns 'd' to the output 'x'.

Synthesised circuit for 'mux(archmux)'

An alternative to the conditional signal assignment is the selected signal assignment statement. This concurrent
statement acts rather like a case..when sequential statement in that none of the particular selection choices has a
higher priority than the others.

The following VHDL listing illustrates the use of the selected signal assignment to describe a data selector circuit.
The design-entity 'mux(with_select_when)' selects between four data inputs, again using a 3-bit select input 'sel'. In this
case, none of the particular inputs has a higher priority than the others and the resulting synthesised circuit requires
less selection logic due to the presence of the ''X' when others' clause.

The ''X' when others' clause is interpreted as a don't-care condition, it is exploited during the optimisation phase to
minimise the number of gates required by the synthesised circuit (shown below the listing).

The OR gate, produced by the design-entity 'mux(archmux)' previously, is not needed here due to the absence of
prioritised conditions.

Design-entity 'mux(with_select_when)'

Synthesised circuit for 'mux(with_select_when)'


A further example of the use of the don't-care condition is illustrated by the design-entity 'good_synthesis(useful)'
shown below.

This example differs from the previous two in that it uses a process statement, rather than concurrent assignments, to
describe the logic.

The process is sensitive to the 2-bit input 's', generating a logic-1 on the output 'y' when s is equal to "00" or "11". The
output is set to logic-0 when s = "01", it can be assumed the remaining possible value for s ("10") does not occur in the
circuit, since it is treated as a don't-care condition.

The resulting synthesised circuit for design-entity 'good_synthesis(useful)' is shown directly below the VHDL listing. As
shown, the circuit is very simple, having been reduced to the minimum complexity necessary to implement the
functionality implied by the code.

Design-entity 'good_synthesis(useful)'

Synthesised circuit for 'good_synthesis(useful)'

Variables
The following VHDL listing makes use of a local variable within a process to store an intermediate result.

The variable 'v' is assigned either 'a' or 'b' depending on the state of input 'c1'. The first if…then…else statement
results in the left-hand data selector shown in the synthesised circuit below the listing.

The second if…then statement conditionally inverts 'v' if inputs 'c2' and 'c3' are both '1', so the output 'f' ends up being
driven by 'v' or 'not v' via the second data selector, shown on the right of the synthesised circuit.

Process with if..else and variable

Like signals, variables must always be assigned a value for all possible combinations of input signal, if sequential
latches are to be avoided.

Synthesised circuit for process with if..else and variable

As shown in the above example, the if…then…else or if…then statement results in a 2-input data selector module (or
multiplexer) being included in the synthesised circuit.

A multi-way decision 'if…..then…..elsif….else' statement tests each condition in strict order of priority, starting with the
first. A true condition causes execution of the corresponding statement(s) following the then keyword and control then
passes to the statement immediately following the if statement.

In hardware terms this is equivalent to a set of cascaded data selectors as illustrated by the VHDL listing and
synthesised circuit shown below.
Process with if..elsif..else

The first condition tested above (c0 = '1') corresponds to the right-hand data selector shown below, since this one has
the highest priority. The final else part of the above statement maps to the upper input of the left-hand data selector
shown below, this feeds the 'd' input to the output 'f' only if all control inputs 'c0', 'c1' and 'c2' are at logic-0.

Synthesised circuit for process with if..elsif..else

Using a process to deliberately infer a latch

A process may be used to deliberately infer a data latch if that is what is required by the design. Signal objects in VHDL
have implicit memory, this means that whenever a signal is assigned a new value, it retains the value until the next time
the signal is updated by a subsequent assignment.

The VHDL source description below shows how a data latch may be described using a process. On first inspection, it
appears that the process given below is combinatorial, it has a sensitivity list containing all inputs and does not contain
any wait statements.

However on closer inspection it is apparent that the if…then statement is incomplete, i.e. there is no else part. The
absence of the else part means that if the 'enable' input goes from logic-1 to logic-0, the process is triggered but the
condition 'enable = '1'' is false, and therefore the output 'q' is not updated.

The fact that the 'q' output is a signal object means that it will retain the value previously assigned to it, in effect latching
the value.
Process describing an enabled latch

The VHDL process shown above synthesises to the simple latch circuit shown below, the feedback connection (shown
in blue) is the hardware configuration needed to maintain the value of the 'q' output when the 'enable' input goes to logic-
0. When the 'enable' input is at logic-1, the latch is said to be transparent, i.e. changes in 'data' are fed straight through
onto the output 'q'.

An enabled latch

The multi-way decision implemented in the form of an if…then…elsif…else statement may be more concisely
represented by a case…when statement when there is no particular priority ordering of the input conditions. The case
statement forces the execution of a statement (or statements) when the value of the expression between case and is
matches one of the choices following a when.

The set of choices elaborated after the when keywords can contain multiple alternatives (separated by the '|' character)
and ranges, however the choices specified in each when limb must not overlap with each other.

Furthermore, if the set of explicitly listed choices does not fully cover all possible values of the select expression, a final
when others clause is required.

A case statement

The case statement shown above must be enclosed within a process (not shown) since it is a sequential statement.
The final when others clause is necessary here even though a 2-bit binary signal can take on only the 4 possible
combinations shown. This is due to the additional combinations possible when using the 9-valued Std_Logic type ('sel'
has 92 = 81 possible values!).

Synthesis result for a case statement

The case statement synthesises to a straightforward parallel data selector, as shown above.

Loops

Like all high-level languages, VHDL provides a number of looping constructs to allow repetition and iteration. In the
context of logic synthesis, loops are acceptable so long as they do not include any statements that result in premature
termination of the loop, such as next or exit.

The following VHDL source listing shows a process containing a for loop being used to describe both iterative and
repetitive logic. The loop statement causes the loop body to execute 4 times, during which the loop index variable 'I'
goes from 0 to 3.

Process containing a for…loop

The first statement in the loop (f(I) <= a(I) and b(3 - I);) creates repetitive logic in the form of four 2-input AND gates, as
shown in the image below. The synthesis process effectively unwinds the loop, producing a circuit for each pass
through the loop body.
Loop-generated repetitive logic

The second statement in the loop body (v := v xor a(I);) creates an iterative logic circuit comprising three 2-input XOR
gates. This is due to the appearance of the variable 'v' on both sides of the assignment operator.

The synthesis result for the second loop body statement is shown below.

Loop-generated iterative logic

Three-state logic

Most digital systems make use of three-state logic in which the basic logical values '0' and '1' and supplemented by the
so-called high-impedance state 'Z'.
Synthesis tools support the use of a high-impedance state by including support for the IEEE standard package
'Std_Logic_1164' and associated types.

The VHDL language does not provide a three-state buffer primitive like some other HDLs, instead these are implied by
the use of conditional signal assignment statements that cause an output to be assigned the value 'Z' under certain
conditions.

The following VHDL description is a design-entity for a single-bit three-state buffer device - the corresponding
synthesised circuit is included directly below the source.
This particular buffer is unidirectional, it drives an output port in one direction with the value of the input port, or a high-
impedance, depending on the state of the 'enable' input.
Unidirectional three-state buffer

Synthesised three-state buffer

The availability of three-state logic provides the underlying mechanism for bus sharing in computer systems. In such
systems a single set of bus wires is used to carry information in either direction at different times, this is only possible if
all but one of the so-called 'bus drivers' are outputting a high-impedance value onto the bus.

The following design-entity is a single-bit bi-directional three-state buffer with XOR gated input. This design makes us of
the port mode inout to allow data to flow in either direction through a single port (bidir_port).

Bi-directional three-state buffer

As shown in the above listing, the signal bidir_port appears on both sides of the signal assignment operator (<=) since
it is capable of acting as an input or output.

It is the responsibility of the designer to ensure that any signal connected to the inout port 'bidir_port' is driven
appropriately, given that a signal conflict would occur if both the design-entity 'bidir(tri-state)' and an external device
were both trying to drive the port to opposing states.

The equivalent circuit for the above design-entity is shown below, along with a truth table summarising its operation.

Synthesised bi-directional three-state buffer

8.6 Inferring Latches

A simple way of describing combinatorial logic in VHDL is to use a concurrent signal assignment. This approach has
the advantage of not producing unwanted latches in the vast majority of cases (one possible exception being the use of
the conditional signal assignment without a final else clause).

The use of concurrent assignments can be limiting however, due to the fact that the logic being described must map to
an equivalent expression involving the VHDL operators.
An ultimately more flexible approach is to use a process to describe combinatorial logic.

The following guidelines must be observed when using a process to describe combinatorial logic. Since VHDL signal
objects have inherent memory, there is always the possibility that an unwanted feedback path may be implied, due to
not assigning a value to a signal under certain conditions.

Here are the guidelines:

● Include all of the inputs to the combinatorial function in the sensitivity list separated by commas.

● To avoid the creation of unwanted latches, ensure either of the following is applicable :

1. Assign a default value to all outputs at the top of the process, prior to any sequential statement such
as if, case etc.
2. In the absence of default assignments, ensure that all possible combinations of input conditions result
in a value being assigned to the outputs.
The reason for observing the above guidelines stems from the behaviour of the VHDL signal object.
When not being updated by an assignment, a signal retains its current value.
A synthesis tool will insert a feedback loop to latch the last value of the circuit output for any input
conditions not resulting in an assignment to the output(s).

The following example illustrates how a latch is implied by an incomplete if statement.

Example showing latch inference


Synopsis FPGA Express Schematic showing output latch (highlighted yellow)

The designer of the design-entity 'Latch_Implied(Bad)' shown above, has used a process to describe the behaviour of
a selector circuit. The 2-bit input 'sel(1 downto 0)' , selects one of three inputs 'a', 'b' or 'c' and feeds it through to the
output 'y'.

The assumption has been made that 'y' will be driven to logic-0 if 'sel' is equal to "11". This is of course incorrect, the
omission of a final else clause results in 'y' retaining its current value, hence the presence of the output latch in the
synthesised logic diagram shown below the VHDL source code.

The corrected version of the data selector source description is given below, along with the corresponding synthesis
result.

Removal of unwanted feedback using final else clause


Synopsis FPGA Express schematic without output latch

An alternative solution to the problem outlined above, would have been to include a default assignment to the circuit
output 'y', at the beginning of the process, as shown below. This solution to the problem produces the same circuit as
shown above.

8.7 Sequential Examples

Synchronous sequential logic makes use of a global clock to control activity within the system. In terms of a VHDL
process, the clock signal is used to synchronise execution of the statements within the process body and any signals
that are assigned to within the process are mapped to the outputs of sequential memory elements (flip-flops).

The VHDL language allows the execution of processes to be controlled in a number of alternative ways. We have
already seen how processes describing combinatorial logic generally use a sensitivity list to trigger execution. For
sequential logic, either a sensitivity list or a wait statement can be used, but the designer must observe certain
conventions if errors are to be avoided when performing logic synthesis.

Simulation allows the use of multiple wait statements, a process can be suspended whenever necessary using a variety
of mechanisms (time delay, signal event, clock edge). For synthesis, the use of the wait statement is far more restricted
in that there must be only one wait statement used to suspend the execution of a process, and this wait statement
usually detects changes in the system clock signal.

Alternatively, a sequential logic process may make use of a sensitivity list containing the clock and perhaps
asynchronous preset and clear signals. The VHDL source shown below illustrates how a positive-edge triggered D-type
flip-flop could be described using a process.

Process describing a D-type flip-flop

The above process is triggered whenever an event occurs on the clock signal, however the if statement ensures that
the 'q' output is only updated by the 'data' input when the clock has undergone a 0 to 1 transition. The absence of an
else part means that the 'q' output signal effectively stores the value read in from 'data' between active clock edges.

The image below shows a D-type flip-flop resulting from synthesising the process given above. The SET and CLR
inputs, although shown on the symbol, are not used by the flip-flop since the VHDL process does not refer to them. The
only way to initialise the flip-flop is through the data input.

A D-type flip-flop

Sequential logic circuits may initialised in one of two ways - synchronously or asynchronously. Depending on the target
technology (PLD, CPLD, FPGA etc.) either one or both of these methods may be employed.

The following VHDL process shows the recommended format for synchronous resetting, the single wait statement
ensures that all assignments to the output 'q' take place on the rising-edge of the clock input. The 'reset' input is tested
first by the if statement, if found to be logic-1, 'q' is cleared to logic-0 on the rising-edge of the clock, otherwise the 'data'
input updates 'q'.
Process describing a D-type flip-flop with synchronous reset

The circuit corresponding to the above process is shown below. The AND gate implements the reset logic by placing it
in the data path, again the SET and CLR input of the flip-flop are not used.

A D-type flip-flop with synchronous reset

If an asynchronous reset mechanism is needed, then the process must have a sensitivity list that includes both the
reset and clock inputs. This ensures that the process body is executed whenever either of these signals changes state.

The following process and corresponding logic diagram illustrate the use of an asynchronous reset. The reset input
takes priority over the clock in this case, therefore the if statement tests the state of 'reset' first, setting 'count' to all
zeros if 'reset' is asserted.

If 'reset' is not asserted, the elsif part of the statement is entered. This tests for a rising edge on the clock input and, if
this is what triggered the process, the register is either loaded or incremented depending on the value of 'load'.
Sequential logic with asynchronous reset

Process Templates

This section presents a set of standard process templates, each one being used to represent a certain
logic configuration. The templates should be acceptable to the majority of logic synthesis tools.

The following two processes represent alternative ways of describing clocked logic that does not include
any asynchronous reset feature. They are semantically identical and will produce identical synthesis
results.

The following process is used to produce latched logic - all inputs to the logic must be included in the
sensitivity list.
The following process is used to implement clocked logic with an asynchronous reset feature. This
template can be modified to allow either resetting or pre-setting asynchronously, the sensitivity list must
include the clock along with all asynchronous inputs
.

The following process uses a single wait statement to imply synchronous clocking and initialisation.

8.8 Finite State Machines

Many digital systems include one or more Finite State Machines that can be either synchronous or asynchronous in
operation.
Asynchronous (sometimes referred to as event driven) FSMs tend to contain both redundant logic and states in order to
avoid logic races and hazards, these extra components must be retained in the final net-list in order to ensure correct
operation.
For the above reason, it is best to describe asynchronous state machines in the form of a low-level net-list of gates and
flip-flops instantiated directly from the technology library. The synthesis tool can then be directed to ignore this circuit
during synthesis, effectively reproducing it exactly as described by the net-list.

The majority of FSMs are synchronous sequential logic circuits comprising a so-called 'state' register, holding the
current state of the machine, and combinatorial logic circuits that the determine the value of any outputs, in addition to
the next state. Changes from one state to another are synchronised to the rising edges of the clock input.

Modern synthesis tools are highly capable of creating optimised logic circuits for synchronous finite state machines. It is
the VHDL designer's responsibility to create a behavioural description of the FSM that is consistent with the
recommended format supported by the synthesis tool. This section describes the various styles and formats used to
describe FSMs that are compatible with most synthesis tools.

To begin with, it is necessary to understand the meaning of the symbols used in the so-called Algorithmic State
Machine (ASM) diagram and how these relate to the actual FSM hardware and VHDL description.
The ASM diagram is a graphical representation of the behaviour of a finite state machine, providing an unambiguous
description of the machine using a standard set of symbols.

The following image shows a typical ASM diagram for a machine having four states, these being represented by the
blue rectangles. Each state has a unique name, normally shown encircled and in close proximity to the state box (S1,
S2, S3 and S4).
A state box may optionally contain the name of an output or outputs. If an output is listed inside a state box then it is
active during that particular state (outputs can be active-high or active-low), the absence of an output name in a state
box indicates that the output is inactive during that state.

The arrows leading from one state to another represent the state transitions, these occur coincident with the rising edge
of the master clock input.
The green (diamond shaped) symbols represent decision boxes, these contain a logical expression which usually
involves one or more of the inputs to the FSM. The logical value of the expression contained in a decision box dictates
the movement from one state to another - decision boxes may be cascaded to form more complex conditions.
The purple oblongs, shown in the above image, represent conditional output boxes. These always follow a green
decision box.

An output contained in a purple oblong is active when the machine is in the current state (at the start of the transition
arrow) and the input condition(s) in the preceding green diamond(s) is true.

It should be noted that outputs appearing in conditional output boxes may change asynchronously, if the inputs being
tested by the preceding decision boxes change asynchronously.

On the other hand, outputs appearing inside state boxes are always synchronous, due to the fact that the state
changes occur on the rising edges of the clock input.

The last point is used to distinguish between the two basic types of FSM, namely Mealy and Moore.

Mealy FSM - the outputs may be dependent on the current state and the current input values.

Moore FSM - the outputs are dependent on the current state only.

One of the advantages of the Moore type of FSM is the predictable nature of the output signals, being dependent purely
on the state means that Moore machine outputs are always synchronised to the system clock.

Defining the state type

The VHDL language allows the use of user-defined enumeration types - these are ideal for representing the state
values in a FSM. The following snippet of VHDL code shows how a user-defined enumeration type is defined and then
used as the type for the state signals 'present_state' and 'next_state'.

Either of the above signals can be assigned any of the possible values of the type 'state_type' in the usual manner:

present_state <= s2;

One of the benefits of using a user-defined enumeration type for the state is seen during simulation. The waveform
showing the state activity displays the enumeration literals as opposed to binary codes, thereby making the waveforms
easier to understand and follow.

During synthesis, the enumeration literals 's0', 's1', 's2' and 's3' are mapped to binary codes using one of a number of
alternative formats such as:

● Binary - straightforward ascending binary numbers.


● One-hot - the number of state bits is equal to the number of states and each state sets one bit high and the
others low.
● Gray - a unit-distance code that often results in more compact feedback logic.

It may be necessary to assign a unique set of codes to the states of a FSM. The following VHDL source extract shows
how this can be achieved using a set of constant declarations. This approach to explicit state assignment is often used
when the state bits themselves are used as outputs.
Classic 2-process FSM

The block diagram shown below represents the general architecture of a finite state machine. Both the sequential (state
register) and combinatorial (Output and N.S. Logic) blocks can be described using separate processes in VHDL.

Whereas the 'Present_State' output of the State Register is synchronised to the clock edges, the 'outputs' and
'Next_State' outputs of the logic block depend on the timing of the 'inputs', hence this machine is of the Mealy variety.

The following VHDL source describes a typical Mealy-type state-machine. As usual, the entity declaration defines the
interface to the system, only the top-level inputs and outputs are accessible from outside the entity 'stmch1', the state of
the machine is an internal signal.

The state is defined in terms of an enumeration type having three values. This means that the synthesised version of
the FSM will use a 2-bit state register and therefore there will be four possible states in the actual hardware, a question
therefore arises - what happens if the machine gets into the unused state?

The answer to the above question depends on how the synthesis tool is set up. There are essentially two options:

1. Fastest and smallest - in this case the synthesis tools uses any unused states during optimisation as don't care
states, this means that the behaviour of the FSM is unknown for any unused state codes and therefore there is a risk of
lock-out (the FSM gets trapped in an unused state). It is important to make sure the FSM has a robust initialisation
mechanism, such as an asynchronous reset (as used in the current example).

2. Safest - the synthesis tool will generate logic to ensure that the state returns to the first state (or reset state) when it
enters an unused state.
In the above example, there is no 'when others =>' limb in the case statement since there are only three states
defined. Most synthesis tools will accept a when others limb in such a case so that the designer can explicitly specify
what should happen when the FSM enters an unused state:

when others => next_state <= s0;


The second process contained within the above FSM architecture describes the logic that defines the next state and the
outputs (shown as a green block in the following image). In order to ensure this is synthesised to purely combinatorial
logic both the next state and outputs are assigned defaults at the top of the process:

out1 <= '0';


next_state <= s0;

The two processes contained within the above VHDL description interact with each other via the signals 'state' and
'next_state'. The sequential logic process transfers the value of the 'next_state' onto the 'state' at the next rising edge of
the system clock, unless it is being asynchronously reset by 'rst'.

Events on 'state' and /or the input 'in1' trigger the second process which uses the case and if..else.. statements to
update the output and next state values immediately. Changes in the value of 'next_state' have no effect on 'state' until
the next clock rising edge.

Moore State Machine

The block diagram shown below depicts the architecture of a Moore-type finite state machine. As shown, the 'Next
State' and 'Output' combinatorial logic functions have been split into two separate blocks, this means that the outputs of
the machine depend only on the 'present state' (the outputs of the state register).
In terms of the ASM diagram, the above structure means that there are no conditional output blocks, only state outputs
are allowed, such as the output 'Y' shown below.

The absence of conditional output blocks means that the possibility of asynchronous output behaviour is eliminated in
the Moore machine since the outputs can only change in response to the state changes (which are caused by the
active clock edge).

The outputs of a Moore machine depend indirectly on the inputs, due to the fact that the next-state, and therefore the
present-state, depend on the inputs.

The following VHDL source listing shows the first part of a block statement containing the declarations and process
statement necessary to describe the state register and next-state logic of a Moore FSM.

The block statement provides a convenient way of localising declarations and concurrent statements to within the
bounds of a region defined by the block….begin….end block; delimiters. The type 'peltype' and signal 'pelstate' are
local to the block 'controller'.
The above source code describes the parts of the Moore FSM shown coloured below, the output logic can be described
separately using either a process or concurrent assignments within the same block.

The remaining portion of the Moore FSM block is shown in the listing below.

These selected signal assignments are concurrent statements that execute in response to events on the signal
'pelstate' (the state of the FSM).

Each with….select statement generates one output of the FSM, all of the outputs are Moore-type outputs since they
depend on the state (pelstate) only.

The selected signal assignment statement is similar to the case statement but it is not required to be located within a
process.

The above statements describe the logic contained in the 'output logic' block shown coloured below.

Moore FSM with outputs driven directly by state bits

A variation on the Moore FSM is illustrated by the block diagram shown below. Careful allocation of the state values
can remove the need for the output logic block - instead, the outputs are taken directly from the state bits. This strategy
leads to a potentially faster implementation due to the removal of output logic block delays.
The main drawback of this arrangement is that it imposes severe constraints on the state assignments, these must
match the requirements of the outputs.
The following VHDL source listing shows an example of the use of direct state outputs. This particular machine uses
only seven states (idle, decision, read1, read2, read3, read4 and write) but the state register needs five bits to
accommodate the number of outputs. The binary patterns for each state are fixed by a set of constants declared in the
architecture, this is essential in this type of FSM.
Mealy FSM with registered outputs

A variation on the Mealy machine is shown below. The asynchronous nature of the Mealy outputs can be eliminated by
clocking them through a register as shown.
The overhead is a one-clock-cycle delay between the change in the state and the updating of the outputs.

The VHDL listing given below shows how the registered outputs are implied by placing both the next-state and output
logic within the same clocked process.
The assignments to the output 'outt' (shown shaded) are subject to the rising edges of the clock, this means that the
outputs must be driven by storage elements.
Advanced Electronic Design Automation

Chapter 9 – From VHDL to Verilog-HDL

Chapter Information

This chapter contains the following sections:

9.4 Styles of Description – Data-flow and Behavioural


9.1 Introduction to Verilog-HDL
9.5 Describing Synthesisable Logic and Verilog-HDL Test-
9.2 Hardware Modelling with Verilog-HDL – the benches
Module
9.6 Hands on Verilog-HDL Simulation Exercise using
9.3 Elements of Verilog-HDL – Built-in Primitives,
Types, Operators and Expressions

9.1 Introduction to Verilog-HDL

Now that you are familiar with the VHDL language, it is time to take a look at the other widely used HDL, namely Verilog-HDL.
Despite its commercial origins, Verilog-HDL, in common with VHDL, is now defined by an IEEE Standard (IEEE Standard 1364-
1995), consequently there are many excellent tools available to support its use.

There exists a significant and well-established Verilog-HDL user base in both industry and academia, more than half of the
industrial design work in the United States is done using Verilog-HDL, and there are a multitude of existing designs written in
Verilog-HDL suitable for both simulation and synthesis. As a result, designers now need to be familiar with both languages in
order to fulfil their roles.

What is the difference between Verilog-HDL and VHDL?


On the whole the two languages are used for the same purpose – to describe, verify and synthesise digital hardware from a
technology-independent behavioural language. However, they do have some important differences.

The VHDL language was designed to support system-level design and specification activities, whereas Verilog-HDL is
primarily aimed at digital hardware designers developing FPGAs and ASICs.
The result is that VHDL provides some high-level constructs which are not available in Verilog-HDL (such as Configurations
and user defined types), while Verilog-HDL provides comprehensive support for low-level digital design (such as MOS
switches and Gates),a feature which native VHDL does not possess.

The good news is that both languages support so-called Register Transfer Level (RTL) design, this being the style used by the
majority of FPGA and ASIC designers, the primary goal of whom is to create digital hardware using logic synthesis.

Despite the similarity in the scope of application of the two languages, they look and feel quite different. One of the problems
with VHDL is that it is long-winded. The syntax is derived from the programming language Ada and the lack of abbreviations
and unforgiving type checking can make a chore out of using the language, even when trying to describe simple hardware.
Verilog-HDL, on the other hand, is reminiscent of the C language in terms of its basic syntax. Therefore most engineers will
already be familiar with many of the Verilog-HDL operators and statements. For a wide variety of designs, Verilog-HDL
descriptions are more straightforward and concise than VHDL.

Native VHDL does not support the technology aspect of digital hardware, which could be considered both an advantage and a
disadvantage. On the one hand it means that VHDL may be ultimately more flexible, due to not being tied in to any specific
hardware technology. The flip-side of the argument becomes apparent when one realises that VHDL requires a range of
additional type definitions and supporting functions (in the form of Packages) to enable its use in digital hardware design. The
multitude of additional types and functions, and their corresponding incompatibility, can be a significant hurdle to the VHDL
novice.
The above problem is compounded by the lack of standardisation across CAD vendors when dealing with arithmetic
operations, although the situation has improved somewhat with the introduction of the IEEE standard packages Numeric_Bit
and Numeric_Std.

The fundamental technology aspects of digital hardware design are built into Verilog-HDL, as are the basic set of unsigned
arithmetic operators. This precludes the need for additional packages or libraries and simplifies descriptions considerably.
The following table lists a selection of the main features of the two languages and highlights some of the differences between
them.

Feature Verilog-HDL VHDL


Built-in support for Gates and MOS switches Yes No
Support for Data-flow style description Yes Yes
Support for Behavioural descriptions Yes Yes
Support for Register Transfer Level Yes Yes
Built-in support for Unsigned Arithmetic* Yes No
General Appearance/Syntax C-like Ada-like
Built-in support for Signed Arithmetic* No No
Built-in support for Hardware Technology (high-impedance, strength etc.) Yes No
Support for User Defined and Composite types No** Yes
Support for Test Bench Modelling Yes Yes
Built-in support for Detailed Timing Modelling Yes No

*Verilog-HDL supports only unsigned arithmetic on binary vectors, VHDL requires additional packages ‘numeric_bit’ or
‘numeric_std’ in order to support arithmetic operations.
**Verilog-HDL supports a one-dimensional array type for memory modelling.

Verilog-HDL and the Internet


The following list represents a selection of useful internet sites containing information (or links to information) on Verilog-HDL.

1. Doulos - A Hardware Designer's Guide to Verilog.


2. Aldec - Free downloadable interactive tutorial on Verilog.
3. Simucad – A useful demonstration version of a Verilog simulator 'SILOS III', the package includes some Verilog
language reference material.
4. Library of Parameterised Modules - Standard library of scalable hardware blocks supported by many design tools.
5. Synapticad - Free limited version of their Verilog simulator ‘Verilogger’.
6. PLD Jumpstation - Jump off point for FPGA and CPLD design tools and language support tools.
7. Bucknell University - Tutorial material and links on Verilog.
8. Open Verilog International - The non-profit making organisation charged with maintaining, promoting and encouraging
the use of Verilog in the design community.
9. Qualis - A useful Verilog-HDL Quick Reference Card (in addition to an equally useful VHDL Quick Reference Card!)

Coverage of Verilog-HDL topics

This chapter provides a brief introduction to the Verilog language, attempting to focus on those aspects which are relevant to
the creation of hardware using logic synthesis. Consequently, several topics are not covered at all and the interested reader is
referred to the recommended textbooks for further information.

The following list outlines those areas not covered by this chapter and provides a reference to further information.

● User Defined Primitives (UDP) – Chapter 12 of Palnitkar and chapter 5 of Ciletti


● Programming Language Interface (PLI) – Appendix B and Chapter 13 of Palnitkar and Appendix G of Ciletti
● Switch-level primitives (including strength modelling and wired logic) – Appendix A and Chapter 11 of both Palnitkar
and Ciletti
● Tasks and Functions - Chapter 8 of Palnitkar and section 7.13 of Ciletti
● Detailed timing models (including Specify blocks) - Chapter 10 of Palnitkar and chapter 6 of Ciletti
● Certain aspects of behavioural modelling are not covered. These are as follows:

❍ Procedural Continuous Assignment – section 7.4 Ciletti


❍ Parallel blocks (fork..join) – section 7.7.1 of Palnitkar and section 7.12.6 of Ciletti
❍ Named event control - section 7.3.2 of Palnitkar and section 7.5.4 of Ciletti
❍ Intra-assignment delay - section 7.3.1 of Palnitkar and section 7.6 of Ciletti

Read Chapter 1 of Ciletti and/or Palnitkar

9.2 Hardware Modelling with Verilog-HDL – the Module

In Verilog-HDL the basic unit of hardware is known as a module. In common with C’s functions, modules are free-standing and
cannot therefore contain definitions of other modules. A module can be instantiated within another module however, in a
similar manner to how a C-function can be called from another C-function, which provides the basic mechanism for the
creation of hierarchy in a Verilog description.

Figure 9.2.1 shows the basic layout of a module, the description is enclosed by the keywords module and endmodule, the
former being immediately followed by the name of the module and an optional list of port names. (In common with VHDL test-
bench design-entities, a test-bench module may have no ports).
Note that the semicolon at the end of the module header is always required but no semicolon is required after the keyword
endmodule.

The list-of-ports enclosed in the parentheses does not specify the size (number of bits) or direction of the ports, only the name
is given. These details are contained within the body of the module immediately below the module header, in this way the top
line of a module definition acts as a sort of module prototype.

module module-name (list-of-ports) ;

input/output declarations
local net declarations

parallel statements

endmodule

Figure 9.2.1 - Basic layout of a module

Following on from the input/output declarations, a set of local nets may be declared. These represent internal connections
within the module linking together primitive gates, module instantiations and continuous assignments, all three being
collectively known as parallel statements.

The parallel statements within a module describe the behaviour, structure and/or data-flow of the module. In this respect a
Verilog module contains the same information as the VHDL Entity-Architecture pair, but uses just one ‘design unit’ instead of
two.

Unlike VHDL, Verilog does not support the idea of multiple architectural descriptions (functionality) for the same entity
(interface). Each module is a unique self-contained design description.

An example of a very simple module is shown in figure 9.2.2.The line numbers are included for reference purposes only. They
must not appear in the source file.

As shown in the figure, the module describes a 2-input Exclusive-OR gate named ‘myxor’ having single-bit inputs ‘a’ and ‘b’
and an output ‘y’. The direction of the module ports are specified on lines 2 and 3. In addition to input and output, a port may
be bi-directional, in which case it is identified as an inout port.

1 module myxor (y, a, b);

2 input a, b;
3 output y;

4 assign y = a ^ b;

5 endmodule

Figure 9.2.2 - A Simple Module

The functionality of the module is defined by the so-called continuous assignment statement on line 4, assigning the output ‘y’
the expression ‘a ^ b’ (the ‘^’ means bit-wise exclusive-OR in Verilog). The keyword assign is used to indicate a continuous
assignment. Such a statement creates a static binding between the expressions on the left and right hand sides of the '='
operator, it is most commonly used to describe combinatorial logic.

Despite the similarity with an assignment used in the C language, the continuous assignment on line 4 in figure 9.2.2 is a
parallel statement, which means that it is constantly active and awaiting events on either of the input signals ‘a’ and ‘b’ to
trigger its execution. Whenever an event occurs on ‘a’ or ‘b’, the expression on the RHS of the assignment is evaluated and the
result is assigned to the LHS at the start of the next simulation cycle.

A module may contain any number of continuous assignment statements, all of which act in parallel, and therefore may be
written in any order.

Figure 9.2.3 shows an example of a module containing multiple continuous assignments. Such a description is sometimes
referred to as a Dataflow style description. The Verilog source describes the logic diagram shown on the right hand side of the
figure. In this example each gate is modelled by a separate continuous assignment, an alternative would have been to
describe the logic using a single statement such as:

assign F = ~((A & B) | (C & D));

The statements on lines 5, 6 and 7 could have been written in any order without changing the behaviour of the logic. Internal
wires (declared on line 4) are used to connect the outputs of the two AND gates to the 2-input NOR gate.

1 module AOI (A, B, C, D, F);

2 input A, B, C, D;
3 output F;

4 wire W1, W2;

5 assign W1 = A & B;
6 assign W2 = C & D;
7 assign F = ~( W1 | W2);

8 endmodule

Figure 9.2.3 – A Verilog And-Or-Invert module

As shown in figure 9.2.3, Verilog allows the declaration of the so-called wire object to allow interconnection of logic.
A wire is a particular case of the more general category of Verilog objects known as nets, all of which share the common
requirement of having to be driven continuously, either by an assignment or a gate/module instantiation.
The left-hand side of a continuous assignment statement must be a wire.

The ports of a module (A, B, C, D and F in the above example) are also wires by default, and as such may appear on the left or
right hand sides of continuous assignments depending on whether they are outputs or inputs respectively. (Note the absence
of the VHDL port mode 'buffer'; Verilog output ports may be read as well as written within the module).

Verilog wires are similar to, but not exactly the same as VHDL’s signals. They are both used to form interconnections.
However, the signal differs from the wire in one very important respect – a signal retains its value in between being assigned
a value, (for example in a clocked process which is activated on the rising edges of a clock). A wire, on the other hand, must
be driven continuously, rather like a real connection in an electronic circuit. Furthermore, a wire which is not driven (ie. left
unconnected) takes on the value ‘z’ (high impedance) to be consistent with real hardware.

Verilog provides the register type variable to describe signals which have to retain a value in between being updated by an
assignment. The most common register variable is referred to simply as reg, a detailed description of this type of variable will
be given in section 9.3.

Modules within Modules – Creating Hierarchy

As stated earlier, modules can instantiate other user-defined modules as well as pre-defined gates and switches. Creating
hierarchical designs in Verilog is very straightforward. There is no concept of component binding or configuration, as in the
case of VHDL. Once a module has been defined it can be compiled into a library (or in some tools, a project database) and
referenced in other modules using the syntax shown below:

module-name instance-name(list-of-connections);

Figure 9.2.4 shows the Verilog description and corresponding block diagram of a hierarchical design.
1 module modT(in1, in2, out1, out2);

2 input [4:0] in1;


3 input in2;

4 output [3:0] out1;


5 output out2;

6 wire [7:0] w2;


7 wire w1;

8 modA U1(in1, w1);


9 modB U2(w1, w2, out1, out2);
10 modC U3(in2, w2);

11 endmodule

Figure 9.2.4 – A Hierarchical Design: Verilog description and


corresponding block diagram

The top-level module 'modT' in figure 9.2.4 contains instances of three previously defined modules 'modA', 'modB' and 'modC'.
The module instantiation statements on lines 8, 9 and 10 define the instance name (U1, U2 and U3) and the connectivity
between the parent and child modules.

In Verilog, there are two alternative ways of specifying connectivity - the first method, known as positional association, is used
in the above example. Each port of the instantiated module is connected to the net occupying the corresponding position in the
port list; unconnected ports are simply left blank (but the separating commas must be included).
The following module instantiation illustrates how to leave ports unconnected:

//module ttl163(CLK, CLR, LOAD, QA, QB, QC, QD, RCO);


ttl163 frtimer(clock, notfr, vdd, ,,, fr, );

The line beginning with '//' is a single-line comment (same as C++) showing the module header for 'ttl163'. The line below
shows an instantiation of 'ttl163' with ports 'QA', 'QB', 'QC' and 'RCO' left unconnected (port 'QD' is connected to 'fr'). The blank
spaces between the commas indicate open circuit ports. Both outputs and inputs may be left unconnected, the latter take on
the high-impedance value 'z' during simulation.

The second method used to define module connectivity is referred to as explicit association. In this notation the names of the
ports of the child module are specified along with the name of the net being connected using the format shown below:

//module myxor(y, a, b);


myxor g1(.y(w1), .a(w2), .b(w3));

When explicit association is used, the connections may be listed in any order.

The Verilog example shown in figure 9.2.4 also illustrates how ports and nets of width greater than 1-bit are defined. Line 2
(repeated below) defines a 5-bit input port, the individual bits being referred to as 'in1[4]', 'in1[3]', …..'in1[0]'. Note that Verilog
always treats the left hand bit as the most significant bit, even if the range is specified as '[0:4]'.

2 input [4:0] in1;

An 8-bit internal wire is defined on line 6. This bus links the 8-bit ports of child modules 'modB' and 'modC'.

6 wire [7:0] w2;

Port Connection Rules

As mentioned previously, Verilog uses two types of object to model digital hardware:

● Net – must be continuously driven, primary use is to model connections between continuous assignments and
instantiations.
● Register – retains the last value that was assigned to it. Often (but not only) used to represent storage elements.

Verilog imposes a set of rules regarding the nature of module ports and the type of object they can be connected to in a
hierarchical design. Within the confines of a module, ports of direction input or inout are implicitly of type net, whereas output
ports can be either nets or registers.
The output and inout ports of a module must be connected to nets at the next level up in the hierarchy. However, an input
port may be connected to either a net or a reg type object.

The above rules are summarised in figure 9.2.5


Figure 9.2.5 – Module Port Rules

Read Chapters 2 and 4 of Palnitkar and/or Chapter 2 of Ciletti

9.3 Elements of Verilog-HDL – Built-in Primitives, Types, Operators and Expressions

This section introduces the basic elements of the Verilog hardware description language, emphasising those aspects relevant
to logic synthesis.

As mentioned in the previous section, Verilog makes use of two basic types of variable, namely Nets and Registers. Within
each category there exist several variants, which are listed in table 9.3.1 shown below. All of the type names listed in Table
9.3.1 are Verilog reserved words, the most commonly used types are shown in bold typeface.

Along with the basic interconnection net type wire, two additional predefined nets are provided to model power supply
connections – supply0 and supply1.

These special nets possess the so-called ‘supply’ drive strength (the strongest, it cannot be overridden by another value) and
are used whenever it is necessary to tie input ports to logic-0 or logic-1. The following snippet of Verilog shows how to declare
and use power supply nets:

module...

supply0 gnd;
supply1 vdd;

nand g1(y, a, b, vdd); //tie one input of nand gate high

endmodule
The power supply nets are also useful when using Verilog to describe switch-level MOS circuits. However, the Verilog switch-
level primitives (nmos, pmos, cmos etc.) are not generally supported by synthesis tools and therefore we will not pursue this
area any further here.

Nets Registers
(Connections) (Storage)
wire reg
tri integer
supply0 real
supply1 time
wand realtime
wor
tri0
tri1
triand
trireg
trior

Table 9.3.1 – Verilog variables

Of the remaining types of net shown in the left hand column of table 9.3.1, most are used to model advanced net types which
are not supported by synthesis tools, the one exception being the net type tri. This net is exactly equivalent to the wire type of
net and is included mainly to improve clarity. Both wire and tri nets can be driven by multiple sources and can therefore be in
the high-impedance state (z). The net type tri can be used instead of wire to indicate that the net spends a significant amount
of the time in the high-impedance state.

The handling of multiple drivers and high impedance states is built-in to the Verilog HDL, unlike VHDL, where additional IEEE
packages are required to define types and supporting functions for this purpose.

The right hand column of table 9.3.1 lists the register type of Verilog variables. These have the ability to retain a value and are
therefore used exclusively inside procedural blocks (similar to sequential processes in VHDL). The two most commonly used
register variable types are reg and integer, the remaining types are generally not supported by synthesis tools.

There are some important differences between the reg and integer type of variable that result in the reg variable being the
preferred type in many situations.

A reg can be declared as a 1-bit object (ie. no size range is specified) or as a vector, as shown by the following examples:

reg a, b; //single-bit register variables


reg [7:0] busa; //an 8-bit register variable, busa[7] is MSB

As shown above, a reg can be declared to be of any required size, it is not limited by the word size of the host processor.

An integer, on the other hand, cannot normally be declared to be of a specified size, it takes on the default size - usually 32-
bits.

The other difference between the two variable types relates to the way they are handled in arithmetic expressions. An integer
is stored as a two’s complement signed number and is handled in arithmetic expressions in the same way, ie. as a signed
quantity. In contrast, a reg variable is always treat as an unsigned quantity by arithmetic operators, it is left to the designer to
make provision for signed values declared as reg.

The differences discussed above mean that the reg and integer variables have different scopes of application in Verilog
descriptions. Generally, reg variables are used to model actual hardware registers such as counters, state registers and
datapath registers, whereas integer variables are used for the computational aspects of a description, such as loop-counting.

The following example (figure 9.3.1) shows the use of the two types of register variable.

The Verilog code shown below describes a 16-bit synchronous binary up counter and makes use of two always procedural
blocks. The first block describes a set of 16 T-type flip-flops in terms of D-types and Exclusive-OR gates, the second always
block describes combinational logic responding to changes in the register ‘q’.
The second block uses a locally declared integer ‘i’ to count through the ‘t’ inputs of the flip-flops and assign the correct logical
value to implement a binary up count.

module longcnt2(clock, reset, q);

input clock, reset;


output [15:0] q;
reg [15:0] q, t; //flip-flop outputs and inputs

//sequential logic
always @(posedge clock)
begin
if (reset)
q = 16'b0;
else
q = q ^ t;
end

always @(q) //combinational logic


begin: t_block
integer i; //integer used as loop-counter
for(i = 0; i < 16; i = i + 1)
if (i == 0)
t[i] = 1'b1;
else
t[i] = q[i-1] & t[i-1];
end

endmodule

Figure 9.3.1 – Use of reg and integer


Logic Values

Each individual bit of a Verilog reg or wire can take on any one of the four values listed in table 9.3.2. Verilog also provides
built-in modelling of signal strength. However this feature is generally not applicable to synthesis and therefore we will not
cover it here.

Logic Value Interpretation


0 Logic-0 or False
1 Logic-1 or True
x Unknown (or Don’t Care)
z High Impedance

Table 9.3.2 – Four-valued Logic

Of the four values listed in the table 9.3.2, logic-0 and logic-1 correspond to boolean false and true respectively. In fact, any
non-zero value is effectively true in Verilog, as it is in the C language. Relational operators all result in a 1-bit result indicating
whether the comparison is true (1) or false (0).

Two meta-logical values are also defined. These model unknown states (x) and high impedance (z), the ‘x’ is also used to
represent ‘don’t care’ conditions in synthesis.

To accurately reflect the unknown state of a circuit when it is first powered up, all nets without drivers (unconnected) are set to
‘z’ and all register variables are set to ‘x’ at the beginning of a simulation run.
Verilog provides a set of built-in pre-defined logic gates, these primitive elements respond to unknown and high-impedance
inputs in a sensible manner. Figure 9.3.2 shows a very simple module for a 2-input AND gate using the built-in and primitive.
The simulation waveforms show how the output responds when driven by 'x' and 'z' states.

module valuedemo(y, a, b);

input a, b;
output y;

and g1(y, a, b);

endmodule

Figure 9.3.2 - and gate response to 'x' and 'z'


where
a = z (high impedence) from 200nS to 250nS
and
b = x (unknown state) from 250nS to 350nS

Refering to the waveforms in figure 9.3.2, during the period 0ns to 200ns the and gate output 'y' (purple wave) responds as
expected to each combination of inputs 'a' and 'b'. At 200ns the 'a' input is driven to the 'z' state and the gate outputs an 'x'
(grey shaded regions) since the logical AND of logic-1 and 'z' is undefined. Similarly, during the interval 250ns to 300ns, the 'x'
on the 'b' input also causes the output 'y' to be an 'x'.
Finally, at 300ns the 'a' input goes low causing 'y' to go low, since anything ANDed with a logic-0 results in logic-0.

Specifying Values

There are two types of number used in Verilog-HDL – sized and un-sized.

The format of a sized number is shown below :

<size>‘<base><number>

Both the <size> and <base> fields are optional. If left out, the number is taken to be in decimal format and the size is implied
from the variable to which the number is being assigned.
The <size> is a decimal number specifying the number of bits in the number, <base> can be any one of the following:

Binary – b or B
Hexadecimal – h or H
Decimal(default) – d or D
Octal – o or O

The actual number is specified using combinations of the digits – 0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f. Hexadecimal numbers may
use all of these digits but binary, octal and decimal are restricted to (0,1), (0–7) and (0-9) respectively.

Examples:

4’b0101 //4-bit binary number


12’hefd //12-bit hex number
16’d245 //16-bit decimal number
Generally, it is not necessary to specify the size of a number being assigned to an integer variable, since such objects are
unsized (commonly occupy 32-bits).

The literals ‘x’ and ‘z’ (unknown and high-impedance) may be used in binary, hex and octal based values. An ‘x’ or ‘z’ sets 4-
bits in a hex number, 3-bits in an octal number and 1-bit in a binary number.

Furthermore, if the most significant digit of a value is 0, z or x, the number is automatically extended using the same digit so
that the upper bits are identical.

Examples:

12’h13x //12-bit hex number ‘00010011xxxx’ in binary


8’hx //8-bit hex number ‘xxxxxxxx’ in binary
16’bz //16-bit binary number ‘zzzzzzzzzzzzzzzz’

The basic single-bit binary states of logic-0 and logic-1 are usually written in the following manner:

1’b0 //logic-0
1’b1 //logic-1

Of course, the single decimal digits 0 and 1 can also be used in place of the above. As mentioned previously, the value 1’b0
(1’b1) represents the false (true) condition in Verilog.

Primitive Gates

The Verilog HDL provides a set of built-in primitive logic and three-state gates. These elements are all synthesizable, however
they are more often used in the output gate-level Verilog net-list produced by a synthesis tool.

Figures 9.3.3 and 9.3.4 show the symbolic representation and Verilog code for each of the primitives. (Detailed truth tables for
all the primitives can be found in Chapter 5 of Palnitkar and Appendix A of Ciletti)

The use of the gates is fairly self-explanatory, the basic logic gates such as AND, OR etc. all have single-bit outputs but allow
any number of inputs (figure 9.3.3 shows 2-input gates only). The Buffer and NOT gates allow multiple outputs and have a
single input. The three-state gates all have three terminals – output, input and control.

For all gate primitives, the output port must be connected to a net, usually a wire, but the inputs may be connected to nets or
register type variables.

An optional delay may be specified in between the gate primitive name, This instance name which can take the form of simple
propagation delays or contain separate values for rise, fall and turn-off delays. (See section 5.2 of Palnitkar for more details).

Figure 9.3.5 shows a simple example of a gate-level Verilog description making use of the built-in primitives.
Figure 9.3.3 – Verilog Primitive Logic Gates

Figure 9.3.4 – Verilog Primitive Tri-State Gates


module x_or_s (y, a, b);

input a, b;
output y;

wire t1, t2, t3, t4;


and #10 g1 (t3, t1, a);
and #10 g2 (t4, t2, b);
not #10 g3 (t1, b);
not #10 g4 (t2, a);
or #10 g5 (y, t3, t4);

endmodule

Figure 9.3.5 – A Verilog Gate-level Circuit

In the gate-level example of figure 9.3.5, each gate has a propagation delay of 10 time units which means that changes at the
input of a gate are reflected at the output after a delay of 10 time units. The actual units of time to be used during the
simulation can be defined using the timescale compiler directive. This directive immediately precedes the module to which it
applies, as shown below:

`timescale 1 ns / 1 ns ( ` is known as the grave accent character)

module ...

In the above case all time delay values would be interpreted as nano-seconds.

Gate delays are inertial, meaning that input pulses which have a duration of less than the gate delay are ignored by the gate in
a similar manner to real logic gates.

Logic synthesis tools ignore time delays.

Operators and Expressions

Verilog provides a powerful set of operators for use in digital hardware modelling. Inspection of table 9.3.3 reveals the similarity
between the Verilog operators and those of the C language. There are however, one or two important differences which are
listed below:

● Verilog provides a powerful set of unary logical operators (Reduction) which operate on all of the bits within a word.
● Additional ‘case’ equality/inequality operators are provided to handle ‘z’ and ‘x’ values.
● The curly braces ‘{‘ and ‘}’ are used in the concatenation and replication operators instead of block delimiters (Verilog
uses begin...end for this).

Operator Type Symbol Operation Operands


* Multiply Two
/ Divide Two
Arithmetic + Add Two
- Subtract Two
% Modulus Two

! Logical negation One


Logical && Logical AND Two
|| Logical OR Two

> Greater than Two


< Less than Two
Relational
>= Greater than or equal Two
<= Less than or equal Two

== Equality Two
!= Inequality Two
Equality
=== Case equality Two
!== Case inequality Two

~ Bitwise NOT One


& Bitwise AND Two
Bitwise | Bitwise OR Two
^ Bitwise Exclusive-OR Two
^~ or ~^ BitwiseExclusive-NOR Two

& Reduction AND One


~& Reduction NAND One
| Reduction OR One
Reduction
~| Reduction NOR One
^ Reduction EXOR One
^~ or ~^ Reduction EXNOR One

>> Shift left Two


Shift
<< Shift right Two

Concatenation {} Concatenate Any number


Replication {{}} Replicate Any number

Conditional ?: Conditional Three

Table 9.3.3 – Verilog Operators

The operators are combined with operands to form expressions which can appear on the right hand side of a continuous
assignment statement or within a procedural block.

An operand can be a whole object (integer or reg), part-select (a subset of the bits within a multi-bit word) or individual bit, as
demonstrated by the following examples:

wire [7:0] a, b; //8-bit wire


wire [3:0] c; //4-bit wire
wire d; //1-bit wire

//4 2-input Exor gates


assign c = a[3:0] ^ b[3:0];

//2-input And with inverted I/P


assign d = c[2] & ~a[6];

//8-input Nor gate using reduction NOR


assign d = ~|a;

//Quad 2-to-1 multiplexor


assign c = d ? a[3:0]: a[7:4];
The last example shows the use of the conditional operator to describe a set of multiplexors. This true_expression ‘a[3:0]’ is
assigned to ‘c’ when the control_expression ‘d’ is equal to 1’b1.

Unlike VHDL, Verilog supports the use of arithmetic operations on multi-bit reg and wire objects, this is very useful when using
the language to describe hardware such as counters (+, - and %) and Digital Signal Processing systems (* and /).
The important thing to bear in mind when using the arithmetic and comparison operators on multi-bit wires and regs is that the
operands are always taken to be unsigned. When the data being manipulated is in signed two’s complement format, the
designer must make provision within the Verilog code to deal with the sign bit (MSB).

The presence of a ‘z’ or ‘x’ in a reg or wire being used in an arithmetic expression results in the whole expression being
unknown.

Here are a selection of examples illustrating the use of the arithmetic operators:

wire [3:0] m; //declare a 4-bit wire


wire inc_dec; //declare a 1-bit wire

//An incrementer/decrementer
assign m = inc_dec ? m + 1: m - 1;

//assigning values to two 4-bit objects


in1 = 4’b101x;
in2 = 4’b0110;

sum = in1 + in2; //sum = 4’bxxxx due to ‘x’ in in1

// A 4-bit adder with carry-in and carry-out


module fulladd4(sum, c_out, a, b, c_in);

output [3:0] sum;


output c_out;
input[3:0] a, b;
input c_in;

//Concatenation operator used to form a 5-bit result


assign #15 {c_out, sum} = a + b + c_in;

endmodule

The Verilog logical operators (!, && and ||) always evaluate to a 1-bit result – 1’b0, 1’b1 or 1’bx.

The values 1’b0 and 1’b1 are equivalent to boolean false and true respectively, hence the following continuous assignment will
generate a logic-1 on ‘y’ when the variable ‘a’ is equal to ‘0101’ AND the variable ‘b’ is greater than decimal 5.

assign y = (a == 4’b0101) && (b > 5);

If any operand of a logical operator is equal to ‘x’ or ‘z’ the result is unknown (x).

The following Verilog extract shows the use of the logical-OR operator in a procedural block (always..) describing a modulo-18
counter.

//a mod-18 counter


always@(posedge clock)
begin
if (reset || (count == 5'd17))
count = 5'b0;
else
count = count + 1;
end

The above counter output, ‘count’, resets to zero on the next positive clock edge if the ‘reset’ input is logic-1 OR if the ‘count’
reaches decimal 17.

The Verilog relational operators (>, <, >=, <=) always evaluate to a single-bit result - 1 if true, and 0 if false. Furthermore, if any
of the bits being compared are unknown or ‘z’, the result of the comparison is ‘x’.

Here a few examples:

//set up some values


// A = 4, B = 3
// X = 4’b1010, Y = 4’b1101, Z = 4’b1xxx

A <= B //Evaluates to logic-0


A > B //Evaluates to logic-1

Y >= X //Evaluates to logic-1


Y < Z //Evaluates to an x

//Address decoding logic – ‘enrom’ is set to logic-0 if the address is less

//than 8000 hex and greater-than-or-equal-to 0000 hex


assign enrom = ~((addr < 16’h8000) & (addr >= 16’h0000));

The equality operators (==, !=, ===, !==), in common with the relational operators, always evaluate to a single-bit result -1 if
true, and 0 if false.

If any of the bits being compared by ‘==‘ or ‘!=‘ are ‘x’ or ‘z’, the result is ‘x’.

The case-equality operators, ‘===‘ and ‘!==‘ compare x’s and z’s, as well as 0's and 1's, the result is always 0 or 1.

Some examples follow:

//Set up some values


// A = 4, B = 3, X = 4’b1010, Y = 4’b1101
// Z = 4’b1xxz, M = 4’b1xxz, N = 4’b1xxx

A == B //Evaluates to logic-0

X != Y //Evaluates to logic-1

X == Z //Evaluates to x

Z === M //Evaluates to logic-1

Z === N //Evaluates to logic-0

M !== N //Evaluates to logic-1

//A 4-input AND gate, the expression ‘a == 4’b1111’ evaluates to a

//single-bit result
assign f = (a == 4’b1111);

The bitwise and reduction logical operators (~, &, |, ^, ~^, ~&, ~|) can be used in place of the Verilog built-in gate primitives.
They have the advantage of being able to handle multi-bit inputs and generate multi-bit results (the reduction operators
produce a 1-bit result).

If any bit or bits within an input operand are high impedance (z), the result is generated as if the corresponding bits were
unknown (x).

The bitwise operators have two operands they work in much the same way as their C-language equivalents. The reduction
operators are unique to Verilog, they process the bits in a single word, rather like a multi-input logic gate. The reduction
operators always produce a single-bit result.

Here are some examples:

//set up some values


// X = 4’b1010, Y = 4’b1101, Z = 4’b10x1

~X //Result is 4’b0101

X&Y //Result is 4’b1000

X|Y //Result is 4’b1111

X&Z //Result is 4’b10x0

&X //Result is 1&0&1&0 = 1’b0

|Y //Result is 1|1|0|1 = 1

~&Y //Result is ~(1&1&0&1) = 1

The shift operators (<<, >>) shift logic-'0’s into the vacant bit positions. For this reason, care must be taken when shifting two’s
complement (signed) numbers.

If a negative two’s complement number is shifted right using the ‘>>’ operator, the sign bit is changed from a ‘1’ to a ‘0’,
changing the polarity of the number from negative to positive. Right-shifting of two’s complement numbers is best done using
the replication/concatenation operators (see later).

//initialise X
//X = 4’b1100

Y = X >> 1 //Result is 4’b0110

Y = X << 1 //Result is 4’b1000

Y = X << 2 //Result is 4’b0000

As mentioned previously, one of the main visual differences between Verilog and C results from the different uses of the curly
braces ({ }). In Verilog, these represent the concatenation operator, the purpose of which is to join together multiple operands
to form a longer operand. In essence, the ‘{ }’ operator represents wiring, ie. joining two or more signals to form a wider signal.

Here are a couple of examples:

//set up some values


//A = 1’b1, B = 2’b00, C = 3’b110

Y = {A, B} //Y is 3’b100

Z = {C, B, 4’b0000} //Z is 9’b110000000

W = {A, B[0], C[1]} //W is 3’b101


One point to note about the concatenation operator is that the size of the operands must be compile time constants.

The replication operator ({{}}) uses a replication constant ‘k’ to specify how many times to replicate the expression ‘j’, the format
is - {k { j }}. In terms of wiring, this corresponds to fanning-out from a single wire or group of wires.

The following examples illustrate the use of the replication operator:

//some values
//a = 1’b1, b = 2’b00, c = 2’b10

Y = {4{a}} //Y is 4’b1111

//replication can be combined with concatenation


//as shown by the following 2 examples

Y = {4{a}, 2{b}} //Y is 8’b11110000

Y = {3{c}, 2{1’b1}} //Y is 8’b10101011

//Shifting a two’s complement number right

//a two’s comp value (-54)


wire [7:0] data = 8’b11001010;

//arithmetic right shift by 2 places


assign data = { 3{data[7]}, data[6:2] }; // data is 8’b11110010 (-14)

The last operator shown in table 9.3.3 is the conditional operator, the format of which is as follows:

condition ? true_expr: false_expr

If ‘condition’ evaluates to 1(0), the expression takes on the value of the ‘true_expr’ (‘false_expr’). The true and false
expressions can be any legal Verilog expression, including one making use of the conditional operator. In other words the
conditional operator may be nested in order to form more complex decisions. The third example shown below illustrates this
last point using a 4-to-1 multiplexer.

Examples of conditional operator usage:

//A 2-to-1 multiplexer


assign out = sel ? In1: in0;

//8-bit three-state buffer


assign data_bus = en ? data: 8’bz;

//4-to-1 multiplexer using nested ‘?:’


assign out = s1 ? ( s0 ? i3: i2) : (s0 ? i1: i0);

This concludes our brief look at Verilog’s primitives, types and operators. The next section examines the alternative ways in
which digital hardware can be described using Verilog.

Read Chapters 3 and 5 of Palnitkar and Chapter 4 and appendix A of Ciletti.


9.4 Styles of Description – Data-flow and Behavioural

Data-flow: The Continuous Assignment

We have already come across numerous examples in the previous sections of Verilog designs written in the so-called Data-
flow style. This style of description makes use of the parallel statement known as a continuous assignment.
This statement, which is equivalent to the concurrent signal assignment statement in VHDL, is identified by the keyword
assign. The keyword is followed by one or more assignments terminated by a semicolon.

For example:

assign A = q[0], B = q[1], C = q[2]; //three continuous assignments

The continuous assignment statement forms a static binding between the wire being assigned on the left-hand-side of the ‘=’
operator and the expression on the right-hand-side of the assignment operator. This means that the assignment is
continuously active and ready to respond to any changes to variables appearing in the right hand side expression (the inputs).
Such changes result in the evaluation of the expression and updating of the target wire (output).

In this manner a continuous assignment is almost exclusively used to describe combinatorial logic.

A Verilog module may contain any number of continuous assignment statements which can be inserted anywhere between the
module port and internal wire/reg declarations and the endmodule keyword.

The expression appearing on the right hand side of the assignment operator may contain both reg and wire type variables and
make use of any of the Verilog operators mentioned previously.

The so-called target of the assignment (left -hand-side) must be a wire, since it is continuously driven. Both single-bit and multi-
bit wires can be driven by continuous assignments.

The following three examples illustrate the use of the continuous assignment statement in data-flow style descriptions.

Figure 9.4.1 shows the description of a 4-to-1 multiplexer using a single continuous assignment statement (shown shaded).
The expression on the right hand side of the assignment makes use of the bit-wise logical operators ‘&’, ‘~’ and ‘|’.
The Verilog shown in Figure 9.4.2 describes a 4-bit binary adder with carry-in and carry-out. The adder is described using a
single assignment and illustrates the use of the unsigned ‘+’ operator and the concatenation operator; the latter is used to form
a 5-bit result from the sum of the two 4-bit operands and the single-bit carry-in.

module mux4_to_1 (out, i0, i1, i2, i3, s1, s0);

output out;
input i0, i1, i2, i3;
input s1, s0;
assign out = (~s1 & ~s0 & i0) |
(~s1 & s0 & i1) |
(s1 & ~s0 & i2) |
(s1 & s0 & i3) ;

endmodule

Figure 9.4.1 – Verilog description of 4-to-1 Multiplexer


module fulladd4(sum, c_out, a, b, c_in);

output [3:0] sum;


output c_out;
input[3:0] a, b;
input c_in;
assign #15 {c_out, sum} = a + b + c_in;

endmodule

Figure 9.4.2 - Verilog description of 4-bit binary adder

Both of the above examples are combinatorial.This is the most common use of the continuous assignment statement.
The last example, shown below in figure 9.4.3, illustrates the use of the continuous assignment statement to describe
sequential logic, in this case a level-sensitive latch.The conditional operator ( ?: ) is used on the right hand side of the
assignment (shaded) to describe a multiplexer.

module latch (q, data, en);

output q;
input data, en;
assign q = en ? data : q;

endmodule

Figure 9.4.3 – Verilog description of a level-sensitive latch

Behavioural: The Procedural Block

The Verilog-HDL procedural block is equivalent to the VHDL process statement. Both constructs define a region within a
hardware description containing sequential statements. These statements execute in the order they are written in just the same
way as a conventional programming language. Our brief look at some of the more commonly used Verilog sequential
statements will reveal their similarity to the statements used in the C language (whereas VHDL sequential statements are
based on ADA).

Strictly speaking, Verilog provides two types of procedural block:

● The always block – very similar to a VHDL process, acting like a continuous loop that never terminates.
● The initial block – executes from beginning to end once only, at the start of a simulation. Rather like a process with a
terminating wait statement. These blocks are used exclusively in Verilog Test-benches.
A module can contain any number of always and initial blocks which all execute concurrently in the same way as multiple
processes.

Figure 9.4.4 shows the basic format of an always block, the keyword ‘always’ is followed by the so-called event expression.
This determines when the sequential statements in the block (between begin and end) execute. The ‘@(event expression)’ is
required for both combinatorial and sequential logic descriptions.

In common with the C language, the begin ({) and end (}) block delimiters can be omitted if there is only one sequential
statement subject to the always @ condition.

always @(a)
y = a * a; //no begin..end required for single statement

always @(event_expression)
begin
...
...//sequential
..//statements

end

Figure 9.4.4 – Basic format of the 'always' block

Assignments within sequential blocks

A sequential block (always or initial) will only execute when the event expression triggers, in between times the block is in a
state of suspension. Any objects being assigned to within the block must therefore be capable of remembering the value that
was last assigned to them in between being updated.
In other words, objects which are assigned to within sequential blocks are not continuously driven. This leads to the following
statement concerning sequential blocks:

● Only reg type variables can be assigned within a sequential block.

Note that a sequential block can be triggered into action by changes in both regs and wires, provided they appear in the event
expression, but the target of an assignment within the block (a sequential assignment) must be a reg.

The VHDL object referred to as a signal is similar to the Verilog reg, in that it also has the ability to retain its value in between
being updated by a signal assignment. There is no equivalent to the Verilog wire in VHDL.

Sequential Statements

Table 9.4.1 contains most of the sequential statements available for use inside of a sequential block. Some are very similar to
those used in the C language, while others are unique to the Verilog-HDL. The table gives a very brief description of each
statement and, in some instances, provides the VHDL nearest equivalent statement. A more detailed description will be
provided for those statements used in the following examples.
Items in square brackets ([ ]) are optional, curly braces ({ }) enclose repeatable items and all bold-face keywords must be lower
case.

Sequential Statement Description


; Null statement (same as C)
begin
{Seq_statements} Bracketing statements, always required if there is more than one sequential statement.
end
if (expr)
seq_statement
Conditional statement, expression must be in parentheses (same as C)
[else
seq_statement]
case (expr)
[{{expr,}: seq_statement}] Multi-way decision, expression must be in parentheses . (Differs from C switch statement
[default: seq_statement] in that no breaks are required.
endcase
forever
Unconditional loop
seq_statement
repeat (expr)
Fixed repetition of seq_statement ‘expr’ times
seq_statement
while (expr)
Entry test loop (same as C)
seq_statement
for (exp1; exp2; exp3)
Universal loop construct (same as C)
seq_statement
#(time_value) Suspends a block for ‘time_value’ time units, similar to VHDL: wait for time_expression;
@(event_expr) Suspends a block until ‘event_expr’ triggers, similar to VHDL: wait until boolean_condition;

Table 9.4.1 – The Verilog sequential statements

A key difference in the syntax between Verilog and VHDL sequential statements concerns bracketing. All VHDL statements are
self-bracketing, whereas Verilog requires begin…end wherever more than one statement is the subject of another statement,
as illustrated by the following VHDL and Verilog sequential statements:

VHDL Verilog
if a + b = c then if (a + b == c)
y: = d + e; begin
x: = f – g; y = d + e;
z <= '0'; x = f - g;
else z = 1'b0;
y: = d - e; end
x: = f + g; else
z <= '1' ; begin
end if; y = d - e;
x = f + g;
z = 1'b1 ;
end

Combinatorial example - A 2-input Multiplexer

Figure 9.4.5 shows the Verilog description of a multiplexer making use of an always sequential block.

The first shaded line highlights the requirement to qualify the output port 'out' as a reg, since it appears on the left hand side of
an assignment within the sequential block.The second shaded line highlights the format of the event expression for a
combinatorial block. All of the inputs to the block must be included between the parentheses and separated by the keyword or.

Note that the word or does not mean logical-OR. It is interpreted as follows:
‘An event on input 'a', or an event on input 'b', or an event on input 'sel' can trigger the execution of the sequential block’.

module mux(out, a, b, sel);


output out; reg out;
input a, b, sel;
always @(a or b or sel)
begin
if (sel)
out = a;
else
out = b;
end

endmodule

Figure 9.4.5 - A 2-input Multiplexer described using an always block

Furthermore, in figure 9.4.5 the begin…end bracketing in the always block is redundant, since it consists of just a single
if…else statement. Despite this, it is probably good practice to include the begin…end bracketing by default in all sequential
blocks.

Figure 9.4.6 shows the VHDL equivalent of the always block contained within the module listed in figure 9.4.5.

process(a, b, sel)
begin
if sel = '1' then
out <= a;
else
out <= b;
end if;
end process;

Figure 9.4.6 - A VHDL process equivalent to the always block in figure 9.4.5

In the VHDL version of the multiplexer, the event expression is replaced by the sensitivity list following the keyword process.
The condition tested by the if statement must be explicitly stated, since ‘1’ is not necessarily equivalent to boolean True, as is
the case in Verilog.

Sequential example - A Positive-Edge Triggered D-type Flip-flop

Verilog always sequential blocks are used to describe sequential logic. Figure 9.4.7 shows a simple example of a D-type Flip-
flop.

As in the previous example, the ‘q’ output must be qualified as a reg due to being assigned the value of the ‘d’ input within the
sequential block. The event expression (second shaded line) makes use of the built-in Verilog keyword posedge to detect
when the ‘clk’ input port undergoes a positive-edge transition. (For negative-edge triggered sequential elements, Verilog
provides the negedge keyword for use in the event expression).

module dff(q, d, clk);

input d, clk;
output q; reg q;

always@(posedge clk)
q = d;

endmodule

Figure 9.4.7 - A D-type Flip-flop

The built-in qualifiers, posedge and negedge, simplify the description of sequential logic when using Verilog. To create the
equivalent behaviour in VHDL requires the use of the IEEE Standard Logic Package (Std_Logic_1164) functions ‘rising_edge’
and ‘falling_edge’, or alternatively the use of the expression ‘(clk’event and clk = ‘1’)’ in a wait or if statement.

Assignments within Sequential Blocks – Blocking or Non-Blocking?

In all of the examples of Verilog descriptions contained in this chapter we have seen the use of the ‘=’ operator when assigning
an expression to a wire or reg. For the continuous assignment parallel statement, the ‘=’ assignment is always used. However
sequential blocks can make use of two different types of assignment:

● Blocking Assignment – uses the ‘=’ operator


● Non-Blocking Assignment – uses the ‘<=’ operator

The difference between the above assignments is quite subtle and can result in simulation and/or synthesis problems if not
fully understood.

The blocking assignment is the most commonly used type of sequential assignment. As the name suggests, the target of the
assignment is updated before the next sequential statement in the procedural block is executed, in much the same way as in a
conventional programming language. In other words, a blocking assignment ‘blocks’ the execution of the subsequent
statements until it has completed.

On encountering a non-blocking assignment, the simulator schedules the assignment to take place at the beginning of the next
simulation cycle. This normally occurs at the end of the sequential block (or at a point when the sequential block is next
suspended), so the subsequent statements are not blocked by the assignment.
In this manner, non-blocking assignments can be used to assign several reg type objects synchronously, under control of a
common clock.

The following example illustrates a common modelling error caused by the misuse of blocking assignments.

A 4-bit Ring Counter

Figure 9.4.8 shows the logic diagram for a 4-bit Ring Counter. The circuit is initialised by setting the asynchronous ‘reset’ input
to logic-1 which has the effect of setting bit-0 of ‘count’ to logic-1 while clearing the remaining bits to logic-0.
The equivalent Verilog assignment is shown below:

count = 4’b0001;

With the ‘reset’ input negated, the application of successive clock pulses causes the logic-1 (initially stored in count[0]) to rotate
around the ring of 4 flip-flops, producing a table of states as shown below.
Figure 9.4.8 – A 4-bit Ring Counter

State Count[3] Count[2] Count[1] Count[0]


1 0 0 0 1
2 0 0 1 0
4 0 1 0 0
8 1 0 0 0

Ring Counter States

Figure 9.4.9 shows the first attempt at a Verilog description of the Ring Counter.

module bad_ring4(clock, reset, count);

input clock, reset;


output [3:0] count;

reg [3:0] count:


always @(posedge clock or posedge reset)
begin
if (reset)
count = 4'b0001;
else
begin
count = count << 1;
count[0] = count[3];

end
end

endmodule

Figure 9.4.9 – Ring Counter – first attempt

The module header defines the name of the module as ‘bad_ring4’, indicating that the description is flawed. The input and
output ports are declared in the usual way, followed by the qualification of the 4-bit output port ‘count’ to be a reg type object.
The first shaded line shows how positive going edges on the ‘clock’ or ‘reset’ inputs are used to trigger the always sequential
block. The asynchronous ‘reset’ input must be preceded by the posedge keyword even though it is a level-triggered input.
Testing the state of ‘reset’ using the if..else statement ensures that it overrides the ‘clock’ input whenever it is changed from
logic-0 to logic-1.
If the positive edge occurs on the ‘clock’ input and the ‘reset’ input is low, the else part of the if statement is executed
(shaded). This comprises two blocking assignments to the output port ‘count’.

The first assignment shifts the contents of the ‘count’ register left by one bit position. The second statement copies the state of
the left-hand bit (right hand bit in the logic diagram of figure 9.4.8) to the right-hand bit, implementing the feedback connection.

On first impressions, it may seem that the Verilog description should produce the correct results, but as the module name
suggests, this module does not work as expected.

Figure 9.4.10 shows a Verilog listing of a test-bench module which is used to supply input stimulus waveforms to the ring
counter during stimulation.

module ring4_test();

reg clock, reset;

wire [3:0] count;


initial //clock generator
begin
clock = 1'b0;
forever
#50 clock = ~clock;
end

initial //reset generator


begin
reset = 1'b1;
#200 reset = 1'b0;
#1600 $stop;
end

bad_ring4 bad_dut(clock, reset, count);

endmodule

Figure 9.4.10 – Test-bench module for the 4-bit Ring Counter

The test-bench module ‘ring4_test’, in common with a VHDL test-bench, has no ports. The purpose of the test-bench is to
supply the module under test ‘bad_ring4’ with a clock and reset signal. These are declared as reg type objects near the top of
the module, since they are generated using two separate initial sequential blocks (shown shaded). A 4-bit wire named ‘count’
connects to the output of the ring counter; this facilitates the viewing of the counter output waveforms during simulation.

The initial blocks contain sequential statements which are executed once only. However, the first block never terminates, due
to the presence of the forever statement causing the clock to toggle every 50 time units (the ‘#’ means wait for time_value).

The second initial block sets the ‘reset’ signal to logic-1 for 200 time units (2 clock cycles), then to logic-0 for 1600 time units,
after which the simulation is stopped by the ‘$stop’ system task (See Appendix E.4 of Ciletti for more details on systems tasks
and functions).

The net result of the above is that the simulation runs for 1800 time units, during which 18 clock cycles are generated. This
produces the set of waveforms shown in figure 9.4.11 below.
Figure 9.4.11 – ‘ring4_test’ simulation results

Inspection of the waveforms shown in figure 9.4.11 reveals that the counter starts off in the reset state 00012 and remains
there for the first 200 time units.

After ‘reset’ goes low, the next two positive clock edges (occurring at 250 and 350 time units) cause the state of the counter to
advance through states 00102 and 01002, as expected. On the third positive clock edge after reset, the state changes to the
incorrect value 10012, rather than the correct value 10002.

The reason for the incorrect sequence can be established by considering the behaviour of the two blocking assignments
contained within the always procedure in figure 9.4.9, which are repeated below:

begin
count = count << 1; //statement 1
count[0] = count[3]; //statement 2
end

Assuming that ‘count’ is equal to 01002, the next rising edge on the ‘clock’ input causes statement 1 to logically shift-left the
‘count’ reg by one position, resulting in ‘count’ temporarily changing to 10002 . The fact that the first assignment is of the
blocking type means that ‘count’ is updated before the next assignment is made.

Statement 2 then copies ‘count[3]’ to ‘count[0]’, resulting in ‘count’ becoming the incorrect value 10012. The sequence then
repeats as long as the ‘clock’ is applied and the ‘reset’ input is logic-0.

Perhaps the designer assumed that the second statement would use the old value of ‘count’ when copying bit-3 to bit-0, which
would have produced the correct result. Unfortunately, the use of the ‘=’ assignment operator ensures that the target is
updated before the next statement is executed.

Solving the problem - ‘bad_ring4’ becomes ‘good_ring4’

There is no rotate operation available in Verilog, so the problem with the existing ring counter must be solved using a different
method. Fortunately, there are at least three alternative ways of fixing the run-time error.

Solution 1 – Use Non-blocking assignments

module good_ring4(clock, reset, count);

input clock, reset;


output [3:0] count;

reg [3:0] count;

always @(posedge clock or posedge reset)


begin
if (reset)
count = 4'b0001;
else
begin
count <= count << 1;
count[0] <= count[3];
end
end

endmodule

Figure 9.4.12 – Ring Counter with non-blocking assignments

Replacing the two blocking assignments in the else part of the if statement with non-blocking assignments (<=) is one solution
to the problem. The first assignment does not change ‘count’ until the end of the simulation cycle (ie. the end of the sequential
block). Therefore the second assignment uses the current value of ‘count’ when copying the bits over. In effect, the non-
blocking assignments make the two assignments act like synchronous data transfers, both occurring on the rising edge of the
clock input.

Solution 2 – Add an external feedback wire

module good_ring4_2(clock, reset, count);

input clock, reset;


output [3:0] count;

reg [3:0] count;


wire feedback = count[3];
always @(posedge clock or posedge reset)
begin
if (reset)
count = 4'b0001;
else
begin
count = count << 1;
count[0] = feedback;
end
end

endmodule

Figure 9.4.13 – Ring counter with external feedback connection

The second solution adds an internal ‘feedback’ connection to the module and continuously assigns ‘count[3]’ to it, outside of
the bounds of the procedural block. Figure 9.4.13 shows how this can be done on a single line (shaded). In Verilog, the
declaration of a wire can be combined with a continuous assignment to the wire.

This approach solves the problem by delaying updating the feedback wire until the end of the always block. Therefore, the
value of ‘feedback’ used inside the always block is the value of ‘count[3]’ just prior to the clock edge.

Solution 3 – Use the Concatenation operator


module good_ring4_3(clock, reset, count);

input clock, reset;


output [3:0] count;

reg [3:0] count;

always @(posedge clock or posedge reset)


if (reset)
count = 4'b0001;
else
count = {count[2:0], count[3]};
endmodule

Figure 9.4.14 – Ring counter using concatenation operator

Perhaps the simplest solution is shown in figure 9.4.14. Here the two blocking/non-blocking assignments are replaced by one
blocking assignment (non-blocking could also have been used with no change in behaviour). Rather than use a shifting
operator, the bits within ‘count’ are re-arranged in order to perform a rotate-left operation.

In order to prove the validity of the solutions given above, the ring counter test-bench module is modified, as shown in figure
9.4.15 below, to include one of the ‘good’ ring counters. A new set of simulation results can be seen in figure 9.4.16, the
original waveforms are included for comparison.

module ring4_test();

reg clock, reset;

wire [3:0] bad_count, good_count;

initial
begin
clock = 1'b0;
forever
#50 clock = ~clock;
end

initial
begin
reset = 1'b1;
#200 reset = 1'b0;
#1600 $stop;
end

//instantiate original 'bad' design


bad_ring4 bad_dut(clock, reset, bad_count);

//instantiate one of the 'good' designs


good_ring4_3 good_dut(clock, reset, good_count);

endmodule

Figure 9.4.15 – Test-bench containing ‘good’ and ‘bad’ ring counters


Figure 9.4.16 – Simulation results for ‘good’ and ‘bad’ ring counters

Read Chapters 6 and 7 of Palnitkar

9.5 Describing Synthesisable Logic and Verilog-HDL Test-benches

This section builds on the material presented in previous sections in order to provide an overview of the accepted formats used
to create synthesisable descriptions of combinatorial and sequential logic. In addition, a further example involving the use of a
Verilog test-bench module is discussed, reinforcing the power and flexibility of the language as a vehicle for generating
stimulus and controlling simulation.

How to avoid unwanted latches when describing combinatorial logic

A simple way of describing combinational logic in Verilog is to use a continuous assignment. This strategy has the advantage
of not producing unwanted latches in the vast majority of cases (the exception being the use of the ‘?: ’ operator to deliberately
create a latch – see Figure 9.4.3).

The use of continuous assignments can be limiting however, due to the fact that the logic being described must map to an
equivalent expression involving the Verilog operators. An ultimately more flexible approach is to use an always procedural
block to describe combinatorial logic; this is equivalent to using a VHDL process.

A similar set of guidelines to those applying to the use of processes must also be observed when using always blocks. Since
an always block can only assign a value to reg type objects, there is always the possibility that an unwanted feedback path
may be implied due to not assigning a value to a reg under certain conditions.

Here are the guidelines:

● Include all of the inputs to the combinatorial function in the event expression separated by the keyword or.
● To avoid the creation of unwanted latches, ensure either of the following is applicable:

❍ Assign a default value to all outputs at the top of the always block, prior to any sequential statement such as if,
case etc.
❍ In the absence of default assignments, ensure that all possible combinations of input conditions result in a value
being assigned to the outputs.
The reason for observing the above guidelines stems from the behaviour of a reg type object. When not being updated
by an assignment, a reg retains its current value (unlike a wire, which must be continuously driven). A synthesis tool
will insert a feedback loop to latch the last value of the circuit output for any input conditions not resulting in an
assignment to the output(s).

The following example illustrates this common pitfall.

module latch_implied(a, b, c, sel ,y);

input a, b, c;
input [1:0] sel;

output y; reg y;
always @(a or b or c or sel)

begin
if (sel == 2’b00)
y = a;
else if (sel == 2’b01)
y = b;
else if (sel == 2’b10)
y = c;

end

endmodule

Figure 9.5.1 – Example showing latch inference

The designer of the module ‘latch_implied’ shown in figure 9.5.1 has used an always block to describe the
behaviour of a selector circuit. The 2-bit input ‘sel[1:0]’, selects one of three inputs ‘a’, ‘b’ or ‘c’ and feeds it
through to the output ‘y’. The assumption has been made that ‘y’ will be driven to logic-0 if ‘sel’ is equal to ‘11’.
This is of course incorrect.

The omission of a final else clause results in ‘y’ retaining its current value (since it is a reg), hence the presence
of the feedback connection (shown in red) in the synthesised logic diagram shown below the Verilog description
in figure 9.5.1.

The corrected version of the data selector is shown in figure 9.5.2, along with the corresponding synthesis
result.

module data_selector(a, b, c, sel ,y);

input a, b, c;
input [1:0] sel;

output y; reg y;

always @(a or b or c or sel)


begin
if (sel == 2’b00)
y = a;
else if (sel == 2’b01)
y = b;
else if (sel == 2’b10)
y = c;

else
y = 1’b0;
end

endmodule

Figure 9.5.2 – Removal of unwanted feedback using final else clause

An alternative solution to the problem outlined above would have been to include a default assignment to the
circuit output ‘y’, at the beginning of the always block as shown below.

always @(a or b or c or sel)


begin
y = 1’b0; //default assignment ensures y gets a value

if (sel == 2’b00)
y = a;
else if (sel == 2’b01)
y = b;
else if (sel == 2’b10)
y = c;

end

Sequential Elements – Asynchronous and Synchronous Reset

We have already seen how a basic sequential element, such as a flip-flop, can be described in Verilog (see
figure 9.4.7).
This sub-section will expand on this aspect of Verilog and show how resetting mechanisms can be incorporated
which are compatible with most synthesis tools.

Asynchronous Reset

module dffar(clk, d, rst, q);

output q; reg q;
input clk, rst, d;
always @(posedge clk or posedge rst)
begin
if (rst)
q = 1'b0;
else
q=d

end

endmodule

Figure 9.5.3 – Flip-flop with asynchronous reset

The Verilog description of a positive-edge triggered D-type flip-flop with asynchronous reset is shown in figure
9.5.3.
The event expression triggering the always block has been modified to include the asynchronous reset input
‘rst’. On first impressions, it may seem as if the ‘rst’ input will act as another positive-edge sensitive clock input,
due to the presence of the posedge qualifier. In fact, the test for ‘rst == 1’b1’ in the if statement results in ‘rst’
behaving as a level-triggered input, overriding the ‘clk’ input as required. The else part of the if statement will
execute in response to rising edges on the ‘clk’ input only when the ‘rst’ input is at logic-0.

A flip-flop that resets in response to an active-low reset input can be achieved by replacing ‘posedge rst’ with
‘negedge rst’ and substituting (!rst) for (rst), in the if statement.

The following example (figure 9.5.4) shows how both reset and clear inputs can be accommodated, if required.
module dff1 (q, qb, d, clk, set, reset);

input d, clk, set, reset;


output q, qb;

// declare q and qb to be reg, because assigned inside always


reg q, qb;

always @(posedge clk or posedge set or


posedge reset)
begin
if (reset) begin //reset has highest priority
q = 0;
qb = 1;
end else if (set) begin //set has second highest
q = 1;
qb = 0;
end else begin //only when set and reset are low
q = d;
qb = ~d;
end
end

endmodule

Figure 9.5.4 – Asynchronous set and reset


Synchronous Reset

Figure 9.5.5 shows the Verilog description for a D-type flip-flop with synchronous reset. As shown in the
accompanying diagram, the reset logic is placed in the data path. In this case, all assignments to the ‘q’ output
of the flip-flop are synchronised to the positive-edges of the ‘clk’ input. The if..else statement determines
whether to reset ‘q’ to logic-0 or load the value on the data input ‘d’.

Asynchronous or Synchronous?

The answer to the above question often depends on the type of hardware being targeted by the synthesis tool.
Most CPLDs (Complex Programmable Logic Devices) and FPGAs (Field Programmable Gate Arrays) are
flexible enough to allow either reset mechanism to be used.
In general however, it is not a good idea to mix the two initialisation mechanisms in the same design.

module dffsr(clk, d, rst, q);

output q; reg q;
input clk, rst, d;
always @(posedge clk)
begin
if (rst == 1’b1)
q = 1'b0;
else
q = d;

end

endmodule

Figure 9.5.5 – Synchronous reset

Describing a Finite State Machine in Verilog


The Verilog language can be used to create concise descriptions of FSMs, making use of the same basic
behavioural elements, such as state registers and next-state logic, that were used in VHDL.

The absence of user defined types in Verilog means that states and state codes have to be defined more
explicitly than in VHDL. For example, consider the following VHDL state-type declaration (normally inserted in
the architecture declarative region):

type state_type is (s0, s1, s2, s3);

In Verilog, the above would be equivalent to the following (normally inserted after the module input/output
definitions):

parameter s0 = 2’b00, s1 = 2’b01, s2 = 2’b10, s3 = 2’b11;

The Verilog parameter definition shown above creates a set of state names and codes which can be referred to
in the module.

The Pattern Detector - Verilog version

The example used in this section is similar to that discussed in Chapter 7 (see Listing 7.9: Pattern Detector
FSM). A serial data stream is continuously checked for the occurrence of the pattern '01111110'. Each time the
pattern is detected, an output named 'match' goes to logic-1 for a single clock cycle after the final '0' of the
pattern.

The state diagram for the pattern detector is shown in figure 9.5.6. The bubble-diagram style has been used to
contrast with the ASM charts used previously.
Figure 9.5.6 - State Diagram for a Pattern Detector

An asynchronous reset input forces the machine into state 's0' (shown in yellow) irrespective of other
conditions.
The FSM states, represented in figure 9.5.6 as circles, contain the name of the state and the corresponding
value of the 'match' output. The FSM described by figure 9.5.6 is of the Moore variety. The output depends on
the value of the machine state only and the next state is determined by the input 'serin'.

After reset, the machine remains in state 's0' as long as the 'serin' input is at logic-0. Forcing the 'serin' input to
logic-1 for six clock cycles causes the state machine to advance through states 's1' to 's6', whereupon if 'serin'
changes to logic-0 the machine enters state 's7' (shown in green) and the 'match' output goes high for one clock
cycle.
The state diagram is designed to detect patterns that do not share adjacent zeros.

The Verilog-HDL description of the state machine is shown in figure 9.5.7. The ports of module 'patdetfsm' are
shown colour co-ordinated with the state diagram of figure 9.5.6.

After declaring the input and output ports, the next line creates a set of parameters which define the symbolic
names and values of the machine states. A parameter is somewhat similar to a VHDL generic, it can be
thought of as a constant, used within the description to aid clarity and make the model more general purpose.
Parameters can be used to set the size of ports, specify delays or define state assignments, as in the present
example.
Like generics, parameters can be assigned default values that can be overridden when a module is
instantiated.

module patdetfsm(clock, reset, serin, match);

output match;
input clock, reset, serin;
parameter s0 = 0, s1 = 1, s2 = 2, s3 = 3, s4 = 4, s5 = 5,
s6 = 6, s7 = 7, s8 = 8;
reg [3:0] state;

always @(posedge clock or posedge reset)


begin
if (reset)
state = s0;
else
begin
case (state)

s0: if (!serin) state = s0; else state = s1;


s1: if (!serin) state = s0; else state = s2;
s2: if (!serin) state = s0; else state = s3;
s3: if (!serin) state = s0; else state = s4;
s4: if (!serin) state = s0; else state = s5;
s5: if (!serin) state = s0; else state = s6;
s6: if (serin) state = s8; else state = s7;
s7: if (!serin) state = s0; else state = s8;
s8: if (!serin) state = s0; else state = s8;

default : state = 4'bx;


endcase
end
end

assign match = (state == s7);

endmodule
Figure 9.5.7 - Verilog description of the Pattern Detector FSM

The values assigned to the parameters 's0' to 's8' must match the size of the 'state' reg declared next. There
are nine states in total, and therefore a 4-bit state register is required. (Note that the state codes defined within
a module can usually be overridden during synthesis by choosing an alternative coding such as ‘Gray’ or ‘One-
hot’)

Having defined the state names and codes and declared the state register, the module continues with an
always procedural block embodying the next state behaviour of the FSM. This parallel statement constitutes
the main body of the FSM description.

The always block is triggered by zero-to-one transitions on both the ‘clock’ and ‘reset’ inputs, the latter having
the highest priority in order to implement an asynchronous reset mechanism, forcing the state machine into a
known initial state. The next-state behaviour of the machine is described by the case statement contained
within the else part of the if..else statement.

The case statement is fairly self-explanatory. Each possible state is elaborated and an if ..else statement
determines the next state depending on the value of the ‘serin’ input signal (note that no begin..end bracketing
is required due to there being just one statement in each case limb).

The machine uses only 9 states out of a possible 16. Therefore a final default clause is required to indicate that
the 7 unused states are effectively ‘don’t care’ states (this is similar to the others clause in VHDL). The logic
synthesis tool will exploit this fact to minimise the next-state logic.

default: state = 4'bx; //don't cares minimise logic

The use of the 'don't care' next-state state in this particular example is fine since we are using an asynchronous
reset mechanism.

Notice also that all of the assignments to the 'state' reg are of the blocking variety. Non-blocking could have
been used with no change to the behaviour of the model, since the 'state' reg is only ever assigned once within
the always block.

The state machine is designed to output a logic-1 on 'match' when the 'state' becomes 's7' (the green state in
figure 9.5.6). This is achieved using a single continuous assignment prior to the end of the module.

assign match = (state == s7);

The above parallel statement reacts to events on the 'state' reg and assigns a logic-1 on 'match' whenever
'state' is equal to 's7' (4'b0111), otherwise 'match' is driven to logic-0. This statement corresponds to
combinatorial decoding of the 'state' register.

Testing the Pattern Detector FSM

Like VHDL, Verilog provides a rich set of statements to aid the construction of powerful test-benches. Test-
bench based design verification has the advantage of being simulation tool independent and saves having to
write complex stimulus files in some proprietary simulator language.

Figure 9.5.8 shows the general form of a rudimentary Verilog test-bench.

`timescale 1 ns/1 ns //set the unit of time for simulation


module test-bench-template; //no ports required

//declare local wires to connect to outputs of dut


wire output1, output2, …..outputn;

//declare local regs to drive inputs of dut from sequential blocks


reg clock, reset, input1, input2,……inputn;

//instantiate module being tested


module-under-test dut(.port1(input1), .port2(input2),
.port3(output1),….port4(output2));
//An initial block to create a clock
initial

forever
begin

#50 clock = 1;
#50 clock = 0;

end

//Another initial block to create test waveforms


initial

begin

reset = 1'b1;
#150 reset = 1'b0;
#60000 $stop; //stop the simulation after 60150 ns

end

endmodule

Figure 9.5.8 - General format of a simple Verilog test-bench

As shown above, a test-bench module is preceded by a 'timescale' compiler directive, which sets up the basic
unit and resolution of simulation time. In this example, both the time unit and resolution are set to 1
nanosecond.

The module itself has no need of ports, due to not having to communicate with the outside world. Instead,
internal wires are declared to connect up to all outputs of the 'module-under-test', and regs are declared and
connected to all inputs. The use of reg type variables is essential to allow inputs to be driven by behavioural
blocks (always and initial).

Next, the module being tested is instantiated in the test-bench. Often this is given the name 'dut' or 'mut', for
device-under-test or module-under-test respectively.

It is common practice to make use of explicit port association in favour of positional association when
connecting up the module-under-test. This represents the clearest way of matching stimulus and response
signals to module ports.

Having declared the test-bench stimulus and response signals and wired them up to the 'dut'. The remainder of
the test bench generates the required stimulus using initial blocks, as shown in figure 9.5.8. (always blocks may
also be used for stimulus, but traditionally initial blocks are favoured, due to the fact that they can be
distinguished from the always blocks used in the design).
Initial blocks execute once at the start of a simulation run and then suspend, which means that they generally
need to include a forever loop if repetitive clocks are being generated.

The following snippet of Verilog shows an alternative way of generating a clock.

initial

begin: clkgen
clock = 1'b0;
forever
#50 clock = ~clock;
end

As shown in figure 9.5.8, additional initial blocks may be used to generate waveforms for reset signals and
control the simulation run.

Pattern Detector Test-bench - block diagram

Figure 9.5.9 – Pattern detector test-bench block diagram

The test-bench for the pattern detector module makes use of a Pseudo-Random Bit Sequence Generator
(PRBSGEN), shown in figure 9.5.9. This module supplies the pattern detector FSM (PATDETFSM) with a bit-
serial test pattern containing just two occurrences of the pattern ‘01111110’ within a 511-bit long sequence. If
the FSM successfully detects these two patterns and ignores the others, then we can conclude that the design
is correct.

The design-under-test and the PRBSG are instantiated in a module called ‘PATDET’ having an output named
‘TEST_PATTERN’ and an input named ‘PATTERN’. These are linked together by the wire named ‘PATTERN’
in the top-level test module named ‘PATBENCH’. The top-level test-bench module also includes two initial
blocks to generate common clock and reset stimulus.

Verilog listings are given below for each part of the test-bench as follows:

Figure 9.5.10: Module ‘Prbsgen’, a 9-bit Pseudo-Random Bit Sequence Generator


Figure 9.5.11: Module ‘Patdet’ instantiating ‘Patdetfsm’ and ‘Prbsgen’
Figure 9.5.12: Top-level module ‘Patbench’ containing ‘Patdet’ and behavioural stimulus.

Figures 9.5.13 and 9.5.14 show the simulation results produced by the test-bench. Figure 9.5.14 shows a
zoomed-in view of the two occurrences of ‘01111110’ being detected, in addition to the ‘state’ vector waveform.
//A Pseudo-Random Bit Sequence Generator
module prbsgen(prbs, clock, reset);

output prbs;
input clock, reset;

wire feedback;
reg [8:0] ppreg;
always @(posedge clock or posedge reset)
if (reset)
ppreg = 9'b000000001;
else
ppreg = {ppreg[7:0],(ppreg[8]^ppreg[4])};

assign prbs = ppreg[8];

endmodule

Figure 9.5.1 - Module ‘Prbsgen’, a 9-bit Pseudo-Random Bit Sequence Generator

module patdet(clock, reset, test_pattern,


pattern, match);

input clock, reset, pattern;


output test_pattern, match;

patdetfsm detector(clock, reset, pattern, match);

prbsgen stimulus(test_pattern, clock, reset);


endmodule

Figure 9.5.11- Module ‘Patdet’ instantiating ‘Patdetfsm’ and ‘Prbsgen’

`timescale 1 ns/1 ns
module patbench;

wire pattern, match;


reg clock, reset;

patdet dut(clock, reset, pattern, pattern, match);

initial
forever
begin
#50 clock = 1;
#50 clock = 0;

end
initial
begin
reset = 1'b1;
#150 reset = 1'b0;
#60000 $stop;

end
endmodule

Figure 9.5.12- Top-level module ‘Patbench’ containing ‘Patdet’ and behavioural stimulus.

Figure 9.5.13 – Simulation waveforms for top-level test-bench ‘Patbench’ (the two occurrences of ‘match’ going high can clearly be seen)

Figure 9.5.14 – Zoomed view of ‘Patbench’ simulation results showing detection of the two occurrences of the ‘01111110’
pattern

in which digital hardware can be described using Verilog.

Skim Chapters 8 and 9 of Cilletti and/or skim Chapter 14 of Palnitkar

9.6 Simulation using Cadence Verilog-XL

These brief instructions outline the procedure for performing a simulation of a simple Verilog-HDL behavioural design.

Listings of the two Verilog-HDL source files used in the exercise are provided at the end of this document. These files have to
be created using a text editor, and should be located in a suitable Verilog project directory.

You can create these files from scratch or you can copy them from '/staff/examples/verilog/' into your own Verilog project
directory.

Start a Unix session and open a terminal window from the CDE in the usual way.

Create a new directory to hold your Verilog project (doesn't necessarily have to be named 'verilog'):

$ mkdir verilog

Move to this new directory:


$ cd verilog

Create a new directory ring4 and move to this directory.

Copy two Verilog source files 'ring4.v' and 'ring4_test.v' from:

/staff/examples/verilog/ring4/

Start using Verilog-XL by entering the following command:

$cadence -verilog ring4.v ring4_test.v

This will open the Cadence Verilog-XL window and compile the two source files. If there are any syntax errors, these will be
reported in the lower half of the window and the upper half of the window will be blank to indicate that the simulator has not
loaded the design.

If you have syntax errors, edit the Verilog source file and correct the error(s) before executing the following Verilog-XL
command:

File -> Reinvoke

This will refresh the Verilog-XL application and re-compile the source files. (This can be repeated until all errors are removed).

Assuming no syntax errors, the Verilog-XL window should appear as shown below.

In the main menu at the top of the window, execute the command:

Control -> Reset Simulation


This will roll back the simulator to zero time.

In the upper half of the window you will see the Verilog source text for the rest-bench module. Click on the reg object named
'clock' and, while holding down the 'Ctrl' key, select objects 'reset' and 'count'. They should all be highlighted.

Execute the main menu command:

Tools -> Waveform…

With three signals still selected, you can also invoke a Watch window by executing:

Tools -> Watch Objects -> New View…

This will open a Watch window showing the values of the selected objects as the simulation proceeds.

The above commands open the two windows shown below.

Now run the simulation by executing the main menu command:

Control -> Run

Both the Watch and Signalscan windows will update to show the results of the simulation; (Watch shows the current values of
signals at time 1800).

Go to the Signalscan window and select the signal named 'count[3:0]' in the left-hand portion of the window, then click on the
button named 'Expand' to reveal the four individual bus waveforms.

The resulting waveforms (shown below) clearly confirm the operation of a 4-bit Ring Counter.
If desired, the simulation can be reset to time zero by executing the command:

Control -> Reset Simulation

The simulation can then be run in bursts by typing in the following Verilog-HDL statement at the Verilog-XL command prompt:

> #100 $stop; <Enter>

Then click on the Run button:

Having completed the exercise, close all applications in the usual manner.

Verilog-HDL Source File Listings

Listing 1: contents of the file 'ring4.v'

module ring4(clock, reset, count);

input clock, reset;


output [3:0] count;

reg [3:0] count;

always @(posedge clock or posedge reset)


begin
if (reset)
count = 4'b0001;
else
count = {count[2:0], count[3]};

end

endmodule
Listing 2: contents of the file 'ring4_test.v'

module ring4_test;

reg clock, reset;

wire [3:0] count;

initial
begin
clock = 1'b0;
forever
#50 clock = ~clock;

end

initial
begin
reset = 1'b1;
#200 reset = 1'b0;
#1600 $stop;
end

ring4 dut(clock, reset, count);

endmodule
Advanced Electronic Design Automation
Examples of VHDL Descriptions

This file contains a selection of VHDL source files which serve to illustrate the diversity and power of the language when used to describe various types of hardware. The examples range from simple combinational logic, described in
terms of basic logic gates, to more complex systems, such as a behavioural model of a microprocessor and associated memory. All of the examples can be simulated using any IEEE compliant VHDL simulator and many can be
synthesised using current synthesis tools.

Use the hierarchical links below to navigate your way through the examples:

● Combinational Logic
● Counters
● Shift Registers
● Memory
● State Machines
● Registers
● Systems
● ADC and DAC
● Arithmetic

Combinational Logic

● Exclusive-OR Gate (Dataflow style)


● Exclusive-OR Gate (Behavioural style)
● Exclusive-OR Gate (Structural style)
● Miscell aneous Logic Gates
● Three-input Majority Voter
● Magnitude Comparator
● Quad 2-input Nand (74x00)
● BCD to Seven Segment Decoder
● Dual 2-to-4 Decoder
● Octal Bus Transceiver
● Quad 2-input OR
● 8-bit Identity Comparator
● Hamming Encoder
● Hamming Decoder
● 2-to-4 Decoder with Testbench and Configuration
● Multiplexer 16-to-4 using Selected Signal Assignment Statement
● Multiplexer 16-to-4 using Conditional Signal Assignment Statement
● Multiplexer 16-to-4 using if-then-elsif-else Statement
● M68008 Address Decoder
● Highest Priority Encoder
● N-input AND Gate

Counters

● Counter using a Conversion Function


● Generated Binary Up Counter
● Counter using Multiple Wait Statements
● Synchronous Down Counter with Parallel Load
● Mod-16 Counter using JK Flip-flops
● Pseudo Random Bit Sequence Generator
● Universal Counter/Register
● n-Bit Synchronous Counter

Shift Registers

● Universal Shift Register/Counter


● TTL164 Shift Register
● Behavioural description of an 8-bit Shift Register
● Structural Description of an 8-bit Shift Register

Memory

● ROM-based Waveform Generator


● A First-in First-out Memory
● Behavioural model of a 16-word, 8-bit Random Access Memory
● Behavioural model of a 256-word, 8-bit Read Only Memory

State Machines

● Classic 2-Process State Machine and Test Bench


● State Machine using Variable
● State Machine with Asynchronous Reset
● Pattern Detector FSM with Test Bench
● State Machine with Moore and Mealy outputs
● Moore State Machine with Explicit State encoding
● Mealy State Machine with Registered Outputs
● Moore State Machine with Concurrent Output Logic

Systems

● Pelican Crossing Controller


● Simple Microprocessor System
● Booth Multiplier
● Lottery Number Generator
● Digital Delay Unit
● Chess Clock

ADC and DAC

● Package defining a Basic Analogue type


● 16-bit Analogue to Digital Converter
● 16-bit Digital to Analogue Converter
● 8-bit Analogue to Digital Converter
● 8-bit Unipolar Successive Approximation ADC

Arithmetic

● 8-bit Unsigned Multiplier


● n-bit Adder using the Generate Statement
● A Variety of Adder Styles
● Booth Multiplier

Registers

● Universal Register
● Octal D-Type Register with 3-State Outputs
● Quad D-Type Flip-flop
● 8-bit Register with Synchronous Load and Clear

Universal Register

Description - This design is a universal register which can be used as a straightforward storage register, a bi-directional shift register, an up counter and a down counter. The register can be loaded from a set of parallel data inputs
and the mode is controlled by a 3-bit input. The 'termcnt' (terminal count) output goes high when the register contains zero.

LIBRARY ieee;
USE ieee.Std_logic_1164.ALL;
USE ieee.Std_logic_unsigned.ALL;
ENTITY unicntr IS
GENERIC(n : Positive := 8); --size of counter/shifter
PORT(clock, serinl, serinr : IN Std_logic; --serial inputs
mode : IN Std_logic_vector(2 DOWNTO 0); --mode control
datain : IN Std_logic_vector((n-1) DOWNTO 0); --parallel inputs
dataout : OUT Std_logic_vector((n-1) DOWNTO 0); --parallel outputs
termcnt : OUT Std_logic); --terminal count output
END unicntr;

ARCHITECTURE v1 OF unicntr IS
SIGNAL int_reg : Std_logic_vector((n-1) DOWNTO 0);

BEGIN
main_proc : PROCESS
BEGIN
WAIT UNTIL rising_edge(clock);
CASE mode IS
--reset
WHEN "000" => int_reg <= (OTHERS => '0');
--parallel load
WHEN "001" => int_reg <= datain;
--count up
WHEN "010" => int_reg <= int_reg + 1;
--count down
WHEN "011" => int_reg <= int_reg - 1;
--shift left
WHEN "100" => int_reg <= int_reg((n-2) DOWNTO 0) & serinl;
--shift right
WHEN "101" => int_reg <= serinr & int_reg((n-1) DOWNTO 1);
--do nothing
WHEN OTHERS => NULL;
END CASE;
END PROCESS;

det_zero : PROCESS(int_reg) --detects when count is 0


BEGIN
termcnt <= '1';
FOR i IN int_reg'Range LOOP
IF int_reg(i) = '1' THEN
termcnt <= '0';
EXIT;
END IF;
END LOOP;
END PROCESS;

--connect internal register to dataout port


dataout <= int_reg;
END v1;

Octal D-Type Register with 3-State Outputs


Simple model of an Octal D-type register with three-state outputs using two concurrent statements.

LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
ENTITY ttl374 IS
PORT(clock, oebar : IN std_logic;
data : IN std_logic_vector(7 DOWNTO 0);
qout : OUT std_logic_vector(7 DOWNTO 0));
END ENTITY ttl374;

ARCHITECTURE using_1164 OF ttl374 IS


--internal flip-flop outputs
SIGNAL qint : std_logic_vector(7 DOWNTO 0);
BEGIN
qint <= data WHEN rising_edge(clock); --d-type flip flops
qout <= qint WHEN oebar = '0' ELSE "ZZZZZZZZ"; --three-state buffers
END ARCHITECTURE using_1164;

Exclusive-OR Gate (Dataflow style)

-- 2 input exclusive or
-- Modeled at the RTL level.

entity x_or is
port (
in1 : in bit ;
in2 : in bit ;
out1 : out bit);
end x_or;

architecture rtl of x_or is


begin
out1 <= in1 xor in2 after 10 ns;
end rtl;

Exclusive-OR Gate (Behavioural style)

-- Exclusive or gate
-- modeled at the behavioral level.

entity x_or is
port (
in1 : in bit ;
in2 : in bit ;
out1 : out bit) ;
end x_or;

architecture behavior of x_or is


begin
process(in1, in2)
begin
if in1 = in2 then
out1 <= '0' after 10 ns;
else out1 <= '1' after 10 ns;
end if;
end process;
end behavior;

Exclusive-OR Gate (Structural style)

-- 2 input exclusive-or gate.


-- Modeled at the structural level.

entity x_or is
port (
in1 : in bit ;
in2 : in bit ;
out1 : out bit) ;
end x_or;

entity and_gate is
port (
a : in bit ;
b : in bit ;
c : out bit) ;
end and_gate;

architecture behavior of and_gate is


begin
process(a,b)
begin
c <= a and b after 5 ns;
end process;
end behavior;

entity or_gate is
port (
d : in bit ;
e : in bit ;
f : out bit) ;
end or_gate;

architecture behavior of or_gate is


begin
process(d,e)
begin
f <= d or e after 4 ns;
end process;
end behavior;

entity inverter is
port (
g : in bit ;
h : out bit) ;
end inverter;

architecture behavior of inverter is


begin
process(g)
begin
h <= not g after 3 ns;
end process;
end behavior;

architecture structural of x_or is


-- signal declarations
signal t1, t2, t3, t4 : bit;

-- local component declarations

component and_gate
port (a, b : in bit; c : out bit) ;
end component;

component or_gate
port (d, e : in bit; f : out bit) ;
end component;

component inverter
port (g : in bit; h : out bit) ;
end component;
begin

-- component instantiation statements


u0: and_gate port map ( a => t1, b => in2, c => t3);

u1: and_gate port map ( a => in1, b => t2, c => t4);

u2: inverter port map ( g => in1, h => t1);

u3: inverter port map ( g => in2, h => t2);

u4: or_gate port map ( d => t3, e => t4, f => out1);

end structural;

Three-input Majority Voter

The entity declaration is followed by three alternative architectures which achieve the same functionality in different ways.

ENTITY maj IS
PORT(a,b,c : IN BIT; m : OUT BIT);
END maj;

--Dataflow style architecture


ARCHITECTURE concurrent OF maj IS
BEGIN
--selected signal assignment statement (concurrent)
WITH a&b&c SELECT
m <= '1' WHEN "110"|"101"|"011"|"111",'0' WHEN OTHERS;
END concurrent;

--Structural style architecture


ARCHITECTURE structure OF maj IS

--declare components used in architecture


COMPONENT and2 PORT(in1, in2 : IN BIT; out1 : OUT BIT);
END COMPONENT;
COMPONENT or3 PORT(in1, in2, in3 : IN BIT; out1 : OUT BIT);
END COMPONENT;
--declare local signals
SIGNAL w1, w2, w3 : BIT;

BEGIN
--component instantiation statements.
--ports of component are mapped to signals
--within architecture by position.
gate1 : and2 PORT MAP (a, b, w1);
gate2 : and2 PORT MAP (b, c, w2);
gate3 : and2 PORT MAP (a, c, w3);
gate4 : or3 PORT MAP (w1, w2, w3, m);
END structure;

--Behavioural style architecture using a look-up table


ARCHITECTURE using_table OF maj IS
BEGIN
PROCESS(a,b,c)
CONSTANT lookuptable : BIT_VECTOR(0 TO 7) := "00010111";
VARIABLE index : NATURAL;
BEGIN
index := 0; --index must be cleared each time process executes
IF a = '1' THEN index := index + 1; END IF;
IF b = '1' THEN index := index + 2; END IF;
IF c = '1' THEN index := index + 4; END IF;
m <= lookuptable(index);
END PROCESS;
END using_table;

Magnitude Comparator

--VHDL description of a 4-bit magnitude comparator with expansion inputs


--first architecture demonstrates use of relational operators on
--bit vectors (=,>,<).Second architecture shows sequential behaviour
--description.Both descriptions do not fully model behaviour of real
--device for all possible combinations of inputs.

ENTITY mag4comp IS
GENERIC(eqdel,gtdel,ltdel : TIME := 10 ns); --output delay parameters
PORT(a,b : IN BIT_VECTOR(3 DOWNTO 0); --input words, DOWNTO ordering needed for
comparison operators
aeqbin,agtbin,altbin : IN BIT; --expansion inputs
aeqbout,agtbout,altbout : OUT BIT); --outputs
END mag4comp;

ARCHITECTURE dataflow OF mag4comp IS


--this architecture assumes that only one of the expansion inputs
--is active at any time,if more than one expansion input is active,
--more than one output may be active.
BEGIN
aeqbout <= '1' AFTER eqdel WHEN ((a = b) AND (aeqbin = '1'))
ELSE '0' AFTER eqdel;
agtbout <= '1' AFTER gtdel WHEN ((a > b) OR ((a = b) AND (agtbin = '1')))
ELSE '0' AFTER gtdel;
altbout <= '1' AFTER ltdel WHEN ((a < b) OR ((a = b) AND (altbin = '1')))
ELSE '0' AFTER ltdel;
END dataflow;

ARCHITECTURE behaviour OF mag4comp IS


BEGIN
PROCESS(a,b,aeqbin,agtbin,altbin)
BEGIN
IF (a > b) THEN
agtbout <= '1' AFTER gtdel;
aeqbout <= '0' AFTER eqdel;
altbout <= '0' AFTER ltdel;
ELSIF (a < b) THEN
altbout <= '1' AFTER ltdel;
aeqbout <= '0' AFTER eqdel;
agtbout <= '0' AFTER gtdel;
ELSE --a=b,expansion inputs have priority ordering
IF (aeqbin = '1') THEN
aeqbout <= '1' AFTER eqdel;
agtbout <= '0' AFTER gtdel;
altbout <= '0' AFTER ltdel;
ELSIF (agtbin = '1') THEN
agtbout <= '1' AFTER gtdel;
altbout <= '0' AFTER ltdel;
aeqbout <= '0' AFTER eqdel;
ELSIF (altbin = '1') THEN
agtbout <= '0' AFTER gtdel;
altbout <= '1' AFTER ltdel;
aeqbout <= '0' AFTER eqdel;
ELSE
agtbout <= '0' AFTER gtdel;
altbout <= '0' AFTER ltdel;
aeqbout <= '0' AFTER eqdel;
END IF;
END IF;
END PROCESS;
END behaviour;

8-bit Register with Synchronous Load and Clear

The design entity shows the standard way of describing a register using a synchronous process, ie. a process containing a single wait statement which is triggered by a rising edge on the clock input.

library ieee;
use ieee.std_logic_1164.all;
entity reg8 is
port(clock, clear, load : in std_logic;
d : in std_logic_vector(7 downto 0);
q : out std_logic_vector(7 downto 0));
end entity reg8;

architecture v1 of reg8 is
begin
reg_proc : process
begin
wait until rising_edge(clock);
if clear = '1' then
q <= (others => '0');
elsif load = '1' then
q <= d;
end if;
end process;
end architecture v1;

BCD to Seven Segment Decoder

The use of the std_logic literal '-' (don't care) is primarily for the synthesis tool. This example illustrates the use of the selected signal assignment.

LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
ENTITY seg7dec IS
PORT(bcdin : IN std_logic_vector(3 DOWNTO 0);
segout : OUT std_logic_vector(6 DOWNTO 0));
END seg7dec;

ARCHITECTURE ver3 OF seg7dec IS


BEGIN
WITH bcdin SELECT
segout <= "1000000" WHEN X"0",
"1100111" WHEN X"1",

"1101101" WHEN X"2",


"0000011" WHEN X"3",
"0100101" WHEN X"4",
"0001001" WHEN X"5",
"0001000" WHEN X"6",
"1100011" WHEN X"7",
"0000000" WHEN X"8",
"0000001" WHEN X"9",
"-------" WHEN OTHERS;
END ver3;

2-to-4 Decoder with Testbench and Configuration

This set of design units illustrates several features of the VHDL language including:

● Using generics to pass time delay values to design entities.


● Design hierarchy using instantiated components.
● Test benches for design verification.
● Configuration declaration for binding components to design entities and setting delay values.

--ANATOMY OF A VHDL MODEL


--This VHDL source description illustrates the use
--of the basic constructs of VHDL.
--The model describes a 2-input/4-output decoder
--comprising two behavioural primitives 'inv' and 'and3'
--instanced in a structure.

--------------------------------------------------------------
ENTITY inv IS
GENERIC(tplh,tphl,tplhe,tphle : TIME := 1 ns);
PORT(a : IN BIT; b : OUT BIT);
END inv;

ARCHITECTURE behaviour OF inv IS


BEGIN
PROCESS(a)
VARIABLE state : BIT;
BEGIN
state := NOT(a);
IF state = '1' THEN
b <= state AFTER (tplh + tplhe);
ELSE
b <= state AFTER (tphl + tphle);
END IF;
END PROCESS;
END behaviour;
---------------------------------------------------------------
ENTITY and3 IS
GENERIC(tplh,tphl,tplhe,tphle : TIME := 1 ns);
PORT(a1,a2,a3 : IN BIT; o1 : OUT BIT);
END and3;

ARCHITECTURE behaviour OF and3 IS


BEGIN
PROCESS(a1,a2,a3)
VARIABLE state : BIT;
BEGIN
state := a1 AND a2 AND a3;
IF state = '1' THEN
o1 <= state AFTER (tplh + tplhe);
ELSE
o1 <= state AFTER (tphl + tphle);
END IF;
END PROCESS;
END behaviour;
---------------------------------------------------------------
ENTITY dec2to4 IS
PORT(s0,s1,en : IN BIT; y0,y1,y2,y3 : OUT BIT);
END dec2to4;

ARCHITECTURE structural OF dec2to4 IS

COMPONENT inv
PORT(a : IN BIT; b : OUT BIT); END COMPONENT;
COMPONENT and3
PORT(a1,a2,a3 : IN BIT; o1 : OUT BIT); END COMPONENT;

SIGNAL ns0,ns1 : BIT;

BEGIN

i1 : inv PORT MAP(s0,ns0);


i2 : inv PORT MAP(s1,ns1);

a1 : and3 PORT MAP(en,ns0,ns1,y0);


a2 : and3 PORT MAP(en,s0,ns1,y1);
a3 : and3 PORT MAP(en,ns0,s1,y2);
a4 : and3 PORT MAP(en,s0,s1,y3);

END structural;
-------------------------------------------------------------------
ENTITY dec2to4_stim IS
PORT(stimulus : OUT BIT_VECTOR(0 TO 2); response : IN BIT_VECTOR(0 TO 3));
END dec2to4_stim;

ARCHITECTURE behavioural OF dec2to4_stim IS


BEGIN
stimulus <= TRANSPORT "000" AFTER 0 ns,
"100" AFTER 100 ns,
"010" AFTER 200 ns,
"110" AFTER 300 ns,
"001" AFTER 400 ns,
"101" AFTER 500 ns,
"011" AFTER 600 ns,
"111" AFTER 700 ns;
END behavioural;
--------------------------------------------------------------------
ENTITY dec2to4_bench IS
END dec2to4_bench;

ARCHITECTURE structural OF dec2to4_bench IS

COMPONENT dec2to4
PORT(s0,s1,en : IN BIT; y0,y1,y2,y3 : OUT BIT);
END COMPONENT;

COMPONENT dec2to4_stim
PORT(stimulus : OUT BIT_VECTOR(0 TO 2); response : IN BIT_VECTOR(0 TO 3));
END COMPONENT;

SIGNAL stimulus : BIT_VECTOR(0 TO 2);


SIGNAL response : BIT_VECTOR(0 TO 3);

BEGIN

generator : dec2to4_stim PORT MAP(stimulus,response);


circuit : dec2to4 PORT MAP(stimulus(1),stimulus(2),stimulus(0),
response(0),response(1),response(2),response(3));

END structural;
---------------------------------------------------------------
CONFIGURATION parts OF dec2to4_bench IS
FOR structural

FOR generator : dec2to4_stim


USE ENTITY work.dec2to4_stim(behavioural);
END FOR;

FOR circuit : dec2to4


USE ENTITY work.dec2to4(structural);
FOR structural
FOR ALL : inv
USE ENTITY work.inv(behaviour)
GENERIC MAP(tplh => 10 ns,
tphl => 7 ns,
tplhe => 15 ns,
tphle => 12 ns);
END FOR;
FOR ALL : and3
USE ENTITY work.and3(behaviour)
GENERIC MAP(tplh => 8 ns,
tphl => 5 ns,
tplhe => 20 ns,
tphle => 15 ns);
END FOR;
END FOR;
END FOR;

END FOR;
END parts;

Generated Binary Up Counter

The first design entity is a T-type flip-flop. The second is an scalable synchronous binary up counter illustrating the use of the generate statement to produce regular structures of components.

library ieee;
use ieee.std_logic_1164.all;
entity tff is
port(clk, t, clear : in std_logic; q : buffer std_logic);
end tff;

architecture v1 of tff is
begin
process(clear, clk)
begin
if clear = '1' then
q <= '0';
elsif rising_edge(clk) then
if t = '1' then
q <= not q;
else
null;
end if;
end if;
end process;
end v1;

library ieee;
use ieee.std_logic_1164.all;
entity bigcntr is
generic(size : positive := 32);
port(clk, clear : in std_logic;
q : buffer std_logic_vector((size-1) downto 0));
end bigcntr;

architecture v1 of bigcntr is

component tff is
port(clk, t, clear : in std_logic; q : buffer std_logic);
end component;

signal tin : std_logic_vector((size-1) downto 0);

begin

genttf : for i in (size-1) downto 0 generate


ttype : tff port map (clk, tin(i), clear, q(i));
end generate;

genand : for i in 0 to (size-1) generate


t0 : if i = 0 generate
tin(i) <= '1';
end generate;
t1_size : if i > 0 generate
tin(i) <= q(i-1) and tin(i-1);
end generate;
end generate;

end v1;

Counter using Multiple Wait Statements

This example shows an inefficient way of describing a counter.

--vhdl model of a 3-state counter illustrating the use


--of the WAIT statement to suspend a process.At each wait
--statement the simulation time is updated one cycle,transferring
--the driver value to the output count.
--This architecture shows that there is no difference between
--WAIT UNTIL (clock'EVENT AND clock = '1') and WAIT UNTIL clock = '1'

ENTITY cntr3 IS
PORT(clock : IN BIT; count : OUT NATURAL);
END cntr3;

ARCHITECTURE using_wait OF cntr3 IS


BEGIN
PROCESS
BEGIN
--WAIT UNTIL (clock'EVENT AND clock = '1');
WAIT UNTIL clock = '1';
count <= 0;
--WAIT UNTIL (clock'EVENT AND clock = '1');
WAIT UNTIL clock = '1';
count <= 1;
--WAIT UNTIL (clock'EVENT AND clock = '1');
WAIT UNTIL clock = '1';
count <= 2;
END PROCESS;
END using_wait;

Counter using a Conversion Function


This counter uses a natural number to hold the count value and converts it into a bit_vector for output. Illustrates the use of a function.

--4-bit binary up counter with asynchronous reset 2/2/93

ENTITY cntr4bit IS
PORT(reset,clock : IN BIT; count : OUT BIT_VECTOR(0 TO 3));
END cntr4bit;

ARCHITECTURE dataflow OF cntr4bit IS

--interface function to generate output bit_vector from


--internal count value.
FUNCTION nat_to_bv(input : NATURAL; highbit : POSITIVE)
RETURN BIT_VECTOR IS
VARIABLE temp : NATURAL := 0;
VARIABLE output : BIT_VECTOR(0 TO highbit);
BEGIN
temp := input;
--check that input fits into (highbit+1) bits
ASSERT (temp <= (2**(highbit + 1) - 1))
REPORT "input no. is out of range" SEVERITY ERROR;
--generate bit values
FOR i IN highbit DOWNTO 0 LOOP
IF temp >= (2**i)
THEN output(i) := '1';
temp := temp - (2**i);
ELSE output(i) := '0';
END IF;
END LOOP;
RETURN output;
END nat_to_bv;

--signal to hold current count value


SIGNAL intcount : NATURAL := 0;

BEGIN
--conditional natural signal assignment models counter
intcount <= 0 WHEN (reset = '1') ELSE
((intcount + 1) MOD 16) WHEN (clock'EVENT AND clock = '1')
ELSE intcount;
--interface function converts natural count to bit_vector count
count <= nat_to_bv(intcount,3);
END;

Quad 2-input Nand

Simple concurrent model of a TTL quad nand gate.

--uses 1993 std VHDL


library IEEE;
use IEEE.Std_logic_1164.all;
entity HCT00 is
port(A1, B1, A2, B2, A3, B3, A4, B4 : in std_logic;
Y1, Y2, Y3, Y4 : out std_logic);
end HCT00;

architecture VER1 of HCT00 is


begin
Y1 <= A1 nand B1 after 10 ns;
Y2 <= A2 nand B2 after 10 ns;
Y3 <= A3 nand B3 after 10 ns;
Y4 <= A4 nand B4 after 10 ns;
end VER1;

Dual 2-to-4 Decoder

A set of conditional signal assignments model a dual 2-to-4 decoder

--uses 1993 std VHDL


library IEEE;
use IEEE.Std_logic_1164.all;
entity HCT139 is
port(A2, B2, G2BAR, A1, B1, G1BAR : in std_logic;
Y20, Y21, Y22, Y23, Y10, Y11, Y12, Y13 : out std_logic);
end HCT139;

architecture VER1 of HCT139 is


begin
Y10 <= '0' when (B1 = '0') and ((A1 = '0') and (G1BAR = '0')) else '1';
Y11 <= '0' when (B1 = '0') and ((A1 = '1') and (G1BAR = '0')) else '1';
Y12 <= '0' when (B1 = '1') and ((A1 = '0') and (G1BAR = '0')) else '1';
Y13 <= '0' when (B1 = '1') and ((A1 = '1') and (G1BAR = '0')) else '1';
Y20 <= '0' when (B2 = '0') and ((A2 = '0') and (G2BAR = '0')) else '1';
Y21 <= '0' when (B2 = '0') and ((A2 = '1') and (G2BAR = '0')) else '1';
Y22 <= '0' when (B2 = '1') and ((A2 = '0') and (G2BAR = '0')) else '1';
Y23 <= '0' when (B2 = '1') and ((A2 = '1') and (G2BAR = '0')) else '1';
end VER1;

Quad D-Type Flip-flop

This example shows how a conditional signal assignment statement could be used to describe sequential logic (it is more common to use a process). The keyword 'unaffected' is equivalent to the 'null' statement in the sequential part
of the language. The model would work exactly the same without the clause 'else unaffected' attached to the end of the statement.

--uses 1993 std VHDL


library IEEE;
use IEEE.Std_logic_1164.all;
entity HCT175 is
port(D : in std_logic_vector(3 downto 0);
Q : out std_logic_vector(3 downto 0);
CLRBAR, CLK : in std_logic);
end HCT175;

architecture VER1 of HCT175 is


begin
Q <= (others => '0') when (CLRBAR = '0')
else D when rising_edge(CLK)
else unaffected;
end VER1;

Octal Bus Transceiver

This example shows the use of the high impedance literal 'Z' provided by std_logic. The aggregate '(others => 'Z')' means all of the bits of B must be forced to 'Z'. Ports A and B must be resolved for this model to work correctly (hence std_logic rather than
std_ulogic).

library IEEE;
use IEEE.Std_logic_1164.all;
entity HCT245 is
port(A, B : inout std_logic_vector(7 downto 0);
DIR, GBAR : in std_logic);
end HCT245;

architecture VER1 of HCT245 is


begin
A <= B when (GBAR = '0') and (DIR = '0') else (others => 'Z');
B <= A when (GBAR = '0') and (DIR = '1') else (others => 'Z');
end VER1;

Quad 2-input OR

--uses 1993 std VHDL


library IEEE;
use IEEE.Std_logic_1164.all;
entity HCT32 is
port(A1, B1, A2, B2, A3, B3, A4, B4 : in std_logic;
Y1, Y2, Y3, Y4 : out std_logic);
end HCT32;

architecture VER1 of HCT32 is


begin
Y1 <= A1 or B1 after 10 ns;
Y2 <= A2 or B2 after 10 ns;
Y3 <= A3 or B3 after 10 ns;
Y4 <= A4 or B4 after 10 ns;
end VER1;

8-bit Identity Comparator

--uses 1993 std VHDL


library IEEE;
use IEEE.Std_logic_1164.all;
entity HCT688 is
port(Q, P : in std_logic_vector(7 downto 0);
GBAR : in std_logic; PEQ : out std_logic);
end HCT688;

architecture VER1 of HCT688 is


begin
PEQ <= '0' when ((To_X01(P) = To_X01(Q)) and (GBAR = '0')) else '1';
end VER1;

Hamming Encoder

A 4-bit Hamming Code encoder using concurrent assignments. The output vector is connected to the individual parity bits using an aggregate assignment.

ENTITY hamenc IS
PORT(datain : IN BIT_VECTOR(0 TO 3); --d0 d1 d2 d3
hamout : OUT BIT_VECTOR(0 TO 7)); --d0 d1 d2 d3 p0 p1 p2 p4
END hamenc;

ARCHITECTURE ver2 OF hamenc IS

SIGNAL p0, p1, p2, p4 : BIT; --check bits

BEGIN

--generate check bits


p0 <= (datain(0) XOR datain(1)) XOR datain(2);
p1 <= (datain(0) XOR datain(1)) XOR datain(3);
p2 <= (datain(0) XOR datain(2)) XOR datain(3);
p4 <= (datain(1) XOR datain(2)) XOR datain(3);

--connect up outputs
hamout(4 TO 7) <= (p0, p1, p2, p4);
hamout(0 TO 3) <= datain(0 TO 3);

END ver2;

Hamming Decoder

This Hamming decoder accepts an 8-bit Hamming code (produced by the encoder above) and performs single error correction and double error detection.

ENTITY hamdec IS
PORT(hamin : IN BIT_VECTOR(0 TO 7); --d0 d1 d2 d3 p0 p1 p2 p4
dataout : OUT BIT_VECTOR(0 TO 3); --d0 d1 d2 d3
sec, ded, ne : OUT BIT); --diagnostic outputs
END hamdec;

ARCHITECTURE ver1 OF hamdec IS

BEGIN

PROCESS(hamin)
VARIABLE syndrome : BIT_VECTOR(3 DOWNTO 0);
BEGIN
--generate syndrome bits
syndrome(0) := (((((((hamin(0) XOR hamin(1)) XOR hamin(2)) XOR hamin(3))
XOR hamin(4)) XOR hamin(5)) XOR hamin(6)) XOR hamin(7));
syndrome(1) := (((hamin(0) XOR hamin(1)) XOR hamin(3)) XOR hamin(5));
syndrome(2) := (((hamin(0) XOR hamin(2)) XOR hamin(3)) XOR hamin(6));
syndrome(3) := (((hamin(1) XOR hamin(2)) XOR hamin(3)) XOR hamin(7));
IF (syndrome = "0000") THEN --no errors
ne <= '1';
ded <= '0';
sec <= '0';
dataout(0 TO 3) <= hamin(0 TO 3);
ELSIF (syndrome(0) = '1') THEN --single bit error
ne <= '0';
ded <= '0';
sec <= '1';

CASE syndrome(3 DOWNTO 1) IS


WHEN "000"|"001"|"010"|"100" =>
dataout(0 TO 3) <= hamin(0 TO 3); -- parity errors

WHEN "011" => dataout(0) <= NOT hamin(0);


dataout(1 TO 3) <= hamin(1 TO 3);

WHEN "101" => dataout(1) <= NOT hamin(1);


dataout(0) <= hamin(0);
dataout(2 TO 3) <= hamin(2 TO 3);

WHEN "110" => dataout(2) <= NOT hamin(2);


dataout(3) <= hamin(3);
dataout(0 TO 1) <= hamin(0 TO 1);

WHEN "111" => dataout(3) <= NOT hamin(3);


dataout(0 TO 2) <= hamin(0 TO 2);
END CASE;
--double error
ELSIF (syndrome(0) = '0') AND (syndrome(3 DOWNTO 1) /= "000") THEN
ne <= '0';
ded <= '1';
sec <= '0';
dataout(0 TO 3) <= "0000";

END IF;

END PROCESS;

END ver1;

Synchronous Down Counter with Parallel Load

This example shows the use of the package 'std_logic_unsigned' . The minus operator '-' is overloaded by this package, thereby allowing an integer to be subracted from a std_logic_vector.

LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE ieee.std_logic_unsigned.ALL;
ENTITY pldcntr8 IS
PORT (clk, load : IN Std_logic;
datain : IN Std_logic_vector(7 DOWNTO 0);
q : OUT Std_logic_vector(7 DOWNTO 0);
tc : OUT Std_logic);
END pldcntr8;

ARCHITECTURE using_std_logic OF pldcntr8 IS

SIGNAL count : Std_logic_vector(7 DOWNTO 0);

BEGIN

PROCESS
BEGIN
WAIT UNTIL rising_edge(clk);
IF load = '1' THEN
count <= datain;
ELSE
count <= count - 1;
END IF;
END PROCESS;

tc <= '1' WHEN count = "00000000" ELSE '0';


q <= count;

END using_std_logic;

Mod-16 Counter using JK Flip-flops

Structural description of a 4-bit binary counter. The first two design entities describe a JK flip-flop and a 2-input AND gate respectively. These are then packaged together along with a signal named 'tied_high' into a package named
'jkpack'. The counter design uses the package 'jkpack', giving it access to the components and the signal declared within the package. The flip-flops and AND-gates are wired together to form a counter. Notice the use of the keyword
OPEN to indicate an open-cct output port.

ENTITY jkff IS
PORT(clock, j, k : IN BIT; q, qbar : BUFFER BIT);
END jkff;

ARCHITECTURE using_process OF jkff IS


BEGIN

--sequential process to model JK flip-flop


PROCESS

--declare a local variable to hold ff state


VARIABLE state : BIT := '0';

BEGIN

--synchronise process to rising edge of clock


WAIT UNTIL (clock'EVENT AND clock = '1');

IF (j = '1' AND k = '1') THEN --toggle


state := NOT state;
ELSIF (j = '0' AND k = '1') THEN --reset
state := '0';
ELSIF (j = '1' AND k = '0') THEN --set
state := '1';
ELSE --no change
state := state;
END IF;

--assign values to output signals


q <= state AFTER 5 ns;
qbar <= NOT state AFTER 5 ns;

END PROCESS;

END using_process;

ENTITY and_gate IS
PORT(a, b : IN BIT; f : OUT BIT);
END and_gate;
ARCHITECTURE simple OF and_gate IS
BEGIN
f <= a AND b AFTER 2 ns;
END simple;

PACKAGE jkpack IS

SIGNAL tied_high : BIT := '1';

COMPONENT jkff
PORT(clock, j, k : IN BIT; q, qbar : BUFFER BIT);
END COMPONENT;

COMPONENT and_gate
PORT(a, b : IN BIT; f : OUT BIT);
END COMPONENT;

END jkpack;

USE work.jkpack.ALL;
ENTITY mod16_cntr IS
PORT(clock : IN BIT; count : BUFFER BIT_VECTOR(0 TO 3));
END mod16_cntr;

ARCHITECTURE net_list OF mod16_cntr IS

SIGNAL s1,s2 : BIT;

BEGIN

a1 : and_gate PORT MAP (count(0),count(1),s1);


a2 : and_gate PORT MAP (s1, count(2), s2);
jk1 : jkff PORT MAP (clock,tied_high,tied_high,count(0),OPEN);
jk2 : jkff PORT MAP (clock,count(0),count(0),count(1),OPEN);
jk3 : jkff PORT MAP (clock,s1,s1,count(2),OPEN);
jk4 : jkff PORT MAP (clock,s2,s2,count(3),OPEN);

END net_list;

Pseudo Random Bit Sequence Generator

This design entity uses a single conditional signal assignment statement to describe a PRBSG register. The length of the register and the two tapping points are defined using generics. The '&' (aggregate) operator is used to form a
vector comprising the shifted contents of the regsiter combined with the XOR feedback which is clocked into the register on the rising edge.

--The following Design Entity defeines a parameterised Pseudo-random


--bit sequence generator, it is useful for generating serial or parallel test waveforms
--(for paralle waveforms you need to add an extra output port)
--The generic 'length' is the length of the register minus one.
--the generics 'tap1' and 'tap2' define the feedabck taps

ENTITY prbsgen IS
GENERIC(length : Positive := 8; tap1 : Positive := 8; tap2 : Positive := 4);
PORT(clk, reset : IN Bit; prbs : OUT Bit);
END prbsgen;

ARCHITECTURE v2 OF prbsgen IS

--create a shift register


SIGNAL prreg : Bit_Vector(length DOWNTO 0);

BEGIN

--conditional signal assignment shifts register and feeds in xor value


prreg <= (0 => '1', OTHERS => '0') WHEN reset = '1' ELSE --set all bits to '0' except lsb
(prreg((length - 1) DOWNTO 0) & (prreg(tap1) XOR prreg(tap2))) --shift left with xor
feedback
WHEN clk'EVENT AND clk = '1'
ELSE prreg;

--connect msb of register to output


prbs <= prreg(length);

END v2;

Pelican Crossing Controller

--Pelican Crossing Controller


library ieee;
use ieee.std_logic_1164.all;
entity pelcross is
port(clock, reset, pedestrian : in std_logic;
red, amber, green : out std_logic); --traffic lights
end pelcross;

architecture v1 of pelcross is

signal en, st, mt, lt, fr : std_logic;

begin

--timer for light sequence


interval_timer : block
constant stime : natural := 50;
constant mtime : natural := 80;
constant ltime : natural := 200;
signal tcount : natural range 0 to ltime;
begin
process begin
wait until rising_edge(clock);
if (en = '0') or (tcount = ltime) then
tcount <= 0;
else
tcount <= tcount + 1;
end if;
end process;
st <= '1' when tcount = stime else '0';
mt <= '1' when tcount = mtime else '0';
lt <= '1' when tcount = ltime else '0';
end block;

--free running timer for amber flashing


free_run : block
constant frtime : natural := 5;
signal frcount : natural range 0 to frtime;
begin
process begin
wait until rising_edge(clock);
if frcount = frtime then
frcount <= 0;
else
frcount <= frcount + 1;
end if;
end process;
fr <= '1' when frcount = frtime else '0';
end block;

--moore state machine to control light sequence


controller : block
type peltype is (res, stop, amb, amb_on, amb_off, grn, ped);
signal pelstate : peltype;
begin
process(clock, reset)
begin
if reset = '1' then
pelstate <= res;
elsif rising_edge(clock) then
case pelstate is
when res => pelstate <= stop;
when stop => if lt = '1' then
pelstate <= amb;
else
pelstate <= stop;
end if;
when amb => pelstate <= amb_on;
when amb_on => if mt = '1' then
pelstate <= grn;
elsif fr = '1' then
pelstate <= amb_off;
else
pelstate <= amb_on;
end if;
when amb_off => if mt = '1' then
pelstate <= grn;
elsif fr = '1' then
pelstate <= amb_on;
else
pelstate <= amb_off;
end if;
when grn => if pedestrian = '1' then
pelstate <= ped;
else
pelstate <= grn;
end if;
when ped => if st = '1' then
pelstate <= res;
else
pelstate <= ped;
end if;
when others => pelstate <= res;
end case;
end if;
end process;
--moore outputs
with pelstate select
en <= '1' when stop|amb_on|amb_off|ped,

'0' when others;


with pelstate select
red <= '1' when res|stop,
'0' when others;
with pelstate select
amber <= '1' when amb|amb_on|ped,
'0' when others;
with pelstate select
green <= '1' when grn,
'0' when others;
end block;

end v1;

--Pelican Crossing Controller test bench


library ieee;
use ieee.std_logic_1164.all;
entity peltest is
end peltest;

architecture v1 of peltest is

signal clock, reset, pedestrian, red, amber, green : std_logic;

component pelcross is
port(clock, reset, pedestrian : in std_logic;
red, amber, green : out std_logic); --traffic lights
end component;

begin

--10 Hz clock generator


process begin
clock <= '0', '1' after 50 ms;
wait for 100 ms;
end process;

--test inputs
process begin
pedestrian <= '0';
reset <= '1';
wait for 300 ms;
reset <= '0';
wait for 40000 ms;
pedestrian <= '1';
wait for 200 ms;
pedestrian <= '0';
wait;
end process;

pelican : pelcross port map (clock, reset, pedestrian,


red, amber, green);

end v1;

Simple Microprocessor System

● Package Defining the Instruction Set of the CPU


● Third Party Package containing functions for Bit_Vector operations
● Behavioural model of a 256-word, 8-bit Read Only Memory
● Behavioural model of a 16-word, 8-bit Random Access Memory
● Behavioural model of a simple 8-bit CPU
● Structural description of a microprocessor system using the above components

Package Defining the Instruction Set of the CPU

PACKAGE cpu8pac IS
--defining instruction set
--instruction format
-- 7----4|3--0|7----------0
-- opcode|page|[page offset]
--instructions which need an address are two bytes
--long all others are single byte
CONSTANT lda : BIT_VECTOR(3 DOWNTO 0) := "0001";
CONSTANT ldb : BIT_VECTOR(3 DOWNTO 0) := "0010";
CONSTANT sta : BIT_VECTOR(3 DOWNTO 0) := "0011";
CONSTANT stb : BIT_VECTOR(3 DOWNTO 0) := "0000";
CONSTANT jmp : BIT_VECTOR(3 DOWNTO 0) := "0100";
CONSTANT add : BIT_VECTOR(3 DOWNTO 0) := "0101";
CONSTANT subr : BIT_VECTOR(3 DOWNTO 0) := "0110";
CONSTANT inc : BIT_VECTOR(3 DOWNTO 0) := "0111";
CONSTANT dec : BIT_VECTOR(3 DOWNTO 0) := "1000";
CONSTANT land : BIT_VECTOR(3 DOWNTO 0) := "1001";
CONSTANT lor : BIT_VECTOR(3 DOWNTO 0) := "1010";
CONSTANT cmp : BIT_VECTOR(3 DOWNTO 0) := "1011";
CONSTANT lxor : BIT_VECTOR(3 DOWNTO 0) := "1100";
CONSTANT lita : BIT_VECTOR(3 DOWNTO 0) := "1101";
CONSTANT litb : BIT_VECTOR(3 DOWNTO 0) := "1110";
CONSTANT clra : BIT_VECTOR(3 DOWNTO 0) := "1111";
END cpu8pac;

Third Party Package containing functions for Bit_Vector operations

-- Cypress Semiconductor WARP 2.0


--
-- Copyright Cypress Semiconductor Corporation, 1994
-- as an unpublished work.
--
-- $Id: libbv.vhd,v 1.4 1994/12/15 18:35:28 hemmert Exp $
--

-- package bv_math
--
-- Bit Vector support package:
--
-- Contains these functions:
-- The output length of the function is the same as the input length.
--
-- inc_bv - increment a bit vector. If function is assigned
-- to a signal within a clocked process, the result
-- will be an up counter. Will require one macrocell
-- for each bit.
--
-- dec_bv - decrement a bit vector. If function is assigned
-- to a signal within a clocked process, the result
-- will be a down counter. Will require one macrocell
-- for each bit.
--
-- "+" - regular addition function for two bit vectors.
-- "+" operator overloads the existing "+" operator
-- definition for arithmetic operations on integers.
-- Will require one macrocell for each bit. The output
-- is the same size as the input so there is no carry output.
-- If a carry out is required, the user should increase the
-- size of the input bit_vectors and use the MSB as the
-- carry bit. There is also no separate carry-in.
--
-- "-" - regular subtraction function for two bit vectors.
-- "-" operator overloads the existing "-" operator
-- definition for arithmetic operations on integers.
--
-- inv - unary invert for use in port maps and sequential
-- assignments. Overloaded for bit_vectors.
--
--
PACKAGE bv_math IS
FUNCTION inc_bv (a : BIT_VECTOR) RETURN BIT_VECTOR;
FUNCTION dec_bv (a : BIT_VECTOR) RETURN BIT_VECTOR;
FUNCTION "+" (a, b : BIT_VECTOR) RETURN BIT_VECTOR;
FUNCTION "+" (a : BIT_VECTOR; b : BIT) RETURN BIT_VECTOR;
FUNCTION "-" (a, b : BIT_VECTOR) RETURN BIT_VECTOR;
FUNCTION "-" (a : BIT_VECTOR; b : BIT) RETURN BIT_VECTOR;
FUNCTION inv (a : BIT) RETURN BIT;
FUNCTION inv (a : BIT_VECTOR) RETURN BIT_VECTOR;
END bv_math;

PACKAGE BODY bv_math IS

-- inc_bv
-- Increment Bit vector.
-- In: bit_vector.
-- Return: bit_vector.
--
FUNCTION inc_bv(a : BIT_VECTOR)RETURN BIT_VECTOR IS
VARIABLE s : BIT_VECTOR (a'RANGE);
VARIABLE carry : BIT;
BEGIN
carry := '1';

FOR i IN a'LOW TO a'HIGH LOOP


s(i) := a(i) XOR carry;
carry := a(i) AND carry;
END LOOP;

RETURN (s);
END inc_bv;

-- "+"
-- Add overload for:
-- In: two bit_vectors.
-- Return: bit_vector.
--
FUNCTION "+"(a, b : BIT_VECTOR)RETURN BIT_VECTOR IS
VARIABLE s : BIT_VECTOR (a'RANGE);
VARIABLE carry : BIT;
VARIABLE bi : integer; -- Indexes b.
BEGIN
ASSERT a'LENGTH <= 8 REPORT
"Addition OF vectors OF LENGTH > 8 may take exponential TIME."
SEVERITY WARNING;

carry := '0';

FOR i IN a'LOW TO a'HIGH LOOP


bi := b'low + (i - a'low);
s(i) := (a(i) XOR b(bi)) XOR carry;
carry := ((a(i) OR b(bi)) AND carry) OR (a(i) AND b(bi));
END LOOP;

RETURN (s);
END "+"; -- Two bit_vectors.

-- "+"
-- Add overload for:
-- In: bit_vector and bit.
-- Return bit_vector.
--
FUNCTION "+"(a : BIT_VECTOR; b : BIT)RETURN BIT_VECTOR IS
VARIABLE s : BIT_VECTOR (a'RANGE);
VARIABLE carry : BIT;
BEGIN
carry := b;

FOR i IN a'LOW TO a'HIGH LOOP


s(i) := a(i) XOR carry;
carry := a(i) AND carry;
END LOOP;

RETURN (s);
END "+"; -- Bit_vector and bit.

-- dec_bv
-- Decrement Bit Vector
-- In: bit_vector.
-- Return: bit_vector.
--
FUNCTION dec_bv(a : BIT_VECTOR) RETURN BIT_VECTOR IS
VARIABLE s : BIT_VECTOR (a'RANGE);
VARIABLE borrow : BIT;
BEGIN
borrow := '1';

FOR i IN a'LOW TO a'HIGH LOOP


s(i) := a(i) XOR borrow;
borrow := NOT (a(i)) AND borrow;
END LOOP;

RETURN (s);
END dec_bv;

-- "-"
-- Subtract overload for:
-- In: two bit_vectors.
-- Return: bit_vector.
--
FUNCTION "-"(a,b : BIT_VECTOR) RETURN BIT_VECTOR IS
VARIABLE s : BIT_VECTOR (a'RANGE);
VARIABLE borrow : BIT;
VARIABLE bi : integer; -- Indexes b.
BEGIN
ASSERT a'LENGTH <= 8 REPORT
"Subtraction OF vectors OF LENGTH > 8 may take exponential TIME."
SEVERITY WARNING;

borrow := '0';

FOR i IN a'LOW TO a'HIGH LOOP


bi := b'low + (i - a'low);
s(i) := (a(i) XOR b(bi)) XOR borrow;

borrow := (
(NOT (a(i)) AND borrow)
OR (b(bi) AND borrow)
OR (NOT (a(i)) AND b(bi))
);

END LOOP;

RETURN (s);
END "-"; -- two bit_vectors

-- "-"
-- Subtract overload for:
-- In: bit_vector, take away bit.
-- Return: bit_vector.
--
FUNCTION "-" (a : BIT_VECTOR; b : BIT) RETURN BIT_VECTOR IS
VARIABLE s : BIT_VECTOR (a'RANGE);
VARIABLE borrow : BIT;
BEGIN
borrow := b;

FOR i IN a'LOW TO a'HIGH LOOP


s(i) := a(i) XOR borrow;
borrow := (NOT(a(i)) AND borrow);
END LOOP;

RETURN (s);
END "-";

-- inv
-- Invert bit.
--
FUNCTION inv (a : BIT) RETURN BIT IS
VARIABLE result : BIT;
BEGIN
result := NOT(a);
RETURN (result);
END inv; -- Invert bit.

-- inv
-- Invert bet_vector.
--
FUNCTION inv (a : BIT_VECTOR) RETURN BIT_VECTOR IS
VARIABLE result : BIT_VECTOR (a'RANGE);
BEGIN
FOR i IN a'RANGE LOOP
result(i) := NOT(a(i));
END LOOP;

RETURN (result);
END inv; -- Invert bit_vector.

END bv_math;

Behavioural model of a 256-word, 8-bit Read Only Memory

LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE work.cpu8pac.ALL;
ENTITY rom256x8 IS
PORT(address : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
csbar, oebar : IN STD_LOGIC;
data : OUT STD_LOGIC_VECTOR(7 DOWNTO 0));
END rom256x8;

--version 1 loads acca and accb from locations 254 and 256
--and exclusive or's the values and jumps back to repeat
ARCHITECTURE version1 OF rom256x8 IS
TYPE rom_array IS ARRAY (0 TO 255) OF BIT_VECTOR(7 DOWNTO 0);
CONSTANT rom_values : rom_array :=
(0 => clra & X"0",
1 => lda & X"0", --lda $FE
2 => X"fe",
3 => ldb & X"0", --ldb $FF
4 => X"ff",
5 => lxor & X"0", --lxor
6 => jmp & X"0", --jmp $001
7 => X"01",
254 => X"aa",
255 => X"55",
OTHERS => X"00");
BEGIN
PROCESS(address, csbar, oebar)
VARIABLE index : INTEGER := 0;
BEGIN
IF (csbar = '1' OR oebar = '1')
THEN data <= "ZZZZZZZZ";
ELSE

--calculate address as an integer


index := 0;
FOR i IN address'RANGE LOOP
IF address(i) = '1' THEN
index := index + 2**i;
END IF;
END LOOP;

--assign to output data lines


data <= To_StdlogicVector(rom_values(index));

END IF;
END PROCESS;
END version1;

--version2 increments a location in the ram


ARCHITECTURE version2 OF rom256x8 IS
TYPE rom_array IS ARRAY (0 TO 255) OF BIT_VECTOR(7 DOWNTO 0);
CONSTANT rom_values : rom_array :=
(0 => clra & X"0",
1 => sta & X"1", --sta $100
2 => X"00",
3 => lda & X"1", --lda $100
4 => X"00",
5 => inc & X"0", --inc a
6 => jmp & X"0", --jmp $001
7 => X"01",
OTHERS => X"00");
BEGIN
PROCESS(address, csbar, oebar)
VARIABLE index : INTEGER := 0;
BEGIN
IF (csbar = '1' OR oebar = '1')
THEN data <= "ZZZZZZZZ";
ELSE

--calculate address as an integer


index := 0;
FOR i IN address'RANGE LOOP
IF address(i) = '1' THEN
index := index + 2**i;
END IF;
END LOOP;

--assign to output data lines


data <= To_StdlogicVector(rom_values(index));

END IF;
END PROCESS;
END version2;

Behavioural model of a 16-word, 8-bit Random Access Memory

LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
ENTITY ram16x8 IS
PORT(address : IN STD_LOGIC_VECTOR(3 DOWNTO 0);
csbar, oebar, webar : IN STD_LOGIC;
data : INOUT STD_LOGIC_VECTOR(7 DOWNTO 0));
END ram16x8;

ARCHITECTURE version1 OF ram16x8 IS

BEGIN

PROCESS(address, csbar, oebar, webar, data)

TYPE ram_array IS ARRAY (0 TO 15) OF BIT_VECTOR(7 DOWNTO 0);


VARIABLE index : INTEGER := 0;
VARIABLE ram_store : ram_array;
BEGIN

IF csbar = '0' THEN

--calculate address as an integer


index := 0;
FOR i IN address'RANGE LOOP
IF address(i) = '1' THEN
index := index + 2**i;
END IF;
END LOOP;

IF rising_edge(webar) THEN
--write to ram on rising edge of write pulse
ram_store(index) := To_bitvector(data);
ELSIF oebar = '0' THEN
data <= To_StdlogicVector(ram_store(index));
ELSE
data <= "ZZZZZZZZ";
END IF;
ELSE
data <= "ZZZZZZZZ";
END IF;

END PROCESS;

END version1;

Behavioural model of a simple 8-bit CPU

LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE work.bv_math.ALL;
USE work.cpu8pac.ALL;
ENTITY cpu IS
GENERIC(cycle_time : TIME := 200 ns); --must be divisible by 8
PORT(reset : IN std_logic;
memrd, memwr : OUT std_logic;
address : OUT std_logic_vector(11 DOWNTO 0);
data : INOUT std_logic_vector(7 DOWNTO 0));
END cpu;

ARCHITECTURE version1 OF cpu IS

--internal clock signal


SIGNAL clock : std_logic;

BEGIN

clock_gen : PROCESS
BEGIN
clock <= '1','0' AFTER cycle_time/2;
WAIT FOR cycle_time;
END PROCESS;

main_sequence : PROCESS

VARIABLE inst_reg : BIT_VECTOR(3 DOWNTO 0);


VARIABLE mar : BIT_VECTOR(11 DOWNTO 0);
VARIABLE acca, accb : BIT_VECTOR(7 DOWNTO 0);
VARIABLE pc : BIT_VECTOR(11 DOWNTO 0);

BEGIN

IF reset = '1' THEN

--initialisation
memrd <= '1';
memwr <= '1';
pc := (OTHERS => '0');
address <= (OTHERS => 'Z');
data <= (OTHERS => 'Z');
WAIT UNTIL rising_edge(clock);

ELSE
--fetch phase
address <= To_StdlogicVector(pc);
WAIT FOR cycle_time/4;
memrd <= '0';
WAIT FOR cycle_time/2;
memrd <= '1';
--read instruction
inst_reg := To_bitvector(data(7 DOWNTO 4));
--load page address
mar(11 DOWNTO 8) := To_bitvector(data(3 DOWNTO 0));
--increment program counter
pc := inc_bv(pc);
--wait until end of cycle
WAIT UNTIL rising_edge(clock);
--execute
CASE inst_reg IS
WHEN add =>
--add and sub use overloaded functions from bv_math package
acca := acca + accb;
WHEN subr =>
acca := acca - accb;
WHEN inc =>
acca := inc_bv(acca);
WHEN dec =>
acca := dec_bv(acca);
WHEN land =>
acca := acca AND accb;
WHEN lor =>
acca := acca OR accb;
WHEN cmp =>
acca := NOT acca;
WHEN lxor =>
acca := acca XOR accb;
WHEN lita =>
acca := acca;
WHEN litb =>
acca := accb;
WHEN clra =>
acca := (OTHERS => '0');
WHEN lda|ldb|sta|stb =>
address <= To_StdlogicVector(pc);
WAIT FOR cycle_time/4;
memrd <= '0';
WAIT FOR cycle_time/2;
memrd <= '1';
--read page offset address
mar(7 DOWNTO 0) := To_bitvector(data);
--increment program counter
pc := inc_bv(pc);
--wait until end of cycle
WAIT UNTIL rising_edge(clock);
--output address of operand
address <= To_StdlogicVector(mar);
IF ((inst_reg = lda) OR (inst_reg = ldb)) THEN
WAIT FOR cycle_time/4;
memrd <= '0';
WAIT FOR cycle_time/2;
memrd <= '1';
IF inst_reg = lda THEN
--load accumulator a from bus
acca := To_bitvector(data);
ELSE
--load accumulator b from bus
accb := To_bitvector(data);
END IF;
--wait until end of cycle
WAIT UNTIL
rising_edge(clock);
ELSE
WAIT FOR cycle_time/8;
IF inst_reg = sta THEN
--ouput data
data <= To_StdlogicVector(acca);
ELSE
--ouput data
data <= To_StdlogicVector(accb);
END IF;
WAIT FOR cycle_time/8;
memwr <= '0';
WAIT FOR cycle_time/2;
memwr <= '1';
WAIT FOR cycle_time/8;
data <= (OTHERS => 'Z');
--wait until end of cycle
WAIT UNTIL rising_edge(clock);
END IF;
WHEN jmp =>
address <= To_StdlogicVector(pc);
--transfer page address to pc from mar
pc(11 DOWNTO 8) := mar(11 DOWNTO 8);
--read in offset address
WAIT FOR cycle_time/4;
memrd <= '0';
WAIT FOR cycle_time/2;
memrd <= '1';
pc(7 DOWNTO 0) := To_bitvector(data);
--wait until end of cycle
WAIT UNTIL rising_edge(clock);
END CASE;
END IF;

END PROCESS main_sequence;

END version1;

Structural description of a Microprocessor System

LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
ENTITY cpudemo IS
END cpudemo;

ARCHITECTURE version1 OF cpudemo IS

COMPONENT rom256x8
PORT(address : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
csbar, oebar : IN STD_LOGIC;
data : OUT STD_LOGIC_VECTOR(7 DOWNTO 0));
END COMPONENT;

COMPONENT ram16x8
PORT(address : IN STD_LOGIC_VECTOR(3 DOWNTO 0);
csbar, oebar, webar : IN STD_LOGIC;
data : INOUT STD_LOGIC_VECTOR(7 DOWNTO 0));
END COMPONENT;

COMPONENT cpu
GENERIC(cycle_time : TIME := 200 ns); --must be divisible by 8
PORT(reset : IN std_logic;
memrd, memwr : OUT std_logic;
address : OUT std_logic_vector(11 DOWNTO 0);
data : INOUT std_logic_vector(7 DOWNTO 0));
END COMPONENT;

SIGNAL reset, memrd, memwr, romenable, ramenable : std_logic;


SIGNAL address : std_logic_vector(11 DOWNTO 0);
SIGNAL data : std_logic_vector(7 DOWNTO 0);

--selecting the rom architecture (program) for simulation


FOR rom : rom256x8 USE ENTITY work.rom256x8(version2);

BEGIN

processor : cpu PORT MAP(reset, memrd, memwr, address, data);

rom : rom256x8 PORT MAP(address(7 DOWNTO 0), romenable, memrd, data);

ram : ram16x8 PORT MAP(address(3 DOWNTO 0), ramenable, memrd, memwr, data);

--memory address decoding ,rom is at bottom of address space


--ram is situated at address $100
romenable <= '0' WHEN (address(11 DOWNTO 8) = "0000") ELSE '1';
ramenable <= '0' WHEN (address(11 DOWNTO 4) = "00010000") ELSE '1';

END version1;

Lottery Number Generator

● Lottery Number Counter


● Lottery Number Register
● BCD to 7-segment Decoder
● Controller
● Structural Model of Lottery Number Generator

Lottery Number Counter

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity count49 is
port(clock, clear : in std_logic;
cnt1to49 : buffer std_logic_vector(7 downto 0));
end entity count49;

architecture v1 of count49 is
begin
count_proc : process
begin
wait until rising_edge(clock);
if (clear = '1') or (cnt1to49 = X"49") then
cnt1to49 <= (0 => '1', others => '0');
elsif cnt1to49(3 downto 0) = 9 then
cnt1to49(3 downto 0) <= (others => '0');
cnt1to49(7 downto 4) <= cnt1to49(7 downto 4) + 1;
else
cnt1to49(3 downto 0) <= cnt1to49(3 downto 0) + 1;
end if;
end process;
end architecture v1;

Lottery Number Register

--synchronous loadable register


library ieee;
use ieee.std_logic_1164.all;
entity lottreg is
port(clock, clear, load : in std_logic;
d : in std_logic_vector(7 downto 0);
q : out std_logic_vector(7 downto 0));
end entity lottreg;

architecture v1 of lottreg is
begin
reg_proc : process
begin
wait until rising_edge(clock);
if clear = '1' then
q <= (others => '0');
elsif load = '1' then
q <= d;
end if;
end process;
end architecture v1;

Controller

--controller for lottery number generator


--new version uses 6 number registers and
--compares all numbers simulateously
library ieee;
use ieee.std_logic_1164.all;
entity lottcont2 is
port(clock, reset, next_no, match : in std_logic;
loadnum1, loadnum2, loadnum3, loadnum4,
loadnum5, loadnum6, sample : out std_logic;
seldisplay : out natural range 0 to 5;
numled : out std_logic_vector(1 to 6));
end entity lottcont2;

architecture fsm2 of lottcont2 is

type lott_state_type is (res, s1, s2, s3, s4, s5, s6, s7,
s8, s9, s10, s11, s12, s13, s14, s15, s16, s17, s18,
s19, s20, s21, s22, s23, s24, s25, s26, s27, s28);

signal lott_ps, lott_ns : lott_state_type;

begin
--next state process
fsm_state_reg : process
begin
wait until rising_edge(clock);
if reset = '1' then
lott_ps <= res;
else
lott_ps <= lott_ns;
end if;
end process;

fsm_logic : process(lott_ps, next_no, match)


begin
--assign default output values
loadnum1 <= '0';
loadnum2 <= '0';
loadnum3 <= '0';
loadnum4 <= '0';
loadnum5 <= '0';
loadnum6 <= '0';
sample <= '0';
seldisplay <= 0;
numled <= "111111";
case lott_ps is
when res => --wait for 1st no
if next_no = '1' then
lott_ns <= s1;
else
lott_ns <= res;
end if;
when s1 => --take first sample
sample <= '1';
lott_ns <= s2;
when s2 => --save first no
loadnum1 <= '1';
numled <= "011111";
lott_ns <= s3;
when s3 => --wait for 2nd no
numled <= "011111";
if next_no = '1' then
lott_ns <= s4;
else
lott_ns <= s3;
end if;
when s4 => --sample 2nd no
numled <= "011111";
sample <= '1';
lott_ns <= s5;
when s5 => --check for duplicate
numled <= "011111";
if match = '1' then
lott_ns <= s4;
else
lott_ns <= s6;
end if;
when s6 => --store second number
numled <= "101111";
loadnum2 <= '1';
lott_ns <= s7;
when s7 => --wait for 3rd no
numled <= "101111";
seldisplay <= 1;
if next_no = '1' then
lott_ns <= s8;
else
lott_ns <= s7;
end if;
when s8 => --sample 3rd no
numled <= "101111";
seldisplay <= 1;
sample <= '1';
lott_ns <= s9;
when s9 => --check against other nos
numled <= "101111";
seldisplay <= 1;
if match = '1' then
lott_ns <= s8;
else
lott_ns <= s10;
end if;
when s10 => --store 3rd no
numled <= "110111";
seldisplay <= 1;
loadnum3 <= '1';

lott_ns <= s11;


when s11 => --wait for 4th no
numled <= "110111";
seldisplay <= 2;
if next_no = '1' then
lott_ns <= s12;
else
lott_ns <= s11;
end if;
when s12 => --sample 4th no
numled <= "110111";
seldisplay <= 2;
sample <= '1';
lott_ns <= s13;
when s13 => --check against other nos
numled <= "110111";
seldisplay <= 2;
if match = '1' then
lott_ns <= s12;
else
lott_ns <= s14;
end if;
when s14 => --store 4th no
numled <= "111011";
seldisplay <= 2;
loadnum4 <= '1';
lott_ns <= s15;
when s15 => --wait for 5th no
numled <= "111011";
seldisplay <= 3;
if next_no = '1' then
lott_ns <= s16;
else
lott_ns <= s15;
end if;
when s16 => --sample 5th no
numled <= "111011";
seldisplay <= 3;
sample <= '1';
lott_ns <= s17;

when s17 => --check against other nos


numled <= "111011";
seldisplay <= 3;
if match = '1' then
lott_ns <= s16;
else
lott_ns <= s18;
end if;
when s18 => --store 5th no
numled <= "111101";
seldisplay <= 3;
loadnum5 <= '1';
lott_ns <= s19;
when s19 => --wait for 6th no
numled <= "111101";
seldisplay <= 4;
if next_no = '1' then
lott_ns <= s20;
else
lott_ns <= s19;
end if;
when s20 => --sample 6th no
numled <= "111101";
seldisplay <= 4;
sample <= '1';
lott_ns <= s21;
when s21 => --check against other nos
numled <= "111101";
seldisplay <= 4;
if match = '1' then
lott_ns <= s20;
else
lott_ns <= s22;
end if;
when s22 => --store 6th no
numled <= "111110";
seldisplay <= 4;
loadnum6 <= '1';
lott_ns <= s23;
when s23 => --review numbers
numled <= "111110";
seldisplay <= 5;
if next_no = '1' then
lott_ns <= s24;
else
lott_ns <= s23;
end if;
when s24 => --review 1st no
numled <= "011111";
seldisplay <= 0;
if next_no = '1' then
lott_ns <= s25;
else
lott_ns <= s24;
end if;
when s25 => --review 2nd no
numled <= "101111";
seldisplay <= 1;
if next_no = '1' then
lott_ns <= s26;
else
lott_ns <= s25;
end if;
when s26 => --review 3rd no
numled <= "110111";
seldisplay <= 2;
if next_no = '1' then
lott_ns <= s27;
else
lott_ns <= s26;
end if;
when s27 => --review 4th no
numled <= "111011";
seldisplay <= 3;
if next_no = '1' then
lott_ns <= s28;
else
lott_ns <= s27;
end if;
when s28 => --review 5th no
numled <= "111101";
seldisplay <= 4;
if next_no = '1' then
lott_ns <= s23;
else
lott_ns <= s28;
end if;
when others =>
lott_ns <= res;
end case;
end process;

end architecture fsm2;

Structural Model of Lottery Number Generator

--top level design for lottery number generator


--version 2 uses 6 number registers
library ieee;
use ieee.std_logic_1164.all;
entity lottery2 is
port(clock, reset, next_no : in std_logic;
numled : out std_logic_vector(1 to 6);
seg0, seg1 : out std_logic_vector(6 downto 0));
end entity lottery2;

architecture structure of lottery2 is

component lottreg
port(clock, clear, load : in std_logic;
d : in std_logic_vector(7 downto 0);
q : out std_logic_vector(7 downto 0));
end component;

component count49
port(clock, clear : in std_logic;
cnt1to49 : buffer std_logic_vector(7 downto 0));
end component;

component seg7dec --see file bcd2seg.vhd


PORT(bcdin : IN std_logic_vector(3 DOWNTO 0);
segout : OUT std_logic_vector(6 DOWNTO 0));
end component;

component lottcont2
port(clock, reset, next_no, match : in std_logic;
loadnum1, loadnum2, loadnum3, loadnum4,
loadnum5, loadnum6, sample : out std_logic;
seldisplay : out natural range 0 to 5;
numled : out std_logic_vector(1 to 6));
end component;

signal match : std_logic;


signal sample : std_logic;

signal seldisplay : natural range 0 to 5;

signal count, samp_reg, display : std_logic_vector(7 downto 0);


signal num_reg1, num_reg2, num_reg3 : std_logic_vector(7 downto 0);
signal num_reg4, num_reg5, num_reg6 : std_logic_vector(7 downto 0);

signal loadnum1, loadnum2, loadnum3, loadnum4, loadnum5, loadnum6 : std_logic;

begin

counter : count49
port map (clock => clock, clear => reset, cnt1to49 => count);

sample_reg : lottreg
port map (clock => clock, clear => reset,
load => sample, d => count, q => samp_reg);

--number registers
numreg1 : lottreg port map
(clock => clock, clear => reset, load => loadnum1,
d => samp_reg, q => num_reg1);
numreg2 : lottreg port map
(clock => clock, clear => reset, load => loadnum2,
d => samp_reg, q => num_reg2);
numreg3 : lottreg port map
(clock => clock, clear => reset, load => loadnum3,
d => samp_reg, q => num_reg3);
numreg4 : lottreg port map
(clock => clock, clear => reset, load => loadnum4,
d => samp_reg, q => num_reg4);
numreg5 : lottreg port map
(clock => clock, clear => reset, load => loadnum5,
d => samp_reg, q => num_reg5);
numreg6 : lottreg port map
(clock => clock, clear => reset, load => loadnum6,
d => samp_reg, q => num_reg6);

compare : match <= '1' when ((((samp_reg = num_reg1)


or (samp_reg = num_reg2))
or (samp_reg = num_reg3))
or (samp_reg = num_reg4))
or (samp_reg = num_reg5)
else '0';

display_mux : with seldisplay select


display <= num_reg1 when 0,
num_reg2 when 1,
num_reg3 when 2,
num_reg4 when 3,
num_reg5 when 4,
num_reg6 when 5,
"00000000" when others;

segdec0 : seg7dec
port map (bcdin => display(3 downto 0), segout => seg0);

segdec1 : seg7dec
port map (bcdin => display(7 downto 4), segout => seg1);

controller : lottcont2
port map (clock => clock, reset => reset, next_no => next_no,
match => match, loadnum1 => loadnum1,
loadnum2 => loadnum2, loadnum3 => loadnum3,
loadnum4 => loadnum4, loadnum5 => loadnum5,
loadnum6 => loadnum6, sample => sample,
seldisplay => seldisplay,
numled => numled);

end architecture structure;

Booth Multiplier

--This file contains all the entity-architectures for a complete


--k-bit x k-bit Booth multiplier.
--the design makes use of the new shift operators available in the VHDL-93 std
--this design passes the Synplify synthesis check
----------------------------------------------------------------------
--top level design unit
ENTITY booth_multiplier IS
GENERIC(k : POSITIVE := 7); --input number word length less one
PORT(multiplicand, multiplier : IN BIT_VECTOR(k DOWNTO 0);
clock : IN BIT; product : INOUT BIT_VECTOR((2*k + 1) DOWNTO 0));
END booth_multiplier;

ARCHITECTURE structural OF booth_multiplier IS

SIGNAL mdreg, adderout, carries, augend, tcbuffout : BIT_VECTOR(k DOWNTO 0);


SIGNAL mrreg : BIT_VECTOR((k + 1) DOWNTO 0);
SIGNAL adder_ovfl : BIT;
SIGNAL comp ,clr_mr ,load_mr ,shift_mr ,clr_md ,load_md ,clr_pp ,load_pp ,shift_pp : BIT;
SIGNAL boostate : NATURAL RANGE 0 TO 2*(k + 1);

BEGIN

PROCESS --main clocked process containing all sequential elements


BEGIN
WAIT UNTIL (clock'EVENT AND clock = '1');

--register to hold multiplicand during multiplication


IF clr_md = '1' THEN
mdreg <= (OTHERS => '0');
ELSIF load_md = '1' THEN
mdreg <= multiplicand;
ELSE
mdreg <= mdreg;
END IF;

--register/shifter to product pair of bits used to control adder


IF clr_mr = '1' THEN
mrreg <= (OTHERS => '0');
ELSIF load_mr = '1' THEN
mrreg((k + 1) DOWNTO 1) <= multiplier;
mrreg(0) <= '0';
ELSIF shift_mr = '1' THEN
mrreg <= mrreg SRL 1;
ELSE
mrreg <= mrreg;
END IF;

--register/shifter accumulates partial product values


IF clr_pp = '1' THEN
product <= (OTHERS => '0');
ELSIF load_pp = '1' THEN
product((2*k + 1) DOWNTO (k + 1)) <= adderout; --add to top half
product(k DOWNTO 0) <= product(k DOWNTO 0); --refresh bootm half
ELSIF shift_pp = '1' THEN
product <= product SRA 1; --shift right with sign extend
ELSE
product <= product;
END IF;

END PROCESS;

--adder adds/subtracts partial product to multiplicand


augend <= product((2*k+1) DOWNTO (k+1));
addgen : FOR i IN adderout'RANGE
GENERATE
lsadder : IF i = 0 GENERATE
adderout(i) <= tcbuffout(i) XOR augend(i) XOR comp;
carries(i) <= (tcbuffout(i) AND augend(i)) OR
(tcbuffout(i) AND comp) OR
(comp AND augend(i));
END GENERATE;
otheradder : IF i /= 0 GENERATE
adderout(i) <= tcbuffout(i) XOR augend(i) XOR carries(i-1);
carries(i) <= (tcbuffout(i) AND augend(i)) OR
(tcbuffout(i) AND carries(i-1)) OR
(carries(i-1) AND augend(i));
END GENERATE;
END GENERATE;
--twos comp overflow bit
adder_ovfl <= carries(k-1) XOR carries(k);

--true/complement buffer to generate two's comp of mdreg


tcbuffout <= NOT mdreg WHEN (comp = '1') ELSE mdreg;

--booth multiplier state counter


PROCESS BEGIN
WAIT UNTIL (clock'EVENT AND clock = '1');
IF boostate < 2*(k + 1) THEN boostate <= boostate + 1;
ELSE boostate <= 0;
END IF;
END PROCESS;

--assign control signal values based on state


PROCESS(boostate)
BEGIN
--assign defaults, all registers refresh
comp <= '0';
clr_mr <= '0';
load_mr <= '0';
shift_mr <= '0';
clr_md <= '0';
load_md <= '0';
clr_pp <= '0';
load_pp <= '0';
shift_pp <= '0';
IF boostate = 0 THEN
load_mr <= '1';
load_md <= '1';
clr_pp <= '1';
ELSIF boostate MOD 2 = 0 THEN --boostate = 2,4,6,8 ....
shift_mr <= '1';
shift_pp <= '1';
ELSE --boostate = 1,3,5,7......
IF mrreg(0) = mrreg(1) THEN
NULL; --refresh pp
ELSE
load_pp <= '1'; --update product
END IF;
comp <= mrreg(1); --subract if mrreg(1 DOWNTO 0) ="10"
END IF;
END PROCESS;

END structural;
A First-in First-out Memory

--a first-in first out memory, uses a synchronising clock


--generics allow fifos of different sizes to be instantiated
library IEEE;
use IEEE.Std_logic_1164.all;
entity FIFOMXN is
generic(m, n : Positive := 8); --m is fifo depth, n is fifo width
port(RESET, WRREQ, RDREQ, CLOCK : in Std_logic;
DATAIN : in Std_logic_vector((n-1) downto 0);
DATAOUT : out Std_logic_vector((n-1) downto 0);
FULL, EMPTY : inout Std_logic);
end FIFOMXN;

architecture V2 of FIFOMXN is

type Fifo_array is array(0 to (m-1)) of Bit_vector((n-1) downto 0);


signal Fifo_memory : Fifo_array;
signal Wraddr, Rdaddr, Offset : Natural range 0 to (m-1);
signal Rdpulse, Wrpulse, Q1, Q2, Q3, Q4 : Std_logic;
signal Databuffer : Bit_vector((n-1) downto 0);

begin

--pulse synchronisers for WRREQ and RDREQ


--modified for Synplify to a process
sync_ffs : process
begin
wait until rising_edge(CLOCK);
Q1 <= WRREQ;
Q2 <= Q1;
Q3 <= RDREQ;
Q4 <= Q3;
end process;

--concurrent logic to generate pulses


Wrpulse <= Q2 and not(Q1);
Rdpulse <= Q4 and not(Q3);

Fifo_read : process
begin
wait until rising_edge(CLOCK);
if RESET = '1' then
Rdaddr <= 0;
Databuffer <= (others => '0');
elsif (Rdpulse = '1' and EMPTY = '0') then
Databuffer <= Fifo_memory(Rdaddr);
Rdaddr <= (Rdaddr + 1) mod m;
end if;
end process;

Fifo_write : process
begin
wait until rising_edge(CLOCK);
if RESET = '1' then
Wraddr <= 0;
elsif (Wrpulse = '1' and FULL = '0') then
Fifo_memory(Wraddr) <= To_Bitvector(DATAIN);
Wraddr <= (Wraddr + 1) mod m;
end if;
end process;

Offset <= (Wraddr - Rdaddr) when (Wraddr > Rdaddr)


else (m - (Rdaddr - Wraddr)) when (Rdaddr > Wraddr)
else 0;

EMPTY <= '1' when (Offset = 0) else '0';


FULL <= '1' when (Offset = (m-1)) else '0';

DATAOUT <= To_Stdlogicvector(Databuffer) when RDREQ = '0'


else (others => 'Z');

end V2;

ROM-based waveform generator

PACKAGE rompac IS

CONSTANT rom_width : POSITIVE := 3;


CONSTANT addr_high : POSITIVE := 12;

SUBTYPE rom_word IS BIT_VECTOR(0 TO rom_width);


TYPE rom_table IS ARRAY(0 TO addr_high) OF rom_word;

CONSTANT rom : rom_table :=


("1100",
"1100",
"0100",
"0000",
"0110",
"0101",
"0111",
"1100",
"0100",
"0000",
"0110",
"0101",
"0111");

END rompac;

--WAVEFORM GENERATOR USING A ROM LOOK-UP TABLE 15-6-92


--THE ROM IS A CONSTANT DECLARED WITHIN THE PACKAGE rompac.
--
USE work.rompac.ALL;
ENTITY romwaves IS
PORT(clock : IN BIT; reset : IN BOOLEAN;
waves : OUT rom_word);
END romwaves;

ARCHITECTURE behaviour OF romwaves IS

SIGNAL step : NATURAL;


BEGIN

--address counter for rom look-up table


step_counter:PROCESS
BEGIN

WAIT UNTIL clock'EVENT AND clock = '1';

IF reset THEN --check for reset condition


step <= 0;
ELSIF step = addr_high THEN --check for last wave value
step <= addr_high;
ELSE
step <= step + 1; --get next wave value
END IF;

END PROCESS;

--output value from rom look-up table


waves <= rom(step);

END behaviour;

Classic 2-Process State Machine and Test Bench

--MEALY TYPE STATE MACHINE EXAMPLE

ENTITY fsm IS
PORT(clock,x : IN BIT; z : OUT BIT);
END fsm;
-------------------------------------------------
ARCHITECTURE behaviour OF fsm IS

TYPE state_type IS (s0,s1,s2,s3);


SIGNAL present_state,next_state : state_type;

BEGIN
--state register process
state_reg:PROCESS
BEGIN
WAIT UNTIL clock'EVENT AND clock = '1';
present_state <= next_state;
END PROCESS;
--combinational logic feedback process
fb_logic:PROCESS(present_state,x)
BEGIN
CASE present_state IS
WHEN s0 =>
IF x = '0' THEN z <= '0'; next_state <= s0;
ELSE z <= '1'; next_state <= s2;
END IF;

WHEN s1 =>
IF x = '0' THEN z <= '0'; next_state <= s0;
ELSE z <= '0'; next_state <= s2;
END IF;

WHEN s2 =>
IF x = '0' THEN z <= '1'; next_state <= s2;
ELSE z <= '0'; next_state <= s3;
END IF;

WHEN s3 =>
IF x = '0' THEN z <= '0'; next_state <= s3;
ELSE z <= '1'; next_state <= s1;
END IF;
END CASE;
END PROCESS;
END behaviour;
-----------------------------------------------------------
--STIMULUS GENERATOR FOR FSM
ENTITY fsm_stim IS
PORT (clock,x: OUT BIT; z: IN BIT);
END fsm_stim;

ARCHITECTURE behavioural OF fsm_stim IS


BEGIN

--clock pulses : __--__--__--__--__--__


--x input : _____------------_____
--each '-' represents 5 ns.

clock <= '0' AFTER 0 ns,


'1' AFTER 10 ns, --clock 1
'0' AFTER 20 ns,
'1' AFTER 30 ns, --clock 2
'0' AFTER 40 ns,
'1' AFTER 50 ns, --clock 3
'0' AFTER 60 ns,
'1' AFTER 70 ns, --clock 4
'0' AFTER 80 ns,
'1' AFTER 90 ns, --clock 5
'0' AFTER 100 ns;

x <= '0' AFTER 0 ns,


'1' AFTER 25 ns,
'0' AFTER 85 ns;

END behavioural;
-----------------------------------------------
ENTITY fsm_bench IS
END fsm_bench;

ARCHITECTURE structural OF fsm_bench IS


COMPONENT fsm_stim PORT (clock,x: OUT BIT; z: IN BIT); END COMPONENT;
COMPONENT fsm PORT (clock,x: IN BIT; z: OUT BIT); END COMPONENT;
SIGNAL clock,x,z: BIT;
BEGIN
generator:fsm_stim PORT MAP(clock,x,z);
circuit:fsm PORT MAP(clock,x,z);
END structural;

State Machine using Variable


ENTITY fsm2 IS

PORT(clock,x : IN BIT; z : OUT BIT);


END fsm2;
-------------------------------------------------
ARCHITECTURE using_wait OF fsm2 IS

TYPE state_type IS (s0,s1,s2,s3);

BEGIN
PROCESS

VARIABLE state : state_type := s0;

BEGIN

WAIT UNTIL (clock'EVENT AND clock = '1');


CASE state IS
WHEN s0 => IF x = '0' THEN
state := s0;
z <= '0';
ELSE
state := s2;
z <= '1';
END IF;
WHEN s2 => IF x = '0' THEN
state := s2;
z <= '1';
ELSE
state := s3;
z <= '0';
END IF;
WHEN s3 => IF x = '0' THEN
state := s3;
z <= '0';
ELSE
state := s1;
z <= '1';
END IF;
WHEN s1 => IF x = '0' THEN
state := s0;
z <= '0';
ELSE
state := s2;
z <= '0';
END IF;

END CASE;
END PROCESS;
END using_wait;
-----------------------------------------------------------

State Machine with Asynchronous Reset

library ieee;
use ieee.std_logic_1164.all;

entity stmch1 is
port(clk, in1, rst: in std_logic; out1: out std_logic);
end stmch1;

architecture behave of stmch1 is


type state_values is (sx, s0, s1);
signal state, next_state: state_values;
begin

process (clk, rst)


begin
if rst = '1' then
state <= s0;
elsif rising_edge(clk) then
state <= next_state;
end if;
end process;

process (state, in1)


begin
-- set defaults for output and state
out1 <= '0';
next_state <= sx; -- catch missing assignments to next_state
case state is
when s0 =>
if in1 = '0' then
out1 <='1';
next_state <= s1;
else
out1 <= '0';
next_state <= s0;
end if;
when s1 =>
if in1 = '0' then
out1 <='0';
next_state <= s0;
else
out1 <= '1';
next_state <= s1;
end if;
when sx =>
next_state <= sx;
end case;
end process;
end behave;

Pattern Detector FSM with Test Bench

LIBRARY ieee;
USE ieee.Std_logic_1164.ALL;
ENTITY patdet IS
PORT(clock, serin, reset : IN Std_logic; match : OUT Std_logic);
END patdet;

ARCHITECTURE v1 OF patdet IS
TYPE state_type IS (s0, s1, s2, s3, s4, s5, s6, s7, s8);
SIGNAL pstate, nstate : state_type;

BEGIN

--state register
PROCESS
BEGIN
WAIT UNTIL rising_edge(clock);
IF reset = '1' THEN
pstate <= s0;
ELSE
pstate <= nstate;
END IF;
END PROCESS;

--next state logic


PROCESS(serin, pstate)
BEGIN
CASE pstate IS
WHEN s0 => IF serin = '0' THEN
nstate <= s0;
ELSE
nstate <= s1;
END IF;
WHEN s1 => IF serin = '0' THEN
nstate <= s0;
ELSE
nstate <= s2;
END IF;
WHEN s2 => IF serin = '0' THEN
nstate <= s0;
ELSE
nstate <= s3;
END IF;
WHEN s3 => IF serin = '0' THEN
nstate <= s0;
ELSE
nstate <= s4;
END IF;
WHEN s4 => IF serin = '0' THEN
nstate <= s0;
ELSE
nstate <= s5;
END IF;
WHEN s5 => IF serin = '0' THEN
nstate <= s0;
ELSE
nstate <= s6;
END IF;
WHEN s6 => IF serin = '1' THEN
nstate <= s8;
ELSE
nstate <= s7;
END IF;
WHEN s7 => IF serin = '0' THEN
nstate <= s0;
ELSE
nstate <= s8;
END IF;
WHEN s8 => IF serin = '0' THEN
nstate <= s0;
ELSE
nstate <= s8;
END IF;
WHEN OTHERS => nstate <= s0;
END CASE;
END PROCESS;

--generate output
match <= '1' WHEN pstate = s7 ELSE '0';

END v1;

--The following Design Entity defines a parameterised Pseudo-random


--bit sequence generator, it is useful for generating serial or parallel test waveforms
--(for parallel waveforms you need to add an extra output port)
--The generic 'length' is the length of the register minus one.
--the generics 'tap1' and 'tap2' define the feedback taps

LIBRARY ieee;
USE ieee.Std_logic_1164.ALL;
ENTITY prbsgen IS
GENERIC(length : Positive := 8; tap1 : Positive := 8; tap2 : Positive := 4);
PORT(clk, reset : IN Std_logic; prbs : OUT Std_logic);
END prbsgen;

ARCHITECTURE v3 OF prbsgen IS

--create a shift register


SIGNAL prreg : Std_logic_vector(length DOWNTO 0);

BEGIN

--conditional signal assignment shifts register and feeds in xor value


prreg <= (0 => '1', OTHERS => '0') WHEN reset = '1' ELSE
(prreg((length - 1) DOWNTO 0) & (prreg(tap1) XOR prreg(tap2)))
WHEN rising_edge(clk) ELSE prreg;

--connect msb of register to output


prbs <= prreg(length);

END v3;

LIBRARY ieee;
USE ieee.Std_logic_1164.ALL;
ENTITY patdetbench IS
END patdetbench;

--defining architecture for pre-synthesis functional simulation


ARCHITECTURE precomp OF patdetbench IS

COMPONENT prbsgen
PORT(clk, reset : IN Std_logic; prbs : OUT Std_logic);
END COMPONENT;
COMPONENT patdet
PORT(clock, serin, reset : IN Std_logic; match : OUT Std_logic);
END COMPONENT;

--configure patdet to be functional model


FOR patdet1 : patdet USE ENTITY work.patdet(v1);

SIGNAL clock, reset, pattern, match : Std_logic;

BEGIN

--clock generator
PROCESS
BEGIN
clock <= '0', '1' AFTER 50 ns;
WAIT FOR 100 ns;
END PROCESS;

patgen1 : prbsgen PORT MAP (clock, reset, pattern);

patdet1 : patdet PORT MAP (clock, pattern, reset, match);

END precomp;

Chess Clock

PACKAGE chesspack IS

SUBTYPE hours IS NATURAL;


SUBTYPE minutes IS INTEGER RANGE 0 TO 60;
SUBTYPE seconds IS INTEGER RANGE 0 TO 60;

TYPE elapsed_time IS
RECORD
hh : hours;
mm : minutes;
ss : seconds;
END RECORD;

TYPE state IS (reset,hold,runb,runa);

FUNCTION inctime (intime : elapsed_time) RETURN elapsed_time;

FUNCTION zero_time RETURN elapsed_time;

END chesspack;

PACKAGE BODY chesspack IS

FUNCTION inctime (intime :elapsed_time) RETURN elapsed_time IS


VARIABLE result : elapsed_time;
BEGIN
result := intime;
result.ss := result.ss + 1;
IF result.ss = 60 THEN
result.ss := 0;
result.mm := result.mm + 1;
IF result.mm = 60 THEN
result.mm := 0;
result.hh := result.hh + 1;
END IF;
END IF;
RETURN result;
END inctime;

FUNCTION zero_time RETURN elapsed_time IS


VARIABLE result : elapsed_time;
BEGIN
result.ss := 0;
result.mm := 0;
result.hh := 0;
RETURN result;
END zero_time;

END chesspack;

USE WORK.chesspack.ALL;
ENTITY timer IS
--time_used must be inout port since signal assignment statement
--reads it's value to compute the new value of time_used.
PORT(enable,clear,one_sec : IN BIT; time_used : INOUT elapsed_time);
END timer;

ARCHITECTURE behaviour OF timer IS

BEGIN

time_used <= zero_time WHEN clear = '1' ELSE


inctime(time_used) WHEN
(enable = '1' AND one_sec'EVENT AND one_sec = '1')
ELSE time_used;

END behaviour;

------------------------------------------------------------------
USE WORK.chesspack.ALL;
ENTITY chessclock IS
PORT(a,b,hold_time,reset_time : IN BIT;
time_a,time_b : INOUT elapsed_time);
END chessclock;

ARCHITECTURE structure OF chessclock IS

COMPONENT timer
PORT(enable,clear,one_sec : IN BIT; time_used : INOUT elapsed_time);
END COMPONENT;

SIGNAL one_sec,clock,ena,enb,clear_timers : BIT := '0';

BEGIN

--instantiating timers a and b


timer_a : timer PORT MAP(ena,clear_timers,one_sec,time_a);
timer_b : timer PORT MAP(enb,clear_timers,one_sec,time_b);

controller:BLOCK --chessclock state machine


SIGNAL present_state,next_state : state := reset;
BEGIN

--state register
state_reg:BLOCK
BEGIN
PROCESS(clock)
BEGIN
IF (clock'EVENT AND clock = '1' AND clock'LAST_VALUE = '0')
THEN present_state <= next_state;
END IF;
END PROCESS;
END BLOCK state_reg;

--output and feedback logic


logic:BLOCK
BEGIN
PROCESS(a,b,hold_time,reset_time,present_state)
VARIABLE a_b : BIT_VECTOR(0 TO 1);
BEGIN
a_b := a&b; --aggregate assignment for case statement
CASE present_state IS
WHEN reset =>
clear_timers <= '1';
ena <= '0';
enb <= '0';
IF reset_time = '1' THEN next_state <= reset;
ELSIF hold_time = '1' THEN next_state <= hold;
ELSE CASE a_b IS
WHEN "00" => next_state <= hold;
WHEN "01" => next_state <= runa;

WHEN "10" => next_state <= runb;


WHEN "11" => next_state <= hold;
END CASE;
END IF;
WHEN hold =>
clear_timers <= '0';
ena <= '0';
enb <= '0';
IF reset_time = '1' THEN next_state <= reset;
ELSIF hold_time = '1' THEN next_state <= hold;
ELSE CASE a_b IS
WHEN "00" => next_state <= hold;
WHEN "01" => next_state <= runa;
WHEN "10" => next_state <= runb;
WHEN "11" => next_state <= hold;
END CASE;
END IF;
WHEN runa =>
clear_timers <= '0';
ena <= '1';
enb <= '0';
IF reset_time = '1' THEN next_state <= reset;
ELSIF hold_time = '1' THEN next_state <= hold;
ELSIF a = '0' THEN next_state <= runa;
ELSIF b = '1' THEN next_state <= hold;
ELSE next_state <= runb;
END IF;
WHEN runb =>
clear_timers <= '0';
ena <= '0';
enb <= '1';
IF reset_time = '1' THEN next_state <= reset;
ELSIF hold_time = '1' THEN next_state <= hold;
ELSIF b = '0' THEN next_state <= runb;
ELSIF a = '1' THEN next_state <= hold;
ELSE next_state <= runa;
END IF;
END CASE;
END PROCESS;
END BLOCK logic;

END BLOCK controller;

one_sec_clock:BLOCK
BEGIN
PROCESS --process to generate one second clock
BEGIN
one_sec <= TRANSPORT '1' AFTER 500 ms;
one_sec <= TRANSPORT '0' AFTER 1000 ms;
WAIT FOR 1000 ms;
END PROCESS;
END BLOCK one_sec_clock;

system_clock:BLOCK
BEGIN
PROCESS --process to generate 10Hz state machine clock
BEGIN
clock <= TRANSPORT '1' AFTER 50 ms;
clock <= TRANSPORT '0' AFTER 100 ms;
WAIT FOR 100 ms;
END PROCESS;
END BLOCK system_clock;

END structure;

Digital Delay Unit

● Package defining types used by the system memory


● Package defining a basic analogue type
● 16-bit Analogue to Digital Converter
● 16-bit Digital to Analogue Converter
● Top-level Digital Delay Unit including RAM and control process
● Sinewave generator for testbench
● Testbench for Digital Delay Unit

Package defining types used by the system memory

PACKAGE rampac IS

SUBTYPE addr10 IS NATURAL RANGE 0 TO 1023;


SUBTYPE data16 IS INTEGER RANGE -32768 TO +32767;
TYPE ram_array IS ARRAY(addr10'LOW TO addr10'HIGH) OF data16;
CONSTANT z_val : data16 := -1;

END rampac;

Package defining a basic analogue type

PACKAGE adcpac IS
SUBTYPE analogue IS REAL RANGE -5.0 TO +5.0;
END adcpac;

16-bit Analogue to Digital Converter

USE WORK.rampac.ALL;
USE WORK.adcpac.ALL;
ENTITY adc16 IS
GENERIC(tconv : TIME := 10 us); --conversion time
PORT(vin : IN analogue; digout : OUT data16; --input and output
sc : IN BIT; busy : OUT BIT); --control
END adc16;

ARCHITECTURE behaviour OF adc16 IS


BEGIN
PROCESS
VARIABLE digtemp : data16;
CONSTANT vlsb : analogue := (analogue'HIGH - analogue'LOW)/REAL(2*ABS(data16'LOW));
BEGIN
digtemp := data16'LOW;
busy <= '0';
WAIT UNTIL (sc'EVENT AND sc = '0');
busy <= '1';
FOR i IN 0 TO (2*data16'HIGH) LOOP
IF vin >= (analogue'LOW + (REAL(i) + 0.5)*vlsb)
THEN digtemp := digtemp + 1;
ELSE EXIT;
END IF;
END LOOP;
WAIT FOR tconv;
digout <= digtemp;
busy <= '0';
END PROCESS;
END behaviour;

16-bit Digital to Analogue Converter

USE WORK.rampac.ALL;
USE WORK.adcpac.ALL;
ENTITY dac16 IS
PORT(vout : INOUT analogue; digin : IN data16; --input and output
en : IN BIT); --latches in data
END dac16;

ARCHITECTURE behaviour OF dac16 IS


CONSTANT vlsb : analogue := (analogue'HIGH - analogue'LOW)/REAL(2*ABS(data16'LOW));
BEGIN
--store analogue equivalent of digin on vout when negative edge on en
vout <= REAL(digin)*vlsb WHEN (en'EVENT AND en = '0') ELSE vout;
END behaviour;

Top-level Digital Delay Unit including RAM and control process

--VHDL model of a ram-based analogue delay system.

USE WORK.rampac.ALL;
USE WORK.adcpac.ALL;
ENTITY digdel2 IS
PORT(clear : IN BIT; --clears address counter
offset : IN addr10; --delay control
sigin : IN analogue; --signal input
sigout : INOUT analogue); --signal output
END digdel2;

ARCHITECTURE block_struct OF digdel2 IS

COMPONENT adc16
PORT(vin : IN analogue; digout : OUT data16;
sc : IN BIT; busy : OUT BIT);
END COMPONENT;

COMPONENT dac16
PORT(vout : INOUT analogue; digin : IN data16;
en : IN BIT);
END COMPONENT;

SIGNAL address : addr10; --pointer to ram location


SIGNAL ram_data_out : data16; --data output of ram
SIGNAL ram_data_in : data16; --data input to ram
SIGNAL clock,cs,write,suboff,adcsc,dacen,adcbusy : BIT; --internal controls

BEGIN

--start conversion on positive edge of 'clock'at beginning of cycle


adcsc <= NOT clock; --|__________----------|
adc1 : adc16 PORT MAP (sigin,ram_data_in,adcsc,adcbusy);

cs <= '1'; --enable ram device

ram:BLOCK -- 16-bit * 1024 location RAM


BEGIN
ram_proc:PROCESS(cs,write,address,ram_data_in)
VARIABLE ram_data : ram_array;

VARIABLE ram_init : BOOLEAN := FALSE;


BEGIN

IF NOT(ram_init) THEN --initialise ram locations


FOR i IN ram_data'RANGE LOOP
ram_data(i) := 0;
END LOOP;
ram_init := TRUE;
END IF;

IF cs = '1' THEN
IF write = '1' THEN
ram_data(address) := ram_data_in;
END IF;
ram_data_out <= ram_data(address);
ELSE
ram_data_out <= z_val;
END IF;

END PROCESS;
END BLOCK ram;

dac1 : dac16 PORT MAP (sigout,ram_data_out,dacen);

-- concurrent statement for 'suboff' (subtract offset) signal for counter


suboff <= clock; --|----------__________|

cntr10:BLOCK --10-bit address counter with offset control


SIGNAL count : addr10 := 0;
BEGIN --dataflow model of address counter
count <= 0 WHEN clear = '1' ELSE
((count + 1) MOD 1024) WHEN (clock'EVENT AND clock = '1')
ELSE count;
address <= count WHEN suboff = '0'
ELSE (count - offset) WHEN ((count - offset) >= 0)
ELSE (1024 - ABS(count - offset));
END BLOCK cntr10;

control_waves:PROCESS --process to generate system control waveforms


BEGIN

clock <= TRANSPORT '1';


clock <= TRANSPORT '0' AFTER 10 us; --|----------__________|

dacen <= TRANSPORT '1',


'0' AFTER 5 us; --|-----_______________|

write <= TRANSPORT '1' AFTER 13 us, --|_____________----___|


'0' AFTER 17 us;

WAIT FOR 20 us;

END PROCESS control_waves;

END block_struct;

Sinewave generator for testbench

--entity to generate a 2.5kHz sampled sinewave (sampled at 20 us intervals)


USE WORK.adcpac.ALL;
ENTITY sinegen IS
PORT(sinewave : OUT analogue);
END sinegen;

ARCHITECTURE behaviour OF sinegen IS

CONSTANT ts : TIME := 20 us; --sample interval


TYPE sinevals IS ARRAY (0 TO 5) OF analogue;
--sample values for one quarter period
CONSTANT qrtrsine : sinevals := (0.0, 1.545, 2.939, 4.045, 4.755, 5.0);
BEGIN
PROCESS --sequential process generates sinewave
BEGIN
FOR i IN 0 TO 19 LOOP --output 20 samples per period
IF (i >= 0) AND (i < 6) THEN --first quarter period
sinewave <= qrtrsine(i);
ELSIF (i >= 6) AND (i < 11) THEN --second quarter period
sinewave <= qrtrsine(10-i);
ELSIF (i >= 11) AND (i < 16) THEN --third quarter period
sinewave <= -qrtrsine(i-10);
ELSE --i IN 16 TO 19
sinewave <= -qrtrsine(20-i); --final quater period
END IF;
WAIT FOR ts;
END LOOP;
END PROCESS;
END behaviour;

Testbench for Digital Delay Unit

USE WORK.rampac.ALL;
USE WORK.adcpac.ALL;
ENTITY delay_bench IS
PORT(reset : IN BIT; delay : IN addr10);
END delay_bench;

ARCHITECTURE version1 OF delay_bench IS

COMPONENT sinegen
PORT(sinewave : OUT analogue);
END COMPONENT;

COMPONENT digdel2
PORT(clear : IN BIT; offset : IN addr10;
sigin : IN analogue; sigout : INOUT analogue);
END COMPONENT;

SIGNAL analogue_in, analogue_out : analogue;

BEGIN

sig_gen : sinegen PORT MAP(analogue_in);


delay_unit : digdel2 PORT MAP(reset, delay, analogue_in, analogue_out);

END;

8-bit Analogue to Digital Converter

--8-bit analogue to digital converter


--demonstrates use of LOOP and WAIT statements
ENTITY adc8 IS
GENERIC(tconv : TIME := 10 us); --conversion time
PORT(vin : IN REAL RANGE 0.0 TO +5.0; --unipolar input
digout : OUT NATURAL RANGE 0 TO 255; --output
sc : IN BIT; busy : OUT BIT); --control
END adc8;

ARCHITECTURE behaviour OF adc8 IS


BEGIN

PROCESS

VARIABLE digtemp : NATURAL;


CONSTANT vlsb : REAL := 5.0/256; --least significant bit value

BEGIN

digtemp := 0;

WAIT UNTIL (sc'EVENT AND sc = '0'); --falling edge on sc starts conv

busy <= '1'; --flag converter busy

WAIT FOR tconv; --conversion time

FOR i IN 0 TO 255 LOOP --do ramp-up conversion


IF vin >= REAL(i)*vlsb
THEN IF digtemp = 255 THEN EXIT;
ELSE digtemp := digtemp + 1;
END IF;
ELSE EXIT;
END IF;
END LOOP;

digout <= digtemp; --output result

busy <= '0'; --flag end of conversion

END PROCESS;

END behaviour;

8-bit Unipolar Successive Approximation ADC

--8-bit unipolar successive approximation analogue to digital converter


--demonstrates use of LOOP and WAIT statements

ENTITY adcsc8 IS
PORT(vin : IN REAL RANGE 0.0 TO +5.0; --unipolar analogue input
digout : OUT BIT_VECTOR(7 DOWNTO 0); --digital output
clock, sc : IN BIT; busy : OUT BIT); --clock & control
END adcsc8;

ARCHITECTURE behaviour OF adcsc8 IS

SIGNAL v_estimate : REAL RANGE 0.0 TO +5.0;

BEGIN

PROCESS

CONSTANT v_lsb : REAL := 5.0/256; --least significant bit value

BEGIN

WAIT UNTIL (sc'EVENT AND sc = '0'); --falling edge on sc starts conv

v_estimate <= 0.0; --initialise v_estimate


digout <= "00000000"; --clear SAR register
busy <= '1'; --flag converter busy

FOR i IN digout'RANGE LOOP --loop for each output bit

WAIT UNTIL (clock'EVENT AND clock = '1');

v_estimate <= v_estimate + (REAL(2**i))*v_lsb;


digout(i) <= '1';

WAIT UNTIL (clock'EVENT AND clock = '1');

IF v_estimate >= vin THEN


v_estimate <= v_estimate - (REAL(2**i))*v_lsb;
digout(i) <= '0';
END IF;

END LOOP;

busy <= '0'; --flag end of conversion

END PROCESS;

END behaviour;
TTL164 Shift Register

ENTITY dev164 IS
PORT(a, b, nclr, clock : IN BIT;
q : BUFFER BIT_VECTOR(0 TO 7));
END dev164;

ARCHITECTURE version1 OF dev164 IS


BEGIN
PROCESS(a,b,nclr,clock)
BEGIN
IF nclr = '0' THEN
q <= "00000000";
ELSE
IF clock'EVENT AND clock = '1'
THEN
FOR i IN q'RANGE LOOP
IF i = 0 THEN q(i) <= (a AND b);
ELSE
q(i) <= q(i-1);
END IF;
END LOOP;
END IF;
END IF;
END PROCESS;
END version1;

Behavioural description of an 8-bit Shift Register

--8-bit universal shift register modelled using a process


ENTITY shftreg8 IS
PORT(clock, serinl, serinr : IN BIT; --clock and serial inputs
mode : IN BIT_VECTOR(0 TO 1);
--"00" : disabled; "10" : shift left; "01" : shift right; "11" : Parallel load;
parin : IN BIT_VECTOR(0 TO 7); --parallel inputs
parout : OUT BIT_VECTOR(0 TO 7)); --parallel outputs
END shftreg8;

ARCHITECTURE behavioural OF shftreg8 IS


BEGIN
PROCESS
--declare variable to hold register state
VARIABLE state : BIT_VECTOR(0 TO 7) := "00000000";
BEGIN
--synchronise process to rising edges of clock
WAIT UNTIL clock'EVENT AND clock = '1';
CASE mode IS
WHEN "00" => state := state; --disabled
WHEN "10" =>
FOR i IN 0 TO 7 LOOP --shift
left
IF i = 7 THEN
state(i) := serinl;
ELSE
state(i) := state(i + 1);
END IF;
END LOOP;
WHEN "01" =>
FOR i IN 7 DOWNTO 0 LOOP --shift
right
IF i = 0 THEN
state(i) := serinr;
ELSE
state(i) := state(i - 1);
END IF;
END LOOP;
WHEN "11" => state := parin; --parallel
load
END CASE;
--assign variable to parallel output port
parout <= state;
END PROCESS;
END behavioural;

Structural Description of an 8-bit Shift Register

ENTITY dtff IS
GENERIC(initial : BIT := '1'); --initial value of q
PORT(d, clock : IN BIT; q : BUFFER BIT := initial);
END dtff;

ARCHITECTURE zero_delay OF dtff IS


BEGIN

q <= d WHEN (clock'EVENT AND clock = '1');

END zero_delay;

--Structural model of an 8-bit universal shift register


--makes use of D-type flip flop component and generate statement
ENTITY shftreg8 IS
PORT(clock, serinl, serinr : IN BIT; mode : IN BIT_VECTOR(0 TO 1);
parin : IN BIT_VECTOR(0 TO 7);
parout : BUFFER BIT_VECTOR(0 TO 7));
END shftreg8;

ARCHITECTURE structural OF shftreg8 IS

COMPONENT dtff
GENERIC(initial : BIT := '1');
PORT(d, clock : IN BIT; q : BUFFER BIT := initial);
END COMPONENT;

FOR ALL : dtff USE ENTITY work.dtff(zero_delay);


SIGNAL datain : BIT_VECTOR(0 TO 7);

BEGIN

reg_cells : FOR i IN 0 TO 7
GENERATE

reg_stage : dtff GENERIC MAP ('0') PORT MAP (datain(i) , clock, parout(i));

lsb_stage : IF i = 0 GENERATE
datain(i) <= parin(i) WHEN mode = "00" ELSE serinl WHEN mode = "10"
ELSE parout(i + 1) WHEN mode = "01" ELSE parout(i);
END GENERATE;

msb_stage : IF i = 7 GENERATE
datain(i) <= parin(i) WHEN mode = "00" ELSE parout(i - 1) WHEN mode = "10"
ELSE serinr WHEN mode = "01" ELSE parout(i);
END GENERATE;

middle_stages : IF (i > 0) AND (i < 7) GENERATE


datain(i) <= parin(i) WHEN mode = "00" ELSE parout(i - 1) WHEN mode = "10"
ELSE parout(i + 1) WHEN mode = "01" ELSE parout(i);
END GENERATE;

END GENERATE;

END structural;

8-bit Unsigned Multiplier

library IEEE;
use IEEE.Std_logic_1164.all;
use IEEE.Std_logic_unsigned.all;
entity MUL8X8 is
port(A, B : in Std_logic_vector(7 downto 0);
PROD : out Std_logic_vector(15 downto 0));
end MUL8X8;

architecture SYNP of MUL8X8 is


begin
PROD <= A * B;
end SYNP;

n-bit Adder using the Generate Statement

ENTITY addn IS
GENERIC(n : POSITIVE := 3); --no. of bits less one
PORT(addend, augend : IN BIT_VECTOR(0 TO n);
carry_in : IN BIT; carry_out, overflow : OUT BIT;
sum : OUT BIT_VECTOR(0 TO n));
END addn;

ARCHITECTURE generated OF addn IS


SIGNAL carries : BIT_VECTOR(0 TO n);
BEGIN
addgen : FOR i IN addend'RANGE
GENERATE
lsadder : IF i = 0 GENERATE
sum(i) <= addend(i) XOR augend(i) XOR carry_in;
carries(i) <= (addend(i) AND augend(i)) OR
(addend(i) AND carry_in) OR
(carry_in AND augend(i));
END GENERATE;
otheradder : IF i /= 0 GENERATE
sum(i) <= addend(i) XOR augend(i) XOR carries(i-1);
carries(i) <= (addend(i) AND augend(i)) OR
(addend(i) AND carries(i-1)) OR
(carries(i-1) AND augend(i));
END GENERATE;
END GENERATE;
carry_out <= carries(n);
overflow <= carries(n-1) XOR carries(n);
END generated;

A Variety of Adder Styles

------------------------------------------------------------------------
-- Single-bit adder
------------------------------------------------------------------------
library IEEE;
use IEEE.std_logic_1164.all;
entity adder is
port (a : in std_logic;
b : in std_logic;
cin : in std_logic;
sum : out std_logic;
cout : out std_logic);
end adder;

-- description of adder using concurrent signal assignments


architecture rtl of adder is
begin
sum <= (a xor b) xor cin;
cout <= (a and b) or (cin and a) or (cin and b);
end rtl;

-- description of adder using component instantiation statements

--Miscellaneous Logic Gates


use work.gates.all;
architecture structural of adder is
signal xor1_out,
and1_out,
and2_out,
or1_out : std_logic;
begin
xor1: xorg port map(
in1 => a,
in2 => b,
out1 => xor1_out);
xor2: xorg port map(
in1 => xor1_out,
in2 => cin,
out1 => sum);
and1: andg port map(
in1 => a,
in2 => b,
out1 => and1_out);
or1: org port map(
in1 => a,
in2 => b,
out1 => or1_out);

and2: andg port map(


in1 => cin,
in2 => or1_out,
out1 => and2_out);
or2: org port map(
in1 => and1_out,
in2 => and2_out,
out1 => cout);
end structural;

------------------------------------------------------------------------
-- N-bit adder
-- The width of the adder is determined by generic N
------------------------------------------------------------------------
library IEEE;
use IEEE.std_logic_1164.all;
entity adderN is
generic(N : integer := 16);
port (a : in std_logic_vector(N downto 1);
b : in std_logic_vector(N downto 1);
cin : in std_logic;
sum : out std_logic_vector(N downto 1);
cout : out std_logic);
end adderN;

-- structural implementation of the N-bit adder


architecture structural of adderN is
component adder
port (a : in std_logic;
b : in std_logic;
cin : in std_logic;
sum : out std_logic;
cout : out std_logic);
end component;

signal carry : std_logic_vector(0 to N);


begin
carry(0) <= cin;
cout <= carry(N);

-- instantiate a single-bit adder N times


gen: for I in 1 to N generate
add: adder port map(
a => a(I),
b => b(I),
cin => carry(I - 1),
sum => sum(I),
cout => carry(I));
end generate;
end structural;

-- behavioral implementation of the N-bit adder


architecture behavioral of adderN is
begin
p1: process(a, b, cin)
variable vsum : std_logic_vector(N downto 1);
variable carry : std_logic;
begin
carry := cin;
for i in 1 to N loop
vsum(i) := (a(i) xor b(i)) xor carry;
carry := (a(i) and b(i)) or (carry and (a(i) or b(i)));
end loop;
sum <= vsum;
cout <= carry;
end process p1;
end behavioral;

n-Bit Synchronous Counter

LIBRARY ieee;
USE ieee.Std_logic_1164.ALL;
USE ieee.Std_logic_unsigned.ALL;
ENTITY cntrnbit IS
GENERIC(n : Positive := 8);
PORT(clock, reset, enable : IN Std_logic;
count : OUT Std_logic_vector((n-1) DOWNTO 0));
END cntrnbit;

ARCHITECTURE v1 OF cntrnbit IS
SIGNAL count_int : Std_logic_vector((n-1) DOWNTO 0);
BEGIN

PROCESS
BEGIN
WAIT UNTIL rising_edge(clock);
IF reset = '1' THEN
count_int <= (OTHERS => '0');
ELSIF enable = '1' THEN
count_int <= count_int + 1;
ELSE
NULL;
END IF;
END PROCESS;
count <= count_int;
END v1;

Moore State Machine with Concurrent Output Logic


library ieee;
use ieee.std_logic_1164.all;

entity moore1 is port(


clk, rst: in std_logic;
id: in std_logic_vector(3 downto 0);
y: out std_logic_vector(1 downto 0));
end moore1;

architecture archmoore1 of moore1 is


type states is (state0, state1, state2, state3, state4);
signal state: states;
begin
moore: process (clk, rst) --this process defines the next state only
begin
if rst='1' then
state <= state0;
elsif (clk'event and clk='1') then
case state is
when state0 =>
if id = x"3" then
state <= state1;
else
state <= state0;
end if;
when state1 =>
state <= state2;
when state2 =>
if id = x"7" then
state <= state3;
else
state <= state2;
end if;
when state3 =>
if id < x"7" then
state <= state0;
elsif id = x"9" then
state <= state4;
else
state <= state3;
end if;
when state4 =>
if id = x"b" then
state <= state0;
else
state <= state4;
end if;
end case;
end if;
end process;

--assign state outputs concurrently;


y <= "00" when (state=state0) else
"10" when (state=state1 or state=state3) else
"11";

end archmoore1;

Mealy State Machine with Registered Outputs

library ieee;
use ieee.std_logic_1164.all;

entity mealy1 is port(


clk, rst: in std_logic;
id: in std_logic_vector(3 downto 0);
y: out std_logic_vector(1 downto 0));
end mealy1;

architecture archmealy of mealy1 is


type states is (state0, state1, state2, state3, state4);
signal state: states;
begin
moore: process (clk, rst)
begin
if rst='1' then
state <= state0;
y <= "00";
elsif (clk'event and clk='1') then
case state is
when state0 =>
if id = x"3" then
state <= state1;
y <= "10";
else
state <= state0;
y <= "00";
end if;
when state1 =>
state <= state2;
y <= "11";
when state2 =>
if id = x"7" then
state <= state3;
y <= "10";
else
state <= state2;
y <= "11";
end if;
when state3 =>
if id < x"7" then
state <= state0;
y <= "00";
elsif id = x"9" then
state <= state4;
y <= "11";
else
state <= state3;
y <= "10";
end if;
when state4 =>
if id = x"b" then
state <= state0;
y <= "00";
else
state <= state4;
y <= "11";
end if;
end case;
end if;
end process;

end archmealy;

Moore State Machine with explicit state encoding

library ieee;
use ieee.std_logic_1164.all;

entity moore2 is port(


clk, rst: in std_logic;
id: in std_logic_vector(3 downto 0);
y: out std_logic_vector(1 downto 0));
end moore2;

architecture archmoore2 of moore2 is


signal state: std_logic_vector(2 downto 0);
-- State assignment is such that 2 LSBs are outputs
constant state0: std_logic_vector(2 downto 0) := "000";
constant state1: std_logic_vector(2 downto 0) := "010";
constant state2: std_logic_vector(2 downto 0) := "011";
constant state3: std_logic_vector(2 downto 0) := "110";
constant state4: std_logic_vector(2 downto 0) := "111";
begin
moore: process (clk, rst)
begin
if rst='1' then
state <= state0;
elsif (clk'event and clk='1') then
case state is

when state0 =>


if id = x"3" then
state <= state1;
else
state <= state0;
end if;
when state1 =>
state <= state2;
when state2 =>
if id = x"7" then
state <= state3;
else
state <= state2;
end if;
when state3 =>
if id < x"7" then
state <= state0;
elsif id = x"9" then
state <= state4;
else
state <= state3;
end if;
when state4 =>
if id = x"b" then
state <= state0;
else
state <= state4;
end if;
when others =>
state <= state0;
end case;
end if;
end process;

--assign state outputs (equal to state std_logics)


y <= state(1 downto 0);
end archmoore2;

State Machine with Moore and Mealy outputs

library ieee;
use ieee.std_logic_1164.all;

entity mealy1 is port(


clk, rst: in std_logic;
id: in std_logic_vector(3 downto 0);
w: out std_logic;
y: out std_logic_vector(1 downto 0));
end mealy1;

architecture archmealy1 of mealy1 is


type states is (state0, state1, state2, state3, state4);
signal state: states;
begin
moore: process (clk, rst)
begin
if rst='1' then
state <= state0;
elsif (clk'event and clk='1') then
case state is
when state0 =>
if id = x"3" then
state <= state1;
else
state <= state0;
end if;
when state1 =>
state <= state2;
when state2 =>
if id = x"7" then
state <= state3;
else
state <= state2;
end if;
when state3 =>
if id < x"7" then
state <= state0;
elsif id = x"9" then
state <= state4;
else
state <= state3;
end if;
when state4 =>
if id = x"b" then
state <= state0;
else
state <= state4;
end if;
end case;
end if;
end process;

--assign moore state outputs;


y <= "00" when (state=state0) else
"10" when (state=state1 or state=state3) else
"11";
--assign mealy output;
w <= '0' when (state=state3 and id < x"7") else
'1';
end archmealy1;

Multiplexer 16-to-4 using if-then-elsif-else Statement

library ieee;
use ieee.std_logic_1164.all;

entity mux is port(


a, b, c, d: in std_logic_vector(3 downto 0);
s: in std_logic_vector(1 downto 0);
x: out std_logic_vector(3 downto 0));
end mux;

architecture archmux of mux is


begin
mux4_1: process (a, b, c, d)
begin
if s = "00" then
x <= a;
elsif s = "01" then
x <= b;
elsif s = "10" then
x <= c;
else
x <= d;
end if;
end process mux4_1;
end archmux;

Multiplexer 16-to-4 using Conditional Signal Assignment Statement

library ieee;
use ieee.std_logic_1164.all;

entity mux is port(


a, b, c, d: in std_logic_vector(3 downto 0);
s: in std_logic_vector(1 downto 0);
x: out std_logic_vector(3 downto 0));
end mux;

architecture archmux of mux is


begin
x <= a when (s = "00") else
b when (s = "01") else
c when (s = "10") else
d;
end archmux;

Multiplexer 16-to-4 using Selected Signal Assignment Statement

library ieee;
use ieee.std_logic_1164.all;

entity mux is port(


a, b, c, d: in std_logic_vector(3 downto 0);
s: in std_logic_vector(1 downto 0);
x: out std_logic_vector(3 downto 0));
end mux;

architecture archmux of mux is


begin

with s select
x <= a when "00",
b when "01",
c when "10",
d when "11",
(others => 'X') when others;

end archmux;

Miscellaneous Logic Gates

------------------------------------------------------------------------
-- package with component declarations
------------------------------------------------------------------------
library IEEE;
use IEEE.std_logic_1164.all;
package gates is
component andg
generic (tpd_hl : time := 1 ns;
tpd_lh : time := 1 ns);
port (in1, in2 : std_ulogic;
out1 : out std_ulogic);
end component;
component org
generic (tpd_hl : time := 1 ns;
tpd_lh : time := 1 ns);
port (in1, in2 : std_logic;
out1 : out std_logic);
end component;
component xorg
generic (tpd_hl : time := 1 ns;
tpd_lh : time := 1 ns);
port (in1, in2 : std_logic;
out1 : out std_logic);
end component;
end gates;

------------------------------------------------------------------------
-- and gate
------------------------------------------------------------------------
library IEEE;
use IEEE.std_logic_1164.all;
entity andg is
generic (tpd_hl : time := 1 ns;
tpd_lh : time := 1 ns);
port (in1, in2 : std_ulogic;
out1 : out std_ulogic);
end andg;

architecture only of andg is


begin
p1: process(in1, in2)
variable val : std_logic;
begin
val := in1 and in2;
case val is
when '0' =>
out1 <= '0' after tpd_hl;
when '1' =>
out1 <= '1' after tpd_lh;
when others =>
out1 <= val;
end case;
end process;
end only;

------------------------------------------------------------------------
-- or gate
------------------------------------------------------------------------
library IEEE;
use IEEE.std_logic_1164.all;

entity org is
generic (tpd_hl : time := 1 ns;
tpd_lh : time := 1 ns);
port (in1, in2 : std_logic;
out1 : out std_logic);
end org;
architecture only of org is
begin
p1: process(in1, in2)
variable val : std_logic;
begin
val := in1 or in2;
case val is
when '0' =>
out1 <= '0' after tpd_hl;
when '1' =>
out1 <= '1' after tpd_lh;
when others =>
out1 <= val;
end case;
end process;
end only;

------------------------------------------------------------------------
-- exclusive or gate
------------------------------------------------------------------------
library IEEE;
use IEEE.std_logic_1164.all;

entity xorg is
generic (tpd_hl : time := 1 ns;
tpd_lh : time := 1 ns);
port (in1, in2 : std_logic;
out1 : out std_logic);
end xorg;
architecture only of xorg is
begin
p1: process(in1, in2)
variable val : std_logic;
begin
val := in1 xor in2;
case val is
when '0' =>
out1 <= '0' after tpd_hl;
when '1' =>
out1 <= '1' after tpd_lh;
when others =>
out1 <= val;
end case;
end process;
end only;

M68008 Address Decoder

--Address decoder for the m68008


--asbar must be '0' to enable any output
--csbar(0) : X"00000" to X"01FFF"
--csbar(1) : X"40000" to X"43FFF"
--csbar(2) : X"08000" to X"0AFFF"
--csbar(3) : X"E0000" to X"E01FF"
library ieee;
use ieee.std_logic_1164.all;
entity addrdec is
port(
asbar : in std_logic;
address : in std_logic_vector(19 downto 0);
csbar : out std_logic_vector(3 downto 0)
);
end entity addrdec;

architecture v1 of addrdec is
begin

csbar(0) <= '0' when


((asbar = '0') and
((address >= X"00000") and (address <= X"01FFF")))
else '1';

csbar(1) <= '0' when


((asbar = '0') and
((address >= X"40000") and (address <= X"43FFF")))
else '1';

csbar(2) <= '0' when


((asbar = '0') and
((address >= X"08000") and (address <= X"0AFFF")))
else '1';

csbar(3) <= '0' when


((asbar = '0') and
((address >= X"E0000") and (address <= X"E01FF")))
else '1';

end architecture v1;

Highest Priority Encoder

entity priority is
port(I : in bit_vector(7 downto 0); --inputs to be prioritised
A : out bit_vector(2 downto 0); --encoded output
GS : out bit); --group signal output
end priority;

architecture v1 of priority is
begin
process(I)
begin
GS <= '1'; --set default outputs
A <= "000";
if I(7) = '1' then
A <= "111";
elsif I(6) = '1' then
A <= "110";
elsif I(5) = '1' then
A <= "101";
elsif I(4) = '1' then
A <= "100";
elsif I(3) = '1' then
A <= "011";
elsif I(2) = '1' then
A <= "010";
elsif I(1) = '1' then
A <= "001";
elsif I(0) = '1' then
A <= "000";
else
GS <= '0';
end if;
end process;
end v1;

N-input AND Gate

--an n-input AND gate


entity and_n is
generic(n : positive := 8); --no. of inputs
port(A : in bit_vector((n-1) downto 0);
F : out bit);
end and_n;

architecture using_loop of and_n is


begin
process(A)
variable TEMP_F : bit;
begin
TEMP_F := '1';
for i in A'range loop
TEMP_F := TEMP_F and A(i);
end loop;
F <= TEMP_F;
end process;
end using_loop;
Advanced Electronic Design Automation

VHDL Language Syntax Guide

This HTML file contains the complete syntax of the VHDL language as defined by the 1993 standard.

The syntax is presented alphabetically in BNF format. Reserved identifiers are coloured blue and shown in bold type face,
braces { } enclose items which are optional and repeatable while square brackets [ ] enclose items which are optional but can
only occur once.

All other punctuation characters, such as parentheses ( ) and commas, are a required part of the construct. Where a number
of alternatives exist for a particular construct, these are separated by the '|' character.

Operators are shown in red.

Underlined items are hypertext links to other parts of the document where more information is provided about a particular
aspect of the syntax.

A VHDL design comprises one or more ASCII text files containing design units. Click on design_file to go to top of the
hierarchy of a VHDL description.

Alternatively, click on any of the VHDL reserved identifiers listed in the table below to go directly to the syntax for the
corresponding language construct.

Table of VHDL Reserved Identifiers

abs exit not signal

access file null shared

after for of sla

alias function on sll

all generate open sra

and generic or srl

architecture group others subtype

array guarded out then

assert if package to

attribute impure port transport

begin in postponed type


block inertial procedure unaffected

body inout process units

buffer is pure until

bus label range use

case library record variable

component linkage register wait

configuration literal reject when

constant loop rem while

disconnect map report with

downto mod return xnor

else nand rol xor

elsif new ror

end next select

entity nor severity

abstract_literal ::= decimal_literal | based_literal

access_type_definition ::= access subtype_indication

actual_designator ::=

expression

| signal_name

| variable_name

| file_name

| open

actual_parameter_part ::= parameter_association_list

actual_part ::=

actual_designator
| function_name ( actual_designator )

| type_mark ( actual_designator )

adding_operator ::= + | - | &

aggregate ::=

( element_association { , element_association } )

alias_declaration ::=

alias alias_designator [ : subtype_indication ] is name [ signature ]


;

alias_designator ::= identifier | character_literal | operator_symbol

allocator ::=

new subtype_indication

| new qualified_expression

architecture_body ::=

architecture identifier of entity_name is

architecture_declarative_part

begin

architecture_statement_part

end [ architecture ] [ architecture_simple_name ] ;

architecture_declarative_part ::=

{ block_declarative_item }

architecture_statement_part ::=
{ concurrent_statement }

array_type_definition ::=

unconstrained_array_definition | constrained_array_definition

assertion ::=

assert condition

[ report expression ]

[ severity expression ]

assertion_statement ::= [ label : ] assertion ;

association_element ::=

[ formal_part => ] actual_part

association_list ::=

association_element { , association_element }

attribute_declaration ::=

attribute identifier : type_mark ;

attribute_designator ::= attribute_simple_name

attribute_name ::=

prefix [ signature ] ' attribute_designator [ ( expression ) ]

attribute_specification ::=

attribute attribute_designator of entity_specification is expression


;
base ::= integer

base_specifier ::= B | O | X

base_unit_declaration ::= identifier ;

based_integer ::=

extended_digit { [ underline ] extended_digit }

based_literal ::=

base # based_integer [ . based_integer ] # [ exponent ]

basic_character ::=

basic_graphic_character | format_effector

basic_graphic_character ::=

upper_case_letter | digit | special_character| space_character

basic_identifier ::=

letter { [ underline ] letter_or_digit }

binding_indication ::=

[ use entity_aspect ]

[ generic_map_aspect ]

[ port_map_aspect ]

bit_string_literal ::= base_specifier " bit_value "


bit_value ::= extended_digit { [ underline ] extended_digit }

block_configuration ::=

for block_specification

{ use_clause }

{ configuration_item }

end for ;

block_declarative_item ::=

subprogram_declaration

| subprogram_body

| type_declaration

| subtype_declaration

| constant_declaration

| signal_declaration

| shared_variable_declaration

| file_declaration

| alias_declaration

| component_declaration

| attribute_declaration

| attribute_specification

| configuration_specification

| disconnection_specification

| use_clause

| group_template_declaration

| group_declaration
block_declarative_part ::=

{ block_declarative_item }

block_header ::=

[ generic_clause

[ generic_map_aspect ; ] ]

[ port_clause

[ port_map_aspect ; ] ]

block_specification ::=

architecture_name

| block_statement_label

| generate_statement_label [ ( index_specification ) ]

block_statement ::=

block_label :

block [ ( guard_expression ) ] [ is ]

block_header

block_declarative_part

begin

block_statement_part

end block [ block_label ] ;

block_statement_part ::=

{ concurrent_statement }

case_statement ::=

[ case_label : ]
case expression is

case_statement_alternative

{ case_statement_alternative }

end case [ case_label ] ;

case_statement_alternative ::=

when choices =>

sequence_of_statements

character_literal ::= ' graphic_character '

choice ::=

simple_expression

| discrete_range

| element_simple_name

| others

choices ::= choice { | choice }

component_configuration ::=

for component_specification

[ binding_indication ; ]

[ block_configuration ]

end for ;

component_declaration ::=

component identifier [ is ]

[ local_generic_clause ]
[ local_port_clause ]

end component [ component_simple_name ] ;

component_instantiation_statement ::=

instantiation_label :

instantiated_unit

[ generic_map_aspect ]

[ port_map_aspect ] ;

component_specification ::=

instantiation_list : component_name

composite_type_definition ::=

array_type_definition

| record_type_definition

concurrent_assertion_statement ::=

[ label : ] [ postponed ] assertion ;

concurrent_procedure_call_statement ::=

[ label : ] [ postponed ] procedure_call ;

concurrent_signal_assignment_statement ::=

[ label : ] [ postponed ] conditional_signal_assignment

| [ label : ] [ postponed ] selected_signal_assignment

concurrent_statement ::=

block_statement
| process_statement

| concurrent_procedure_call_statement

| concurrent_assertion_statement

| concurrent_signal_assignment_statement

| component_instantiation_statement

| generate_statement

condition ::= boolean_expression

condition_clause ::= until condition

conditional_signal_assignment ::=

target <= options conditional_waveforms ;

conditional_waveforms ::=

{ waveform when condition else }

waveform [ when condition ]

configuration_declaration ::=

configuration identifier of entity_name is

configuration_declarative_part

block_configuration

end [ configuration ] [ configuration_simple_name ] ;

configuration_declarative_item ::=

use_clause

| attribute_specification
| group_declaration

configuration_declarative_part ::=

{ configuration_declarative_item }

configuration_item ::=

block_configuration

| component_configuration

configuration_specification ::=

for component_specification binding_indication ;

constant_declaration ::=

constant identifier_list : subtype_indication [ := expression ] ;

constrained_array_definition ::=

array index_constraint of element_subtype_indication

constraint ::=

range_constraint

| index_constraint

context_clause ::= { context_item }

context_item ::=

library_clause

| use_clause

decimal_literal ::= integer [ . integer ] [ exponent ]


declaration ::=

type_declaration

| subtype_declaration

| object_declaration

| interface_declaration

| alias_declaration

| attribute_declaration

| component_declaration

| group_template_declaration

| group_declaration

| entity_declaration

| configuration_declaration

| subprogram_declaration

| package_declaration

delay_mechanism ::=

transport

| [ reject time_expression ] inertial

design_file ::= design_unit { design_unit }

design_unit ::= context_clause library_unit

designator ::= identifier | operator_symbol

direction ::= to | downto


disconnection_specification ::=

disconnect guarded_signal_specification after time_expression ;

discrete_range ::= discrete_subtype_indication | range

element_association ::=

[ choices => ] expression

element_declaration ::=

identifier_list : element_subtype_definition ;

element_subtype_definition ::= subtype_indication

entity_aspect ::=

entity entity_name [ ( architecture_identifier) ]

| configuration configuration_name

| open

entity_class ::=

entity | architecture | configuration

| procedure | function | package

| type | subtype | constant

| signal | variable | component

| label | literal | units

| group | file

entity_class_entry ::= entity_class [ <> ]


entity_class_entry_list ::=

entity_class_entry { , entity_class_entry }

entity_declaration ::=

entity identifier is

entity_header

entity_declarative_part

[ begin

entity_statement_part ]

end [ entity ] [ entity_simple_name ] ;

entity_declarative_item ::=

subprogram_declaration

| subprogram_body

| type_declaration

| subtype_declaration

| constant_declaration

| signal_declaration

| shared_variable_declaration

| file_declaration

| alias_declaration

| attribute_declaration

| attribute_specification

| disconnection_specification

| use_clause

| group_template_declaration

| group_declaration
entity_declarative_part ::=

{ entity_declarative_item }

entity_designator ::= entity_tag [ signature ]

entity_header ::=

[ formal_generic_clause ]

[ formal_port_clause ]

entity_name_list ::=

entity_designator { , entity_designator }

| others

| all

entity_specification ::=

entity_name_list : entity_class

entity_statement ::=

concurrent_assertion_statement

| passive_concurrent_procedure_call_statement

| passive_process_statement

entity_statement_part ::=

{ entity_statement }

entity_tag ::= simple_name | character_literal | operator_symbol

enumeration_literal ::= identifier | character_literal


enumeration_type_definition ::=

( enumeration_literal { , enumeration_literal } )

exit_statement ::=

[ label : ] exit [ loop_label ] [ when condition ] ;

exponent ::= E [ + ] integer | E - integer

expression ::=

relation { and relation }

| relation { or relation }

| relation { xor relation }

| relation [ nand relation ]

| relation [ nor relation ]

| relation { xnor relation }

extended_digit ::= digit | letter

extended_identifier ::=

\ graphic_character { graphic_character } \

factor ::=

primary [ ** primary ]

| abs primary

| not primary

file_declaration ::=
file identifier_list : subtype_indication file_open_information ] ;

file_logical_name ::= string_expression

file_open_information ::=

[ open file_open_kind_expression ] is file_logical_name

file_type_definition ::=

file of type_mark

floating_type_definition := range_constraint

formal_designator ::=

generic_name

| port_name

| parameter_name

formal_parameter_list ::= parameter_interface_list

formal_part ::=

formal_designator

| function_name ( formal_designator )

| type_mark ( formal_designator )

full_type_declaration ::=

type identifier is type_definition ;

function_call ::=

function_name [ ( actual_parameter_part ) ]
generate_statement ::=

generate_label :

generation_scheme generate

[ { block_declarative_item }

begin ]

{ concurrent_statement }

end generate [ generate_label ] ;

generation_scheme ::=

for generate_parameter_specification

| if condition

generic_clause ::=

generic ( generic_list ) ;

generic_list ::= generic_interface_list

generic_map_aspect ::=

generic map ( generic_association_list )

graphic_character ::=

basic_graphic_character | lower_case_letter |
other_special_character

group_constituent ::= name | character_literal

group_constituent_list ::= group_constituent { , group_constituent }


group_template_declaration ::=

group identifier is ( entity_class_entry_list ) ;

group_declaration ::=

group identifier : group_template_name ( group_constituent_list ) ;

guarded_signal_specification ::=

guarded_signal_list : type_mark

identifier ::=

basic_identifier | extended_identifier

identifier_list ::= identifier { , identifier }

if_statement ::=

[ if_label : ]

if condition then

sequence_of_statements

{ elsif condition then

sequence_of_statements }

[ else

sequence_of_statements ]

end if [ if_label ] ;

incomplete_type_declaration ::= type identifier ;

index_constraint ::= ( discrete_range { , discrete_range } )


index_specification ::=

discrete_range

| static_expression

index_subtype_definition ::= type_mark range <>

indexed_name ::= prefix ( expression { , expression } )

instantiated_unit ::=

[ component ] component_name

| entity entity_name [ ( architecture_identifier ) ]

| configuration configuration_name

instantiation_list ::=

instantiation_label { , instantiation_label }

| others

| all

integer ::= digit { [ underline ] digit }

integer_type_definition ::= range_constraint

interface_constant_declaration ::=

[ constant ] identifier_list : [ in ] subtype_indication [ :=


static_expression ]

interface_declaration ::=

interface_constant_declaration

| interface_signal_declaration
| interface_variable_declaration

| interface_file_declaration

interface_element ::= interface_declaration

interface_file_declaration ::=

file identifier_list : subtype_indication

interface_list ::=

interface_element { ; interface_element }

interface_signal_declaration ::=

[signal] identifier_list : [ mode ] subtype_indication [ bus ] [ :=


static_expression ]

interface_variable_declaration ::=

[variable] identifier_list : [ mode ] subtype_indication [ :=


static_expression ]

iteration_scheme ::=

while condition

| for loop_parameter_specification

label ::= identifier

letter ::= upper_case_letter | lower_case_letter

letter_or_digit ::= letter | digit


library_clause ::= library logical_name_list ;

library_unit ::=

primary_unit

| secondary_unit

literal ::=

numeric_literal

| enumeration_literal

| string_literal

| bit_string_literal

| null

logical_name ::= identifier

logical_name_list ::= logical_name { , logical_name }

logical_operator ::= and | or | nand | nor | xor | xnor

loop_statement ::=

[ loop_label : ]

[ iteration_scheme ] loop

sequence_of_statements

end loop [ loop_label ] ;

miscellaneous_operator ::= ** | abs | not

mode ::= in | out | inout | buffer | linkage


multiplying_operator ::= * | / | mod | rem

name ::=

simple_name

| operator_symbol

| selected_name

| indexed_name

| slice_name

| attribute_name

next_statement ::=

[ label : ] next [ loop_label ] [ when condition ] ;

null_statement ::= [ label : ] null ;

numeric_literal ::=

abstract_literal

| physical_literal

object_declaration ::=

constant_declaration

| signal_declaration

| variable_declaration

| file_declaration

operator_symbol ::= string_literal

options ::= [ guarded ] [ delay_mechanism ]


package_body ::=

package body package_simple_name is

package_body_declarative_part

end [ package body ] [ package_simple_name ] ;

package_body_declarative_item ::=

subprogram_declaration

| subprogram_body

| type_declaration

| subtype_declaration

| constant_declaration

| shared_variable_declaration

| file_declaration

| alias_declaration

| use_clause

| group_template_declaration

| group_declaration

package_body_declarative_part ::=

{ package_body_declarative_item }

package_declaration ::=

package identifier is

package_declarative_part

end [ package ] [ package_simple_name ] ;

package_declarative_item ::=
subprogram_declaration

| type_declaration

| subtype_declaration

| constant_declaration

| signal_declaration

| shared_variable_declaration

| file_declaration

| alias_declaration

| component_declaration

| attribute_declaration

| attribute_specification

| disconnection_specification

| use_clause

| group_template_declaration

| group_declaration

package_declarative_part ::=

{ package_declarative_item }

parameter_specification ::=

identifier in discrete_range

physical_literal ::= [ abstract_literal ] unit_name

physical_type_definition ::=

range_constraint

units
base_unit_declaration

{ secondary_unit_declaration }

end units [ physical_type_simple_name ]

port_clause ::=

port ( port_list ) ;

port_list ::= port_interface_list

port_map_aspect ::=

port map ( port_association_list )

prefix ::=

name

| function_call

primary ::=

name

| literal

| aggregate

| function_call

| qualified_expression

| type_conversion

| allocator

| ( expression )

primary_unit ::=
entity_declaration

| configuration_declaration

| package_declaration

procedure_call ::= procedure_name [ ( actual_parameter_part ) ]

procedure_call_statement ::=

[ label : ] procedure_call ;

process_declarative_item ::=

subprogram_declaration

| subprogram_body

| type_declaration

| subtype_declaration

| constant_declaration

| variable_declaration

| file_declaration

| alias_declaration

| attribute_declaration

| attribute_specification

| use_clause

| group_template_declaration

| group_declaration

process_declarative_part ::=

{ process_declarative_item }

process_statement ::=
[ process_label : ]

[ postponed ] process [ ( sensitivity_list ) ] [ is ]

process_declarative_part

begin

process_statement_part

end [ postponed ] process [ process_label ] ;

process_statement_part ::=

{ sequential_statement }

qualified_expression ::=

type_mark ' ( expression )

| type_mark ' aggregate

range ::=

range_attribute_name

| simple_expression direction simple_expression

range_constraint ::= range range

record_type_definition ::=

record

element_declaration

{ element_declaration }

end record [ record_type_simple_name ]

relation ::=

shift_expression [ relational_operator shift_expression ]


relational_operator ::= = | /= | < | <= | > | >=

report_statement ::=

[ label : ]

report expression

[ severity expression ] ;

return_statement ::=

[ label : ] return [ expression ] ;

scalar_type_definition ::=

enumeration_type_definition | integer_type_definition

| floating_type_definition | physical_type_definition

secondary_unit ::=

architecture_body

| package_body

secondary_unit_declaration ::= identifier = physical_literal ;

selected_name ::= prefix . suffix

selected_signal_assignment ::=

with expression select

target <= options selected_waveforms ;

selected_waveforms ::=
{ waveform when choices , }

waveform when choices

sensitivity_clause ::= on sensitivity_list

sensitivity_list ::= signal_name { , signal_name }

sequence_of_statements ::=

{ sequential_statement }

sequential_statement ::=

wait_statement

| assertion_statement

| report_statement

| signal_assignment_statement

| variable_assignment_statement

| procedure_call_statement

| if_statement

| case_statement

| loop_statement

| next_statement

| exit_statement

| return_statement

| null_statement

shift_expression ::=

simple_expression [ shift_operator simple_expression ]


shift_operator ::= sll | srl | sla | sra | rol | ror

sign ::= + | -

signal_assignment_statement ::=

[ label : ] target <= [ delay_mechanism ] waveform ;

signal_declaration ::=

signal identifier_list : subtype_indication [ signal_kind ] [ :=


expression ] ;

signal_kind ::= register | bus

signal_list ::=

signal_name { , signal_name }

| others

| all

signature ::= [ [ type_mark { , type_mark } ] [ return type_mark ] ]

simple_expression ::=

[ sign ] term { adding_operator term }

simple_name ::= identifier

slice_name ::= prefix ( discrete_range )

string_literal ::= " { graphic_character } "


subprogram_body ::=

subprogram_specification is

subprogram_declarative_part

begin

subprogram_statement_part

end [ subprogram_kind ] [ designator ] ;

subprogram_declaration ::=

subprogram_specification ;

subprogram_declarative_item ::=

subprogram_declaration

| subprogram_body

| type_declaration

| subtype_declaration

| constant_declaration

| variable_declaration

| file_declaration

| alias_declaration

| attribute_declaration

| attribute_specification

| use_clause

| group_template_declaration

| group_declaration

subprogram_declarative_part ::=
{ subprogram_declarative_item }

subprogram_kind ::= procedure | function

subprogram_specification ::=

procedure designator [ ( formal_parameter_list ) ]

| [ pure | impure ] function designator [ ( formal_parameter_list )


]

return type_mark

subprogram_statement_part ::=

{ sequential_statement }

subtype_declaration ::=

subtype identifier is subtype_indication ;

subtype_indication ::=

[ resolution_function_name ] type_mark [ constraint ]

suffix ::=

simple_name

| character_literal

| operator_symbol

| all

target ::=

name

| aggregate
term ::=

factor { multiplying_operator factor }

timeout_clause ::= for time_expression

type_conversion ::= type_mark ( expression )

type_declaration ::=

full_type_declaration

| incomplete_type_declaration

type_definition ::=

scalar_type_definition

| composite_type_definition

| access_type_definition

| file_type_definition

type_mark ::=

type_name

| subtype_name

unconstrained_array_definition ::=

array ( index_subtype_definition { , index_subtype_definition } )

of element_subtype_indication

use_clause ::=

use selected_name { , selected_name } ;


variable_assignment_statement ::=

[ label : ] target := expression ;

variable_declaration ::=

[ shared ] variable identifier_list : subtype_indication [ :=


expression ] ;

wait_statement ::=

[ label : ] wait [ sensitivity_clause ] [ condition_clause ] [


timeout_clause ] ;

waveform ::=

waveform_element { , waveform_element }

| unaffected

waveform_element ::=

value_expression [ after time_expression ]

| null [ after time_expression ]


Advanced Electronic Design Automation
VHDL Quick Reference Guide

The Quick Reference contains the following sections:

● The Design Entity ● Packages


● Pre-defined Types and Literals ● Library and Use Clauses
● Objects and Operators ● IEEE Standard Logic
● Attributes ● IEEE Standard Logic Support
● Structured Types Packages
● Configurations ● Process Templates for Synthesis

The Design Entity


The Design Entity is the basic building block of a VHDL description. It comprises two parts:

● Entity Declaration
● Architecture Body

The Entity Declaration defines the interface of a hardware element (Ports) along with any parameters of the hardware element
(Generics). There can only be one Entity in the library with a given entity_name.

The Architecture Body describes the internals of an Entity in terms of a Behaviour, Structure or Dataflow, or a combination of
these. All statements within an Architecture are concurrent, ie. they execute in parallel. An Entity may have many Architectures
with different architecture_names.
The syntax of the Entity Declaration and the Architecture Body is shown below:

--Entity Declaration with library and use clause

library lib_name;

use lib_name.package_name.all;

entity entity_name is

generic(generic_name : type := default_value);

port(port_names : direction type);

end entity entity_name; --entity[93]

--Architecture Body

architecture arch_name of entity_name is

architecture declarations

begin

concurrent statements

end architecture arch_name; --architecure[93]

Example
entity halfadd is

generic(delay : time := 10 ns);

port(a, b : in bit; sum, carry : out bit);

end entity halfadd;

architecture v1 of halfadd is

begin

sum <= a xor b after delay;

carry <= a and b after delay;

end architecture v1;

The Network Model

A VHDL description of a digital system may be hierarchical, ie composed of a number of Design Entities connected together to
form a network The Ports of each Design Entity allow dynamic information to pass between them. At the top level, the design may
be described in terms of a Test Bench Entity which is a special entity having no ports.

Entity Ports
Most Entity Declarations contain a Port Clause. (The exceptions are usually Test Bench Entities which do not need to
communicate with other Entities). This forms part of the Entity Header and defines the interface to the Design Entity. A Port Clause
defines the name, type and direction of the ports of the Design Entity. As far as the Architecture Body of the Entity is concerned,
the ports are effectively signals which can be accessed by the concurrent statements within the Architecture. The direction or
mode of a port determines the way in which the statements within the Architecture may access it. The table below summarises the
four different port modes.

Direction Properties
The Design Entity can read the port, ie, the name of the port
in can only appear on the right hand side (input) of a signal
assignment statement.

The Design Entity can write to the port, ie. the name of the
out port can only appear on the left hand side (target) of a signal
assignment statement.

Similar to mode out,but the port may be fed back internally


buffer (such as the outputs of a counter). A Buffer port is not
bidirectional.

The Design Entity can both read and drive the bidirectional
inout signal, ie. the name of the port can appear on both sides of a
signal assignment statement.

The image shown below illustrates the logical equivalent of the port modes. Ports of modes IN and OUT are shown in red and blue
respectively. A BUFFER port is shown in green. In this case the signal coming from the AND gate is also feeding the input of
another gate (hence the signal will appear on the right hand side of an assignment statement), but the BUFFER port cannot act as
an input.

The INOUT port, shown in purple, is truly bi-directional, and therefore will usually involve a three-state buffer. When the input IN3 is
active, the three-state buffer will be enabled and the INOUT1 port is driven by the 2-input AND gate. When the IN3 input is
inactive, the three-state buffer is turned off and the INOUT1 port can act as an input to the 2-input OR gate via the buffer.

Generics
Generics are a means of passing instance specific information into a Design Entity. Inside the entity they are effectively a
Constant. They are commonly used to pass time delay values into a Design Entity or to set the size of a scalable description.

Syntax:

entity entity_name is

generic(generic_name : type := default_value);


......port clause

end entity_name;

Examples:

entity myreg is

generic(numbits : positive := 8); --sets width of register

port(clock : in bit;

datain : in bit_vector((numbits - 1) downto 0);

dataout : out bit_vector((numbits - 1) downto 0));

end myreg;

entity mygate is

generic(delay : time := 10 ns); --sets delay of gate

port(a, b : in bit; f : out bit);

end mygate;

..........in the architecture

f <= a xor b after delay;

Concurrent Statements
Concurrent statements are at the heart of a VHDL description; any of the statements listed below may be used within the
statement part of the Architecture Body. The ordering of concurrent statements is not important since they are all effectively active
at the same time. Execution of concurrent statements is determined by signal events communicated from one statement to
another. For example if the same signal appears on the input side of a number of concurrent statements, then all of the statements
would execute at the same time in response to an event on that signal.

Block statement

Process statement

Concurrent Assertion statement

Concurrent Signal Assignment statement

Conditional Signal Assignment

Selected Signal Assignment

Component Instantiation statement

Generate statement
Concurrent Procedure Call

Architecture Local Declarations


The following declarations may appear in the architecture declarative area (between is and begin). The most commonly used are
signal, type and component. These three declare a local signal (to interconnect components or processes), local type (to define
the states of a state machine for example) and local components respectively. Any component used inside an architecture must be
declared, either in the architecture itself, or in a package which is used by the architecture. The exception to this is the use of direct
instantiation whereby a design entity can be instantiated without the need for a component declaration (VHDL 1993 only).

Type declaration

Subtype declaration

Signal declaration

Constant declaration

Component declaration

Function declaration

Procedure declaration

Configuration specification

Sequential Statements
These statements are used within the statement part of a process (between begin and end process) and also within sub-
programs (functions and procedures). Sequential statements execute in the order they are written, much the same as in any
general purpose high level language. The most important sequential statement is probably the wait statement which is often used
in the description of sequential systems. The sequential statements provided in the VHDL language are based on those available
in the ADA language.

wait

sequential signal assignment

variable assignment

if then else

case

loop

next

exit

null
Process Declarations
A variable is commonly declared within the declarative region of a sequential process. The variable may be used within the
process to hold an intermediate value. Assignments to a variable take immediate effect, whereas signals do not get updated until
the end of the entire process (assuming there are no wait statements between the signal assignment and the end of the process).

variable variable_name : type;

variable variable_name : type := initial_value;

example :

--sequential process to model JK flip-flop


process
--declare a local variable to hold ff state
variable state : bit := '0';
begin
--synchronise process to rising edge of clock
wait until (clock'event and clock = '1');
if (j = '1' and k = '1') then --toggle
state := not state;
elsif (j = '0' and k = '1') then --reset
state := '0';
elsif (j = '1' and k = '0') then --set
state := '1';
else --no change
state := state;
end if;
--assign values to output signals
q <= state after 5 ns;
qbar <= not state after 5 ns;
end process;

Pre-defined Types and Literals


The predefined types provided by the VHDL language are defined in Package Standard. This package is included implicitly by all
Design Entities, ie. there is no need to attach a library or use clause to the Design. Some elements of the Standard Package are
implementation dependent, such as the range of predefined type INTEGER for example. The contents of Package Standard are
listed below (for Model technology's V-System/PLUS):

package standard is
type boolean is (false,true);
type bit is ('0', '1');
type character is (
nul, soh, stx, etx, eot, enq, ack, bel,
bs, ht, lf, vt, ff, cr, so, si,
dle, dc1, dc2, dc3, dc4, nak, syn, etb,
can, em, sub, esc, fsp, gsp, rsp, usp,

' ', '!', '"', '#', '$', '%', '&', ''',


'(', ')', '*', '+', ',', '-', '.', '/',
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', ':', ';', '<', '=', '>', '?',

'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',


'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
'X', 'Y', 'Z', '[', '\', ']', '^', '_',

'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',


'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
'x', 'y', 'z', '{', '|', '}', '~', del.....);

type severity_level is (note, warning, error, failure);


type integer is range -2147483648 to 2147483647;
type real is range -1.0E308 to 1.0E308;
type time is range -2147483647 to 2147483647
units
fs;
ps = 1000 fs;
ns = 1000 ps;
us = 1000 ns;
ms = 1000 us;
sec = 1000 ms;
min = 60 sec;
hr = 60 min;
end units;
subtype delay_length is time range 0 fs to time'high;
impure function now return delay_length;
subtype natural is integer range 0 to integer'high;
subtype positive is integer range 1 to integer'high;
type string is array (positive range <>) of character;
type bit_vector is array (natural range <>) of bit;
type file_open_kind is (
read_mode,
write_mode,
append_mode);
type file_open_status is (
open_ok,
status_error,
name_error,
mode_error);
attribute foreign : string;
end standard;

Literals

Integer types

constant freeze : integer := 32; --no decimal point


constant a_pos : positive := 16#ff#; --hexadecimal notation
constant b_nat : natural := 2#10101111#; --binary notation
constant delay_time : time := 10 us; --physical types must have units
Floating point numbers

variable factor : real := 32.0; --decimal point required


constant x : real := 2.2e-6; --exponential form

Enumeration type literals

signal flag : boolean := false; --type boolean can be true or false

constant myconst : boolean := true

type state_type is (s0, s1, s2, s3); --define a user enumeration type

signal ps, ns : state_type := s0; --create signals and initialise

variable temp : bit := '1'; --type bit can be '0' or '1'

signal tied_low : bit := '0';

signal parity : std_logic := 'H'; --could be one of


'U','X','0','1','Z','W','H','L','-'

Array type literals

constant flag : bit_vector(0 to 7) := "11110101"; --bit string literal


variable var1 : bit_vector(7 downto 0) := X"AA"; --hexadecimal notation

signal bus_9_bit : bit_vector(0 to 8);

.........
bus_9_bit <= O"393"; --octal notation

.........

bus_9_bit <= (others => '0'); --aggregate assignment, set all bits to '0'

bus_9_bit <= (0 => '1', 2 => '0', others => '1'); --setting individual bits

signal databus : std_logic_vector(7 downto 0); --ieee std_logic type

.........

databus <= "ZZZZZZZZ"; --setting the bus to high impedance

signal bus_n_bits : std_logic_vector((n-1) downto 0); --an n-bit signal

bus_n_bits <= (others => 'Z'); --sets n bits to 'Z' without a loop

constant message : string := "hello"; --an array of ASCII characters

Objects and Operators


VHDL Objects

Object Properties

Signal Signal objects are used to communicate dynamic events around the model of a hardware system;
they have both a time dimension and a value. When a signal object is assigned a new value, it does
not take on the value immediately, but after an infinitesimally small time delay known as a delta delay.
When a signal changes it is said to have undergone an event. Such an event can trigger further
assignments to take place. Only a signal can have an event occur on it.

Variable Can be changed by a variable assignment statement (usually within a process). A variable changes
immediately and has no time dimension. Variables are most often used within the confines of a
process to keep track of a local value such as the state of a memory element or the value of a counter
register.

Constant A constant object is normally set to a particular value when declared; it cannot be changed by
assignment in the model. Constants are useful for defining ROM contents or fixed parameters.

VHDL Built-in Types

Type Class VHDL Name

Numeric integer(-maxint to +maxint)

positive(1 to +maxint)

natural(0 to +maxint)

real(-maxreal to +maxreal)
Enumeration boolean(false, true)

bit('0', '1')

character(ASCII set)
Array bit_vector(array of bit)

string(array of character)
Physical time(units fs, ps , ns, us....)

VHDL Built-in Operators

Operator Class VHDL Name Operands

+ | - | * | / | mod | rem |
Arithmetic Integer, Real and Physical
abs | **
and | or | not | nand | nor |
xor | xnor
Logical Boolean, Bit and Bit_vector
sll | srl | sra | sla | ror |
rol
Valid for all types, ordering operators
Relational = | /= | < | > | <= | >= work from left to right

Miscellaneous & (Aggregate operator - concatenates two One dimensional arrays


arrays to form a larger array)

Attributes
Attributes supply additional information about an item, ie. a signal, variable, type or component. Certain attributes are predefined
for types, array objects and signals. User defined attributes may be declared. These may have no effect on simulation, and are
often used to supply information to other design tools such as PCB layout or PLD/FPGA synthesis tools.

Listed below are the most commonly used attributes:

Scalar and Array Attributes

Attribute Description - X is a Scalar or Array object

X'high The upper bound of X (or upper index if X is an array)

X'low The lower bound of X (or lower index if X is an array)

X'left The leftmost bound of X(or leftmost index if X is an array)

X'right The rightmost bound of X (or rightmost index if X is an array)

Constrained Array Attributes

Attribute Description - X is a constrained array object

X'range The range of X (often used in loop statement)

X'reverse_range The range of X back-to-front

X'length X'high - X'low + 1 (integer)

Signal Attributes

Attribute Description - X is a signal object

X'event True when X has an event on it (boolean, often used to detect a clock edge)

X'active True when X is assignment to (boolean)

X'last_event When X last had an event (time)

X'last_active When X was last assigned to (time)

X'last_value Previous value of X (same type as X)


New Signal Creating Attributes

Attribute Description - X is a signal object

X'delayed(t) A copy of signal X, delayed by time t (same type as X)

X'stable(t) True when X is unchanged for time t (Boolean)

X'quiet(t) True if X is unassigned for time t (Boolean)

X'transaction Toggles when X is assigned to (Bit)


Examples

--user defined attribute to define a components PCB package


type ic_package is (dil, plcc, pga);
attribute ptype : ic_package;
attribute ptype of u1 : component is plcc;
attribute ptype of u2 : component is dil;

--loop statement using range of a bit_vector for iteration

variable temp : bit_vector(15 downto 0);

...........
for i in temp'range loop...... --loop 16 times

--suspend a process until clock rises


wait until clock'event and clock'last_value = '0';

Structured Types
The VHDL language provides two structured types - Arrays and Records.

Built-in types bit_vector and string are arrays of bit and characters respectively. These array types are unconstrained, which
means that the actual number of elements is not defined in the type declaration. The number of elements is defined when an
object is created.

type bit_vector is array (integer range <>) of bit;

type std_logic_vector is array ( natural range <>) of std_logic;

Object declarations:

signal my_bus : bit_vector(7 downto 0);

General syntax

type type_name is type_definition;

examples

type t_int is range 0 to 9; --user defined numeric type


type t_real is range -9.9 to 9.9;

type my_state is (reset, idle, acka); --user defined enumerated type


signal state : my_state := reset;

Arrays type declaration

type type_name is array(range) of element_type;

examples

type ram is array (0 to 31) of bit_vector(3 downto 0);


--creating a ram variable with initial contents
variable ram_var : ram := ("0000","0001","0101",......."1111");

Records

type type_name is record


element_declarations
end record;

examples

--declaring subtypes for hours, minutes and seconds


subtype hours is natural;
subtype minutes is integer range 0 to 60;
subtype seconds is integer range 0 to 60;

--record type to define elapsed time


type elapsed_time is
record
hh : hours;
mm : minutes;
ss : seconds;
end record;

--function to increment elapsed time by one second


function inctime (intime :elapsed_time) return elapsed_time is
variable result : elapsed_time;
begin
result := intime;
--notice use of selected naming to access fields of
--the record structure (object_name.field_name)
result.ss := result.ss + 1;
if result.ss = 60 then
result.ss := 0;
result.mm := result.mm + 1;
if result.mm = 60 then
result.mm := 0;
result.hh := result.hh + 1;
end if;
end if;
return result;
end function inctime;

Assigning to records using aggregates:

--an object which is a record type may be assigned to using an aggregate


--for example in the case of the object 'result' within the above function
result := (0,0,0); --zero time
result := (2,45,6); --2 hours, 45 minutes and 6 seconds

Configurations
The Configuration Declaration is a mechanism for Binding Components to Entity-Architecture pairs within a structural Architecture.
Configurations can also be used to assign values to the generics of a component which override the default values.

Syntax:

configuration config_name of entity_name is

for architecture_name

for instance_label : component_name

use entity library_name.entity_name(arch_name);

for arch_name ..--lower level configuration specifications

end for;

end for;

end for;

end configuration config_name; --configuration [93]

Example:

configuration parts of dec2to4_bench is

for structural

for generator : dec2to4_stim


use entity work.dec2to4_stim(behavioural);
end for;

for circuit : dec2to4


use entity work.dec2to4(structural);
for structural
for all : inv
use entity work.inv(behaviour)
generic map(tplh => 10 ns,
tphl => 7 ns,
tplhe => 15 ns,
tphle => 12 ns);
end for;
for all : and3
use entity work.and3(behaviour)
generic map(tplh => 8 ns,
tphl => 5 ns,
tplhe => 20 ns,
tphle => 15 ns);
end for;
end for;
end for;

end for;
end configuration parts;

Packages
A Package is used to group together declarations which may be used by several design units (mainly entities). The declarations
include Types, Constants, Components, Attributes, Functions and Procedures. Items contained within a package can be made
visible to an entity by attaching a use clause to the entity.

Syntax:

package package_name is

declarations

end package package_name; --package [93]

Example:

package demo_pack is

constant some_flag : bit_vector := "11111111";

type state is (reset, idle, acka);

component halfadd

port(a, b : in bit; sum, carry : out bit);

end component;

end package demo_pack;

Library and Use Clauses


The VHDL language makes use of Library and Use clauses to organise design libraries and promote design re-use. The operating system
dependent aspect of libraries is abstracted out of the language by means of a mapping between a logical name and an operating system
path name; the latter points to the directory containing the library. The library itself is a database containing analysed design units.
Typically a library will contain a Package which declares all of the components in the library along with the compiled design units for each
component; the latter are required for simulation purposes.

library library_name_1, library_name_2, ...;

use library_name_1.package_name_1.all;

use library_name_2.package_name_2.all;

Example - Source file listing showing typical format of design units for compilation into a library. This file will be compiled into a library
named mylib.

entity mycomp1 is....--design entity for component

architecture v1 of mycomp1 is...

.....

entity mycomp2 is....--design entity for component

architecture v1 of mycomp2 is...

.....

entity mycomp3 is....--design entity for component

architecture v1 of mycomp3 is...

.....

--package declaration groups declarations together

package mycomponents is

component mycomp1 ....end component--component declarations

component mycomp2 ....end component

component mycomp3 ....end component

signal power : std_logic := '1'; --global signal declarations

signal ground : std_logic := '0';

end package mycomponents;

Example - Clauses attached to a design entity using above library of components

library mylib; --this clause makes the library name 'mylib' visible

use mylib.mycomponents.all; --makes all mycomponents declarations visible


entity ....--this entities' architecture can use mycomp1, mycomp2 etc..

IEEE Standard Logic


Package std_logic_1164 defines industry standard digital types for modelling real hardware. In addition to defining the standard
logic types, the package also includes definitions of the basic logical functions (and, or, xor etc...) for types std_logic and
std_logic_vector, as well as the functions rising_edge(signal) and falling_edge(signal). The latter are used with signals of type
std_logic and return a Boolean result which is true if the signal has changed from '0' to '1' (rising_edge) or '1' to '0' (falling_edge)
during the current simulation cycle.

Type std_logic supports accurate simulation using 9-values:

'U'
Uninitialised

'X'
Forcing Unknown

'0'
Forcing Zero

'1'
Forcing One

'Z'
High Impedance

'W'
Weak Unknown

'L'
Resistive Zero

'H'
Resistive One

'-'
Don't care

To use IEEE Std_logic, precede Entity Declaration with the following context clause:

library ieee;

use ieee.std_logic.all;

Example - An Octal D-Type Register with three-state outputs:

LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
ENTITY ttl374 IS
PORT(clock, oebar : IN std_logic;
data : IN std_logic_vector(7 DOWNTO 0);
qout : OUT std_logic_vector(7 DOWNTO 0));
END ENTITY ttl374;

ARCHITECTURE using_1164 OF ttl374 IS


--internal flip-flop outputs
SIGNAL qint : std_logic_vector(7 DOWNTO 0);
BEGIN
qint <= data WHEN rising_edge(clock);
qout <= qint WHEN oebar = '0' ELSE "ZZZZZZZZ"; --high impedance
END ARCHITECTURE using_1164;

IEEE Standard Logic Support Packages


The following packages are widely used for simulation and synthesis; each package contains overloaded functions for arithmetic
operations on digital numbers represented as bit_vectors or std_logic_vectors.

● Std_logic_arith

Defines a set of arithmetic, conversion and comparison functions for types SIGNED*, UNSIGNED*, INTEGER, STD_LOGIC and
STD_LOGIC_VECTOR. *Types SIGNED and UNSIGNED are arrays of Std_logic

● Std_logic_signed

Defines a set of signed two’s complement arithmetic, conversion and comparison functions for type STD_LOGIC_VECTOR.

● Std_logic_unsigned

Defines a set of unsigned arithmetic, conversion and comparison functions for type STD_LOGIC_VECTOR.

● Numeric_bit

Defines a set of signed and unsigned arithmetic, conversion and comparison functions for types SIGNED and UNSIGNED. The
base element of types SIGNED and UNSIGNED is type BIT.

● Numeric_std

Defines a set of signed and unsigned arithmetic, conversion and comparison functions for types SIGNED and UNSIGNED. The
base element of types SIGNED and UNSIGNED is type STD_LOGIC.

Example - context clause:

LIBRARY ieee;

USE ieee.Std_logic_1164.ALL;

USE ieee.Numeric_bit | Numeric_std | Std_logic_arith.ALL;

ENTITY .......

Example:

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity edgecntr is
port(clk, pulsein, clear : in std_logic;
count : buffer std_logic_vector(3 downto 0));
end entity edgecntr;

architecture v1 of edgecntr is
begin
cntr : process
begin
wait until rising_edge(clk);
if clear = '1' then
count <= (others => '0');
elsif pulsein = '1' then
count <= count + 1; --std_logic_unsigned function '+'
else
null;
end if;
end process;
end architecture v1;

Process Templates for Synthesis


A VHDL Process can be used to describe both combinational and sequential logic. The templates given below illustrate standard
formats for describing the basic types of logic for the purposes of synthesis (they can also be used for simulation).

Combinational Logic

Latches plus Logic

Flip-flops plus logic

Asynchronous Reset

Synchronous Reset

Combinational Logic

--combinational logic
process(all_inputs)
begin
...To avoid unwanted latches....
...assign all outputs a default value at the top of the process
...OR specify output values for ALL possible input combinations.
...Describe combinational logic using if..then..else,
...case..when, loop statements etc.
end process;

Latches plus Logic

--latches plus logic


process(all_inputs)
begin
if enable = ‘1’ then
...latches plus logic
...when en -> '0' outputs retain value
end if;
end process;

Flip-flops plus logic

--flip-flops plus logic


process
begin
wait until rising_edge(clock);
...flip-flops plus logic
...any assigned output is a flip-flop output
end process;

Asynchronous Reset

--asynchronous reset
process(clock, reset)
begin
if reset = ‘1’ then
..asynchronous reset action
elsif rising_edge(clock) then
...flip-flops plus logic
end if;
end process;

Synchronous Reset

--synchronous reset
process
begin
wait until rising_edge(clock);
if reset = ‘1’ then
...synchronous reset action
else
...flip-flops plus logic
end if;
end process;

Block Statement
A Block defines a region of visibility within an Architecture. (An Architecture is itself a Block).

Block statements are used to partition a design into sub-units without having to use components. The declarative part of a Block
may declare local signals, types and functions etc. which are only visible within the enclosing block. Blocks may have Ports and
Generics (with Port and Generic mapping to signals within the parent Architecture).

Signals declared outside a Block are visible within the Block, but the reverse is not true. Blocks may be nested.

Syntax:

label : block is --is [93]

local_declarations

begin

concurrent_statements

end block label;

Example:

architecture ..
signal address, offset : natural range 0 to 1023; --10-bit +ve numbers
signal suboff, clear, clock : bit;
begin
cntr10 : block
signal count : natural := 0; --declare a local signal
begin --dataflow model of address counter
count <= 0 when clear = '1' else
((count + 1) mod 1024) when (clock'event and clock = '1')
else count; --10-bit counter with async clear
address <= count when suboff = '0' else
(count - offset) when ((count - offset) >= 0)
else (1024 - abs(count - offset)); --arithmetic logic
end block cntr10;

Process Statement
A process statement is a concurrent statement which contains a sequential algorithm. The statements within a process are
sequential statements and they execute in the order that they appear. A process can contain wait statements, which suspend the
execution of the process, or it may contain a sensitivity list, but not both.

A process never terminates. It is either active or suspended.

Syntax:

optional_label : process (optional sensitivity list) is --is [93]

process declarations

begin

sequential statements

end process optional_label;

Concurrent Assertion Statement


The concurrent assertion statement is often used in the statement part of an entity declaration. This is an optional part of the entity
declaration which follows the entity header containing the generic and port clauses. The statement is typically used to check for
timing violations occurring on inputs to the entity such as a set-up time, hold time or minimum pulse width requirement. The check
is defined by the boolean condition which will often refer to the signal ports of the entity.

Being a concurrent statement, it may also be placed in an architecture statement part.

assert Boolean_condition [ report String_literal ] [ severity Level ] ;

Concurrent Signal Assignment Statement


The Concurrent Signal Assignment Statement assigns a value to a target signal whenever any of the signals on the right hand side
(input) of the statement have an event on them. Every concurrent signal assignment statement creates a driver for the target
signal. The expression may be a logical expression involving other signals, in which case the statement represents simple
combinational logic. Alternatively the expressions may be values which are scheduled to occur on the target signal during the
simulation. The default delay mode for all signal assignments in VHDL is inertial, which means that input signal pulses which are
shorter than the specified delay clause will be ignored by the statement, ie. the input signal fails to overcome the inertia of the logic
circuit. If pure time delays are being modelled, such as in the case of a delay line, the keyword 'transport' precedes the value
expression.

signal_name <= [transport] expression [after delay]


{,expression2 [after delay2]};

The following examples illustrate the use of the concurrent signal assignment statement:

--a half adder with delays


sum <= a xor b after 5 ns;
carry <= a and b after 10 ns;

--creating a waveform -__--_ , at 50 ns the signals stays at '0'


pulse <= '1', '0' after 10 ns,
'1' after 30 ns,
'0' after 50 ns;
--generating a clock, this statement triggers itself
clock <= not clock after period/2;

Conditional Signal Assignment


The Conditional Signal Assignment statement is a concurrent statement which is somewhat similar to the if..then..else statement in
the sequential part of the VHDL language. The statement executes whenever an event occurs on any signal appearing on the right
hand side (input) of the statement, ie. in any of the expressions or boolean_conditions. Unlike the Selected Signal Assignment
statement, each condition is tested in sequence (condition1 then condition2 etc.), the first to return a true result determining the
value assigned to the target signal; the remaining conditions are ignored. In the 1987 version of VHDL the else part is mandatory.
However, the 1993 standard allows the else part to be missed out. This implies that the conditional signal assignment statement
could be used to describe simple flip-flops and latches. A new keyword, unaffected, was added in the 1993 release for use in
concurrent signal assignments to indicate that under certain conditions the target signal is unaffected, thereby implying memory.

signal_name <= expression1 when boolean_condition1 else


expression2 when boolean_condition2 else expression3;

The following examples illustrate the use of the conditional signal assignment statement:

--a d-type flip-flop


q <= d when (clock'event and clock = '1');

--a 4 input multiplexer


q <= i0 when (a = '0' and b = '0') else
i1 when (a = '1' and b = '0') else
i2 when (a = '0' and b = '1') else
i3 ;

--an 8-input priority encoder, in7 is highest priority input


level <= "111" when in7 = '1' else
"110" when in6 = '1' else
"101" when in5 = '1' else
"100" when in4 = '1' else
"011" when in3 = '1' else
"010" when in2 = '1' else
"001" when in1 = '1' else
"000";

Selected Signal Assignment


The Selected Signal Assignment statement is a concurrent statement which is somewhat similar to a Case statement in the
sequential part of the VHDL language. All signals appearing on the right hand (input) side of the statement can cause the
statement to execute. This includes any signals in the select_expression as well as in any of the expressions and choices listed in
each 'when' limb of the statement.

There is no priority associated with any particular 'when' alternative which means that choices must not overlap and all possible
choices for the select_expression must be included. Otherwise a final 'when others' limb must be included to cover those choices
not elaborated.

with select_expression select


signal_name <= expression1 when choice1,
expression2 when choice2,
expression3 when choice3|choice4, --multiple alternative choices
expression4 when choice5 to choice7, --discrete range of choices
expression5 when others; --covers all possible choices not
mentioned

The selected signal assignment statement is useful for describing multiplexers and arbitrary combinational logic based on a truth
table description, as the following example illustrates:

entity fulladd is
port(a,b,cin : in bit; s,cout : out bit);
end fulladd;

architecture zero_delay_behave of fulladd is


begin
with a&b&cin select --select expression is an aggregate
s <= '1' when "010"|"100"|"001"|"111",'0' when others;
with a&b&cin select
cout <= '1' when "011"|"101"|"110"|"111",'0' when others;
end zero_delay_behave;

Component Instantiation Statement


A Component Instantiation Statement creates an occurence of a component. The label is compulsory, in order to differentiate
between instantiations of the same component. The port_association_list defines which local signals connect to which ports of the
component. The association list can be positional or named (see examples). Ports may be left unconnected by associating the key
word open to a component port. A component having generics has a generic_association_list which maps values to the generics
for a given instance; these will override values defined by the component's entity declaration.

instance_label : component_name
generic map(generic_association_list)
port map(port_association_list);

The following example shows a 2-to-4 decoder described in terms of gates:

entity dec2to4 is
port(s0,s1,en : in bit; y0,y1,y2,y3 : out bit);
end dec2to4;
architecture structural of dec2to4 is

--components must be declared before being used

component inv
port(a : in bit; b : out bit);
end component;

component and3
port(a1,a2,a3 : in bit; o1 : out bit);
end component;

signal ns0,ns1 : bit;

begin

i1 : inv port map(s0,ns0); --positional association


i2 : inv port map(s1,ns1);

--positional association, actuals are connected by position


a1 : and3 port map(en,ns0,ns1,y0);
a2 : and3 port map(en,s0,ns1,y1);
a3 : and3 port map(en,ns0,s1,y2);

--named association formal => actual


a4 : and3 port map(a1 => en, a2 => s0, a3 => s1, o1 => y3);

end structural;

Direct Instantiation (VHDL 1993)

The 1993 release of VHDL allows design entities to be instantiated in other design entities directly, ie. without the need to declare a
component or set up a binding. The syntax for direct instantiation is shown below:

label : entity lib_name.entity_name(architecture_name) port map (.....);

G1 : entity WORK.BLOCK1(RTL) port map (A, B, F);

Generate Statement
The Generate Statement is used to replicate concurrent statements over a specified range. If the concurrent statements are
component instantiations, then this creates an array of components. This is very useful for creating regular structures like shift
registers, memory circuits and ripple carry adders.

The label is compulsory with a generate statement.

The statement may be nested to create two dimesional arrays of components.

The if.. generate statement is often used within a generate structure to account for irregularities (see example).

label : for parameter in range generate


concurrent_statements
label : if condition generate
concurrent_statements
end generate label;

end generate label;

Example 1 - n-bit binary adder:

--n-bit binary adder using generated signal assignments


entity addn is
generic(n : positive := 3); --no. of bits less one
port(addend, augend : in bit_vector(0 to n);
carry_in : in bit; carry_out, overflow : out bit;
sum : out bit_vector(0 to n));
end addn;

architecture generated of addn is


signal carries : bit_vector(0 to n);
begin
addgen : for i in addend'range --range is 0 to n
generate
lsadder : if i = 0 generate --lsb is a special case
sum(i) <= addend(i) xor augend(i) xor carry_in;
carries(i) <= (addend(i) and augend(i)) or
(addend(i) and carry_in) or
carry_in and augend(i));
end generate;
otheradder : if i /= 0 generate --all other stages cascade
sum(i) <= addend(i) xor augend(i) xor carries(i-1);
carries(i) <= (addend(i) and augend(i)) or
(addend(i) and carries(i-1)) or
(carries(i-1) and augend(i));
end generate;
end generate;
carry_out <= carries(n);
overflow <= carries(n-1) xor carries(n);
end architecture generated;

Example 2 - n-bit Synchronous Binary Counter:

--a T-type flip flop

library ieee;
use ieee.std_logic_1164.all;
entity tff is
port(clk, t, clear : in std_logic; q : buffer std_logic);
end tff;

architecture v1 of tff is
begin
process(clear, clk) --signals process is sensitive to
begin
if clear = '1' then --asynchronous reset
q <= '0';
elsif rising_edge(clk) then
if t = '1' then
q <= not q;
else
null; --no change , q retains current value
end if;
end if;
end process;
end v1;

--An scalable synchronous binary counter


library ieee;
use ieee.std_logic_1164.all;
entity bigcntr is
generic(size : positive := 32);
port(clk, clear : in std_logic;
q : buffer std_logic_vector((size-1) downto 0));
end bigcntr;

architecture v1 of bigcntr is

component tff is
port(clk, t, clear : in std_logic; q : buffer std_logic);
end component;

signal tin : std_logic_vector((size-1) downto 0);

begin

--generate counter T-type flip-flops

genttf : for i in (size-1) downto 0 generate


ttype : tff port map (clk, tin(i), clear, q(i));
end generate;

--generate counter carry AND gates


genand : for i in 0 to (size-1) generate
t0 : if i = 0 generate --T(0) tied to logic-1
tin(i) <= '1';
end generate;
t1_size : if i > 0 generate --cascaded 2-input AND gates
tin(i) <= q(i-1) and tin(i-1);
end generate;
end generate;

end v1;

Concurrent Procedure Call


A Procedure is one of the two types of sub-program provided by VHDL, the other is a Function. Both Procedures and Functions
embody a group of sequential statements into a well defined routine which carries out some specific task. Functions are used to
compute and return a value, given a set of input parameters. A common function provided within the Std_logic_1164 package is
rising_edge(..). This function is passed a signal parameter and returns a Boolean value indicating whether or not the signal has
undergone a rising edge transition. The function rising_edge(..) makes use of the attributes of the signal being passed to it, such
as event and last_value, to deduce the return value.

Procedures can have multiple parameters of mode IN, OUT and INOUT. Signals which are either IN or INOUT parameters of a
procedure are effectively in the sensitivity list for that procedure. This results in the procedure being called (invoked) whenever an
event occurs on any of the actual signal parameters being passed into the procedure. In this way concurrent procedures can be
used instead of component instantiation statements.

Syntax:

procedure_name [ ( actual_parameter1, actual_parameter2,... ) ];

Example - creating a D-Type Flip-Flop using a procedure:

dff1 : dff_proc(clk, clear, q); --dff_proc is a procedure

Type Declaration
A type declaration creates a new type which can be assigned to any of the VHDL objects (signal, variable and constant). The most
common use of the type declaration is to declare an enumeration type to represent the state of a Finite State Machine. Commonly
used types are often declared inside of a package and the package can then be used by any design unit by including a use clause.

type identifier is type_definition ;

Subtype Declaration
Subtypes are often used to create a type which can make use of the functions supported by the base type, while having a
constrained set of values, or being subject to a resolution function.

The most common example of this is the sub-type std_logic which is a resolved subtype of std_ulogic. This means that objects of
type std_logic can make use of all of the functions provided for objects of type std_ulogic in addition to being resolved, ie. such
objects can have multiple drivers.

subtype subtype_name is

[ resolution_function ] base_type_name [range constraint];


Signal Declaration
Creates a signal object. Signals declared within Architectures are local signals. They are only accessible within the confines of the
Architecture. A signal may be given an initial value on declaration; if this is omitted then the signal takes on the value
corresponding to the left hand element of the signal type declaration. For example, signals of type bit are initialised to '0' by
default, whereas signals of type std_logic are initialised to 'U' (Unitialised).

signal signal_name : type;

signal signal_name : type := initial_value;

Constant Declaration
constant constant_name : type := value;

Component Declaration
An architecture which instantiates other design entities in the form of components must declare them explicitly in the architecture
declarative part.

component component_name is --is [93]

generic(generic_list);

port(port_list);

end component;

Function Declaration
Sub-programs (Functions and Procedures) can be declared in the declarative part of an architecture (or an entity). However it is
more usual for them to be declared in a package body. This allows any design unit to make use of them by means of a context
clause.

function function_name(parameter_list) return type is

declarations

begin

sequential_statements

end function function_name;


Procedure Declaration
Sub-programs (Functions and Procedures) can be declared in the declarative part of an architecture (or an entity). However it is
more usual for them to be declared in a package body. This allows any design unit to make use of them by means of a context
clause.

procedure procedure_name (parameter_list) is

declarations

begin

sequential statements

end procedure procedure_name;

Configuration Specification
The Configuration Specification is used in the declarations part of the architecture body. It is used to bind together a component
instance with a library unit, ie. an entity-architecture pair. It is not as powerful as the primary design unit known as a configuration
declaration.

for instance_name : component_name

use entity library_name.entity_name(architecture_name);

Examples:

FOR rom : rom256x8 USE ENTITY work.rom256x8(version2);

FOR ALL : andg3 USE ENTITY work.andg3(behaviour); --ALL selects every


instance of andg3

Wait
The wait statement provides a mechanism for suspending execution of a process until one or more conditions are met.

There are three types of wait statement:

wait on signal_event;

wait until boolean_condition;

wait for time_expression;

A single wait statement can combine all three conditions or have none at all.

The following statement suspends a process indefinitely:

wait;
Execution of a wait statement causes the simulator to complete the current simulation cycle and increment time by one delta. This
has the effect of updating all signals which have previously been assigned with their corresponding driver values. This would
otherwise occur after execution of the process statement itself.

If a process contains wait statements, it cannot have a sensitivity list:

process(a,b,c)
begin
.......
wait on a,b,c ; X incorrect !
.......
end process;

The wait on signal_event statement provides an alternative to the sensitivity list which can appear after the word process. The
following processes p1 and p2 are exactly the same:

p1 : process (a,b,c)
begin
......
......
......
end process p1;

p2 : process
begin
......
......
wait on a,b,c;
end process p2;

Both processes are sensitive to events on signals a, b and c. In the lower example, the wait statement is placed at the end of the
process, since all processes are executed once at the start of a simulation.

The wait until boolean_condition statement will suspend a process until the specified boolean condition becomes true. It is usual
for the boolean condition to involve signals. These signals will be in an effective sensitivity list created by the statement. Whenever
an event occurs on one or more of the signals, the boolean condition is tested, and if true, the process is resumed. For example,
the two statements below suspend a process until a rising edge occurs on the signal named clock.

wait until (clock'event and clock = '1'); --signal clock is of type bit

wait until rising_edge(clock); --signal clock is of type std_logic

examples:

entity cntr3 is
port(clock : in bit; count : out natural);
end cntr3;

architecture using_wait of cntr3 is


begin
process
begin
wait until (clock'event and clock = '1');
count <= 0;
wait until (clock'event and clock = '1');
count <= 1;
wait until (clock'event and clock = '1');
count <= 2;
end process;
end using_wait;

wait for 10 ns;


--updates signals and suspends a
--process for 10 ns.

wait for 0 ns;


--updates signals and advances
--delta time.

Sequential signal assignment


Signal assignment statements appearing inside a process statement are sequential signal assignments. Within a process, only one
driver is allowed per signal. Therefore multiple assignments to the same signal behave in a totally different way to multiple
concurrent assignments. Each signal assignment within a process to a given signal contributes to the overall driver for that signal.

Syntax:

signal_name <= expression;

signal_name <= expression1 after delay1,

expr2 after del2,

expr3 after del3,....;

Example:

process begin

rx_data <= transport 11 after 10 ns; --(1)

rx_data <= transport 20 after 22 ns; --(2)

rx_data <= transport 35 after 18 ns; --(3)

end process;

In the above example, the driver for signal rx_data is updated following the execution of each statement as follows:

after (1) : rx_data <------ curr @ now ,11 @ 10 ns

after (2) : rx_data <------ curr @ now ,11 @ 10 ns , 20 @ 22 ns

after (3) : rx_data <------ curr @ now ,11 @ 10 ns , 35 @ 18 ns (previous transaction is overwritten)

In the above case, after statement (3) is executed the transaction '20 @ 22 ns' is deleted from the driver and replaced with
transaction '35 @ 18 ns', since the delay for the latter is shorter. The rules governing how signal drivers are affected by multiple
sequential signal assignments are complex and therefore beyond the scope of this guide. A basic rule which must be observed
when modelling digital hardware is:

● Within the confines of a process there is only one driver allowed per signal

Multiple signal assignments to the same signal within a process are often used to avoid unwanted latches being created when a
combinational process is synthesised. All output signals can be assigned default output values at the top of the process. These
values can be conditionally overwritten by statements in the body of the process which ensures that all outputs have a defined
output value for all possible input combinations. See the example below:

process(I)
begin
GS <= '1'; --set default outputs
A <= "000";
if I(7) = '1' then
A <= "111"; --override default A
elsif I(6) = '1' then
A <= "110";
elsif I(5) = '1' then
A <= "101";
elsif I(4) = '1' then
A <= "100";
elsif I(3) = '1' then
A <= "011";
elsif I(2) = '1' then
A <= "010";
elsif I(1) = '1' then
A <= "001";
elsif I(0) = '1' then
null; --assign defaul outputs
else
GS <= '0'; --override default GS
end if;
end process;

The following process creates a repetitive clock

clock_process : process

begin

clock <= '0', '1' after 50 ns;

wait for 100 ns;

end process;

Variable assignment
A variable assignment takes effect immediately; there is no time aspect. The values of signals may be assigned to variables
provided the types match. Variables are used inside processes to hold local values.

variable_name := expression;
examples:

x := y;

q := d;

If then else
Note that both the elsif and else parts of the statement are optional and the elsif part is repeatable. The boolean_expression is
usually a relational expression which returns either true or false. If none of the conditions are true, the statements following the
else keyword are executed, if an else part is included. Each condition is tested sequentially, the first to return a true result causes
the statement immediately following to execute. The next statement to execute is that which follows the 'end if' keyword.

if boolean_expression then
{sequential_statements}

{elsif boolean_expression then


{sequential_statements} }

[ else
{sequential_statements} ]
end if;

Examples:

if (x < 10) then


a := b;
end if;

if (day = sunday) then


weekend := true;
elsif (day = saturday) then
weekend := true;
else
weekday := true;
end if;

Case
When expression evaluates to a value which matches one of the choices in a when statement part, the statements immediately
following will be executed up to the next when part. If none of the specified choices match, the case statement should include an
others clause in the final when part. After the selected statements have executed control is transferred to the statement following
the end case key words.

case expression is
when choice { | choice } =>
{sequential_statements}
{ when choice { | choice } =>
{sequential_statements} }
end case;
Examples:

case instruction is
when load_accum =>
accum <= data;
when store_accum =>
data_out <= accum;
when load|store =>
process_io(addr);
when others =>
process_error(instruction);
end case;

...........
variable word3 : bit_vector(0 to 2);
variable number : natural;

case word4 is
when "000" =>
number := 0;
when "100" =>
number := 1;
when "010" =>
number := 2;
when "110" =>
number := 3;
when "001" =>
number := 4;
when "101" =>
number := 5;
when "011" =>
number := 6;
when "111" =>
number := 7;
end case;

Loop
Used for repetitive execution of a statement or statements. The sequential statements enclosed within a 'while' loop will execute
repeatedly as long as the boolean_expression returns a true value. A 'for' loop will execute as many times as specified in the
discrete_range which can take the form:

simple_expression to|downto simple_expression

In the above, the two simple_expressions are usually integers specifying the number of times the loop is to execute.

A simple loop statement without an iteration scheme or boolean_condition is an infinite loop.

The index variable i is declared locally by the for loop statement. There is no need to declare variable i explicitly in the process,
function or procedure. In a for loop statement the index variable i must not be assigned a value within the body of the loop, ie. i
must not appear on the left hand side of a variable assignment statement. However, i can be used on the right hand side of a
statement.
The syntax of the three variations on the loop statement are shown below:

--looping through a set range

[loop_label : ] for identifier in discrete_range loop


{ sequential_statements }
end loop [loop_label];

--entry test loop

[loop_label : ] while boolean_expression loop


{ sequential_statements }
end loop [loop_label] ;

--unconditional loop

[loop_label : ] loop
{ sequential_statements }
end loop [loop_label] ;

Examples:

while (day = weekday) loop


day := get_next_day(day);
end loop;
-------------------------------------
for i in 1 to 10 loop
i_squared(i) := i * i;
end loop;
----------------------------------------------------
variable binword8 : bit_vector(0 to 7) := "10101111";
variable outputnum : integer := 0;

for i in 0 to 7 loop
if binword8(i) = '1' then
outputnum := outputnum + 2**i ;
end if;
end loop;
----------------------------------------------------
type day_of_week is (sun,mon,tue,wed,thu,fri,sat);

for i in day_of_week loop


if i = sat then son <= mow_lawn;
elsif i = sun then church <= family;
else dad <= go_to_work;
end if;
end loop;

Next
The next statement is used to prematurely terminate the current iteration of a while, for or infinite loop.
next;
next loop_label;
next loop_label when condition;

Example:

for i in 0 to 7 loop
if skip = '1' then
next;
else
n_bus <= table(i);
wait for 5 ns;
end if;
end loop;

Exit
The exit statement is used to prematurely terminate a while, for or infinite loop.

exit ;
exit loop_label;
exit loop_label when condition;

Example:

for i in 0 to 7 loop
if finish_loop_early = '1' then
exit;
else
n_bus <= table(i);
wait for 5 ns;
end if;
end loop;

Null
The null statement performs no action. It is usually used with the case or if..then statement, to indicate that under certain
conditions no action is required.

null;

Potrebbero piacerti anche