/****
 * sd_controller.sv - Initializes and reads data off an sd card, feeding it
 *                    into the audio buffer
 *
 * @author: Waylon Cude, Dilanthi Prentice
 * @date: 6/12/25
 *
 * **/
module sd_controller(
(* MARK_DEBUG = "TRUE" *)
    input logic slow_clk,
(* MARK_DEBUG = "TRUE" *)
    input logic fast_clk,
    input logic crc_clk,
    input logic reset,
(* MARK_DEBUG = "TRUE" *)
    input logic sd_data,
    inout logic sd_cmd,
    output logic ready,
    output wire clk,

    audio_buffer_interface.driver buffer
);
// NOTE: this gets encoded as one-hot, even if in here I set it as a logic[5:0]
(* MARK_DEBUG = "TRUE" *)
enum logic [5:0] {
    INIT,WAIT,SEND_CMD0,WAIT_CMD0,DELAY_CMD0, // last 4
    SEND_CMD8,WAIT_CMD8,LISTEN_RESPONSE_CMD8,WAIT_RESPONSE_CMD8, // last 8
    SEND_CMD55,WAIT_CMD55,LISTEN_RESPONSE_CMD55,WAIT_RESPONSE_CMD55, // last 12
    SEND_ACMD41,WAIT_ACMD41,LISTEN_RESPONSE_ACMD41,WAIT_RESPONSE_ACMD41,ACMD41_DELAY, //17
    SEND_CMD2,WAIT_CMD2,LISTEN_RESPONSE_CMD2,WAIT_RESPONSE_CMD2, //21
    SEND_CMD3,WAIT_CMD3,LISTEN_RESPONSE_CMD3,WAIT_RESPONSE_CMD3, //25
    SEND_CMD7,WAIT_CMD7,LISTEN_RESPONSE_CMD7,WAIT_RESPONSE_CMD7, //29
    READY_TO_TRANSMIT,
    TRANSMIT,WAIT_TRANSMIT,WAIT_END,FINISH_TRANSMIT,
    TRANSMIT2,WAIT_TRANSMIT2,WAIT_END2,FINISH_TRANSMIT2,
    WAIT_FOR_BUFFER
    } state, next_state;

(* MARK_DEBUG = "TRUE" *)
logic fast_clk_enable;
assign clk = fast_clk_enable ? fast_clk : slow_clk;

logic [$clog2(4114):0] counter;
logic sd_buffer_half;
logic [31:0] address;

(* MARK_DEBUG = "TRUE" *)
logic send_command_start;//, send_command_start_fast;
(* MARK_DEBUG = "TRUE" *)
logic [5:0] cmd;
(* MARK_DEBUG = "TRUE" *)
logic [31:0] arg;
(* MARK_DEBUG = "TRUE" *)
wire send_command_ready;//, send_command_ready_fast;

(* MARK_DEBUG = "TRUE" *)
logic stored_sd_cmd;

// This needs to swap speeds
send_command slowSender(
    clk,
    crc_clk,
    reset,
    send_command_start,
    cmd,
    arg,
    send_command_ready,
    sd_cmd);

//send_command fastSender(
//    fast_clk,
//    crc_clk,
//    reset,
//    send_command_start_fast,
//    cmd,
//    arg,
//    send_command_ready_fast,
//    sd_cmd);

(* MARK_DEBUG = "TRUE" *)
logic read_command_listen;
logic [2:0] response_type;
(* MARK_DEBUG = "TRUE" *)
wire read_command_received;
wire [135:0] out_data;

// Hopefully this doesn't get optimized out...
(* MARK_DEBUG = "TRUE" *)
logic [47:0] received_data;
assign received_data = out_data[47:0];

// This doesn't need to swap speeds as we ignore responses to CMD17
read_command slowReader(
    slow_clk,
    reset,
    read_command_listen,
    response_type,
    sd_cmd,
    read_command_received,
    out_data
);

// The data line is only ever used at fast_clk speeds
read_data dataHandler(
    fast_clk,
    reset,
    sd_data,
    buffer
);

//always_ff @(posedge crc_clk) begin
//    stored_sd_cmd <= sd_cmd;
//end

// Next state logic
always_comb begin
    case (state)
        INIT: next_state=WAIT;
        WAIT: 
            if (counter==0)
                next_state=SEND_CMD0;
            else
                next_state=WAIT;
        SEND_CMD0:
            next_state=WAIT_CMD0;
        WAIT_CMD0:
            if (send_command_ready)
                next_state=DELAY_CMD0;
            else
                next_state=WAIT_CMD0;
        DELAY_CMD0:
            if (counter == 0)
                next_state=SEND_CMD8;
            else
                next_state=DELAY_CMD0;
        // These state transitions are all very similar, this probably could
        // be a macro
        SEND_CMD8:
            next_state=WAIT_CMD8;
        WAIT_CMD8:
            if (send_command_ready)
                next_state=LISTEN_RESPONSE_CMD8;
            else
                next_state=WAIT_CMD8;
        LISTEN_RESPONSE_CMD8:
            next_state=WAIT_RESPONSE_CMD8;
        WAIT_RESPONSE_CMD8:
            if (read_command_received)
                next_state=SEND_CMD55;
            else
                next_state=WAIT_RESPONSE_CMD8;
        SEND_CMD2:
            next_state=WAIT_CMD2;
        WAIT_CMD2:
            if (send_command_ready)
                next_state=LISTEN_RESPONSE_CMD2;
            else
                next_state=WAIT_CMD2;
        LISTEN_RESPONSE_CMD2:
            next_state=WAIT_RESPONSE_CMD2;
        WAIT_RESPONSE_CMD2:
            if (read_command_received)
                next_state=SEND_CMD3;
            else
                next_state=WAIT_RESPONSE_CMD2;
        SEND_CMD3:
            next_state=WAIT_CMD3;
        WAIT_CMD3:
            if (send_command_ready)
                next_state=LISTEN_RESPONSE_CMD3;
            else
                next_state=WAIT_CMD3;
        LISTEN_RESPONSE_CMD3:
            next_state=WAIT_RESPONSE_CMD3;
        WAIT_RESPONSE_CMD3:
            if (read_command_received)
                next_state=SEND_CMD7;
            else
                next_state=WAIT_RESPONSE_CMD3;
        SEND_CMD7:
            next_state=WAIT_CMD7;
        WAIT_CMD7:
            if (send_command_ready)
                next_state=LISTEN_RESPONSE_CMD7;
            else
                next_state=WAIT_CMD7;
        LISTEN_RESPONSE_CMD7:
            next_state=WAIT_RESPONSE_CMD7;
        WAIT_RESPONSE_CMD7:
            if (read_command_received)
                next_state=READY_TO_TRANSMIT;
            else
                next_state=WAIT_RESPONSE_CMD7;

        
        SEND_CMD55:
            next_state=WAIT_CMD55;
        WAIT_CMD55:
            if (send_command_ready)
                next_state=LISTEN_RESPONSE_CMD55;
            else
                next_state=WAIT_CMD55;
        LISTEN_RESPONSE_CMD55:
            next_state=WAIT_RESPONSE_CMD55;
        WAIT_RESPONSE_CMD55:
            if (read_command_received)
                next_state=SEND_ACMD41;
            else
                next_state=WAIT_RESPONSE_CMD55;
        SEND_ACMD41:
            next_state=WAIT_ACMD41;
        WAIT_ACMD41:
            if (send_command_ready)
                next_state=LISTEN_RESPONSE_ACMD41;
            else
                next_state=WAIT_ACMD41;
        LISTEN_RESPONSE_ACMD41:
            next_state=WAIT_RESPONSE_ACMD41;
        WAIT_RESPONSE_ACMD41:
            if (read_command_received && !out_data[39])
                next_state=ACMD41_DELAY;
            else if (read_command_received && out_data[39])
                next_state=SEND_CMD2;
            else
                next_state=WAIT_RESPONSE_ACMD41;
        ACMD41_DELAY:
            if (counter == 0)
                next_state=SEND_CMD55;
            else
                next_state=ACMD41_DELAY;

        READY_TO_TRANSMIT:
            next_state=TRANSMIT;
        TRANSMIT:
            next_state=WAIT_TRANSMIT;
        WAIT_TRANSMIT:
            if (sd_data==0)
                next_state=WAIT_END;
            else
                next_state=WAIT_TRANSMIT;
        WAIT_END:
            if (counter==0)
                next_state=FINISH_TRANSMIT;
            else
                next_state=WAIT_END;
        FINISH_TRANSMIT:
            next_state=TRANSMIT2;
        TRANSMIT2:
            next_state=WAIT_TRANSMIT2;
        WAIT_TRANSMIT2:
            if (sd_data==0)
                next_state=WAIT_END2;
            else
                next_state=WAIT_TRANSMIT2;
        WAIT_END2:
            if (counter==0)
                next_state=FINISH_TRANSMIT2;
            else
                next_state=WAIT_END2;
        FINISH_TRANSMIT2:
            next_state=WAIT_FOR_BUFFER;

        WAIT_FOR_BUFFER:
            if (sd_buffer_half==buffer.address_half)
                next_state=WAIT_FOR_BUFFER;
            else
                next_state=TRANSMIT;
        default:
            next_state=INIT;
    endcase

end
// This could mostly swap speeds ... however detecting the data line going low
// would not work
always_ff @(posedge clk) begin
    stored_sd_cmd <= sd_cmd;
    // Transition states
    if (reset)
        state <= INIT;
    else
        state <= next_state;

    // Sequential outputs/logic
    case (state)
        INIT: begin
            counter <= 80;
            fast_clk_enable <= 0;
        end
        SEND_CMD0: begin
            cmd<=0;
            arg<=0;
            send_command_start <= 1;
        end
        WAIT_CMD0: begin
            counter<=20;
            send_command_start<=0;
        end
        SEND_CMD8: begin
            cmd <=8;
            arg <='h1AA;
            send_command_start <=1;
        end
        LISTEN_RESPONSE_CMD8: begin
            response_type <= 7;
            read_command_listen <= 1;
        end
        SEND_CMD55: begin
            cmd <= 55;
            arg <= 0;
            send_command_start <=1;
        end
        LISTEN_RESPONSE_CMD55: begin
            response_type <= 1;
            read_command_listen <= 1;
        end
        SEND_ACMD41: begin
            cmd <= 41;
            arg <= 'h40100000;
            send_command_start <=1;
        end
        LISTEN_RESPONSE_ACMD41: begin
            response_type <= 3;
            read_command_listen <= 1;
        end
        WAIT_RESPONSE_ACMD41: begin
            read_command_listen<=0;
            counter<=100;
        end
        SEND_CMD2: begin
            cmd <= 2;
            arg <= 0;
            send_command_start <=1;
        end
        LISTEN_RESPONSE_CMD2: begin
            response_type <= 2;
            read_command_listen <= 1;
        end
        SEND_CMD3: begin
            cmd <= 3;
            arg <= 0;
            send_command_start <=1;
        end
        LISTEN_RESPONSE_CMD3: begin
            response_type <= 6;
            read_command_listen <= 1;
        end
        SEND_CMD7: begin
            cmd <= 7;
            arg <= {out_data[39:24],16'h0000};
            send_command_start <=1;
        end
        LISTEN_RESPONSE_CMD7: begin
            response_type <= 1;
            read_command_listen <= 1;
        end
        READY_TO_TRANSMIT: begin
            fast_clk_enable <= 1;
            address <= 0;
            sd_buffer_half <= 0;
        end
        TRANSMIT, TRANSMIT2: begin
            cmd <= 17;
            arg <= address;
            send_command_start <= 1;
        end
        WAIT_TRANSMIT, WAIT_TRANSMIT2: begin
            send_command_start <= 0;
            counter <= 411;
        end
        FINISH_TRANSMIT:
            address <= address +1;
        FINISH_TRANSMIT2: begin
            address <= address +1;
            sd_buffer_half <= ~sd_buffer_half;
        end
        WAIT_FOR_BUFFER:
            ready <= 1;
        // The logic is simple enough in these to group them
        WAIT, DELAY_CMD0, ACMD41_DELAY, WAIT_END, WAIT_END2:
            counter <= counter - 1;
        WAIT_CMD8,WAIT_CMD55,WAIT_ACMD41,WAIT_CMD2,WAIT_CMD3,WAIT_CMD7:
            send_command_start<=0;
        WAIT_RESPONSE_CMD8,WAIT_RESPONSE_CMD55,WAIT_RESPONSE_CMD2,WAIT_RESPONSE_CMD3,
        WAIT_RESPONSE_CMD7:
            read_command_listen<=0;
        default: ;

    endcase
end
endmodule
