zxuno-git/cores/KypSpectrum/ps2.vhd

159 lines
4.5 KiB
VHDL

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity ps2 is
port
(
clockPs2 : in std_logic;
clock : inout std_logic;
data : inout std_logic;
received : out std_logic;
scancode : out std_logic_vector(7 downto 0)
);
end;
architecture behavioral of ps2 is
constant state_idle : std_logic_vector(1 downto 0) := "00";
constant state_receiving : std_logic_vector(1 downto 0) := "01";
constant state_waiting : std_logic_vector(1 downto 0) := "10";
constant state_received : std_logic_vector(1 downto 0) := "11";
constant minClockPulseLen : natural := 30*7; -- 30us @ 7 MHz
constant maxClockPulseLen : natural := 50*7; -- 50us @ 7 MHz
type reg is
record
state : std_logic_vector( 1 downto 0);
clockCounter : std_logic_vector(10 downto 0);
dataReg : std_logic_vector(10 downto 0);
receivedCount : std_logic_vector( 3 downto 0);
ps2clkDeglitch : std_logic_vector( 4 downto 0);
ps2dataDeglitch : std_logic_vector( 4 downto 0);
scancode : std_logic_vector( 7 downto 0);
ps2lastClkDeglitched : std_logic;
ps2lastDataDeglitched : std_logic;
ps2ClkDeglitched : std_logic;
ps2DataDeglitched : std_logic;
dataReceived : std_logic;
end record;
signal n : reg;
signal r : reg :=
(
(others => '0'),
(others => '0'),
(others => '0'),
(others => '0'),
(others => '0'),
(others => '0'),
(others => '0'),
'0',
'0',
'0',
'0',
'0'
);
begin
clock <= 'Z';
data <= 'Z';
received <= r.dataReceived;
scancode <= r.scancode;
r <= n when rising_edge(clockPs2);
process(r, clock, data)
begin
n <= r;
-- remember what the last signals were
n.ps2lastClkDeglitched <= r.ps2clkDeglitched;
n.ps2lastClkDeglitched <= r.ps2clkDeglitched;
-- Deglitch the clock signal
if clock = '1' then
if r.ps2clkDeglitch < 31 then
n.ps2clkDeglitch <= r.ps2clkDeglitch+1;
else
n.ps2clkDeglitched <= '1';
n.clockCounter <= (others => '0');
end if;
else
if r.ps2clkDeglitch > 0 then
n.ps2clkDeglitch <= r.ps2clkDeglitch-1;
else
n.ps2clkDeglitched <= '0';
n.clockCounter <= (others => '0');
end if;
end if;
-- Deglitch the data signal
if data = '1' then
if r.ps2dataDeglitch < 31 then
n.ps2dataDeglitch <= r.ps2dataDeglitch+1;
else
n.ps2dataDeglitched <= '1';
end if;
else
if r.ps2dataDeglitch > 0 then
n.ps2dataDeglitch <= r.ps2dataDeglitch-1;
else
n.ps2dataDeglitched <= '0';
end if;
end if;
----------------------------------------------------
-- Now the actual processing of the tidied up signal
----------------------------------------------------
case r.state is
when state_idle =>
-- Are we waiting for the ps2clk to go low? (start of data)
n.clockCounter <= (others => '0');
n.receivedCount <= (others => '0');
n.dataReceived <= '0';
if r.ps2clkDeglitched = '0' then n.state <= state_receiving; end if;
when state_receiving =>
n.clockCounter <= r.clockCounter+1;
-- is the pulse too long?
if r.clockCounter > maxClockPulseLen then
if r.ps2clkDeglitched = '1' then n.state <= state_idle; else n.state <= state_receiving; end if;
end if;
if r.ps2lastClkDeglitched = '0' and r.ps2clkDeglitched = '1' then
-- we on the rising edge of the clock singal
if r.clockCounter < minClockPulseLen then
n.state <= state_idle;
else
n.receivedCount <= r.receivedCount+1;
n.clockCounter <= (others => '0');
n.dataReg <= r.ps2dataDeglitched & r.dataReg(10 downto 1);
if r.receivedCount = 10 then n.state <= state_received; else n.receivedCount <= r.receivedCount+1; end if;
end if;
elsif r.ps2lastClkDeglitched = '1' and r.ps2clkDeglitched = '0' then
-- we on the falling edge of the clock singal
if r.clockCounter < minClockPulseLen then n.state <= state_waiting; end if;
n.clockCounter <= (others => '0');
end if;
when state_received =>
-- Check the start, parity and stopbits are valid, if all are correct, set the "data Received" signal
if r.dataReg(10) = '1' and (r.dataReg(9) xor r.dataReg(8) xor r.dataReg(7) xor r.dataReg(6) xor r.dataReg(5) xor r.dataReg(4) xor r.dataReg(3) xor r.dataReg(2) xor r.dataReg(1)) = '1' and r.dataReg (0) = '0' then
n.scancode <= r.dataReg(8 downto 1);
n.dataReceived <= '1';
n.state <= state_idle;
end if;
when others =>
-- We are waiting for the ps2clk to go high
if r.ps2clkDeglitched = '1' then n.state <= state_idle; end if;
end case;
end process;
end;