In short: A VHDL design is an entity (the pin-out) plus an architecture (the behaviour), built from concurrent statements and clocked process blocks that update signals one delta cycle after they are read.
VHDL looks verbose next to Verilog, and that verbosity is deliberate: it is strongly typed, explicit about interfaces, and catches many mistakes at compile time rather than on the board. This cheat sheet walks through the constructs you will use every day and the ones interviewers love to probe. If you are still deciding on a language, our overview of what an FPGA is gives the hardware context first.
Entity and architecture: interface versus implementation
Every VHDL module is split in two. The entity declares ports and generics; the architecture describes what is inside. One entity can have several architectures (for example a fast behavioural model for simulation and a structural one for synthesis), selected by a configuration or at instantiation.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity counter is
generic ( WIDTH : positive := 8 );
port ( clk, rst : in std_logic;
en : in std_logic;
q : out unsigned(WIDTH-1 downto 0) );
end entity;
Port modes are in, out, inout and buffer. Before VHDL-2008 you could not read an out port inside the architecture, which is why older code keeps an internal signal and assigns it to the port. Generics (like WIDTH) are elaboration-time constants, the VHDL equivalent of Verilog parameters.
Signals, variables and constants
A signal models a wire or register and has a driver; a variable exists only inside a process and updates immediately; a constant never changes. The difference trips up almost every beginner.
| Object | Declared in | Update timing | Typical use |
|---|---|---|---|
| signal | architecture, package | After process suspends (delta cycle) | Registers, wires between processes |
| variable | process, subprogram | Immediately (:=) | Temporary intermediate math |
| constant | anywhere | Never | Widths, reset values, lookup tables |
Key fact: signal assignment (<=) schedules a new value for the next delta cycle; variable assignment (:=) takes effect at once. Two reads of the same signal in one process activation always see the old value.
For a deeper look at std_logic, unsigned and friends, see our article on HDL data types, which compares the Verilog approach with VHDL's stricter typing.
Processes: where sequential code lives
A process wakes when a signal in its sensitivity list changes and then runs its statements top to bottom. The canonical clocked process uses rising_edge:
architecture rtl of counter is
signal cnt : unsigned(WIDTH-1 downto 0) := (others => '0');
begin
process (clk)
begin
if rising_edge(clk) then
if rst = '1' then
cnt <= (others => '0');
elsif en = '1' then
cnt <= cnt + 1;
end if;
end if;
end process;
q <= cnt;
end architecture;
Synthesis tools such as Vivado, Quartus, Synopsys Synplify and Cadence Genus infer a bank of flip-flops plus an incrementer from this. Only clk belongs in the sensitivity list here; adding rst would imply an asynchronous reset only if you also test it outside the clock branch. For purely combinational logic, list every input or, in VHDL-2008, write process (all).
cnt only becomes visible after the edge that computed it.Concurrent versus sequential statements
Outside a process, statements run concurrently, like hardware that all exists at once. Inside a process they run in order. The concurrent forms are plain assignment, when ... else and with ... select; their sequential cousins are if and case.
-- concurrent 4:1 mux
y <= a when sel = "00" else
b when sel = "01" else
c when sel = "10" else d;
-- sequential equivalent
process (a, b, c, d, sel)
begin
case sel is
when "00" => y <= a;
when "01" => y <= b;
when "10" => y <= c;
when others => y <= d;
end case;
end process;
Read concurrent vs sequential statements in VHDL for the full rule set. The interview classic: a missing else or an unassigned branch in a combinational process creates an unintended latch, because the signal must hold its value.
Key fact: always assign a default value to every output at the top of a combinational process. That single habit eliminates almost all accidental latches.
Common constructs worth memorising
- Vectors:
std_logic_vector(7 downto 0)is just bits; useunsignedorsignedfromnumeric_stdfor arithmetic. Avoid the non-standardstd_logic_arithpackage. - Aggregates:
(others => '0')fills a vector of any width, so resets survive width changes. - Generate:
for ... generatereplicates hardware, such as 16 identical adder bits;if ... generateincludes optional blocks. - Enumerated types:
type state_t is (IDLE, LOAD, RUN);gives readable FSMs that tools can re-encode as one-hot. - Component instantiation: direct entity instantiation (
u1 : entity work.counter port map (...)) is shorter than declaring components, and it mirrors the structural style described in structural modelling in Verilog. - Attributes:
'length,'range,'eventkeep code width-independent.
Watch it run: a shift register
The animation below shows a 4-bit shift register. A bit enters on every rising edge and moves one stage to the right, exactly what sr <= sr(2 downto 0) & din; describes inside a clocked process.
Pitfalls interviewers ask about
- Latch inference: incomplete
if/casein a combinational process. Fix with defaults. - Multiple drivers: assigning one signal from two processes gives
'X'in simulation and a synthesis error on most tools. - Mixing edge styles: using both
rising_edgeandfalling_edgeon one signal is rarely synthesisable on FPGA fabric. - Initial values:
:= '0'on a signal works as a power-up value on Xilinx and Intel FPGAs, but is ignored in ASIC flows, so do not rely on it there. - Timing closure: long combinational chains between registers fail setup; tell the tool your clock via XDC/SDC timing constraints.
The same ideas carry onto the device itself; the LUT and CLB architecture explains why a 6-input function costs one LUT while a 7-input one costs two. For hands-on design reviews, the team at Avecas design services applies these coding rules in production RTL.
Ready to write synthesisable VHDL with confidence? Explore the hands-on FPGA and RTL lessons in our CourseTron courses.
FAQ
What is the difference between a signal and a variable in VHDL?
A signal updates after the process suspends (next delta cycle) and can connect processes; a variable updates immediately and is local to one process.
Why does my VHDL design infer a latch?
A combinational process did not assign an output in every branch, so the hardware must remember the old value. Add a default assignment or complete the else/others branch.
Should I use std_logic_vector or unsigned?
Use std_logic_vector for ports and buses where bits are just data, and convert to unsigned or signed from numeric_std when you do arithmetic.
Is VHDL or Verilog better for FPGA jobs?
Both are accepted by Vivado and Quartus. VHDL is common in defence, aerospace and Europe; Verilog and SystemVerilog dominate ASIC verification. Learning both is the safest route, and our Xilinx vs Intel FPGA comparison shows how the tool flows differ.
Training partners such as ChipXpert also run instructor-led VLSI batches if you prefer a classroom format.