The following tutorial covers important concepts of synthesizable VHDL.
VHDL was originally introduced for describing circuit behavior, not for
automatically synthesizing a circut from that description. Therefore, there are
many constructs and coding practices that cause problems with synthesis,
despite simulating perfectly.
My biggest suggestion for writing VHDL is to design the circuit, then write
the code. Basically, should be able to hierarchically divide a large
circuit into smaller and smaller components, until you have either combinational
logic, sequential logic, a combination of both (e.g., a datapath),
finite state machines, or memories. Then, you can simply follow the following
synthesis guidelines for each of these types of circuits. Note that some of the
guidelines contradict each other if used for the wrong type of logic, which is
why it is always important to know the type of logic that you are designing. In
other words, don't start writing the VHDL until you know what type of circuit
you are describing.
Behavioral Descriptions
Combinational Logic
The following examples introduce VHDL constructs and synthesis coding
guidelines that should be used when describing combinational logic. Make sure
to look at the examples in the order shown, as some of the examples build
on concepts illustrated in the previous examples.
Sequential Logic
The following examples introduce VHDL constructs and synthesis coding
guidelines that should be used when describing circuits that contain
sequential logic. Make sure
to look at the examples in the order shown, as some of the examples build
on concepts illustrated in the previous examples.
More complex examples:
Structural Descriptions
The following examples illustrate how to create structural descriptions that
combine existing components to form a specific structure. Structural
descriptions are very common when creating custom datapaths. For example,
the custom pipelines used by many FPGA applications are often appropriate for
structural description.
- Basic example for an adder tree
- Ripple-carry adder showing how the "for generate" statement can simplify specification of structural patterns
Important points: Use the "for generate" statement anytime that there
is a pattern in a structural description. This construct will allow you to
specify very large structures with very little code. In many cases, structural
descriptions will benefit from arrays, which are discussed later.
Finite State Machines
Although finite state machines could potentially be created by combining
combinational and sequential logic, fortunately there are ways of abstracting
away some of these details. The following example illustrates several different
models for describing state machines in VHDL. Although there are many models
suggested my different resources, I suggest using what I refer to as either the
1-process or 2-process model.
-
1- and 2-process FSM models
Important points: I recommend using the 2-process model whenever possible. The 1-process
model implements all internal signals and outputs as registers, which can
add latency. The 2-process model does not require registers, and therefore
has more flexible timing.
Controllers+Datapaths
Most realistic circuits combine a controller and a datapath to perform
some computation. This combination can be represented using several models in VHDL that
abstract away unnecessary details. The following example illustrates these
models. I use the terminology FSMD model for examples that integrate datapath
operations with the controller (I show a 1- and 2-process FSMD model) and FSM+D model for examples that explicitly specify the structure of the datapath.
-
Controller+Datapath Models Example (main file is bit_diff.vhd). Updated 2/14/11
Important points: I highly recommend using the FSM+D model whenever the
structure of the datapath is important. For example, if you are creating a
custom pipelined datapath for a specific application, specifying the
structure of the pipeline is likely important. When structure isn't important,
I recommend the 2-process FSMD model, although it is slightly tricky to learn.
The 1-process model is appropriate when you don't care about the registers that
are used for every output and internal signal (i.e., when exact timing isn't
important).
Misc Synthesis Guidelines
- Initialization of signals: Students often ask about what situations
are appropriate for initializing declared signals.
The correct answer is you generally shouldn't initialize signals when writing
synthesizable code.
Although this may seem strange, think about what initializing a signal means
from the point of view of the synthesized circuit. In the case of
combinational logic, a signal corresponds to a wire. How do you initialize a
wire? It doesn't really make sense. For sequential logic, a signal generally
corresponds to a register. How do you initialize a register? Typically, you
give it a default value when reset is asserted, which you already have to do
in your code. Therefore, initialization of signals should never be needed.
There are device- and tool-specific situations where you may want to initialize
signals. For example, in an FPGA, a signal corresponding to a register can
potentially be
given a default value that is included in the bitfile and does not require an
explicit reset. However, this is a very specialized situation, so unless you
have a very good reason to do this (in my class you won't), you should never
initialize any signals. In some cases, initializing signals can cause
differences between simulation and synthesis, because the simulator can
initialize a signal, but the synthesized circuit can't.
Examples showing useful VHDL constructs
Delay entity
This entity creates a chain of registers that can be used to delay a signal,
which is common in pipelined circuits. The following implementation illustrate
arrays, "if generate", and unconstrained generics (which are rarely needed,
but this is a good example of when to use them).
- Delay entity supporting cycle delays > 0. This
illustrates basic usage of arrays.
- An extended version of the delay entity that supports
cycles delays >= 0 with generic initialization values the registers. This
implementation shows the "if generate" construct in addition to unconstrained
generics. You will rarely need an unconstrained generic, but this is a valid
example.
Sum entity
The sum entity simply adds a certain number of inputs to produce an output. The
intent of this entity is not to show how to efficiently add numbers (this
implementation is quite inefficient), but this is a simple example that
illustrates an appropriate usage of arrays for I/O, packages,
unconstrained arrays, and limitations of VHDL that prevent use of generics in
some situations.
- Illustrates using arrays for I/O, which requires the use
of packages.
- Extends the previous entity to handle a generic number
of inputs, which requires unconstrained arrays.
- Attempts to extend the entity to support a generic
number of inputs and a generic width, but illustrates a major limitation of
VHDL (pre 2008): you cannot create an array of an unconstrained type. The code
discusses several workarounds, none of which are ideal. This limitation is
addressed in VHDL 2008, but unfortunately most tools do not yet support VHDL
2008, at least for synthesis.
Important point: VHDL (pre-2008) does not support arrays of
unconstrained types.