Skip to content

Core VHDL syntax

Lexical essentials

VHDL is case-insensitive. Clock, CLOCK, and clock denote the same basic identifier. Comments begin with two hyphens.

signal count : unsigned(7 downto 0); -- Eight bits

Readable formatting is still important even though capitalization is not semantic.

Declarations

Every object has a type.

constant CLOCK_HZ : positive := 100_000_000;
signal   enabled  : std_logic := '0';
signal   count    : unsigned(7 downto 0) := (others => '0');
variable result   : natural;
Object Meaning
Constant Fixed after elaboration
Signal A time-varying connection with one or more drivers
Variable Updates immediately inside its sequential scope
File External stream, normally a testbench feature
Alias Another name or slice view for an existing item

Signals versus variables

Signal assignment uses <=. The new value is scheduled and becomes visible after the process suspends, or after its specified delay.

Variable assignment uses :=. The new value is immediately visible to the following sequential statement.

process(clk_i)
  variable incremented : unsigned(7 downto 0);
begin
  if rising_edge(clk_i) then
    incremented := count_q + 1; -- immediate local calculation
    count_q     <= incremented; -- schedules register output
  end if;
end process;

Classic signal-assignment surprise

Two assignments to the same signal in one process do not make a two-step calculation. The last assignment for the same simulation time wins. Use a variable for intermediate arithmetic or express the final value directly.

Literals and aggregates

bit_s     <= '1';
vector_s  <= "1010";
hex_s     <= x"AF";
count_s   <= 16#2A#;
all_zero  <= (others => '0');
pair_s    <= (7 => '1', 0 => '1', others => '0');

Time literals require a space: 10 ns, 2 us, 1 ms.

Operators

From common high precedence to low:

Family Examples
Miscellaneous abs, not, exponentiation
Multiplying *, /, mod, rem
Sign unary +, unary -
Adding +, -, concatenation &
Shift/rotate sll, srl, rol
Relational =, /=, <, >=
Logical and, or, xor, xnor

Use parentheses when the intended hardware is not instantly obvious.

y_o <= (a_i and b_i) or (enable_i and c_i);
bus_o <= high_nibble_i & low_nibble_i;

Conditional concurrent assignment

This describes a priority selection.

y_o <= a_i when sel_i = "00" else
       b_i when sel_i = "01" else
       c_i when sel_i = "10" else
       d_i;

Selected concurrent assignment

This cleanly expresses a mux without intended priority.

with sel_i select
  y_o <= a_i when "00",
         b_i when "01",
         c_i when "10",
         d_i when others;

If statements

if is sequential, so it belongs inside a process, function, or procedure.

if value_i = 0 then
  zero_o <= '1';
elsif value_i > threshold_i then
  high_o <= '1';
else
  zero_o <= '0';
  high_o <= '0';
end if;

In combinational logic, assign every output on every path. In clocked logic, a missing assignment normally means the register holds its value.

Case statements

Every possible selector value must be covered.

case opcode_i is
  when "00"   => result_o <= a_i + b_i;
  when "01"   => result_o <= a_i - b_i;
  when "10"   => result_o <= a_i and b_i;
  when others => result_o <= (others => '0');
end case;

others also handles meta-values such as X when the selector uses std_logic.

Loops

Synthesis unrolls statically bounded loops into hardware.

parity_v := '0';
for index in data_i'range loop
  parity_v := parity_v xor data_i(index);
end loop;

The loop variable is declared implicitly and exists only inside the loop.

Use a while loop mainly in testbenches unless the synthesizer can prove a static bound.

Attributes

Attributes make code independent of exact array bounds:

Attribute Meaning
'left, 'right Leftmost and rightmost index
'high, 'low Highest and lowest index
'range, 'reverse_range Index range
'length Number of elements
'event Signal changed in current cycle
'image Value converted to text
'value Text converted to a value
'pos, 'val Enumeration position conversion
result_o <= resize(a_i, result_o'length) + resize(b_i, result_o'length);
for index in data_i'range loop
  -- Works for ascending or descending declared ranges.
end loop;

Generate statements

Generate replicates or conditionally includes hardware at elaboration time.

gen_bits : for bit_index in data_i'range generate
  result_o(bit_index) <= data_i(bit_index) and enable_i;
end generate;

gen_parity : if G_INCLUDE_PARITY generate
  parity_o <= xor data_i;
end generate;

Generics used in generate conditions are static: they configure the synthesized circuit, not runtime behavior.

Assertions

Assertions are executable requirements.

assert G_WIDTH > 0
  report "G_WIDTH must be positive"
  severity failure;

Severity levels are note, warning, error, and failure. Simulator stop behavior can be configured, so test scripts should also check the tool exit status and logs.

VHDL-2008 features used here

  • process(all) automatically forms a complete sensitivity list.
  • std.env.finish ends a successful test cleanly.
  • Output ports can be read inside their architecture.
  • Better generic packages, external names, and matching operators exist for advanced use.

Compile examples with xvhdl -2008, and set each Vivado source file type to VHDL 2008 when using the GUI.