Practice

Workshop PS2 Keyboard

Marlow Toolroom still runs its CNC console from an old PS/2 keyboard with a space bar worn smooth. Operators use it to send jog commands while setting up each job.

The keyboard sends each bit on two slow wires and supplies its own clock.

One winter morning, a damaged frame passed through the console reader without a check. The bad scan code was read as "jog left, fast."

The steel table struck its end stop, and coolant sloshed from the tray before the operator could react.

Your task: build a PS/2 receiver that checks each start bit, parity bit, and stop bit. Quietly discard broken or incomplete frames. Only a complete, correct frame may update the saved scan code.

Interface

PortDirectionTypeDescription
clk_iin1 bitConsole system clock, much faster than the keyboard clock
rst_iin1 bitSynchronous reset (active high)
ps2_clk_iin1 bitKeyboard clock line
ps2_data_iin1 bitKeyboard data line
scan_oout8-bit vectorLast good scan code, held
valid_oout1 bitOne-clock pulse for each good frame
parity_err_oout1 bitOne-clock pulse on a parity failure
frame_err_oout1 bitOne-clock pulse on a bad start, bad stop, or mid-frame timeout

Behavior

  • Each PS/2 input passes through its own two-flop synchronizer before any receiver logic examines it
  • Data is captured on each detected falling edge of the synchronized ps2_clk_i
  • Each frame contains exactly 11 captured bits
  • The first bit is the start bit, and it must be 0
  • A bad start bit makes frame_err_o pulse on the clock that detects its falling edge
  • A bad start bit is not treated as a frame, and the receiver remains idle
  • The next eight bits form the scan code, least significant bit first
  • The tenth bit is the odd-parity bit
  • The eight data bits and parity bit together must contain an odd number of ones
  • The eleventh bit is the stop bit, and it must be 1
  • For a good frame, scan_o updates one clock after the stop bit's captured edge
  • valid_o pulses on that same update clock
  • For a completed frame with bad parity, parity_err_o pulses instead, and scan_o does not change
  • A bad stop bit makes frame_err_o pulse, and scan_o does not change
  • If 128 system clocks pass mid-frame without the next captured falling edge, frame_err_o pulses
  • A timeout discards the partial frame and returns the receiver to idle
  • valid_o, parity_err_o, and frame_err_o are one-clock pulses and never overlap
  • scan_o holds its last good value through every failed or incomplete frame
  • rst_i high at a rising edge clears all outputs, clears scan_o to zero, and returns the receiver to idle
  • All outputs are registers

What the bench checks

  • The testbench drives keyboard lines with irregular but legal timing and checks every output on every clock against a model with both synchronizer chains
  • It sends good frames whose codes reveal the least significant bit first order
  • It completes a frame with bad parity and checks a parity_err_o pulse with scan_o untouched
  • It sends a bad stop bit and a bad start bit and checks the right frame_err_o pulse for each
  • It silences the keyboard mid-frame and checks one frame_err_o pulse after 128 quiet clocks, with the partial frame discarded
  • It checks that scan_o holds its last good value through every failure and updates only on valid_o
  • It resets while idle and mid-frame and checks cleared outputs and an idle receiver

Constraints

TIMING: receiver behavior is synchronous to clk_i after two-flop input synchronization. Keyboard edges arrive far slower than clk_i in this bench. All outputs are registered.

SCOPE: the two-flop chain is the required discipline for a foreign clock line. An RTL run does not prove metastability safety.

Do not add ports.

Loading editor...

Click Run to execute your code. Output will appear here.