Код: Выделить всё
--------------------------------------------------------------------------------
--
-- Copyright (c) 2009, 2010 SII
--
-- Version 1.0
--
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
--
--------------------------------------------------------------------------------
--
-- КОНТРОЛЛЕР ИНТЕРФЕЙСА PS/2
--
-- Этот контроллер позволяет подключить к ПЛИС любое устройство с интерфейсом
-- PS/2 (клавиатуру или мышь). Универсальные устройства, допускающие подключе-
-- ние к компьютеру как к порту USB, так и к PS/2 (через переходник), могут
-- оказаться неработоспособными, поскольку встроенный в них контроллер часто
-- ориентирован на особенности работы контроллера PS/2, применяемого в совре-
-- менных ПК. Иногда отказываются работать и "чистые" устройства PS/2; причина
-- этого явления пока не ясна.
--
--
-- Сигналы контроллера:
--
-- CLK синхронизация (частота 50 МГц);
-- RST сброс;
-- PS2_CLK линия синхронизации интерфейса PS/2;
-- PS2_DATA линия данных интерфейса PS/2;
-- BUSY признак занятости контроллера;
-- PE признак ошибки чётности в принятых данных;
-- TE признак таймаута в операции приёма или передачи данных;
-- SE признак ошибки в последовательности сигналов интерфейса;
-- DIN входной байт данных для передачи устройству;
-- WR команда вывода байта данных на устройство;
-- DOUT выходной байт данных, полученный от устройства;
-- RDY признак поступления байта данных от устройства;
-- ACK подтверждение приёма данных/состояния.
--
--
-- После сброса (сразу после включения питания или после поступления сигнала
-- RST) контроллер переходит в состояние бездействия, при этом на линиях BUSY,
-- PE, TE, SE, RDY устанавливается низкий (неактивный уровень). В этом состоя-
-- нии он остаётся до начала приёма или передачи данных, причём на BUSY будет
-- удерживаться низкий уровень (остальные указанные сигналы характеризуют по-
-- следнюю выполненную операцию и поэтому могут иметь различные состояния).
-- Как только контроллер выйдет из состояния бездействия в связи с началом
-- приёма или передачи данных, на линии BUSY появится высокий уровень, а сигна-
-- лы PE, TE, SE и RDY будут сброшены.
--
-- В состояние приёма контроллер переходит, обнаружив, что подключенное к нему
-- устройство желает передать ему данные (при переходе линии PS2_CLK в низкий
-- уровень). После успешного окончания приёма выдаётся сигнал RDY, который ос-
-- танется активным до начала следующей операции приёма или передачи либо до
-- поступления сигнала сброса. Считать полученный байт можно в любой момент,
-- когда активен RDY, по линиям DOUT. RDY сбрасывается в начале новой операции
-- приёма/передачи данных, при сбросе контроллера (сигнал RST) или при получении
-- сигнала подтверждения приёма данных/состояния ACK.
--
-- В процессе приёма могут возникнуть ошибки всех трёх типов. Ошибка последова-
-- тельности (SE) возникает, если устройство выдало единичный стартовый бит, а
-- не нулевой. Таймаут (TE) будет обнаружен, если очередного ожидаемого фронта
-- или спада сигнала PS2_CLK не будет в течение достаточно длительного времени
-- (более 100 мкс для частоты 50 МГц). Ошибка чётности (PE) обнаруживается в
-- конце приёма при неверном бите чётности. Обнаружение любой ошибки вызывает
-- установку соответствующего сигнала и переход контроллера в состояние бездей-
-- ствия. Сигналы ошибок останутся активными до тех пор, пока не будет начата
-- новая операция приёма или передачи данных, не поступит сигнал сброса контрол-
-- лера либо не поступит сигнал подтверждения приёма данных/состояния ACK.
--
-- В состояние передачи контроллер переходит, если он находится в состоянии
-- бездействия, устройство не начинает выдачу данных (линия PS2_CLK имеет высо-
-- кий уровень) и на линии WR появляется высокий уровень. В этом случае кон-
-- троллер запоминает данные, находящиеся на линиях DIN, и переходит к передаче
-- сохранённого байта данных. Когда передача данных оканчивается, контроллер
-- возвращается в состояние бездействия.
--
-- В процессе передачи могут возникнуть таймаут (TE) и ошибка в последователь-
-- ности сигналов (SE); последняя возникает, если после окончания передачи
-- устройство выдаёт неправильный бит подтверждения. При обнаружении любой из
-- ошибок устанавливается сигнал на соответствующей линии, и контроллер перехо-
-- дит в состояние бездействия. Сигналы ошибок останутся активными до начала но-
-- вой операции приёма или передачи данных, поступления сигналап сброса контрол-
-- лера либо сигнала подтверждения приёма данных/состояния ACK.
--
-- Для сигналов интерфейса PS/2 предусмотрена "очистка" от дребезга: переход
-- сигнала из одного состояние в другое фиксируется только в том случае, если
-- сигнал находится в новом состоянии не менее 16 тактов.
--
-- Контроллер реагирует на сигнал сброса RST всегда (немедленно прекращает теку-
-- щую операцию и выполняет сброс), а на сигнал ACK - только в состоянии бездей-
-- ствия.
--
--------------------------------------------------------------------------------
--
-- Известные проблемы:
--
-- 1. Универсальные клавиатуры (USB, подключаемые к PS/2 через переходник) могут
-- не работать. Вероятно, они "закладываются" на особенности поведения кон-
-- троллера PS/2, используемого в ПК, и не могут правильно опознать подключен-
-- ие к этому контроллеру.
--
--------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity PS2 is
port ( CLK : in std_logic;
RST : in std_logic;
PS2_CLK : inout std_logic;
PS2_DATA : inout std_logic;
BUSY : out std_logic;
PE : out std_logic;
TE : out std_logic;
SE : out std_logic;
DIN : in std_logic_vector (7 downto 0);
WR : in std_logic;
DOUT : out std_logic_vector (7 downto 0);
RDY : out std_logic;
ACK : in std_logic);
end PS2;
architecture Behavioral of PS2 is
-- Состояния контроллера.
type TState is (
Reset, -- Сброс контроллера
WaitAfterReset, -- Ожидание успокоения сигналов интерфейса после сброса
Idle, -- Бездействие
RX_Start, -- Начало приёма
RX_WaitClkUp, -- Ожидание фронта PS2C при приёме
RX_WaitClkDown, -- Ожидание среза PS2C и приём очередного бита
RX_Done, -- Приём окончен, проверка корректности
TX_Start, -- Начало передачи, формирование передаваемых битов
TX_InitWait, -- Удержание PS2_CLK на низком уровне в течение 100 мкс
TX_WaitInitClkUp, -- Ожидание установки PS2C после освобождения PS2_CLK
TX_WaitInitClkDown, -- Ожидание первого спада PS2C
TX_WaitClkUp, -- Ожидание фронта PS2C после выдачи очередного бита
TX_WaitClkDown, -- Ожидание спада PS2C и выдача очередного бита
TX_WaitAck, -- Ожидание спада PS2C и поступления бита подтверждения
TX_Done -- Окончание передачи, ожидание 1 на PS2C и PS2D
);
signal State : TState;
-- Сдвиговый регистр для приёма и передачи данных. Сдвиг всегда происходит
-- вправо. В конечном состоянии при приёме и начальном при передаче разряд
-- 0 содержит стартовый бит (всегда 0), биты 1-8 - байт данных (биты 0-7),
-- бит 9 является контрольным, 10-й разряд - стоп-бит (всегда 1).
signal ShiftReg : std_logic_vector (10 downto 0);
-- Счётчик количества обработанных битов.
signal BitCnt : std_logic_vector (3 downto 0);
-- Счётчик для реализации таймаутов и константы задержек (для частоты 50 МГц).
signal TimeoutCnt : std_logic_vector (19 downto 0);
constant Wait100us : std_logic_vector (19 downto 0) := "00000001001110001000";
constant Wait15ms : std_logic_vector (19 downto 0) := "10110111000110110000";
-- Внутренние сигналы для "очистки" сигналов интерфейса PS/2 от дребезга.
signal PS2CL, PS2DL : std_logic;
signal PS2CC, PS2DC : std_logic_vector (3 downto 0);
-- "Очищенные" значения сигналов интерфейса PS/2.
signal PS2C, PS2D : std_logic;
-- Постоянно вычисляемые значения принимаемого и передаваемого байтов,
-- используются для контроля принятого байта и формирования передаваемой
-- последовательности битов.
signal ParityIn, ParityOut : std_logic;
begin
-- "Очистка" сигнала PS2_CLK.
process (CLK)
begin
if rising_edge(CLK) then
if PS2_CLK /= PS2CL then
PS2CL <= PS2_CLK;
PS2CC <= "0000";
elsif PS2CC = "1111"
then PS2C <= PS2CL;
else PS2CC <= PS2CC + 1;
end if;
end if;
end process;
-- "Очистка" сигнала PS2_DATA.
process (CLK)
begin
if rising_edge(CLK) then
if PS2_DATA /= PS2DL then
PS2DL <= PS2_DATA;
PS2DC <= "0000";
elsif PS2DC = "1111"
then PS2D <= PS2DL;
else PS2DC <= PS2DC + 1;
end if;
end if;
end process;
-- Вычисление паритета принятого и передаваемого байтов.
ParityIn <= ShiftReg(1) xor ShiftReg(2) xor ShiftReg(3) xor ShiftReg(4)
xor ShiftReg(5) xor ShiftReg(6) xor ShiftReg(7) xor ShiftReg(8);
ParityOut <= DIN(0) xor DIN(1) xor DIN(2) xor DIN(3)
xor DIN(4) xor DIN(5) xor DIN(6) xor DIN(7);
-- Вывод принятого байта.
DOUT <= ShiftReg(8 downto 1);
-- Формирование признака занятости контроллера.
BUSY <= '0' when State = Idle else '1';
-- Машина состояний.
process (CLK, RST)
begin
if RST = '1' then
RDY <= '0';
TE <= '0';
SE <= '0';
PE <= '0';
ShiftReg <= "00000000000";
TimeoutCnt <= (others => '0');
PS2_CLK <= '0';
PS2_DATA <= 'Z';
State <= Reset;
elsif rising_edge(CLK) then
case State is
-- Состояние сброса. Исходные значения сигналов уже установлены, здесь
-- лишь ждём не менее 100 мкс, чтобы устройство, подключенное к интер-
-- фейсу, успело опознать запрет передачи.
when Reset =>
if TimeoutCnt = Wait100us
then
TimeoutCnt <= (others => '0');
PS2_CLK <= 'Z';
State <= WaitAfterReset;
else
TimeoutCnt <= TimeoutCnt + 1;
State <= Reset;
end if;
-- Ожидание не меньше 100 мкс после сброса для успокоения значений сиг-
-- налов интерфейса.
when WaitAfterReset =>
if TimeoutCnt = Wait100us
then State <= Idle;
else
TimeoutCnt <= TimeoutCnt + 1;
State <= WaitAfterReset;
end if;
-- Состояние бездействия. Анализируется начало передачи от устройства
-- (переход PS2C в низкий уровень) и появление запроса хоста на вывод
-- данных (появление высокого уровня на WR).
when Idle =>
if ACK = '1' then
TE <= '0';
SE <= '0';
PE <= '0';
RDY <= '0';
State <= Idle;
elsif PS2C = '0' then
if PS2D = '0'
then State <= RX_Start;
else
SE <= '1';
State <= Idle;
end if;
elsif WR = '1'
then State <= TX_Start;
else State <= Idle;
end if;
-- Начало приёма. Переход сюда происходит из состояния бездействия
-- (Idle) при обнаружении на PS2C и PS2D низкого уровня (начало переда-
-- чи от устройства).
when RX_Start =>
RDY <= '0';
TE <= '0';
PE <= '0';
SE <= '0';
BitCnt <= "0000";
TimeoutCnt <= (others => '0');
ShiftReg <= "00000000000";
State <= RX_WaitClkUp;
-- Ожидание фронта PS2C при приёме. Переход в это состояние осуществля-
-- ется из состояний RX_Start и RX_WaitClkDown после приёма очередного
-- бита.
when RX_WaitClkUp =>
if PS2C = '1' then
TimeoutCnt <= (others => '0');
if BitCnt = "1010"
then State <= RX_Done;
else State <= RX_WaitClkDown;
end if;
elsif TimeoutCnt = Wait100us
then
TE <= '1';
State <= Idle;
else
TimeoutCnt <= TimeoutCnt + 1;
State <= RX_WaitClkUp;
end if;
-- Ожидание спада PS2C и приём по нему очередного бита. В это состояние
-- контроллер переходит из RX_WaitClkUp после появления на PS2C высоко-
-- го уровня.
when RX_WaitClkDown =>
if PS2C = '0' then
ShiftReg(9 downto 0) <= ShiftReg(10 downto 1);
ShiftReg(10) <= PS2D;
TimeoutCnt <= (others => '0');
BitCnt <= BitCnt + 1;
State <= RX_WaitClkUp;
elsif TimeoutCnt = Wait100us
then
TE <= '1';
State <= Idle;
else
TimeoutCnt <= TimeoutCnt + 1;
State <= RX_WaitClkDown;
end if;
-- Проверка паритета принятого байта и значе-
-- ния стоп-бита. Контроллер переходит в это состояние из состояния
-- RX_WaitClkUp после приёма последнего бита.
when RX_Done =>
if ShiftReg(10) = '0'
then SE <= '1';
elsif (ParityIn xor ShiftReg(9)) = '0'
then PE <= '1';
else RDY <= '1';
end if;
State <= Idle;
-- Начало передачи. Переход сюда из состояния бездействия при обнаруже-
-- нии активного сигнала WR при PS2C='1' (устройство не начинает пере-
-- дачу). Формируется битовая последовательность, которая должна быть
-- передана, и устройство оповещается о начале передачи.
when TX_Start =>
RDY <= '0';
TE <= '0';
PE <= '0';
SE <= '0';
ShiftReg(0) <= '0';
ShiftReg(8 downto 1) <= DIN(7 downto 0);
ShiftReg(9) <= not ParityOut;
ShiftReg(10) <= '1';
PS2_CLK <= '0';
PS2_DATA <= '0';
TimeoutCnt <= (others => '0');
BitCnt <= "0000";
State <= TX_InitWait;
-- Оповещение устройства о начале передачи: в течение не менее 100 мкс
-- на линии PS2_CLK контроллер удерживает низкий уровень, по окончании
-- чего линия PS2_CLK освобождается (на PS2_DATA присутствует 0 - стар-
-- товый бит из ShiftReg).
when TX_InitWait =>
if TimeoutCnt /= Wait100us then
TimeoutCnt <= TimeoutCnt + 1;
State <= TX_InitWait;
else
PS2_CLK <= 'Z';
PS2_DATA <= ShiftReg(0);
ShiftReg(9 downto 0) <= ShiftReg(10 downto 1);
ShiftReg(10) <= PS2D;
BitCnt <= BitCnt + 1;
TimeoutCnt <= (others => '0');
State <= TX_WaitInitClkUp;
end if;
-- Ожидание установления на PS2C высокого уровня после освобождения
-- PS2_CLK (до 100 мкс, потом таймаут).
when TX_WaitInitClkUp =>
if PS2C = '1' then
TimeoutCnt <= (others => '0');
State <= TX_WaitInitClkDown;
elsif TimeoutCnt = Wait100us
then
TE <= '1';
PS2_DATA <= 'Z';
State <= Idle;
else
TimeoutCnt <= TimeoutCnt + 1;
State <= TX_WaitInitClkUp;
end if;
-- Ожидание первого спада на PS2C после освобождения PS2_CLK. Это приз-
-- нак того, что устройство приняло стартовый бит, и надо выдавать сле-
-- дующий (первый из битов данных). Ожидание продолжается до 15 мс.
when TX_WaitInitClkDown =>
if PS2C = '0' then
TimeoutCnt <= (others => '0');
PS2_DATA <= ShiftReg(0);
ShiftReg(9 downto 0) <= ShiftReg(10 downto 1);
ShiftReg(10) <= PS2D;
BitCnt <= BitCnt + 1;
State <= TX_WaitClkUp;
elsif TimeoutCnt = Wait15ms
then
TE <= '1';
PS2_DATA <= 'Z';
State <= Idle;
else
TimeoutCnt <= TimeoutCnt + 1;
State <= TX_WaitInitClkDown;
end if;
-- Ожидание фронта PS2C после выдачи очередного бита.
when TX_WaitClkUp =>
if PS2C = '1' then
TimeoutCnt <= (others => '0');
if BitCnt = "1011"
then State <= TX_WaitAck;
else State <= TX_WaitClkDown;
end if;
elsif TimeoutCnt = Wait100us
then
TE <= '1';
PS2_DATA <= 'Z';
State <= Idle;
else
TimeoutCnt <= TimeoutCnt + 1;
State <= TX_WaitClkUp;
end if;
-- Ожидание спада PS2C и выдача очередного бита.
when TX_WaitClkDown =>
if PS2C = '0' then
PS2_DATA <= ShiftReg(0);
ShiftReg(9 downto 0) <= ShiftReg(10 downto 1);
ShiftReg(10) <= PS2D;
TimeoutCnt <= (others => '0');
BitCnt <= BitCnt + 1;
State <= TX_WaitClkUp;
elsif TimeoutCnt = Wait100us
then
TE <= '1';
PS2_DATA <= 'Z';
State <= Idle;
else
TimeoutCnt <= TimeoutCnt + 1;
State <= TX_WaitClkDown;
end if;
-- Ожидание спада PS2C и прихода бита подтверждения.
when TX_WaitAck =>
PS2_DATA <= 'Z';
if PS2C = '0' then
TimeoutCnt <= (others => '0');
SE <= PS2D;
State <= TX_Done;
elsif TimeoutCnt = Wait100us
then
TE <= '1';
State <= Idle;
else
TimeoutCnt <= TimeoutCnt + 1;
State <= TX_WaitAck;
end if;
-- Ожидание перехода PS2C и PS2D в высокий уровень после окончания пе-
-- редачи данных и приёма бита подтверждения.
when TX_Done =>
if (PS2C = '1') and (PS2D = '1')
then State <= Idle;
elsif TimeoutCnt = Wait100us
then
TE <= '1';
State <= Idle;
else
TimeoutCnt <= TimeoutCnt + 1;
State <= TX_Done;
end if;
end case;
end if;
end process;
end Behavioral;