terminal/ps2.vhd

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;