0
0
VhdlHow-ToIntermediate · 4 min read

VHDL Code for PS2 Keyboard Interface: Syntax and Example

A PS2 keyboard interface in VHDL reads serial data from the keyboard clock and data lines using a state machine to capture bits on falling clock edges. The interface decodes the 11-bit PS2 frame (start, data, parity, stop) and outputs the received scan code for further processing.
📐

Syntax

The PS2 keyboard interface in VHDL typically uses a process triggered on the falling edge of the PS2 clock line. It reads the PS2 data line bit by bit, assembling an 11-bit frame that includes a start bit, 8 data bits, a parity bit, and a stop bit. A state machine or counter tracks the bit position to know when a full byte is received.

  • ps2_clk: Input clock from the keyboard, triggers data sampling.
  • ps2_data: Input data line from the keyboard.
  • bit_count: Counts bits received (0 to 10).
  • shift_reg: Stores bits as they arrive.
  • scan_code: Output byte after full frame received.
vhdl
process(ps2_clk, reset_n)
begin
  if reset_n = '0' then
    bit_count <= 0;
    shift_reg <= (others => '0');
    scan_code <= (others => '0');
  elsif falling_edge(ps2_clk) then
    if bit_count < 11 then
      shift_reg(bit_count) <= ps2_data;
      bit_count <= bit_count + 1;
    else
      bit_count <= 0;
      scan_code <= shift_reg(8 downto 1); -- data bits
    end if;
  end if;
end process;
💻

Example

This example shows a complete VHDL module that interfaces with a PS2 keyboard. It captures the scan code sent by the keyboard and outputs it on scan_code. The process triggers on the falling edge of ps2_clk and uses a counter to track bits. After receiving 11 bits, it extracts the 8 data bits as the scan code.

vhdl
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity ps2_keyboard is
  port(
    clk       : in std_logic;
    reset_n   : in std_logic;
    ps2_clk   : in std_logic;
    ps2_data  : in std_logic;
    scan_code : out std_logic_vector(7 downto 0);
    data_ready: out std_logic
  );
end entity;

architecture rtl of ps2_keyboard is
  signal bit_count : integer range 0 to 11 := 0;
  signal shift_reg : std_logic_vector(10 downto 0) := (others => '0');
  signal ps2_clk_sync, ps2_clk_prev : std_logic := '1';
begin
  process(clk, reset_n)
  begin
    if reset_n = '0' then
      bit_count <= 0;
      shift_reg <= (others => '0');
      scan_code <= (others => '0');
      data_ready <= '0';
      ps2_clk_sync <= '1';
      ps2_clk_prev <= '1';
    elsif rising_edge(clk) then
      -- Synchronize ps2_clk to system clock
      ps2_clk_sync <= ps2_clk;
      ps2_clk_prev <= ps2_clk_sync;

      -- Detect falling edge of ps2_clk
      if ps2_clk_prev = '1' and ps2_clk_sync = '0' then
        if bit_count < 11 then
          shift_reg(bit_count) <= ps2_data;
          bit_count <= bit_count + 1;
          data_ready <= '0';
        else
          bit_count <= 0;
          scan_code <= shift_reg(8 downto 1); -- data bits
          data_ready <= '1';
        end if;
      else
        data_ready <= '0';
      end if;
    end if;
  end process;
end architecture;
Output
Outputs 8-bit scan_code when a full PS2 frame is received; data_ready pulses high for one clk cycle.
⚠️

Common Pitfalls

  • Not synchronizing the PS2 clock input: The PS2 clock is asynchronous to your system clock, so failing to synchronize it can cause metastability and incorrect data capture.
  • Missing falling edge detection: PS2 data bits are valid on the falling edge of the PS2 clock, so sampling on the wrong edge leads to wrong data.
  • Ignoring start, parity, and stop bits: The PS2 frame includes these bits; ignoring them can cause errors or misalignment.
  • Not resetting counters properly: Without proper reset, the bit counter may get stuck or miscount bits.
vhdl
Wrong approach (sampling on rising edge):
process(ps2_clk)
begin
  if rising_edge(ps2_clk) then
    shift_reg(bit_count) <= ps2_data; -- Wrong edge
    bit_count <= bit_count + 1;
  end if;
end process;

Correct approach (sampling on falling edge with synchronization):
-- See example code above for proper synchronization and falling edge detection.
📊

Quick Reference

Remember these key points when writing a PS2 keyboard interface in VHDL:

  • Synchronize ps2_clk to your system clock to avoid metastability.
  • Sample ps2_data on the falling edge of ps2_clk.
  • Use a counter to track 11 bits: 1 start, 8 data, 1 parity, 1 stop.
  • Extract the 8 data bits as the scan code after full frame reception.
  • Reset counters and registers properly on system reset.

Key Takeaways

Always synchronize the PS2 clock input to your system clock before sampling.
Sample PS2 data bits on the falling edge of the PS2 clock signal.
Use a bit counter to track the full 11-bit PS2 frame including start, data, parity, and stop bits.
Extract the 8 data bits as the scan code after receiving the full frame.
Reset your state machine and counters properly to avoid data corruption.