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.
Code Testbench Synthesis Files
2x1 mux Testbench
8-bit adder Testbench Top-level Entity
Generic adder
ALU Top-level entity

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.
Code Testbench Synthesis Files
Register Testbench none
Register with load and clear(Updated 9/8/10 to show common mistakes) Testbench Top-level entity for architeture not following guidelines.

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.
  1. Basic example for an adder tree
  2. 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. 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.
  1. 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

  1. 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).
  1. Delay entity supporting cycle delays > 0. This illustrates basic usage of arrays.
  2. 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.
  1. Illustrates using arrays for I/O, which requires the use of packages.
  2. Extends the previous entity to handle a generic number of inputs, which requires unconstrained arrays.
  3. 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.