Teensy 4.1 Beta Test

When you declare an array, that's right. When you want to read it with an AVR, you need to use pgm_read_xxx functions afaik.
 
@Frank B
Having a little issue with SPIFFS with the address change.

In the original SPIFF code we had a offset of 0x01000000u and now I think the new offset for FlashRam is 0x00800000. If I change the offset in the SPIFFs it no longer seems to be working. In otherwords this works:
Code:
static const void* extBase = (void*)0x70000000u;
static const uint32_t flashBaseAddr = 0x01000000u;
static uint32_t flashCapacity = 16u * 1024u * 1024u;
this doesn't work
Code:
static const void* extBase = (void*)0x70000000u;
static const uint32_t flashBaseAddr = 0x00800000u;
static uint32_t flashCapacity = 16u * 1024u * 1024u;
What am I missing?

EDIT: Maybe not missing anything. When I uncomment the a test to print the flash ID I only see the correct address if I use 0x01000000u. Anything else gives me all 0xFFs.

Here is the code (NOTE you have to change 0x2000 to 0x4000 otherwise it hangs:
Code:
	FLEXSPI2_FLSHA1CR0 = 0x4000; // 8 MByte
	FLEXSPI2_FLSHA1CR1 = FLEXSPI_FLSHCR1_CSINTERVAL(2)
		| FLEXSPI_FLSHCR1_TCSH(3) | FLEXSPI_FLSHCR1_TCSS(3);
	FLEXSPI2_FLSHA1CR2 = FLEXSPI_FLSHCR2_AWRSEQID(6) | FLEXSPI_FLSHCR2_AWRSEQNUM(0)
		| FLEXSPI_FLSHCR2_ARDSEQID(5) | FLEXSPI_FLSHCR2_ARDSEQNUM(0);

	FLEXSPI2_FLSHA2CR0 = 0x4000; // 8 MByte

Code:
/*
   This test uses the optional quad spi flash on Teensy 4.1
   https://github.com/pellepl/spiffs/wiki/Using-spiffs
   https://github.com/pellepl/spiffs/wiki/FAQ

   ATTENTION: Flash needs to be empty before first use of SPIFFS


   Frank B, 2020
*/


#include <spiffs.h>

static spiffs fs; //filesystem

char buf[512] = "Hello World! What a wonderful World :) Hello World! What a wonderful World :) Hello World! What a wonderful World :) Hello World! What a wonderful World :) Hello World! What a wonderful World :) Hello World! What a wonderful World :) Hello World! What a wonderful World :) Hello World! What a wonderful World :) Hello World! What a wonderful World :) Hello World! What a wonderful World :) Hello World! What a wonderful World :)";
int szLen = strlen( buf );

void test_spiffs_write() {
  // Surely, I've mounted spiffs before entering here
  spiffs_file fd = SPIFFS_open(&fs, "my_file", SPIFFS_CREAT | SPIFFS_TRUNC | SPIFFS_RDWR, 0);
  if (SPIFFS_write(&fs, fd, (u8_t *)buf, szLen) < 0) Serial.printf("errno %i\n", SPIFFS_errno(&fs));
  SPIFFS_close(&fs, fd);
  SPIFFS_fflush(&fs, fd);
}

static void test_spiffs_read() {
  // Surely, I've mounted spiffs before entering here
  spiffs_file  fd = SPIFFS_open(&fs, "my_file", SPIFFS_RDWR, 0);
  if (SPIFFS_read(&fs, fd, (u8_t *)buf, szLen) < 0) Serial.printf("errno %i\n", SPIFFS_errno(&fs));
  SPIFFS_close(&fs, fd);
}

static void test_spiffs_listDir() {
  spiffs_DIR d;
  struct spiffs_dirent e;
  struct spiffs_dirent *pe = &e;

  SPIFFS_opendir(&fs, "/", &d);
  while ((pe = SPIFFS_readdir(&d, pe))) {
    Serial.printf("%s [%04x] size:%i\n", pe->name, pe->obj_id, pe->size);
  }
  SPIFFS_closedir(&d);
}

void setup() {
  while (!Serial);

#if 1
  Serial.println("\n Enter 'y' in 6 seconds to format FlashChip - other to skip");
  uint32_t pauseS = millis();
  char chIn = 9;
  while ( pauseS + 6000 > millis() && 9 == chIn ) {
    if ( Serial.available() ) {
      do {
        if ( chIn != 'y' )
          chIn = Serial.read();
        else
          Serial.read();
      }
      while ( Serial.available() );
    }
  }
  if ( chIn == 'y' ) {
    eraseFlashChip();
  }
#endif

  Serial.println("Mount SPIFFS:");
  int res = my_spiffs_mount();
  Serial.printf("mount res: %i\n", res);

#if 1
  Serial.println("Write file:");
  Serial.println(buf);
  test_spiffs_write();
#endif

  Serial.println("Directoy contents:");
  test_spiffs_listDir();

  memset(buf, 0, sizeof(buf)); //emtpy buffer
  Serial.println("Read file:");
  test_spiffs_read();
  Serial.println(buf);

}

void loop() {
}

//********************************************************************************************************
//********************************************************************************************************
//********************************************************************************************************
/*
   QSPI Flash Interface
*/

#define LUT0(opcode, pads, operand) (FLEXSPI_LUT_INSTRUCTION((opcode), (pads), (operand)))
#define LUT1(opcode, pads, operand) (FLEXSPI_LUT_INSTRUCTION((opcode), (pads), (operand)) << 16)
#define CMD_SDR         FLEXSPI_LUT_OPCODE_CMD_SDR
#define ADDR_SDR        FLEXSPI_LUT_OPCODE_RADDR_SDR
#define READ_SDR        FLEXSPI_LUT_OPCODE_READ_SDR
#define WRITE_SDR       FLEXSPI_LUT_OPCODE_WRITE_SDR
#define DUMMY_SDR       FLEXSPI_LUT_OPCODE_DUMMY_SDR
#define PINS1           FLEXSPI_LUT_NUM_PADS_1
#define PINS4           FLEXSPI_LUT_NUM_PADS_4

#define FLASH_MEMMAP 1 //Use memory-mapped access

static const void* extBase = (void*)0x70000000u;
static const uint32_t flashBaseAddr = 0x01000000u;
static uint32_t flashCapacity = 16u * 1024u * 1024u;
static char flashID[8];

void setupFlexSPI2() {
  memset(flashID, 0, sizeof(flashID));


  // cmd index 7 = read ID bytes SPI
  FLEXSPI2_LUT28 = LUT0(CMD_SDR, PINS1, 0x9F) | LUT1(READ_SDR, PINS1, 1); //RAM, FLASH

  // ----------------- FLASH only ----------------------------------------------

  // cmd index 8 = read Status register #1 SPI
  FLEXSPI2_LUT32 = LUT0(CMD_SDR, PINS1, 0x05) | LUT1(READ_SDR, PINS1, 1);

  // cmd index 9 = read Status register #2 SPI
  FLEXSPI2_LUT36 = LUT0(CMD_SDR, PINS1, 0x35) | LUT1(READ_SDR, PINS1, 1);

  //cmd index 10 = exit QPI mode
  FLEXSPI2_LUT40 = LUT0(CMD_SDR, PINS4, 0xFF);

  //cmd index 11 = write enable QPI
  FLEXSPI2_LUT44 = LUT0(CMD_SDR, PINS4, 0x06);

  //cmd index 12 = sector erase
  FLEXSPI2_LUT48 = LUT0(CMD_SDR, PINS4, 0x20) | LUT1(ADDR_SDR, PINS4, 24);

  //cmd index 13 = page program
  FLEXSPI2_LUT52 = LUT0(CMD_SDR, PINS4, 0x02) | LUT1(ADDR_SDR, PINS4, 24);
  FLEXSPI2_LUT53 = LUT0(WRITE_SDR, PINS4, 1);

  //cmd index 14 = set read parameters
  FLEXSPI2_LUT56 = LUT0(CMD_SDR, PINS4, 0xc0) | LUT1(CMD_SDR, PINS4, 0x20);

  //cmd index 15 = enter QPI mode
  FLEXSPI2_LUT60 = LUT0(CMD_SDR, PINS1, 0x38);
}

void printStatusRegs() {
#if 0
  uint8_t val;

  flexspi_ip_read(8, flashBaseAddr, &val, 1 );
  Serial.print("Status 1:");
  Serial.printf(" %02X", val);
  Serial.printf("\n");

  // cmd index 9 = read Status register #2 SPI
  flexspi_ip_read(9, flashBaseAddr, &val, 1 );
  Serial.print("Status 2:");
  Serial.printf(" %02X", val);
  Serial.printf("\n");
#endif
}

/*
   Waits for busy bit = 0 (statusregister #1 )
   Timeout is optional
*/
bool waitFlash(uint32_t timeout = 0) {
  uint8_t val;
  uint32_t t = millis();
  FLEXSPI_IPRXFCR = FLEXSPI_IPRXFCR_CLRIPRXF; // clear rx fifo
  do {
    flexspi_ip_read(8, flashBaseAddr, &val, 1 );
    if (timeout && (millis() - t > timeout)) return 1;
  } while  ((val & 0x01) == 1);
  return 0;
}

void setupFlexSPI2Flash() {

  // reset the chip
  flexspi_ip_command(10, flashBaseAddr); //exit QPI
  flexspi_ip_command(1, flashBaseAddr); //reset enable
  flexspi_ip_command(2, flashBaseAddr); //reset
  delayMicroseconds(50);

  flexspi_ip_read(7, flashBaseAddr, flashID, sizeof(flashID) );

#if 1
  Serial.print("ID:");
  for (unsigned i = 0; i < sizeof(flashID); i++) Serial.printf(" %02X", flashID[i]);
  Serial.printf("\n");
#endif

  printStatusRegs();
  //TODO!!!!! set QPI enable bit in status reg #2 if not factory set!!!!!

  //  Serial.println("ENTER QPI MODE");
  flexspi_ip_command(15, flashBaseAddr);

  //patch LUT for QPI:
  // cmd index 8 = read Status register #1
  FLEXSPI2_LUT32 = LUT0(CMD_SDR, PINS4, 0x05) | LUT1(READ_SDR, PINS4, 1);
  // cmd index 9 = read Status register #2
  FLEXSPI2_LUT36 = LUT0(CMD_SDR, PINS4, 0x35) | LUT1(READ_SDR, PINS4, 1);

  flexspi_ip_command(14, flashBaseAddr);

  printStatusRegs();

}

void flexspi_ip_command(uint32_t index, uint32_t addr)
{
  uint32_t n;
  FLEXSPI2_IPCR0 = addr;
  FLEXSPI2_IPCR1 = FLEXSPI_IPCR1_ISEQID(index);
  FLEXSPI2_IPCMD = FLEXSPI_IPCMD_TRG;
  while (!((n = FLEXSPI2_INTR) & FLEXSPI_INTR_IPCMDDONE)); // wait
  if (n & FLEXSPI_INTR_IPCMDERR) {
    Serial.printf("Error: FLEXSPI2_IPRXFSTS=%08lX\n", FLEXSPI2_IPRXFSTS);
  }
  FLEXSPI2_INTR = FLEXSPI_INTR_IPCMDDONE;
}

void flexspi_ip_read(uint32_t index, uint32_t addr, void *data, uint32_t length)
{
  uint32_t n;
  uint8_t *p = (uint8_t *)data;
  const uint8_t *src;

  FLEXSPI2_IPCR0 = addr;
  FLEXSPI2_IPCR1 = FLEXSPI_IPCR1_ISEQID(index) | FLEXSPI_IPCR1_IDATSZ(length);
  FLEXSPI2_IPCMD = FLEXSPI_IPCMD_TRG;
  while (!((n = FLEXSPI2_INTR) & FLEXSPI_INTR_IPCMDDONE)) {
    if (n & FLEXSPI_INTR_IPRXWA) {
      //Serial.print("*");
      if (length >= 8) {
        length -= 8;
        *(uint32_t *)(p + 0) = FLEXSPI2_RFDR0;
        *(uint32_t *)(p + 4) = FLEXSPI2_RFDR1;
        p += 8;
      } else {
        src = (const uint8_t *)&FLEXSPI2_RFDR0;
        while (length > 0) {
          length--;
          *p++ = *src++;
        }
      }
      FLEXSPI2_INTR = FLEXSPI_INTR_IPRXWA;
    }
  }
  if (n & FLEXSPI_INTR_IPCMDERR) {
    Serial.printf("Error: FLEXSPI2_IPRXFSTS=%08lX\r\n", FLEXSPI2_IPRXFSTS);
  }
  FLEXSPI2_INTR = FLEXSPI_INTR_IPCMDDONE;
  //printf(" FLEXSPI2_RFDR0=%08lX\r\n", FLEXSPI2_RFDR0);
  //if (length > 4) Serial.printf(" FLEXSPI2_RFDR1=%08lX\n", FLEXSPI2_RFDR1);
  //if (length > 8) Serial.printf(" FLEXSPI2_RFDR1=%08lX\n", FLEXSPI2_RFDR2);
  //if (length > 16) Serial.printf(" FLEXSPI2_RFDR1=%08lX\n", FLEXSPI2_RFDR3);
  src = (const uint8_t *)&FLEXSPI2_RFDR0;
  while (length > 0) {
    *p++ = *src++;
    length--;
  }
  if (FLEXSPI2_INTR & FLEXSPI_INTR_IPRXWA) FLEXSPI2_INTR = FLEXSPI_INTR_IPRXWA;
}

static void flexspi_ip_write(uint32_t index, uint32_t addr, const void *data, uint32_t length)
{
  const uint8_t *src;
  uint32_t n, wrlen;

  FLEXSPI2_IPCR0 = addr;
  FLEXSPI2_IPCR1 = FLEXSPI_IPCR1_ISEQID(index) | FLEXSPI_IPCR1_IDATSZ(length);
  src = (const uint8_t *)data;
  FLEXSPI2_IPCMD = FLEXSPI_IPCMD_TRG;

  while (!((n = FLEXSPI2_INTR) & FLEXSPI_INTR_IPCMDDONE)) {

    if (n & FLEXSPI_INTR_IPTXWE) {
      wrlen = length;
      if (wrlen > 8) wrlen = 8;
      if (wrlen > 0) {

        //memcpy((void *)&FLEXSPI2_TFDR0, src, wrlen); !crashes sometimes!
        //src += wrlen;
        uint8_t *p = (uint8_t *) &FLEXSPI2_TFDR0;
        for (unsigned i = 0; i < wrlen; i++) *p++ = *src++;
        length -= wrlen;
        FLEXSPI2_INTR = FLEXSPI_INTR_IPTXWE;
      }
    }

  }

  if (n & FLEXSPI_INTR_IPCMDERR) {
    Serial.printf("Error: FLEXSPI2_IPRXFSTS=%08lX\r\n", FLEXSPI2_IPRXFSTS);
  }

  FLEXSPI2_INTR = FLEXSPI_INTR_IPCMDDONE;
}

void eraseFlashChip() {
  setupFlexSPI2();
  setupFlexSPI2Flash();

  waitFlash();
  flexspi_ip_command(11, flashBaseAddr);

  Serial.println("Erasing... (may take some time)");
  uint32_t t = millis();
  FLEXSPI2_LUT60 = LUT0(CMD_SDR, PINS4, 0x60); //Chip erase
  flexspi_ip_command(15, flashBaseAddr);

#ifdef FLASH_MEMMAP
  arm_dcache_delete((void*)((uint32_t)extBase + flashBaseAddr), flashCapacity);
#endif

  while (waitFlash(500)) {
    Serial.print(".");
  }

  t = millis() - t;
  Serial.printf("\nChip erased in %d seconds.\n", t / 1000);
}

//********************************************************************************************************
//********************************************************************************************************
//********************************************************************************************************
/*
   SPIFFS interface
*/

#define LOG_PAGE_SIZE       256

static u8_t spiffs_work_buf[LOG_PAGE_SIZE * 2];
static u8_t spiffs_fds[32 * 4];
static u8_t spiffs_cache_buf[(LOG_PAGE_SIZE + 32) * 4];

//********************************************************************************************************
static const u32_t blocksize = 4096; //or 32k or 64k (set correct flash commands above)

static s32_t my_spiffs_read(u32_t addr, u32_t size, u8_t *dst) {

#ifdef FLASH_MEMMAP
  memcpy(dst, (uint8_t *)extBase + addr, size);
#else
  flexspi_ip_read(5, addr, dst, size);
#endif
  return SPIFFS_OK;
}

static s32_t my_spiffs_write(u32_t addr, u32_t size, u8_t *src) {
  flexspi_ip_command(11, flashBaseAddr); // write enable
  flexspi_ip_write(13, addr, src, size); // write

#ifdef FLASH_MEMMAP
  arm_dcache_delete((void*)((uint32_t)extBase + addr), size);
#endif
  waitFlash();
  return SPIFFS_OK;
}

static s32_t my_spiffs_erase(u32_t addr, u32_t size) {
  int s = size;
  while (s > 0) { //TODO: Is this loop needed, or is size max 4096?
    flexspi_ip_command(11, flashBaseAddr);  //write enable
    flexspi_ip_command(12, addr);

#ifdef FLASH_MEMMAP
    arm_dcache_delete((void*)((uint32_t)extBase + addr), blocksize);
#endif

    addr += blocksize;
    s -= blocksize;
    waitFlash();
  }
  return SPIFFS_OK;
}

//********************************************************************************************************

int my_spiffs_mount() {

  setupFlexSPI2();
  setupFlexSPI2Flash();

  spiffs_config cfg;

  cfg.phys_size = flashCapacity; // use 16 MB flash TODO use ID to get capacity
  cfg.phys_addr = /* 0x70000000 + */flashBaseAddr; // start spiffs here (physical adress)
  cfg.phys_erase_block = blocksize; //4K sectors
  cfg.log_block_size = cfg.phys_erase_block; // let us not complicate things
  cfg.log_page_size = LOG_PAGE_SIZE; // as we said

  cfg.hal_read_f = my_spiffs_read;
  cfg.hal_write_f = my_spiffs_write;
  cfg.hal_erase_f = my_spiffs_erase;

  int res = SPIFFS_mount(&fs,
                         &cfg,
                         spiffs_work_buf,
                         spiffs_fds,
                         sizeof(spiffs_fds),
                         spiffs_cache_buf,
                         sizeof(spiffs_cache_buf),
                         0);
  return res;
}
 
Last edited:
I'm just guessin, but Paul said default size for 2nd flash/ram was 8 MB in his latest core. so for 16MB you need
FLEXSPI2_FLSHA1CR0 = 0x4000;
FLEXSPI2_FLSHA2CR0 = 0x40000;

Thanks @manitou. Gave it a try but still didn't work. Reading the flash chip id give me 0xffs. See the edit I made in post 527
 
When you declare an array, that's right. When you want to read it with an AVR, you need to use pgm_read_xxx functions afaik.

As long as the pointer is sent that is all the compiler does ? it is a normal RAM ptr*. No need to PROGMEM decorate the ptr* on passing it?

Then it would be up to the called code to have knowledge when the data needs to come from PROGMEM?

Code:
 func1( short *progmemData ) {
#ifdef ARM
// use ptr direct
#endif
#ifdef AVR
// use ptr to get PROGMEM data
#endif
 
You can't know at compile time whether the PSRAM chip will be installed on the board.

I would create 2 different constructors for the library which initialize a constexpr pointer or other variable you use. If it's constexpr and initialized by the constructor, the compiler should optimize away code which reads it and decides which memory to use.

Thanks Paul,

I guess the real question will be if there should be a way for the user to specify that I am using memory chips or please don't try to initialize memory chips for their different setups. But I will drop that for now :D

But as back to the display driver. The interesting issue is currently the code that deals with the frame buffer is all over every function. Like:
Code:
void ILI9488_t3::fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color)
{
....
	#ifdef ENABLE_ILI9488_FRAMEBUFFER
	if (_use_fbtft) {
		RAFB color_index = mapColorToPalletIndex(color);
		RAFB * pfbPixel_row = &_pfbtft[ y*_width + x];
		for (;h>0; h--) {
			RAFB * pfbPixel = pfbPixel_row;
			for (int i = 0 ;i < w; i++) {
				*pfbPixel++ = color_index;
			}
			pfbPixel_row += _width;
		}
	} else 
	#endif
	{
...
Where RAFB is defined in the header file as either a uint8_t (pallet usage for T3.5/6), uint16_t for T4.x and maybe uint32_t for T4.1 with external memroy. And the compiler takes care of the memory indexing.

There are obviously some other ways of doing this. Try to use templates where RAFB is a template setting. But if I understand correctly all of the code would then need to be within the header file. Or at least anything that RAFB influences. Not sure if I will go this route as may be hard to teach this old...

Instead if I get beyond my procrastination here ;) may try to rework the frame buffer code, that all of the places that touch the frame buffer get reduced down to a hand full of methods.
like setPixel, setRepeatedPixel, ... getPixel... And all of these places that update the frame buffer will call these methods. Not sure yet if then have sub-classes for the different pixel sizes or have function to set which mode we are in and have these methods do their own memory addressing, which is again like RA8875 does. But for this round (release) will probably just punt.

@all back to PROGMEM - Thanks Frank, Maybe the simplest thing would be to avr support for the program. But again I am wondering why I did not run into this earlier.
 
I'm just guessin, but Paul said default size for 2nd flash/ram was 8 MB in his latest core. so for 16MB you need
FLEXSPI2_FLSHA1CR0 = 0x4000;
FLEXSPI2_FLSHA2CR0 = 0x40000;

@manitou - @Frank B - @PaulStoffregen
Ok think I figured out what the problem was. Basically its with the settings for FLEXSPI2_FLSHA1CR0 and FLEXSPI2_FLSHA2CR0. Decided to go back to the manual. FLSHAnCR0 is the memory size of the chips that are in use. So to get it to work with an offset of 0x0800000

Code:
FLEXSPI2_FLSHA1CR0 = 0x2000;  //8MB PSRAM chip
FLEXSPI2_FLSHA2CR0 = 0x4000;   //16MB FLASH Chip
Now if you have 2 PSRAM chips then
Code:
FLEXSPI2_FLSHA1CR0 = 0x2000;  //8MB PSRAM chip
FLEXSPI2_FLSHA2CR0 = 0x2000;   //16MB FLASH Chip
which is what is there currently for the 2 PSRAM chips. Not sure if I can change these on the fly with out having to reinitialize the PSRAM settings?
 
I will ask a naive question, that I think may come up, and which I can probably guess the answer.

Suppose I am only going to solder on one PSRAM chip. Is it OK for me to use the larger pattern on the back of the T4.1? What will the address be then?
 
Don’t even want to make a guess at this point, but from start up code 0x70800000 same as the flash. I think.
 
Will get back {Code has ping/ponged between myself and mjs513 recently} and post a clearer example. Have seen it repro today with PJRC PSRAM init so that removes the need for external lib - and it is also now running PSRAM at slower speed - but still shows up in repro case.

Looking forward to seeing an example. If there's a memory corruption issue, I really want to find a way to reproduce it.

I created a memory test program which fills the entire PSRAM with pseudo-random and fixed patters, then checks them. So far it's showing no errors, even at 132 MHz clocks.

https://github.com/PaulStoffregen/teensy41_psram_memtest/blob/master/teensy41_psram_memtest.ino

If all testing passes, it also prints the total time taken for a simple benchmark.

I committed this change to the startup code to default to 88 MHz. Also fixes a bug where the other CCM_CBCMR bits were being clobbered.

https://github.com/PaulStoffregen/cores/commit/1889c06e56186efbf84dd51e108308f2c0b2a52a
 
Hmm... looks like overclocking Teensy 4.1 to 720 MHz makes this memory test occasionally fail. Overclocking to 816 MHz fails quickly, but still well into the 8MB range.

Strange, since the memory speed is based on PLL2 or PLL3 which don't change with overclocking.
 
Suppose I am only going to solder on one PSRAM chip. Is it OK for me to use the larger pattern on the back of the T4.1?

Nope. The startup code checks the smaller pads first. If no PSRAM chip is found there, the larger pads aren't checked.

FlexSPI is highly configurable. You could create different code that uses only the larger pads and maps the chip at 70000000, which is where the linker script puts EXTMEM variables.

But if using the startup code in the core library, for 8MB RAM the smaller pads must be used.
 
Nope. The startup code checks the smaller pads first. If no PSRAM chip is found there, the larger pads aren't checked.

...

I had the same question - nice to have the answer up front - I was going to put first PSRAM on larger pads and test … won't bother with two trips to the iron.
 
Looking forward to seeing an example. If there's a memory corruption issue, I really want to find a way to reproduce it.

I created a memory test program which fills the entire PSRAM with pseudo-random and fixed patters, then checks them. So far it's showing no errors, even at 132 MHz clocks.

https://github.com/PaulStoffregen/teensy41_psram_memtest/blob/master/teensy41_psram_memtest.ino

If all testing passes, it also prints the total time taken for a simple benchmark.

I committed this change to the startup code to default to 88 MHz. Also fixes a bug where the other CCM_CBCMR bits were being clobbered.

https://github.com/PaulStoffregen/cores/commit/1889c06e56186efbf84dd51e108308f2c0b2a52a

There seems to be an issue. Should have something clean to post in coming hours.

Goes back to this post on 5/7 p#287 forum.pjrc.com/threads/60532-Teensy-4-1-Beta-Test. @mjs513 has been going back and fourth with about 4 refinements from each of us since it was posted.

Could write 9 GB binary data file w/SD Fat Beta in short order (20K _isr samp/sec of 16 bytes) - parsing the data was a bit much - did some program compare checks and detected the issue - been looking/posting at it on and off and on and on and off since.

Eliminated SD write and it persisted. It has to do with _isr - because just as many updates celled from loop works fine - as well the other alternatives and changes tried it persists in the same way.

With 1.52_B5 change to PSRAM init in startup it still persists - though it seems to have moved unless that was just my last Address * address change and allow further simplification as noted not using any external PSRAM lib. So will get back to that.

Seeing your p#536 test fail when OC's - I just tested at 528 Mhz - and current repro code still fails as before.

In weeks past other mem testing was done and all looked good and right. Doing it with real world SD logger seemed more real type use - and this showed up ...
 
You can probably use:
Code:
extern "C" uint8_t external_psram_size;
and test if external_psram_size > 0 to include the buffer?

If you still want to run on older versions of 1.52 beta 1-4, you probably want:
Code:
#if (TEENSYDUINO > 151) && defined(ARDUINO_TEENSY41)
extern "C" uint8_t external_psram_size __attribute__((weak));
#endif

// ...

#if (TEENSYDUINO > 151) && defined(ARDUINO_TEENSY41)
  if (&external_psram_size)
    {
      if (external_psram_size > 0) {
        Serial.printf("PSRAM: %d MB\n", external_psram_size);
      } else {
        Serial.println("PSRAM: none");
      }
    }
#endif
 
Paul: Getting close to an example that @mjs513 found usable and editable - as noted it started from what seemed a nice SD log RAM buffer by @MBorgerson.

Just thought of something to add - do you want it here or a new thread?

<EDIT>: Added drop in sample rate and as @mjs513 noted before - lower sample rate of _isr() stops the failure

In so doing the output got really ugly when no error - fixing that makes it look cleaner only showing error lines : where buffer1 data is injected into buffer0

I'll put on this thread shortly if no reply ...
 
Last edited:
Paul: False Alarm … PSRAM test with _isr() - HEX math error apparently and buffer overlap.

Hopfully eRam_malloc() makes it into TD 1.53
 
if using the startup code in the core library, for 8MB RAM the smaller pads must be used.

Nearly missed that! Very good to know.
I would write this all over the place: users would almost certainly go for the larger pad first (a bit easier to solder).
 
If you still want to run on older versions of 1.52 beta 1-4, you probably want:
Code:
#if (TEENSYDUINO > 151) && defined(ARDUINO_TEENSY41)
extern "C" uint8_t external_psram_size __attribute__((weak));
#endif

@Michael, a question: What means the "weak" attribute here?

I've seen it for functions only, so far.
 
Hmm... looks like overclocking Teensy 4.1 to 720 MHz makes this memory test occasionally fail. Overclocking to 816 MHz fails quickly, but still well into the 8MB range.

Strange, since the memory speed is based on PLL2 or PLL3 which don't change with overclocking.

Morning Paul - was this resolved. I just ran a simple test sketch at 960Mhz using a modified lib (uses startup for PSRAM and just initializers FLASH) as well as the library example flashtest6.ino and don't seem to be getting errors anymore. I was getting last night before sleep took over. The only thing I did this morning was copied the latest startup.c to the core?

EDIT: Well just reran the tests after testing your memTest sketch which failed at clock >600Mhz and the other tests failed. So more work.
 
Last edited:
If you still want to run on older versions of 1.52 beta 1-4, you probably want:
Code:
#if (TEENSYDUINO > 151) && defined(ARDUINO_TEENSY41)
extern "C" uint8_t external_psram_size __attribute__((weak));
#endif

// ...

#if (TEENSYDUINO > 151) && defined(ARDUINO_TEENSY41)
  if (&external_psram_size)
    {
      if (external_psram_size > 0) {
        Serial.printf("PSRAM: %d MB\n", external_psram_size);
      } else {
        Serial.println("PSRAM: none");
      }
    }
#endif

Thanks Michael - just added this to my code snippets list that I just started :)
 
Whew, that's a big relief. ;)

I'm feeling pretty good about wrapping up 1.52. Anyone see any issues that should block a full non-beta release?

Don't see any issues with the changes to the PSRAM startup - especially since I figure out my problem :) Now have to rewicker the lib, mostly because of the possibility for using 2 PSRAM chips.
 
Back
Top