diff --git a/src/PS2/Debouncer.vhd b/src/PS2/Debouncer.vhd new file mode 100644 index 0000000..6ef0d4d --- /dev/null +++ b/src/PS2/Debouncer.vhd @@ -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; \ No newline at end of file diff --git a/src/PS2/KeyboardMapper.vhd b/src/PS2/KeyboardMapper.vhd new file mode 100644 index 0000000..f4a43f1 --- /dev/null +++ b/src/PS2/KeyboardMapper.vhd @@ -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; \ No newline at end of file diff --git a/src/PS2/PS2Controller.vhd b/src/PS2/PS2Controller.vhd new file mode 100644 index 0000000..e30c126 --- /dev/null +++ b/src/PS2/PS2Controller.vhd @@ -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; \ No newline at end of file