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
| Port | Direction | Type | Description |
|---|---|---|---|
clk | in | 1 bit | Console system clock, much faster than the keyboard clock |
rst | in | 1 bit | Synchronous reset (active high) |
ps2_clk | in | 1 bit | Keyboard clock line |
ps2_data | in | 1 bit | Keyboard data line |
scan | out | 8-bit vector | Last good scan code, held |
valid | out | 1 bit | One-clock pulse for each good frame |
parity_err | out | 1 bit | One-clock pulse on a parity failure |
frame_err | out | 1 bit | One-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 - 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_errpulse 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,
scanupdates one clock after the stop bit's captured edge validpulses on that same update clock- For a completed frame with bad parity,
parity_errpulses instead, andscandoes not change - A bad stop bit makes
frame_errpulse, andscandoes not change - If 128 system clocks pass mid-frame without the next captured falling edge,
frame_errpulses - A timeout discards the partial frame and returns the receiver to idle
valid,parity_err, andframe_errare one-clock pulses and never overlapscanholds its last good value through every failed or incomplete framersthigh at a rising edge clears all outputs, clearsscanto 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_errpulse withscanuntouched - It sends a bad stop bit and a bad start bit and checks the right
frame_errpulse for each - It silences the keyboard mid-frame and checks one
frame_errpulse after 128 quiet clocks, with the partial frame discarded - It checks that
scanholds its last good value through every failure and updates only onvalid - It resets while idle and mid-frame and checks cleared outputs and an idle receiver
Constraints
receiver behavior is synchronous to clk after two-flop input synchronization. Keyboard edges arrive far slower than clk in this bench. All outputs are registered.
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.
Click Run to execute your code. Output will appear here.