------------------------------------------------------------------------------- -- Title : PS/2 interface -- Project : ------------------------------------------------------------------------------- -- File : ps2.vhd -- Author : Daniel Quintero -- Company : Itoo Software -- Created : 2003-04-14 -- Last update: 2003-10-30 -- Platform : VHDL'87 ------------------------------------------------------------------------------- -- Description: PS/2 generic UART for mice/keyboard ------------------------------------------------------------------------------- -- This code is distributed under the terms and conditions of the -- GNU General Public License ------------------------------------------------------------------------------- -- Revisions : -- Date Version Author Description -- 2003-04-14 1.0 daniel Created ------------------------------------------------------------------------------- -- URL: https://opencores.org/project,ps2core library IEEE; use IEEE.std_logic_1164.all; use IEEE.std_logic_unsigned.all; use ieee.std_logic_arith.all; entity ps2 is port ( clk_i : in std_logic; -- Global clk rst_i : in std_logic; -- GLobal Asinchronous reset data_o : out std_logic_vector(7 downto 0); -- Data in data_i : in std_logic_vector(7 downto 0); -- Data out ibf_clr_i : in std_logic; -- Ifb flag clear input obf_set_i : in std_logic; -- Obf flag set input ibf_o : out std_logic; -- Received data available obf_o : out std_logic; -- Data ready to sent frame_err_o : out std_logic; -- Error receiving data parity_err_o : out std_logic; -- Error in received data parity busy_o : out std_logic; -- uart busy err_clr_i : in std_logic; -- Clear error flags wdt_o : out std_logic; -- Watchdog timer out every 400uS ps2_clk_io : inout std_logic; -- PS2 Clock line ps2_data_io : inout std_logic); -- PS2 Data line end ps2; architecture rtl of ps2 is type states is (idle, write_request, start, data, parity, stop); type debounce_states is (stable, rise, fall, wait_stable); --constant DEBOUNCE_TIMEOUT : integer := 200; -- clks to debounce the ps2_clk signal constant DEBOUNCE_BITS : integer := 8; --constant WATCHDOG_TIMEOUT : integer := 19200 / DEBOUNCE_TIMEOUT; -- clks to wait 400uS constant WATCHDOG_BITS : integer := 8; signal state : states; signal debounce_state : debounce_states; signal debounce_cnt : std_logic_vector(DEBOUNCE_BITS-1 downto 0); signal debounce_cao : std_logic; signal ps2_clk_syn : std_logic; -- PS2 clock input syncronized signal ps2_clk_clean : std_logic; -- PS2 clock debounced and clean signal ps2_clk_fall : std_logic; -- PS2 clock fall edge signal ps2_clk_rise : std_logic; -- PS2 clock rise edge signal ps2_data_syn : std_logic; -- PS2 data input syncronized signal ps2_clk_out : std_logic; -- PS2 clock output signal ps2_data_out : std_logic; -- PS2 clock output signal writing : std_logic; -- read / write cycle flag signal shift_cnt : std_logic_vector(2 downto 0); signal shift_cao : std_logic; -- Shift counter carry out signal shift_reg : std_logic_vector(8 downto 0); signal shift_in : std_logic; -- Shift register to right signal shift_load : std_logic; -- Shift register parallel load signal shift_calc_parity : std_logic; -- Shift register set parity signal wdt_cnt : std_logic_vector(WATCHDOG_BITS-1 downto 0); signal wdt_rst : std_logic; -- watchdog reset signal wdt_cao : std_logic; -- watchdog carry out signal shift_parity : std_logic; -- Current parity of shift_reg signal ibf : std_logic; -- IBF, In Buffer Full signal obf : std_logic; -- OBF, Out Buffer Full signal parity_err : std_logic; -- Parity error signal frame_err : std_logic; -- Frame error begin -- rtl -- Sincronize input signals syn_ps2 : process (clk_i, rst_i) begin if rst_i = '0' then -- asynchronous reset (active low) ps2_clk_syn <= '0'; ps2_data_syn <= '0'; elsif clk_i'event and clk_i = '1' then -- rising clock edge ps2_clk_syn <= TO_X01(ps2_clk_io); ps2_data_syn <= TO_X01(ps2_data_io); end if; end process syn_ps2; -- clk debounce timer debounce_count : process (clk_i, rst_i) begin if rst_i = '0' then -- asynchronous reset (active low) debounce_cnt <= (others => '0'); elsif clk_i'event and clk_i = '1' then -- rising clock edge if (ps2_clk_fall or ps2_clk_rise or debounce_cao) = '1' then debounce_cnt <= (others => '0'); else debounce_cnt <= debounce_cnt + 1; end if; end if; end process; debounce_cao <= debounce_cnt(DEBOUNCE_BITS-1); -- debounce_cao <= '1' when debounce_cnt = -- CONV_STD_LOGIC_VECTOR(DEBOUNCE_TIMEOUT-1, DEBOUNCE_BITS) -- else '0'; -- PS2 clock debounce and edge detector debounce_stm : process (clk_i, rst_i) begin if rst_i = '0' then debounce_state <= stable; ps2_clk_clean <= '0'; elsif clk_i'event and clk_i = '1' then case debounce_state is when stable => if ps2_clk_clean /= ps2_clk_syn then if ps2_clk_syn = '1' then debounce_state <= rise; else debounce_state <= fall; end if; end if; when wait_stable => if debounce_cao = '1' then debounce_state <= stable; end if; when rise => debounce_state <= wait_stable; ps2_clk_clean <= '1'; when fall => debounce_state <= wait_stable; ps2_clk_clean <= '0'; when others => null; end case; end if; end process; ps2_clk_fall <= '1' when debounce_state = fall else '0'; ps2_clk_rise <= '1' when debounce_state = rise else '0'; -- PS2 watchdog wdt_proc : process(clk_i, rst_i) begin if rst_i = '0' then -- asynchronous reset (active low) wdt_cnt <= (others => '0'); elsif clk_i'event and clk_i = '1' then -- rising clock edge if (wdt_rst or wdt_cao) = '1' then wdt_cnt <= (others => '0'); elsif debounce_cao = '1' then wdt_cnt <= wdt_cnt + 1; end if; end if; end process; wdt_cao <= wdt_cnt(WATCHDOG_BITS-1); -- wdt_cao <= '1' when wdt_cnt = -- CONV_STD_LOGIC_VECTOR(WATCHDOG_TIMEOUT-1, WATCHDOG_BITS) -- else '0'; wdt_rst <= ps2_clk_fall; -- Shift register shift : process (clk_i, rst_i) begin if rst_i = '0' then -- asynchronous reset (active low) shift_reg <= (others => '0'); elsif clk_i'event and clk_i = '1' then -- rising clock edge if shift_load = '1' then shift_reg(7 downto 0) <= data_i; shift_reg(8) <= '0'; elsif shift_calc_parity = '1' then shift_reg(8) <= not shift_parity; elsif shift_in = '1' then shift_reg(7 downto 0) <= shift_reg(8 downto 1); shift_reg(8) <= ps2_data_syn; end if; end if; end process; -- Shift counter sft_cnt : process(clk_i, rst_i) begin if rst_i = '0' then -- asynchronous reset (active low) shift_cnt <= (others => '0'); elsif clk_i'event and clk_i = '1' then -- rising clock edge if state = start then shift_cnt <= (others => '0'); elsif state = data and ps2_clk_fall = '1' then shift_cnt <= shift_cnt + 1; end if; end if; end process; shift_cao <= '1' when shift_cnt = "111" else '0'; -- Odd Parity generator shift_parity <= (shift_reg(0) xor shift_reg(1) xor shift_reg(2) xor shift_reg(3) xor shift_reg(4) xor shift_reg(5) xor shift_reg(6) xor shift_reg(7)); -- Main State Machine stm : process (clk_i, rst_i) begin if rst_i = '0' then -- asynchronous reset (active low) state <= idle; writing <= '0'; elsif clk_i'event and clk_i = '1' then -- rising clock edge case state is -- Waiting for clk when idle => if obf_set_i = '1' and writing = '0' then state <= write_request; writing <= '1'; elsif ps2_clk_fall = '1' then state <= start; end if; -- Write request, clk low when write_request => if wdt_cao = '1' then state <= idle; end if; -- Clock 1, start bit when start => if wdt_cao = '1' then state <= idle; elsif ps2_clk_fall = '1' then state <= data; end if; -- Clocks 2-9, Data bits (LSB first) when data => if wdt_cao = '1' then state <= idle; elsif ps2_clk_fall = '1' and shift_cao = '1' then state <= parity; end if; -- Clock 10, Parity bit when parity => if wdt_cao = '1' then state <= idle; elsif ps2_clk_fall = '1' then state <= stop; end if; -- Clock 11, Stop bit when stop => writing <= '0'; state <= idle; when others => null; end case; end if; end process; -- State flags flags_proc : process (clk_i, rst_i, state, writing) begin -- process stm_out -- Input Buffer write flag if rst_i = '0' then -- asynchronous reset (active low) --obf <= '0'; ibf <= '0'; parity_err <= '0'; frame_err <= '0'; elsif clk_i'event and clk_i = '1' then -- rising clock edge -- Parity error flag if err_clr_i = '1' then parity_err <= '0'; elsif writing = '0' and state = stop then if shift_reg(8) /= not shift_parity then parity_err <= '1'; end if; end if; -- Frame error flag if err_clr_i = '1' then frame_err <= '0'; elsif (state = start or state = data or state = parity) and wdt_cao = '1' then frame_err <= '1'; end if; -- Input Buffer full flag if ibf_clr_i = '1' then ibf <= '0'; elsif writing = '0' and state = stop then if shift_reg(8) = not shift_parity then ibf <= '1'; end if; end if; -- Output buffer full flag --if state = stop and writing = '1' then -- obf <= '0'; --elsif obf_set_i = '1' then -- obf <= '1'; --end if; end if; end process; obf <= writing; -- Shift register control shift_load <= '1' when obf_set_i = '1' else '0'; shift_calc_parity <= '1' when state = idle and writing = '1' else '0'; shift_in <= ps2_clk_fall when state = data or state = start else '0'; -- PS2 Registered outputs syn_ps2_out : process (clk_i, rst_i) begin if rst_i = '0' then -- asynchronous reset (active low) ps2_data_out <= '1'; ps2_clk_out <= '1'; elsif clk_i'event and clk_i = '1' then -- rising clock edge -- PS2 Data out if writing = '1' then if state = idle then ps2_data_out <= '0'; elsif state = data or state = start then ps2_data_out <= shift_reg(0); else ps2_data_out <= '1'; end if; end if; -- PS2 Clk out if state = write_request then ps2_clk_out <= '0'; else ps2_clk_out <= '1'; end if; end if; end process; data_o <= shift_reg(7 downto 0); ibf_o <= ibf; obf_o <= obf; busy_o <= '0' when state = idle and writing = '0' else '1'; parity_err_o <= parity_err; frame_err_o <= frame_err; wdt_o <= wdt_cao; ps2_clk_io <= '0' when ps2_clk_out = '0' else 'Z'; ps2_data_io <= '0' when ps2_data_out = '0' else 'Z'; end rtl;