PS2 Receiver Module for VHDL

Description

I have several VHDL modules that have been coded to use with the Spartan 3E. My next couple of posts will have various modules followed by a top level design that connects them all together. This module is for a keyboard receiver. Keyboards communicate two ways. They transmit the key strokes in a 11 bit serial transmission. The first bit is a start bit, followed by 8 bits of data, then a parody bit, and finally ends with the stop bit.

Each key is transmitted one at a time. It is up the bios to interpet if keys are pushed in a combination, if a shift key is pushed, or if caps lock is on. Fortunately the ps2 protocol allows an easy way to check if shift is being pushed or not.

The key is pushed and transmits the keyboard code, and then periodically retransmits that code if the key is held down. When the key is released the keyboard will translate the hex value of F0 and then transmits the pressed key again. I use this to set a register if the shift is pushed and when the code gets resent it sets the register to to zero. The state machine for the ps2 receiver then just disregards two data sets sent after F0.

PS2 Rx

[vhdl]

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity ps2_rx is
port (
clk, rst : in std_logic;
ps2d, ps2c : in std_logic;
rx_en : in std_logic;
rx_done_tick : out std_logic;
shift_en : out std_logic;
dout : out std_logic_vector(7 downto 0)
);
end ps2_rx;

architecture arch of ps2_rx is
type statetype is (idle, dps, load);
signal state_reg, state_next : statetype;
— filter
signal filter_reg, filter_next : std_logic_vector(7 downto 0);
signal f_ps2c_reg, f_ps2c_next : std_logic;

signal b_reg, b_next : std_logic_vector(10 downto 0);
signal n_reg, n_next : unsigned(3 downto 0);
signal shift_reg, shift_next : std_logic := ‘0’;
signal last_reg, last_next : std_logic := ‘0’;

signal fall_edge : std_logic;

begin

— filter
process (clk, rst)
begin
if (rst = ‘1’) then
filter_reg <= (others => ‘0’);
f_ps2c_reg <= ‘0’;
elsif (clk’event and clk=’1′) then
filter_reg <= filter_next;
f_ps2c_reg <= f_ps2c_next;
end if;
end process;

filter_next <= ps2c & filter_reg(7 downto 1);
f_ps2c_next <= ‘1’ when filter_reg = "11111111" else
‘0’ when filter_reg = "00000000" else
f_ps2c_reg;

fall_edge <= f_ps2c_reg and (not f_ps2c_next);

— registers
process (clk, rst)
begin
if (rst = ‘1’) then
state_reg <= idle;
n_reg <= (others => ‘0’);
b_reg <= (others => ‘0’);
elsif (clk’event and clk=’1′) then
state_reg <= state_next;
n_reg <= n_next;
b_reg <= b_next;
shift_reg <= shift_next;
last_reg <= last_next;
end if;
end process;

— next-state logic
process(state_reg, n_reg, b_reg, fall_edge, rx_en, ps2d, shift_reg, last_reg)
begin
rx_done_tick <= ‘0’;
state_next <= state_reg;
n_next <= n_reg;
b_next <= b_reg;
shift_next <= shift_reg;
last_next <= last_reg;

case state_reg is
when idle =>
if (fall_edge = ‘1’ and rx_en=’1′) then
–shift in start bit
b_next <= ps2d & b_reg(10 downto 1);
n_next <= "1001"; — set count to 8 again
state_next <= dps;
end if;
when dps =>
if (fall_edge = ‘1’ ) then
b_next <= ps2d & b_reg(10 downto 1);
if (n_reg = 0) then
state_next <= load;
else
n_next <= n_reg – 1;
end if;
end if;
when load =>
— here we handle if signal f0 and following signal are
— asserted – we don’t want to transmit them to dout.
— one more state to complete last shift
state_next <= idle;
rx_done_tick <= ‘1’;
if (b_reg(8 downto 1) = x"12" or b_reg(8 downto 1) = x"59") then
shift_next <= not shift_reg;
if (last_reg = ‘1’) then
rx_done_tick <= ‘1’;
last_next <= ‘0’;
end if;
elsif (b_reg(8 downto 1) = "11110000") then
last_next <= ‘1’;
rx_done_tick <= ‘0’;
elsif (last_reg = ‘1’) then
last_next <= ‘0’;
rx_done_tick <= ‘0’;
end if;
end case;
end process;

shift_en <= shift_reg;
dout <= b_reg(8 downto 1);

end arch;
[/vhdl]

Keyboard to ASCII Decoder

There are 2^8 combinations – 256 options available and each keyboard code needs to be decoded to ascii codes. The following is a decoder checks if the registers are set for either caps or shift and determines the ascii output based on keyboard inputs.

key2ascii
[code lang=”vhdl”]
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity key2ascii is
port (
caps_enabled : in std_logic;
shift_enabled : in std_logic;
key_code : in std_logic_vector(7 downto 0);
ascii_code : out std_logic_vector(7 downto 0)
);
end key2ascii;

architecture arch of key2ascii is
signal ascii_normal, ascii_caps : std_logic_vector(7 downto 0);
begin
with key_code select
ascii_normal <=
"00110000" when "01000101", –0
"00110001" when "00010110", –1
"00110010" when "00011110", –2
"00110011" when "00100110", –3
"00110100" when "00100101", –4
"00110101" when "00101110", –5
"00110110" when "00110110", –6
"00110111" when "00111101", –7
"00111000" when "00111110", –8
"00111001" when "01000110", –9

"01100001" when "00011100", –a
"01100010" when "00110010", –b
"01100011" when "00100001", –c
"01100100" when "00100011", –d
"01100101" when "00100100", –e
"01100110" when "00101011", –f
"01100111" when "00110100", –g
"01101000" when "00110011", –h
"01101001" when "01000011", –i
"01101010" when "00111011", –j
"01101011" when "01000010", –k
"01101100" when "01001011", –l
"01101101" when "00111010", –m
"01101110" when "00110001", –n
"01101111" when "01000100", –o
"01110000" when "01001101", –p
"01110001" when "00010101", –q
"01110010" when "00101101", –r
"01110011" when "00011011", –s
"01110100" when "00101100", –t
"01110101" when "00111100", –u
"01110110" when "00101010", –v
"01110111" when "00011101", –w
"01111000" when "00100010", –x
"01111001" when "00110101", –y
"01111010" when "00011010", –z

"01100000" when "00001110", –‘
"00101101" when "01001110", —
"00111101" when "01010101", –=
"01011011" when "01010100", –[
"01011101" when "01011011", –]
"01011100" when "01011101", —
"00111011" when "01001100", –;
"00100111" when "01010010", —
"00101100" when "01000001", –,
"00101110" when "01001001", –.
"00101111" when "01001010", –/
"01111010" when "11110000", — F0

"00100000" when "00101001", — space
"00001101" when "01011010", — enter
"00001000" when "01100110", — backspace
"00101010" when others; –*

with key_code select
ascii_caps <=
"00101001" when "01000101", –)
"00010001" when "00010110", –!
"00100000" when "00011110", –@
"00010011" when "00100110", –#
"00010100" when "00100101", –$
"00100101" when "00101110", –%
"01011110" when "00110110", –^
"00100110" when "00111101", –&
"00101010" when "00111110", –*
"00101000" when "01000110", –(

"01000001" when "00011100", –A
"01000010" when "00110010", –B
"01000011" when "00100001", –C
"01000100" when "00100011", –D
"01000101" when "00100100", –E
"01000110" when "00101011", –F
"01000111" when "00110100", –G
"01001000" when "00110011", –H
"01001001" when "01000011", –I
"01001010" when "00111011", –J
"01001011" when "01000010", –K
"01001100" when "01001011", –L
"01001101" when "00111010", –M
"01001110" when "00110001", –N
"01001111" when "01000100", –O
"01010000" when "01001101", –P
"01010001" when "00010101", –Q
"01010010" when "00101101", –R
"01010011" when "00011011", –S
"01010100" when "00101100", –T
"01010101" when "00111100", –U
"01010110" when "00101010", –V
"01010111" when "00011101", –W
"01011000" when "00100010", –X
"01011001" when "00110101", –Y
"01011010" when "00011010", –Z

"00111111" when "01001010", –?

"00100000" when "00101001", — space
"00001101" when "01011010", — enter
"00001000" when "01100110", — backspace
"00101010" when others; –*

ascii_code <= ascii_normal when (caps_enabled = ‘0’ and shift_enabled = ‘0’) else
ascii_caps when (caps_enabled = ‘0’ and shift_enabled = ‘1’) else
— ascii_caps when (caps_enabled = ‘1’ and shift_enabled = ‘0’) else
ascii_normal;
end arch;
[/code]

2 Responses

Subscribe to comments with RSS, or TrackBack to 'PS2 Receiver Module for VHDL'.

  • peter dibal says:

    Good day sir. I have followed your code closely and I am really impressed.
    Sir, I want to ur vhdl code for PS2 receiver module. However I can’t find the top module. I will be grateful if you can help me with the link to the top module.

    Warm regards,
    Peter

    • zaphinath says:

      Hi Peter,

      I have looked for my old code, but I cannot seem to find it. If you need a top level module you can use xilinx SDK to generate a new project and just call it top level. Then all you need to do is add the other two vhd modules to the top level, and wire them up. I don’t really use VHDL anymore.

Leave a Reply

Your email address will not be published. Required fields are marked *