All checks were successful
ci/woodpecker/push/test-workflow Pipeline was successful
The audio output is still messed up, but this commit gets everything as ready as it can get. Fixed up all the testbenches and added state machines for everything
444 lines
13 KiB
Systemverilog
444 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 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,
|
|
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
|