§ Как работает протокол PS/2

Этот материал будет очень обширным, потому что сегодня я буду делать сразу по 3-м направлениям — эмуляцию для верилятора, сам код верилога и немного по физической реализации на отладочной плате. Начну с теории, о том, как передаются данные от клавиатуры к контроллеру.

Схема работы протокола. Есть 2 провода, по которым передаются данные, как от клавиатуры, так и к ней. Передачу данных к клавиатуре я делать пока не буду, а вот от клавиатуры — да.
Большую часть времени оба провода содержат сигнал 1 (притянуты к Vcc). Когда клавиатура начинает передавать данные, она опускает в 0 оба провода, что сигнализирует о начале передачи.
  • Первый такт на позитивном фронте принимает старт-бит, который всегда равен 0 и всегда должен быть равен 0, иначе это ошибка
  • Второй такт принимает бит 0 и записывает его в результирующий регистр
  • Далее, принимаются все оставшиеся биты 1-7
  • После приема битов идет бит четности, который нужен для проверки того, что данные пришли успешно. Бит четности равен 0, если количество принятых единиц было нечетно и 1, если четно
  • Самый последний такт называется "стоп"-бит, на проводе Data всегда должно быть значение 1
После приема одного байта данных, с клавиатуры ничего не должно отсылаться в течении нескольких миллисекунд.

§ Событие нажатия кнопки в эмуляторе

Теперь, давайте добавим обработчик события при нажатии на кнопку. Когда на клавиатуре нажимается кнопка, то по протоколу PS2 отсылается ее сканкод. Я постарался обработать как можно больше клавиш и записал все, что смог найти.
Этот метод встраивается в класс App.
Первым аргументом идет int scancode — он приходит из события SDL2. Второй аргумент int release равен либо 0 — что означает, что клавиша нажата, либо 1 — клавиша отпущена.
Когда нажимается клавиша, то, в зависимости от того, какой номер был у этой клавиши, отсылается некоторый код клавиши. Он может не один, есть клавиши, которые отсылают целую последовательность кодов.
К примеру, если нажать пробел, то будет отослан код 0x29. Если пробел отпустить — то будет отослал код 0xF0, а потом 0x29. В случае нажатия клавиши "Направо", будет отосланы 0xE0, 0x74, а если отпустить, то 0xE0, 0xF0, 0x74. При разработке программного обеспечения, которое это будет обрабатывать, надо это учитывать.
Можно заметить, что используется метод kbd_push, который занимается тем, что добавляет новый символ в очередь на отправку:
В очередь kbd будет добавлен новый код, и увеличена высота очереди. Максимальное количество элементов в очереди 256.
И да, надо объявить новые переменные и буферы в зоне protected класса App:
  • kbd[256] — очередь
  • kbd_top — верх очереди
  • kbd_phase и kbd_ticker — необходимы для симуляции протокола ps2
В конструкторе класса надо обязательно инициализировать переменные:
Необходимо добавить код, который будет отслеживать новые события, которые приходят от SDL2, код добавляется в метод main():
При появлении события SDL_KEYDOWN (нажата клавиша), будет вызван kbd_scancode с параметром 0, который добавил в очередь сканкод, основанный на сканкоде SDL. При отпускании клавиши, будет вызвал kbd_scancode с параметром 1.
Итак, приготовления готовы, при нажатии и отпускании клавиш добавляется в очередь, но теперь нужно выполнить обработку этих данных, симулировать отсылку их по протоколу PS2.
Задача такая. На каждый такт (25 мгц) вызывается метод, который устанавливает ps2_clock и ps2_data в нужные значения, симулируя протокол. Вот и всё.
Первоначально, ps_clock и ps_data должны быть равны 1.
Рассмотрим то, как будет работать метод kbd_pop, который работает на каждом такте (25 Мгц):
Как видно из кода, если в очереди нет больше данных, то есть kbd_top == 0, то ничего не делается. Если данные есть, то спустя 2000 тактов (работает клавиатура не быстро), будет установлен ps_clock = 0 или 1. Сначала ставится 0, потом после kbd_phase++ становится 1, и так далее. Фаза kbd_phase необходима, чтобы точно знать, в какой момент и что выдавать в ps_data. Таймер kbd_ticker сбрасывается в 0, чтобы через 2000 тактов снова сделать полутакт.
Вот здесь представлен код основной части симулятора протокола.
  • Для полутактов 0 и 1 на шину ps_data выставляется 0 — старт-бит
  • Далее идут пары полутактов от 2 до 17 включительно (8 бит). На шину данных выставляется последовательно биты от 0 до 7 (начиная с 0) из первого символа в буфере kbd, очередь работает в режиме FIFO
  • Полутакты 18,19 выставляют бит четности, который равен 1, если количество единиц в отправленном символе было четно, и 0, если нет
  • В 20,21 записывается стоп-бит, ps_data=1, это последний такт, после него ps_data=1, ps_clock=1
  • В течении 22-26 полутакта это состояние продлевается
  • На 26 полутакте из буфера kbd удаляется первый символ и очередь уменьшается на -1. В данном случае ставится kbd_phase=-1, потому что в конце метода идет инкремент kbd_phase, и он снова становится равным 0.
Собственно, на этом вся логика работы данного кода и заканчивается. Симулятор протокола PS/2 готов. Теперь остается сделать обработчик данных на верилоге, встроить в верилятор и начать работу.
Но этот материал уже в следующей статье.
Скачать код можно здесь.