module matrix_kbd (input CLK, input RESET, input FREEZE, inout [15:0] ROWS, input [7:0] COLUMNS, output [7:0] kbd_r0, kbd_r2, kbd_r3, kbd_r4, kbd_r5, kbd_r6, kbd_r7,/*output [63:0] kbd_report,*/ output INT);

//  * - ESC (29),        7 - F1 (3A),   4 - F2 (3B),         1 - NUM_LOCK (53)
//  0 - CAPS LOCK (39),  8 - R (15),    5 - BACKSPACE (2A),  2 - ENTER (58)
//  # - LSHIFT (E1),     9 - C (06),    6 - V (19),          3 - DELETE (4C)
//  D - LCTRL (E0),      C - LALT (E2), B - SPACE (2C),      A - RGUI (E7)

  parameter ONE_ROW_TIME = 12000;
  parameter ROW_STT_PROCESS_TIME = 11000;
  parameter ONE_COLUMN_PROCESS_TIME = 50;

  reg [15:0] row_time = 0;
  reg [3:0] row_counter;

  //reg [7:0] last_data [3:0];//[31:0] last_data;
  reg [7:0] temp;
  reg [7:0] i;

  //reg [63:0] report;
  reg [7:0] report [6:0]; //  NO BYTE 2
  //reg [5:0] report_free_place;
  reg isr;

  reg [15:0] ROWS_EN = 0;
  reg [15:0] ROWS_OUT = 0;
  wire [15:0] ROWS_IN;
  reg [7:0] COLS_SHADOW;

  reg [7:0] kbd_code;
  wire [7:0] kbd_code_hid;
  reg is_pressed;

  reg ram_wr;
  reg [8:0] ram_adr;
  wire [7:0] ram_rd;

  always @ (negedge CLK)  begin
    COLS_SHADOW <= COLUMNS;
  end

  ram RAM (CLK, ram_wr, ram_adr, temp, ram_rd);//module ram(input clk, wen, input [8:0] addr, input [7:0] wdata, output [7:0] rdata);

  always @ (posedge CLK)  begin
    if (RESET == 0) begin
      //last_data <= 32'hFFFFFFFF;
      //report_free_place <= 6'h3F;
      //report <= 0;
    end
    else begin
      if (FREEZE == 0)  begin
        if (row_time == ONE_ROW_TIME) begin
          ram_wr = 0;
          row_time <= 0;
          row_counter = row_counter + 1;
          ROWS_EN = 1 << row_counter;
          ram_adr = row_counter;
        end
        else
          row_time <= row_time + 1;
        //  ROW 0 - D, 1 - A, 2 - C, 3 - B
        if (row_time == (ROW_STT_PROCESS_TIME - 1))
          temp = ram_rd;
        if (row_time == (ROW_STT_PROCESS_TIME + ONE_COLUMN_PROCESS_TIME * 7 + 1))
          ram_wr = 1;
        if (row_time == (ROW_STT_PROCESS_TIME + ONE_COLUMN_PROCESS_TIME * 0/*0*/)) begin
          check_column (0);
          /*if (COLS_SHADOW[0] != last_data[row_counter*4 + 0]) begin
            case (row_counter)  0: kbd_code = 8'h29;  1: kbd_code = 8'h53;  2: kbd_code = 8'h3A;  3: kbd_code = 8'h3B;  //  ESC, F1-F2, NUM LOCK
                                default: kbd_code = 1;
            endcase
            if ((COLS_SHADOW[0] == 0) && (last_data[row_counter*4 + 0] == 1))            is_pressed = 1;
            else                                                                         is_pressed = 0;
          end
          else          kbd_code = 255;
          last_data[row_counter*4 + 0] <= COLS_SHADOW[0];*/
        end

        else if (row_time == (ROW_STT_PROCESS_TIME + ONE_COLUMN_PROCESS_TIME * 2/*4*/)) begin
          check_column (2);
        end
          /*if (COLS_SHADOW[2] != last_data[row_counter*4 + 2]) begin
            case (row_counter)  0: kbd_code = 8'h39;  1: kbd_code = 8'h58;  2: kbd_code = 8'h15;  3: kbd_code = 8'h2A;  //  CAPS LOCK, R, BACKSPACE, ENTER
                                default: kbd_code = 1;
            endcase
            if ((COLS_SHADOW[2] == 0) && (last_data[row_counter*4 + 2] == 1))            is_pressed = 1;
            else                                                                         is_pressed = 0;
          end
          else          kbd_code = 0;
          last_data[row_counter*4 + 2] <= COLS_SHADOW[2];
        end*/

        else if (row_time == (ROW_STT_PROCESS_TIME + ONE_COLUMN_PROCESS_TIME * 1/*2*/)) begin
          check_column (1);
          /*
          if (COLS_SHADOW[1] != last_data[row_counter*4 + 1]) begin
            case (row_counter)  0: kbd_code = 8'hE1;  1: kbd_code = 8'h4C;  2: kbd_code = 8'h06;  3: kbd_code = 8'h19;  //  LEFT SHIFT, C, V, DELETE
                                default: kbd_code = 1;
            endcase
            if ((COLS_SHADOW[1] == 0) && (last_data[row_counter*4 + 1] == 1))            is_pressed = 1;
            else                                                                         is_pressed = 0;
          end
          else          kbd_code = 255;
          last_data[row_counter*4 + 1] <= COLS_SHADOW[1];*/
        end

        else if (row_time == (ROW_STT_PROCESS_TIME + ONE_COLUMN_PROCESS_TIME * 3/*6*/)) //begin
          check_column (3);

        else if (row_time == (ROW_STT_PROCESS_TIME + ONE_COLUMN_PROCESS_TIME * 4/*6*/))
          check_column (4);
        else if (row_time == (ROW_STT_PROCESS_TIME + ONE_COLUMN_PROCESS_TIME * 5/*6*/))
          check_column (5);
        else if (row_time == (ROW_STT_PROCESS_TIME + ONE_COLUMN_PROCESS_TIME * 6/*6*/))
          check_column (6);
        else if (row_time == (ROW_STT_PROCESS_TIME + ONE_COLUMN_PROCESS_TIME * 7/*6*/))
          check_column (7);
        /*
          if (COLS_SHADOW[3] != last_data[row_counter*4 + 3]) begin
            case (row_counter)  0: kbd_code = 8'hE0;  1: kbd_code = 8'hE7;  2: kbd_code = 8'hE2;  3: kbd_code = 8'h2C;  //  LCTRL, LALT, SPACE, RGUI
                                default: kbd_code = 1;
            endcase
            if ((COLS_SHADOW[3] == 0) && (last_data[row_counter*4 + 3] == 1))            is_pressed = 1;
            else                                                                         is_pressed = 0;
          end
          else          kbd_code = 255;
          last_data[row_counter*4 + 3] <= COLS_SHADOW[3];
          */
        //end

        else
          kbd_code = 255;

  //      START PACK I2C_HID REPORT
        if (kbd_code_hid != 0 /*kbd_code != 255*/)  begin
          isr = 1;
          //report[15:8] <= 0;
          //report[63:56] <= 0;
          if ((kbd_code_hid > 8'hDF) && (kbd_code_hid < 8'hE8))/*((kbd_code > 8'hDF) && (kbd_code < 8'hE8))*/ begin
            //kbd_code = kbd_code & 8'h07;
            if (is_pressed)
              //report [7:0] <= report [7:0] | (1<<(kbd_code_hid & 8'h07));//(1<<kbd_code);
              report [0] = report [0] | (1<<(kbd_code_hid & 8'h07));//(1<<kbd_code);
            else
              report [0] <= report [0] & (~(1<<(kbd_code_hid & 8'h07)));//(~(1<<kbd_code));
              //report [7:0] <= report [7:0] & (~(1<<(kbd_code_hid & 8'h07)));//(~(1<<kbd_code));
          end
          else  begin
            if (is_pressed) begin
              /*for (i = 0; i < 4; i = i + 1) begin
                if (report_free_place[i] == 1)  begin
                  report [ ((i + 2) * 8 + 7) : ((i + 2) * 8 + 0)] <= kbd_code;
                  report_free_place[i] = 0;
                  is_pressed = 0;           //  NO ERROR DETECTED
                end
              end
              if (report_free_place[0] == 1)  begin
                report [ ((0 + 2) * 8 + 7) : ((0 + 2) * 8 + 0)] <= kbd_code;
                report_free_place[0] = 0;
              end*/
              if (report [ 1 ] == 0)
                report [ 1 ] <= kbd_code_hid;//kbd_code;
              else if (report [ 2 ] == 0)
                report [ 2 ] <= kbd_code_hid;//kbd_code;
              else if (report [ 3 ] == 0)
                report [ 3 ] <= kbd_code_hid;//kbd_code;
              else if (report [ 4 ] == 0)
                report [ 4 ] <= kbd_code_hid;//kbd_code;
              else if (report [ 5 ] == 0)
                report [ 5 ] <= kbd_code_hid;//kbd_code;
              else if (report [ 6 ] == 0)
                report [ 6 ] <= kbd_code_hid;//kbd_code;
              /*if (report [ 23 : 16 ] == 0)
                report [ 23 : 16 ] <= kbd_code_hid;//kbd_code;
              else if (report [ 31 : 24 ] == 0)
                report [ 31 : 24 ] <= kbd_code_hid;//kbd_code;
              else if (report [ 39 : 32 ] == 0)
                report [ 39 : 32 ] <= kbd_code_hid;//kbd_code;
              else if (report [ 47 : 40 ] == 0)
                report [ 47 : 40 ] <= kbd_code_hid;//kbd_code;
              else if (report [ 55 : 48 ] == 0)
                report [ 55 : 48 ] <= kbd_code_hid;//kbd_code;
              else if (report [ 63 : 56 ] == 0)
                report [ 63 : 56 ] <= kbd_code_hid;//kbd_code;*/
            end

            else  begin
              for (i = 1; i < 7; i = i + 1) begin
                //if (report [ ((i + 2) * 8 + 7) : ((i + 2) * 8 + 0)] == kbd_code_hid/*kbd_code*/)  begin
                  //report [ ((i + 2) * 8 + 7) : ((i + 2) * 8 + 0)] <= 0;
                if (report [i] == kbd_code_hid/*kbd_code*/)  begin
                  report [i] <= 0;
                  //report_free_place[i] = 1;
                end
              end
            end
            //if (kbd_code == 8'h2C)  begin
              //if (is_pressed)
              //  report [15:8] <= kbd_code;
              //else
              //  report [15:8] <= 0;
            //end
            //else if (kbd_code == 1)  begin
            //  if (is_pressed)
            //    report [23:16] <= kbd_code;
            //  else
            //    report [23:16] <= 0;
            //end
          end
        end //  END OF KBD CODE SEND ALG
        else
          isr <= 0;

        /*if (kbd_code != 0)  begin
          if (is_pressed)
            report [7:0] <= kbd_code;
          else
            report [7:0] <= 0;
        end*/
      end
    end
  end

  /*else if (row_time == (ONE_ROW_TIME/2 + 6)) begin
    if (COLS_SHADOW[3] != last_data[row_counter*4 + 3]) begin
      case (row_counter)  0: kbd_code = 8'hE0;  1: kbd_code = 8'hE7;  2: kbd_code = 8'hE2;  3: kbd_code = 8'h2C;  //  LCTRL, LALT, SPACE, RGUI
                          default: kbd_code = 1;
      endcase
      if ((COLS_SHADOW[3] == 0) && (last_data[row_counter*4 + 3] == 1))            is_pressed = 1;
      else                                                                         is_pressed = 0;
    end
    else          kbd_code = 0;
    last_data[row_counter*4 + 3] <= COLS_SHADOW[3];
  end*/

  task check_column; //(input [2:0] column);
    input [2:0] column;
    begin
      //if (COLS_SHADOW[column] != last_data[row_counter][column]/*[row_counter*8 + column]*/) begin
      if (COLS_SHADOW[column] != temp[column]) begin
        kbd_code = row_counter*8 + column;
        //if ((COLS_SHADOW[column] == 0) && (last_data[row_counter][column]/*[row_counter*8 + column]*/ == 1))  is_pressed = 1;
        if ((COLS_SHADOW[column] == 0) && (temp[column] == 1))  is_pressed = 1;
        else                                                    is_pressed = 0;
      end
      else          kbd_code = 255;
      //last_data[row_counter][column]/*[row_counter*8 + column]*/ <= COLS_SHADOW[column];
      temp[column] = COLS_SHADOW[column];
    end
  endtask

  //assign kbd_report = report;
  assign kbd_r0 = report[0];
  assign kbd_r2 = report[1];
  assign kbd_r3 = report[2];
  assign kbd_r4 = report[3];
  assign kbd_r5 = report[4];
  assign kbd_r6 = report[5];
  assign kbd_r7 = report[6];
  assign INT = isr;
  //assign ROWS_EN = (1 << row_counter);

  SB_RAM40_4K #(
    .INIT_0(256'h0000_0001_0001_0001_00E7_0058_004C_0053__0001_0001_0001_0001_00E0_0039_00E1_0029), //  ROW 0-1
    .INIT_1(256'h0001_0001_0001_0001_002C_002A_0019_003B__0001_0001_0001_0001_00E2_0015_0006_003A), //  ROW 2-3
    .INIT_2(256'h0001_0001_0001_0001_002C_002A_0019_003B__0001_0001_0001_0001_00E2_0015_0006_003A), //  ROW 4-5
    .INIT_3(256'h0001_0001_0001_0001_002C_002A_0019_003B__0001_0001_0001_0001_00E2_0015_0006_003A), //  ROW 6-7
    .INIT_4(256'h0001_0001_0001_0001_002C_002A_0019_003B__0001_0001_0001_0001_00E2_0015_0006_003A), //  ROW 8-9
    .INIT_5(256'h0001_0001_0001_0001_002C_002A_0019_003B__0001_0001_0001_0001_00E2_0015_0006_003A), //  ROW 10-11
    .INIT_6(256'h0001_0001_0001_0001_002C_002A_0019_003B__0001_0001_0001_0001_00E2_0015_0006_003A), //  ROW 12-13
    .INIT_7(256'h0001_0001_0001_0001_002C_002A_0019_003B__0001_0001_0001_0001_00E2_0015_0006_003A), //  ROW 14-15
    .INIT_8(256'h0001_0001_0001_0001_002C_002A_0019_003B__0001_0001_0001_0001_00E2_0015_0006_003A), //  ROW 16-17
    .INIT_9(256'h0001_0001_0001_0001_002C_002A_0019_003B__0001_0001_0001_0001_00E2_0015_0006_003A), //  ROW 18-19
    .WRITE_MODE(1),
    .READ_MODE(1)
  ) kbd_codes (
    .RDATA(kbd_code_hid),
    .RADDR(kbd_code),
    .RCLK(CLK),
    .RCLKE(1'b1),
    .RE(1'b1),
    .WADDR(8'b0),
    .WCLK(1'b0),
    .WCLKE(1'b0),
    .WDATA(8'b0),
    .WE(1'b0)
  );

  SB_IO #(
    .PIN_TYPE(6'b 1010_01),
    .PULLUP(1'b 0)
  ) rows_io [15:0] (
    .PACKAGE_PIN(ROWS),
    .OUTPUT_ENABLE(ROWS_EN),
    .D_OUT_0(ROWS_OUT),
    .D_IN_0(ROWS_IN)
  );

endmodule
