add PS2 Keyboard

This commit is contained in:
Stefan 2016-01-05 12:03:51 +01:00
parent cc4743296e
commit 5258fcf4d9
3 changed files with 422 additions and 0 deletions

45
src/PS2/Debouncer.vhd Normal file
View File

@ -0,0 +1,45 @@
-- (C) Rui T. Sousa from http://sweet.ua.pt/~a16360
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity Debouncer is
generic (Delay : positive);
port (
Clock : in STD_LOGIC;
Reset : in STD_LOGIC;
Input : in STD_LOGIC;
Output : out STD_LOGIC
);
end Debouncer;
architecture Behavioral of Debouncer is
signal DelayCounter : natural range 0 to Delay;
signal Internal : STD_LOGIC;
begin
process(Clock, Reset)
begin
if rising_edge(Clock) then
if Reset = '1' then
Output <= '0';
Internal <= '0';
DelayCounter <= 0;
else
if Input /= Internal then
Internal <= Input;
DelayCounter <= 0;
elsif DelayCounter = Delay then
Output <= Internal;
else
DelayCounter <= DelayCounter + 1;
end if;
end if;
end if;
end process;
end Behavioral;

166
src/PS2/KeyboardMapper.vhd Normal file
View File

@ -0,0 +1,166 @@
-- (C) Rui T. Sousa from http://sweet.ua.pt/~a16360
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
entity KeyboardMapper is
port (
Clock : in std_logic;
Reset : in std_logic;
PS2Busy : in std_logic;
PS2Error : in std_logic;
DataReady : in std_logic;
DataByte : in std_logic_vector(7 downto 0);
Send : out std_logic;
Command : out std_logic_vector(7 downto 0);
CodeReady : out std_logic;
ScanCode : out std_logic_vector(9 downto 0)
);
end KeyboardMapper;
-- ScanCode(9) = 1 -> Extended
-- = 0 -> Regular (Not Extended)
-- ScanCode(8) = 1 -> Break
-- = 0 -> Make
-- ScanCode(7 downto 0) -> Key Code
architecture Behavioral of KeyboardMapper is
type StateType is (ResetKbd, ResetAck, WaitForBAT, Start, Extended, ExtendedBreak, Break, LEDs, CheckAck);
signal State : StateType := Start;
signal CapsLock : STD_LOGIC;
signal NumLock : STD_LOGIC;
signal ScrollLock : STD_LOGIC;
-- signal PauseON : STD_LOGIC;
begin
process(Reset, PS2Error, Clock)
begin
if Reset = '1' or PS2Error = '1' then
CapsLock <= '0';
NumLock <= '0';
ScrollLock <= '0';
-- PauseON <= '0';
Send <= '0';
Command <= (others => '0');
CodeReady <= '0';
ScanCode <= (others => '0');
State <= Start;
elsif rising_edge(Clock) then
case State is
when ResetKbd =>
if PS2Busy = '0' then
Send <= '1';
Command <= x"FF";
State <= ResetAck;
end if;
when ResetAck =>
Send <= '0';
if Dataready = '1' then
if DataByte = x"FA" then
State <= WaitForBAT;
else
State <= ResetKbd;
end if;
end if;
when WaitForBAT =>
if DataReady = '1' then
if DataByte = x"AA" then -- BAT(self test) completed successfully
State <= Start;
else
State <= ResetKbd;
end if;
end if;
when Start =>
CodeReady <= '0';
if DataReady = '1' then
case DataByte is
when x"E0" =>
State <= Extended;
when x"F0" =>
State <= Break;
when x"FA" => --Acknowledge
null;
when x"AA" =>
State <= Start;
when x"FC" =>
State <= ResetKbd;
when x"58" =>
Send <= '1';
Command <= x"ED";
CapsLock <= not CapsLock;
ScanCode <= "00" & DataByte;
CodeReady <= '1';
State <= LEDs;
when x"77" =>
Send <= '1';
Command <= x"ED";
NumLock <= not NumLock;
ScanCode <= "00" & DataByte;
CodeReady <= '1';
State <= LEDs;
when x"7E" =>
Send <= '1';
Command <= x"ED";
ScrollLock <= not ScrollLock;
ScanCode <= "00" & DataByte;
CodeReady <= '1';
State <= LEDs;
when others =>
ScanCode <= "00" & DataByte;
CodeReady <= '1';
State <= Start;
end case;
end if;
when Extended =>
if DataReady = '1' then
if DataByte = x"F0" then
State <= ExtendedBreak;
else
ScanCode <= "10" & DataByte;
CodeReady <= '1';
State <= Start;
end if;
end if;
when ExtendedBreak =>
if DataReady = '1' then
ScanCode <= "11" & DataByte;
CodeReady <= '1';
State <= Start;
end if;
when Break =>
if DataReady = '1' then
ScanCode <= "01" & DataByte;
CodeReady <= '1';
State <= Start;
end if;
when LEDs =>
Send <= '0';
CodeReady <= '0';
if Dataready = '1' then
if DataByte = x"FA" then
Send <= '1';
Command <= "00000" & CapsLock & NumLock & ScrollLock;
State <= CheckAck;
elsif DataByte = x"FE" then
Send <= '1';
end if;
end if;
when CheckAck =>
Send <= '0';
if Dataready = '1' then
if DataByte = x"FA" then
State <= Start;
elsif DataByte = x"FE" then
Send <= '1';
end if;
end if;
when others => null;
end case;
end if;
end process;
end Behavioral;

211
src/PS2/PS2Controller.vhd Normal file
View File

@ -0,0 +1,211 @@
-- (C) Rui T. Sousa from http://sweet.ua.pt/~a16360
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
entity PS2Controller is
GENERIC(
clk_freq : INTEGER := 32 );-- system clock frequency in MHz
port (
Reset : in std_logic;
Clock : in std_logic;
PS2Clock : inout std_logic;
PS2Data : inout std_logic;
Send : in std_logic;
Command : in std_logic_vector(7 downto 0);
PS2Busy : out std_logic;
PS2Error : buffer std_logic;
DataReady : out std_logic;
DataByte : out std_logic_vector(7 downto 0)
);
end PS2Controller;
architecture Behavioral of PS2Controller is
constant ClockFreq : natural := clk_freq; -- MHz
constant Time100us : natural := 100 * ClockFreq;
constant Time20us : natural := 20 * ClockFreq;
constant DebounceDelay : natural := 16;
type StateType is (Idle, ReceiveData, InhibitComunication, RequestToSend, SendData, CheckAck, WaitRiseClock);
signal State : StateType;
signal BitsRead : natural range 0 to 10;
signal BitsSent : natural range 0 to 10;
signal Byte : std_logic_vector(7 downto 0);
signal CountOnes : std_logic; -- One bit only to know if even or odd number of ones
signal DReady : std_logic;
signal PS2ClockPrevious : std_logic;
signal PS2ClockOut : std_logic;
signal PS2Clock_Z : std_logic;
signal PS2Clock_D : std_logic;
signal PS2DataOut : std_logic;
signal PS2Data_Z : std_logic;
signal PS2Data_D : std_logic;
signal TimeCounter : natural range 0 to Time100us;
begin
DebounceClock: entity work.Debouncer
generic map (Delay => DebounceDelay)
port map (
Clock => Clock,
Reset => Reset,
Input => PS2Clock,
Output => PS2Clock_D
);
DebounceData: entity work.Debouncer
generic map (Delay => DebounceDelay)
port map (
Clock => Clock,
Reset => Reset,
Input => PS2Data,
Output => PS2Data_D
);
PS2Clock <= PS2ClockOut when PS2Clock_Z <= '0' else 'Z';
PS2Data <= PS2DataOut when PS2Data_Z <= '0' else 'Z';
process(Reset, Clock)
begin
if rising_edge(Clock) then
if Reset = '1' then
PS2Clock_Z <= '1';
PS2ClockOut <= '1';
PS2Data_Z <= '1';
PS2DataOut <= '1';
DataReady <= '0';
DReady <= '0';
DataByte <= (others => '0');
PS2Busy <= '0';
PS2Error <= '0';
BitsRead <= 0;
BitsSent <= 0;
CountOnes <= '0';
TimeCounter <= 0;
PS2ClockPrevious <= '1';
Byte <= x"FF";
State <= InhibitComunication;
else
PS2ClockPrevious <= PS2Clock_D;
case State is
when Idle =>
DataReady <= '0';
DReady <= '0';
BitsRead <= 0;
PS2Error <= '0';
CountOnes <= '0';
if PS2Data_D = '0' then -- Start bit
PS2Busy <= '1';
State <= ReceiveData;
elsif Send = '1' then
Byte <= Command;
PS2Busy <= '1';
TimeCounter <= 0;
State <= InhibitComunication;
else
State <= Idle;
end if;
when ReceiveData =>
if PS2ClockPrevious = '1' and PS2Clock_D = '0' then -- falling edge
case BitsRead is
when 1 to 8 => -- 8 Data bits
Byte(BitsRead - 1) <= PS2Data_D;
if PS2Data_D = '1' then
CountOnes <= not CountOnes;
end if;
when 9 => -- Parity bit
case CountOnes is
when '0' =>
if PS2Data_D = '0' then
PS2Error <= '1'; -- Error when CountOnes is even (0)
else -- and parity bit is unasserted
PS2Error <= '0';
end if;
when others =>
if PS2Data_D = '1' then
PS2Error <= '1'; -- Error when CountOnes is odd (1)
else -- and parity bit is asserted
PS2Error <= '0';
end if;
end case;
when 10 => -- Stop bit
if PS2Error = '0' then
DataByte <= Byte;
DReady <= '1';
else
DReady <= '0';
end if;
State <= WaitRiseClock;
when others => null;
end case;
BitsRead <= BitsRead + 1;
end if;
when InhibitComunication =>
PS2Clock_Z <= '0';
PS2ClockOut <= '0';
if TimeCounter = Time100us then
TimeCounter <= 0;
State <= RequestToSend;
else
TimeCounter <= TimeCounter + 1;
end if;
when RequestToSend =>
PS2Clock_Z <= '1';
PS2Data_Z <= '0';
PS2DataOut <= '0'; -- Sets the start bit, valid when PS2Clock is high
if TimeCounter = Time20us then
TimeCounter <= 0;
PS2ClockOut <= '1';
BitsSent <= 1;
State <= SendData;
else
TimeCounter <= TimeCounter + 1;
end if;
when SendData =>
PS2Clock_Z <= '1';
if PS2ClockPrevious = '1' and PS2Clock_D = '0' then -- falling edge
case BitsSent is
when 1 to 8 => -- 8 Data bits
if Byte(BitsSent - 1) = '0' then
PS2DataOut <= '0';
else
CountOnes <= not CountOnes;
PS2DataOut <= '1';
end if;
when 9 => -- Parity bit
if CountOnes = '0' then
PS2DataOut <= '1';
else
PS2DataOut <= '0';
end if;
when 10 => -- Stop bit
PS2DataOut <= '1';
State <= CheckAck;
when others => null;
end case;
BitsSent <= BitsSent + 1;
end if;
when CheckAck =>
PS2Data_Z <= '1';
if PS2ClockPrevious = '1' and PS2Clock_D = '0' then
if PS2Data_D = '1' then -- no Acknowledge received
PS2Error <= '1';
end if;
State <= WaitRiseClock;
end if;
when WaitRiseClock =>
if PS2ClockPrevious = '0' and PS2Clock_D = '1' then
PS2Busy <= '0';
DataReady <= DReady;
State <= Idle;
end if;
when others => null;
end case;
end if;
end if;
end process;
end Behavioral;