//////////////////////////////////////////////////////////////////////////////////
// Company: Digilent Inc.
// Engineer: Thomas Kappenman
//
// Create Date: 03/03/2015 09:33:36 PM
// Design Name:
// Module Name: PS2Receiver
// Project Name: Nexys4DDR Keyboard Demo
// Target Devices: Nexys4DDR
// Tool Versions:
// Description: PS2 Receiver module used to shift in keycodes from a keyboard plugged into the PS2 port
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// Modified in 2023 by engineers of
// National Research University of Electronic Technology
// Nikita Bulavin and Andrei Solodovnikov
//
//////////////////////////////////////////////////////////////////////////////////

module PS2Receiver(
  input  logic        clk_i,
  input  logic        rst_i,
  input  logic        kclk_i,
  input  logic        kdata_i,
  output logic [7:0]  keycode_o,
  output              keycode_valid_o
  );

  logic       flag;
  logic [3:0] flag_shift;
  logic       kclkf, kdataf;
  logic [3:0] cnt;

  assign keycode_valid_o = flag_shift[0] && !flag_shift[2];

debouncer debounce(
  .clk(clk_i),
  .I0(kclk_i),
  .I1(kdata_i),
  .O0(kclkf),
  .O1(kdataf)
);
always@(posedge clk_i) begin
  if(rst_i) begin
    flag_shift <= '0;
  end
  else begin
    flag_shift <= {flag_shift[2:0], flag};
  end
end

always_ff @(negedge kclkf or posedge rst_i)begin
  if(rst_i) begin
    cnt <= '0;
  end
  else if (cnt <= 9) begin
    cnt <= cnt + 1;
  end
  else begin
    cnt <= '0;
  end
end

always_ff @(negedge kclkf or posedge rst_i) begin
  if(rst_i) begin
    keycode_o <= '0;
  end
  else begin
    case(cnt)
      1:keycode_o[0]<=kdataf;
      2:keycode_o[1]<=kdataf;
      3:keycode_o[2]<=kdataf;
      4:keycode_o[3]<=kdataf;
      5:keycode_o[4]<=kdataf;
      6:keycode_o[5]<=kdataf;
      7:keycode_o[6]<=kdataf;
      8:keycode_o[7]<=kdataf;
      default: keycode_o <= keycode_o;
    endcase
  end
end

assign flag = cnt == 9;

endmodule


module debouncer(
  input  logic clk,
  input  logic I0,
  input  logic I1,
  output logic O0,
  output logic O1
    );

  logic [4:0]cnt0, cnt1;
  logic Iv0=0,Iv1=0;
  logic out0, out1;

  always_ff @(posedge(clk))begin
    if (I0==Iv0) begin
      if (cnt0==19)O0<=I0;
      else cnt0<=cnt0+1;
    end
    else begin
      cnt0<=5'd0;
      Iv0<=I0;
    end
    if (I1==Iv1)begin
      if (cnt1==19)O1<=I1;
      else cnt1<=cnt1+1;
    end
    else begin
      cnt1<=5'd0;
      Iv1<=I1;
    end
  end

endmodule
