OpenCores
URL https://opencores.org/ocsvn/cpu8080/cpu8080/trunk

Subversion Repositories cpu8080

[/] [cpu8080/] [trunk/] [project/] [ps2_kbd.vhd] - Rev 33

Compare with Previous | Blame | View Log

--
-- This circuit accepts a serial datastream and clock from a PS/2 keyboard
-- and outputs the scancode for any key that is pressed.
--
-- Notes:
--   
--   1. The clock from the PS/2 keyboard does not drive the clock inputs of
--      any of the registers in this circuit.  Instead, it is sampled at the
--      frequency of the main clock input and edges are extracted from the samples.
--      So you have to apply a main clock that is substantially faster than
--      the 10 KHz PS/2 clock.  It should be 200 KHz or more.
--
--   2. The scancode is only valid when the ready signal is high.  The scancode
--      should be registered by an external circuit on the first clock edge
--      after the ready signal goes high.
--
--   3. The ready signal pulses only after the key is released.
--
--   4. The error flag is set whenever the PS/2 clock stops pulsing and the
--      PS/2 clock is either at a low level or less than 11 bits of serial
--      data have been received (start + 8 data + parity + stop).  The circuit
--      locks up once an error is detected and will not resume operation until
--      a reset is applied.
--
 
 
 
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
 
 
package ps2_kbd_pckg is
  component ps2_kbd
    generic(
      FREQ     :     natural := 50_000  -- frequency of the main clock (KHz)
      );
    port(
      clk      : in  std_logic;         -- main clock
      rst      : in  std_logic;         -- asynchronous reset
      ps2_clk  : in  std_logic;         -- clock from keyboard
      ps2_data : in  std_logic;         -- data from keyboard
      scancode : out std_logic_vector(7 downto 0);  -- key scancode
      parity   : out std_logic;         -- parity bit for scancode
      busy     : out std_logic;         -- busy receiving scancode
      rdy      : out std_logic;         -- scancode ready pulse
      error    : out std_logic          -- error receiving scancode
      );
  end component ps2_kbd;
end package ps2_kbd_pckg;
 
 
 
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
 
 
entity ps2_kbd is
  generic(
    FREQ     :     natural := 50_000   -- frequency of the main clock (KHz)
    );
  port(
    clk      : in  std_logic;           -- main clock
    rst      : in  std_logic;           -- asynchronous reset
    ps2_clk  : in  std_logic;           -- clock from keyboard
    ps2_data : in  std_logic;           -- data from keyboard
    scancode : out std_logic_vector(7 downto 0);  -- key scancode
    parity   : out std_logic;           -- parity bit for scancode
    busy     : out std_logic;           -- busy receiving scancode
    rdy      : out std_logic;           -- scancode ready pulse
    error    : out std_logic            -- error receiving scancode
    );
end entity ps2_kbd;
 
 
architecture arch of ps2_kbd is
 
  constant YES         : std_logic                    := '1';
  constant NO          : std_logic                    := '0';
  constant PS2_FREQ    : natural                      := 10;  -- keyboard clock frequency (KHz)
  constant TIMEOUT     : natural                      := FREQ / PS2_FREQ;  -- ps2_clk quiet timeout
  constant KEY_RELEASE : std_logic_vector(7 downto 0) := "11110000";  -- scancode sent when key is released
 
  signal timer_x, timer_r     : natural range 0 to TIMEOUT;  -- counts time since last PS/2 clock edge
  signal bitcnt_x, bitcnt_r   : natural range 0 to 11;  -- counts number of received scancode bits
  signal ps2_clk_x, ps2_clk_r : std_logic_vector(5 downto 1);  -- PS/2 clock synchronization / edge detect shift register
  signal ps2_clk_fall_edge    : std_logic;  -- pulses on falling edge of PS/2 clock
  signal ps2_clk_rise_edge    : std_logic;  -- pulses on rising edge of PS/2 clock
  signal ps2_clk_edge         : std_logic;  -- pulses on either edge of PS/2 clock
  signal ps2_clk_quiet        : std_logic;  -- pulses when no edges on PS/2 clock for TIMEOUT
  signal sc_x, sc_r           : std_logic_vector(9 downto 0);  -- scancode shift register
  signal keyrel_x, keyrel_r   : std_logic;  -- this flag is set when the key release scancode is received
  signal scancode_rdy         : std_logic;  -- indicates when any scancode has been received
  signal rdy_x, rdy_r         : std_logic;  -- this flag is set when scancode for the pressed key is ready
  signal error_x, error_r     : std_logic;  -- this flag is set when an error occurs
 
begin
 
  -- shift the level on the PS/2 clock into a shift register
  ps2_clk_x <= ps2_clk_r(4 downto 1) & ps2_clk;
 
  -- look at the PS/2 clock levels stored in the shift register and find rising or falling edges
  ps2_clk_fall_edge <= YES when ps2_clk_r(5 downto 2) = "1100" else NO;
  ps2_clk_rise_edge <= YES when ps2_clk_r(5 downto 2) = "0011" else NO;
  ps2_clk_edge      <= ps2_clk_fall_edge or ps2_clk_rise_edge;
 
  -- shift the keyboard scancode into the shift register on the falling edge of the PS/2 clock
  sc_x <= ps2_data & sc_r(9 downto 1) when ps2_clk_fall_edge = YES else sc_r;
 
  -- clear the timer right after a PS/2 clock edge and then keep incrementing it until the next edge
  timer_x <= 0 when ps2_clk_edge = YES else timer_r + 1;
 
  -- indicate when the PS/2 clock has stopped pulsing and is at a high level.
  ps2_clk_quiet <= YES when timer_r = TIMEOUT and ps2_clk_r(2) = '1' else NO;
 
  -- increment the bit counter on each falling edge of the PS/2 clock.
  -- reset the bit counter if the PS/2 clock stops pulsing or if there was an error receiving the scancode.
  -- otherwise, keep the bit counter unchanged.
  bitcnt_x <= bitcnt_r + 1 when ps2_clk_fall_edge = YES              else
              0            when ps2_clk_quiet = YES or error_r = YES else
              bitcnt_r;
 
  -- a scancode has been received if the bit counter is 11 and the PS/2 clock has stopped pulsing
  scancode_rdy <= YES when bitcnt_r = 11 and ps2_clk_quiet = YES else NO;
 
  -- look for the scancode sent when the key is released
  keyrel_x <= YES when sc_r(scancode'range) = KEY_RELEASE and scancode_rdy = YES else
              NO  when rdy_r = YES or error_r = YES                              else
              keyrel_r;
 
  -- the scancode for the pressed key arrives after receiving the key-release scancode 
  -- Changed: removed key release requirement, send all keys up.
  -- rdy_x <= YES when keyrel_r = YES and scancode_rdy = YES else NO;
  rdy_x <= YES when scancode_rdy = YES else NO;
 
  -- indicate an error if the clock is low for too long or if it stops pulsing in the middle of a scancode
  error_x <= YES when (timer_r = TIMEOUT and ps2_clk_r(2) = '0') or
             (ps2_clk_quiet = YES and bitcnt_r/=11 and bitcnt_r/=0) else
             error_r;
 
  scancode <= sc_r(scancode'range);     -- output scancode
  parity   <= sc_r(scancode'high+1);    -- output parity bit for the scancode
  busy     <= YES when bitcnt_r/=0 else NO;  -- output busy signal when receiving a scancode
  rdy      <= rdy_r;                    -- output scancode ready flag
  error    <= error_r;                  -- output error flag
 
  -- update the various registers
  process(rst, clk)
  begin
    if rst = YES then
      ps2_clk_r <= (others => '1');     -- start by assuming PS/2 clock has been high for a while
      sc_r      <= (others => '0');     -- clear scancode register
      keyrel_r  <= NO;                  -- key-release scancode has not been received yet
      rdy_r     <= NO;                  -- no scancodes received yet
      timer_r   <= 0;                   -- clear PS/2 clock pulse timer
      bitcnt_r  <= 0;                   -- clear scancode bit counter
      error_r   <= NO;                  -- clear any errors
    elsif rising_edge(clk) then
      ps2_clk_r <= ps2_clk_x;
      sc_r      <= sc_x;
      keyrel_r  <= keyrel_x;
      rdy_r     <= rdy_x;
      timer_r   <= timer_x;
      bitcnt_r  <= bitcnt_x;
      error_r   <= error_x;
    end if;
  end process;
 
end architecture arch;
 

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.