// 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 clk14, // 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 clk14 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; // debounce ps2clk_debounce // ( // .clk14(clk14), // .rst(rst), // .sig_in(key_clk), // .sig_out(ps2_clkdb) // ); always @(posedge clk14 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) && (key_clk == 1'b0))//(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 <= key_clk;//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 clk14 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 <= "/"; default: // unsupported key! begin ascii_rdy <= 1'b0; // shift is not a key! ascii <= " "; end 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: // unsupported key! begin ascii_rdy <= 1'b0; // shift is not a key! ascii <= " "; end 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