Problem reading from SD card directly into PSRAM

Status
Not open for further replies.
You need to test any driver mod with files of at least 8 GiB on the popular SD cards from Samsung, Sandisk and the new Kingston Canvas Plus. I also test on older cards. You need to use a variety of write patterns then test read. Modern cards change read timing, buffering and cache use depending on how the card was was written.

The Kingston Canvas cards mark regions by the type access that is expected, that's why this card is popular with Raspberry PI users, random read access to small OS files is good.

I will soon be releasing a new version of the Teensy driver that makes major changes to the SD access pattern. It allows overlap of sending to the SD card with program execution by filling the SDIO FIFO and returning while the data is sent to the SD.

These new cards pull lots of current when they program flash since pages are extremely large on big cards, The SD spec now allows Record Units to be up to 512 KiB so I now test on 256GB and soon 512GB cards.
 
Last edited:
Could you tell me what driver strength mods you think would work and I will include them in my tests. I have a number of Tennsy 4.1 and 3.6 boards running tests on my new Teensy driver mods.

I am in the process of adding new SDIO drivers to SdFat so I will be going to standard base class for SDIO drivers.
 
For 4.1, please use 7. 6 also works in my test but 7 is probably safer.

I don't know if any change is needed for 3.6.
 
I am testing with 7 for the soon, I hope, release of the next beta. I have had lots of setbacks with fixes for cards that are very popular.

I am feeling good about mods to the Teensy driver. On Teensy 4.1 busy test plus overlap of SDIO bus activity with program execution means this statement executes in no more than 11 usec.
Code:
  // The first sector puts the controller in write mode and takes about
  // 11 usec on a Teensy 4.1. About 5 usec is required to write a sector
  // when the controller is in write mode.
  if (!file.isBusy()) {
    if (512 != file.write(buf, 512)) {
      // Handle write error.
    }
  }
This allows simple high speed data loggers with use of a new ring buffer class in the beta.
 
I set the drive strength to 7 and looked at the SD CLK signal with a SD sniffer board. It is really being driven. The noise I made with scope probes only caused a data error once.

I suspect the error occurred when I probed the CMD line and the error appeared in the command response when Teensy was reading, not driving CMD.

I tested with lots of SD cards and all seems OK.
 
With this edit in ...\hardware\teensy\avr\libraries\SdFat\src\SdCard\SdioTeensy.cpp:
Code:
static void enableGPIO(bool enable) {
  const uint32_t CLOCK_MASK = IOMUXC_SW_PAD_CTL_PAD_PKE |
[B]#if defined(ARDUINO_TEENSY41) || defined(ARDUINO_TEENSY40) 
                              IOMUXC_SW_PAD_CTL_PAD_DSE(7) |[/B]

I ran code below on a T_4.1 and T_4.0 ( using TallDog PCB to present SDIO to SD card - with no PSRAM - defaults to chip RAM ).

SD card is New 32GB 'Team'. Added Timing and throughput notes to prior code:
Code:
running variant 1
reads succeeded 2299501 bytes in 100268 us at 22.933548 MB/sec
1: SD -> PSRAM
2: SD -> ITCM
running variant 2
reads succeeded 2299501 bytes in 100266 us at 22.934006 MB/sec

<edit> : one edit to the code is enter 'a' to repeat the following 'variant' 10 times in a row > like "a1"

Edit of prior code that showed failure with lower DSE() on PSRAM buffer read: : Using TD 1.54 b5 with SD.h mapped to Sd Fat
Code:
#include <SD.h>
#include <SPI.h>

// https://forum.pjrc.com/threads/65851-Problem-reading-from-SD-card-directly-into-PSRAM?p=266951&viewfull=1#post266951

#if defined(ARDUINO_TEENSY41)
#define BUFSIZE (256*1024)
#elif defined(ARDUINO_TEENSY40)
#define BUFSIZE (200*1024)
#endif
#define FILENAME "TESTFILE.BIN"

File file;
char itcmBuf[BUFSIZE];
EXTMEM char psramBuf[BUFSIZE];

bool runTest(int variant) {
	Serial.print("running variant ");
	Serial.println(variant);
	file = SD.open(FILENAME);
	if (!file) {
		Serial.println("file open failed");
		return false;
	}
	int total_bytes_read = 0;
	uint32_t rTime = micros();
	while (file.available()) {
		int bytes_read;
		if (variant == 1) {
			bytes_read = file.read(psramBuf, BUFSIZE);
		} else {
			bytes_read = file.read(itcmBuf, BUFSIZE);
		}

		if (bytes_read < 0) {
			Serial.print("read failed after ");
			Serial.print(total_bytes_read);
			Serial.println(" bytes");
			file.close();
			return false;
		} else {
			//Serial.println("read ok");
			total_bytes_read += bytes_read;
		}

		if (variant == 3 || variant == 4) {
			memcpy(psramBuf, itcmBuf, BUFSIZE);
			if (variant == 4) {
				arm_dcache_flush_delete(psramBuf, BUFSIZE);
			}
		}
	}
	rTime = micros() - rTime;
	Serial.printf("reads succeeded %lu bytes in %lu us at %f MB/sec\n", total_bytes_read, rTime, (float)total_bytes_read/rTime );
	file.close();
	return true;
}

void setup() {
	Serial.begin(115200);
	while (!Serial);
	Serial.println("\n" __FILE__ " " __DATE__ " " __TIME__);

	if (!SD.begin(BUILTIN_SDCARD)) {
		Serial.println("Card failed, or not present");
		// don't do anything more:
		return;
	}
	Serial.println("card initialized.");

	// stress test
#if 0
	uint32_t lCnt = 0;
	while (runTest(1)) {
		lCnt++;
		if ( !(lCnt % 10) ) Serial.printf("\t%lu\t", lCnt );
	}
#endif
}

void loop() {
	int repeats = 1;
	// put your main code here, to run repeatedly:
	Serial.println("Select test variant");
	Serial.println("1: SD -> PSRAM");
	Serial.println("2: SD -> ITCM");
	Serial.println("3: SD -> ITCM, ITCM -> PSRAM");
	Serial.println("4: SD -> ITCM, ITCM -> PSRAM with cache flush");
	int variant = 0;
	while (variant == 0) {
		while (!Serial.available());
		char c = Serial.read();
		if (c == 'a') {
			repeats = 10;
		}
		if (c >= '1' && c <= '4') {
			variant = c - '0';
		}
	}
	while ( repeats > 0 ) {
		runTest(variant);
		repeats--;
	}
}
 
Last edited:
@Bill Greiman - is there a feel for the coincidence of the 2.0.5 b1 timeline being weeks away from release that might coincide with TD 1.54 for use as SD.h ? ... both of course being unknown? Or is the new sdFat beta known to be farther out from release?

Added a quick variant #5/6 to the prior posted code where #5 just adds 5MB to the file and #6 removes the file then writes 5MB writing from PSRAM when available.
Here on a T_4.1 with PSRAM:
Code:
running Write variant 5
write succeeded 5242880 bytes in 270332 us at 19.394226 MB/sec
running Write variant 5
write succeeded 5242880 bytes in 270351 us at 19.392864 MB/sec
running Write variant 5
write succeeded 5242880 bytes in 274697 us at 19.086048 MB/sec
running Write variant 5
write succeeded 5242880 bytes in 272272 us at 19.256039 MB/sec
running Write variant 5
write succeeded 5242880 bytes in 445823 us at 11.760003 MB/sec
running Write variant 5
write succeeded 5242880 bytes in 508780 us at 10.304808 MB/sec
running Write variant 5
write succeeded 5242880 bytes in 522286 us at 10.038331 MB/sec
running Write variant 5
write succeeded 5242880 bytes in 367491 us at 14.266689 MB/sec
running Write variant 5
write succeeded 5242880 bytes in 380814 us at 13.767561 MB/sec
running Write variant 5
write succeeded 5242880 bytes in 390015 us at 13.442765 MB/sec
Select test variant
1: SD -> PSRAM
2: SD -> ITCM
3: SD -> ITCM, ITCM -> PSRAM
4: SD -> ITCM, ITCM -> PSRAM with cache flush
5: WRITE PSRAM -> SD
6: delete> WRITE PSRAM -> SD
running variant 1
reads succeeded 57671680 bytes in 2511907 us at 22.959322 MB/sec
 
Release of SdFat is not related to Teensy. I like playing with Teensy but the overwhelming number of users are on other boards. Most of the mods in 2.0.5 will be for other systems.

I will be adding more support for STM32, STM32 is becoming more popular since ST now has an official Arduino board support package that supports many STM32 chips. I hate trying to find problems in STM32Cube it is the typical big company corporate army built software.

I mainly use STM32 for my projects but using the native ChibiOS RTOS, not Arduino. ChibiOS has great STM32 driver support for the multi-threaded RTOS environment.
 
Release of SdFat is not related to Teensy. ...

Thought so - but had to ask if is was near done and might line up with TD 1.54 now it is incorporated for awesome improvement - those write rates in P#33 are better than the old SD read rates.

I've had the SdFat Beta here and seeing it release to 2.0 and go into SD.h was a very nice change.

Seems PJRC will need to fork a copy to make Teensy specific changes like the DSE(7)
 
Thought so - but had to ask if is was near done and might line up with TD 1.54 now it is incorporated for awesome improvement - those write rates in P#33 are better than the old SD read rates.

I've had the SdFat Beta here and seeing it release to 2.0 and go into SD.h was a very nice change.

Seems PJRC will need to fork a copy to make Teensy specific changes like the DSE(7)

@Paul - this is the PSRAM issue that exists in SdFat v2.0 that had a fix pushed into new SdFatBeta V2.0.5

V2.0.5 :: github.com/greiman/SdFat-beta/blob/master/src/SdCard/SdioTeensy.cpp#L320

versus:

V2.0 :: github.com/greiman/SdFat/blob/master/src/SdCard/SdioTeensy.cpp#L287
 
Ok, I'm pulling 2.0.5 into 1.54-beta6...

Your call - wasn't sure if you'd fork the V_2.0 and do a PJRC edit for just that issue?

No idea what changes from V_2.0 to Beta 2.0.5 so far other than that DSE() edit? But of course the Beta has this obligatory status note:
"Warning: This version, 2.0.5-beta.1, has many internal changes so may be unstable."
 
Tested this to work with TD 1.54 Beta 6.

Using this updated code with more variants and 'a' 10X repeat:
Code:
#include <SD.h>
#include <SPI.h>

// https://forum.pjrc.com/threads/65851-Problem-reading-from-SD-card-directly-into-PSRAM?p=266951&viewfull=1#post266951

#if defined(ARDUINO_TEENSY41)
#define BUFSIZE (256*1024)
#elif defined(ARDUINO_TEENSY40)
#define BUFSIZE (200*1024)
#endif
#define FILENAME "TESTFILE.BIN"
#define FILENAME2 "TESTFILE.BIN"

File file;
char itcmBuf[BUFSIZE];
EXTMEM char psramBuf[BUFSIZE];

bool runTest(int variant) {
	Serial.print("running variant ");
	Serial.println(variant);
	file = SD.open(FILENAME);
	if (!file) {
		Serial.println("file open failed");
		return false;
	}
	int total_bytes_read = 0;
	uint32_t rTime = micros();
	while (file.available()) {
		int bytes_read;
		if (variant == 1) {
			bytes_read = file.read(psramBuf, BUFSIZE);
		} else {
			bytes_read = file.read(itcmBuf, BUFSIZE);
		}

		if (bytes_read < 0) {
			Serial.print("read failed after ");
			Serial.print(total_bytes_read);
			Serial.println(" bytes");
			file.close();
			return false;
		} else {
			//Serial.println("read ok");
			total_bytes_read += bytes_read;
		}

		if (variant == 3 || variant == 4) {
			memcpy(psramBuf, itcmBuf, BUFSIZE);
			if (variant == 4) {
				arm_dcache_flush_delete(psramBuf, BUFSIZE);
			}
		}
	}
	rTime = micros() - rTime;
	Serial.printf("reads succeeded %lu bytes in %lu us at %f MB/sec\n", total_bytes_read, rTime, (float)total_bytes_read / rTime );
	file.close();
	return true;
}

bool writeTest(int variant) {
	Serial.print("running Write variant ");
	Serial.println(variant);
	if (variant == 5) {
		file = SD.open(FILENAME2, FILE_WRITE);
	}
	else {
		if (SD.exists(FILENAME2))
			SD.remove(FILENAME2);
		file = SD.open(FILENAME2, FILE_WRITE);
	}

	if (!file) {
		Serial.println("file open failed");
		return false;
	}
	int total_bytes_written = 0;
	int bytes2Write = 5 * 1024 * 1024;
	uint32_t rTime = micros();
	while (total_bytes_written < bytes2Write) {
		int bytes_read;
		if (variant == 5) {
			bytes_read = file.write(psramBuf, BUFSIZE);
		} else {
			bytes_read = file.write(itcmBuf, BUFSIZE);
		}

		if (bytes_read < 0) {
			Serial.print("write failed after ");
			Serial.print(total_bytes_written);
			Serial.println(" bytes");
			file.close();
			return false;
		} else {
			//Serial.println("read ok");
			total_bytes_written += bytes_read;
		}

		if (variant == 3 || variant == 4) {
			memcpy(psramBuf, itcmBuf, BUFSIZE);
			if (variant == 4) {
				arm_dcache_flush_delete(psramBuf, BUFSIZE);
			}
		}
	}
	rTime = micros() - rTime;
	Serial.printf("write succeeded %lu bytes in %lu us at %f MB/sec\n", total_bytes_written, rTime, (float)total_bytes_written / rTime );
	file.close();
	return true;
}

void setup() {
	Serial.begin(115200);
	while (!Serial);
	Serial.println("\n" __FILE__ " " __DATE__ " " __TIME__);

	if (!SD.begin(BUILTIN_SDCARD)) {
		Serial.println("Card failed, or not present");
		// don't do anything more:
		return;
	}
	Serial.println("card initialized.");

	// stress test
#if 0
	uint32_t lCnt = 0;
	while (runTest(1)) {
		lCnt++;
		if ( !(lCnt % 10) ) Serial.printf("\t%lu\t", lCnt );
	}
#endif
}

void loop() {
	int repeats = 1;
	// put your main code here, to run repeatedly:
	Serial.println("Select test variant ('a#' repeats 10 times)");
	Serial.println("1: SD -> PSRAM");
	Serial.println("2: SD -> ITCM");
	Serial.println("3: SD -> ITCM, ITCM -> PSRAM");
	Serial.println("4: SD -> ITCM, ITCM -> PSRAM with cache flush");
	Serial.println("5: WRITE PSRAM -> SD");
	Serial.println("6: delete> WRITE PSRAM -> SD");
	int variant = 0;
	while (variant == 0) {
		while (!Serial.available());
		char c = Serial.read();
		if (c == 'a') {
			repeats = 10;
		}
		if (c >= '1' && c <= '6') {
			variant = c - '0';
		}
	}
	while ( repeats > 0 ) {
		if ( variant <= 4 )
			runTest(variant);
		else
			writeTest(variant);
		repeats--;
	}
}
 
Status
Not open for further replies.
Back
Top