module rtc_read_cntr (
    input clk, reset_p,
    input read_en,
    inout rtc_dat,
    output reg rtc_clk,
    output reg rtc_rst,
    output reg [7:0] sec, min, hour,
    output reg [7:0] date, month, day, year,
    output reg data_valid, busy
    );

    localparam IDLE       = 9'b0_0000_0001;
    localparam RST_SET    = 9'b0_0000_0010;
    localparam ADDR_IN    = 9'b0_0000_0100;
    localparam ADDR_SET   = 9'b0_0000_1000;
    localparam ADDR_RESET = 9'b0_0001_0000;
    localparam DATA_IN    = 9'b0_0010_0000;
    localparam DATA_SET   = 9'b0_0100_0000;
    localparam DATA_RESET = 9'b0_1000_0000;
    localparam RST_RESET  = 9'b1_0000_0000;

    reg rtc_dat_dir, rtc_dat_out;
    assign rtc_dat = rtc_dat_dir ? rtc_dat_out : 1'bz;

    reg [9:0] state, next_state;
    always @(negedge clk, posedge reset_p) begin
        if (reset_p) state = IDLE;
        else state = next_state;
    end

    reg [7:0] com_addr [0:6];
    initial begin
        com_addr[0] = 8'h81; // SEC
        com_addr[1] = 8'h83; // MIN
        com_addr[2] = 8'h85; // HOUR
        com_addr[3] = 8'h87; // DATE
        com_addr[4] = 8'h89; // MONTH
        com_addr[5] = 8'h8B; // DAY
        com_addr[6] = 8'h8D; // YEAR
    end

    reg [6:0] clk_div;
    reg sclk_en;
    always @(posedge clk, posedge reset_p) begin
        if (reset_p) begin
            clk_div <= 0;
            sclk_en <= 0;
        end
        else begin
            clk_div <= clk_div + 1;
            sclk_en <= (clk_div == 0);
        end
    end

    reg [3:0] cnt_addr, cnt_bit;
    reg [7:0] buff_addr, buff_data;
    always @(posedge clk, posedge reset_p) begin
        if (reset_p) begin
            rtc_rst = 0;
            next_state = IDLE;
            cnt_addr = 0;
            buff_addr = 0;
            buff_data = 0;
            rtc_clk = 0;
            rtc_dat_dir = 0;
            rtc_dat_out = 0;
            cnt_bit = 0;
            data_valid = 0;
            busy = 0;
        end
        else if (sclk_en) begin
            case (state)
                IDLE       : begin
                    rtc_rst = 0;
                    cnt_addr = 0;
                    rtc_clk = 0;
                    cnt_bit = 0;
                    buff_addr = 0;
                    buff_data = 0;
                    data_valid = 0;
                    if (read_en) begin
                        busy = 1;
                        next_state = RST_SET;
                    end
                end
                RST_SET    : begin
                    rtc_rst = 1;
                    rtc_clk = 0;
                    rtc_dat_dir = 1;
                    buff_addr = com_addr[cnt_addr];
                    next_state = ADDR_IN;
                end
                ADDR_IN    : begin
                    rtc_dat_out = buff_addr[0];
                    buff_addr = {1'b0, buff_addr[7:1]};
                    cnt_bit = cnt_bit + 1;
                    next_state = ADDR_SET;
                end
                ADDR_SET   : begin
                    rtc_clk = 1;
                    next_state = ADDR_RESET;
                end
                ADDR_RESET : begin
                    rtc_clk = 0;
                    if (cnt_bit >= 8) begin
                        cnt_bit = 0;
                        rtc_dat_dir = 0;
                        next_state = DATA_IN;
                    end
                    else next_state = ADDR_IN;
                end
                DATA_IN    : begin
                    buff_data = {rtc_dat, buff_data[7:1]};
                    cnt_bit = cnt_bit + 1;
                    next_state = DATA_SET;
                end
                DATA_SET   : begin
                    rtc_clk = 1;
                    next_state = DATA_RESET;
                end
                DATA_RESET : begin
                    rtc_clk = 0;
                    if (cnt_bit >= 8) begin
                        cnt_bit = 0;
                        next_state = RST_RESET;
                    end
                    else next_state = DATA_IN;
                end
                RST_RESET  : begin
                    rtc_rst = 0;
                    case (cnt_addr)
                        4'd0 : sec = buff_data;
                        4'd1 : min = buff_data;
                        4'd2 : hour = buff_data;
                        4'd3 : date = buff_data;
                        4'd4 : month = buff_data;
                        4'd5 : day = buff_data;
                        4'd6 : year = buff_data;
                    endcase
                    if (cnt_addr >= 6) begin
                        cnt_addr = 0;
                        data_valid = 1;
                        busy = 0;
                        next_state = IDLE;
                    end
                    else begin
                        cnt_addr = cnt_addr + 1;
                        next_state = RST_SET;
                    end
                end
            endcase
        end
    end
endmodule