verilog-apple-one/rtl/ps2keyboard/ps2keyboard.v

334 lines
15 KiB
Verilog

// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
// Description: PS/2 keyboard interface
//
// Author.....: Niels A. Moseley
// Date.......: 28-1-2018
//
module ps2keyboard (
input clk25, // 25MHz clock
input rst, // active high reset
// I/O interface to keyboard
input key_clk, // clock input from keyboard / device
input key_din, // data input from keyboard / device
// I/O interface to computer
input cs, // chip select, active high
input address, // =0 RX buffer, =1 RX status
output reg [7:0] dout // 8-bit output bus.
);
reg [3:0] rxcnt; // count how many bits have been shift into rxshiftbuf
reg [10:0] rxshiftbuf; // 11 bit shift receive register
reg rx_flag = 0; // this flag is 1 when
// valid data is available in rxshiftbuf
reg [7:0] rx; // scancode receive buffer
wire ps2_clkdb; // debounced PS/2 clock signal
reg prev_ps2_clkdb; // previous clock state (in clk25 domain)
// keyboard translation signals
reg [7:0] ascii; // ASCII code of received character
reg ascii_rdy; // new ASCII character received
reg shift; // state of the shift key
reg [2:0] cur_state;
reg [2:0] next_state;
reg [15:0] debounce_timer;
debounce ps2clk_debounce
(
.clk25(clk25),
.rst(rst),
.sig_in(key_clk),
.sig_out(ps2_clkdb)
);
always @(posedge clk25 or posedge rst)
begin
if (rst)
begin
prev_ps2_clkdb <= 1'b0;
rx_flag <= 1'b0;
end
else
begin
rx_flag <= 1'b0; // reset the new data flag register
// check for negative edge of PS/2 clock
// and sample the state of the PS/2 data line
if ((prev_ps2_clkdb == 1'b1) && (ps2_clkdb == 1'b0))
begin
rxshiftbuf <= {key_din, rxshiftbuf[10:1]};
rxcnt <= rxcnt + 4'b1;
if (rxcnt == 4'd10)
begin
// 10 bits have been shifted in
// we should have a complete
// scan code here, including
// start, parity and stop bits.
rxcnt <= 0;
// signal new data is present
// note: this signal will only remain high for one
// clock cycle!
//
// TODO: check parity here?
rx_flag <= 1'b1;
end
end
// update previous clock state
prev_ps2_clkdb <= ps2_clkdb;
end
end
//
// IBM Keyboard code page translation
// state machine for US keyboard layout
//
// http://www.computer-engineering.org/ps2keyboard/scancodes2.html
//
localparam S_KEYNORMAL = 3'b000;
localparam S_KEYF0 = 3'b001; // regular key release state
localparam S_KEYE0 = 3'b010; // extended key state
localparam S_KEYE0F0 = 3'b011; // extended release state
always @(posedge clk25 or posedge rst)
begin
if (rst)
begin
rx <= 0;
ascii_rdy <= 0;
shift <= 0;
cur_state <= S_KEYNORMAL;
end
else
begin
// handle I/O from CPU
if (cs == 1'b1)
begin
if (address == 1'b0)
begin
// RX buffer address
dout <= {1'b1, ascii[6:0]};
ascii_rdy <= 1'b0;
end
else
begin
// RX status register
dout <= {ascii_rdy, 7'b0};
end
end
// keyboard translation state machine
if (rx_flag == 1'b1)
begin
// latch data from the serial buffer into
// the rx scancode buffer.
rx <= rxshiftbuf[8:1];
case(cur_state)
S_KEYNORMAL:
begin
if (rx == 8'hF0)
next_state = S_KEYF0;
else if (rx == 8'hE0)
next_state = S_KEYE0;
else
begin
// check the debounce timer, if this is
// not zero, a new key arrived too quickly
// and we simply discard it. For better
// debouncing, we should check if the key
// is actually the same as the previous received/
// key, but let's try this first to see if it works
// ok...
//if (debounce_timer == 16'd0)
//begin
ascii_rdy <= 1'b1; // new key has arrived!
//debounce_timer <= 16'hFFFF; // reset the debounce timer
//end
// check for a SHIFT key
if ((rx == 8'h59) || (rx == 8'h12))
begin
shift <= 1'b1;
ascii_rdy <= 1'b0; // shift is not a key!
end
else begin
if (!shift)
case(rx)
8'h1C: ascii <= "A";
8'h32: ascii <= "B";
8'h21: ascii <= "C";
8'h23: ascii <= "D";
8'h24: ascii <= "E";
8'h2B: ascii <= "F";
8'h34: ascii <= "G";
8'h33: ascii <= "H";
8'h43: ascii <= "I";
8'h3B: ascii <= "J";
8'h42: ascii <= "K";
8'h4B: ascii <= "L";
8'h3A: ascii <= "M";
8'h31: ascii <= "N";
8'h44: ascii <= "O";
8'h4D: ascii <= "P";
8'h15: ascii <= "Q";
8'h2D: ascii <= "R";
8'h1B: ascii <= "S";
8'h2C: ascii <= "T";
8'h3C: ascii <= "U";
8'h2A: ascii <= "V";
8'h1D: ascii <= "W";
8'h22: ascii <= "X";
8'h35: ascii <= "Y";
8'h1A: ascii <= "Z";
8'h45: ascii <= "0";
8'h16: ascii <= "1";
8'h1E: ascii <= "2";
8'h26: ascii <= "3";
8'h25: ascii <= "4";
8'h2E: ascii <= "5";
8'h36: ascii <= "6";
8'h3D: ascii <= "7";
8'h3E: ascii <= "8";
8'h46: ascii <= "9";
8'h4E: ascii <= "-";
8'h55: ascii <= "=";
8'h5D: ascii <= 8'h34; // backslash
8'h66: ascii <= 8'd8; // backspace
8'h29: ascii <= " ";
8'h5A: ascii <= 8'd13; // enter
8'h54: ascii <= "[";
8'h5B: ascii <= "]";
8'h4C: ascii <= ";";
8'h52: ascii <= "'";
8'h41: ascii <= ",";
8'h49: ascii <= ".";
8'h4A: ascii <= "/";
8'h59: shift <= 1'b1; // right shfit
8'h12: shift <= 1'b1; // left shift
default: ascii <= ".";
endcase
else
// Here, we're in a shifted state
case(rx)
8'h1C: ascii <= "A";
8'h32: ascii <= "B";
8'h21: ascii <= "C";
8'h23: ascii <= "D";
8'h24: ascii <= "E";
8'h2B: ascii <= "F";
8'h34: ascii <= "G";
8'h33: ascii <= "H";
8'h43: ascii <= "I";
8'h3B: ascii <= "J";
8'h42: ascii <= "K";
8'h4B: ascii <= "L";
8'h3A: ascii <= "M";
8'h31: ascii <= "N";
8'h44: ascii <= "O";
8'h4D: ascii <= "P";
8'h15: ascii <= "Q";
8'h2D: ascii <= "R";
8'h1B: ascii <= "S";
8'h2C: ascii <= "T";
8'h3C: ascii <= "U";
8'h2A: ascii <= "V";
8'h1D: ascii <= "W";
8'h22: ascii <= "X";
8'h35: ascii <= "Y";
8'h1A: ascii <= "Z";
8'h45: ascii <= ")";
8'h16: ascii <= "!";
8'h1E: ascii <= "@";
8'h26: ascii <= "#";
8'h25: ascii <= "$";
8'h2E: ascii <= "%";
8'h36: ascii <= "^";
8'h3D: ascii <= "&";
8'h3E: ascii <= "*";
8'h46: ascii <= "(";
8'h4E: ascii <= "_";
8'h55: ascii <= "+";
8'h5D: ascii <= "|";
8'h66: ascii <= 8'd8; // backspace
8'h29: ascii <= " ";
8'h5A: ascii <= 8'd13; // enter
8'h54: ascii <= "{";
8'h5B: ascii <= "}";
8'h4C: ascii <= ":";
8'h52: ascii <= "\"";
8'h41: ascii <= "<";
8'h49: ascii <= ">";
8'h4A: ascii <= "?";
default: ascii <= ".";
endcase
end
end
end
S_KEYF0:
// when we end up here, a 0xF0 byte was received
// which usually means a key release event
begin
if ((rx == 8'h59) || (rx == 8'h12))
shift <= 1'b0;
next_state = S_KEYNORMAL;
end
S_KEYE0:
begin
if (rx == 8'hF0)
next_state = S_KEYE0F0;
else
next_state = S_KEYNORMAL;
end
S_KEYE0F0:
begin
next_state = S_KEYNORMAL;
end
default:
begin
next_state = S_KEYNORMAL;
end
endcase;
end
else
begin
next_state = cur_state; // deliberate blocking assingment!
end
cur_state <= next_state;
end
end
endmodule