Skip to content

Reusable RTL recipes

These are starting points. Match reset polarity, widths, latency, and interface behavior to the project.

Register with enable

process(clk_i)
begin
  if rising_edge(clk_i) then
    if reset_i = '1' then
      data_q <= (others => '0');
    elsif enable_i = '1' then
      data_q <= data_i;
    end if;
  end if;
end process;

Up counter

process(clk_i)
begin
  if rising_edge(clk_i) then
    if reset_i = '1' then
      count_q <= (others => '0');
    elsif enable_i = '1' then
      count_q <= count_q + 1;
    end if;
  end if;
end process;

Unsigned overflow wraps naturally. If saturation is required, compare with the maximum before incrementing.

Modulo-N counter with tick

process(clk_i)
begin
  if rising_edge(clk_i) then
    tick_o <= '0';
    if reset_i = '1' then
      count_q <= 0;
    elsif count_q = G_MODULUS - 1 then
      count_q <= 0;
      tick_o  <= '1';
    else
      count_q <= count_q + 1;
    end if;
  end if;
end process;

Require G_MODULUS > 0 with an assertion.

Two-flop synchronizer

signal sync_q : std_logic_vector(1 downto 0) := (others => '0');
attribute ASYNC_REG : string;
attribute ASYNC_REG of sync_q : signal is "TRUE";

process(clk_i)
begin
  if rising_edge(clk_i) then
    sync_q <= sync_q(0) & async_i;
  end if;
end process;

sync_o <= sync_q(1);

Check concatenation direction carefully: after the first edge, bit 0 receives the asynchronous input; after the second, bit 1 receives the previous bit 0.

Rising-edge detector

process(clk_i)
begin
  if rising_edge(clk_i) then
    if reset_i = '1' then
      previous_q <= '0';
      pulse_o    <= '0';
    else
      pulse_o    <= level_i and not previous_q;
      previous_q <= level_i;
    end if;
  end if;
end process;

Use only on a signal already synchronous to clk_i.

Debouncer

Algorithm:

  1. Synchronize the input.
  2. If sampled input equals accepted output, clear the stability counter.
  3. Otherwise increment the counter.
  4. When the counter reaches the threshold, accept the new value and clear the counter.

At 100 MHz, 10 ms corresponds to 1,000,000 cycles. Make the duration a generic expressed in cycles or derive it from G_CLOCK_HZ and milliseconds.

Shift register

process(clk_i)
begin
  if rising_edge(clk_i) then
    if enable_i = '1' then
      shift_q <= shift_q(shift_q'high - 1 downto 0) & serial_i;
    end if;
  end if;
end process;

Define which direction and which end is the newest bit.

Priority encoder

process(all)
begin
  valid_o <= '0';
  index_o <= (others => '0');

  for index in request_i'reverse_range loop
    if request_i(index) = '1' then
      valid_o <= '1';
      index_o <= to_unsigned(index, index_o'length);
      exit;
    end if;
  end loop;
end process;

Loop direction defines priority. Test multiple simultaneous requests.

Clock-enable divider calculation

For input clock F_CLK and desired tick frequency F_TICK:

DIVISOR = F_CLK / F_TICK

This simple integer divider is exact only when the frequencies divide evenly. For fractional ratios, use a phase accumulator:

accumulator_next = accumulator + phase_increment
tick = carry_out

Handshake register

Valid/ready transfer occurs on a rising edge when both are high:

transfer_s <= valid_i and ready_o;

Rules:

  • Producer keeps valid and data stable until transfer.
  • Consumer asserts ready when it can accept.
  • Neither side should create a combinational loop through valid/ready.

Seven-segment scan skeleton

Use:

  • A scan tick generator.
  • A two-bit digit index.
  • A mux selecting the current nibble.
  • A hexadecimal decoder.
  • One-hot active-low digit enables.

Blank digits for one system cycle during selection changes if ghosting becomes visible.

UART baud tick

For simple 115,200 baud transmission at 100 MHz:

integer divisor ≈ 868
relative baud error ≈ (100 MHz / 868 − 115200) / 115200

Calculate and document the actual baud. For receiving, generate an oversample tick, commonly 8× or 16×, and sample near the bit center.

Recipe verification rule

Every reusable recipe needs tests for:

  • Reset.
  • Enable/hold.
  • Minimum and maximum values.
  • Boundary transition.
  • Generic extremes.
  • Timing/latency contract.