// keyboard controller
module kb_ctr(
    input         i_clk,        // main clock, at 27 MHz
    input         i_clk_100hz,  // clock for button debouncing, used here to trigger scan 100 times a second
    input  [2:0]  i_row_addr,   // these are the same pins driving 7-segment display digit seection
    input  [3:0]  i_columns,    // same pins used for left, bottom, top and right buttons in cross pattern

    output [15:0] o_keys
);

    localparam IDLE       = 7'b0000001;
    localparam START      = 7'b0000010;
    localparam ROW_0_READ = 7'b0000100;
    localparam ROW_1_READ = 7'b0001000;
    localparam ROW_2_READ = 7'b0010000;
    localparam ROW_3_READ = 7'b0100000;
    localparam OUT_WRITE  = 7'b1000000;

    localparam DELAY      = 4'b1111;

    reg [3:0]  r_columns [4];
    reg [15:0] r_out = 16'h00;

    reg [6:0]  State = IDLE;

    reg        r_100Hz = 1'b0;
    reg [3:0]  r_delay = 4'b0000;

    always @(posedge i_clk) begin
        r_100Hz <= i_clk_100hz;

        case(State)
            // this state is active most of the time,
            // period 10 milliseconds
            IDLE: begin
                if(i_clk_100hz & ~r_100Hz) begin
                    State <= State << 1;
                end
            end

            // Address is valid for 100 microseconds
            // The value should be read once
            // Read time is one clock cycle
            // Read should be delayed at least half a microsecond (14 clock cycles)
            // for RC equilibration
            // otherwise button press detection is not stable
            //
            // START state is waiting for address
            // to rollower to zero
            // when row address is zero,
            // move to the next state
            START: begin
                if(i_row_addr == 3'b000) begin
                    State <= State << 1;
                end
            end

            ROW_0_READ: begin
                if(i_row_addr == 3'b000) begin
                    if(r_delay != DELAY) begin
                        r_delay <= r_delay + 1;
                    end else begin
                        r_delay <= 4'b0000;
                        r_columns[0] <= i_columns;
                        State <= State << 1;
                    end
                end
            end

            ROW_1_READ: begin
                if(i_row_addr == 3'b001) begin
                    if(r_delay != DELAY) begin
                        r_delay <= r_delay + 1;
                    end else begin
                        r_delay <= 4'b0000;
                        r_columns[1] <= i_columns;
                        State <= State << 1;
                    end
                end
            end

            ROW_2_READ: begin
                if(i_row_addr == 3'b010) begin
                    if(r_delay != DELAY) begin
                        r_delay <= r_delay + 1;
                    end else begin
                        r_delay <= 4'b0000;
                        r_columns[2] <= i_columns;
                        State <= State << 1;
                    end
                end
            end

            ROW_3_READ: begin
                if(i_row_addr == 3'b011) begin
                    if(r_delay != DELAY) begin
                        r_delay <= r_delay + 1;
                    end else begin
                        r_delay <= 4'b0000;
                        r_columns[3] <= i_columns;
                        State <= State << 1;
                    end
                end
            end

            OUT_WRITE: begin
                r_out <= {r_columns[0][3], r_columns[1][3], r_columns[2][3], r_columns[3][3],
                          r_columns[0][2], r_columns[1][2], r_columns[2][2], r_columns[3][2],
                          r_columns[0][1], r_columns[1][1], r_columns[2][1], r_columns[3][1],
                          r_columns[0][0], r_columns[1][0], r_columns[2][0], r_columns[3][0]};
                State <= IDLE;
            end

            default: begin
                State <= IDLE;
            end
        endcase
    end

    assign o_keys = r_out;
endmodule
