SDVD/design/sd/sd_controller.sv
Waylon Cude 43573f5d8f
All checks were successful
ci/woodpecker/push/test-workflow Pipeline was successful
Added a few more demos and one last attempt
at audio fixup. It did nothing of course lol

Reverted back to 16-bit pcm because it sounds marginally better than
8-bit
2025-06-10 14:27:39 -07:00

446 lines
13 KiB
Systemverilog

/****
* 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