/**** * 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 mem_clk, input logic reset, (* MARK_DEBUG = "TRUE" *) input logic [3:0] 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] 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 SEND2_CMD55,WAIT2_CMD55,LISTEN_RESPONSE2_CMD55,WAIT_RESPONSE2_CMD55, SEND_ACMD6,WAIT_ACMD6,LISTEN_RESPONSE_ACMD6,WAIT_RESPONSE_ACMD6, READY_TO_TRANSMIT, DELAY_CLOCK_SWITCH, TRANSMIT,WAIT_TRANSMIT,WAIT_END,FINISH_TRANSMIT, TRANSMIT2,WAIT_TRANSMIT2,WAIT_END2,FINISH_TRANSMIT2, WAIT_FOR_BUFFER } state, next_state; 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; 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, mem_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=SEND2_CMD55; 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; SEND2_CMD55: next_state=WAIT2_CMD55; WAIT2_CMD55: if (send_command_ready) next_state=LISTEN_RESPONSE2_CMD55; else next_state=WAIT2_CMD55; LISTEN_RESPONSE2_CMD55: next_state=WAIT_RESPONSE2_CMD55; WAIT_RESPONSE2_CMD55: if (read_command_received) next_state=SEND_ACMD6; else next_state=WAIT_RESPONSE2_CMD55; SEND_ACMD6: next_state=WAIT_ACMD6; WAIT_ACMD6: if (send_command_ready) next_state=LISTEN_RESPONSE_ACMD6; else next_state=WAIT_ACMD6; LISTEN_RESPONSE_ACMD6: next_state=WAIT_RESPONSE_ACMD6; WAIT_RESPONSE_ACMD6: if (read_command_received) next_state=READY_TO_TRANSMIT; else next_state=WAIT_RESPONSE_ACMD6; READY_TO_TRANSMIT: next_state=DELAY_CLOCK_SWITCH; DELAY_CLOCK_SWITCH: if (counter == 0) next_state = TRANSMIT; else next_state = DELAY_CLOCK_SWITCH; 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 SEND2_CMD55: begin cmd <= 55; // the arg should be the same as the preceding command // and otherwise we'll lose the RCA bits //arg <= 0; send_command_start <=1; end LISTEN_RESPONSE_CMD55,LISTEN_RESPONSE2_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_ACMD6: begin cmd <= 6; arg <= 'b10; send_command_start <=1; end LISTEN_RESPONSE_ACMD6: begin response_type <= 1; read_command_listen <= 1; 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; // Wait to actually transmit until clock is running and stable counter <= 100; end TRANSMIT, TRANSMIT2: begin cmd <= 17; arg <= address; send_command_start <= 1; end WAIT_TRANSMIT, WAIT_TRANSMIT2: begin send_command_start <= 0; counter <= 512*2+16+1; 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, DELAY_CLOCK_SWITCH: counter <= counter - 1; WAIT_CMD8,WAIT_CMD55,WAIT_ACMD41,WAIT2_CMD55,WAIT_ACMD6,WAIT_CMD2,WAIT_CMD3,WAIT_CMD7: send_command_start<=0; WAIT_RESPONSE_CMD8,WAIT_RESPONSE_CMD55,WAIT_RESPONSE2_CMD55,WAIT_RESPONSE_ACMD6, WAIT_RESPONSE_CMD2,WAIT_RESPONSE_CMD3,WAIT_RESPONSE_CMD7: read_command_listen<=0; default: ; endcase end endmodule