📄 ps2_keyboard_interface.vhd
字号:
---------------------------------------------------------------------------------------
--
-- Author: John Clayton
-- Date : April 30, 2001
-- Update: 4/30/01 copied this file from lcd_2.v (pared down).
-- Update: 5/24/01 changed the first module from "ps2_keyboard_receiver"
-- to "ps2_keyboard_interface"
-- Update: 5/29/01 Added input synchronizing flip-flops. Changed state
-- encoding (m1) for good operation after part config.
-- Update: 5/31/01 Added low drive strength and slow transitions to ps2_clk
-- and ps2_data in the constraints file. Added the signal
-- "tx_shifting_done" as distinguished from "rx_shifting_done."
-- Debugged the transmitter portion in the lab.
-- Update: 6/01/01 Added horizontal tab to the ascii output.
-- Update: 6/01/01 Added parameter TRAP_SHIFT_KEYS.
-- Update: 6/05/01 Debugged the "debounce" timer functionality.
-- Used 60usec timer as a "watchdog" timeout during
-- receive from the keyboard. This means that a keyboard
-- can now be "hot plugged" into the interface, without
-- messing up the bit_count, since the bit_count is reset
-- to zero during periods of inactivity anyway. This was
-- difficult to debug. I ended up using the logic analyzer,
-- and had to scratch my head quite a bit.
-- Update: 6/06/01 Removed extra comments before the input synchronizing
-- flip-flops. Used the correct parameter to size the
-- 5usec_timer_count. Changed the name of this file from
-- ps2.v to ps2_keyboard.v
-- Update: 6/06/01 Removed "&& q[7:0]" in output_strobe logic. Removed extra
-- commented out "else" condition in the shift register and
-- bit counter.
-- Update: 6/07/01 Changed default values for 60usec timer parameters so that
-- they correspond to 60usec for a 49.152MHz clock.
--
-- Converted to VHDL: 10 February 2004 - John Kent
--
--
--
-- Description
---------------------------------------------------------------------------------------
-- This is a state-machine driven serial-to-parallel and parallel-to-serial
-- interface to the ps2 style keyboard interface. The details of the operation
-- of the keyboard interface were obtained from the following website:
--
-- http:--www.beyondlogic.org/keyboard/keybrd.htm
--
-- Some aspects of the keyboard interface are not implemented (e.g, parity
-- checking for the receive side, and recognition of the various commands
-- which the keyboard sends out, such as "power on selt test passed," "Error"
-- and "Resend.") However, if the user wishes to recognize these reply
-- messages, the scan code output can always be used to extend functionality
-- as desired.
--
-- Note that the "Extended" (0xE0) and "Released" (0xF0) codes are recognized.
-- The rx interface provides separate indicator flags for these two conditions
-- with every valid character scan code which it provides. The shift keys are
-- also trapped by the interface, in order to provide correct uppercase ASCII
-- characters at the ascii output, although the scan codes for the shift keys
-- are still provided at the scan code output. So, the left/right ALT keys
-- can be differentiated by the presence of the rx_entended signal, while the
-- left/right shift keys are differentiable by the different scan codes
-- received.
--
-- The interface to the ps2 keyboard uses ps2_clk clock rates of
-- 30-40 kHz, dependent upon the keyboard itself. The rate at which the state
-- machine runs should be at least twice the rate of the ps2_clk, so that the
-- states can accurately follow the clock signal itself. Four times
-- oversampling is better. Say 200kHz at least. The upper limit for clocking
-- the state machine will undoubtedly be determined by delays in the logic
-- which decodes the scan codes into ASCII equivalents. The maximum speed
-- will be most likely many megahertz, depending upon target technology.
-- In order to run the state machine extremely fast, synchronizing flip-flops
-- have been added to the ps2_clk and ps2_data inputs of the state machine.
-- This avoids poor performance related to slow transitions of the inputs.
--
-- Because this is a bi-directional interface, while reading from the keyboard
-- the ps2_clk and ps2_data lines are used as inputs. While writing to the
-- keyboard, however (which may be done at any time. If writing interrupts a
-- read from the keyboard, the keyboard will buffer up its data, and send
-- it later) both the ps2_clk and ps2_data lines are occasionally pulled low,
-- and pullup resistors are used to bring the lines high again, by setting
-- the drivers to high impedance state.
--
-- The tx interface, for writing to the keyboard, does not provide any special
-- pre-processing. It simply transmits the 8-bit command value to the
-- keyboard.
--
-- Pullups MUST BE USED on the ps2_clk and ps2_data lines for this design,
-- whether they be internal to an FPGA I/O pad, or externally placed.
-- If internal pullups are used, they may be fairly weak, causing bounces
-- due to crosstalk, etc. There is a "debounce timer" implemented in order
-- to eliminate erroneous state transitions which would occur based on bounce.
--
-- Parameters are provided in order to configure and appropriately size the
-- counter of a 60 microsecond timer used in the transmitter, depending on
-- the clock frequency used. The 60 microsecond period is guaranteed to be
-- more than one period of the ps2_clk_s signal.
--
-- Also, a smaller 5 microsecond timer has been included for "debounce".
-- This is used because, with internal pullups on the ps2_clk and ps2_data
-- lines, there is some bouncing around which occurs
--
-- A parameter TRAP_SHIFT_KEYS allows the user to eliminate shift keypresses
-- from producing scan codes (along with their "undefined" ASCII equivalents)
-- at the output of the interface. If TRAP_SHIFT_KEYS is non-zero, the shift
-- key status will only be reported by rx_shift_key_on. No ascii or scan
-- codes will be reported for the shift keys. This is useful for those who
-- wish to use the ASCII data stream, and who don't want to have to "filter
-- out" the shift key codes.
--
---------------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use ieee.numeric_std.all;
entity ps2_keyboard_interface is
port(
clk : in std_logic;
reset : in std_logic;
ps2_clk : inout std_logic;
ps2_data : inout std_logic;
rx_extended : out std_logic;
rx_released : out std_logic;
rx_shift_key_on : out std_logic;
-- rx_scan_code : out std_logic_vector(7 downto 0);
rx_ascii : out std_logic_vector(7 downto 0);
rx_data_ready : out std_logic; -- rx_read_o
rx_read : in std_logic; -- rx_read_ack_i
tx_data : in std_logic_vector(7 downto 0);
tx_write : in std_logic;
tx_write_ack : out std_logic;
tx_error_no_keyboard_ack : out std_logic
);
end ps2_keyboard_interface;
-------------------------------------------------------------------------------
-- Architecture for ps2 keyboard interface
-------------------------------------------------------------------------------
architecture my_ps2_keyboard of ps2_keyboard_interface is
-----------------------------------------------------------------------------
constant TOTAL_BITS : integer := 11;
constant EXTEND_CODE : integer := 16#E0#;
constant RELEASE_CODE : integer := 16#F0#;
constant LEFT_SHIFT : integer := 16#12#;
constant RIGHT_SHIFT : integer := 16#59#;
-- constants
-- The timer value can be up to (2^bits) inclusive.
-- Values for 49.152 MHz clock
--constant TIMER_60USEC_VALUE_PP : integer := 2950; -- Number of sys_clks for 60usec.
--constant TIMER_60USEC_BITS_PP : integer := 12; -- Number of bits needed for timer
--constant TIMER_5USEC_VALUE_PP : integer := 186; -- Number of sys_clks for debounce
--constant TIMER_5USEC_BITS_PP : integer := 8; -- Number of bits needed for timer
-- Values for 12.5 MHz Clock
constant TIMER_60USEC_VALUE_PP : integer := 750; -- Number of sys_clks for 60usec.
constant TIMER_60USEC_BITS_PP : integer := 10; -- Number of bits needed for timer
constant TIMER_5USEC_VALUE_PP : integer := 62; -- Number of sys_clks for debounce
constant TIMER_5USEC_BITS_PP : integer := 6; -- Number of bits needed for timer
constant TRAP_SHIFT_KEYS_PP : integer := 0; -- Default: No shift key trap.
-- State encodings, provided as constants
-- for flexibility to the one instantiating the module.
-- In general, the default values need not be changed.
-- State "m1_rx_clk_l" has been chosen on purpose. Since the input
-- synchronizing flip-flops initially contain zero, it takes one clk
-- for them to update to reflect the actual (idle = high) status of
-- the I/O lines from the keyboard. Therefore, choosing 0 for m1_rx_clk_l
-- allows the state machine to transition to m1_rx_clk_h when the true
-- values of the input signals become present at the outputs of the
-- synchronizing flip-flops. This initial transition is harmless, and it
-- eliminates the need for a "reset" pulse before the interface can operate.
type m1_type is ( m1_rx_clk_h, m1_rx_clk_l,
m1_tx_wait_clk_h, m1_tx_force_clk_l,
m1_tx_clk_h, m1_tx_clk_l,
m1_tx_wait_keyboard_ack, m1_tx_done_recovery,
m1_tx_error_no_keyboard_ack, m1_tx_rising_edge_marker,
m1_tx_first_wait_clk_h, m1_tx_first_wait_clk_l, m1_tx_reset_timer,
m1_rx_falling_edge_marker, m1_rx_rising_edge_marker );
type m2_type is ( m2_rx_data_ready_ack, m2_rx_data_ready );
-- Internal signal declarations
signal timer_60usec_done : std_logic;
signal timer_5usec_done : std_logic;
signal extended : std_logic;
signal released : std_logic;
signal shift_key_on : std_logic;
-- NOTE: These two signals used to be one. They
-- were split into two signals because of
-- shift key trapping. With shift key
-- trapping, no event is generated externally,
-- but the "hold" data must still be cleared
-- anyway regardless, in preparation for the
-- next scan codes.
signal rx_output_event : std_logic; -- Used only to clear: hold_released, hold_extended
signal rx_output_strobe : std_logic; -- Used to produce the actual output.
signal tx_parity_bit : std_logic;
signal rx_shifting_done : std_logic;
signal tx_shifting_done : std_logic;
signal shift_key_plus_code: std_logic_vector(11 downto 0);
signal q : std_logic_vector(TOTAL_BITS-1 downto 0);
signal m1_state : m1_type;
signal m1_next_state : m1_type;
signal m2_state : m2_type;
signal m2_next_state : m2_type;
signal bit_count : std_logic_vector(3 downto 0);
signal enable_timer_60usec: std_logic;
signal enable_timer_5usec : std_logic;
signal timer_60usec_count : std_logic_vector(TIMER_60USEC_BITS_PP-1 downto 0);
signal timer_5usec_count : std_logic_vector(TIMER_5USEC_BITS_PP-1 downto 0);
signal ascii : std_logic_vector(7 downto 0); -- "REG" type only because a case statement is used.
signal left_shift_key : std_logic;
signal right_shift_key : std_logic;
signal hold_extended : std_logic; -- Holds prior value, cleared at rx_output_strobe
signal hold_released : std_logic; -- Holds prior value, cleared at rx_output_strobe
signal ps2_clk_s : std_logic; -- Synchronous version of this input
signal ps2_data_s : std_logic; -- Synchronous version of this input
signal ps2_clk_hi_z : std_logic; -- Without keyboard, high Z equals 1 due to pullups.
signal ps2_data_hi_z : std_logic; -- Without keyboard, high Z equals 1 due to pullups.
signal tx_write_ack_o : std_logic;
begin
----------------------------------------------------------------------------
-- Module code
-- assign ps2_clk = ps2_clk_hi_z?1'bZ:1'b0;
-- assign ps2_data = ps2_data_hi_z?1'bZ:1'b0;
--
ps2_direction : process( ps2_clk_hi_z, ps2_data_hi_z )
begin
if( ps2_clk_hi_z = '1' ) then
ps2_clk <= 'Z';
else
ps2_clk <= '0';
end if;
if( ps2_data_hi_z = '1' ) then
ps2_data <= 'Z';
else
ps2_data <= '0';
end if;
end process;
-- Input "synchronizing" logic -- synchronizes the inputs to the state
-- machine clock, thus avoiding errors related to
-- spurious state machine transitions.
ps2_synch : process(clk, ps2_clk, ps2_data)
begin
if clk'event and clk='1' then
ps2_clk_s <= ps2_clk;
ps2_data_s <= ps2_data;
end if;
end process;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -