362 lines
16 KiB
VHDL
362 lines
16 KiB
VHDL
|
-------------------------------------------------------------------------------
|
||
|
-- Title : PS/2 interface
|
||
|
-- Project :
|
||
|
-------------------------------------------------------------------------------
|
||
|
-- File : ps2.vhd
|
||
|
-- Author : Daniel Quintero <danielqg@infonegocio.com>
|
||
|
-- 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;
|