Bug in SPI block transfer complete callback

The SPI DMA block transfer call back for transfer complete is based on DMA TX complete. The SPI interface may still be busy which may cause following single byte transfers to fail.

Here is part of a routine that writes a block to an SD card.

spiSend(src, 512);  // send data
spiSend(crc >> 8);  // send dummy crc byte 
spiSend(crc & 0XFF);  // send dummy crc byte 
m_status = spiReceive();  // get status token
chipSelectHigh();  // set chip select high and send byte to make MISO high-Z

This transfer works correctly if the 512 data bytes are sent byte at a time. Here is the end of the transfer. D0 is CS, D1 is SCK, D2 is MOSI, and D3 is MISO.

You can see a few clocks of the last data byte, two bytes of CRC, the return status and the dummy byte to release MISO.

If the 512 byte data transfer is done with a block transfer like this, The status read returns the wrong value.

static volatile bool SPI_DMA_TransferCompleted = false;

void SD_SPI_DMA_TransferComplete_Callback(void) {
    SPI_DMA_TransferCompleted = true;
}
void SdSpiLib::send(const uint8_t* buf , size_t n) {
  SPI_DMA_TransferCompleted = false;
  SPI.transfer((void *)buf, 0, n, SD_SPI_DMA_TransferComplete_Callback);
  while(!SPI_DMA_TransferCompleted);
}

The problem appears to be that Transfer complete callback happens too soon. The byte transfers seem to overlap with the DMA. SCK has no gaps as expected for single byte transfer.

Here is a scope picture.

If I add a bit of delay after the transfer complete call the SD write works correctly.

void SdSpiLib::send(const uint8_t* buf , size_t n) {
  SPI_DMA_TransferCompleted = false;
  SPI.transfer((void *)buf, 0, n, SD_SPI_DMA_TransferComplete_Callback);
  while(!SPI_DMA_TransferCompleted);
  delayMicroseconds(10);  // <<== ADDED DELAY
}

Here is the SPI trace after the delay.

You can see the 10 μs delay after the DMA followed by the single byte transfers.

Various SPI transfers fail depending on clock rate and program timing unless the delay is added. I suspect basing the SPI transfer done call on RX done would solve the problem.

1 Like

Issue added here - https://github.com/spark/firmware/issues/791

Thank you so much. Such a pleasure to see an engineer looking at user posts.

Love your hardware and the direction your firmware is taking.

1 Like

Thanks for your support! If I make a version of firmware that sends the callback on RX received, would you be willing to test with your current setup?

Yes, I would be happy to test it.

Note that this problem is most severe at low SCK rate since it takes a long time to shift out the last bytes.

Older SD cards should be initialized with a 100 KHz-400 KHz clock so clock rate can be very slow. After initialization, all cards support at least 25 MHz. I use 60/128 MHz for initialization.

The example scope traces were with a 3.75 or 60/16 MHz clock.