save writeback on reset fully working

This commit is contained in:
Polprzewodnikowy 2021-02-19 01:36:57 +01:00
parent 3200919328
commit aa74831511
13 changed files with 194 additions and 131 deletions

View File

@ -19,7 +19,7 @@
#
# Quartus Prime
# Version 20.1.1 Build 720 11/11/2020 SJ Lite Edition
# Date created = 00:33:20 February 17, 2021
# Date created = 01:35:54 February 19, 2021
#
# -------------------------------------------------------------------------- #
#
@ -77,6 +77,7 @@ set_global_assignment -name VERILOG_FILE rtl/top.v
set_global_assignment -name VERILOG_FILE rtl/usb/usb_ftdi_fsi.v
set_global_assignment -name VERILOG_FILE rtl/usb/usb_pc.v
set_global_assignment -name VERILOG_INCLUDE_FILE rtl/constants.vh
set_global_assignment -name SLD_FILE db/signal_tap_logic_analyzer_auto_stripped.stp
# Pin & Location Assignments
# ==========================
@ -272,11 +273,10 @@ set_global_assignment -name OUTPUT_IO_TIMING_FAR_END_VMEAS "HALF SIGNAL SWING" -
set_global_assignment -name PARTITION_NETLIST_TYPE SOURCE -section_id Top
set_global_assignment -name PARTITION_FITTER_PRESERVATION_LEVEL PLACEMENT_AND_ROUTING -section_id Top
set_global_assignment -name PARTITION_COLOR 16764057 -section_id Top
set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top
# end DESIGN_PARTITION(Top)
# -------------------------
# end ENTITY(top)
# ---------------
set_global_assignment -name SLD_FILE db/signal_tap_logic_analyzer_auto_stripped.stp
set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top
# ---------------

View File

@ -43,14 +43,13 @@ set_false_path -from [get_ports {i_ftdi_so i_ftdi_cts}]
set_output_delay -clock [get_clocks {sd_clk}] -max 6.0 [get_ports {io_sd_cmd io_sd_dat[*]}]
set_output_delay -clock [get_clocks {sd_clk}] -min -2.0 [get_ports {io_sd_cmd io_sd_dat[*]}]
set_input_delay -clock [get_clocks {sd_clk}] -max 14.0 [get_ports {io_sd_cmd io_sd_dat[*]}]
set_input_delay -clock [get_clocks {sd_clk}] -min 2.5 [get_ports {io_sd_cmd io_sd_dat[*]}]
set_input_delay -clock [get_clocks {sd_clk}] -max 15.0 [get_ports {io_sd_cmd io_sd_dat[*]}]
set_input_delay -clock [get_clocks {sd_clk}] -min 6.5 [get_ports {io_sd_cmd io_sd_dat[*]}]
set_multicycle_path -setup -start 1 -from [get_clocks $sys_clk] -to [get_clocks {sd_clk}]
set_multicycle_path -hold -start 1 -from [get_clocks $sys_clk] -to [get_clocks {sd_clk}]
set_multicycle_path -setup -end 3 -from [get_clocks {sd_clk}] -to [get_clocks $sys_clk]
set_multicycle_path -hold -end 2 -from [get_clocks {sd_clk}] -to [get_clocks $sys_clk]
set_multicycle_path -hold -end 1 -from [get_clocks {sd_clk}] -to [get_clocks $sys_clk]
# N64, PI and SI timings

View File

@ -17,6 +17,7 @@ module sd_dat (
output o_dat_write_busy,
output reg o_dat_crc_error,
output reg o_dat_write_error,
output reg o_dat_write_ok,
input i_rx_fifo_overrun,
output reg o_rx_fifo_push,
@ -41,7 +42,7 @@ module sd_dat (
reg [6:0] r_state;
assign o_dat_busy = !r_state[STATE_IDLE];
assign o_dat_write_busy = r_state[STATE_SENDING] || r_state[STATE_STATUS] || r_state[STATE_BUSY];
assign o_dat_write_busy = !io_sd_dat[0];
// Bit counter logic
@ -50,13 +51,13 @@ module sd_dat (
reg r_bit_counter_done;
wire w_read_start = r_state[STATE_READ_WAIT] && !io_sd_dat[0] && i_sd_clk_strobe_rising;
wire w_write_start = r_state[STATE_WRITE_WAIT] && (i_tx_fifo_items == ({1'b0, i_dat_block_size} + 1)) && i_sd_clk_strobe_falling;
wire w_write_start = r_state[STATE_WRITE_WAIT] && (i_tx_fifo_items >= ({1'b0, i_dat_block_size} + 1)) && i_sd_clk_strobe_falling;
wire w_status_start = r_state[STATE_SENDING] && r_bit_counter_done;
wire w_status_done = r_state[STATE_STATUS] && r_bit_counter_done;
wire w_block_read_done = r_state[STATE_RECEIVING] && r_bit_counter_done;
wire w_block_write_done = r_state[STATE_SENDING] && r_bit_counter_done;
wire w_block_done = w_block_read_done || w_block_write_done;
wire w_block_write_busy_done = r_state[STATE_BUSY] && io_sd_dat[0];
wire w_block_done = w_block_read_done || w_block_write_busy_done;
wire [12:0] w_block_bit_length = i_dat_width ? ({2'b00, {1'b0, i_dat_block_size} + 1'd1, 3'b000}) : ({{1'b0, i_dat_block_size} + 1'd1, 5'b00000});
@ -138,12 +139,14 @@ module sd_dat (
reg [4:0] r_status;
wire w_no_write_error = r_status == STATUS_NO_ERROR;
wire w_crc_write_error = r_status == STATUS_CRC_ERROR;
wire w_data_write_error = r_status == STATUS_WRITE_ERROR;
always @(posedge i_clk) begin
if (i_reset) begin
if (i_reset || w_block_start) begin
o_dat_crc_error <= 1'b0;
o_dat_write_error <= 1'b0;
end else begin
if (w_block_read_done) begin
o_dat_crc_error <= w_crc_read_error;
@ -151,6 +154,7 @@ module sd_dat (
if (w_status_done) begin
o_dat_crc_error <= w_crc_write_error;
o_dat_write_error <= w_data_write_error;
o_dat_write_ok <= w_no_write_error;
end
end
end
@ -230,7 +234,7 @@ module sd_dat (
r_state[STATE_BUSY]: begin
if (w_block_write_busy_done) begin
if (w_block_stop) begin
if (w_block_stop || !w_no_write_error) begin
r_state[STATE_IDLE] <= 1'b1;
end else begin
r_state[STATE_WRITE_WAIT] <= 1'b1;
@ -274,7 +278,7 @@ module sd_dat (
wire w_tx_latch = r_state[STATE_SENDING] && i_sd_clk_strobe_falling;
wire w_tx_data_phase = r_bit_counter >= 13'd17;
wire w_tx_crc_phase = r_bit_counter >= 13'd1;
wire w_tx_fifo_pop = i_dat_width ? (r_bit_counter[2:0] == 3'd0) : (r_bit_counter[4:0] == 5'd16);
wire w_tx_fifo_pop = i_dat_width ? (r_bit_counter[2:0] == 3'd0) : (r_bit_counter[4:0] == 5'd17);
wire w_tx_shift_load = r_bit_counter[2:0] == 3'd0;
reg [7:0] r_tx_shift [0:3];
@ -309,7 +313,12 @@ module sd_dat (
end
end
end else begin
r_tx_shift[0] <= i_tx_fifo_data[{3'b000, r_bit_counter[4:3]} +: 8];
case (r_bit_counter[4:3])
2'b10: r_tx_shift[0] <= i_tx_fifo_data[31:24];
2'b01: r_tx_shift[0] <= i_tx_fifo_data[23:16];
2'b00: r_tx_shift[0] <= i_tx_fifo_data[15:8];
2'b11: r_tx_shift[0] <= i_tx_fifo_data[7:0];
endcase
end
end
end else if (w_tx_crc_phase) begin

View File

@ -159,6 +159,7 @@ module sd_interface (
wire w_dat_write_busy;
wire w_dat_crc_error;
wire w_dat_write_error;
wire w_dat_write_ok;
sd_dat sd_dat_inst (
.i_clk(i_clk),
@ -179,6 +180,7 @@ module sd_interface (
.o_dat_write_busy(w_dat_write_busy),
.o_dat_crc_error(w_dat_crc_error),
.o_dat_write_error(w_dat_write_error),
.o_dat_write_ok(w_dat_write_ok),
.i_rx_fifo_overrun(w_rx_fifo_overrun),
.o_rx_fifo_push(w_rx_fifo_push),
@ -267,6 +269,7 @@ module sd_interface (
.i_dat_write_busy(w_dat_write_busy),
.i_dat_crc_error(w_dat_crc_error),
.i_dat_write_error(w_dat_write_error),
.i_dat_write_ok(w_dat_write_ok),
.o_rx_fifo_flush(w_rx_fifo_flush),
.o_rx_fifo_pop(w_rx_fifo_regs_pop),

View File

@ -25,6 +25,7 @@ module sd_regs (
input i_dat_write_busy,
input i_dat_crc_error,
input i_dat_write_error,
input i_dat_write_ok,
output reg o_rx_fifo_flush,
output reg o_rx_fifo_pop,
@ -204,7 +205,8 @@ module sd_regs (
SD_REG_DAT: begin
o_data <= {
4'd0,
3'd0,
i_dat_write_ok,
i_dat_write_error,
i_dat_write_busy,
i_tx_fifo_items,

View File

@ -539,7 +539,7 @@ module top (
// LED
wire w_led_trigger = (w_n64_request && !w_n64_busy) || (w_pc_request && !w_pc_busy);
wire w_led_trigger = (w_n64_request && !w_n64_busy) || (w_pc_request && !w_pc_busy) || (w_sd_dma_request && !w_sd_dma_busy);
cart_led cart_led_inst (
.i_clk(w_sys_clk),

View File

@ -53,7 +53,7 @@ DRESULT disk_read(BYTE pdrv, BYTE *buff, LBA_t sector, UINT count) {
#if !FF_FS_READONLY
DRESULT disk_write (BYTE pdrv, const BYTE* buff, LBA_t sector, UINT count) {
DRESULT disk_write(BYTE pdrv, const BYTE* buff, LBA_t sector, UINT count) {
sc64_sd_err_t error;
if (pdrv > 0) {
@ -79,5 +79,15 @@ DRESULT disk_write (BYTE pdrv, const BYTE* buff, LBA_t sector, UINT count) {
#endif
DRESULT disk_ioctl(BYTE pdrv, BYTE cmd, void *buff) {
return RES_PARERR;
switch (cmd) {
case CTRL_SYNC: {
sc64_sd_err_t error = sc64_sd_dat_busy_wait();
if (error != E_OK) {
return RES_ERROR;
}
break;
}
}
return RES_OK;
}

View File

@ -97,24 +97,6 @@ void loader_display_logo(void) {
display_show(display);
}
void loader_display_message(const char *message) {
if (!loader_initialized) {
loader_init();
}
display_context_t display;
display = loader_get_display(true);
graphics_fill_screen(display, 0);
loader_draw_version_and_logo(display);
graphics_draw_text(display, x_offset, y_offset, message);
display_show(display);
}
void loader_display_error_and_halt(menu_load_error_t error, const char *message) {
if (!loader_initialized) {
loader_init();

View File

@ -7,7 +7,6 @@
void loader_cleanup(void);
void loader_display_logo(void);
void loader_display_message(const char *message);
void loader_display_error_and_halt(menu_load_error_t error, const char *message);

View File

@ -146,6 +146,9 @@ typedef struct sc64_sd_registers_s {
#define SC64_SD_CMD_INDEX(i) ((i) & 0x3F)
#define SC64_SD_DAT_WRITE_OK (1 << 28)
#define SC64_SD_DAT_WRITE_ERROR (1 << 27)
#define SC64_SD_DAT_WRITE_BUSY (1 << 26)
#define SC64_SD_DAT_TX_FIFO_ITEMS_GET(dat) (((dat) >> 17) & 0x1FF)
#define SC64_SD_DAT_TX_FIFO_BYTES_GET(dat) (SC64_SD_DAT_TX_FIFO_ITEMS_GET(dat) * 4)
#define SC64_SD_DAT_TX_FIFO_FULL (1 << 16)
@ -184,6 +187,7 @@ typedef struct sc64_sd_registers_s {
#define SC64_SD_DMA_BANK_ADDR(b, a) ((((b) & 0xF) << 28) | ((a) & 0x3FFFFFC))
#define SC64_SD_DMA_LEN_GET(len) (((len) & 0x7FFF) * 4)
#define SC64_SD_DMA_LEN(l) ((((l) / 4) - 1) & 0x7FFF)

View File

@ -157,20 +157,24 @@ static sc64_sd_err_t sc64_sd_cmd_send(uint8_t index, uint32_t arg, sc64_sd_cmd_f
}
static void sc64_sd_dat_prepare(size_t num_blocks, size_t block_size, sc64_sd_dat_direction_t direction) {
platform_pi_io_write(&SC64_SD->DAT, (
uint32_t reg = (
SC64_SD_DAT_NUM_BLOCKS(num_blocks) |
SC64_SD_DAT_BLOCK_SIZE(block_size) |
((direction == DAT_DIR_TX) ? SC64_SD_DAT_DIRECTION : 0) |
SC64_SD_DAT_START
));
);
platform_pi_io_write(&SC64_SD->DAT, reg);
}
static void sc64_sd_dat_abort(void) {
platform_pi_io_write(&SC64_SD->DAT, (
uint32_t reg = (
SC64_SD_DAT_TX_FIFO_FLUSH |
SC64_SD_DAT_RX_FIFO_FLUSH |
SC64_SD_DAT_STOP
));
);
platform_pi_io_write(&SC64_SD->DAT, reg);
}
static sc64_sd_err_t sc64_sd_dat_read(size_t block_size, void *buffer) {
@ -212,7 +216,7 @@ static sc64_sd_err_t sc64_sd_dat_read(size_t block_size, void *buffer) {
static sc64_sd_err_t sc64_sd_dat_write(size_t block_size, void *buffer) {
int timeout;
uint32_t reg;
timeout = 1000000;
do {
reg = platform_pi_io_read(&SC64_SD->DAT);
@ -221,11 +225,21 @@ static sc64_sd_err_t sc64_sd_dat_write(size_t block_size, void *buffer) {
}
} while ((reg & SC64_SD_DAT_BUSY) && (--timeout));
if (timeout == 0) {
sc64_sd_dat_abort();
return E_TIMEOUT;
}
if (!(reg & SC64_SD_DAT_BUSY)) {
if (reg & SC64_SD_DAT_CRC_ERROR) {
return E_CRC_ERROR;
}
if (reg & SC64_SD_DAT_WRITE_ERROR) {
return E_WRITE_ERROR;
}
if (reg & SC64_SD_DAT_TX_FIFO_UNDERRUN) {
platform_pi_io_write(&SC64_SD->DAT, SC64_SD_DAT_TX_FIFO_FLUSH);
@ -233,14 +247,8 @@ static sc64_sd_err_t sc64_sd_dat_write(size_t block_size, void *buffer) {
}
}
if (timeout == 0) {
sc64_sd_dat_abort();
return E_TIMEOUT;
}
platform_cache_writeback(buffer, block_size);
platform_pi_dma_write(buffer, &SC64_SD->FIFO, block_size);
platform_cache_invalidate(buffer, block_size);
return E_OK;
}
@ -420,8 +428,11 @@ sc64_sd_err_t sc64_sd_sectors_read(uint32_t starting_sector, size_t count, uint8
sc64_sd_err_t sc64_sd_sectors_write(uint32_t starting_sector, size_t count, uint8_t *buffer) {
sc64_sd_err_t error;
sc64_sd_err_t write_error;
uint32_t response;
uint32_t current_sector;
size_t sectors_remaining;
size_t num_blocks;
error = sc64_sd_sectors_parameters_check(count, buffer, true);
if (error != E_OK) {
@ -433,23 +444,51 @@ sc64_sd_err_t sc64_sd_sectors_write(uint32_t starting_sector, size_t count, uint
current_sector *= SD_BLOCK_SIZE;
}
for (size_t i = 0; i < count; i++) {
sc64_sd_dat_prepare(1, SD_BLOCK_SIZE, DAT_DIR_TX);
sectors_remaining = count;
error = sc64_sd_cmd_send(24, current_sector, NO_FLAGS, &response);
while (sectors_remaining) {
num_blocks = (sectors_remaining > SC64_SD_DAT_NUM_BLOCKS_MAX) ? SC64_SD_DAT_NUM_BLOCKS_MAX : sectors_remaining;
sc64_sd_dat_prepare(num_blocks, SD_BLOCK_SIZE, DAT_DIR_TX);
error = sc64_sd_cmd_send(25, current_sector, NO_FLAGS, &response);
if (error != E_OK) {
sc64_sd_dat_abort();
return error;
}
error = sc64_sd_dat_write(SD_BLOCK_SIZE, buffer);
for (size_t i = 0; i < num_blocks; i++) {
write_error = sc64_sd_dat_write(SD_BLOCK_SIZE, buffer);
if (write_error != E_OK) {
break;
}
buffer += SD_BLOCK_SIZE;
}
error = sc64_sd_dat_busy_wait();
if (error != E_OK) {
sc64_sd_dat_abort();
return error;
}
error = sc64_sd_cmd_send(12, 0, NO_FLAGS, &response);
if (error != E_OK) {
return error;
}
buffer += SD_BLOCK_SIZE;
current_sector += sd_card_type_block ? 1 : SD_BLOCK_SIZE;
error = sc64_sd_dat_busy_wait();
if (error != E_OK) {
return error;
}
if (write_error != E_OK) {
return write_error;
}
current_sector += num_blocks * (sd_card_type_block ? 1 : SD_BLOCK_SIZE);
sectors_remaining -= num_blocks;
}
return E_OK;
@ -534,79 +573,95 @@ sc64_sd_err_t sc64_sd_sectors_read_dma(uint32_t starting_sector, size_t count, u
return E_OK;
}
sc64_sd_err_t sc64_sd_sectors_write_dma(uint32_t starting_sector, size_t count, uint8_t bank, uint32_t address) {
size_t sectors_left;
uint32_t current_sector;
uint32_t current_address;
uint32_t num_blocks;
uint32_t reg;
sc64_sd_err_t error;
uint32_t response;
// sc64_sd_err_t sc64_sd_sectors_write_dma(uint32_t starting_sector, size_t count, uint8_t bank, uint32_t address) {
// size_t sectors_left;
// uint32_t current_sector;
// uint32_t current_address;
// uint32_t num_blocks;
// uint32_t reg;
// sc64_sd_err_t error;
// uint32_t response;
// int timeout;
// error = sc64_sd_sectors_parameters_check(count, NULL, false);
// if (error != E_OK) {
// return error;
// }
// sectors_left = count;
// current_sector = starting_sector;
// if (!sd_card_type_block) {
// current_sector *= SD_BLOCK_SIZE;
// }
// current_address = address;
// do {
// num_blocks = (sectors_left > SC64_SD_DAT_NUM_BLOCKS_MAX) ? SC64_SD_DAT_NUM_BLOCKS_MAX : sectors_left;
// sc64_sd_dat_prepare(num_blocks, SD_BLOCK_SIZE, DAT_DIR_TX);
// sc64_sd_dma_prepare(num_blocks, SD_BLOCK_SIZE, DMA_DIR_CARD, bank, current_address);
// error = sc64_sd_cmd_send(25, current_sector, NO_FLAGS, &response);
// if (error != E_OK) {
// sc64_sd_dma_abort();
// sc64_sd_dat_abort();
// return error;
// }
// timeout = 1000000;
// do {
// reg = platform_pi_io_read(&SC64_SD->DAT);
// } while ((reg & SC64_SD_DAT_BUSY) && (--timeout));
// error = sc64_sd_cmd_send(12, 0, NO_FLAGS, &response);
// if (error != E_OK) {
// sc64_sd_dma_abort();
// sc64_sd_dat_abort();
// return error;
// }
// if (reg & SC64_SD_DAT_CRC_ERROR) {
// sc64_sd_dma_abort();
// return E_CRC_ERROR;
// }
// if (reg & SC64_SD_DAT_TX_FIFO_UNDERRUN) {
// sc64_sd_dma_abort();
// platform_pi_io_write(&SC64_SD->DAT, SC64_SD_DAT_TX_FIFO_FLUSH);
// return E_FIFO_ERROR;
// }
// if (timeout == 0) {
// sc64_sd_dma_abort();
// sc64_sd_dat_abort();
// return E_TIMEOUT;
// }
// sectors_left -= num_blocks;
// current_sector += num_blocks * (sd_card_type_block ? 1 : SD_BLOCK_SIZE);
// current_address += num_blocks * SD_BLOCK_SIZE;
// } while (sectors_left > 0);
// return E_OK;
// }
sc64_sd_err_t sc64_sd_dat_busy_wait(void) {
int timeout;
uint32_t reg;
error = sc64_sd_sectors_parameters_check(count, NULL, false);
if (error != E_OK) {
return error;
}
sectors_left = count;
current_sector = starting_sector;
if (!sd_card_type_block) {
current_sector *= SD_BLOCK_SIZE;
}
current_address = address;
timeout = 1000000;
do {
num_blocks = (sectors_left > SC64_SD_DAT_NUM_BLOCKS_MAX) ? SC64_SD_DAT_NUM_BLOCKS_MAX : sectors_left;
reg = platform_pi_io_read(&SC64_SD->DAT);
} while ((reg & (SC64_SD_DAT_WRITE_BUSY | SC64_SD_DAT_BUSY)) && (--timeout));
sc64_sd_dat_prepare(num_blocks, SD_BLOCK_SIZE, DAT_DIR_TX);
sc64_sd_dma_prepare(num_blocks, SD_BLOCK_SIZE, DMA_DIR_CARD, bank, current_address);
error = sc64_sd_cmd_send(25, current_sector, NO_FLAGS, &response);
if (error != E_OK) {
sc64_sd_dma_abort();
sc64_sd_dat_abort();
return error;
}
timeout = 1000000;
do {
reg = platform_pi_io_read(&SC64_SD->DAT);
} while ((reg & SC64_SD_DAT_BUSY) && (--timeout));
error = sc64_sd_cmd_send(12, 0, NO_FLAGS, &response);
if (error != E_OK) {
sc64_sd_dma_abort();
sc64_sd_dat_abort();
return error;
}
if (reg & SC64_SD_DAT_CRC_ERROR) {
sc64_sd_dma_abort();
return E_CRC_ERROR;
}
if (reg & SC64_SD_DAT_TX_FIFO_UNDERRUN) {
sc64_sd_dma_abort();
platform_pi_io_write(&SC64_SD->DAT, SC64_SD_DAT_TX_FIFO_FLUSH);
return E_FIFO_ERROR;
}
if (timeout == 0) {
sc64_sd_dma_abort();
sc64_sd_dat_abort();
return E_TIMEOUT;
}
sectors_left -= num_blocks;
current_sector += num_blocks * (sd_card_type_block ? 1 : SD_BLOCK_SIZE);
current_address += num_blocks * SD_BLOCK_SIZE;
} while (sectors_left > 0);
if (timeout == 0) {
return E_TIMEOUT;
}
return E_OK;
}

View File

@ -12,6 +12,7 @@ typedef enum sc64_sd_err_e {
E_BAD_INDEX,
E_PAR_ERROR,
E_FIFO_ERROR,
E_WRITE_ERROR,
E_NO_INIT,
} sc64_sd_err_t;
@ -22,7 +23,8 @@ bool sc64_sd_status_get(void);
sc64_sd_err_t sc64_sd_sectors_read(uint32_t starting_sector, size_t count, uint8_t *buffer);
sc64_sd_err_t sc64_sd_sectors_write(uint32_t starting_sector, size_t count, uint8_t *buffer);
sc64_sd_err_t sc64_sd_sectors_read_dma(uint32_t starting_sector, size_t count, uint8_t bank, uint32_t address);
sc64_sd_err_t sc64_sd_sectors_write_dma(uint32_t starting_sector, size_t count, uint8_t bank, uint32_t address);
// sc64_sd_err_t sc64_sd_sectors_write_dma(uint32_t starting_sector, size_t count, uint8_t bank, uint32_t address);
sc64_sd_err_t sc64_sd_dat_busy_wait(void);
#endif

View File

@ -12,6 +12,8 @@
static uint8_t current_bank = SC64_BANK_INVALID;
static uint32_t current_offset = 0;
static uint8_t save_buffer[128 * 1024] __attribute__((aligned(16)));
static bool fs_initialized = false;
static FATFS fatfs;
static DRESULT sc64_sd_fs_load_with_dma(BYTE pdrv, FSIZE_t offset, LBA_t sector, UINT count) {
@ -38,13 +40,9 @@ static DRESULT sc64_sd_fs_load_with_dma(BYTE pdrv, FSIZE_t offset, LBA_t sector,
}
static bool fs_initialized = false;
static FATFS fatfs;
sc64_sd_fs_error_t sc64_sd_fs_init(void) {
FRESULT fresult;
fresult = f_mount(&fatfs, "", 1);
if (fresult != FR_OK) {
switch (fresult) {