When using -O0 (no optimization), file writes perform as expected. However, when any level of optimization is used (-O1, -O2, or -O3) file writes of size 512 bytes or less are successful, but any larger size fails with FR_DISK_ERROR.
The issue seems to lie in fatfs_sd.c:
/* transmit data block */
#if _USE_WRITE == 1
static bool SD_TxDataBlock(const uint8_t *buff, BYTE token)
{
uint8_t resp;
uint8_t i = 0;
/* REST OF FUNCTION */
/* transmit 0x05 accepted */
if ((resp & 0x1F) == 0x05) return TRUE;
return FALSE;
}
resp is only updated in this function if the token to send isn't the stop token, which is sent once the write is complete. Stepping through with a debugger shows that everything works as normal in a block write up until this point. However, once the stop token is sent, the function relies on an external mechanism to set the value of resp/its memory address (I'm a bit unclear on where exactly this is done, but it must be occurring as otherwise the function would always return FALSE (a failure) when sending a stop token). GCC optimization recognizes this and so always returns FALSE when a stop token is sent.
I was able to successfully fix this by declaring resp as volatile:
/* transmit data block */
#if _USE_WRITE == 1
static bool SD_TxDataBlock(const uint8_t *buff, BYTE token)
{
volatile uint8_t resp;
uint8_t i = 0;
/* REST OF FUNCTION */
Hopefully this will help anyone else who happens to run into this issue. I can open a PR with the change as well, and perhaps someone might have some insight as to how the memory address of resp has the correct value for stop token transmission despite not being set in the function.
When using -O0 (no optimization), file writes perform as expected. However, when any level of optimization is used (-O1, -O2, or -O3) file writes of size 512 bytes or less are successful, but any larger size fails with
FR_DISK_ERROR.The issue seems to lie in
fatfs_sd.c:respis only updated in this function if the token to send isn't the stop token, which is sent once the write is complete. Stepping through with a debugger shows that everything works as normal in a block write up until this point. However, once the stop token is sent, the function relies on an external mechanism to set the value ofresp/its memory address (I'm a bit unclear on where exactly this is done, but it must be occurring as otherwise the function would always returnFALSE(a failure) when sending a stop token). GCC optimization recognizes this and so always returns FALSE when a stop token is sent.I was able to successfully fix this by declaring
respasvolatile:Hopefully this will help anyone else who happens to run into this issue. I can open a PR with the change as well, and perhaps someone might have some insight as to how the memory address of
resphas the correct value for stop token transmission despite not being set in the function.