29
\$\begingroup\$

I'm using a STM32L432 microcontroller to read data from a microSD card via SPI. In my application, I'm playing a sound file with a length of 10 s in loop.

I found out that after the application ran for approximately 20 - 25 hours, the audio stream gets heavily interrupted by noticeable gaps that occur multiple times per second. This can be observed on 10% of all microSD cards that I tested. It occurs for SanDisk, Kingston and all other brands that I tested. Stopping the application and letting the microSD card rest for some days does not solve the problem. It seems that the flash section of the microSD cards that stores the sound file got deteriorated. Reformatting the microSD card fixes it, probably because the sound file gets stored on another flash section.

A further investigation yielded that the gaps in the audio stream occur because the microSD card takes too long to respond to the CMD17 command. When the microSD card is new, the responds take never longer than 4 ms in our tests. However, after a usage of 20 - 25 hours, the respond time increases to up to 95 ms on 10% of all microSD cards.

I'm a bit confused of this result because I did not expect the performance of the microSD cards to deteriorate if only read operations were carried out. Do you have any experience with that? Would the response time decrease when using SDIO instead?

\$\endgroup\$
9
  • \$\begingroup\$ I wonder if stackoverflow might be better place to ask? \$\endgroup\$ – relayman357 Dec 20 '20 at 13:11
  • 2
    \$\begingroup\$ @XCSource Don't answer in the comment section. \$\endgroup\$ – pipe Dec 21 '20 at 9:53
  • 1
    \$\begingroup\$ This isn't offered as an answer but as an observation or even a question for OP. Bunnie Huang at bunniestudios.com/blog/?p=918 observes that there's a substantial trade in counterfeit/sub-spec memory cards, and elsewhere I've seen that it's possible to wring about 25% extra storage from a card. Are the failing cards from traceable sources and guaranteed to be full spec? \$\endgroup\$ – Mark Morgan Lloyd Dec 21 '20 at 11:47
  • \$\begingroup\$ There is not enough information here. Please show the exact code you use to read the audio chunks - without that we can only guess. My guess is that you read much less than block size. Unfortunately, many unrelated SD card families seem to use related firmware that handles this terribly. You should also perform asynchronous ahead-of-time reads, i.e. reading from the card should not block processing in your code, and you should be prepared to buffer 25-50ms of data to protect against long but infrequent latencies. I'm also not sure that you know what other commands are used behind your back. \$\endgroup\$ – Kuba hasn't forgotten Monica Dec 21 '20 at 14:58
  • 3
    \$\begingroup\$ @Nyos From my read of the question, there was no corruption in the stored data — the issue is that repeatedly reading the same 10s worth of audio data from the card over and over and over again caused the read performance to degrade, but without data loss. \$\endgroup\$ – FeRD Dec 21 '20 at 18:57
43
\$\begingroup\$

Hm, my usual suspicion would be that your read access goes through a file system and that updates something, e.g. a file access time, in the file system, but that doesn't seem to be the case here.

Next thing I'm guessing: You're experiencing read disturb, a phenomenon where reading the same cell over and over again charges neighboring memory cells ever so slightly every time, or the consequence of the flash controller avoiding it:

You're probably aware that between your SD card interface and the physical isolated gate charges in your NAND flash, there's multiple layers of abstraction:

  1. There's a higher-reliability table that maps "external" block addresses to internal ones, so to provide wear leveling, i.e. to avoid writing to a single block until it fails
  2. There's (in many cases) copious amounts of error-correcting codes (ECC): Flash memory is cheap. Can't make a billion nanometer-sized MOSFETs without having a couple of less reliable ones. Also, in case of multi-level memory, there's more than one bit stored in the gate charge's value, so there's also "soft-ish" boundary on what means what. You'll need to add an algorithm that takes the raw things stored in the flash cells, checks whether that looks correct, and if not, correct it. That's ECC's job, and typically, reading only "good" cells is much faster than correcting "partially wrong" data.
  3. Word sizes on the memory don't necessarily match word sizes on the external bus (SPI/MMC/xSD, whatever), so there's often some "getting more and ignoring all that wasn't asked for" or "getting multiple and assembling to what was asked for" operation.

and, here:

  1. To avoid charging neighboring cells through read disturb, the controller takes oft-read cells and redistributes them after a threshold number of reads. That redistribution takes time.

You reading the same cells with CMD17 (which will most certainly be very suboptimal in terms of 3., use CMD18 maybe?) very much sounds either you're corrupting their neighbors (thus leading to increased time in 2.) or leading to copious re-mapping of blocks (in 4.).

I'm a bit surprised: reading audio does sound like a low-throughput operation, compared to, say, reading camera photos, but if your access pattern is very inefficient, say, you read a byte using CMD17; the flash controller has to fetch a full 2048 B block¹, then correct it, throw away all but one byte of the result, give that byte back to the SPI interface, and you then read another byte, which requires the flash controller to read 2048 B, correct.. and so on, you might be doing a very high multiple of the actual read access you think you're doing! That's unfortunate, because audio reading is probably pretty sequential.

So, my recommendation is to write a cleverer SPI SD card interface that fetches long blocks of data, and caches these in MCU RAM. This

  • can fully eradicate problems with randomly varying latency, because you could start fetching the next large block as soon as the first is finished being fetched via SPI, so that you always have a lot of readahead buffer
  • makes better use of bus bandwidth
  • allows the flash controller to really only access raw memory cells the necessary amount of times
  • converts an operation that reads a small amount of data often to an operation that reads a large amount of data rarely, allowing a lot of things to return to sleep in between, which can help save battery

¹number taken from random guessing, but order of magnitude would fit the size of error correction block codes I'd expect on modern cheap NAND flash.

\$\endgroup\$
5
  • \$\begingroup\$ Weird in a sense though if it were to be that. Because with all the juggling the flash controller chip has to do, it wouldn't seem too bad to include a cache for those 2048 bytes, if not for readers reading one byte at a time, but just for readers reading blocks of 512 bytes at a time. Which I suppose might not be that uncommon given it's a traditionally common block size, but also because you might have microcontrollers that even can't read the full 2k at once. \$\endgroup\$ – ilkkachu Dec 22 '20 at 19:39
  • \$\begingroup\$ @ilkkachu fair point, but cost-effective microSD cards are made for billions of phones and cameras, not for the ca 1000 people who attach a microSD cards to a microcontroller. \$\endgroup\$ – Marcus Müller Dec 23 '20 at 9:13
  • \$\begingroup\$ yes, that they are. But even that bit of caching might help in reviews if someone were to do speed testing with what ended up using a suboptimal block size. Then again... who cares enough to review cheap SD cards... \$\endgroup\$ – ilkkachu Dec 23 '20 at 11:07
  • \$\begingroup\$ @MarcusMüller: Thank you very much for your answer. May I ask some follow-up questions? One of your recommendations is to align the number of read byte with the block size of the microSD card. As far as I know, the block size is 512 byte. However, that's already the number of byte that is being read by our application. After sending CMD17, we receive 512 byte and buffer them internally. After that, we proceed with reading another 512 byte to refill the buffer. If I understood you correctly, it would still be a benefit to extend the internal buffer and use CMD18 instead? \$\endgroup\$ – Hermetica Dec 23 '20 at 12:00
  • \$\begingroup\$ @MarcusMüller: And would using SDIO instead of SPI give any benefit, too? \$\endgroup\$ – Hermetica Dec 23 '20 at 12:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.