mirror of
https://github.com/livingcomputermuseum/cpus-pdp8.git
synced 2026-01-13 15:37:04 +00:00
161 lines
5.2 KiB
Verilog
161 lines
5.2 KiB
Verilog
// ps2.v
|
|
//
|
|
// Monitor the serial datastream and clock from a PS/2 keyboard
|
|
// and output a scancode for any key that is pressed.
|
|
//
|
|
// Notes:
|
|
//
|
|
// The clock from the PS/2 keyboard is used directly. It is sampled at
|
|
// the frequency of the main clock input; edges are extracted from the
|
|
// sample clock. The main clock must be substantially faster than
|
|
// the 10 KHz PS/2 clock - 200 KHz or more.
|
|
//
|
|
// The scancode is only valid when the ready signal is high. The scancode
|
|
// should be registered by an external circuit on the first clock edge
|
|
// after the ready signal goes high.
|
|
//
|
|
// The ready signal pulses only after the key is released.
|
|
//
|
|
// The error flag is set whenever the PS/2 clock stops pulsing and the
|
|
// PS/2 clock is either at a low level or less than 11 bits of serial
|
|
// data have been received (start + 8 data + parity + stop).
|
|
//
|
|
|
|
module ps2(clk, // main clock
|
|
rst_n, // asynchronous reset
|
|
ps2_clk, // clock from keyboard
|
|
ps2_data, // data from keyboard
|
|
scancode, // key scancode
|
|
parity, // parity bit for scancode
|
|
busy, // busy receiving scancode
|
|
rdy, // scancode ready pulse
|
|
error // error receiving scancode
|
|
);
|
|
|
|
input clk, rst_n;
|
|
input ps2_clk;
|
|
input ps2_data;
|
|
output [7:0] scancode;
|
|
output parity;
|
|
output busy;
|
|
output rdy;
|
|
output error;
|
|
|
|
|
|
parameter FREQ = 25000; // frequency of the main clock (KHz)
|
|
parameter PS2_FREQ = 10; // keyboard clock frequency (KHz)
|
|
parameter TIMEOUT = FREQ / PS2_FREQ; // ps2_clk quiet timeout
|
|
parameter [7:0] KEY_RELEASE = 8'b11110000; // sent when key is rel'd
|
|
|
|
reg [13:0] timer_r; // time since last PS/2 clock edge
|
|
wire [13:0] timer_x;
|
|
|
|
reg [3:0] bitcnt_r; // number of received scancode bits
|
|
wire [3:0] bitcnt_x;
|
|
|
|
reg [4:0] ps2_clk_r; // PS/2 clock sync / edge detect
|
|
wire [4:0] ps2_clk_x;
|
|
|
|
reg [9:0] sc_r; // scancode shift register
|
|
wire [9:0] sc_x;
|
|
|
|
reg keyrel_r; // set when key release received
|
|
wire keyrel_x;
|
|
|
|
reg rdy_r; // set when scancode ready
|
|
wire rdy_x;
|
|
|
|
reg error_r; // set when an error occurs
|
|
wire error_x;
|
|
|
|
wire ps2_clk_fall_edge; // on falling edge of PS/2 clock
|
|
wire ps2_clk_rise_edge; // on rising edge of PS/2 clock
|
|
wire ps2_clk_edge; // on either edge of PS/2 clock
|
|
wire ps2_clk_quiet; // when no edges on PS/2 clock for TIMEOUT
|
|
wire scancode_rdy; // when scancode has been received
|
|
|
|
|
|
// shift the level on the PS/2 clock into a shift register
|
|
assign ps2_clk_x = {ps2_clk_r[3:0], ps2_clk};
|
|
|
|
// look at the PS/2 clock levels stored in the shift register
|
|
// and find rising or falling edges
|
|
assign ps2_clk_fall_edge = ps2_clk_r[4:1] == 4'b1100;
|
|
assign ps2_clk_rise_edge = ps2_clk_r[4:1] == 4'b0011;
|
|
assign ps2_clk_edge = ps2_clk_fall_edge || ps2_clk_rise_edge;
|
|
|
|
// shift the keyboard scancode into the shift register on the
|
|
// falling edge of the PS/2 clock
|
|
assign sc_x = ps2_clk_fall_edge ? {ps2_data, sc_r[9:1]} : sc_r;
|
|
|
|
// clear the timer right after a PS/2 clock edge and
|
|
// then keep incrementing it until the next edge
|
|
assign timer_x = ps2_clk_edge ? 0 : (timer_r + 1);
|
|
|
|
// indicate when the PS/2 clock has stopped pulsing and
|
|
// is at a high level.
|
|
assign ps2_clk_quiet = timer_r == TIMEOUT && ps2_clk_r[1];
|
|
|
|
// increment bit counter on each falling edge of the PS/2 clock.
|
|
// reset the bit counter if the PS/2 clock stops pulsing or
|
|
// if there was an error receiving the scancode.
|
|
// otherwise, keep the bit counter unchanged.
|
|
assign bitcnt_x = ps2_clk_fall_edge ? (bitcnt_r + 1) :
|
|
(ps2_clk_quiet || error_r) ? 0 :
|
|
bitcnt_r;
|
|
|
|
// a scancode has been received if the bit counter is 11 and
|
|
// the PS/2 clock has stopped pulsing
|
|
assign scancode_rdy = bitcnt_r == 4'd11 && ps2_clk_quiet;
|
|
|
|
/*
|
|
// look for the scancode sent when the key is released
|
|
assign keyrel_x = (sc_r[7:0] == KEY_RELEASE && scancode_rdy) ? 1 :
|
|
(rdy_r || error_r) ? 0 :
|
|
keyrel_r;
|
|
|
|
// the scancode for the pressed key arrives after receiving
|
|
// the key-release scancode
|
|
assign rdy_x = keyrel_r && scancode_rdy;
|
|
*/
|
|
assign rdy_x = scancode_rdy;
|
|
|
|
// indicate an error if the clock is low for too long or
|
|
// if it stops pulsing in the middle of a scancode
|
|
assign error_x = (timer_r == TIMEOUT && ps2_clk_r[1] == 0) ||
|
|
(ps2_clk_quiet && bitcnt_r != 4'd11 && bitcnt_r != 4'd0) ?
|
|
1 : error_r;
|
|
|
|
// outputs
|
|
assign scancode = sc_r[7:0]; // scancode
|
|
assign parity = sc_r[8]; // parity bit for the scancode
|
|
assign busy = bitcnt_r != 4'd0; // busy when recv'ing scancode
|
|
assign rdy = rdy_r; // scancode ready flag
|
|
assign error = error_r; // error flag
|
|
|
|
// update the various registers
|
|
always @(posedge clk or negedge rst_n)
|
|
if (~rst_n)
|
|
begin
|
|
ps2_clk_r <= 5'b11111; // assume PS/2 clock has been high
|
|
sc_r <= 0;
|
|
keyrel_r <= 0;
|
|
rdy_r <= 0;
|
|
timer_r <= 0;
|
|
bitcnt_r <= 0;
|
|
error_r <= 0;
|
|
end
|
|
else
|
|
begin
|
|
ps2_clk_r <= ps2_clk_x;
|
|
sc_r <= sc_x;
|
|
keyrel_r <= keyrel_x;
|
|
rdy_r <= rdy_x;
|
|
timer_r <= timer_x;
|
|
bitcnt_r <= bitcnt_x;
|
|
error_r <= error_x;
|
|
end
|
|
|
|
|
|
endmodule
|