Teensy 3.6 SD busy yield()?

Status
Not open for further replies.

Bill Greiman

Well-known member
The first release of SdFat for SDHC on Teensy 3.6 will have lots of SD busy delays. I currently call yield() while the SD is busy or during DMA transfers.

Is yield() good enough or should I use another SD specific weak function?

Here is a program I am using to check SDHC performance and yield efficiency. It checks performance for read/write sizes from 512 bytes to 32 KiB and monitors busy time.

Code:
// Simple performance test for Teensy 3.5/3.6 SDHC.
// Demonstrates yield() efficiency.

#include "SdFat.h"

// 32 KiB buffer.
const size_t BUF_DIM = 32768;

// 8 MiB file.
const uint32_t FILE_SIZE = 256UL*BUF_DIM;

SdFatSdio sd;

File file;

uint8_t buf[BUF_DIM];

// buffer as uint32_t
uint32_t* buf32 = (uint32_t*)buf;

// Total usec in read/write calls.
uint32_t totalMicros = 0;
// Time in yield() function.
uint32_t yieldMicros = 0;
// Number of yield calls.
uint32_t yieldCalls = 0;
// Max busy time for single yield call.
uint32_t yieldMaxUsec = 0; 
//-----------------------------------------------------------------------------
// Replace "weak" system yield()function.
void yield() {
  // Only count cardBusy time.
  if (!sd.card()->dmaBusy()) {
    return;
 }
  uint32_t m = micros();
  yieldCalls++;
  while (sd.card()->dmaBusy()) {
    // Do something here.
  }
  m = micros() - m;
  if (m > yieldMaxUsec) {
    yieldMaxUsec = m;
  }
  yieldMicros += m;
}
//-----------------------------------------------------------------------------
void setup() {
  Serial.begin(9600);
  while (!Serial) {
  }
  Serial.println("Type any character to begin");
  while (!Serial.available()) {
  }
  if (!sd.begin()) {
    sd.initErrorHalt();
  }
  if (!file.open("TeensyDemo.bin", O_RDWR | O_CREAT)) {
    sd.errorHalt("open failed");
  }
  Serial.println("\nsize,write,read");
  Serial.println("bytes,KB/sec,KB/sec");
  for (size_t nb = 512; nb <= BUF_DIM; nb *= 2) {
    file.truncate(0);
    uint32_t nRdWr = FILE_SIZE/nb;
    Serial.print(nb);
    Serial.print(',');
    uint32_t t = micros();
    for (uint32_t n = 0; n < nRdWr; n++) {
      // Set start and end of buffer.
      buf32[0] = n;
      buf32[nb/4 - 1] = n;
      if (nb != file.write(buf, nb)) {
        sd.errorHalt("write failed");
      }
    }
    t = micros() - t;
    totalMicros += t;
    Serial.print(1000.0*FILE_SIZE/t);
    Serial.print(',');
    file.rewind();
    t = micros();
    
    for (uint32_t n = 0; n < nRdWr; n++) {
      if ((int)nb != file.read(buf, nb)) {
        sd.errorHalt("read failed");
      }
      // crude check of data.     
      if (buf32[0] != n || buf32[nb/4 - 1] != n) {
        sd.errorHalt("data check");
      }
    }
    t = micros() - t;
    totalMicros += t;   
    Serial.println(1000.0*FILE_SIZE/t);    
  }
  file.close();
  Serial.print("\ntotalMicros  ");
  Serial.println(totalMicros);
  Serial.print("yieldMicros  ");
  Serial.println(yieldMicros);
  Serial.print("yieldCalls   ");
  Serial.println(yieldCalls);
  Serial.print("yieldMaxUsec ");
  Serial.println(yieldMaxUsec);  
  Serial.println("Done");
}

void loop() {
}

Here is output from a test:
size,write,read
bytes,KB/sec,KB/sec
512,597.08,2198.12
1024,1139.13,3191.10
2048,2189.89,5146.60
4096,3729.06,7888.76
8192,4169.73,9330.20
16384,7411.91,13304.00
32768,12544.24,15121.20

totalMicros 42528552
yieldMicros 42183231
yieldCalls 78580
yieldMaxUsec 48156

So 99% of the time is spent in yield(). The max single busy period is about 48 ms.

The answer would be simple in an RTOS like ChibiOS. A context switch takes about a half microsecond on a 180 MHz cpu so you would just put the thread to sleep and wake it in sdhc_isr().
 
Can the SD busy time can be held up without consequence?

Calling yield() will defer to Serial I/O through SerialEvent that could take some time - and would just add that much longer to completion of the SD I/O without risking it?

Anything that makes using the built in SD on the T_3.5/3.6 easier.

Having a SDUserEvent() callback to perform other necessary processes could be very nice - quick display updates ... a gas gauge % complete? The user could call yield() - you could have a default weak SDyield() that just called yield and could be user replaced?

The user work around would be a timer interrupt - but that would not be best case 'co-operative' if it pulled away from your code just before you started/completed an operation.

All my sample testing with USB/Serial# I/O has been done in SerialEvent() and it works very well at high speed.

Made me think that the CORES yield() might do well to have a weak UserEvent() called after the SerialEvent()'s- this would make long running user tasks able to yield() and get called back - with of course any dangers of doing that up to the user.
<edit>: of course those users could clone the yield code and add their own calls.
 
Last edited:
Biil - what library does this link with:: github.com/greiman/SdFat-beta? I was going to try this as a test for my new T_3.6's and the github tree is laid out oddly for my libraries directory?

I get it so it finds SDFAT.h then I get:
K66_SDFAT_test:35: error: 'class SdioCard' has no member named 'dmaBusy'
if (!sd.card()->dmaBusy()) {
 
Biil - what library does this link with:: github.com/greiman/SdFat-beta? I was going to try this as a test for my new T_3.6's and the github tree is laid out oddly for my libraries directory?
I get it so it finds SDFAT.h then I get:

I think after you download the github stuff, you move the SdFat folder into you libraries/ folder
 
After copying ONLY the SdFat folder into my libraries/ folder, I got the TeensySdioDemo example working on (beta) T3.6
https://github.com/greiman/SdFat-beta/tree/master/SdFat/examples/TeensySdioDemo

I would like to get some ADC data-logging code working, but so far my attempts to convert LowLatencyLogger to use the SDIO interface https://forum.pjrc.com/threads/36737-Try-SdFat-forTeensy-3-5-3-6?p=116801&viewfull=1#post116801 have not met with success, just
Code:
error: writeStart failed
SD errorCode: 0X65,0X1
 
I think after you download the github stuff, you move the SdFat folder into you libraries/ folder

Indeed that is what I did - then SDFAT.h was found and I got dmaBusy error on that sample. Moved to sample TeensySdioDemo and got it to work - at first only one option #2 - then #1 started. But #2 worked to autostart in setup on a T_3.6 and insert HSRUN EEPROM reads during the yield callbacks. So the EEPROM WRITE HSRUN drop over 120 MHz didn't break anything and the test verify of the file data passed too. I used this to test SD and EEPROM all 6 on my KS T_3.6's. >> Try-SdFat-forTeensy-3-5-3-6

I did this on IDE 1.6.12.
 
Last edited:
Status
Not open for further replies.
Back
Top