All checks were successful
ci/woodpecker/push/test-workflow Pipeline was successful
at audio fixup. It did nothing of course lol Reverted back to 16-bit pcm because it sounds marginally better than 8-bit
446 lines
13 KiB
Systemverilog
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
|