SD I/O rate question (Teensy 3.6)

Status
Not open for further replies.

cubic1271

New member
Hi,

I recently picked up a Teensy 3.6. I've been having a lot of fun playing with the thing :)

As part of the project I'm doing, I have a (possibly) unique use-case where I need an SD card more for buffer than for anything else - I need to be able to buffer for relatively long periods of time, then burst through a higher-rate connection (e.g. Ethernet) for a relatively brief time. Thus, I don't need a file system on the card - it's not there for anyone else to read, it's just there to be a buffer.

With this in mind, I took a look at KinetisSDHC.c (and glanced through the corresponding sample code it was based on). I wrapped the above into my own little library that lets me do raw I/O, since the interface (thus far) has been incredibly simple. I also wrote some very simple micro-benchmark code to read / write to a few blocks of my SD card, something like (one can substitute WriteBlock for ReadBlock when testing in the other direction):

Code:
    uint32_t start = micros();
    for(int i = 0; i < NUM_TEST_BLOCKS; ++i) {
        SDHC_ReadBlock(rbuf, TEST_BLOCK_OFFSET + i);
    }
    uint32_t end = start - micros();

What I found (with a 64 GB class 10) is that I was seeing read rates of about 4200 blocks / second, and write rates of around 1100 blocks / second. Given that each block is 512B, that works out to roughly 21.5 Mbps read and 5.6 Mbps write.

I was hoping to see higher numbers, so I went back to revisit the code a bit. I started poking through the KinetisSDHC.c code, and saw a call to SDHC_SetClock(SDHC_SYSCTL_25MHZ). I replaced that with: SDHC_SetClock(SDHC_SYSCTL_50MHZ), since I was reasonably certain the card I'm using can support it ... but this did not yield a noticeable change in the rates I observed.

My other thought is that the latency involved is somehow killing my transfer rates, but I haven't found a good example of a transfer that operates on more than one block at a time. I'll note that I'm also still studying what's in the ReadBlock / WriteBlock methods to understand how they work.

Technically, 20 / 5 should work just fine for what I need, but ... I'd still like to understand what I'm doing wrong, since it seems like others have reported much higher speeds with a real filesystem. Along these lines, I have ordered another SD card to see if that affects my results, but ... I figured I'd post this here while I wait for the shipping just to see if someone could tell me if the logic I was using above was incorrect and / or is somehow dumb.

In any event, thanks in advance for any thoughts / assistance / etc!
 
What are your numbers if you increase the block size to say 8 or 16 KIB?
write performance should increase.
 
Thanks for the reply!

I think the call is keyed to sectors, and I don't think it's safe to change the sector size (based on |1|). Is there a separate setting for block size? I'll admit that I find the terminology a little confusing, because it seems like the SDHC_ReadBlock call (which just is a thin wrapper for KinetisSDHC_ReadBlock()) will read one sector instead of one block.

So ... I guess my question here would be: how would I increase the amount of data I read at once, given the calls I'm using?

As a reference, code for KinetisSDHC_ReadBlock is included at |2|

|1| https://electronics.stackexchange.com/questions/227686/sd-card-sector-size
|2|
Code:
// read a block from disk
//   buff - pointer on buffer where read data should be stored
//   sector - index of start sector
int KinetisSDHC_ReadBlock(void * buff, uint32_t sector)
{
  int result;
  uint32_t* pData = (uint32_t*)buff;

  // Check if this is ready
  if (sdCardDesc.status != 0)
     return SDHC_RESULT_NOT_READY;

  // Convert LBA to uint8_t address if needed
  if (!sdCardDesc.highCapacity)
    sector *= 512;

  SDHC_IRQSTAT = 0xffff;

  // Just single block mode is needed
  result = SDHC_CMD17_ReadBlock(sector);
  if(result != SDHC_RESULT_OK) return result;
  result = SDHC_ReadBlock(pData);

  // finish up
  while (!(SDHC_IRQSTAT & SDHC_IRQSTAT_TC)) { }  // wait for transfer to complete
  SDHC_IRQSTAT = (SDHC_IRQSTAT_TC | SDHC_IRQSTAT_BRR | SDHC_IRQSTAT_AC12E);

  return result;
}
 
Despite what it says on StackOverflow, I might go ahead and try playing with the block size. I'm still a bit confused by the difference between "block" and "sector" in this context, though.

Anyway, I've been doing a bit of hacking today just because I want to see the blocking behavior of the KinetisSDHC read / write operations go away. Along these lines, I started searching to see where other folks had tried to do that, and found |1|.

This led me to |2| which I had missed when I was reading through the forums before. WMXZ - thank you for putting that up on github :) |2| also led me to |3|, which is an exceptionally useful reference.

At this point, I think I have non-blocking behavior that I'm happy with. I have a lot of random read / write and want to minimize data loss in the event of e.g. power loss, so ... keeping the block size small might make sense just to minimize read and write latency. That said, it's something I think I just need to play with a little bit to get a better feel for.

|1| https://forum.pjrc.com/threads/34808-K66-Beta-Test/page21
|2| https://github.com/WMXZ-EU/uSDFS
|3| http://elm-chan.org/docs/mmc/mmc_e.html#dataxfer
 
Best write performance requires using the mult-block write command in RU (Recording Unit) sized chunks. For large SD cards this is quite large. (RU is a multiple of 16KB)

When I test write speed the way I do it is to put a time stamp into the data buffer just before writing it and processing that data to see what is happening.. When I compared writing single 512B blocks to writing 8KB blocks, the write time was essentially the same for both. Both exhibited periodic stalls where it took much longer. Less than 2ms most of the time but sometimes between 10ms and 20ms. Add any file system overheads onto that.

SD cards seem to have always been this way as I first noticed it with the SparkFun Logomatic V1.
 
Status
Not open for further replies.
Back
Top