//////////////////////////////////////////////////////////////////////////////
//
// kbscan.v - simple PS/2-port byte scancode reader
//
// --------------------------------------------------------------------------
//
// This module is a byte-level scancode reader for the PS/2-Keyboard device.
// Currently the reader allows only unidirectional communication
// (device->host); output is not supported yet.
//
// Module inputs are the kbclk/kbdata outputs from a PS/2 device.
// Outputs are a byte-scancode and its parity-check status. The module has
// two main components, the input-scanner and the output byte-buffer.
//
// The input-scanner contains a 4-bit counter, 10-bit shift-register, and
// a state-machine to generate control signals. The output byte-buffer
// is a register storing the last received scancode. The buffer also
// stores its current state (empty or full), and the parity-status of the
// last received scancode.
// The module can store up to 1 byte-scancode for a client, while
// simultaneously receiving the next-scancode. Logically, this makes
// the reader a 2-stage pipeline.
//
// Inputs : i_kbclk, i_kbdata (signal lines from the keyboard-device)
// clk, rstn (synchronous logic signals for the flipflops, latch)
//
// Outputs: o_byte (one byte scancode, valid when o_byte_rdy==1)
// o_byte_rdy (ready line, 1=o_byte contains valid scancode)
// o_byte_err (parity error status, 1=error)
//
// * both o_byte_rdy and o_byte_err are cleared 1T after i_byte_ack
//
// Known limitations :
// -------------------
// No transmit capability (from client to PS/2 device)
// adherence to PS/2 electrical protocol may not be 100%
//
//
// Want to know more about the Keyboard protocol?
// http://panda.cs.ndsu.nodak.edu/~achapwes/PICmicro/keyboard/atkeyboard.html
// http://www.repairfaq.org/filipg/LINK/PORTS/F_Keyboard_FAQ.html
//
// ... or do a google.com search on '8042 keyboard'
//
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//
// Output byte-client timing
// -------------------------
// kbscan uses a very simple rdy/ack protocol. When kbscan has received
// a scancode, it raises the rdy flag. kbscan forces kbclk LOW, to
// inhibit the PS/2-device from transmitting new scancodes. After the
// byte-client reads the data, it raises the ack flag for 1T.
// When kbscan receives ack, it internally resets, releases kbclk (to allow
// incoming scancodes), and waits for the next scancode.
// _ _ _ _ _ _ _ _ _ _ _ _
// clk |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_|
// ___
// i_byte_ack ____________________| |___________
// ___________________
// o_byte_rdy ____| |___________
// ___________________
// o_byte_err ____|:ERROR:STATUS::::::|___________
// ___________________
// o_byte XXXX< new_data ________>XXXXXXXXXXX
//
//////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////// -----
//
// liaor@iname.com - http://members.tripod.com/~liaor (05/01/2001)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., http://www.gnu.org
//
/////////////////////////////////////////////////////////////////////////////
module kbscan(
// inputs
clk, rstn,
i_kbclk, i_kbdata, i_byte_ack,
// outputs
o_byte, o_byte_rdy, o_byte_err, o_kbclk_en
);
//parameter SM_0 = 0;
parameter SM_1 = 0;
parameter SM_2 = 1;
parameter S_W = 10; // shift register width (DO NOT CHANGE)
parameter BYTE_ERROR = 8'd0; // error-code to flag parity-error
parameter KBCNT = 11; // 11 clocks
input clk; // system clock
input rstn; // async global reset (active low)
input i_kbclk; // Keybaord clock line
input i_kbdata; // Keyboard data line
input i_byte_ack; // byte acknolwedge
output o_byte_rdy; // Byte data ready (cleared on i_byte_ack)
output o_byte_err; // Byte parity error (1=error, cleared on i_byte_ack)
output [7:0] o_byte; // Byte-scancode (valid on o_byte_rdy)
output o_kbclk_en; // enable-input on kbclk (1=allow in, 0=force low)
// reg'd versions of inputs, for synchronization to local master clock (clk)
reg r_kbclk; // reg'd (synchronized) kbclk
reg r_kbdata; // reg'd (synchronized) kbdata
reg r_kbclk_1d; // 1 clk delayed version of r_kbclk
reg kbclk_stb; // pulse on negedge of r_kbclk
// Component (1) : input-scanner
reg [(S_W-1):0] s_reg; // input (kbdata) shift register
reg s_reg_err; // final parity result on s_reg (1=error)
reg s_reg_parity; // s_reg's running parity
reg fsm_ps, fsm_ns; // state-machine
reg fsm_lock_kbclkn, fsm_lock_kbclkn_ns; // control, 'lock kbclk'
reg [3:0] kbclk_ctr;
reg kbclk_ctr_eqcnt;
wire kbclk_ctr_eqcnt_ns;
wire kbclk_ctr_reset;
// Component (2) : output buffer logic
reg [7:0] o_byte; // byte data register (last received scancode)
reg o_byte_rdy; // byte-status, (1=contain valid data, 0=empty)
reg o_byte_err; // o_byte parity error status (1=error, 0=ok)
wire o_byte_load; // control, load s_reg into o_byte
//////////////////////////////////////////////////////////////////////////////
//
// module inputs
//
// In the top-level Xilinx schematic, the signals are brought in and
// registered in the IO PADs. Why do we need the additional register in
// this module? The second register on the input signals (kbclk, kbdata)
// addresses metastability.
always @ (posedge clk or negedge rstn )
begin
if ( !rstn )
begin
kbclk_stb<= 1'b0;
r_kbclk_1d <= 1'b0;
r_kbclk <= 1'b0;
r_kbdata <= 1'b0;
end
else
begin
kbclk_stb<= (!r_kbclk) && (r_kbclk_1d); // negedge of r_kbclk
r_kbclk_1d <= r_kbclk;
r_kbclk <= i_kbclk;
r_kbdata <= i_kbdata;
end
end
//////////////////////////////////////////////////////////////////////////////
//
// Input-scanner : Finite State Machine Controller
//
// This state machine performs the following actions in order:
// 1) Countdown 1 complete scancode byte. (Sit here until we capture
// 1 incoming byte scancode from the keyboard-device.)
//
// 2) assert o_kbclk_en, which controls a tristate-driver on kbclk.
// (the driver forces kbclk to 1'b0. This tells keyboard-device to
// wait.)
//
// 3) Check the output-buffer status (o_byte_rdy.)
// Wait until (o_byte_rdy==0), indicating the output-buffer is free.
//
// 4) Transfer the captured byte-scancode to o_byte. Assert o_byte_rdy
// ('byte ready') to indicate valid data in o_byte. Then
// shut off the tristate-driver on kbclk (o_kbclk_en.) Return to 1.
//
// Notice that this state machine does NOT wait for i_byte_ack...
// Instead of checking i_byte_ack, the state-machine checks o_byte_rdy.
// If (o_byte_rdy==1), the output-buffer o_byte is full (has data), so
// it can't accept new data.
// If (o_byte_rdy==0), the output-buffer o_byte is empty (no data), so
// it can accept new data.
// generate next_state equations : COMBINATIONAL LOGIC ONLY
always @( fsm_ps or kbclk_ctr_reset or o_byte_load )
begin
fsm_ns = fsm_ps;
fsm_lock_kbclkn_ns = 1'b1; // 1=don't lock kbclk, 0=lock kbclk
case (fsm_ps)
SM_1 : // READ SCANCODE
begin
fsm_lock_kbclkn_ns = 1'b1; // don't lock kbclk
if ( kbclk_ctr_reset ) // kbclk_ctr_reset also clears the latch -> 0
begin
fsm_ns = SM_2; // after 1 complete scancode (11bits), go to SM_2
fsm_lock_kbclkn_ns = 1'b0; // force lock on keyboard
end
end
SM_2 : // BYTE (scancode) ready for output-buffer (o_byte)
begin
fsm_lock_kbclkn_ns = 1'b0; // force lock on keyboard
if ( o_byte_load ) // wait for previous scancode to leave o_byte
begin
fsm_ns = SM_1;
fsm_lock_kbclkn_ns = 1'b1; // don't lock kbclk
end
end
default :
fsm_ns = SM_1;
endcase
end
// the state-registers
always @ (posedge clk or negedge rstn )
begin
if ( !rstn )
begin
fsm_ps <= SM_1;
fsm_lock_kbclkn <= 1'b0; // force lock on keyboard
end
else
begin
fsm_ps <= fsm_ns;
fsm_lock_kbclkn <= fsm_lock_kbclkn_ns;
end
end
////////////////////////
always @ (posedge clk or negedge rstn )
begin
if ( !rstn )
begin
s_reg <= 0;
s_reg_parity <= 1'b0; // working (temp) parity of s_reg
s_reg_err <= 1'b0; // final parity check result on s_reg
kbclk_ctr <= 0;
kbclk_ctr_eqcnt <= 1'b0;
end
else
begin
if ( kbclk_stb )
begin
// shift data in from MSB, shift right
s_reg <= {r_kbdata, s_reg[(S_W-1):1]};
end
kbclk_ctr_eqcnt <= kbclk_ctr_eqcnt_ns;
if ( kbclk_ctr_reset )
begin
kbclk_ctr <= 0;
s_reg_parity <= 1'b0;
s_reg_err <= s_reg_parity; // get final-parity result
end
else if ( kbclk_stb )
begin
kbclk_ctr <= kbclk_ctr + 1;
s_reg_parity <= s_reg_parity ^ r_kbdata;
end
end
end
assign kbclk_ctr_reset = kbclk_ctr_eqcnt; // glitchless (reg'd signal)
assign kbclk_ctr_eqcnt_ns = (kbclk_ctr ==(KBCNT-1) && kbclk_stb);// next-state
// time ->
// ! kbclk_ctr_eqcnt_ns
// ! ! ! ! ! ! ! ! ! ! ! ! kbclk_stb (negedge r_kbclk)
// __ _ _ _ _ _ _ _ _ _ _ _
// |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| \_force_low
//___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___
// 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |10 |11 kbclk_ctr
//
// --<0>-<1>-<2>-<3>-<4>-<5>-<6>-<7>-
-