module matrix_kbd ( input CLK, input RESET, input FREEZE, inout [15:0] ROWS, input [7:0] COLUMNS,
                    input [3:0] REPORT_ADRESS, output [7:0] REPORT_DATA, output INT);
                    //output [7:0] kbd_r0, kbd_r2, kbd_r3, kbd_r4, kbd_r5, kbd_r6, kbd_r7, 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 = 8000;
  parameter ROW_STT_PROCESS_TIME = 7000;
  parameter ONE_COLUMN_PROCESS_TIME = 50;
  parameter ONE_ROW_TIME_POW = 14;  //  15 - 65536 tacts or 5.46 ms, 14 - 32768 tacts or 2.73 ms, 13 - 16384 tacts or 1.36 ms,
  //  12 - 8191 tacts or 683 mks, 11 - 4096 tacts or 341 mks, 10 - 2048 tacts or 171 mks, 9 - 1024 tacts or 85 mks, 8 - 512 tacts or 43 mks,
  //  7 - 256 tacts or 21 mks, other values have no guaranties
  parameter ONE_CALC_TIME_POW = 4;  //  3 - 16 tacts or 1.3 mks, 4 - 32 tacts or 2.7 mks, 5 - 64 tacts or 5.3 mks, 6 - 128 tacts or 10.7 mks
  //  ONE_ROW_TIME_POW > (ONE_CALC_TIME_POW - 3); ONE_CALC_TIME_POW > 2 (if 2 or smaller, top module overrun may occur)

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

  reg [7:0] temp;
  reg [7:0] i;

  //reg [7:0] report [6:0]; //  NO BYTE 2
  //reg [7:0] report_byte;
  reg isr;
  reg isr_internal;

  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 [6:0] kbd_code;
  assign kbd_code [2:0] = row_time[7:5];  //  COLUMN NUM
  assign kbd_code [6:3] = row_counter;    //  ROW NUM
  wire [7:0] kbd_code_hid;
  reg is_pressed;

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

  //reg [3:0] init_delay_cnt;
  //reg [8:0] init_ram_cnt;
  reg IS_RAM_INIT = 0;

  /*always @ (negedge CLK)  begin
    COLS_SHADOW <= COLUMNS;
  end*/
  wire [7:0] report_data_rd;
  reg [3:0] report_adress_rd;
  reg [7:0] report_data_wr;
  wire [3:0] report_adress_wr;
  assign report_adress_wr = report_adress_rd;
  assign REPORT_DATA = report_data_rd;
  reg report_wr_en;
  ram REPORT (CLK, report_wr_en, report_adress_wr, report_data_wr, report_adress_rd, report_data_rd);

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

  always @ (negedge CLK)  begin
    if (RESET == 0) begin
      //for (i = 0; i < 6; i = i + 1)
      //  report[i] = 0;
      isr = 0;
      isr_internal = 0;
      //init_delay_cnt = 0;
      //init_ram_cnt = 0;
      row_time = 0;
      IS_RAM_INIT = 1;
      ram_adr = 500;
      report_adress_rd = 5;
      report_wr_en = 0;
      //report_byte = 0;
    end
    else begin
      if (FREEZE == 0)  begin
        /*if (REPORT_ADRESS == 0)
          report_byte <= 10;
        else if ((REPORT_ADRESS == 1) || (REPORT_ADRESS == 3))
          report_byte <= 0;
        else if (REPORT_ADRESS == 2)
          report_byte <= report[0];
        else
          report_byte <= report[REPORT_ADRESS-3];*/
        /*if (init_delay_cnt != 15)
          init_delay_cnt = init_delay_cnt + 1;
        else if (init_ram_cnt < 256) begin
          ram_wr = 1;
          ram_adr = init_ram_cnt;
          temp = 255;
          init_ram_cnt = init_ram_cnt + 1;
        end*/
        if (IS_RAM_INIT)  begin
          ram_wr = 1;
          ram_adr = ram_adr + 1;
          temp = 255;
          report_adress_rd = report_adress_rd + 1;
          if (report_adress_rd == 0)
            report_data_wr = 10;
          else
            report_data_wr = 0;//report_adress_rd & 1;
          report_wr_en = 1;
          if (ram_adr == 130) begin
            //ram_wr = 0;
            IS_RAM_INIT = 0;
            report_wr_en = 0;
          end
        end
        /*else if (init_ram_cnt == 256) begin
          ram_wr = 0;
          init_ram_cnt = init_ram_cnt + 1;
        end*/
        else  begin
          row_time = row_time + 1;
          if (row_time == 0) begin//== 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
          //  ROW 0 - D, 1 - A, 2 - C, 3 - B
          /*if (row_time == (ROW_STT_PROCESS_TIME - 1)) begin
            temp = ram_rd;
            COLS_SHADOW <= COLUMNS;
          end*/
          //if (row_time == 8191/*(ROW_STT_PROCESS_TIME + ONE_COLUMN_PROCESS_TIME * 7 + 1)*/)
          //  ram_wr = 1;

          //if ((row_time[12:8] == 31) && (row_time[4:0] == 0))  begin
          if ((row_time[ONE_ROW_TIME_POW:8] == ((1<<(ONE_ROW_TIME_POW-7))-1)) && (row_time[4:0] == 0))  begin
            //temp = ram_rd;
            //COLS_SHADOW = COLUMNS;
            if (row_time[7:5] == 0) begin
              temp = ram_rd;
              COLS_SHADOW = COLUMNS;
            end
            check_column (row_time[7:5]);
            if (row_time[7:5] == 7)
              ram_wr = 1;
          end
          //else
          //  kbd_code = 255;

          /*if (row_time == (ROW_STT_PROCESS_TIME + ONE_COLUMN_PROCESS_TIME * 0))
            check_column (0);
          else if (row_time == (ROW_STT_PROCESS_TIME + ONE_COLUMN_PROCESS_TIME * 2))
            check_column (2);
          else if (row_time == (ROW_STT_PROCESS_TIME + ONE_COLUMN_PROCESS_TIME * 1))
            check_column (1);
          else if (row_time == (ROW_STT_PROCESS_TIME + ONE_COLUMN_PROCESS_TIME * 3))
            check_column (3);
          else if (row_time == (ROW_STT_PROCESS_TIME + ONE_COLUMN_PROCESS_TIME * 4))
            check_column (4);
          else if (row_time == (ROW_STT_PROCESS_TIME + ONE_COLUMN_PROCESS_TIME * 5))
            check_column (5);
          else if (row_time == (ROW_STT_PROCESS_TIME + ONE_COLUMN_PROCESS_TIME * 6))
            check_column (6);
          else if (row_time == (ROW_STT_PROCESS_TIME + ONE_COLUMN_PROCESS_TIME * 7))
            check_column (7);
          else
            kbd_code = 255;*/

    //      START PACK I2C_HID REPORT

          else if ((isr_internal == 1)/* && (row_time[4:0] > 1)*/) begin
            if (report_wr_en == 1)  begin
              report_wr_en = 0;
              isr_internal = 0;
              isr = 1;
            end
            else if (kbd_code_hid == 0) //  IF KEY NOT EXIST, DO NOTHING
              isr_internal = 0;
            else if (kbd_code_hid[7:3] == 5'b11100)  begin //  BYTE WITH MODIFIERS IS READ AT START OF ALG
              if (is_pressed)
                report_data_wr = report_data_rd | (1<<(kbd_code_hid & 8'h07));
              else
                report_data_wr = report_data_rd & (~(1<<(kbd_code_hid & 8'h07)));
              report_wr_en = 1;
            end
            //else
            //  isr_internal = 0;
            else if (report_adress_rd == 2) //  IF BUTTON IS NOT MODIFIER, SET ADRESS TO FIRST BUTTON BYTE
              report_adress_rd = 4;
            else if (report_adress_rd == 10) //  IF TOO MUTCH ADRESSES SEEK, END ALG (BUTTONS ARE IN ADRESSES 4-9)
              isr_internal = 0;
            else begin
              if (is_pressed) begin
                if (report_data_rd == kbd_code_hid) //  IF BUTTON WITH SAME CODE IS IN REPORT
                  isr_internal = 0;                 //  CLEAR INTERNAL INTERRUPT, NO EXT INTERRUPT
                else if (report_data_rd == 0) begin //  IF FREE ADRESS FOUND
                  report_data_wr = kbd_code_hid;    //  WRITE CODE TO THIS ADRESS
                  report_wr_en = 1;
                end
                else
                  report_adress_rd = report_adress_rd + 1;  //  IF NO FREE PLACE, CONTINUE SEEK
              end
              else begin  //  BUTTON RELEASED
                if (report_data_rd == kbd_code_hid) begin   //  IF ADRESS WITH THIS CODE FOUND
                  report_data_wr = 0;                       //  WRITE 0 TO THIS ADRESS (BTN RELEASED)
                  report_wr_en = 1;
                end
                else
                  report_adress_rd = report_adress_rd + 1;  //  IF THIS CODE NOT FOUND, CONTINUE SEEK
              end
            end
          end

          else begin// if (isr_internal == 0) begin
            report_adress_rd = REPORT_ADRESS /*- 1*/; //  IF REPORT FILLING PROCESS IS ENDED, SET ADRESS FROM TOP MODULE
            isr <= 0;
          end
          /*if (kbd_code_hid != 0)  begin
            if (kbd_code_hid[7:3] == 5'b11100)  begin
            //if ((kbd_code_hid > 8'hDF) && (kbd_code_hid < 8'hE8)) begin
              if (is_pressed)
                report [0] = report [0] | (1<<(kbd_code_hid & 8'h07));
              else
                report [0] <= report [0] & (~(1<<(kbd_code_hid & 8'h07)));
              isr = 1;
            end
            else  begin
              if (is_pressed) begin
                isr = 1;
                if (report [ 1 ] == 0)
                  report [ 1 ] <= kbd_code_hid;
                else if (report [ 2 ] == 0)
                  report [ 2 ] <= kbd_code_hid;
                else if (report [ 3 ] == 0)
                  report [ 3 ] <= kbd_code_hid;
                else if (report [ 4 ] == 0)
                  report [ 4 ] <= kbd_code_hid;
                else if (report [ 5 ] == 0)
                  report [ 5 ] <= kbd_code_hid;
                else if (report [ 6 ] == 0)
                  report [ 6 ] <= kbd_code_hid;
                else
                  isr = 0;
              end

              else  begin
                for (i = 1; i < 7; i = i + 1) begin
                  if (report [i] == kbd_code_hid)  begin
                  //if (report [i] == kbd_code)  begin
                    report [i] = 0;
                    isr = 1;
                  end
                end
              end
            end
          end //  END OF KBD CODE SEND ALG
          else
            isr <= 0;*/
        end
      end
    end
  end

  task check_column;
    input [2:0] column;
    begin
      if (COLS_SHADOW[column] != temp[column]) begin
        //kbd_code = row_counter*8 + column;
        if ((COLS_SHADOW[column] == 0) && (temp[column] == 1))  is_pressed = 1;
        else                                                    is_pressed = 0;
        isr_internal = 1;    //  INTERNAL ISR AT NEXT TACT
        report_adress_rd = 2; //  ADRESS TO MODIFIERS
      end
      //else          kbd_code = 255;
      temp[column] = COLS_SHADOW[column];
    end
  endtask

  /*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 REPORT_DATA = report_byte;
  assign INT = isr;

  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
