Papilio Plus/PS2 Keyboard

From Hamsterworks Wiki!

Jump to: navigation, search

This Papilio Plus project tests the PS/2 Port A (or Port B, if you update the UCF file).

As this project has to speak a high level protocol it is quite a bit more complex than the other tests. It also only tests receiving, so can't do things like change the keyboard LEDs.

Once loaded the LEDs show the scancode of the last key pressed or released. To see the high four bits push the 'up' button.

It has lots of features.

  • It does full start bit, stopbit and parity check
  • it fully deglitches the signals, by requiring a state change of longer than 1us before it is acted upon
  • Checks that the clock speed is within limits
  • Can be adapted to stuff keyboard scan codes into a FIFO for later processing.

It doesn't interperate the 'break' commands that prefixes key releases, nor does it handle the extended keycodes (prefixed by 0xE0).

If you are really really tight for space you could try removing the 'deglitch' counters, but given the length and construction of keyboard cables you reliability might suffer. You could also just use r.datareg(8 downto 1) rather than r.scancode for presenting the scancode to the outside world, but for this test we need the value in the register to be latched so it can be displayed on the LEDs.

PS/2 Keyboard protocol

The protocol is pretty basic if you are only receiving.

  • The clock and data lines are 'open collector', and in the idle state are pulled high by resisters
  • Keyboard generates all the clock and data signals
  • The clock pulse has to be between 30us and 50us, with 30 to 50us between pulse (or another way, 10kHz to 16Khz with somewhere between a 37.5 to 62.5% duty cycle
  • The data is sampled on the rising edge of the clock
  • It data is framed with a start bit ('0'), 8 data bits, parity bit giving odd parity, stop bit ('1')
  • The scan codes issued by the keyboard have no direct relationship to the ASCII code
  • When a key is pushed just the scancode is sent.
  • When a key is released the "BREAK" code is sent, followed by the key's scancode
  • 'Extended' keys (those that were not on the original XT keyboard) generate two byte scancodes

See http://www.computer-engineering.org/ps2keyboard/scancodes2.html for a full table of scancodes.

Keyboard.ucf

NET CLK32  LOC = "P94" | IOSTANDARD=LVCMOS25 | PERIOD = 31.25ns;

# These are for PS2/2 Port A
NET PS2data    LOC="P114" | IOSTANDARD=LVTTL | PULLUP; # C0
NET PS2clk     LOC="P115" | IOSTANDARD=LVTTL | PULLUP; # C1

# These are for PS/2 Port B 
#NET PS2data    LOC="P88"  | IOSTANDARD=LVTTL | PULLUP; # A12
#NET PS2clk     LOC="P93"  | IOSTANDARD=LVTTL | PULLUP; # A13


NET LED(0)    LOC="P75"  | IOSTANDARD=LVTTL; # A7  LED1
NET LED(1)    LOC="P67"  | IOSTANDARD=LVTTL; # A6  LED2
NET LED(2)    LOC="P66"  | IOSTANDARD=LVTTL; # A5  LED3
NET LED(3)    LOC="P61"  | IOSTANDARD=LVTTL; # A4  LED3

NET Button   LOC="P95"  | IOSTANDARD=LVTTL; # B9   Up

Keyboard.vhd

----------------------------------------------------------------------------------
-- Engineer:       Mike Field <hamster@snap.net.nz>
-- 
-- Create Date:    20:56:04 10/03/2011 
-- Module Name:    keyboard - Behavioral 
-- Description:     Input raw scancodes from a PS2 keyboard and display on LEDs
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity keyboard is
    Port ( clk32        : in  STD_LOGIC;
           ps2clk       : inout  STD_LOGIC;
           ps2data      : inout  STD_LOGIC;

           button         : in   STD_LOGIC;
           LED            : out  STD_LOGIC_VECTOR (3 downto 0)
        );
end keyboard;

architecture Behavioral of keyboard is
   constant state_idle      : std_logic_vector(1 downto 0) := "00";
   constant state_receiving : std_logic_vector(1 downto 0) := "01";
   constant state_waiting   : std_logic_vector(1 downto 0) := "10";
   constant state_received  : std_logic_vector(1 downto 0) := "11";
   
   constant minClockPulseLen : natural := 30*32; -- 30us @ 32MHz
   constant maxClockPulseLen : natural := 50*32; -- 50us @ 32MHz

   type reg is record
      state               : std_logic_vector(1 downto 0);
      clockCounter      : std_logic_vector(10 downto 0);
      dataReg            : std_logic_vector(10 downto 0);
      receivedCount     : std_logic_vector(3 downto 0);
      ps2clkDeglitch    : std_logic_vector(4 downto 0);
      ps2dataDeglitch   : std_logic_vector(4 downto 0);
      scancode          : std_logic_vector(7 downto 0);
      ps2lastClkDeglitched  : std_logic;
      ps2lastDataDeglitched : std_logic;
      ps2ClkDeglitched  : std_logic;
      ps2DataDeglitched : std_logic;
      dataReceived      : std_logic;
   end record;
   signal r : reg := ("00",(others => '0'),(others => '0'),(others => '0'),
                      (others => '0'),(others => '0'),(others => '0'),
                      '0','0','0','0','0');
   signal n : reg;
   signal dataReceived    : STD_LOGIC;
begin
   ps2clk       <= 'Z';
   ps2data      <= 'Z';
   
   dataReceived <= r.dataReceived;
   
   process(r,button)
   begin
      if button = '1' then
         LED <= r.scancode(7 downto 4);
      else
         LED <= r.scancode(3 downto 0);
      end if;
   end process;
   
   process(r, ps2clk, ps2data)
   begin
      n <= r;
      
      -- remember what the last signals were
      n.ps2lastClkDeglitched <= r.ps2clkDeglitched;
      n.ps2lastClkDeglitched <= r.ps2clkDeglitched;


      -- Deglitch the clock signal
      if ps2clk = '1' then
         if r.ps2clkDeglitch < 31 then
            n.ps2clkDeglitch <= r.ps2clkDeglitch+1;
         else
            n.ps2clkDeglitched <= '1';
            n.clockCounter     <= (others => '0');
         end if;
      else
         if r.ps2clkDeglitch > 0 then
            n.ps2clkDeglitch <= r.ps2clkDeglitch-1;
         else
            n.ps2clkDeglitched <= '0';
            n.clockCounter <= (others => '0');
         end if;      
      end if;

      -- Deglitch the data signal
      if ps2data = '1' then
         if r.ps2dataDeglitch < 31 then
            n.ps2dataDeglitch <= r.ps2dataDeglitch+1;
         else
            n.ps2dataDeglitched <= '1';
         end if;
      else
         if r.ps2dataDeglitch > 0 then
            n.ps2dataDeglitch <= r.ps2dataDeglitch-1;
         else
            n.ps2dataDeglitched <= '0';
         end if;
      end if;

      ----------------------------------------------------
      -- Now the actual processing of the tidied up signal
      ----------------------------------------------------
      case r.state is
      when   state_idle   =>
         -- Are we waiting for the ps2clk to go low? (start of data)
         n.clockCounter  <= (others => '0');
         n.receivedCount <= (others => '0');      
         n.dataReceived  <= '0';
         if r.ps2clkDeglitched = '0' then
            n.state <= state_receiving;
         end if;

      when state_receiving =>
         n.clockCounter <= r.clockCounter + 1;
         -- is the pulse too long?
         if r.clockCounter > maxClockPulseLen then
            if r.ps2clkDeglitched = '1' then
               n.state <= state_idle;
            else
               n.state <= state_receiving;
            end if;
         end if;
         
         if r.ps2lastClkDeglitched = '0' and r.ps2clkDeglitched = '1' then
            -- we on the rising edge of the clock singal
            if r.clockCounter < minClockPulseLen then
               n.state <= state_idle;
            else
               n.receivedCount <= r.receivedCount+1;
               n.clockCounter  <= (others => '0');
               n.dataReg       <= r.ps2dataDeglitched & r.dataReg(10 downto 1);
               if r.receivedCount = 10 then
                  n.state            <= state_received;
               else
                  n.receivedCount    <= r.receivedCount+1;
               end if;
            end if;
         elsif r.ps2lastClkDeglitched = '1' and r.ps2clkDeglitched = '0' then
            -- we on the falling edge of the clock singal
            if r.clockCounter < minClockPulseLen then
               n.state <= state_waiting;
            end if;
            n.clockCounter  <= (others => '0');
         end if;

      when state_received =>
         -- Check the start, parity and stopbits are valid, if all are correct, set the "data Received" signal
         if r.dataReg(10) = '1' and (r.dataReg(9) xor r.dataReg(8) xor r.dataReg(7) xor r.dataReg(6) xor r.dataReg(5) xor r.dataReg(4) xor r.dataReg(3) xor r.dataReg(2) xor r.dataReg(1)) = '1'  and r.dataReg (0) = '0' then            
            n.scancode      <= r.dataReg(8 downto 1);
            n.dataReceived <= '1';
            n.state          <= state_idle;
         end if;

      when others =>
         -- We are waiting for the ps2clk to go high
         if r.ps2clkDeglitched = '1' then
            n.state <= state_idle;
         end if;
      end case;
   end process;
   
   process(clk32, n)
   begin
      if rising_edge(clk32) then 
         r <= n;
      end if;
   end process;
end Behavioral;

Personal tools