So I've been learning as I'm going, developing the
FPGA GPU with BrianHG, but my Verilog-fu is failing me now.
I've found a PS2 keyboard interface somewhere online (yes, I know that's a terrible way to start a sentence) and have used it to try and provide some form of keyboard input via the FPGA to my DIY computer.
The problem I'm having is that I'm getting duplicated characters - if I press A, I get AA appear on the screen. This seems to be because the keyboard.sv module is ignoring the BREAK keycode but not the following keycode for the key that is released. I can hold a key down and just get one character on the screen (until the repeat timer kicks in, obviously).
Sometimes I get three characters appear for one keypress, too, which is causing me real stress, messing around with latches and debouncers, so I thought I'd focus on this one main problem first.
Here's the code for the main keyboard.sv module - it instantiates a ps2_rx.sv which is included below:
module keyboard (
input wire clk, // FPGA-based clock (50 MHz)
input wire reset,
input wire ps2d, ps2c, // PS2 data and clock lines
output wire [7:0] scan_code, // scan_code received from keyboard to process
output wire scan_code_ready, // signal to outer control system to sample scan_code
output wire letter_case_out // output to determine if scan code is converted to lower or upper ascii code for a key
);
parameter TIMEOUT = 500000;
parameter RXDELAY = 500000;
// constant declarations
localparam BREAK = 8'hF0, // break code
SHIFT1 = 8'h12, // first shift scan
SHIFT2 = 8'h59, // second shift scan
CAPS = 8'h58; // caps lock
// FSM symbolic states
localparam [2:0] lowercase = 3'b000, // idle, process lower case letters
ignore_break = 3'b001, // ignore repeated scan code after break code -F0- received
shift = 3'b010, // process uppercase letters for shift key held
ignore_shift_break = 3'b011, // check scan code after F0, either idle or go back to uppercase
capslock = 3'b100, // process uppercase letter after capslock button pressed
ignore_caps_break = 3'b101; // check scan code after F0, either ignore repeat, or decrement caps_num
// internal signal declarations
reg [2:0] state_reg, state_next; // FSM state register and next state logic
wire [7:0] scan_out; // scan code received from keyboard
reg got_code_tick; // asserted to write current scan code received to FIFO
wire scan_done_tick; // asserted to signal that ps2_rx has received a scan code
reg letter_case; // 0 for lower case, 1 for uppercase, outputed to use when converting scan code to ascii
reg [7:0] shift_type_reg, shift_type_next; // register to hold scan code for either of the shift keys or caps lock
reg [1:0] caps_num_reg, caps_num_next; // keeps track of number of capslock scan codes received in capslock state (3 before going back to lowecase state)
// ************** instantiate PS2 receiver **************
ps2_rx ps2_rx_unit (
.clk(clk),
.rst(reset),
.ps2d(ps2d),
.ps2c(ps2c),
.rx_done_tick(scan_done_tick),
.rx_data(scan_out)
);
defparam ps2_rx_unit.nDelay = TIMEOUT,
ps2_rx_unit.rxDelay = RXDELAY;
// ********************************************************
// FSM stat, shift_type, caps_num register
always @(posedge clk, posedge reset) begin
if (reset) begin
state_reg <= lowercase;
shift_type_reg <= 0;
caps_num_reg <= 0;
end
else begin
state_reg <= state_next;
shift_type_reg <= shift_type_next;
caps_num_reg <= caps_num_next;
end
end
//FSM next state logic
always @* begin
// defaults
got_code_tick = 1'b0;
letter_case = 1'b0;
caps_num_next = caps_num_reg;
shift_type_next = shift_type_reg;
state_next = state_reg;
case(state_reg)
// state to process lowercase key strokes, go to uppercase state to process shift/capslock
lowercase: begin
if(scan_done_tick) begin // if scan code received
if(scan_out == SHIFT1 || scan_out == SHIFT2) begin // if code is shift
shift_type_next = scan_out; // record which shift key was pressed
state_next = shift; // go to shift state
end
else if(scan_out == CAPS) begin // if code is capslock
caps_num_next = 2'b11; // set caps_num to 3, num of capslock scan codes to receive before going back to lowecase
state_next = capslock; // go to capslock state
end
end
else if (scan_out == BREAK) begin // else if code is break code
state_next = ignore_break; // go to ignore_break state
end
else begin // else if code is none of the above...
got_code_tick = 1'b1; // assert got_code_tick to write scan_out to FIFO
end
end
// state to ignore repeated scan code after break code FO received in lowercase state
ignore_break: begin
if(scan_done_tick) begin // if scan code received,
state_next = lowercase; // go back to lowercase state
end
end
// state to process scan codes after shift received in lowercase state
shift: begin
letter_case = 1'b1; // routed out to convert scan code to upper value for a key
if(scan_done_tick) begin // if scan code received,
if(scan_out == BREAK) begin // if code is break code
state_next = ignore_shift_break; // go to ignore_shift_break state to ignore repeated scan code after F0
end
else if(scan_out != SHIFT1 && scan_out != SHIFT2 && scan_out != CAPS) begin // else if code is not shift/capslock
got_code_tick = 1'b1; // assert got_code_tick to write scan_out to FIFO
end
end
end
// state to ignore repeated scan code after break code F0 received in shift state
ignore_shift_break: begin
if(scan_done_tick) begin // if scan code received
if(scan_out == shift_type_reg) begin // if scan code is shift key initially pressed
state_next = lowercase; // shift/capslock key unpressed, go back to lowercase state
end
else begin // else repeated scan code received, go back to uppercase state
state_next = shift;
end
end
end
// state to process scan codes after capslock code received in lowercase state
capslock: begin
letter_case = 1'b1; // routed out to convert scan code to upper value for a key
if(caps_num_reg == 0) begin // if capslock code received 3 times,
state_next = lowercase; // go back to lowecase state
end
if(scan_done_tick) begin // if scan code received
if(scan_out == CAPS) begin // if code is capslock,
caps_num_next = caps_num_reg - 1; // decrement caps_num
end
else if(scan_out == BREAK) begin // else if code is break, go to ignore_caps_break state
state_next = ignore_caps_break;
end
else if(scan_out != SHIFT1 && scan_out != SHIFT2) begin // else if code isn't a shift key
got_code_tick = 1'b1; // assert got_code_tick to write scan_out to FIFO
end
end
end
// state to ignore repeated scan code after break code F0 received in capslock state
ignore_caps_break: begin
if(scan_done_tick) begin // if scan code received
if(scan_out == CAPS) begin // if code is capslock
caps_num_next = caps_num_reg - 1; // decrement caps_num
end
state_next = capslock; // return to capslock state
end
end
endcase
end
assign letter_case_out = letter_case; // output, route letter_case to output to use during scan to ascii code conversion
assign scan_code_ready = got_code_tick; // output, route got_code_tick to out control circuit to signal when to sample scan_out
assign scan_code = scan_out; // route scan code data out
endmodule
And here's ps2_rx.sv:
module ps2_rx (
input wire clk,
input wire rst,
input wire ps2d, ps2c, // ps2 data and clock inputs, receive enable input
output reg rx_done_tick, // ps2 receive done tick
output wire [7:0] rx_data // data received
);
parameter nDelay = 500000; // 10 millisecond Rx timeout
reg [19:0] timeout_counter;
reg timeout = 1'b0;
parameter rxDelay = 500000; // 10 millisecond delay before next char can be rx'd
reg [19:0] rx_counter;
// FSMD state declaration
localparam
idle = 1'b0,
rx = 1'b1;
// internal signal declaration
reg state_reg, state_next; // FSMD state register
reg [7:0] filter_reg; // shift register filter for ps2c
wire [7:0] filter_next; // next state value of ps2c filter register
reg f_val_reg; // reg for ps2c filter value, either 1 or 0
wire f_val_next; // next state for ps2c filter value
reg [3:0] n_reg, n_next; // register to keep track of bit number
reg [10:0] d_reg, d_next; // register to shift in rx data
wire neg_edge; // negative edge of ps2c clock filter value
wire reset = rst || timeout;
// register for ps2c filter register and filter value
always @(posedge clk, posedge reset) begin
if (reset) begin
filter_reg <= 0;
f_val_reg <= 0;
timeout <= 0;
timeout_counter <= 20'b0;
end
else begin
filter_reg <= filter_next;
f_val_reg <= f_val_next;
if (neg_edge) begin
timeout_counter<= 20'b0; // reset timeout counter
end
else begin
timeout_counter<= timeout_counter + 1;
end
if (timeout_counter == nDelay - 1) begin
timeout <= 1'b1;
end
end
end
// next state value of ps2c filter: right shift in current ps2c value to register
assign filter_next = {ps2c, filter_reg[7:1]};
// filter value next state, 1 if all bits are 1, 0 if all bits are 0, else no change
assign f_val_next = (filter_reg == 8'b11111111) ? 1'b1 : (filter_reg == 8'b00000000) ? 1'b0 : f_val_reg;
// negative edge of filter value: if current value is 1, and next state value is 0
assign neg_edge = f_val_reg & ~f_val_next;
// assign data to the output
assign rx_data = d_reg[8:1]; // output data bits
// FSMD state, bit number and data registers
always @(posedge clk, posedge reset) begin
if (reset) begin
state_reg <= idle;
n_reg <= 0;
d_reg <= 0;
end
else begin
state_reg <= state_next;
n_reg <= n_next;
d_reg <= d_next;
end
end
// Rx delay counter
always @(posedge clk) begin
if (rx_counter > 0)
rx_counter <= rx_counter - 1'b1;
if (rx_done_tick == 1'b1 && rx_counter < 1'b1)
rx_counter <= rxDelay;
end
// FSMD next state logic
always @* begin
// defaults
state_next = state_reg;
rx_done_tick = 1'b0;
n_next = n_reg;
d_next = d_reg;
case (state_reg)
idle:
begin
if (neg_edge) begin // start bit received
n_next = 4'b1010; // set bit count down to 10
state_next = rx; // go to rx state
end
end
rx: // shift in 8 data, 1 parity, and 1 stop bit
begin
if (neg_edge) begin // if ps2c negative edge...
d_next = {ps2d, d_reg[10:1]};// sample ps2d, right shift into data register
n_next = n_reg - 1; // decrement bit count
end
if (n_reg == 0) begin // after 10 bits shifted in, go to done state
//if (rx_counter == 0)
rx_done_tick = 1'b1; // assert dat received done tick
state_next = idle; // go back to idle
end
end
endcase
end
endmodule
I know this is probably a big ask, but if anyone can spot any glaring errors in the code that may be causing the state machine to mess up on the BREAK keycodes, I'd be pleased to hear from you!
All these modules are running on the FPGA's 50 MHz clock, including the debouncers on PS2_DATA and PS2_CLK inputs.