1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-07-02 14:22:55 +03:00

PoC cache configuration control (#7060)

* PoC cache configuration control

Expaned boards.txt.py to allow new MMU options and create revised .ld's
Updated eboot to pass 48K IRAM segments.
Added Cache_Read_Enable intercept to modify call for 16K ICACHE
Update platform.txt to pass new mmu options through to compiler and linker preprocessor.
Added quick example: esp8266/MMU48K

* Style corrections
Added MMU_ qualifier to new defines.
Moved changes into their own file.
Don't know how to fix platformio issue.

* Added detailed description for Cache_Read_Enable.
Updated tools/sizes.py to report correct IRAM size and indicate ICACHE size.
Merged in earlephilhower's work on unaligned exception. Refactored and added
support for store operations and changed the name to be more closely aligned
with its function. Improved crash reporting path.

* Style and MMU_SEC_HEAP corrections.

* Improved asm register usage.
Added some inline functions to aid in byte and short access to iRAM.
 * only byte read has been tested
Updated .ld file to work better with platform.io; however, I am still
missing some steps, so platformio will still fail.

* Interesting glitch in boards.txt after github merge. A new board in
master was missing new additions added by boards.txt.py in the PR.
Which the CI flags when it rebuilds boards.txt.

* Support for 2nd Heap, excess IRAM, through umm_malloc.

Adapted changes to umm_malloc, Esp.cpp, StackThunk.cpp,
WiFiClientSecureBearSSL.cpp, and virtualmem.ino to irammem.ino from
@earlephilhower PR #6994.

Reworked umm_malloc to use context pointers instead of copy context.
umm_malloc now supports allocations from IRAM. Added class
HeapSelectIram, ... to aid in selecting alternate heaps,
modeled after class InterruptLock.
Restrict alloc request from ISRs to DRAM.

Never ending improvements to debug printing.

Sec Heap option now pulls in free IRAM left over in the 1st 32K block.
Managed through umm_malloc with HeapSelectIram.

Updated examples.

* Post push CI cleanup.

* Cleanup part II

* Cleanup part III

* Updates to support platformio, maybe.

* Added exception C wrapper replacement.

* CI Cleanup

* CI Cleanup II

Don't know what to do with platformio it doesn't like my .S file.
ifdef out USE_ISR_SAFE_EXC_WRAPPER to block the new assemlby module
from building on platformio only.

* Changes to exc-c-wrapper-handler.S to assemble under platformio.

* For platformio, Correction to toolchain-xtensa include path.
@mcspr, Thankyou!

* Temporarily added --print-memory-usage to ld parameters for cross-checking IRAM size.

* undo change to platform.txt

* correct merge conflict. take 1

* Fixed #if... for building umm_get_oom_count. It was not building when UMM_STATS_FULL was used.

* Commented out XMC support. Compatibility issues with PoC when using 16K ICACHE.

* Corrected size.py, DRAM bracketing changed to not include ICACHE with DRAM total.

* Added additional _context for support of use of UMM_INLINE_METRICS.
Corrected some UMM_POSION missed edits.

* Changes to clear errors and warnings from toolchain 10.1

Several fixes and improvements to example MMU48K.

With the improved optimization in toolchain 10.1 The example divide by 0
exception was failing with a HWDT event instead of its exception handler.
The compiler saw the obscured divide by 0 and replaced it with a break point.

* Isolated incompatable definitions related to _xtos_set_exception_handler.
GDBSTUB definitions are different from the BootROM's.

* Update tools/platformio-build.py

Co-authored-by: Max Prokhorov <prokhorov.max@outlook.com>

* Requested changes

Changed mmu related usages of ETS_... defines to DBG_MMU_...

Cleanup in example MMU48K.ino. Removed stale memory reference macro
and mmu_status print statement. Cleanup printf '\n' to be '\r\n'.

Improved issolation of development debug prints from the rest of the debug prints.

* Corrected comment. And added missing include.

* Improve comment.

* style and comment correction

* Added draft mmu.rst file and updated index.
Updated example HeapMetric.ino to also illustrate use of IRAM
Improved comments in exc-c-wrapper-handler.S. Added insurance IRQ disable.

* Updated mmu.rst

Improved function name uniqueness for is_iram, is_dram, and is_icache by
adding prefix mmu_. Also, made them available outside of a debug build.
Made pointer precision width more specific.

Made some of the static inline functions in mmu_irm.h safe for ISRs by
setting then for always inline.

* Add a default MMU_IRAM_SIZE value for a new CI test to pass.

Extended use 'umm_heap_context_t *_context' argument in ..._core functions
and expanded its usage to reduce unnecessary repeated calls to
umm_info(NULL, false), also removed recursion from umm_info(NULL, true).

Fixed stack buffer length in umm_info_safe_printf_P and heap.cpp.

Added example for creating an IRAM reserve section.

Updated mmu.rst. Grammar and spelling corrections.

* CI appeasement

* CI appeasement with comment correction.

* Ensure SYS always runs with DRAM Heap selected.

* Add/move heap stack overflow/underflow check to Esp.cpp where the event was discarded.

* Improved comment clarity of purpose for IramReserve.ino. Clean up MMU48K.ino

* Added missing #include

* Corrected usage of warning

* CI appeasement and use #message not #pragma message

* Updated git version of eboot.elf to match build version.
Good test catch.

* Remove conditional build option USE_ISR_SAFE_EXC_WRAPPER, always install.

Use the replacement wrapper on non32xfer_exception_handler install.

Added comments to code describing some exception handling issues.

* Updated mmu.rst

* Expanded and clarified comments.

Limited access to some detailed typdefs/prototypes to .cpp
modules, to avoid future build conflicts.

Completed TODO for verifing that the "C" structure struct __exception_frame
matches the ASM version.

Fixed some typo's, code rot, and added some more cases in examaple irammem.ino.
Refactored a little and reordered printing to ease comparison between methods.

Corrected `#ifdef __cplusplus` coverage area. Cleaned up `extern "C" ...` usage.
Fixes issues with including mmu_iram.h or esp8266_undocumented.h in .c files.

* Style fixes and more cleanup

* Style fix

* Remove unnessasary IRAM_ATTR from install_non32xfer_exception_handler

Some comment tuning.

In the context of _xtos_set_exception_handler and the functions it registers,
changed to type int for exception cause type. This is also the type used by gdbstub
and some other Xtensa files I found.
This commit is contained in:
M Hightower
2020-12-06 05:15:42 -08:00
committed by GitHub
parent 47e7b2db6c
commit 8b662ed3b3
69 changed files with 3928 additions and 300 deletions

View File

@ -4,6 +4,7 @@
#include <ESP8266WiFi.h>
#include <umm_malloc/umm_malloc.h>
#include <umm_malloc/umm_heap_select.h>
void stats(const char* what) {
// we could use getFreeHeap() getMaxFreeBlockSize() and getHeapFragmentation()
@ -109,6 +110,7 @@ void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_OFF);
Serial.printf("\r\nDemo Heap Metrics for DRAM\r\n");
tryit(8000);
tryit(4000);
tryit(2000);
@ -118,6 +120,21 @@ void setup() {
tryit(100);
tryit(50);
tryit(15);
#ifdef UMM_HEAP_IRAM
{
HeapSelectIram ephemeral;
Serial.printf("\r\nDemo Heap Metrics for IRAM\r\n");
tryit(8000);
tryit(4000);
tryit(2000);
tryit(1000);
tryit(500);
tryit(200);
tryit(100);
tryit(50);
tryit(15);
}
#endif
}
void loop() {

View File

@ -0,0 +1,124 @@
/*
Overview: Of the 48KB of IRAM, the remaining IRAM after your code is untouched
during the reboot process. For a sketch that does not use deep-sleep, it is
possible to pass/hold information across boot cycles in this area of IRAM.
With the selection of Arduino IDE Tools Option: 'MMU: 16KB cache + 48KB IRAM
and 2nd Heap (shared)' all of this space goes into a managed 2nd Heap.
Managed, in this case, refers to using malloc, free, realloc, etc. API.
The objective of this example is to show how to modify the 2nd Heap creation
to omit a block of IRAM at the end of the 2nd Heap. In this example, we use
this block to store a boot count.
*/
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <umm_malloc/umm_malloc.h>
#if defined(UMM_HEAP_IRAM)
// durable - as in long life, persisting across reboots.
struct durable {
uint32_t bootCounter;
uint32_t chksum;
};
// Leave a durable block of IRAM after the 2nd heap.
// The block should be in 8-byte increments and fall on an 8-byte alignment.
#define IRAM_RESERVE_SZ ((sizeof(struct durable) + 7UL) & ~7UL)
// Position its address just above the reduced 2nd Heap.
#define IRAM_RESERVE (0x40100000UL + 0xC000UL - IRAM_RESERVE_SZ)
// Define a reference with the right properties to make access easier.
#define DURABLE ((struct durable *)IRAM_RESERVE)
#define INCREMENT_BOOTCOUNT() (DURABLE->bootCounter)++
extern struct rst_info resetInfo;
/*
Define a function to determine if IRAM stored data is valid. The criteria used
here can vary with how exhaustively you want the process to be.
In this example, we are just going to look at the reset cause and assume all
is well in certain situations. For this example, we include
REASON_EXT_SYS_RST as a possible case for IRAM not being valid. The problem
here is some devices will indicate REASON_EXT_SYS_RST for the Power-on case.
If you wanted to be able to isolate the power-on case from a
REASON_EXT_SYS_RST, you could add additional logic to set and verify a CRC or
XOR sum on the IRAM data (or just a section of the IRAM data).
*/
inline bool is_iram_valid(void) {
return (REASON_WDT_RST <= resetInfo.reason &&
REASON_SOFT_RESTART >= resetInfo.reason);
}
void setup() {
WiFi.persistent(false);
WiFi.mode(WIFI_OFF);
Serial.begin(115200);
delay(10);
Serial.printf_P(PSTR("\r\nSetup ...\r\n"));
if (!is_iram_valid()) {
DURABLE->bootCounter = 0;
}
DURABLE->bootCounter++;
Serial.printf("Number of reboots at %u\r\n", DURABLE->bootCounter);
Serial.printf("\r\nSome less than direct, ways to restart:\r\n");
processKey(Serial, '?');
}
void loop(void) {
if (Serial.available() > 0) {
int hotKey = Serial.read();
processKey(Serial, hotKey);
}
}
//////////////////////////////////////////////////////////////////////////////
/*
Create a block of unmanaged IRAM for special uses.
This is done by reducing the size of the managed 2nd Heap (Shared) at
initialization time.
*/
extern "C" void _text_end(void);
extern "C" void umm_init_iram(void) {
/*
Calculate the start of 2nd heap, staying clear of possible segment alignment
adjustments and checksums. These can affect the persistence of data across
reboots.
*/
uint32_t sec_heap = (uint32_t)_text_end + 32;
sec_heap &= ~7;
size_t sec_heap_sz = 0xC000UL - (sec_heap - 0x40100000UL);
sec_heap_sz -= IRAM_RESERVE_SZ; // Shrink IRAM heap
if (0xC000UL > sec_heap_sz) {
umm_init_iram_ex((void *)sec_heap, sec_heap_sz, true);
}
}
#else
void setup() {
WiFi.persistent(false);
WiFi.mode(WIFI_OFF);
Serial.begin(115200);
delay(10);
Serial.println("\r\n\r\nThis sketch requires Tools Option: 'MMU: 16KB cache + 48KB IRAM and 2nd Heap (shared)'");
}
void loop(void) {
}
#endif

View File

@ -0,0 +1,116 @@
#include <esp8266_undocumented.h>
void crashMeIfYouCan(void)__attribute__((weak));
int divideA_B(int a, int b);
int* nullPointer = NULL;
void processKey(Print& out, int hotKey) {
switch (hotKey) {
case 'r':
out.printf_P(PSTR("Reset, ESP.reset(); ...\r\n"));
ESP.reset();
break;
case 't':
out.printf_P(PSTR("Restart, ESP.restart(); ...\r\n"));
ESP.restart();
break;
case 's': {
uint32_t startTime = millis();
out.printf_P(PSTR("Now crashing with Software WDT. This will take about 3 seconds.\r\n"));
ets_install_putc1(ets_putc);
while (true) {
ets_printf("%9lu\r", (millis() - startTime));
ets_delay_us(250000);
// stay in an loop blocking other system activity.
}
}
break;
case 'h':
out.printf_P(PSTR("Now crashing with Hardware WDT. This will take about 6 seconds.\r\n"));
asm volatile("mov.n a2, %0\n\t"
"mov.n a3, %1\n\t"
"mov.n a4, %2\n\t"
"mov.n a5, %3\n\t"
"mov.n a6, %4\n\t"
: : "r"(0xaaaaaaaa), "r"(0xaaaaaaaa), "r"(0xaaaaaaaa), "r"(0xaaaaaaaa), "r"(0xaaaaaaaa) : "memory");
// Could not find these in the stack dump, unless interrupts were enabled.
{
uint32_t startTime = millis();
// Avoid all the Core functions that play nice, so we can hog
// the system and crash.
ets_install_putc1(ets_putc);
xt_rsil(15);
while (true) {
ets_printf("%9lu\r", (millis() - startTime));
ets_delay_us(250000);
// stay in an loop blocking other system activity.
//
// Note:
// Hardware WDT kicks in if Software WDT is unable to perform.
// With the Hardware WDT, nothing is saved on the stack, that I have seen.
}
}
break;
case 'p':
out.println(F("Time to panic()!"));
panic();
break;
case 'z':
out.println(F("Crashing by dividing by zero. This should generate an exception(0)."));
out.printf_P(PSTR("This should not print %d\n"), divideA_B(1, 0));
break;
case 'w':
out.println(F("Now calling: void crashMeIfYouCan(void)__attribute__((weak));"));
out.println(F("This function has a prototype but was missing when the sketch was linked. ..."));
crashMeIfYouCan();
break;
case 'b':
out.println(F("Executing a break instruction w/o GDB will cause a HWDT reset."));
asm volatile("break 1, 15;");
out.println(F("This line will not be printable w/o running GDB"));
break;
case '0':
out.println(F("Crashing at an embeded 'break 1, 15' instruction that was generated"));
out.println(F("by the compiler after detecting a divide by zero."));
out.printf_P(PSTR("This should not print %d\n"), divideA_B_bp(1, 0));
break;
case '\r':
out.println();
case '\n':
break;
case '?':
out.println();
out.println(F("Press a key + <enter>"));
out.println(F(" r - Reset, ESP.reset();"));
out.println(F(" t - Restart, ESP.restart();"));
out.println(F(" ? - Print Help"));
out.println();
out.println(F("Crash with:"));
out.println(F(" s - Software WDT"));
out.println(F(" h - Hardware WDT - looping with interrupts disabled"));
out.println(F(" w - Hardware WDT - calling a missing (weak) function."));
out.println(F(" 0 - Hardware WDT - a hard coded compiler breakpoint from a compile time detected divide by zero"));
out.println(F(" b - Hardware WDT - a forgotten hard coded 'break 1, 15;' and no GDB running."));
out.println(F(" z - Divide by zero, exception(0);"));
out.println(F(" p - panic();"));
out.println();
break;
default:
out.printf_P(PSTR("\"%c\" - Not an option? / ? - help"), hotKey);
out.println();
processKey(out, '?');
break;
}
}
// With the current toolchain 10.1, using this to divide by zero will *not* be
// caught at compile time.
int __attribute__((noinline)) divideA_B(int a, int b) {
return (a / b);
}
// With the current toolchain 10.1, using this to divide by zero *will* be
// caught at compile time. And a hard coded breakpoint will be inserted.
int divideA_B_bp(int a, int b) {
return (a / b);
}

View File

@ -0,0 +1,322 @@
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <umm_malloc/umm_malloc.h>
#include <umm_malloc/umm_heap_select.h>
uint32_t timed_byte_read(char *pc, uint32_t * o);
uint32_t timed_byte_read2(char *pc, uint32_t * o);
int divideA_B(int a, int b);
int* nullPointer = NULL;
char *probe_b = NULL;
short *probe_s = NULL;
char *probe_c = (char *)0x40110000;
short *unaligned_probe_s = NULL;
uint32_t read_var = 0x11223344;
/*
Notes,
When accessing IRAM as data storage all access must be word aligned and
full word length.
*/
#if defined(MMU_IRAM_HEAP) || defined(MMU_SEC_HEAP)
uint32_t *gobble;
size_t gobble_sz;
#elif (MMU_IRAM_SIZE > 32*1024)
uint32_t gobble[4 * 1024] IRAM_ATTR;
constexpr size_t gobble_sz = sizeof(gobble);
#else
uint32_t gobble[256] IRAM_ATTR;
constexpr size_t gobble_sz = sizeof(gobble);
#endif
bool isValid(uint32_t *probe) {
bool rc = true;
if (NULL == probe) {
ets_uart_printf("\nNULL memory pointer %p ...\n", probe);
return false;
}
ets_uart_printf("\nTesting for valid memory at %p ...\n", probe);
uint32_t savePS = xt_rsil(15);
uint32_t saveData = *probe;
for (size_t i = 0; i < 32; i++) {
*probe = BIT(i);
asm volatile("" ::: "memory");
uint32_t val = *probe;
if (val != BIT(i)) {
ets_uart_printf(" Read 0x%08X != Wrote 0x%08X\n", val, (uint32_t)BIT(i));
rc = false;
}
}
*probe = saveData;
xt_wsr_ps(savePS);
ets_uart_printf(" %s\n", (rc) ? "Pass" : "Fail!");
return rc;
}
void dump_mem32(const void * addr, const size_t len) {
uint32_t *addr32 = (uint32_t *)addr;
ets_uart_printf("\n");
if ((uintptr_t)addr32 & 3) {
ets_uart_printf("non-32-bit access\n");
ets_delay_us(12000);
}
for (size_t i = 0; i < len;) {
ets_uart_printf("%p: ", &addr32[i]);
do {
ets_uart_printf(" 0x%08x", addr32[i]);
} while (i++, (i & 3) && (i < len));
ets_uart_printf("\n");
}
ets_uart_printf("\n");
}
extern "C" void _text_end(void);
// extern void *_text_end;
void print_mmu_status(Print& oStream) {
oStream.println();
oStream.printf_P(PSTR("MMU Configuration"));
oStream.println();
oStream.println();
uint32_t iram_bank_reg = ESP8266_DREG(0x24);
if (0 == (iram_bank_reg & 0x10)) { // if bit clear, is enabled
oStream.printf_P(PSTR(" IRAM block mapped to: 0x40108000"));
oStream.println();
}
if (0 == (iram_bank_reg & 0x08)) {
oStream.printf_P(PSTR(" IRAM block mapped to: 0x4010C000"));
oStream.println();
}
#ifdef MMU_ICACHE_SIZE
oStream.printf_P(PSTR(" ICACHE Size: %u"), MMU_ICACHE_SIZE);
oStream.println();
#endif
#ifdef MMU_IRAM_SIZE
oStream.printf_P(PSTR(" IRAM Size: %u"), MMU_IRAM_SIZE);
oStream.println();
const uint32_t iram_free = MMU_IRAM_SIZE - (uint32_t)((uintptr_t)_text_end - 0x40100000UL);
oStream.printf_P(PSTR(" IRAM free: %u"), iram_free);
oStream.println();
#endif
oStream.printf_P(PSTR(" IRAM _text_end: %p"), _text_end);
oStream.println();
#ifdef MMU_SEC_HEAP
oStream.printf_P(PSTR(" Secondary Heap at: %p"), MMU_SEC_HEAP);
oStream.println();
oStream.printf_P(PSTR(" Secondary Heap Size: %u"), MMU_SEC_HEAP_SIZE);
oStream.println();
#endif
}
void setup() {
WiFi.persistent(false);
WiFi.mode(WIFI_OFF);
// Serial.begin(74880);
Serial.begin(115200);
delay(10);
Serial.printf_P(PSTR("\r\n\r\nSetup ...\r\n"));
print_mmu_status(Serial);
#if defined(MMU_IRAM_HEAP)
{
HeapSelectIram ephemeral;
// Serial.printf_P(PSTR("ESP.getFreeHeap(): %u\n"), ESP.getFreeHeap());
gobble_sz = ESP.getFreeHeap() - UMM_OVERHEAD_ADJUST; // - 4096;
gobble = (uint32_t *)malloc(gobble_sz);
}
Serial.printf_P(PSTR("\r\nmalloc() from IRAM Heap:\r\n"));
Serial.printf_P(PSTR(" gobble_sz: %u\r\n"), gobble_sz);
Serial.printf_P(PSTR(" gobble: %p\r\n"), gobble);
#elif defined(MMU_SEC_HEAP)
gobble = (uint32_t *)MMU_SEC_HEAP;
gobble_sz = MMU_SEC_HEAP_SIZE;
#endif
#if (MMU_IRAM_SIZE > 0x8000) || defined(MMU_IRAM_HEAP) || defined(MMU_SEC_HEAP)
if (isValid(gobble)) {
// Put something in our new memory
for (size_t i = 0; i < (gobble_sz / 4); i++) {
gobble[i] = (uint32_t)&gobble[i];
}
// Now is it there?
dump_mem32(gobble, 32);
// dump_mem32(&gobble[gobble_sz / 4 / 2], 32);
dump_mem32(&gobble[gobble_sz / 4 - 32], 32);
}
#endif
// Lets peak over the edge
Serial.printf_P(PSTR("\r\nPeek over the edge of memory at 0x4010C000\r\n"));
dump_mem32((void *)(0x4010C000 - 16 * 4), 32);
probe_b = (char *)gobble;
probe_s = (short *)((uintptr_t)gobble);
unaligned_probe_s = (short *)((uintptr_t)gobble + 1);
}
void processKey(Print& out, int hotKey) {
switch (hotKey) {
case 't': {
uint32_t tmp;
out.printf_P(PSTR("Test how much time is added by exception handling"));
out.println();
out.printf_P(PSTR("Timed byte read from iCACHE %u cpu cycle count, 0x%02X."), timed_byte_read((char *)0x40200003, &tmp), tmp);
out.println();
out.printf_P(PSTR("Timed byte read from iCACHE %u cpu cycle count, 0x%02X."), timed_byte_read((char *)0x40200003, &tmp), tmp);
out.println();
out.printf_P(PSTR("Timed byte read from iRAM %u cpu cycle count, 0x%02X."), timed_byte_read((char *)0x40108000, &tmp), tmp);
out.println();
out.printf_P(PSTR("Timed byte read from dRAM %u cpu cycle count, 0x%02X."), timed_byte_read((char *)((uintptr_t)&read_var + 1), &tmp), tmp);
out.println();
out.printf_P(PSTR("Test how much time is used by the inline function method"));
out.println();
out.printf_P(PSTR("Timed byte read from iCACHE %u cpu cycle count, 0x%02X."), timed_byte_read2((char *)0x40200003, &tmp), tmp);
out.println();
out.printf_P(PSTR("Timed byte read from iCACHE %u cpu cycle count, 0x%02X."), timed_byte_read2((char *)0x40200003, &tmp), tmp);
out.println();
out.printf_P(PSTR("Timed byte read from iRAM %u cpu cycle count, 0x%02X."), timed_byte_read2((char *)0x40108000, &tmp), tmp);
out.println();
out.printf_P(PSTR("Timed byte read from dRAM %u cpu cycle count, 0x%02X."), timed_byte_read2((char *)((uintptr_t)&read_var + 1), &tmp), tmp);
out.println();
out.println();
break;
}
case '9':
out.printf_P(PSTR("Unaligned exception by reading short"));
out.println();
out.flush();
xt_rsil(3);
out.printf_P(PSTR("Read short, 0x%02X at %p"), unaligned_probe_s[0], unaligned_probe_s);
xt_rsil(0);
out.println();
break;
case 'c':
out.printf_P(PSTR("Load/Store exception by reading byte outside of handler range"));
out.println();
out.flush();
xt_rsil(3);
out.printf_P(PSTR("Read Byte, 0x%02X at %p"), probe_c[0], probe_c);
xt_rsil(0);
out.println();
out.printf_P(PSTR("With Non32-bit access enabled, access range check is only done when 'Tools->Debug Level: CORE ...' is set."));
out.println();
break;
case 'b':
out.printf_P(PSTR("Load/Store exception by reading byte from iRAM"));
out.println();
out.flush();
out.printf_P(PSTR("Read Byte from iRAM, 0x%02X at %p"), probe_b[0], probe_b);
out.println();
break;
case 'B': {
out.printf_P(PSTR("Load/Store exception by writing byte to iRAM"));
out.println();
char val = 0x55;
out.printf_P(PSTR("Write byte, 0x%02X, to iRAM at %p"), val, probe_b);
out.println();
out.flush();
probe_b[0] = val;
out.printf_P(PSTR("Read Byte back from iRAM, 0x%02X at %p"), probe_b[0], probe_b);
out.println();
break;
}
case 's':
out.printf_P(PSTR("Load/Store exception by reading short from iRAM"));
out.println();
out.flush();
out.printf_P(PSTR("Read short from iRAM, 0x%04X at %p"), probe_s[0], probe_s);
out.println();
break;
case 'S': {
out.printf_P(PSTR("Load/Store exception by writing short to iRAM"));
out.println();
short int val = 0x0AA0;
out.printf_P(PSTR("Write short, 0x%04X, to iRAM at %p"), val, probe_s);
out.println();
out.flush();
probe_s[0] = val;
out.printf_P(PSTR("Read short back from iRAM, 0x%04X at %p"), probe_s[0], probe_s);
out.println();
break;
}
case 'R':
out.printf_P(PSTR("Restart, ESP.restart(); ..."));
out.println();
ESP.restart();
break;
case 'p':
out.println(F("Time to panic()!"));
panic();
break;
case '0':
out.println(F("Crashing by dividing by zero."));
out.printf_P(PSTR("This should not print %d"), divideA_B(1, 0));
out.println();
break;
case '\r':
out.println();
case '\n':
break;
case '?':
out.println();
out.println(F("Press a key + <enter>"));
out.println(F(" R - Restart, ESP.restart();"));
out.println(F(" t - exception vs inline method timing info."));
out.println(F(" ? - Print Help"));
out.println();
#if defined(NON32XFER_HANDLER)
out.println(F("Test exception handling with non-32 bit transfer handler:"));
#else
out.println(F("Crash with:"));
#endif
out.println(F(" b - read byte, Load/Store exception"));
out.println(F(" B - write byte, Load/Store exception"));
out.println(F(" s - read short, Load/Store exception"));
out.println(F(" S - write short, Load/Store exception"));
#if defined(NON32XFER_HANDLER)
out.println();
out.println(F("Crash with:"));
#endif
out.println(F(" c - read byte, Load/Store exception outside of handler range"));
out.println(F(" 9 - read short, Unaligned exception"));
out.println(F(" 0 - Divide by zero, exception(0);"));
out.println(F(" p - panic();"));
out.println();
break;
default:
out.printf_P(PSTR("\"%c\" - Not an option? / ? - help"), hotKey);
out.println();
break;
}
}
void serialClientLoop(void) {
if (Serial.available() > 0) {
int hotKey = Serial.read();
processKey(Serial, hotKey);
}
}
void loop() {
serialClientLoop();
}
int __attribute__((noinline)) divideA_B(int a, int b) {
return (a / b);
}

View File

@ -0,0 +1,16 @@
#include <Arduino.h>
#include <mmu_iram.h>
uint32_t IRAM_ATTR timed_byte_read(char *pc, uint32_t * o) {
uint32_t start = esp_get_cycle_count();
*o = *pc;
// return clockCyclesToMicroseconds(esp_get_cycle_count() - start);
return (esp_get_cycle_count() - start);
}
uint32_t IRAM_ATTR timed_byte_read2(char *pc, uint32_t * o) {
uint32_t start = esp_get_cycle_count();
*o = mmu_get_uint8(pc);
// return clockCyclesToMicroseconds(esp_get_cycle_count() - start);
return (esp_get_cycle_count() - start);
}

View File

@ -0,0 +1,364 @@
/*
This sketch assumes you have selected IRAM as a Second Heap from
the Arduino IDE tools menu.
*/
#include <ESP8266WiFi.h>
#include <umm_malloc/umm_malloc.h>
#include <umm_malloc/umm_heap_select.h>
// #define USE_SET_IRAM_HEAP
#ifndef ETS_PRINTF
#define ETS_PRINTF ets_uart_printf
#endif
uint32_t cyclesToRead_nKx32(int n, unsigned int *x, uint32_t *res) {
uint32_t b = ESP.getCycleCount();
uint32_t sum = 0;
for (int i = 0; i < n * 1024; i++) {
sum += *(x++);
}
*res = sum;
return ESP.getCycleCount() - b;
}
uint32_t cyclesToWrite_nKx32(int n, unsigned int *x) {
uint32_t b = ESP.getCycleCount();
uint32_t sum = 0;
for (int i = 0; i < n * 1024; i++) {
sum += i;
*(x++) = sum;
}
return ESP.getCycleCount() - b;
}
uint32_t cyclesToRead_nKx16(int n, unsigned short *x, uint32_t *res) {
uint32_t b = ESP.getCycleCount();
uint32_t sum = 0;
for (int i = 0; i < n * 1024; i++) {
sum += *(x++);
}
*res = sum;
return ESP.getCycleCount() - b;
}
uint32_t cyclesToWrite_nKx16(int n, unsigned short *x) {
uint32_t b = ESP.getCycleCount();
uint32_t sum = 0;
for (int i = 0; i < n * 1024; i++) {
sum += i;
*(x++) = sum;
}
return ESP.getCycleCount() - b;
}
uint32_t cyclesToRead_nKx8(int n, unsigned char*x, uint32_t *res) {
uint32_t b = ESP.getCycleCount();
uint32_t sum = 0;
for (int i = 0; i < n * 1024; i++) {
sum += *(x++);
}
*res = sum;
return ESP.getCycleCount() - b;
}
uint32_t cyclesToWrite_nKx8(int n, unsigned char*x) {
uint32_t b = ESP.getCycleCount();
uint32_t sum = 0;
for (int i = 0; i < n * 1024; i++) {
sum += i;
*(x++) = sum;
}
return ESP.getCycleCount() - b;
}
// Compare with Inline
uint32_t cyclesToRead_nKx16_viaInline(int n, unsigned short *x, uint32_t *res) {
uint32_t b = ESP.getCycleCount();
uint32_t sum = 0;
for (int i = 0; i < n * 1024; i++) {
sum += mmu_get_uint16(x++); //*(x++);
}
*res = sum;
return ESP.getCycleCount() - b;
}
uint32_t cyclesToWrite_nKx16_viaInline(int n, unsigned short *x) {
uint32_t b = ESP.getCycleCount();
uint32_t sum = 0;
for (int i = 0; i < n * 1024; i++) {
sum += i;
// *(x++) = sum;
mmu_set_uint16(x++, sum);
}
return ESP.getCycleCount() - b;
}
uint32_t cyclesToRead_nKx8_viaInline(int n, unsigned char*x, uint32_t *res) {
uint32_t b = ESP.getCycleCount();
uint32_t sum = 0;
for (int i = 0; i < n * 1024; i++) {
sum += mmu_get_uint8(x++); //*(x++);
}
*res = sum;
return ESP.getCycleCount() - b;
}
uint32_t cyclesToWrite_nKx8_viaInline(int n, unsigned char*x) {
uint32_t b = ESP.getCycleCount();
uint32_t sum = 0;
for (int i = 0; i < n * 1024; i++) {
sum += i;
// *(x++) = sum;
mmu_set_uint8(x++, sum);
}
return ESP.getCycleCount() - b;
}
void perfTest_nK(int nK, uint32_t *mem, uint32_t *imem) {
uint32_t res;
uint32_t t;
t = cyclesToWrite_nKx16(nK, (uint16_t*)imem);
Serial.printf("IRAM Memory Write: %6d cycles for %dK by 16, %3d AVG cycles/transfer\r\n", t, nK, t / (nK * 1024));
t = cyclesToRead_nKx16(nK, (uint16_t*)imem, &res);
Serial.printf("IRAM Memory Read: %6d cycles for %dK by 16, %3d AVG cycles/transfer (sum %08x)\r\n", t, nK, t / (nK * 1024), res);
t = cyclesToWrite_nKx16_viaInline(nK, (uint16_t*)imem);
Serial.printf("IRAM Memory Write Inline: %6d cycles for %dK by 16, %3d AVG cycles/transfer\r\n", t, nK, t / (nK * 1024));
t = cyclesToRead_nKx16_viaInline(nK, (uint16_t*)imem, &res);
Serial.printf("IRAM Memory Read Inline: %6d cycles for %dK by 16, %3d AVG cycles/transfer (sum %08x)\r\n", t, nK, t / (nK * 1024), res);
t = cyclesToWrite_nKx16(nK, (uint16_t*)mem);
Serial.printf("DRAM Memory Write: %6d cycles for %dK by 16, %3d AVG cycles/transfer\r\n", t, nK, t / (nK * 1024));
t = cyclesToRead_nKx16(nK, (uint16_t*)mem, &res);
Serial.printf("DRAM Memory Read: %6d cycles for %dK by 16, %3d AVG cycles/transfer (sum %08x)\r\n", t, nK, t / (nK * 1024), res);
t = cyclesToWrite_nKx8(nK, (uint8_t*)imem);
Serial.printf("IRAM Memory Write: %6d cycles for %dK by 8, %3d AVG cycles/transfer\r\n", t, nK, t / (nK * 1024));
t = cyclesToRead_nKx8(nK, (uint8_t*)imem, &res);
Serial.printf("IRAM Memory Read: %6d cycles for %dK by 8, %3d AVG cycles/transfer (sum %08x)\r\n", t, nK, t / (nK * 1024), res);
t = cyclesToWrite_nKx8_viaInline(nK, (uint8_t*)imem);
Serial.printf("IRAM Memory Write Inline: %6d cycles for %dK by 8, %3d AVG cycles/transfer\r\n", t, nK, t / (nK * 1024));
t = cyclesToRead_nKx8_viaInline(nK, (uint8_t*)imem, &res);
Serial.printf("IRAM Memory Read Inline: %6d cycles for %dK by 8, %3d AVG cycles/transfer (sum %08x)\r\n", t, nK, t / (nK * 1024), res);
t = cyclesToWrite_nKx8(nK, (uint8_t*)mem);
Serial.printf("DRAM Memory Write: %6d cycles for %dK by 8, %3d AVG cycles/transfer\r\n", t, nK, t / (nK * 1024));
t = cyclesToRead_nKx8(nK, (uint8_t*)mem, &res);
Serial.printf("DRAM Memory Read: %6d cycles for %dK by 8, %3d AVG cycles/transfer (sum %08x)\r\n", t, nK, t / (nK * 1024), res);
}
void setup() {
WiFi.persistent(false);
WiFi.mode(WIFI_OFF);
// Serial.begin(74880);
Serial.begin(115200);
delay(20);
Serial.printf_P(PSTR("\n\nSetup ...\r\n"));
#ifndef UMM_HEAP_IRAM
Serial.printf("\r\n"
"This example needs IRAM Heap support enabled.\r\n"
" eg. Arduino IDE 'Tools->MMU:\"16KB cache + 48KB IRAM and 2nd Heap (shared)\"'\r\n"
"This build has IRAM Heap support disabled.\r\n"
"In this situation, all IRAM requests are satisfied with DRAM.\r\n\r\n");
#endif
// Compiling with Secondary Heap option does not change malloc to use the
// IRAM region. It will continue to use the builtin DRAM until we request
// otherwise.
Serial.printf("DRAM free: %6d\r\n", ESP.getFreeHeap());
uint32_t *mem = (uint32_t *)malloc(2 * 1024 * sizeof(uint32_t));
Serial.printf("DRAM buffer: Address %p, free %d\r\n", mem, ESP.getFreeHeap());
if (!mem) {
return;
}
// Now request from the IRAM heap
#ifdef USE_SET_IRAM_HEAP
ESP.setIramHeap();
Serial.printf("IRAM free: %6d\r\n", ESP.getFreeHeap());
uint32_t *imem = (uint32_t *)malloc(2 * 1024 * sizeof(uint32_t));
Serial.printf("IRAM buffer: Address %p, free %d\r\n", imem, ESP.getFreeHeap());
// Make sure we go back to the DRAM heap for other allocations. Don't forget to ESP.resetHeap()!
ESP.resetHeap();
#else
uint32_t *imem;
{
HeapSelectIram ephemeral;
// This class effectively does this
// size_t _heap_id = umm_get_current_heap_id();
// umm_set_heap_by_id(UMM_HEAP_IRAM);
// ...
// umm_set_heap_by_id(_heap_id);
Serial.printf("IRAM free: %6d\r\n", ESP.getFreeHeap());
imem = (uint32_t *)malloc(2 * 1024 * sizeof(uint32_t));
Serial.printf("IRAM buffer: Address %p, free %d\r\n", imem, ESP.getFreeHeap());
}
#endif
if (!imem) {
return;
}
uint32_t res;
uint32_t t;
int nK = 1;
Serial.println();
t = cyclesToWrite_nKx32(nK, imem);
Serial.printf("IRAM Memory Write: %6d cycles for %dK by 32, %3d AVG cycles/transfer\r\n", t, nK, t / (nK * 1024));
t = cyclesToRead_nKx32(nK, imem, &res);
Serial.printf("IRAM Memory Read: %6d cycles for %dK by 32, %3d AVG cycles/transfer (sum %08x)\r\n", t, nK, t / (nK * 1024), res);
t = cyclesToWrite_nKx32(nK, mem);
Serial.printf("DRAM Memory Write: %6d cycles for %dK by 32, %3d AVG cycles/transfer\r\n", t, nK, t / (nK * 1024));
t = cyclesToRead_nKx32(nK, mem, &res);
Serial.printf("DRAM Memory Read: %6d cycles for %dK by 32, %3d AVG cycles/transfer (sum %08x)\r\n", t, nK, t / (nK * 1024), res);
Serial.println();
perfTest_nK(1, mem, imem);
Serial.println();
perfTest_nK(4, mem, imem);
Serial.println();
#ifdef USE_SET_IRAM_HEAP
// Let's use IRAM heap to make a big ole' String
ESP.setIramHeap();
String s = "";
for (int i = 0; i < 100; i++) {
s += i;
s += ' ';
}
ESP.resetHeap();
Serial.printf("DRAM free: %6d\r\n", ESP.getFreeHeap());
ESP.setIramHeap();
Serial.printf("IRAM free: %6d\r\n", ESP.getFreeHeap());
ESP.resetHeap();
Serial.printf("String: %s\r\n", s.c_str());
ESP.setIramHeap();
Serial.printf("IRAM free: %6d\r\n", ESP.getFreeHeap());
ESP.resetHeap();
#else
{
// Let's use IRAM heap to make a big ole' String
HeapSelectIram ephemeral;
String s = "";
for (int i = 0; i < 100; i++) {
s += i;
s += ' ';
}
{
HeapSelectDram ephemeral;
Serial.printf("DRAM free: %6d\r\n", ESP.getFreeHeap());
}
// Back to IRAM
Serial.printf("IRAM free: %6d\r\n", ESP.getFreeHeap());
Serial.printf("String: %s\r\n", s.c_str());
}
{
HeapSelectIram ephemeral;
Serial.printf("IRAM free: %6d\r\n", ESP.getFreeHeap());
}
#endif
// Note that free/realloc will use the heap specified when the pointer was created.
// No need to change heaps to delete an object, only to create it.
free(imem);
free(mem);
imem = NULL;
mem = NULL;
Serial.printf("DRAM free: %6d\r\n", ESP.getFreeHeap());
#ifdef USE_SET_IRAM_HEAP
ESP.setIramHeap();
Serial.printf("IRAM free: %6d\r\n", ESP.getFreeHeap());
ESP.resetHeap();
#else
{
HeapSelectIram ephemeral;
Serial.printf("IRAM free: %6d\r\n", ESP.getFreeHeap());
}
#endif
{
ETS_PRINTF("Try and allocate all of the heap in one chunk\n");
HeapSelectIram ephemeral;
size_t free_iram = ESP.getFreeHeap();
ETS_PRINTF("IRAM free: %6d\n", free_iram);
uint32_t hfree;
uint16_t hmax;
uint8_t hfrag;
ESP.getHeapStats(&hfree, &hmax, &hfrag);
ETS_PRINTF("ESP.getHeapStats(free: %u, max: %u, frag: %u)\n",
hfree, hmax, hfrag);
if (free_iram > UMM_OVERHEAD_ADJUST) {
void *all = malloc(free_iram - UMM_OVERHEAD_ADJUST);
ETS_PRINTF("%p = malloc(%u)\n", all, free_iram);
umm_info(NULL, true);
free_iram = ESP.getFreeHeap();
ETS_PRINTF("IRAM free: %6d\n", free_iram);
free(all);
ETS_PRINTF("IRAM free: %6d\n", ESP.getFreeHeap());
}
}
}
void processKey(Print& out, int hotKey) {
switch (hotKey) {
case 'd': {
HeapSelectDram ephemeral;
umm_info(NULL, true);
break;
}
case 'i': {
HeapSelectIram ephemeral;
umm_info(NULL, true);
break;
}
case 'h': {
{
HeapSelectIram ephemeral;
Serial.printf(PSTR("IRAM ESP.getFreeHeap: %u\n"), ESP.getFreeHeap());
}
{
HeapSelectDram ephemeral;
Serial.printf(PSTR("DRAM ESP.getFreeHeap: %u\r\n"), ESP.getFreeHeap());
}
break;
}
case 'R':
out.printf_P(PSTR("Restart, ESP.restart(); ...\r\n"));
ESP.restart();
break;
case '\r':
out.println();
case '\n':
break;
case '?':
out.println();
out.println(F("Press a key + <enter>"));
out.println(F(" h - Free Heap Report;"));
out.println(F(" i - iRAM umm_info(null, true);"));
out.println(F(" d - dRAM umm_info(null, true);"));
out.println(F(" R - Restart, ESP.restart();"));
out.println(F(" ? - Print Help"));
out.println();
break;
default:
out.printf_P(PSTR("\"%c\" - Not an option? / ? - help"), hotKey);
out.println();
processKey(out, '?');
break;
}
}
void loop(void) {
if (Serial.available() > 0) {
int hotKey = Serial.read();
processKey(Serial, hotKey);
}
}

View File

@ -71,8 +71,12 @@ getResetInfo KEYWORD2
getResetInfoPtr KEYWORD2
eraseConfig KEYWORD2
getCycleCount KEYWORD2
enableVM KEYWORD2
setExternalHeap KEYWORD2
setDramHeap KEYWORD2
setIramHeap KEYWORD2
resetHeap KEYWORD2
random->KEYWORD2
setCtMinDataLength KEYWORD2
getCtMinDataLength KEYWORD2
setCtMaxDataLength KEYWORD2
@ -87,6 +91,7 @@ produce KEYWORD2
encrypt KEYWORD2
decrypt KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################