Teensy 4.x's 1062 MCU has 16 bytes of NVRAM on RTC unit

Status
Not open for further replies.

defragster

Senior Member+
Code below shows usage of the 1062 NVRAM bytes. For use it shows the "SDK" documented bit change required for function.

This code is RAM'ish - but on the RTC unit it appears a bit sluggish - versus a real RAM write - perhaps as it is in a different exec unit running on a 32 MHz clock.

This code has SerMon input options: " 'r':RESTART 'z':ZERO '?':INC 10 to each "
> 'r' will restart to demo the values are held
> 'z' to ZERO the values
> any other ( including the '\n' ) input char will add 10 by inc++ to each of the four DWORDS.

DOES IT WORK:
> restart on a powered T_4.x :: YES
> restart on a powered T_4.x with RTC cell :: YES
> Power PIN OFF/ON on a powered T_4.x :: YES
> Power PIN OFF/ON on a powered T_4.x with RTC cell :: YES
> Power PIN OFF/ON on a then UNplugged T_4.x with RTC cell :: YES
> Power interrupted T_4.x with RTC cell :: YES

This code demonstrates usage:
Code:
// TWO ways to access as 4 32 bit DWORDS of NVRAM backed data
// #define NVRAM_UINT32 ((uint32_t *)0x400D4100)
uint32_t *NVRAM_UINT32 ((uint32_t *)0x400D4100);

elapsedMillis tSome;
void setup() {
	SNVS_LPCR |= (1 << 24); //Enable NVRAM - documented in SDK
	Serial.begin(115200);
	while (!Serial && millis() < 4000 );
	Serial.println("\n" __FILE__ " " __DATE__ " " __TIME__);
	tSome = 0;
	Serial.print(" 'r':RESTART 'z':ZERO '?':INC 10 to each\n");
}

void loop() {
	if ( tSome >= 1000 ) {
		tSome -= 1000;
		for ( int ii = 0; ii < 4; ii++ ) {
			Serial.print( '\t' );
			Serial.print( NVRAM_UINT32[ii] );
		}
		Serial.println();
		for ( int ii = 0; ii < 4; ii++ ) {
			NVRAM_UINT32[ii]++;
		}
	}
	while ( Serial.available() ) {
		char cc = Serial.read();
		if ( 'r' == cc ) {
			SCB_AIRCR = 0x5FA0004;
			while (true);
		}
		else if ( 'z' == cc ) {
			Serial.print( "_ZERO_ NVRAM\n" );
			for ( int ii = 0; ii < 4; ii++ ) {
				NVRAM_UINT32[ii] = ii * 1000;
			}
		}
		else {
			Serial.print( "_10_INC_ NVRAM\n" );
			tSome += 10000;
		}
	}
}

<edit>: updated code to better show 10 Increment to each ( not 100 )
 
Last edited:
Quick update: continue from the Beta 5 thread:
As I mention in the post: https://forum.pjrc.com/threads/64592-Teensyduino-1-54-Beta-5?p=268336&viewfull=1#post268336

That bit was defined in the SDK... Turning it on in my example from that thread:
Code:
#define SNVS_LPGPR0      (IMXRT_SNVS.offset100)
#define SNVS_LPGPR1      (IMXRT_SNVS.offset104)
#define SNVS_LPGPR2      (IMXRT_SNVS.offset108)
#define SNVS_LPGPR3      (IMXRT_SNVS.offset10C)

void setup() {
  while (!Serial);
  Serial.begin(115200);
  Serial.println("At startup");
  SNVS_HPCOMR |= 0x80000000l;
  Serial.printf("%x %x %x %x\n", SNVS_LPGPR0, SNVS_LPGPR1, SNVS_LPGPR2,
                SNVS_LPGPR3);
  delay(100);
  SNVS_LPCR |= (1 << 24);
  SNVS_LPGPR0 += 1;
  SNVS_LPGPR1 += 2;
  SNVS_LPGPR2 += 3;
  SNVS_LPGPR3 += 4;
  Serial.println("After settting");
  Serial.printf("%x %x %x %x\n", SNVS_LPGPR0, SNVS_LPGPR1, SNVS_LPGPR2,
                SNVS_LPGPR3);
  SNVS_LPGPR = 100;
  Serial.printf("%x\n", SNVS_LPGPR);              
}
void loop() {
  
}
Code:
At startup
0 0 0 0
After settting
1 2 3 4
64
At startup
64 2 3 4
After settting
65 4 6 8
64
At startup
64 4 6 8
After settting
65 6 9 c
64

It now works. Tried just running and values updated. Tried reset in IDE and updated values were still there. Turned on and off power and again still working...

@Paul - I (or someone else) should probably do a PR to define SNVS_LPGR0-3. Wondering if also should define
Code:
#define SNVS_LPCR_GPR_Z_DIS ((uint32_t)(1 << 24))
 
Indeed the address of the four DWORDS in some fashion should be included in the imxrt.h for use rather than hardcoded numbers.

They could of course be used as 16 or 8 bit values for the 'best' 128 bit usage as needed.

Glad you found the SDK useful for document that as used. FrankB came up with that on some random test as posted some time back.
 
Quick edit to time 1000 increments of RTC MVRAM versus RAM shows:
_Add_1K_ NVRAM
732281 us for 1K increments of 4 RTC DWORDS
35 us for 1K increments of 4 RAM DWORDS

Added a ref to tRam[] and the time was the same - so it seems the RAM test is not being optimized away - but really that much faster:
Code:
// TWO ways to access as 4 32 bit DWORDS of NVRAM backed data
// #define NVRAM_UINT32 ((uint32_t *)0x400D4100)
uint32_t *NVRAM_UINT32 ((uint32_t *)0x400D4100);
uint32_t tRam[4];

elapsedMillis tSome;
void setup() {
	SNVS_LPCR |= (1 << 24); //Enable NVRAM - documented in SDK
	Serial.begin(115200);
	while (!Serial && millis() < 4000 );
	Serial.println("\n" __FILE__ " " __DATE__ " " __TIME__);
	tSome = 0;
	Serial.print(" 'r':RESTART 'z':ZERO '?':INC 100 to each\n");
	for ( int ii = 0; ii < 4; ii++ ) {
		tRam[ii] = 0;
	}
}

void loop() {
	if ( tSome >= 1000 ) {
		tSome -= 1000;
		for ( int ii = 0; ii < 4; ii++ ) {
			Serial.print( '\t' );
			Serial.print( NVRAM_UINT32[ii] );
		}
		Serial.println();
		for ( int ii = 0; ii < 4; ii++ ) {
			NVRAM_UINT32[ii]++;
		}
	}
	while ( Serial.available() ) {
		char cc = Serial.read();
		if ( 'r' == cc ) {
			SCB_AIRCR = 0x5FA0004;
			while (true);
		}
		else if ( 'z' == cc ) {
			Serial.print( "_ZERO_ NVRAM\n" );
			for ( int ii = 0; ii < 4; ii++ ) {
				NVRAM_UINT32[ii] = ii * 1000;
			}
		}
		else if ( 't' == cc ) {
			Serial.print( "_Add_1K_ NVRAM\n" );
			uint32_t tt = micros();
			for ( int jj = 0; jj < 1000; jj++ ) {
				for ( int ii = 0; ii < 4; ii++ ) {
					NVRAM_UINT32[ii]++;
				}
			}
			tt = micros() - tt;
			Serial.printf("\t%lu us\t for 1K increments of 4 RTC DWORDS\n", tt );
			tt = micros();
			for ( int jj = 0; jj < 1000; jj++ ) {
				for ( int ii = 0; ii < 4; ii++ ) {
					tRam[ii]++;
				}
			}
			tt = micros() - tt;
			Serial.printf("\t%lu us\t\t for 1K increments of 4 RAM DWORDS\n", tt );
		}
		else {
			Serial.print( "_100_INC_ NVRAM\n" );
			tSome += 10000;
		}
	}
}
 
Yup, I think its clock is the RTC and 32kHz are not that much, compared to the 600MHz core.
I found that bit by try& error.
 
Yup, I think its clock is the RTC and 32kHz are not that much, compared to the 600MHz core

Yeah - that was our thought back on that other thread - decided to show the time penalty for use.

Note: Tested on T_4.0 with and without RTC Battery, and T_4.1 powered. Both across multiple uploads and 'r' resets.
 
Afternoon all
Just getting back. Glad that you showed how to get the SNVS_LPRG0-3 bit manipulation working. To be honest missed the post where you showed how to enable: NPSWA_EN. That equates to this I guess in the example: SNVS_LPCR |= (1 << 24);
 
Aah, so it is actually just SNVS_LPGPR0 not fully available for user data, because it gets set during boot. As seen in post #2
Thank you @KurtE !

SNVS_LPGPR1..3 work as expected and do not get changed. So these fully usable for now.

@Defragster
Could be accessed by any data format with a »union.
 
Note: with things like UNION, the 16 bytes must be accessed as 4 32 bit entries. Who knows might fault if you try to address differently.
 
These seem to be 32 bit access memory locations. Quick edit of code for 8 or 16 bit use fails.

Similar code for the T_3.x could modify for the use as 8 and 16 byte entries.

KurtE: <edit - failure to read code ... >

When I hardcode the address it works - ...

<edited to hide confusion >
 
Last edited:
These seem to be 32 bit access memory locations. Quick edit of code for 8 or 16 bit use fails.

Similar code for the T_3.x could modify for the use as 8 and 16 byte entries.

KurtE: <edit - failure to read code ... >

When I hardcode the address it works - ...

<edited to hide confusion >

<edit2>
Wondered if the #define usage might alter the write speed versus hardcoded pointer - not faster:
Code:
_Add_1K_ NVRAM
	732326 us	 for 1K increments of 4 RTC DWORDS
	732413 us	 for 1K increments of 4 RTC #define DWORDS
	33 us		 for 1K increments of 4 RAM DWORDS
 
Teensy 4.x's 1062 MCU has 3 longwords of NVRAM on RTC unit

These locations seem to be 32bit only. That is, to read as individual byte a way could be to copy the uit32_t into a temporary variable as union and then read or write that byte and then wirte that uint32_t back to the register.

This is anyway useful to keep speed, as the acces to these registers is slow.

It would have been nice to have documented this SNVS_LPGPR, wich is a direct hardwired alias of SNVS_LPGPR0, is written during boot, would have avoided much confusion.

Anyway, for now 1..3 are working as expected.

But these will likely be used as soon as secure boot or any kind of key handling is implemeted. I've now designed in an external SLP RAM in my project. Is way cooler anyways, because the imxrt draws 100 times more standby current and RTC is not used with it.

EDIT: So this thread should be titled as Teensy 4.x's 1062 MCU has 3 longwords of NVRAM on RTC unit
 
Last edited:
These locations seem to be 32bit only. That is, to read as individual byte a way could be to copy the uit32_t into a temporary variable as union and then read or \It would have been nice to have documented this SNVS_LPGPR, wich is a direct hardwired alias of SNVS_LPGPR0, is written during boot, would have avoided much confusion.
The Aliases are mentioned in the Reference Manual, both as separate sections (20.6.15,20.6.16,20.6.17) and each of these sections mention:
like:
20.6.15.2 Function
The SNVS_LP General Purpose Register is a 128-bit read/write register located in
SNVS_LP, which can be used by any application for retaining data during an SoC powerdown
mode. This is a privileged read/write register. The full GPR register is accessed as
4 32-bit registers located in successive word addresses starting at offset 100h. For
backward compatibility with earlier versions of SNVS, LPGPR0..LPGPR3 are aliased at
the earlier offset of 90h and LPGPR0 is aliased at its original offset of 68h. The GPR will
be automatically zeroized when a tamper event occurs, unless GPR zeroization is
disabled via the GPR_Z_DIS bit in the LP Control Register.

Note: I do not find anywhere in teensy 4 code that touches the first register? And my simple test sketch appears to maintain that word as well.
 
Quick update: I did a global search on the Teensy install...
Code:
Searching 6638 files for "SNVS_LPGPR"

C:\arduino-1.8.13\hardware\teensy\avr\cores\teensy4\imxrt.h:
 8105  #define SNVS_LPSMCLR			(IMXRT_SNVS.offset060)
 8106  #define SNVS_LPPGDR			(IMXRT_SNVS.offset064)
 8107: #define SNVS_LPGPR			(IMXRT_SNVS.offset068)
 8108  #define IMXRT_SNVS_b		(*(IMXRT_REGISTER32_t *)0x400D4800)
 8109  #define SNVS_HPVIDR1			(IMXRT_SNVS_b.offset3F8)

C:\arduino-1.8.13\hardware\teensy\avr\cores_release\teensy4\imxrt.h:
 7914  #define SNVS_LPSMCLR			(IMXRT_SNVS.offset060)
 7915  #define SNVS_LPPGDR			(IMXRT_SNVS.offset064)
 7916: #define SNVS_LPGPR			(IMXRT_SNVS.offset068)
 7917  #define IMXRT_SNVS_b		(*(IMXRT_REGISTER32_t *)0x400D4800)
 7918  #define SNVS_HPVIDR1			(IMXRT_SNVS_b.offset3F8)

C:\arduino-1.8.13\hardware\teensy\avr\libraries\Snooze\src\hal\TEENSY_40\SnoozeAlarm.cpp:
   71      //SNVS_HPCR &= ~SNVS_HPCR_RTC_EN;
   72      //while ( SNVS_HPCR & SNVS_HPCR_RTC_EN );
   73:     SNVS_LPGPR = SNVS_DEFAULT_PGD_VALUE;
   74      SNVS_LPSR = SNVS_LPSR_PGD_MASK | SNVS_LPSR_LPTA;
   75      

3 matches across 3 files
Disregard the cores_release branch, this is the core that shipped in last beta. core is a symbolic link to current sources.

So th eonly other place I see it referred to is in snooze.
 
Title was picked to get onto a thread where it belonged, based on this @Deleted User post: pjrc.com/threads/64592-Teensyduino-1-54-Beta-5

RTC RAM
16 bytes of memory are located within the RTC. If a coin cell is connected to VBAT, contents of
this memory is preserved while power is off. This memory is accessed as 32 bit registers
LPGPR0-LPGPR3.

There are indeed 16 bytes of memory - and as indicated from that manual text it also makes clear the bytes are presented as 4 (four) 32 bit registers: LPGPR0, LPGPR1, LPGPR2, LPGPR3

These registers are NOT set to zero on restart, startup, upload, PWR management OFF/ON or any other observable condition found and their value is maintained by RTC battery - or when VIN power is maintained, but as noted write access to these registers requires their access to be enabled : SNVS_LPCR |= (1 << 24); //Enable NVRAM - documented in SDK

This is an MCU design constraint - just like 32 bit only access to the GPIO.

And as noted the "read, update, write" of these values appears to take about 22K times longer to perform than a 600 MHz full speed RAM process.
 
Status
Not open for further replies.
Back
Top