----------------------------------------------------------------------------------
-- Company: www.mlab.cz
-- Engineer: miho
--
-- Create Date: 19:31:10 02/20/2011
-- Design Name: S3AN01A Test Design
-- Module Name: PS2
-- Project Name: S3AN01A Test Design
-- Target Devices: XILINX FPGA (Spartan3A/3AN)
-- Tool versions: ISE 12.4 / 13.1 / 13.3
-- Description: Test design for PCB verification
--
-- Dependencies: None
--
-- Revision: 0.01 File Created
--
----------------------------------------------------------------------------------
--
-- PS/2 Keyboard Driver
-- ====================
--
-- PS2_Code
-- --------
--
-- Standard PS/2 Scan Code
--
--
-- PS2_Attribs
-- -----------
--
-- Bit 0 - Shift
-- Bit 1 - Ctrl
-- Bit 2 - Alt
-- Bit 3 - Ext0 (arrows, ...)
-- Bit 4 - Ext1
-- Bit 5 - Shift Num (arrows with NumLock)
-- Bit 6
-- Bit 7 - Break (key release)
--
--
-- PS2_Shifts
-- ----------
--
-- Bit 0 - Shift Left
-- Bit 1 - Shift Right
-- Bit 2 - Ctrl Left
-- Bit 3 - Ctrl Right
-- Bit 4 - Alt Left
-- Bit 5 - Alt Right
-- Bit 6 - Num Lock
-- Bit 7 - Caps Lock
-- Bit 8 - Scroll Lock
-- Bit 9 - Shift Num (virtual state) - Not to be used
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity PS2 is
generic (
-- Top Value for 100MHz Clock Counter
CLKFREQ: integer -- Frequency in Hz (minimum cca 50_000)
);
port (
-- Main Clock
Clk: in std_logic;
-- PS/2 Port
PS2_Clk: inout std_logic;
PS2_Data: inout std_logic;
-- Result - valid when PS2_Valid
PS2_Valid: out boolean; -- Valid Data (synchronous with Clk)
PS2_Code: out std_logic_vector(7 downto 0); -- Key Scan Code
PS2_Attribs: out std_logic_vector(7 downto 0); -- State of Shifts for Scan Code
-- Immediate State of Shifts
PS2_Shifts: out std_logic_vector(9 downto 0) -- Immediate (live) State of Shift/Alt/Ctrl etc.
);
end entity PS2;
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
package PS2_pkg is
component PS2
generic (
-- Top Value for 100MHz Clock Counter
CLKFREQ: integer -- Frequency in Hz (minimum cca 50_000)
);
port (
-- Main Clock
Clk: in std_logic;
-- PS/2 Port
PS2_Clk: inout std_logic;
PS2_Data: inout std_logic;
-- Result - valid when PS2_Valid
PS2_Valid: out boolean; -- Valid Data (synchronous with Main Clock)
PS2_Code: out std_logic_vector(7 downto 0); -- Key Scan Code
PS2_Attribs: out std_logic_vector(7 downto 0); -- State of Shifts for Scan Code
-- Immediate State of Shifts
PS2_Shifts: out std_logic_vector(9 downto 0) -- Immediate (live) State of Shift/Alt/Ctrl etc.
);
end component PS2;
end package;
architecture PS2_a of PS2 is
function to_std_logic(State: boolean) return std_logic is
begin
if State then
return '1';
else
return '0';
end if;
end function to_std_logic;
-- Sampled PS/2 Clock and Data
signal PS2_Clk_d: std_logic := '0'; -- For sync with systerm clock
signal PS2_Clk_dd: std_logic := '0'; -- For falling edge detection
signal PS2_Data_d: std_logic := '0'; -- For sync with systerm clock
-- Level 0 - Read Byte from PS/2 Interface
type ReadByte_t is ( -- Read Byte FSM State Type
Idle, -- Inactive State
D0, D1, D2, D3, D4, D5, D6, D7, -- Receiving Bits
Parity, -- Receiving Parity
Final -- Receiving Stop Bit and Sending ReadByte_rdy
);
signal ReadByteState: ReadByte_t := Idle; -- Read Byte FSM State
signal ReadByte: std_logic_vector(7 downto 0) := (others => '0'); -- Read Byte (Raw Scan Code Byte)
signal ReadByte_rdy: boolean := false; -- Read Byte Ready (synchronous with Clk)
-- Level 1 - Process Raw Scan Codes E0,F1 and F0 - valid only when Level1_rdy
signal FlagE0: boolean := false; -- E0 - Ext0 Key
signal FlagE1: boolean := false; -- E1 - Ext1 Key
signal FlagF0: boolean := false; -- F0 - Break (release) Key
signal Level1_rdy: boolean := false; -- Send Data and Flags to the next level
-- Level 2 - Process Raw Scan Codes and Shift-Like Atributes E0, E1 and F0 - valid only when Level2_rdy
signal FlagBreak: boolean := false; -- F0 - Break (depress) Key
signal FlagAltR: boolean := false; -- E0 11 - State of Right Alt Key
signal FlagAltL: boolean := false; -- 11 - State of Left Alt Key
signal FlagShiftNum: boolean := false; -- E0 12 - State of Ext Left Shift (pseudo key)
signal FlagShiftL: boolean := false; -- 12
signal FlagShiftR: boolean := false; -- 59
signal FlagCtrlR: boolean := false; -- E0 14
signal FlagCtrlL: boolean := false; -- 14
signal FlagExt0: boolean := false; -- E0 Keys (extended keys)
signal FlagExt1: boolean := false; -- E1 Keys (extended keys - Prt_Scr and Pause_Brk)
signal FlagNumLock: boolean := false; -- 77 Num Lock State
signal FlagScrollLock: boolean := false; -- 7E Scroll Lock State
signal FlagCapsLock: boolean := false; -- 58 Caps Lock State
signal Level2_rdy: boolean := false; -- Send Data and Flags to the next level
signal Level2a_rdy: boolean := false; -- Send Read Ack for Write Byte
-- Write Byte
type WriteByteState_t is ( -- Write Byte FSM State Type
Idle, -- Idle State
WriteStart, -- Start (pull PS2_Clk down)
WaitStart, -- Wait
SendBits, -- Send Data Bits
WriteParity, -- Send Parity
WriteStop, -- Send Stop Bit
AckBit, -- Wait for Ack Bit from Keyboard
Final, -- Wait for Idle on PS2_Clk and PS2_Data
WaitAckByte -- Wait for Ack Byte from Keyboard
);
signal WriteByteState: WriteByteState_t := Idle; -- Write Byte FSM State
signal WriteCode: std_logic_vector(7 downto 0) := (others =>'0'); -- What to Write
signal WriteByte: boolean := false; -- Init Write Byte Sequence
signal SendingData: boolean := false; -- Block Receiver when Sending Data
signal WriteByte_ack: boolean := false; -- Ack Writen Byte
signal WriteReg: std_logic_vector(7 downto 0) := (others =>'0'); -- Transmit Shift Register
signal ParityBit: std_logic := '0'; -- Parity Bit
signal StartTime: unsigned(31 downto 0) := (others =>'0'); -- Timer for Start of Write (PS2_Clk low)
signal WriteBits: unsigned(3 downto 0) := (others =>'0'); -- Bit Counter
-- Update LED Indicators
type UpdState_t is ( -- Update Led Indicators FSM State Type
Idle, -- Inactive State
SendReset, -- For Debug - Reset Keyboard
SendLed1, -- Send FD
SendLed2, -- Send New LED State
SendFinal --
);
signal UpdState: UpdState_t := Idle; -- Update Led Indicators FSM State
signal UpdateLed: boolean := false; -- Send new LED State to the Keyboard
signal UpdateLed_ack: boolean := false; -- Ack (1 clock pulse)
begin
-- Sync External Signals with Clock
process (Clk)
begin
if rising_edge(Clk) then
-- Sync
PS2_Clk_d <= PS2_Clk;
PS2_Data_d <= PS2_Data;
-- For Falling Edge Detection
PS2_Clk_dd <= PS2_Clk_d;
end if;
end process;
-- Level 0 - Read Byte from PS/2 Interface
process (Clk)
begin
if rising_edge(Clk) then
ReadByte_rdy <= false;
if PS2_Clk_dd='1' and PS2_Clk_d='0' and not SendingData then
-- Falling Edge of PS2_Clk
case ReadByteState is
when Idle =>
-- Test Start Bit
if PS2_Data='0' then
ReadByteState <= D0;
end if;
when D0 =>
-- Bit 0
ReadByte <= PS2_Data & ReadByte(7 downto 1);
ReadByteState <= D1;
when D1 =>
-- Bit 1
ReadByte <= PS2_Data & ReadByte(7 downto 1);
ReadByteState <= D2;
when D2 =>
-- Bit 2
ReadByte <= PS2_Data & ReadByte(7 downto 1);
ReadByteState <= D3;
when D3 =>
-- Bit 3
ReadByte <= PS2_Data & ReadByte(7 downto 1);
ReadByteState <= D4;
when D4 =>
-- Bit 4
ReadByte <= PS2_Data & ReadByte(7 downto 1);
ReadByteState <= D5;
when D5 =>
-- Bit 5
ReadByte <= PS2_Data & ReadByte(7 downto 1);
ReadByteState <= D6;
when D6 =>
-- Bit 6
ReadByte <= PS2_Data & ReadByte(7 downto 1);
ReadByteState <= D7;
when D7 =>
-- Bit 7
ReadByte <= PS2_Data & ReadByte(7 downto 1);
ReadByteState <= Parity;
when Parity =>
-- Check Parity Here...
ReadByteState <= Final;
when Final =>
-- End of Byte
ReadByteState <= Idle;
ReadByte_rdy <= true; -- Scan Code Ready (8 bit word)
end case;
end if;
end if;
end process;
-- Level 1 - Process Raw Scan Codes and ESC Atributes E0, E1 and F0
process (Clk)
begin
if rising_edge(Clk) then
if Level1_rdy then
-- Clean State when Sent Data from Level1 processing
Level1_rdy <= false;
FlagE0 <= false;
FlagE1 <= false;
FlagF0 <= false;
else
if ReadByte_rdy then
-- Process Scan Code Byte from Level 0
if ReadByte=X"E0" then
-- Ext Code
FlagE0 <= true;
elsif ReadByte=X"E1" then
-- Special Ext Code
FlagE1 <= true;
elsif ReadByte=X"F0" then
-- Break Flag
FlagF0 <= true;
else
-- Scan Code
Level1_rdy <= true;
end if;
end if;
end if;
end if;
end process;
-- Level 2 - Process Shift (left and right shift, alt and ctrl) and Num Lock (numeric virtual shift)
process (Clk)
begin
if rising_edge(Clk) then
-- Clear Comands to Higher Level
UpdateLed <= false;
Level2a_rdy <= false;
Level2_rdy <= false;
-- Process Read Byte
if Level1_rdy then
if ReadByte=X"11" then
-- Alt Key
if FlagE0 then
FlagAltR <= not FlagF0;
else
FlagAltL <= not FlagF0;
end if;
elsif ReadByte=X"12" then
-- Left Shift
if FlagE0 then
FlagShiftNum <= not FlagF0;
else
FlagShiftL <= not FlagF0;
end if;
elsif ReadByte=X"59" then
-- Right Shift
FlagShiftR <= not FlagF0;
elsif ReadByte=X"14" then
-- Ctrl
if FlagE0 then
FlagCtrlR <= not FlagF0;
else
FlagCtrlL <= not FlagF0;
end if;
elsif ReadByte=X"77" and not FlagCtrlL and not FlagCtrlR and not FlagAltL and not FlagAltR then
-- Num Lock On/Off
if not FlagF0 then
FlagNumLock <= not FlagNumLock;
UpdateLed <= true; -- Set UpdateLed Request
end if;
elsif ReadByte=X"58" then
-- Caps Lock
if not FlagF0 then
FlagCapsLock <= not FlagCapsLock;
UpdateLed <= true; -- Set UpdateLed Request
end if;
elsif ReadByte=X"7E" then
if not FlagF0 then
FlagScrollLock <= not FlagScrollLock;
UpdateLed <= true; -- Set UpdateLed Request
end if;
else
-- Send Data to the next Level
FlagExt0 <= FlagE0;
FlagExt1 <= FlagE1;
FlagBreak <= FlagF0;
if WriteByteState=WaitAckByte then
-- Send Data (Ack Byte) to WriteByte
Level2a_rdy <= true;
else
-- Send Scan Code to the next Level
Level2_rdy <= true;
end if;
end if;
end if;
end if;
end process;
-- Send Data
PS2_Valid <= Level2_rdy;
-- Scan COde
PS2_Code(7 downto 0) <= ReadByte;
-- Attribs
PS2_Attribs(0) <= '1' when FlagShiftL or FlagShiftR else '0'; -- Bit 0 - Shift
PS2_Attribs(1) <= '1' when FlagCtrlL or FlagCtrlR else '0'; -- Bit 1 - Ctrl
PS2_Attribs(2) <= '1' when FlagAltL or FlagAltR else '0'; -- Bit 2 - Alt
PS2_Attribs(3) <= '1' when FlagExt0 else '0'; -- Bit 3 - Ext Code E0
PS2_Attribs(4) <= '1' when FlagExt1 else '0'; -- Bit 4 - Ext Code E1
PS2_Attribs(5) <= '1' when FlagShiftNum else '0'; -- Bit 5 - Shift Num (Arrows,...) - only if NumLock Led is Off
PS2_Attribs(6) <= '0';
PS2_Attribs(7) <= '1' when FlagBreak else '0'; -- Bit 7 - Break (release) Key
-- Immediate State of Shift Like Keys
PS2_Shifts(0) <= '1' when FlagShiftL else '0'; -- Bit 0 - Shift Left
PS2_Shifts(1) <= '1' when FlagShiftR else '0'; -- Bit 1 - Shift Right
PS2_Shifts(2) <= '1' when FlagCtrlL else '0'; -- Bit 2 - Ctrl Left
PS2_Shifts(3) <= '1' when FlagCtrlR else '0'; -- Bit 3 - Ctrl Right
PS2_Shifts(4) <= '1' when FlagAltL else '0'; -- Bit 4 - Alt Left
PS2_Shifts(5) <= '1' when FlagAltR else '0'; -- Bit 5 - Alt Right
PS2_Shifts(6) <= '1' when FlagNumLock else '0'; -- Bit 7 - Num Lock
PS2_Shifts(7) <= '1' when FlagCapsLock else '0'; -- Bit 8 - Caps Lock
PS2_Shifts(8) <= '1' when FlagScrollLock else '0'; -- Bit 9 - Scroll Lock
PS2_Shifts(9) <= '1' when FlagShiftNum else '0'; -- Bit 6 - Shift Num (virtual state) - Not to be used
-- Write Byte to PS/2 Interface
-- Init By: WriteByte
-- Finish Indication: WriteByte_ack
process (Clk)
begin
if rising_edge(Clk) then
WriteByte_ack <= false;
case WriteByteState is
when Idle =>
PS2_Clk <= 'Z';
PS2_Data <= 'Z';
if WriteByte then
WriteByteState <= WriteStart;
WriteReg <= WriteCode;
end if;
when WriteStart =>
if PS2_Data_d='1' and PS2_Clk_d='1' then
-- PS2 Interface in Idle State
PS2_Clk <= '0'; -- Start of Write (PS2_Clk=L)
StartTime <= to_unsigned(CLKFREQ/16000, StartTime'length); -- cca 60us Start
WriteBits <= to_unsigned(7, WriteBits'length); -- 8 bits
WriteByteState <= WaitStart;
SendingData <= true;
end if;
when WaitStart =>
if StartTime>0 then
StartTime <= StartTime - 1;
else
PS2_Data <= '0'; -- Start Bit
PS2_Clk <= 'Z'; -- Release Clk
ParityBit <= '1'; -- Init Parity Generator (code 1111_1111 has parity 1)
WriteByteState <= SendBits;
end if;
when SendBits =>
if PS2_Clk_dd='1' and PS2_Clk_d='0' then
PS2_Data <= WriteReg(0);
ParityBit <= ParityBit xor WriteReg(0);
WriteReg <= '1' & WriteReg(7 downto 1);
if WriteBits>0 then
WriteBits <= WriteBits - 1;
else
WriteByteState <= WriteParity;
end if;
end if;
when WriteParity =>
if PS2_Clk_dd='1' and PS2_Clk_d='0' then
PS2_Data <= ParityBit;
WriteByteState <= WriteStop;
end if;
when WriteStop =>
if PS2_Clk_dd='1' and PS2_Clk_d='0' then
PS2_Data <= '1';
WriteByteState <= AckBit;
end if;
when AckBit =>
PS2_Data <= 'Z';
if PS2_Clk_dd='1' and PS2_Clk_d='0' then
WriteByteState <= Final;
end if;
when Final =>
if PS2_Clk_d='1' then
WriteByteState <= WaitAckByte;
SendingData <= false;
end if;
when WaitAckByte =>
if Level2a_rdy then
WriteByteState <= Idle;
WriteByte_ack <= true;
end if;
end case;
end if;
end process;
-- Level 3 - Update LED Indicators
-- Init By: UpdateLed or Level2_rdy(with scan code and attrib)
-- Finish Indication: UpdateLed_ack (not used)
process (Clk)
begin
if rising_edge(Clk) then
UpdateLed_ack <= false; -- 1 Clock Pulse
WriteByte <= false;
case UpdState is
when Idle =>
-- Register the request
if Level2_rdy and ReadByte=X"07" and not FlagBreak then
WriteCode <= X"FF";
WriteByte <= true;
elsif UpdateLed then
UpdateLed_ack <= true;
UpdState <= SendLed1;
end if;
when SendReset =>
if WriteByteState=Idle then
-- Send Keyborad Reset
WriteCode <= X"FF";
WriteByte <= true;
UpdState <= SendFinal;
end if;
when SendLed1 =>
if WriteByteState=Idle then
-- Send LED Command
WriteCode <= X"ED";
WriteByte <= true;
UpdState <= SendLed2;
end if;
when SendLed2 =>
if WriteByte_ack then
-- Send LED State
WriteCode <= "00000" & to_std_logic(FlagCapsLock)
& to_std_logic(FlagNumLock)
& to_std_logic(FlagScrollLock);
WriteByte <= true;
UpdState <= SendFinal;
end if;
when SendFinal =>
if WriteByte_ack then ---WriteByteState=Idle then
-- Last Data has been Send
UpdState <= Idle;
end if;
end case;
end if;
end process;
end architecture PS2_a;