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]