The Process Block

The process block: sensitivity list, sequential statements, and combinational/sequential patterns.

What is a Process?

The process is the only place in VHDL where instructions execute sequentially (like a traditional programming language).

-- General structure
process(sensitivity_list)
  -- local declarations (variables)
begin
  -- sequential statements
end process;

A process is a concurrent block relative to the rest of the architecture: multiple processes execute in parallel with each other, but the instructions inside a process are sequential.


The Sensitivity List

The process "wakes up" only when a signal in its sensitivity list changes.

Combinational Process

-- All signals read in the process must be in the list
process(i_a, i_b, i_sel)
begin
  if i_sel = '0' then
    w_mux <= i_a;
  else
    w_mux <= i_b;
  end if;
end process;

If a signal is forgotten in the sensitivity list of a combinational process, the simulation will be incorrect (implicit memory). Synthesis may generate a warning.

Sequential Process

-- Only the clock (and asynchronous reset if used)
process(i_clk)
begin
  if rising_edge(i_clk) then
    r_q <= i_d;
  end if;
end process;

VHDL-2008: process(all)

-- Automatically captures all read signals
process(all)
begin
  w_result <= i_a + i_b;
end process;

Combinational Pattern

architecture rtl of alu is
  signal w_result : std_logic_vector(7 downto 0);
begin
 
  p_alu : process(i_op, i_a, i_b)
  begin
    case i_op is
      when "00" => w_result <= i_a AND i_b;
      when "01" => w_result <= i_a OR  i_b;
      when "10" => w_result <= i_a XOR i_b;
      when others => w_result <= (others => '0');
    end case;
  end process p_alu;
 
  o_result <= w_result;
 
end architecture rtl;

Note the process label (p_alu :) — good practice for readability.


Sequential Pattern (register + synchronous reset)

p_reg : process(i_clk)
begin
  if rising_edge(i_clk) then
    if i_rst = '1' then
      r_data    <= (others => '0');
      r_valid   <= '0';
      r_counter <= (others => '0');
    else
      r_data    <= i_data;
      r_valid   <= i_valid;
      r_counter <= r_counter + 1;
    end if;
  end if;
end process p_reg;

Sequential Statements Inside a Process

if / elsif / else

process(i_clk)
begin
  if rising_edge(i_clk) then
    if i_rst = '1' then
      r_state <= IDLE;
    elsif i_enable = '1' then
      r_state <= ACTIVE;
    else
      r_state <= r_state;  -- unnecessary but explicit
    end if;
  end if;
end process;

case

process(i_clk)
begin
  if rising_edge(i_clk) then
    case r_state is
      when IDLE =>
        if i_start = '1' then
          r_state <= RUNNING;
        end if;
      when RUNNING =>
        if r_done = '1' then
          r_state <= IDLE;
        end if;
      when others =>
        r_state <= IDLE;
    end case;
  end if;
end process;

when others is mandatory in a case on non-enumerated types. It is good practice even for enumerated types (covers unexpected cases).

for / loop

process(i_clk)
  variable v_sum : unsigned(15 downto 0);
begin
  if rising_edge(i_clk) then
    v_sum := (others => '0');
    for i in 0 to 7 loop
      if w_data(i) = '1' then
        v_sum := v_sum + 1;
      end if;
    end loop;
    r_popcount <= v_sum(3 downto 0);
  end if;
end process;

Common Mistakes

Inferred Latch (combinational logic error)

-- BAD: not all branches cover all outputs
process(i_sel, i_a, i_b)
begin
  if i_sel = '0' then
    w_out <= i_a;
  end if;
  -- i_sel = '1' → w_out not assigned → inferred latch!
end process;
 
-- GOOD: default value + full coverage
process(i_sel, i_a, i_b)
begin
  w_out <= i_a;  -- default value
  if i_sel = '1' then
    w_out <= i_b;
  end if;
end process;

Latches should be avoided on FPGAs (poor timing, inefficient resources). Always cover all cases in a combinational process.