From 8b662ed3b345d2ded3c2ac484949b03e10bdc43c Mon Sep 17 00:00:00 2001 From: M Hightower <27247790+mhightower83@users.noreply.github.com> Date: Sun, 6 Dec 2020 05:15:42 -0800 Subject: [PATCH] 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 * 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. --- boards.txt | 410 ++++++++++++++++++ bootloaders/eboot/eboot.c | 4 +- bootloaders/eboot/eboot.elf | Bin 44860 -> 44788 bytes cores/esp8266/Arduino.h | 4 + cores/esp8266/Esp-frag.cpp | 19 +- cores/esp8266/Esp.cpp | 76 +++- cores/esp8266/Esp.h | 10 +- cores/esp8266/StackThunk.cpp | 9 + cores/esp8266/core_esp8266_main.cpp | 22 +- cores/esp8266/core_esp8266_non32xfer.cpp | 217 +++++++++ cores/esp8266/core_esp8266_non32xfer.h | 14 + cores/esp8266/esp8266_undocumented.h | 240 ++++++++++ cores/esp8266/exc-c-wrapper-handler.S | 208 +++++++++ cores/esp8266/mmu_iram.cpp | 192 ++++++++ cores/esp8266/mmu_iram.h | 221 ++++++++++ cores/esp8266/umm_malloc/umm_heap_select.h | 78 ++++ cores/esp8266/umm_malloc/umm_info.c | 119 ++--- cores/esp8266/umm_malloc/umm_integrity.c | 7 +- cores/esp8266/umm_malloc/umm_local.c | 120 ++++- cores/esp8266/umm_malloc/umm_local.h | 21 +- cores/esp8266/umm_malloc/umm_malloc.cpp | 394 ++++++++++++++--- cores/esp8266/umm_malloc/umm_malloc.h | 15 + cores/esp8266/umm_malloc/umm_malloc_cfg.h | 168 +++---- cores/esp8266/umm_malloc/umm_poison.c | 12 +- cores/esp8266/xtruntime-frames.h | 162 +++++++ doc/index.rst | 3 +- doc/mmu.rst | 237 ++++++++++ .../HelloServerBearSSL/HelloServerBearSSL.ino | 63 +++ .../src/WiFiClientSecureBearSSL.cpp | 61 ++- .../examples/HeapMetric/HeapMetric.ino | 17 + .../examples/IramReserve/IramReserve.ino | 124 ++++++ .../examples/IramReserve/ProcessKey.ino | 116 +++++ libraries/esp8266/examples/MMU48K/MMU48K.ino | 322 ++++++++++++++ libraries/esp8266/examples/MMU48K/timed.cpp | 16 + .../esp8266/examples/irammem/irammem.ino | 364 ++++++++++++++++ libraries/esp8266/keywords.txt | 7 +- platform.txt | 21 +- tests/host/common/MockEsp.cpp | 16 + tools/boards.txt.py | 30 +- tools/platformio-build.py | 21 +- tools/sdk/ld/eagle.app.v6.common.ld.h | 11 +- tools/sdk/ld/eagle.flash.16m14m.ld | 1 - tools/sdk/ld/eagle.flash.16m15m.ld | 1 - tools/sdk/ld/eagle.flash.1m.ld | 1 - tools/sdk/ld/eagle.flash.1m128.ld | 1 - tools/sdk/ld/eagle.flash.1m144.ld | 1 - tools/sdk/ld/eagle.flash.1m160.ld | 1 - tools/sdk/ld/eagle.flash.1m192.ld | 1 - tools/sdk/ld/eagle.flash.1m256.ld | 1 - tools/sdk/ld/eagle.flash.1m512.ld | 1 - tools/sdk/ld/eagle.flash.1m64.ld | 1 - tools/sdk/ld/eagle.flash.2m.ld | 1 - tools/sdk/ld/eagle.flash.2m128.ld | 1 - tools/sdk/ld/eagle.flash.2m1m.ld | 1 - tools/sdk/ld/eagle.flash.2m256.ld | 1 - tools/sdk/ld/eagle.flash.2m512.ld | 1 - tools/sdk/ld/eagle.flash.2m64.ld | 1 - tools/sdk/ld/eagle.flash.4m.ld | 1 - tools/sdk/ld/eagle.flash.4m1m.ld | 1 - tools/sdk/ld/eagle.flash.4m2m.ld | 1 - tools/sdk/ld/eagle.flash.4m3m.ld | 1 - tools/sdk/ld/eagle.flash.512k.ld | 1 - tools/sdk/ld/eagle.flash.512k128.ld | 1 - tools/sdk/ld/eagle.flash.512k32.ld | 1 - tools/sdk/ld/eagle.flash.512k64.ld | 1 - tools/sdk/ld/eagle.flash.8m6m.ld | 1 - tools/sdk/ld/eagle.flash.8m7m.ld | 1 - tools/sdk/ld/eagle.rom.addr.v6.ld | 5 + tools/sizes.py | 22 +- 69 files changed, 3926 insertions(+), 298 deletions(-) create mode 100644 cores/esp8266/core_esp8266_non32xfer.cpp create mode 100644 cores/esp8266/core_esp8266_non32xfer.h create mode 100644 cores/esp8266/exc-c-wrapper-handler.S create mode 100644 cores/esp8266/mmu_iram.cpp create mode 100644 cores/esp8266/mmu_iram.h create mode 100644 cores/esp8266/umm_malloc/umm_heap_select.h create mode 100644 cores/esp8266/xtruntime-frames.h create mode 100644 doc/mmu.rst create mode 100644 libraries/esp8266/examples/IramReserve/IramReserve.ino create mode 100644 libraries/esp8266/examples/IramReserve/ProcessKey.ino create mode 100644 libraries/esp8266/examples/MMU48K/MMU48K.ino create mode 100644 libraries/esp8266/examples/MMU48K/timed.cpp create mode 100644 libraries/esp8266/examples/irammem/irammem.ino diff --git a/boards.txt b/boards.txt index d32a47e85d..12afade093 100644 --- a/boards.txt +++ b/boards.txt @@ -24,6 +24,8 @@ menu.wipe=Erase Flash menu.sdk=Espressif FW menu.ssl=SSL Support menu.waveform=Waveform Flavour +menu.mmu=MMU +menu.non32xfer=Non-32-Bit Access ############################################################## generic.name=Generic ESP8266 Module @@ -68,6 +70,18 @@ generic.menu.waveform.pwm=Locked PWM generic.menu.waveform.pwm.build.waveform= generic.menu.waveform.phase=Locked Phase generic.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE +generic.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) +generic.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +generic.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) +generic.menu.mmu.4816.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 +generic.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) +generic.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP +generic.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) +generic.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +generic.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM +generic.menu.non32xfer.fast.build.non32xferflags= +generic.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) +generic.menu.non32xfer.safe.build.non32xferflags=-DNON32XFER_HANDLER generic.menu.ResetMethod.nodemcu=dtr (aka nodemcu) generic.menu.ResetMethod.nodemcu.upload.resetmethod=--before default_reset --after hard_reset generic.menu.ResetMethod.ck=no dtr (aka ck) @@ -541,6 +555,18 @@ esp8285.menu.waveform.pwm=Locked PWM esp8285.menu.waveform.pwm.build.waveform= esp8285.menu.waveform.phase=Locked Phase esp8285.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE +esp8285.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) +esp8285.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +esp8285.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) +esp8285.menu.mmu.4816.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 +esp8285.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) +esp8285.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP +esp8285.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) +esp8285.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +esp8285.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM +esp8285.menu.non32xfer.fast.build.non32xferflags= +esp8285.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) +esp8285.menu.non32xfer.safe.build.non32xferflags=-DNON32XFER_HANDLER esp8285.menu.ResetMethod.nodemcu=dtr (aka nodemcu) esp8285.menu.ResetMethod.nodemcu.upload.resetmethod=--before default_reset --after hard_reset esp8285.menu.ResetMethod.ck=no dtr (aka ck) @@ -884,6 +910,18 @@ gen4iod.menu.waveform.pwm=Locked PWM gen4iod.menu.waveform.pwm.build.waveform= gen4iod.menu.waveform.phase=Locked Phase gen4iod.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE +gen4iod.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) +gen4iod.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +gen4iod.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) +gen4iod.menu.mmu.4816.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 +gen4iod.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) +gen4iod.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP +gen4iod.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) +gen4iod.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +gen4iod.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM +gen4iod.menu.non32xfer.fast.build.non32xferflags= +gen4iod.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) +gen4iod.menu.non32xfer.safe.build.non32xferflags=-DNON32XFER_HANDLER gen4iod.upload.resetmethod=--before default_reset --after hard_reset gen4iod.menu.FlashMode.dout=DOUT (compatible) gen4iod.menu.FlashMode.dout.build.flash_mode=dout @@ -1142,6 +1180,18 @@ huzzah.menu.waveform.pwm=Locked PWM huzzah.menu.waveform.pwm.build.waveform= huzzah.menu.waveform.phase=Locked Phase huzzah.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE +huzzah.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) +huzzah.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +huzzah.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) +huzzah.menu.mmu.4816.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 +huzzah.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) +huzzah.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP +huzzah.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) +huzzah.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +huzzah.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM +huzzah.menu.non32xfer.fast.build.non32xferflags= +huzzah.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) +huzzah.menu.non32xfer.safe.build.non32xferflags=-DNON32XFER_HANDLER huzzah.upload.resetmethod=--before default_reset --after hard_reset huzzah.build.flash_mode=qio huzzah.build.flash_flags=-DFLASHMODE_QIO @@ -1333,6 +1383,18 @@ wifi_slot.menu.waveform.pwm=Locked PWM wifi_slot.menu.waveform.pwm.build.waveform= wifi_slot.menu.waveform.phase=Locked Phase wifi_slot.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE +wifi_slot.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) +wifi_slot.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +wifi_slot.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) +wifi_slot.menu.mmu.4816.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 +wifi_slot.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) +wifi_slot.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP +wifi_slot.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) +wifi_slot.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +wifi_slot.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM +wifi_slot.menu.non32xfer.fast.build.non32xferflags= +wifi_slot.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) +wifi_slot.menu.non32xfer.safe.build.non32xferflags=-DNON32XFER_HANDLER wifi_slot.upload.resetmethod=--before default_reset --after hard_reset wifi_slot.menu.FlashFreq.40=40MHz wifi_slot.menu.FlashFreq.40.build.flash_freq=40 @@ -1650,6 +1712,18 @@ arduino-esp8266.menu.waveform.pwm=Locked PWM arduino-esp8266.menu.waveform.pwm.build.waveform= arduino-esp8266.menu.waveform.phase=Locked Phase arduino-esp8266.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE +arduino-esp8266.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) +arduino-esp8266.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +arduino-esp8266.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) +arduino-esp8266.menu.mmu.4816.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 +arduino-esp8266.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) +arduino-esp8266.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP +arduino-esp8266.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) +arduino-esp8266.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +arduino-esp8266.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM +arduino-esp8266.menu.non32xfer.fast.build.non32xferflags= +arduino-esp8266.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) +arduino-esp8266.menu.non32xfer.safe.build.non32xferflags=-DNON32XFER_HANDLER arduino-esp8266.upload.resetmethod=--before no_reset --after soft_reset arduino-esp8266.build.flash_mode=qio arduino-esp8266.build.flash_flags=-DFLASHMODE_QIO @@ -1842,6 +1916,18 @@ espmxdevkit.menu.waveform.pwm=Locked PWM espmxdevkit.menu.waveform.pwm.build.waveform= espmxdevkit.menu.waveform.phase=Locked Phase espmxdevkit.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE +espmxdevkit.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) +espmxdevkit.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +espmxdevkit.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) +espmxdevkit.menu.mmu.4816.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 +espmxdevkit.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) +espmxdevkit.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP +espmxdevkit.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) +espmxdevkit.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +espmxdevkit.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM +espmxdevkit.menu.non32xfer.fast.build.non32xferflags= +espmxdevkit.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) +espmxdevkit.menu.non32xfer.safe.build.non32xferflags=-DNON32XFER_HANDLER espmxdevkit.upload.resetmethod=--before default_reset --after hard_reset espmxdevkit.build.flash_mode=dout espmxdevkit.build.flash_flags=-DFLASHMODE_DOUT @@ -2074,6 +2160,18 @@ oak.menu.waveform.pwm=Locked PWM oak.menu.waveform.pwm.build.waveform= oak.menu.waveform.phase=Locked Phase oak.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE +oak.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) +oak.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +oak.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) +oak.menu.mmu.4816.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 +oak.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) +oak.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP +oak.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) +oak.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +oak.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM +oak.menu.non32xfer.fast.build.non32xferflags= +oak.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) +oak.menu.non32xfer.safe.build.non32xferflags=-DNON32XFER_HANDLER oak.upload.resetmethod=--before no_reset --after soft_reset oak.build.flash_mode=dio oak.build.flash_flags=-DFLASHMODE_DIO @@ -2274,6 +2372,18 @@ espduino.menu.waveform.pwm=Locked PWM espduino.menu.waveform.pwm.build.waveform= espduino.menu.waveform.phase=Locked Phase espduino.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE +espduino.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) +espduino.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +espduino.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) +espduino.menu.mmu.4816.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 +espduino.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) +espduino.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP +espduino.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) +espduino.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +espduino.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM +espduino.menu.non32xfer.fast.build.non32xferflags= +espduino.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) +espduino.menu.non32xfer.safe.build.non32xferflags=-DNON32XFER_HANDLER espduino.build.flash_mode=dio espduino.build.flash_flags=-DFLASHMODE_DIO espduino.build.flash_freq=40 @@ -2464,6 +2574,18 @@ espectro.menu.waveform.pwm=Locked PWM espectro.menu.waveform.pwm.build.waveform= espectro.menu.waveform.phase=Locked Phase espectro.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE +espectro.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) +espectro.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +espectro.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) +espectro.menu.mmu.4816.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 +espectro.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) +espectro.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP +espectro.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) +espectro.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +espectro.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM +espectro.menu.non32xfer.fast.build.non32xferflags= +espectro.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) +espectro.menu.non32xfer.safe.build.non32xferflags=-DNON32XFER_HANDLER espectro.upload.resetmethod=--before default_reset --after hard_reset espectro.build.flash_mode=dio espectro.build.flash_flags=-DFLASHMODE_DIO @@ -2655,6 +2777,18 @@ espino.menu.waveform.pwm=Locked PWM espino.menu.waveform.pwm.build.waveform= espino.menu.waveform.phase=Locked Phase espino.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE +espino.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) +espino.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +espino.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) +espino.menu.mmu.4816.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 +espino.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) +espino.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP +espino.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) +espino.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +espino.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM +espino.menu.non32xfer.fast.build.non32xferflags= +espino.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) +espino.menu.non32xfer.safe.build.non32xferflags=-DNON32XFER_HANDLER espino.menu.ResetMethod.nodemcu=dtr (aka nodemcu) espino.menu.ResetMethod.nodemcu.upload.resetmethod=--before default_reset --after hard_reset espino.menu.ResetMethod.ck=no dtr (aka ck) @@ -2849,6 +2983,18 @@ espresso_lite_v1.menu.waveform.pwm=Locked PWM espresso_lite_v1.menu.waveform.pwm.build.waveform= espresso_lite_v1.menu.waveform.phase=Locked Phase espresso_lite_v1.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE +espresso_lite_v1.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) +espresso_lite_v1.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +espresso_lite_v1.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) +espresso_lite_v1.menu.mmu.4816.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 +espresso_lite_v1.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) +espresso_lite_v1.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP +espresso_lite_v1.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) +espresso_lite_v1.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +espresso_lite_v1.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM +espresso_lite_v1.menu.non32xfer.fast.build.non32xferflags= +espresso_lite_v1.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) +espresso_lite_v1.menu.non32xfer.safe.build.non32xferflags=-DNON32XFER_HANDLER espresso_lite_v1.build.flash_mode=dio espresso_lite_v1.build.flash_flags=-DFLASHMODE_DIO espresso_lite_v1.build.flash_freq=40 @@ -3043,6 +3189,18 @@ espresso_lite_v2.menu.waveform.pwm=Locked PWM espresso_lite_v2.menu.waveform.pwm.build.waveform= espresso_lite_v2.menu.waveform.phase=Locked Phase espresso_lite_v2.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE +espresso_lite_v2.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) +espresso_lite_v2.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +espresso_lite_v2.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) +espresso_lite_v2.menu.mmu.4816.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 +espresso_lite_v2.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) +espresso_lite_v2.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP +espresso_lite_v2.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) +espresso_lite_v2.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +espresso_lite_v2.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM +espresso_lite_v2.menu.non32xfer.fast.build.non32xferflags= +espresso_lite_v2.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) +espresso_lite_v2.menu.non32xfer.safe.build.non32xferflags=-DNON32XFER_HANDLER espresso_lite_v2.build.flash_mode=dio espresso_lite_v2.build.flash_flags=-DFLASHMODE_DIO espresso_lite_v2.build.flash_freq=40 @@ -3247,6 +3405,18 @@ sonoff.menu.waveform.pwm=Locked PWM sonoff.menu.waveform.pwm.build.waveform= sonoff.menu.waveform.phase=Locked Phase sonoff.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE +sonoff.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) +sonoff.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +sonoff.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) +sonoff.menu.mmu.4816.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 +sonoff.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) +sonoff.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP +sonoff.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) +sonoff.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +sonoff.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM +sonoff.menu.non32xfer.fast.build.non32xferflags= +sonoff.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) +sonoff.menu.non32xfer.safe.build.non32xferflags=-DNON32XFER_HANDLER sonoff.upload.resetmethod=--before no_reset --after soft_reset sonoff.build.flash_mode=dout sonoff.build.flash_flags=-DFLASHMODE_DOUT @@ -3478,6 +3648,18 @@ inventone.menu.waveform.pwm=Locked PWM inventone.menu.waveform.pwm.build.waveform= inventone.menu.waveform.phase=Locked Phase inventone.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE +inventone.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) +inventone.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +inventone.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) +inventone.menu.mmu.4816.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 +inventone.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) +inventone.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP +inventone.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) +inventone.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +inventone.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM +inventone.menu.non32xfer.fast.build.non32xferflags= +inventone.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) +inventone.menu.non32xfer.safe.build.non32xferflags=-DNON32XFER_HANDLER inventone.upload.resetmethod=--before default_reset --after hard_reset inventone.build.flash_mode=dio inventone.build.flash_flags=-DFLASHMODE_DIO @@ -3669,6 +3851,18 @@ d1_mini.menu.waveform.pwm=Locked PWM d1_mini.menu.waveform.pwm.build.waveform= d1_mini.menu.waveform.phase=Locked Phase d1_mini.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE +d1_mini.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) +d1_mini.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +d1_mini.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) +d1_mini.menu.mmu.4816.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 +d1_mini.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) +d1_mini.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP +d1_mini.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) +d1_mini.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +d1_mini.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM +d1_mini.menu.non32xfer.fast.build.non32xferflags= +d1_mini.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) +d1_mini.menu.non32xfer.safe.build.non32xferflags=-DNON32XFER_HANDLER d1_mini.upload.resetmethod=--before default_reset --after hard_reset d1_mini.build.flash_mode=dio d1_mini.build.flash_flags=-DFLASHMODE_DIO @@ -3860,6 +4054,18 @@ d1_mini_lite.menu.waveform.pwm=Locked PWM d1_mini_lite.menu.waveform.pwm.build.waveform= d1_mini_lite.menu.waveform.phase=Locked Phase d1_mini_lite.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE +d1_mini_lite.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) +d1_mini_lite.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +d1_mini_lite.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) +d1_mini_lite.menu.mmu.4816.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 +d1_mini_lite.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) +d1_mini_lite.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP +d1_mini_lite.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) +d1_mini_lite.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +d1_mini_lite.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM +d1_mini_lite.menu.non32xfer.fast.build.non32xferflags= +d1_mini_lite.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) +d1_mini_lite.menu.non32xfer.safe.build.non32xferflags=-DNON32XFER_HANDLER d1_mini_lite.upload.resetmethod=--before default_reset --after hard_reset d1_mini_lite.build.flash_mode=dout d1_mini_lite.build.flash_flags=-DFLASHMODE_DOUT @@ -4091,6 +4297,18 @@ d1_mini_pro.menu.waveform.pwm=Locked PWM d1_mini_pro.menu.waveform.pwm.build.waveform= d1_mini_pro.menu.waveform.phase=Locked Phase d1_mini_pro.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE +d1_mini_pro.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) +d1_mini_pro.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +d1_mini_pro.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) +d1_mini_pro.menu.mmu.4816.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 +d1_mini_pro.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) +d1_mini_pro.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP +d1_mini_pro.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) +d1_mini_pro.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +d1_mini_pro.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM +d1_mini_pro.menu.non32xfer.fast.build.non32xferflags= +d1_mini_pro.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) +d1_mini_pro.menu.non32xfer.safe.build.non32xferflags=-DNON32XFER_HANDLER d1_mini_pro.upload.resetmethod=--before default_reset --after hard_reset d1_mini_pro.build.flash_mode=dio d1_mini_pro.build.flash_flags=-DFLASHMODE_DIO @@ -4265,6 +4483,18 @@ d1.menu.waveform.pwm=Locked PWM d1.menu.waveform.pwm.build.waveform= d1.menu.waveform.phase=Locked Phase d1.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE +d1.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) +d1.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +d1.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) +d1.menu.mmu.4816.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 +d1.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) +d1.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP +d1.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) +d1.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +d1.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM +d1.menu.non32xfer.fast.build.non32xferflags= +d1.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) +d1.menu.non32xfer.safe.build.non32xferflags=-DNON32XFER_HANDLER d1.upload.resetmethod=--before default_reset --after hard_reset d1.build.flash_mode=dio d1.build.flash_flags=-DFLASHMODE_DIO @@ -4456,6 +4686,18 @@ nodemcu.menu.waveform.pwm=Locked PWM nodemcu.menu.waveform.pwm.build.waveform= nodemcu.menu.waveform.phase=Locked Phase nodemcu.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE +nodemcu.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) +nodemcu.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +nodemcu.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) +nodemcu.menu.mmu.4816.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 +nodemcu.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) +nodemcu.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP +nodemcu.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) +nodemcu.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +nodemcu.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM +nodemcu.menu.non32xfer.fast.build.non32xferflags= +nodemcu.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) +nodemcu.menu.non32xfer.safe.build.non32xferflags=-DNON32XFER_HANDLER nodemcu.upload.resetmethod=--before default_reset --after hard_reset nodemcu.build.flash_mode=qio nodemcu.build.flash_flags=-DFLASHMODE_QIO @@ -4647,6 +4889,18 @@ nodemcuv2.menu.waveform.pwm=Locked PWM nodemcuv2.menu.waveform.pwm.build.waveform= nodemcuv2.menu.waveform.phase=Locked Phase nodemcuv2.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE +nodemcuv2.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) +nodemcuv2.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +nodemcuv2.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) +nodemcuv2.menu.mmu.4816.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 +nodemcuv2.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) +nodemcuv2.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP +nodemcuv2.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) +nodemcuv2.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +nodemcuv2.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM +nodemcuv2.menu.non32xfer.fast.build.non32xferflags= +nodemcuv2.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) +nodemcuv2.menu.non32xfer.safe.build.non32xferflags=-DNON32XFER_HANDLER nodemcuv2.upload.resetmethod=--before default_reset --after hard_reset nodemcuv2.build.flash_mode=dio nodemcuv2.build.flash_flags=-DFLASHMODE_DIO @@ -4842,6 +5096,18 @@ modwifi.menu.waveform.pwm=Locked PWM modwifi.menu.waveform.pwm.build.waveform= modwifi.menu.waveform.phase=Locked Phase modwifi.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE +modwifi.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) +modwifi.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +modwifi.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) +modwifi.menu.mmu.4816.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 +modwifi.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) +modwifi.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP +modwifi.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) +modwifi.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +modwifi.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM +modwifi.menu.non32xfer.fast.build.non32xferflags= +modwifi.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) +modwifi.menu.non32xfer.safe.build.non32xferflags=-DNON32XFER_HANDLER modwifi.upload.resetmethod=--before no_reset --after soft_reset modwifi.build.flash_mode=qio modwifi.build.flash_flags=-DFLASHMODE_QIO @@ -5053,6 +5319,18 @@ phoenix_v1.menu.waveform.pwm=Locked PWM phoenix_v1.menu.waveform.pwm.build.waveform= phoenix_v1.menu.waveform.phase=Locked Phase phoenix_v1.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE +phoenix_v1.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) +phoenix_v1.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +phoenix_v1.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) +phoenix_v1.menu.mmu.4816.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 +phoenix_v1.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) +phoenix_v1.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP +phoenix_v1.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) +phoenix_v1.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +phoenix_v1.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM +phoenix_v1.menu.non32xfer.fast.build.non32xferflags= +phoenix_v1.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) +phoenix_v1.menu.non32xfer.safe.build.non32xferflags=-DNON32XFER_HANDLER phoenix_v1.build.flash_mode=dio phoenix_v1.build.flash_flags=-DFLASHMODE_DIO phoenix_v1.build.flash_freq=40 @@ -5247,6 +5525,18 @@ phoenix_v2.menu.waveform.pwm=Locked PWM phoenix_v2.menu.waveform.pwm.build.waveform= phoenix_v2.menu.waveform.phase=Locked Phase phoenix_v2.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE +phoenix_v2.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) +phoenix_v2.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +phoenix_v2.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) +phoenix_v2.menu.mmu.4816.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 +phoenix_v2.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) +phoenix_v2.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP +phoenix_v2.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) +phoenix_v2.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +phoenix_v2.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM +phoenix_v2.menu.non32xfer.fast.build.non32xferflags= +phoenix_v2.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) +phoenix_v2.menu.non32xfer.safe.build.non32xferflags=-DNON32XFER_HANDLER phoenix_v2.build.flash_mode=dio phoenix_v2.build.flash_flags=-DFLASHMODE_DIO phoenix_v2.build.flash_freq=40 @@ -5441,6 +5731,18 @@ eduinowifi.menu.waveform.pwm=Locked PWM eduinowifi.menu.waveform.pwm.build.waveform= eduinowifi.menu.waveform.phase=Locked Phase eduinowifi.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE +eduinowifi.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) +eduinowifi.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +eduinowifi.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) +eduinowifi.menu.mmu.4816.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 +eduinowifi.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) +eduinowifi.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP +eduinowifi.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) +eduinowifi.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +eduinowifi.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM +eduinowifi.menu.non32xfer.fast.build.non32xferflags= +eduinowifi.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) +eduinowifi.menu.non32xfer.safe.build.non32xferflags=-DNON32XFER_HANDLER eduinowifi.upload.resetmethod=--before default_reset --after hard_reset eduinowifi.build.flash_mode=dio eduinowifi.build.flash_flags=-DFLASHMODE_DIO @@ -5632,6 +5934,18 @@ wiolink.menu.waveform.pwm=Locked PWM wiolink.menu.waveform.pwm.build.waveform= wiolink.menu.waveform.phase=Locked Phase wiolink.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE +wiolink.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) +wiolink.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +wiolink.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) +wiolink.menu.mmu.4816.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 +wiolink.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) +wiolink.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP +wiolink.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) +wiolink.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +wiolink.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM +wiolink.menu.non32xfer.fast.build.non32xferflags= +wiolink.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) +wiolink.menu.non32xfer.safe.build.non32xferflags=-DNON32XFER_HANDLER wiolink.upload.resetmethod=--before default_reset --after hard_reset wiolink.build.flash_mode=qio wiolink.build.flash_flags=-DFLASHMODE_QIO @@ -5823,6 +6137,18 @@ blynk.menu.waveform.pwm=Locked PWM blynk.menu.waveform.pwm.build.waveform= blynk.menu.waveform.phase=Locked Phase blynk.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE +blynk.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) +blynk.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +blynk.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) +blynk.menu.mmu.4816.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 +blynk.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) +blynk.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP +blynk.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) +blynk.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +blynk.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM +blynk.menu.non32xfer.fast.build.non32xferflags= +blynk.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) +blynk.menu.non32xfer.safe.build.non32xferflags=-DNON32XFER_HANDLER blynk.upload.resetmethod=--before default_reset --after hard_reset blynk.build.flash_mode=qio blynk.build.flash_flags=-DFLASHMODE_QIO @@ -6014,6 +6340,18 @@ thing.menu.waveform.pwm=Locked PWM thing.menu.waveform.pwm.build.waveform= thing.menu.waveform.phase=Locked Phase thing.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE +thing.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) +thing.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +thing.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) +thing.menu.mmu.4816.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 +thing.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) +thing.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP +thing.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) +thing.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +thing.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM +thing.menu.non32xfer.fast.build.non32xferflags= +thing.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) +thing.menu.non32xfer.safe.build.non32xferflags=-DNON32XFER_HANDLER thing.upload.resetmethod=--before no_reset --after soft_reset thing.build.flash_mode=qio thing.build.flash_flags=-DFLASHMODE_QIO @@ -6205,6 +6543,18 @@ thingdev.menu.waveform.pwm=Locked PWM thingdev.menu.waveform.pwm.build.waveform= thingdev.menu.waveform.phase=Locked Phase thingdev.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE +thingdev.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) +thingdev.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +thingdev.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) +thingdev.menu.mmu.4816.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 +thingdev.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) +thingdev.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP +thingdev.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) +thingdev.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +thingdev.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM +thingdev.menu.non32xfer.fast.build.non32xferflags= +thingdev.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) +thingdev.menu.non32xfer.safe.build.non32xferflags=-DNON32XFER_HANDLER thingdev.upload.resetmethod=--before default_reset --after hard_reset thingdev.build.flash_mode=dio thingdev.build.flash_flags=-DFLASHMODE_DIO @@ -6396,6 +6746,18 @@ esp210.menu.waveform.pwm=Locked PWM esp210.menu.waveform.pwm.build.waveform= esp210.menu.waveform.phase=Locked Phase esp210.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE +esp210.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) +esp210.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +esp210.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) +esp210.menu.mmu.4816.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 +esp210.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) +esp210.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP +esp210.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) +esp210.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +esp210.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM +esp210.menu.non32xfer.fast.build.non32xferflags= +esp210.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) +esp210.menu.non32xfer.safe.build.non32xferflags=-DNON32XFER_HANDLER esp210.upload.resetmethod=--before no_reset --after soft_reset esp210.build.flash_mode=qio esp210.build.flash_flags=-DFLASHMODE_QIO @@ -6587,6 +6949,18 @@ espinotee.menu.waveform.pwm=Locked PWM espinotee.menu.waveform.pwm.build.waveform= espinotee.menu.waveform.phase=Locked Phase espinotee.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE +espinotee.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) +espinotee.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +espinotee.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) +espinotee.menu.mmu.4816.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 +espinotee.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) +espinotee.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP +espinotee.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) +espinotee.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +espinotee.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM +espinotee.menu.non32xfer.fast.build.non32xferflags= +espinotee.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) +espinotee.menu.non32xfer.safe.build.non32xferflags=-DNON32XFER_HANDLER espinotee.upload.resetmethod=--before default_reset --after hard_reset espinotee.build.flash_mode=qio espinotee.build.flash_flags=-DFLASHMODE_QIO @@ -6778,6 +7152,18 @@ wifiduino.menu.waveform.pwm=Locked PWM wifiduino.menu.waveform.pwm.build.waveform= wifiduino.menu.waveform.phase=Locked Phase wifiduino.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE +wifiduino.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) +wifiduino.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +wifiduino.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) +wifiduino.menu.mmu.4816.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 +wifiduino.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) +wifiduino.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP +wifiduino.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) +wifiduino.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +wifiduino.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM +wifiduino.menu.non32xfer.fast.build.non32xferflags= +wifiduino.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) +wifiduino.menu.non32xfer.safe.build.non32xferflags=-DNON32XFER_HANDLER wifiduino.upload.resetmethod=--before default_reset --after hard_reset wifiduino.build.flash_mode=dio wifiduino.build.flash_flags=-DFLASHMODE_DIO @@ -6986,6 +7372,18 @@ wifinfo.menu.waveform.pwm=Locked PWM wifinfo.menu.waveform.pwm.build.waveform= wifinfo.menu.waveform.phase=Locked Phase wifinfo.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE +wifinfo.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) +wifinfo.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +wifinfo.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) +wifinfo.menu.mmu.4816.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 +wifinfo.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) +wifinfo.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP +wifinfo.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) +wifinfo.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +wifinfo.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM +wifinfo.menu.non32xfer.fast.build.non32xferflags= +wifinfo.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) +wifinfo.menu.non32xfer.safe.build.non32xferflags=-DNON32XFER_HANDLER wifinfo.upload.resetmethod=--before default_reset --after hard_reset wifinfo.build.flash_mode=qio wifinfo.build.flash_flags=-DFLASHMODE_QIO @@ -7224,6 +7622,18 @@ cw01.menu.waveform.pwm=Locked PWM cw01.menu.waveform.pwm.build.waveform= cw01.menu.waveform.phase=Locked Phase cw01.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE +cw01.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) +cw01.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +cw01.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) +cw01.menu.mmu.4816.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 +cw01.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) +cw01.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP +cw01.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) +cw01.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +cw01.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM +cw01.menu.non32xfer.fast.build.non32xferflags= +cw01.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) +cw01.menu.non32xfer.safe.build.non32xferflags=-DNON32XFER_HANDLER cw01.upload.resetmethod=--before default_reset --after hard_reset cw01.menu.CrystalFreq.26=26 MHz cw01.menu.CrystalFreq.40=40 MHz diff --git a/bootloaders/eboot/eboot.c b/bootloaders/eboot/eboot.c index cb65557270..6e188d806e 100644 --- a/bootloaders/eboot/eboot.c +++ b/bootloaders/eboot/eboot.c @@ -68,7 +68,9 @@ int load_app_from_flash_raw(const uint32_t flash_addr) load = true; } - if (address >= 0x40100000 && address < 0x40108000) { + // The final IRAM size, once boot has completed, can be either 32K or 48K. + // Allow for the higher in range testing. + if (address >= 0x40100000 && address < 0x4010C000) { load = true; } diff --git a/bootloaders/eboot/eboot.elf b/bootloaders/eboot/eboot.elf index 5843b8fed46267eb1df2be2e043373c25fcef2c5..52004fdbb83113c8fbab2d5a7dcefa7c1cae4cf8 100755 GIT binary patch delta 3594 zcmbVOeQ;FO6~Fg=?R!f$+3e5kJ{u;7T=Vuz1u&>1Pp&~_Y|3Rq|@gOvWxdn={3e{}k0 z&VBd%&bjBDciuhc-eVt2H!n)tNAtcTS-O)SkNTYtx;jotjKwv}jNJycvwuC3qAvMP zACnc^0)Vn)dzjI{-_eFY%ZoJ;B^5)Z(Z2Z@gtA4v0F^-H4Z>KdFF8 zk2kgS8zMP3KVc`7wMzFD$eDhdiErfw$`=wgQGN$lqKmw$T9saiPt*SZNUQPEoav`A zXtgGz=)K}lu4l||Nj}Y01c_5GCT2#`n_!akIv?x!3%n_+%$#>?QX2_cDz5o5v@7t)R|{8OcK>6> z5$HgL-K|L*Ae9R$g*9~38Tpdb2H#d0^Q`RbW&ENUqa7$c-bGl%KQ_PUVer#HBfs7A z2;-l4dl8qshiCX@Mk@R@AXX%WrK&@5bhkT-V3H5dI=X-9j; zD3Orgm37~ebi|R#ayl7L<1Pxy(%O|j)IXKtUSr11{^E?hOnwF_ufF9J3kc`X?DfF~6%dB-yF)_rXY!v3 zpT9)gPf0l;+WaG9sgJJrCve^)wic#he7UfoYz?V&8c)H_@E6WV0Yxx)oHE);AJ#dB zGH?b;zX27#LfB#<7gh3uqN>P~e2$j;i%7`^8TF*RgQB}=W(w6muSp}xIY`ccyhphh zE^1HjB+*FoFW$WFjMSPwTSvg>Iaxdu2=P&3L!iE&wnS`O1V&*_#PifZA50PP6i46< zI3(i9jsX1*A~j15{2myQnxh4-<4vS3cLc(a6KN|P0XN=6+UmHmQxQWoNN0iFa6{Cu zt#kw;@I#~|sewJPL1as@1rFd%q`1|OVsqL6+Z6Y&x zUmrET?xQv=vd8);En@aqAAJdP#UAUUgz5GhqA!^3rnK>}HuZ1LND_4TV7w%L8Z6-N zi{AxX`H%>N0+`PYjpjGRx==O$qc|FB;e3OY7bQCD<2^X5p| ztF$=*KgW{9YEMGG+%qKX>f+~+2eYeIHrhinG;4_hxgzOW(vMl!n z(u^cLYvS3!-EWH4vO2z843-V?-J-rcz}H55%WEYb7O#)b$+`wVyhI9Zp!ZB)1KBN+ zCXsvI#B#37;@0?f>6R+yR^(>{NqUv1kknsfF`2}mVYG?873HBb1n!P$tR`lVJ18?_ zde`(Y{-VWDpP^4LEEh^;bN*!tdOUZjc50#9znkJApn@lo>G`O&IiQqP;)%+nl=GIz z^tuo59uoaR9HhgMT2@35|tWV!7bpbyvimLPZ zWzmH3j96FQ$TP+1>cGq)3b4m950%#oKYY*zFBLaUw3s%W%?%V(G0()Nkh~InDPglI z#GXcq@KNxO5Y{IitQqc~0&OB?AhG6iXb~yTBu2h&dMc}Z01y$13&i|7Myo_soI6ya zf5*0?=QZR-vhJ8>n?l~LdW?0oSlueM(6#DTW@?T2NhIi}B2{Z)PtG#Cn9VHMUqVrj z6)ero%iEpEy@K5dDDGat-bNVa+*(zT?fXxyrE!; zq08HkYOTu~3U(u)Tt}>6(-En4c~e@9Qb?@Jo6>4B=H=~Lt#x@r#gaAa@`j2fr`F{S z6-$hDc|*ls05ds$iwM^hYgDm`ZqZveolg)a>OSJr#O_HQ+%NtyX}Y=B)3s!ASKqS7M5J%Ni$CL^M^ANiNQrSc z(7Bo>uQp`7vt_ra>P}7XGdV+(B|}2nmPjc!YGQeJS4syd$mxcLkr^tMLb+XB?Jh0N zH=~#V&Q;D-iEbA1pG4ErW&Eq?$)z7E{6chaMW0mAfiIQW z{{Qpyn&ZEbgG-CU(Uw)EinP%yvYs#dMn3*qhMGlaryBk7+VvjWXV~O2D-(Io2mhCa z(a_Fe+*V%r(7tNXqRy2IdY7#2jQV~UlKwL&pQwJp63*@S#7*-Z!}vPm|42tx@m@?jz$NdsZ{6eYxn zpb@MJI<&!KrnCfTt+fa>h1s#4Dis~YelPn5Sp0j~Qv#VGh_z<=V06fH6o^`p z(aA^)r4$yYy#z&=J?F5V-Om%Fu0ntZhwBn_iPr{&{ zn`&#BS17BfB<)=&8+92MAs6N}N#808*Zi2ViRuwx>33kR=|;U-j!!cSS<)EfxiF7m zFq&*eGY``XMS(FXET7@4fW&J)i1kKu=5N4cnwweDVlz4B3A$7i7+=TGT=(mc7x}u^ zEEUE)_e@w0nSH#n&Emw4F)fTnyUkRXWeU}Ya%%3yhZqZNYqjPv)?R2kjhT>4V0hYb66zL+Fx`B_!*#) zi&rlZ@?5Y74%F=z9?E?aW_ZCoSk7C4Fy$LrYEd~wS$N)+cA4o;_v$By``k9Sqs z-kH}yX(h`FzOf`1apa4_ZXx~B$3Zz-J4*KUw{YBjcHH7`-uQFuXNdC}I>0gZ~J4|y$lYr;&(hxLG%{*R~S3YaXo7Pw|8PX6%_m_uYJtMJ_3IMRJ;R&d_0hi zm%9BxZ?Knjw%Ccj3heKo-0k?z9l-B%2|*2|hTKV0OMey|;HA%}hahP{+%b@|cl*gG z%a;czR90pDnUnGgEi44XxOs_(z_)}V0G274eFB@}M@-`&L zK|z;7Co4L04` zOw#WOe+7q3GRqUb46{snfga9($7Tz*&hBHm0|h8{K{XVS8C!Tm%N`>m-$cL6}>CJR}Y&5?Gv8v(BIZ)Smztp4b^zH0U z%#B@?U-^wbYF+Q6E-Z2%M?C1Gyhyl{ee@@oYwlzpO@Wr_zC!CNM`v)_WXzcU4{tmZ zI&vu0qUS41tG2 z^Z9TxI~A?gIs|=-&jPJ3gXhA+SAteCsy6##ww6XbWs(wH6-*+Cp0`X7N#=EwY)o#YchG znRX6qrlg&w9o11kC)dr1s=;Ig$C)R53HoDoL1+OoEN}HrgFsiukGj_Ao6LJupBA!_ zwTwJZp>YKnAF*aUZJUjc2A=T-wU3)5`)Fv~UU`P5)`aDzL{CkVBDc_s6ABBsSIR0S zvI+j=?9e>6Tcu29_i~OXjA-=Lgib{mG{3eaZ#+vcNk2==svRbu88nO~w5PTva*Dy7 zNsR}Y8Im5(%$T0_tA*U9@9)k73t$%B%`lc+DfkuR#8K2HWHa zj>qJ`805c{*mP@yL~350xK(~ZGh&nEV%ip~m(SAC*e!~1(WkK%xr}1k#!q^6y)p)Xh7POhPt?7-Ip~a*`PEIlLgCj`||d6{6@hJ0!q12uvZa=eR)H{^4z(+p|EYZ zup7ZRmp2qFGkkgbQIm6dL&0taROCq(Y%U^oE^k_!RSk)AdDGf$#=g9L*5q8?P_b;y zxxArb*{O4RL&Y-VT;5QzXTVHJJx#GmQG+Wsy@Prt&6G{_>ZA{3EA5=zC8PB3$uspq zY{X!qWy)uxBggQMs)BEkQeI80aAYxn%j;m6cQ0Ebd+1efPQ^iaxz4 z^Cp5hRJ?3Zev&w}tV>CE zgca1FWndJRj-^oUq6;gl^Fy|>v`5WKfyis9t+pGfMk%@~Q~8sicwc}zSIw1I62Dya zo+jU?^hf=4yw{c3fA5`2gwGFEy!8M1T)((kieibH-fGP?5M*z+)0*{_H_O?7N#^HI z8;N@!cqpKJsnUw|Rr0Ddu>P0@&~#T z!J!Rmg7z#-m1pUT7Yg+t?uXx|z;L15K=s2V`nEmzAL+y44B9yCmA|8H!zHqfUK}n_ La(+(dhyDKn7OTQQ diff --git a/cores/esp8266/Arduino.h b/cores/esp8266/Arduino.h index ec0d460d8b..b484a3765a 100644 --- a/cores/esp8266/Arduino.h +++ b/cores/esp8266/Arduino.h @@ -225,6 +225,10 @@ void optimistic_yield(uint32_t interval_us); #include #include + +#include "mmu_iram.h" + + using std::min; using std::max; using std::round; diff --git a/cores/esp8266/Esp-frag.cpp b/cores/esp8266/Esp-frag.cpp index dc5970e854..30332aca62 100644 --- a/cores/esp8266/Esp-frag.cpp +++ b/cores/esp8266/Esp-frag.cpp @@ -30,14 +30,15 @@ void EspClass::getHeapStats(uint32_t* hfree, uint16_t* hmax, uint8_t* hfrag) // 100 * (1 - sqrt(sum(hole-sizeĀ²)) / sum(hole-size)) umm_info(NULL, false); - uint8_t block_size = umm_block_size(); + + uint32_t free_size = umm_free_heap_size_core(umm_get_current_heap()); if (hfree) - *hfree = ummHeapInfo.freeBlocks * block_size; + *hfree = free_size; if (hmax) - *hmax = (uint16_t)ummHeapInfo.maxFreeContiguousBlocks * block_size; + *hmax = (uint16_t)umm_max_block_size_core(umm_get_current_heap()); if (hfrag) { - if (ummHeapInfo.freeBlocks) { - *hfrag = 100 - (sqrt32(ummHeapInfo.freeBlocksSquared) * 100) / ummHeapInfo.freeBlocks; + if (free_size) { + *hfrag = umm_fragmentation_metric_core(umm_get_current_heap()); } else { *hfrag = 0; } @@ -46,11 +47,5 @@ void EspClass::getHeapStats(uint32_t* hfree, uint16_t* hmax, uint8_t* hfrag) uint8_t EspClass::getHeapFragmentation() { -#ifdef UMM_INLINE_METRICS - return (uint8_t)umm_fragmentation_metric(); -#else - uint8_t hfrag; - getHeapStats(nullptr, nullptr, &hfrag); - return hfrag; -#endif + return (uint8_t)umm_fragmentation_metric(); } diff --git a/cores/esp8266/Esp.cpp b/cores/esp8266/Esp.cpp index 60990103f1..f4fe3a9a82 100644 --- a/cores/esp8266/Esp.cpp +++ b/cores/esp8266/Esp.cpp @@ -28,6 +28,8 @@ #include "cont.h" #include "coredecls.h" +#include "umm_malloc/umm_malloc.h" +// #include "core_esp8266_vm.h" #include extern "C" { @@ -516,45 +518,45 @@ uint8_t *EspClass::random(uint8_t *resultArray, const size_t outputSizeBytes) co { /** * The ESP32 Technical Reference Manual v4.1 chapter 24 has the following to say about random number generation (no information found for ESP8266): - * + * * "When used correctly, every 32-bit value the system reads from the RNG_DATA_REG register of the random number generator is a true random number. * These true random numbers are generated based on the noise in the Wi-Fi/BT RF system. * When Wi-Fi and BT are disabled, the random number generator will give out pseudo-random numbers. - * + * * When Wi-Fi or BT is enabled, the random number generator is fed two bits of entropy every APB clock cycle (normally 80 MHz). * Thus, for the maximum amount of entropy, it is advisable to read the random register at a maximum rate of 5 MHz. * A data sample of 2 GB, read from the random number generator with Wi-Fi enabled and the random register read at 5 MHz, * has been tested using the Dieharder Random Number Testsuite (version 3.31.1). * The sample passed all tests." - * + * * Since ESP32 is the sequal to ESP8266 it is unlikely that the ESP8266 is able to generate random numbers more quickly than 5 MHz when run at a 80 MHz frequency. * A maximum random number frequency of 0.5 MHz is used here to leave some margin for possibly inferior components in the ESP8266. * It should be noted that the ESP8266 has no Bluetooth functionality, so turning the WiFi off is likely to cause RANDOM_REG32 to use pseudo-random numbers. - * - * It is possible that yield() must be called on the ESP8266 to properly feed the hardware random number generator new bits, since there is only one processor core available. + * + * It is possible that yield() must be called on the ESP8266 to properly feed the hardware random number generator new bits, since there is only one processor core available. * However, no feeding requirements are mentioned in the ESP32 documentation, and using yield() could possibly cause extended delays during number generation. * Thus only delayMicroseconds() is used below. - */ + */ constexpr uint8_t cooldownMicros = 2; static uint32_t lastCalledMicros = micros() - cooldownMicros; uint32_t randomNumber = 0; - + for(size_t byteIndex = 0; byteIndex < outputSizeBytes; ++byteIndex) { if(byteIndex % 4 == 0) { // Old random number has been used up (random number could be exactly 0, so we can't check for that) - + uint32_t timeSinceLastCall = micros() - lastCalledMicros; if(timeSinceLastCall < cooldownMicros) delayMicroseconds(cooldownMicros - timeSinceLastCall); - + randomNumber = RANDOM_REG32; lastCalledMicros = micros(); } - + resultArray[byteIndex] = randomNumber; randomNumber >>= 8; } @@ -969,3 +971,57 @@ String EspClass::getSketchMD5() result = md5.toString(); return result; } + +void EspClass::enableVM() +{ +#ifdef UMM_HEAP_EXTERNAL + if (!vmEnabled) + install_vm_exception_handler(); + vmEnabled = true; +#endif +} + +void EspClass::setExternalHeap() +{ +#ifdef UMM_HEAP_EXTERNAL + if (vmEnabled) + umm_push_heap(UMM_HEAP_EXTERNAL); +#endif +} + +void EspClass::setIramHeap() +{ +#ifdef UMM_HEAP_IRAM + umm_push_heap(UMM_HEAP_IRAM); +#endif +} + +void EspClass::setDramHeap() +{ +#if defined(UMM_HEAP_EXTERNAL) && !defined(UMM_HEAP_IRAM) + if (vmEnabled) { + if (!umm_push_heap(UMM_HEAP_DRAM)) { + panic(); + } + } +#elif defined(UMM_HEAP_IRAM) + if (!umm_push_heap(UMM_HEAP_DRAM)) { + panic(); + } +#endif +} + +void EspClass::resetHeap() +{ +#if defined(UMM_HEAP_EXTERNAL) && !defined(UMM_HEAP_IRAM) + if (vmEnabled) { + if (!umm_pop_heap()) { + panic(); + } + } +#elif defined(UMM_HEAP_IRAM) + if (!umm_pop_heap()) { + panic(); + } +#endif +} diff --git a/cores/esp8266/Esp.h b/cores/esp8266/Esp.h index 54f8d21752..bc7fcb1e04 100644 --- a/cores/esp8266/Esp.h +++ b/cores/esp8266/Esp.h @@ -123,7 +123,7 @@ class EspClass { uint8_t getBootMode(); #if defined(F_CPU) || defined(CORE_MOCK) - constexpr + constexpr #endif inline uint8_t getCpuFreqMHz() const __attribute__((always_inline)) { @@ -215,7 +215,15 @@ class EspClass { #else uint32_t getCycleCount(); #endif // !defined(CORE_MOCK) + void enableVM(); + void setDramHeap(); + void setIramHeap(); + void setExternalHeap(); + void resetHeap(); private: +#ifdef UMM_HEAP_EXTERNAL + bool vmEnabled = false; +#endif /** * @brief Replaces @a byteCount bytes of a 4 byte block on flash * diff --git a/cores/esp8266/StackThunk.cpp b/cores/esp8266/StackThunk.cpp index 4b708bcc6f..7456fcaeeb 100644 --- a/cores/esp8266/StackThunk.cpp +++ b/cores/esp8266/StackThunk.cpp @@ -31,6 +31,8 @@ #include "debug.h" #include "StackThunk.h" #include +#include +#include extern "C" { @@ -48,7 +50,14 @@ void stack_thunk_add_ref() { stack_thunk_refcnt++; if (stack_thunk_refcnt == 1) { + DBG_MMU_PRINTF("\nStackThunk malloc(%u)\n", _stackSize * sizeof(uint32_t)); + // The stack must be in DRAM, or an Soft WDT will follow. Not sure why, + // maybe too much time is consumed with the non32-bit exception handler. + // Also, interrupt handling on an IRAM stack would be very slow. + // Strings on the stack would be very slow to access as well. + HeapSelectDram ephemeral; stack_thunk_ptr = (uint32_t *)malloc(_stackSize * sizeof(uint32_t)); + DBG_MMU_PRINTF("StackThunk stack_thunk_ptr: %p\n", stack_thunk_ptr); if (!stack_thunk_ptr) { // This is a fatal error, stop the sketch DEBUGV("Unable to allocate BearSSL stack\n"); diff --git a/cores/esp8266/core_esp8266_main.cpp b/cores/esp8266/core_esp8266_main.cpp index 21724e2eb4..5f3a5d96b6 100644 --- a/cores/esp8266/core_esp8266_main.cpp +++ b/cores/esp8266/core_esp8266_main.cpp @@ -35,6 +35,9 @@ extern "C" { #include #include "gdb_hooks.h" #include "flash_quirks.h" +#include +#include + #define LOOP_TASK_PRIORITY 1 #define LOOP_QUEUE_SIZE 1 @@ -204,7 +207,9 @@ static void loop_wrapper() { static void loop_task(os_event_t *events) { (void) events; s_cycles_at_yield_start = ESP.getCycleCount(); + ESP.resetHeap(); cont_run(g_pcont, &loop_wrapper); + ESP.setDramHeap(); if (cont_check(g_pcont) != 0) { panic(); } @@ -256,6 +261,7 @@ void init_done() { std::set_terminate(__unhandled_exception_cpp); do_global_ctors(); esp_schedule(); + ESP.setDramHeap(); } /* This is the entry point of the application. @@ -311,14 +317,22 @@ extern "C" void app_entry_redefinable(void) cont_t s_cont __attribute__((aligned(16))); g_pcont = &s_cont; +#ifdef DEV_DEBUG_MMU_IRAM + DBG_MMU_PRINT_STATUS(); + + DBG_MMU_PRINT_IRAM_BANK_REG(0, ""); + + DBG_MMU_PRINTF("\nCall call_user_start()\n"); +#endif + /* Call the entry point of the SDK code. */ call_user_start(); } - static void app_entry_custom (void) __attribute__((weakref("app_entry_redefinable"))); extern "C" void app_entry (void) { + umm_init(); return app_entry_custom(); } @@ -342,6 +356,12 @@ extern "C" void user_init(void) { cont_init(g_pcont); +#if defined(NON32XFER_HANDLER) || defined(MMU_IRAM_HEAP) + install_non32xfer_exception_handler(); +#endif +#if defined(MMU_IRAM_HEAP) + umm_init_iram(); +#endif preinit(); // Prior to C++ Dynamic Init (not related to above init() ). Meant to be user redefinable. ets_task(loop_task, diff --git a/cores/esp8266/core_esp8266_non32xfer.cpp b/cores/esp8266/core_esp8266_non32xfer.cpp new file mode 100644 index 0000000000..eb4741e1ed --- /dev/null +++ b/cores/esp8266/core_esp8266_non32xfer.cpp @@ -0,0 +1,217 @@ +/* 020819 + Based on PR https://github.com/esp8266/Arduino/pull/6978 + Enhanced to also handle store operations to iRAM and optional range + validation. Also improved failed path to generate crash report. + And, partially refactored. + + Apologies if this is being pedantic, I was getting confused over these so + I tried to understand what makes them different. + + EXCCAUSE_LOAD_STORE_ERROR 3 is a non-32-bit load or store to an address that + only supports a full 32-bit aligned transfer like IRAM or ICACHE. i.e., No + 8-bit char or 16-bit short transfers allowed. + + EXCCAUSE_UNALIGNED 9 is an exception cause when load or store is not on an + aligned boundary that matches the element's width. + eg. *(short *)0x3FFF8001 = 1; or *(long *)0x3FFF8002 = 1; + + */ + +/* + * This exception handler, allows for byte or short accesses to iRAM or PROGMEM + * to succeed without causing a crash. It is still preferred to use the xxx_P + * macros whenever possible, since they are probably 30x faster than this + * exception handler method. + * + * Code taken directly from @pvvx's public domain code in + * https://github.com/pvvx/esp8266web/blob/master/app/sdklib/system/app_main.c + * + * + */ + +#include +#define VERIFY_C_ASM_EXCEPTION_FRAME_STRUCTURE +#include +#include +#include +#include +#include + +extern "C" { + +#define LOAD_MASK 0x00f00fu +#define L8UI_MATCH 0x000002u +#define L16UI_MATCH 0x001002u +#define L16SI_MATCH 0x009002u +#define S8I_MATCH 0x004002u +#define S16I_MATCH 0x005002u + +#define EXCCAUSE_LOAD_STORE_ERROR 3 /* Non 32-bit read/write error */ + +static fn_c_exception_handler_t old_c_handler = NULL; + +static IRAM_ATTR void non32xfer_exception_handler(struct __exception_frame *ef, int cause) +{ + do { + /* + Had to split out some of the asm, compiler was reusing a register that it + needed later. A crash would come or go away with the slightest unrelated + changes elsewhere in the function. + + Register a15 was used for epc1, then clobbered for rsr. Maybe an + __asm("":::"memory") before starting the asm would help for these cases. + For this instance moved setting epc1 closer to where it was used. + Edit. "&" on output register would have resolved the problem. + Refactored to reduce and consolidate register usage. + */ + uint32_t insn; + __asm( + "movi %0, ~3\n\t" /* prepare a mask for the EPC */ + "and %0, %0, %1\n\t" /* apply mask for 32bit aligned base */ + "ssa8l %1\n\t" /* set up shift register for src op */ + "l32i %1, %0, 0\n\t" /* load part 1 */ + "l32i %0, %0, 4\n\t" /* load part 2 */ + "src %0, %0, %1\n\t" /* right shift to get faulting instruction */ + :"=&r"(insn) + :"r"(ef->epc) + : + ); + + uint32_t what = insn & LOAD_MASK; + uint32_t valmask = 0; + + uint32_t is_read = 1; + if (L8UI_MATCH == what || S8I_MATCH == what) { + valmask = 0xffu; + if (S8I_MATCH == what) { + is_read = 0; + } + } else if (L16UI_MATCH == what || L16SI_MATCH == what || S16I_MATCH == what) { + valmask = 0xffffu; + if (S16I_MATCH == what) { + is_read = 0; + } + } else { + continue; /* fail */ + } + + int regno = (insn & 0x0000f0u) >> 4; + if (regno == 1) { + continue; /* we can't support storing into a1, just die */ + } else if (regno != 0) { + --regno; /* account for skipped a1 in exception_frame */ + } + + uint32_t excvaddr; + /* read out the faulting address */ + __asm("rsr %0, EXCVADDR;" :"=r"(excvaddr)::); + +#ifdef DEBUG_ESP_MMU + /* debug option to validate address so we don't hide memory access bugs in APP */ + if (mmu_is_iram((void *)excvaddr) || (is_read && mmu_is_icache((void *)excvaddr))) { + /* all is good */ + } else { + continue; /* fail */ + } +#endif + + if (is_read) { + /* Load, shift and mask down to correct size */ + uint32_t val = (*(uint32_t *)(excvaddr & ~0x3)); + val >>= (excvaddr & 0x3) * 8; + val &= valmask; + + /* Sign-extend for L16SI, if applicable */ + if (what == L16SI_MATCH && (val & 0x8000)) { + val |= 0xffff0000; + } + + ef->a_reg[regno] = val; /* carry out the load */ + + } else { /* is write */ + uint32_t val = ef->a_reg[regno]; /* get value to store from register */ + val <<= (excvaddr & 0x3) * 8; + valmask <<= (excvaddr & 0x3) * 8; + val &= valmask; + + /* Load, mask out field, and merge */ + uint32_t dst_val = (*(uint32_t *)(excvaddr & ~0x3)); + dst_val &= (~valmask); + dst_val |= val; + (*(uint32_t *)(excvaddr & ~0x3)) = dst_val; /* carry out the store */ + } + + ef->epc += 3; /* resume at following instruction */ + return; + + } while(false); + +/* Fail request, die */ + /* + The old handler points to the SDK. Be alert for HWDT when Calling with + INTLEVEL != 0. I cannot create it any more. I thought I saw this as a + problem; however, my test case shows no problem ?? Maybe I was confused. + */ + if (old_c_handler) { // if (0 == (ef->ps & 0x0F)) { + DBG_MMU_PRINTF("\ncalling previous load/store handler(%p)\n", old_c_handler); + old_c_handler(ef, cause); + return; + } + + /* + Calling _xtos_unhandled_exception(ef, cause) in the Boot ROM, gets us a + hardware wdt. + + Use panic instead as a fall back. It will produce a stack trace. + */ + panic(); +} + +/* + An issue, the Boot ROM "C" wrapper for exception handlers, + _xtos_c_wrapper_handler, turns interrupts back on. To address this issue we + use our replacement in file `exc-c-wrapper-handler.S`. + + An overview, of an exception at entry: New interrupts are blocked by EXCM + being set. Once cleared, interrupts above the current INTLEVEL and exceptions + (w/o creating a DoubleException) can occur. + + Using our replacement for _xtos_c_wrapper_handler, INTLEVEL is raised to 15 + with EXCM cleared. + + The original Boot ROM `_xtos_c_wrapper_handler` would set INTLEVEL to 1 with + EXCM cleared, saved registers, then do a `rsil 0`, and called the registered + "C" Exception handler with interrupts fully enabled! Our replacement keeps + INTLEVEL at 15. This is needed to support the Arduino model of interrupts + disabled while an ISR runs. + + And we also need it for umm_malloc to work safely with an IRAM heap from an + ISR call. While malloc() will supply DRAM for all allocation from an ISR, + we want free() to safely operate from an ISR to avoid a leak potential. + + This replacement "C" Wrapper is only applied to this exception handler. + */ + +#define ROM_xtos_c_wrapper_handler (reinterpret_cast<_xtos_handler>(0x40000598)) + +static void _set_exception_handler_wrapper(int cause) { + _xtos_handler old_wrapper = _xtos_exc_handler_table[cause]; + if (old_wrapper == ROM_xtos_c_wrapper_handler) { + _xtos_exc_handler_table[cause] = _xtos_c_wrapper_handler; + } +} + +void install_non32xfer_exception_handler(void) { + if (NULL == old_c_handler) { + // Set the "C" exception handler the wrapper will call + old_c_handler = + _xtos_set_exception_handler(EXCCAUSE_LOAD_STORE_ERROR, + non32xfer_exception_handler); + + // Set the replacement ASM based exception "C" wrapper function which will + // be calling `non32xfer_exception_handler`. + _set_exception_handler_wrapper(EXCCAUSE_LOAD_STORE_ERROR); + } +} + +}; diff --git a/cores/esp8266/core_esp8266_non32xfer.h b/cores/esp8266/core_esp8266_non32xfer.h new file mode 100644 index 0000000000..93151239c4 --- /dev/null +++ b/cores/esp8266/core_esp8266_non32xfer.h @@ -0,0 +1,14 @@ +#ifndef __CORE_ESP8266_NON32XFER_H +#define __CORE_ESP8266_NON32XFER_H + +#ifdef __cplusplus +extern "C" { +#endif + +extern void install_non32xfer_exception_handler(); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/cores/esp8266/esp8266_undocumented.h b/cores/esp8266/esp8266_undocumented.h index 22216d962a..a19835218b 100644 --- a/cores/esp8266/esp8266_undocumented.h +++ b/cores/esp8266/esp8266_undocumented.h @@ -1,9 +1,17 @@ // ROM and blob calls without official headers available +#if !defined(__ESP8266_UNDOCUMENTED_H) && !(defined(_ASMLANGUAGE) || defined(__ASSEMBLER__)) +#define __ESP8266_UNDOCUMENTED_H + #ifdef __cplusplus extern "C" { #endif +#ifndef XCHAL_EXCCAUSE_NUM +// from tools/xtensa-lx106-elf/include/xtensa/config/core.h:629:#define XCHAL_EXCCAUSE_NUM 64 +#define XCHAL_EXCCAUSE_NUM 64 +#endif + // ROM extern void rom_i2c_writeReg_Mask(int, int, int, int, int, int); @@ -34,6 +42,238 @@ extern int ets_uart_printf(const char *format, ...) __attribute__ ((format (prin extern void ets_delay_us(uint32_t us); +#ifndef GDBSTUB_H +/* + GDBSTUB duplicates these with some variances that are not compatible with our + references (offsets), which are synced with those used by the BootROM. + Specifically, the BootROM does not have register "a1" in the structure where + GDBSTUB does. +*/ + +/* + This structure is used in the argument list of "C" callable exception handlers. + See `_xtos_set_exception_handler` details below. +*/ +struct __exception_frame +{ + uint32_t epc; + uint32_t ps; + uint32_t sar; + uint32_t unused; + union { + struct { + uint32_t a0; + // note: no a1 here! + uint32_t a2; + uint32_t a3; + uint32_t a4; + uint32_t a5; + uint32_t a6; + uint32_t a7; + uint32_t a8; + uint32_t a9; + uint32_t a10; + uint32_t a11; + uint32_t a12; + uint32_t a13; + uint32_t a14; + uint32_t a15; + }; + uint32_t a_reg[15]; + }; + uint32_t cause; +}; +#endif + +/* + Most of the comments here are gleamed from the xtensa files found at the site + listed below and are mostly unverified: + https://github.com/qca/open-ath9k-htc-firmware/tree/master/sboot/magpie_1_1/sboot/athos/src/xtos + * exc-c-wrapper-handler.S + * exc-sethandler.c +*/ + +/* + The Boot ROM sets up a table of dispatch handlers at 0x3FFFC000. This table + has an entry for each of the EXCCAUSE values, 0 through 63. The exception + handler at the `User Exception Vector` uses EXCCAUSE with the base address + 0x3FFFC000 to build a jump address to the respective cause handler. Of the + cause handle functions, `_xtos_c_wrapper_handler` and `_xtos_unhandled_exception` + are of interest. + + Exception handler entries that do not have a specific handler are set to + `_xtos_unhandled_exception`. This handler will execute a `break 1, 1` + (0x4000DC4Bu) before doing a `rfe` (return from exception). Since the PC has + not changed, the event that caused the 1st exception will likely keep + repeating until the HWDT kicks in. + + These exception handling functions are in assembly, and do not conform to the + typical "C" function conventions. However, some form of prototype/typedef is + needed to reference these function addresses in "C" code. In + `RTOS_SDK/components/esp8266/include/xtensa/xtruntime.h`, it uses a compounded + definition that equates to `void (*)(...)` for .cpp modules to use. I have + noticed this creates sufficient confusion at compilation to get your attention + when used in the wrong place. I have copied that definition here. + + Added to eagle.rom.addr.v6.ld: + PROVIDE ( _xtos_exc_handler_table = 0x3fffc000 ); + PROVIDE ( _xtos_c_handler_table = 0x3fffc100 ); +*/ +#ifndef XTRUNTIME_H +// This is copy/paste from RTOS_SDK/components/esp8266/include/xtensa/xtruntime.h +#ifdef __cplusplus +typedef void (_xtos_handler_func)(...); +#else +typedef void (_xtos_handler_func)(); +#endif +typedef _xtos_handler_func *_xtos_handler; + +extern _xtos_handler _xtos_exc_handler_table[XCHAL_EXCCAUSE_NUM]; + +/* + Assembly-level handler, used in the _xtos_exc_handler_table[]. It is a wrapper + for calling registered "C" exception handlers. +*/ +_xtos_handler_func _xtos_c_wrapper_handler; + +/* + Assembly-level handler, used in the _xtos_exc_handler_table[]. It is the + default handler, for exceptions without a registered handler. +*/ +_xtos_handler_func _xtos_unhandled_exception; +#endif + + +#ifdef __cplusplus +// For these definitions, try to be more precise for .cpp module usage. + +/* + A detailed typdef for the "C" callable functions found in + `_xtos_c_handler_table[]` More details in `_xtos_set_exception_handler` + comments below. +*/ +typedef void (*fn_c_exception_handler_t)(struct __exception_frame *ef, int cause); + +/* + TMI maybe? However, it may be useful for a deep debugging session. + `_xtos_p_none` is the default "C" exception handler that fills the + _xtos_c_handler_table[]. It is present when an exception handler has not been + registered. It simply consist of a single instruction, `ret`. + It is also internally used by `_xtos_set_exception_handler(cause, NULL)` to + reset a "C" exception handler back to the unhandled state. The coresponding + `_xtos_exc_handler_table` entry will be set to `_xtos_unhandled_exception`. + Note, if nesting handlers is desired this must be implemented in the new "C" + exception handler(s) being registered. +*/ +extern void _xtos_p_none(struct __exception_frame *ef, int cause); + +/* + TMI maybe? + For `extern _xtos_handler _xtos_c_handler_table[XCHAL_EXCCAUSE_NUM];`, defined + in in `xtensa/xtos/exc-sethandler.c`. _xtos_handler is a generalized + definition that doesn't match the actual function definition of those + assigned to `_xtos_c_handler_table` entries. + + At this time we do not require direct access to this table. We perform updates + by calling the ROM function `_xtos_set_exception_handler`. + + A corrected version for .cpp would look like this: +*/ +extern fn_c_exception_handler_t _xtos_c_handler_table[XCHAL_EXCCAUSE_NUM]; + +/* + ROM API function `_xtos_set_exception_handler` registers a "C" callable + exception handler for a specified general exception, (EXCCAUSE value). (source + in xtensa/xtos/exc-sethandler.c) + * If `cause`/reason (EXCCAUSE) is out of range, >=64, it returns NULL. + * If the new exception handler is installed, it returns the previous handler. + * If the previous handler was `_xtos_unhandled_exception`/`_xtos_p_none`, it + returns NULL. + + Note, the installed "C" exception handler is noramlly called from the ROM + function _xtos_c_wrapper_handler with IRQs enabled. This build now includes a + replacement wrapper that is used with the "C" exception handler for + EXCCAUSE_LOAD_STORE_ERROR (3), Non 32-bit read/write error. + + This prototype has been corrected (changed from a generalized to specific + argument list) for the .cpp files in this projects; however, it does not match + the over generalized version in some Xtensa .h files (not currently part of + this project) + + To aid against future conflicts, keep these new defines limited to .cpp with + `#ifdef __cplusplus`. +*/ +extern fn_c_exception_handler_t _xtos_set_exception_handler(int cause, fn_c_exception_handler_t fn); +#endif + #ifdef __cplusplus }; #endif + +#endif + +#if defined(VERIFY_C_ASM_EXCEPTION_FRAME_STRUCTURE) || defined(_ASMLANGUAGE) || defined(__ASSEMBLER__) +/* + Extracted from information at + From https://github.com/fdivitto/ESPWebFramework/blob/master/SDK/xtensa-lx106-elf/xtensa-lx106-elf/lib/libhandlers-null.txt + + The UEXC_... values are create by the macro STRUCT_FIELD in `xtruntime-frames.h` + + These VERIFY_... values are used to confirm that the "C" structure offsets + match those generated in exc-c-wrapper-handler.S. +*/ +#define VERIFY_UEXC_pc 0x0000 +#define VERIFY_UEXC_ps 0x0004 +#define VERIFY_UEXC_sar 0x0008 +#define VERIFY_UEXC_vpri 0x000c +#define VERIFY_UEXC_a0 0x0010 +#define VERIFY_UEXC_a2 0x0014 +#define VERIFY_UEXC_a3 0x0018 +#define VERIFY_UEXC_a4 0x001c +#define VERIFY_UEXC_a5 0x0020 +#define VERIFY_UEXC_a6 0x0024 +#define VERIFY_UEXC_a7 0x0028 +#define VERIFY_UEXC_a8 0x002c +#define VERIFY_UEXC_a9 0x0030 +#define VERIFY_UEXC_a10 0x0034 +#define VERIFY_UEXC_a11 0x0038 +#define VERIFY_UEXC_a12 0x003c +#define VERIFY_UEXC_a13 0x0040 +#define VERIFY_UEXC_a14 0x0044 +#define VERIFY_UEXC_a15 0x0048 +#define VERIFY_UEXC_exccause 0x004c +#define VERIFY_UserFrameSize 0x0050 +#define VERIFY_UserFrameTotalSize 0x0100 +#endif + +#if defined(VERIFY_C_ASM_EXCEPTION_FRAME_STRUCTURE) && !(defined(_ASMLANGUAGE) || defined(__ASSEMBLER__)) +/* + A set of static_asserts test to confirm both "C" and ASM structures match. + + This only needs to be verified once. + We use `#define VERIFY_C_ASM_EXCEPTION_FRAME_STRUCTURE` to limit number of + times tested in a build. Testing is done from core_esp8266_non32xfer.cpp. + + ASM structure defines are verified in exc-c-wrapper-handler.S +*/ +static_assert(offsetof(struct __exception_frame, epc) == VERIFY_UEXC_pc, "offsetof(struct __exception_frame, epc) != VERIFY_UEXC_pc, expected 0x0000"); +static_assert(offsetof(struct __exception_frame, ps) == VERIFY_UEXC_ps, "offsetof(struct __exception_frame, ps) != VERIFY_UEXC_ps, expected 0x0004"); +static_assert(offsetof(struct __exception_frame, sar) == VERIFY_UEXC_sar, "offsetof(struct __exception_frame, sar) != VERIFY_UEXC_sar, expected 0x0008"); +static_assert(offsetof(struct __exception_frame, unused) == VERIFY_UEXC_vpri, "offsetof(struct __exception_frame, unused) != VERIFY_UEXC_vpri, expected 0x000c"); +static_assert(offsetof(struct __exception_frame, a0) == VERIFY_UEXC_a0, "offsetof(struct __exception_frame, a0) != VERIFY_UEXC_a0, expected 0x0010"); +static_assert(offsetof(struct __exception_frame, a2) == VERIFY_UEXC_a2, "offsetof(struct __exception_frame, a2) != VERIFY_UEXC_a2, expected 0x0014"); +static_assert(offsetof(struct __exception_frame, a3) == VERIFY_UEXC_a3, "offsetof(struct __exception_frame, a3) != VERIFY_UEXC_a3, expected 0x0018"); +static_assert(offsetof(struct __exception_frame, a4) == VERIFY_UEXC_a4, "offsetof(struct __exception_frame, a4) != VERIFY_UEXC_a4, expected 0x001c"); +static_assert(offsetof(struct __exception_frame, a5) == VERIFY_UEXC_a5, "offsetof(struct __exception_frame, a5) != VERIFY_UEXC_a5, expected 0x0020"); +static_assert(offsetof(struct __exception_frame, a6) == VERIFY_UEXC_a6, "offsetof(struct __exception_frame, a6) != VERIFY_UEXC_a6, expected 0x0024"); +static_assert(offsetof(struct __exception_frame, a7) == VERIFY_UEXC_a7, "offsetof(struct __exception_frame, a7) != VERIFY_UEXC_a7, expected 0x0028"); +static_assert(offsetof(struct __exception_frame, a8) == VERIFY_UEXC_a8, "offsetof(struct __exception_frame, a8) != VERIFY_UEXC_a8, expected 0x002c"); +static_assert(offsetof(struct __exception_frame, a9) == VERIFY_UEXC_a9, "offsetof(struct __exception_frame, a9) != VERIFY_UEXC_a9, expected 0x0030"); +static_assert(offsetof(struct __exception_frame, a10) == VERIFY_UEXC_a10, "offsetof(struct __exception_frame, a10) != VERIFY_UEXC_a10, expected 0x0034"); +static_assert(offsetof(struct __exception_frame, a11) == VERIFY_UEXC_a11, "offsetof(struct __exception_frame, a11) != VERIFY_UEXC_a11, expected 0x0038"); +static_assert(offsetof(struct __exception_frame, a12) == VERIFY_UEXC_a12, "offsetof(struct __exception_frame, a12) != VERIFY_UEXC_a12, expected 0x003c"); +static_assert(offsetof(struct __exception_frame, a13) == VERIFY_UEXC_a13, "offsetof(struct __exception_frame, a13) != VERIFY_UEXC_a13, expected 0x0040"); +static_assert(offsetof(struct __exception_frame, a14) == VERIFY_UEXC_a14, "offsetof(struct __exception_frame, a14) != VERIFY_UEXC_a14, expected 0x0044"); +static_assert(offsetof(struct __exception_frame, a15) == VERIFY_UEXC_a15, "offsetof(struct __exception_frame, a15) != VERIFY_UEXC_a15, expected 0x0048"); +static_assert(offsetof(struct __exception_frame, cause) == VERIFY_UEXC_exccause, "offsetof(struct __exception_frame, cause) != VERIFY_UEXC_exccause, expected 0x004c"); +#endif diff --git a/cores/esp8266/exc-c-wrapper-handler.S b/cores/esp8266/exc-c-wrapper-handler.S new file mode 100644 index 0000000000..f1ec1c391e --- /dev/null +++ b/cores/esp8266/exc-c-wrapper-handler.S @@ -0,0 +1,208 @@ +// exc-c-wrapper-handler.S, this is a reduced version of the original file at +// https://github.com/qca/open-ath9k-htc-firmware/blob/master/sboot/magpie_1_1/sboot/athos/src/xtos/exc-c-wrapper-handler.S#L62-L67 +// + +// exc-c-wrapper-handler.S - General Exception Handler that Dispatches C Handlers + +// Copyright (c) 2002-2004, 2006-2007, 2010 Tensilica Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +#include +#include +#include +// #include "xtos-internal.h" +// #ifdef SIMULATOR +// #include +// #endif + +#include "xtruntime-frames.h" +///////////////////////////////////////////////////////////////////////////// +// +// Verified that the ASM generated UEXC_xxx values match, the corresponding +// values in `struct __exception_frame` used in the "C" code. +// +#include "esp8266_undocumented.h" +.if (UEXC_pc != VERIFY_UEXC_pc) +.err +.endif +.if (UEXC_ps != VERIFY_UEXC_ps) +.err +.endif +.if (UEXC_sar != VERIFY_UEXC_sar) +.err +.endif +.if (UEXC_vpri != VERIFY_UEXC_vpri) +.err +.endif +.if (UEXC_a0 != VERIFY_UEXC_a0) +.err +.endif +.if (UEXC_a2 != VERIFY_UEXC_a2) +.err +.endif +.if (UEXC_a3 != VERIFY_UEXC_a3) +.err +.endif +.if (UEXC_a4 != VERIFY_UEXC_a4) +.err +.endif +.if (UEXC_a5 != VERIFY_UEXC_a5) +.err +.endif +.if (UEXC_a6 != VERIFY_UEXC_a6) +.err +.endif +.if (UEXC_a7 != VERIFY_UEXC_a7) +.err +.endif +.if (UEXC_a8 != VERIFY_UEXC_a8) +.err +.endif +.if (UEXC_a9 != VERIFY_UEXC_a9) +.err +.endif +.if (UEXC_a10 != VERIFY_UEXC_a10) +.err +.endif +.if (UEXC_a11 != VERIFY_UEXC_a11) +.err +.endif +.if (UEXC_a12 != VERIFY_UEXC_a12) +.err +.endif +.if (UEXC_a13 != VERIFY_UEXC_a13) +.err +.endif +.if (UEXC_a14 != VERIFY_UEXC_a14) +.err +.endif +.if (UEXC_a15 != VERIFY_UEXC_a15) +.err +.endif +.if (UEXC_exccause != VERIFY_UEXC_exccause) +.err +.endif +.if (UserFrameSize != VERIFY_UserFrameSize) +.err +.endif +.if (UserFrameTotalSize != VERIFY_UserFrameTotalSize) +.err +.endif +/////////////////////////////////////////////////////////////////////////////// + +/* + * This is the general exception assembly-level handler that dispatches C handlers. + */ + .section .iram.text + .align 4 + .literal_position + .global _xtos_c_wrapper_handler +_xtos_c_wrapper_handler: + + // HERE: a2, a3, a4 have been saved to exception stack frame allocated with a1 (sp). + // a2 contains EXCCAUSE. + s32i a5, a1, UEXC_a5 // a5 will get clobbered by ENTRY after the pseudo-CALL4 + // (a4..a15 spilled as needed; save if modified) + + //NOTA: Possible future improvement: + // keep interrupts disabled until we get into the handler, such that + // we don't have to save other critical state such as EXCVADDR here. +// @mhightower83 - This promise was broken by an "rsil a13, 0" below. + //rsr a3, EXCVADDR + s32i a2, a1, UEXC_exccause + //s32i a3, a1, UEXC_excvaddr + + // Set PS fields: + // EXCM = 0 + // WOE = __XTENSA_CALL0_ABI__ ? 0 : 1 + // UM = 1 + // INTLEVEL = EXCM_LEVEL = 1 + // CALLINC = __XTENSA_CALL0_ABI__ ? 0 : 1 + // OWB = 0 (really, a dont care if !__XTENSA_CALL0_ABI__) + +// movi a2, 0x23 // 0x21, PS_UM|PS_INTLEVEL(XCHAL_EXCM_LEVEL) +// @mhightower83 - use INTLEVEL 15 instead of 3 for Arduino like interrupt support?? + movi a2, 0x2F // 0x21, PS_UM|PS_INTLEVEL(15) + rsr a3, EPC_1 +// @mhightower83 - I assume PS.EXCM was set and now is being cleared, thus +// allowing new exceptions and interrupts within PS_INTLEVEL to be possible. +// We have set INTLEVEL to 15 to block any possible interrupts. + xsr a2, PS + + // HERE: window overflows enabled, but NOT SAFE because we're not quite + // in a valid windowed context (haven't restored a1 yet...); + // so don't cause any (keep to a0..a3) until we've saved critical state and restored a1: + + // NOTE: MUST SAVE EPC1 before causing any overflows, because overflows corrupt EPC1. + s32i a3, a1, UEXC_pc + s32i a2, a1, UEXC_ps + s32i a0, a1, UEXC_a0 // save the rest of the registers + s32i a6, a1, UEXC_a6 + s32i a7, a1, UEXC_a7 + s32i a8, a1, UEXC_a8 + s32i a9, a1, UEXC_a9 + s32i a10, a1, UEXC_a10 + s32i a11, a1, UEXC_a11 + s32i a12, a1, UEXC_a12 + s32i a13, a1, UEXC_a13 + s32i a14, a1, UEXC_a14 + s32i a15, a1, UEXC_a15 + rsync // wait for WSR to PS to complete + rsr a12, SAR + +// @mhightower83 - I think after the next instruction we have the potential of +// loosing UEXC_excvaddr Which the earlier comment said we need to preserve for +// the exception handler. +// +// rsil a13, 0 + + movi a13, _xtos_c_handler_table // &table + l32i a15, a1, UEXC_exccause // arg2: exccause + s32i a12, a1, UEXC_sar + addx4 a12, a15, a13 // a12 = table[exccause] + l32i a12, a12, 0 // ... + mov a2, a1 // arg1: exception parameters + mov a3, a15 // arg2: exccause + beqz a12, 1f // null handler => skip call + callx0 a12 // call C exception handler for this exception +1: + // Now exit the handler. + + // Restore special registers + l32i a14, a1, UEXC_sar + +// For compatibility with Arduino interrupt architecture, we keep interrupts 100% +// disabled. +// /* +// * Disable interrupts while returning from the pseudo-CALL setup above, +// * for the same reason they were disabled while doing the pseudo-CALL: +// * this sequence restores SP such that it doesn't reflect the allocation +// * of the exception stack frame, which we still need to return from +// * the exception. +// */ +// rsil a12, 1 // XCHAL_EXCM_LEVEL + rsil a12, 15 // All levels blocked. + wsr a14, SAR + movi a0, _xtos_return_from_exc + jx a0 + + /* FIXME: what about _GeneralException ? */ + .size _xtos_c_wrapper_handler, . - _xtos_c_wrapper_handler diff --git a/cores/esp8266/mmu_iram.cpp b/cores/esp8266/mmu_iram.cpp new file mode 100644 index 0000000000..488e596ccd --- /dev/null +++ b/cores/esp8266/mmu_iram.cpp @@ -0,0 +1,192 @@ +/* + * Copyright 2020 M Hightower + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include "Arduino.h" +#include "mmu_iram.h" +#include + +extern "C" { + +#if (MMU_ICACHE_SIZE == 0x4000) +#define SOC_CACHE_SIZE 0 // 16KB +#pragma message("ICACHE size 16K") +#else +#define SOC_CACHE_SIZE 1 // 32KB +#endif + +#if (MMU_ICACHE_SIZE == 0x4000) +/* + * "Cache_Read_Enable" as in Instruction Read Cache enable, ICACHE. + * + * The Boot ROM "Cache_Read_Enable" API enables virtual execution of code in + * flash memory via an instruction cache, ICACHE. The cache size can be set to + * 16K or 32K, and the NONOS SDK 2.x will always set ICACHE to 32K during + * initialization. + * + * When you select a 16K vs. a 32K ICACHE size, you get 48K contiguous IRAM to + * work with. The NONOS SDK 2.x does not have an option to select 16K/32K. This + * is where this Boot ROM wrapper for Cache_Read_Enable comes in. + * Note, there is support for 16K/32K cache size in NONOS SDK 3.0; however, I + * do not see an option to have it has part of your general IRAM. That SDK adds + * it to the heap. + * + * With this wrapper function, we override the SDK's ICACHE size. + * A build-time define MMU_ICACHE_SIZE selects 16K or 32K ICACHE size. + * + * mmu_status is used to help understand calling behavior. At some point, it + * should be trimmed down to the essentials. + * + * During NONOS SDK init, it will call to enable. Then call later, to process a + * spi_flash_get_id request, it will disable/enable around the Boot ROM SPI calls. + * + * + * + * Arguments for Cache_Read_Enable + * + * The first two arguments appear to specify which 1MB block of the flash to + * access with the ICACHE. + * + * The first argument, map, is partly understood. It has three values 0, 1, + * and 2+. The value 0 selects the even 1MB block, and 1 selects the odd 1MB + * block, in other words, bit20 of the flash address. No guesses for a value + * of 2 or greater. + * + * The second argument, p, bit 21 of the flash address. Or, it may be bits 23, + * 22, 21 of the flash address. A three-bit field is cleared in the register + * for this argument; however, I have not seen any examples of it being used + * that way. + * + * The third argument, v, holds our center of attention. A value of 0 selects + * 16K, and a value of 1 selects a 32K ICACHE. This is the only parameter we + * need to modify on Cache_Read_Enable calls. + * + * + * + * Clues and Information sources + * + * "Cache_Read_Enable" is underdocumented. Main sources of information were from + * rboot, zboot, https://richard.burtons.org/2015/06/12/esp8266-cache_read_enable/, + * and other places. And some additional expermentation. + * + * Searching through the NONOS SDK shows nothing on this API; however, some + * clues on what the NONOS SDK might be doing with ICACHE related calls can be + * found in the RTOS SDK. + * eg. ESP8266_RTOS_SDK/blob/master/components/spi_flash/src/spi_flash_raw.c + * also calls to it in the bootloader. + * + */ + +#ifndef ROM_Cache_Read_Enable +#define ROM_Cache_Read_Enable 0x40004678U +#endif + +typedef void (*fp_Cache_Read_Enable_t)(uint8_t map, uint8_t p, uint8_t v); +#define real_Cache_Read_Enable (reinterpret_cast(ROM_Cache_Read_Enable)) + +void IRAM_ATTR Cache_Read_Enable(uint8_t map, uint8_t p, uint8_t v) { + (void)v; + real_Cache_Read_Enable(map, p, SOC_CACHE_SIZE); +} + +#ifdef DEV_DEBUG_PRINT + +#if 0 +#ifndef ROM_Cache_Read_Disable +#define ROM_Cache_Read_Disable 0x400047f0 +#endif + +typedef void (*fp_Cache_Read_Disable_t)(void); +#define real_Cache_Read_Disable (reinterpret_cast(ROM_Cache_Read_Disable)) +/* + * + */ +void IRAM_ATTR Cache_Read_Disable(void) { + real_Cache_Read_Disable(); +} +#endif + +/* + * Early adjustment for CPU crystal frequency, so debug printing will work. + * This should not be left enabled all the time in Cashe_Read..., I am concerned + * that there may be unknown interference with the NONOS SDK startup. + * + * Inspired by: + * https://github.com/pvvx/esp8266web/blob/2e25559bc489487747205db2ef171d48326b32d4/app/sdklib/system/app_main.c#L581-L591 + */ +extern "C" uint8_t rom_i2c_readReg(uint8_t block, uint8_t host_id, uint8_t reg_add); +extern "C" void rom_i2c_writeReg(uint8_t block, uint8_t host_id, uint8_t reg_add, uint8_t data); + +extern "C" void IRAM_ATTR set_pll(void) +{ +#if !defined(F_CRYSTAL) +#define F_CRYSTAL 26000000 +#endif + if (F_CRYSTAL != 40000000) { + // At Boot ROM(-BIOS) start, it assumes a 40MHz crystal. + // If it is not, we assume a 26MHz crystal. + // There is no support for 24MHz crustal at this time. + if(rom_i2c_readReg(103,4,1) != 136) { // 8: 40MHz, 136: 26MHz + // Assume 26MHz crystal + // soc_param0: 0: 40MHz, 1: 26MHz, 2: 24MHz + // set 80MHz PLL CPU + rom_i2c_writeReg(103,4,1,136); + rom_i2c_writeReg(103,4,2,145); + } + } +} + +//C This was used to probe at different stages of boot the state of the PLL +//C register. I think we can get rid of this one. +extern "C" void IRAM_ATTR dbg_set_pll(void) +{ + char r103_4_1 = rom_i2c_readReg(103,4,1); + char r103_4_2 = rom_i2c_readReg(103,4,2); + set_pll(); + ets_uart_printf("\nrom_i2c_readReg(103,4,1) == %u\n", r103_4_1); + ets_uart_printf( "rom_i2c_readReg(103,4,2) == %u\n", r103_4_2); +} + +/* + This helps keep the UART enabled at user_init() so we can get a few more + messages printed. +*/ +extern struct rst_info resetInfo; +extern "C" void __pinMode( uint8_t pin, uint8_t mode ); + +inline bool is_gpio_persistent(void) { + return REASON_EXCEPTION_RST <= resetInfo.reason && + REASON_SOFT_RESTART >= resetInfo.reason; +} + +extern "C" void pinMode( uint8_t pin, uint8_t mode ) { + static bool in_initPins = true; + if (in_initPins && (1 == pin)) { + if (!is_gpio_persistent()) { + /* Restore pin to TX after Power-on and EXT_RST */ + __pinMode(pin, FUNCTION_0); + } + in_initPins = false; + return; + } + + __pinMode( pin, mode ); +} +#endif // #ifdef DEV_DEBUG_PRINT + +#endif // #if (MMU_ICACHE_SIZE == 0x4000) + +}; diff --git a/cores/esp8266/mmu_iram.h b/cores/esp8266/mmu_iram.h new file mode 100644 index 0000000000..e372fa1047 --- /dev/null +++ b/cores/esp8266/mmu_iram.h @@ -0,0 +1,221 @@ +/* + * Copyright 2020 M Hightower + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MMU_IRAM_H +#define __MMU_IRAM_H + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +//C This turns on range checking. Is this the value you want to trigger it? +#ifdef DEBUG_ESP_CORE +#define DEBUG_ESP_MMU +#endif + +#if defined(CORE_MOCK) +#define ets_uart_printf(...) do {} while(false) +#endif + +/* + * DEV_DEBUG_PRINT: + * Debug printing macros for printing before before, during, and after + * NONOS SDK initializes. May or maynot be safe during NONOS SDK + * initialization. As in printing from functions called on by the SDK + * during the SDK initialization. + * + #define DEV_DEBUG_PRINT + */ + +#if defined(DEV_DEBUG_PRINT) || defined(DEBUG_ESP_MMU) +#include + +#define DBG_MMU_FLUSH(a) while((USS(a) >> USTXC) & 0xff) {} + +#if defined(DEV_DEBUG_PRINT) +extern void set_pll(void); +extern void dbg_set_pll(void); + +#define DBG_MMU_PRINTF(fmt, ...) \ +set_pll(); \ +uart_buff_switch(0); \ +ets_uart_printf(fmt, ##__VA_ARGS__); \ +DBG_MMU_FLUSH(0) + +#else // ! defined(DEV_DEBUG_PRINT) +#define DBG_MMU_PRINTF(fmt, ...) ets_uart_printf(fmt, ##__VA_ARGS__) +#endif + +#else // ! defined(DEV_DEBUG_PRINT) || defined(DEBUG_ESP_MMU) +#define DBG_MMU_FLUSH(...) do {} while(false) +#define DBG_MMU_PRINTF(...) do {} while(false) +#endif // defined(DEV_DEBUG_PRINT) || defined(DEBUG_ESP_MMU) + +static inline __attribute__((always_inline)) +bool mmu_is_iram(const void *addr) { + #define IRAM_START 0x40100000UL +#ifndef MMU_IRAM_SIZE +#if defined(__GNUC__) && !defined(CORE_MOCK) + #warning "MMU_IRAM_SIZE was undefined, setting to 0x8000UL!" +#endif + #define MMU_IRAM_SIZE 0x8000UL +#endif + #define IRAM_END (IRAM_START + MMU_IRAM_SIZE) + + return (IRAM_START <= (uintptr_t)addr && IRAM_END > (uintptr_t)addr); +} + +static inline __attribute__((always_inline)) +bool mmu_is_dram(const void *addr) { + #define DRAM_START 0x3FF80000UL + #define DRAM_END 0x40000000UL + + return (DRAM_START <= (uintptr_t)addr && DRAM_END > (uintptr_t)addr); +} + +static inline __attribute__((always_inline)) +bool mmu_is_icache(const void *addr) { + #define ICACHE_START 0x40200000UL + #define ICACHE_END (ICACHE_START + 0x100000UL) + + return (ICACHE_START <= (uintptr_t)addr && ICACHE_END > (uintptr_t)addr); +} + +#ifdef DEBUG_ESP_MMU +#define ASSERT_RANGE_TEST_WRITE(a) \ + if (mmu_is_iram(a) || mmu_is_dram(a)) { \ + } else { \ + DBG_MMU_PRINTF("\nexcvaddr: %p\n", a); \ + assert(("Outside of Range - Write" && false)); \ + } + +#define ASSERT_RANGE_TEST_READ(a) \ + if (mmu_is_iram(a) || mmu_is_dram(a) || mmu_is_icache(a)) { \ + } else { \ + DBG_MMU_PRINTF("\nexcvaddr: %p\n", a); \ + assert(("Outside of Range - Read" && false)); \ + } + +#else +#define ASSERT_RANGE_TEST_WRITE(a) do {} while(false) +#define ASSERT_RANGE_TEST_READ(a) do {} while(false) +#endif + +/* + * Some inlines to allow faster random access to non32bit access of iRAM or + * iCACHE data elements. These remove the extra time and stack space that would + * have occured by relying on exception processing. + */ +static inline __attribute__((always_inline)) +uint8_t mmu_get_uint8(const void *p8) { + ASSERT_RANGE_TEST_READ(p8); + uint32_t val = (*(uint32_t *)((uintptr_t)p8 & ~0x3)); + uint32_t pos = ((uintptr_t)p8 & 0x3) * 8; + val >>= pos; + return (uint8_t)val; +} + +static inline __attribute__((always_inline)) +uint16_t mmu_get_uint16(const uint16_t *p16) { + ASSERT_RANGE_TEST_READ(p16); + uint32_t val = (*(uint32_t *)((uintptr_t)p16 & ~0x3)); + uint32_t pos = ((uintptr_t)p16 & 0x3) * 8; + val >>= pos; + return (uint16_t)val; +} + +static inline __attribute__((always_inline)) +int16_t mmu_get_int16(const int16_t *p16) { + ASSERT_RANGE_TEST_READ(p16); + uint32_t val = (*(uint32_t *)((uintptr_t)p16 & ~0x3)); + uint32_t pos = ((uintptr_t)p16 & 0x3) * 8; + val >>= pos; + return (int16_t)val; +} + +static inline __attribute__((always_inline)) +uint8_t mmu_set_uint8(void *p8, const uint8_t val) { + ASSERT_RANGE_TEST_WRITE(p8); + uint32_t pos = ((uintptr_t)p8 & 0x3) * 8; + uint32_t sval = val << pos; + uint32_t valmask = 0x0FF << pos; + + uint32_t *p32 = (uint32_t *)((uintptr_t)p8 & ~0x3); + uint32_t ival = *p32; + ival &= (~valmask); + ival |= sval; + *p32 = ival; + return val; +} + +static inline __attribute__((always_inline)) +uint16_t mmu_set_uint16(uint16_t *p16, const uint16_t val) { + ASSERT_RANGE_TEST_WRITE(p16); + uint32_t pos = ((uintptr_t)p16 & 0x3) * 8; + uint32_t sval = val << pos; + uint32_t valmask = 0x0FFFF << pos; + + uint32_t *p32 = (uint32_t *)((uintptr_t)p16 & ~0x3); + uint32_t ival = *p32; + ival &= (~valmask); + ival |= sval; + *p32 = ival; + return val; +} + +static inline __attribute__((always_inline)) +int16_t mmu_set_int16(int16_t *p16, const int16_t val) { + ASSERT_RANGE_TEST_WRITE(p16); + uint32_t sval = (uint16_t)val; + uint32_t pos = ((uintptr_t)p16 & 0x3) * 8; + sval <<= pos; + uint32_t valmask = 0x0FFFF << pos; + + uint32_t *p32 = (uint32_t *)((uintptr_t)p16 & ~0x3); + uint32_t ival = *p32; + ival &= (~valmask); + ival |= sval; + *p32 = ival; + return val; +} + +#if (MMU_IRAM_SIZE > 32*1024) && !defined(MMU_SEC_HEAP) +extern void _text_end(void); +#define MMU_SEC_HEAP mmu_sec_heap() +#define MMU_SEC_HEAP_SIZE mmu_sec_heap_size() + +static inline __attribute__((always_inline)) +void *mmu_sec_heap(void) { + uint32_t sec_heap = (uint32_t)_text_end + 32; + return (void *)(sec_heap &= ~7); +} + +static inline __attribute__((always_inline)) +size_t mmu_sec_heap_size(void) { + return (size_t)0xC000UL - ((size_t)mmu_sec_heap() - 0x40100000UL); +} +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/cores/esp8266/umm_malloc/umm_heap_select.h b/cores/esp8266/umm_malloc/umm_heap_select.h new file mode 100644 index 0000000000..4c9150819f --- /dev/null +++ b/cores/esp8266/umm_malloc/umm_heap_select.h @@ -0,0 +1,78 @@ +#ifndef UMM_MALLOC_SELECT_H +#define UMM_MALLOC_SELECT_H + +#include + +/* + This class is modeled after interrupts.h + + HeapSelectIram is used to temporarily select an alternate Heap. + + { + { + HeapSelectIram lock; + // allocate memory here + } + allocations here are from the old Heap selection + } + */ + +class HeapSelect { +public: +#if (UMM_NUM_HEAPS == 1) + HeapSelect(size_t id) { (void)id; } + ~HeapSelect() {} +#else + HeapSelect(size_t id) : _heap_id(umm_get_current_heap_id()) { + umm_set_heap_by_id(id); + } + + ~HeapSelect() { + umm_set_heap_by_id(_heap_id); + } + +protected: + size_t _heap_id; +#endif +}; + +class HeapSelectIram { +public: +#ifdef UMM_HEAP_IRAM + HeapSelectIram() : _heap_id(umm_get_current_heap_id()) { + umm_set_heap_by_id(UMM_HEAP_IRAM); + } + + ~HeapSelectIram() { + umm_set_heap_by_id(_heap_id); + } + +protected: + size_t _heap_id; + +#else + HeapSelectIram() {} + ~HeapSelectIram() {} +#endif +}; + +class HeapSelectDram { +public: +#if (UMM_NUM_HEAPS == 1) + HeapSelectDram() {} + ~HeapSelectDram() {} +#else + HeapSelectDram() : _heap_id(umm_get_current_heap_id()) { + umm_set_heap_by_id(UMM_HEAP_DRAM); + } + + ~HeapSelectDram() { + umm_set_heap_by_id(_heap_id); + } + +protected: + size_t _heap_id; +#endif +}; + +#endif // UMM_MALLOC_SELECT_H diff --git a/cores/esp8266/umm_malloc/umm_info.c b/cores/esp8266/umm_malloc/umm_info.c index 1c78ef3f6e..0860fe79ea 100644 --- a/cores/esp8266/umm_malloc/umm_info.c +++ b/cores/esp8266/umm_malloc/umm_info.c @@ -23,25 +23,25 @@ * ---------------------------------------------------------------------------- */ -UMM_HEAP_INFO ummHeapInfo; +// UMM_HEAP_INFO ummHeapInfo; void *umm_info( void *ptr, bool force ) { UMM_CRITICAL_DECL(id_info); - if(umm_heap == NULL) { - umm_init(); - } + UMM_INIT_HEAP; uint16_t blockNo = 0; /* Protect the critical section... */ UMM_CRITICAL_ENTRY(id_info); + umm_heap_context_t *_context = umm_get_current_heap(); + /* * Clear out all of the entries in the ummHeapInfo structure before doing * any calculations.. */ - memset( &ummHeapInfo, 0, sizeof( ummHeapInfo ) ); + memset( &_context->info, 0, sizeof( _context->info ) ); DBGLOG_FORCE( force, "\n" ); DBGLOG_FORCE( force, "+----------+-------+--------+--------+-------+--------+--------+\n" ); @@ -65,18 +65,18 @@ void *umm_info( void *ptr, bool force ) { while( UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK ) { size_t curBlocks = (UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK )-blockNo; - ++ummHeapInfo.totalEntries; - ummHeapInfo.totalBlocks += curBlocks; + ++_context->info.totalEntries; + _context->info.totalBlocks += curBlocks; /* Is this a free block? */ if( UMM_NBLOCK(blockNo) & UMM_FREELIST_MASK ) { - ++ummHeapInfo.freeEntries; - ummHeapInfo.freeBlocks += curBlocks; - ummHeapInfo.freeBlocksSquared += (curBlocks * curBlocks); + ++_context->info.freeEntries; + _context->info.freeBlocks += curBlocks; + _context->info.freeBlocksSquared += (curBlocks * curBlocks); - if (ummHeapInfo.maxFreeContiguousBlocks < curBlocks) { - ummHeapInfo.maxFreeContiguousBlocks = curBlocks; + if (_context->info.maxFreeContiguousBlocks < curBlocks) { + _context->info.maxFreeContiguousBlocks = curBlocks; } DBGLOG_FORCE( force, "|0x%08lx|B %5d|NB %5d|PB %5d|Z %5u|NF %5d|PF %5d|\n", @@ -98,8 +98,8 @@ void *umm_info( void *ptr, bool force ) { return( ptr ); } } else { - ++ummHeapInfo.usedEntries; - ummHeapInfo.usedBlocks += curBlocks; + ++_context->info.usedEntries; + _context->info.usedBlocks += curBlocks; DBGLOG_FORCE( force, "|0x%08lx|B %5d|NB %5d|PB %5d|Z %5u|\n", DBGLOG_32_BIT_PTR(&UMM_BLOCK(blockNo)), @@ -131,35 +131,35 @@ void *umm_info( void *ptr, bool force ) { DBGLOG_FORCE( force, "+----------+-------+--------+--------+-------+--------+--------+\n" ); DBGLOG_FORCE( force, "Total Entries %5d Used Entries %5d Free Entries %5d\n", - ummHeapInfo.totalEntries, - ummHeapInfo.usedEntries, - ummHeapInfo.freeEntries ); + _context->info.totalEntries, + _context->info.usedEntries, + _context->info.freeEntries ); DBGLOG_FORCE( force, "Total Blocks %5d Used Blocks %5d Free Blocks %5d\n", - ummHeapInfo.totalBlocks, - ummHeapInfo.usedBlocks, - ummHeapInfo.freeBlocks ); + _context->info.totalBlocks, + _context->info.usedBlocks, + _context->info.freeBlocks ); DBGLOG_FORCE( force, "+--------------------------------------------------------------+\n" ); - DBGLOG_FORCE( force, "Usage Metric: %5d\n", umm_usage_metric()); - DBGLOG_FORCE( force, "Fragmentation Metric: %5d\n", umm_fragmentation_metric()); + DBGLOG_FORCE( force, "Usage Metric: %5d\n", umm_usage_metric_core(_context)); + DBGLOG_FORCE( force, "Fragmentation Metric: %5d\n", umm_fragmentation_metric_core(_context)); DBGLOG_FORCE( force, "+--------------------------------------------------------------+\n" ); #if defined(UMM_STATS) || defined(UMM_STATS_FULL) #if !defined(UMM_INLINE_METRICS) - if (ummHeapInfo.freeBlocks == ummStats.free_blocks) { + if (_context->info.freeBlocks == _context->stats.free_blocks) { DBGLOG_FORCE( force, "heap info Free blocks and heap statistics Free blocks match.\n"); } else { DBGLOG_FORCE( force, "\nheap info Free blocks %5d != heap statistics Free Blocks %5d\n\n", - ummHeapInfo.freeBlocks, - ummStats.free_blocks ); + _context->info.freeBlocks, + _context->stats.free_blocks ); } DBGLOG_FORCE( force, "+--------------------------------------------------------------+\n" ); #endif - print_stats(force); + umm_print_stats(force); #endif /* Release the critical section... */ @@ -170,20 +170,29 @@ void *umm_info( void *ptr, bool force ) { /* ------------------------------------------------------------------------ */ +size_t umm_free_heap_size_core( umm_heap_context_t *_context ) { + return (size_t)_context->info.freeBlocks * sizeof(umm_block); +} + size_t umm_free_heap_size( void ) { #ifndef UMM_INLINE_METRICS umm_info(NULL, false); #endif - return (size_t)ummHeapInfo.freeBlocks * sizeof(umm_block); + + return umm_free_heap_size_core(umm_get_current_heap()); } //C Breaking change in upstream umm_max_block_size() was changed to //C umm_max_free_block_size() keeping old function name for (dot) releases. //C TODO: update at next major release. //C size_t umm_max_free_block_size( void ) { +size_t umm_max_block_size_core( umm_heap_context_t *_context ) { + return _context->info.maxFreeContiguousBlocks * sizeof(umm_block); +} + size_t umm_max_block_size( void ) { umm_info(NULL, false); - return ummHeapInfo.maxFreeContiguousBlocks * sizeof(umm_block); + return umm_max_block_size_core(umm_get_current_heap()); } /* @@ -191,50 +200,62 @@ size_t umm_max_block_size( void ) { umm_fragmentation_metric() must to be preceeded by a call to umm_info(NULL, false) for updated results. */ +int umm_usage_metric_core( umm_heap_context_t *_context ) { +//C Note, umm_metrics also appears in the upstrean w/o definition. I suspect it is suppose to be ummHeapInfo. + // DBGLOG_DEBUG( "usedBlocks %d totalBlocks %d\n", umm_metrics.usedBlocks, ummHeapInfo.totalBlocks); + DBGLOG_DEBUG( "usedBlocks %d totalBlocks %d\n", _context->info.usedBlocks, _context->info.totalBlocks); + if (_context->info.freeBlocks) + return (int)((_context->info.usedBlocks * 100)/(_context->info.freeBlocks)); + + return -1; // no freeBlocks +} + int umm_usage_metric( void ) { #ifndef UMM_INLINE_METRICS umm_info(NULL, false); #endif - DBGLOG_DEBUG( "usedBlocks %d totalBlocks %d\n", umm_metrics.usedBlocks, ummHeapInfo.totalBlocks); - if (ummHeapInfo.freeBlocks) - return (int)((ummHeapInfo.usedBlocks * 100)/(ummHeapInfo.freeBlocks)); - return -1; // no freeBlocks + return umm_usage_metric_core(umm_get_current_heap()); } - uint32_t sqrt32 (uint32_t n); -int umm_fragmentation_metric( void ) { -#ifndef UMM_INLINE_METRICS - umm_info(NULL, false); -#endif - DBGLOG_DEBUG( "freeBlocks %d freeBlocksSquared %d\n", umm_metrics.freeBlocks, ummHeapInfo.freeBlocksSquared); - if (0 == ummHeapInfo.freeBlocks) { +int umm_fragmentation_metric_core( umm_heap_context_t *_context ) { + // DBGLOG_DEBUG( "freeBlocks %d freeBlocksSquared %d\n", umm_metrics.freeBlocks, ummHeapInfo.freeBlocksSquared); + DBGLOG_DEBUG( "freeBlocks %d freeBlocksSquared %d\n", _context->info.freeBlocks, _context->info.freeBlocksSquared); + if (0 == _context->info.freeBlocks) { return 0; } else { //upstream version: return (100 - (((uint32_t)(sqrtf(ummHeapInfo.freeBlocksSquared)) * 100)/(ummHeapInfo.freeBlocks))); - return (100 - (((uint32_t)(sqrt32(ummHeapInfo.freeBlocksSquared)) * 100)/(ummHeapInfo.freeBlocks))); + return (100 - (((uint32_t)(sqrt32(_context->info.freeBlocksSquared)) * 100)/(_context->info.freeBlocks))); } } +int umm_fragmentation_metric( void ) { +#ifndef UMM_INLINE_METRICS + umm_info(NULL, false); +#endif + + return umm_fragmentation_metric_core(umm_get_current_heap()); +} + #ifdef UMM_INLINE_METRICS -static void umm_fragmentation_metric_init( void ) { - ummHeapInfo.freeBlocks = UMM_NUMBLOCKS - 2; - ummHeapInfo.freeBlocksSquared = ummHeapInfo.freeBlocks * ummHeapInfo.freeBlocks; +static void umm_fragmentation_metric_init( umm_heap_context_t *_context ) { + _context->info.freeBlocks = UMM_NUMBLOCKS - 2; + _context->info.freeBlocksSquared = _context->info.freeBlocks * _context->info.freeBlocks; } -static void umm_fragmentation_metric_add( uint16_t c ) { +static void umm_fragmentation_metric_add( umm_heap_context_t *_context, uint16_t c ) { uint16_t blocks = (UMM_NBLOCK(c) & UMM_BLOCKNO_MASK) - c; DBGLOG_DEBUG( "Add block %d size %d to free metric\n", c, blocks); - ummHeapInfo.freeBlocks += blocks; - ummHeapInfo.freeBlocksSquared += (blocks * blocks); + _context->info.freeBlocks += blocks; + _context->info.freeBlocksSquared += (blocks * blocks); } -static void umm_fragmentation_metric_remove( uint16_t c ) { +static void umm_fragmentation_metric_remove( umm_heap_context_t *_context, uint16_t c ) { uint16_t blocks = (UMM_NBLOCK(c) & UMM_BLOCKNO_MASK) - c; DBGLOG_DEBUG( "Remove block %d size %d from free metric\n", c, blocks); - ummHeapInfo.freeBlocks -= blocks; - ummHeapInfo.freeBlocksSquared -= (blocks * blocks); + _context->info.freeBlocks -= blocks; + _context->info.freeBlocksSquared -= (blocks * blocks); } #endif // UMM_INLINE_METRICS diff --git a/cores/esp8266/umm_malloc/umm_integrity.c b/cores/esp8266/umm_malloc/umm_integrity.c index 6c3c4c2f11..bb68c52f4d 100644 --- a/cores/esp8266/umm_malloc/umm_integrity.c +++ b/cores/esp8266/umm_malloc/umm_integrity.c @@ -33,13 +33,14 @@ bool umm_integrity_check(void) { uint16_t prev; uint16_t cur; - if (umm_heap == NULL) { - umm_init(); - } + UMM_INIT_HEAP; /* Iterate through all free blocks */ prev = 0; UMM_CRITICAL_ENTRY(id_integrity); + + umm_heap_context_t *_context = umm_get_current_heap(); + while(1) { cur = UMM_NFREE(prev); diff --git a/cores/esp8266/umm_malloc/umm_local.c b/cores/esp8266/umm_malloc/umm_local.c index 392ef13c8f..3eca39c6d8 100644 --- a/cores/esp8266/umm_malloc/umm_local.c +++ b/cores/esp8266/umm_malloc/umm_local.c @@ -42,7 +42,7 @@ bool ICACHE_FLASH_ATTR get_umm_get_perf_data(UMM_TIME_STATS *p, size_t size) #if defined(UMM_POISON_CHECK_LITE) // We skip this when doing the full poison check. -static bool check_poison_neighbors( uint16_t cur ) { +static bool check_poison_neighbors( umm_heap_context_t *_context, uint16_t cur ) { uint16_t c; if ( 0 == cur ) @@ -96,12 +96,16 @@ static void *get_unpoisoned_check_neighbors( void *vptr, const char* file, int l UMM_CRITICAL_DECL(id_poison); uint16_t c; bool poison = false; - + umm_heap_context_t *_context = umm_get_ptr_context( vptr ); + if (NULL == _context) { + panic(); + return NULL; + } /* Figure out which block we're in. Note the use of truncated division... */ - c = (ptr - (uintptr_t)(&(umm_heap[0])))/sizeof(umm_block); + c = (ptr - (uintptr_t)(&(_context->heap[0])))/sizeof(umm_block); UMM_CRITICAL_ENTRY(id_poison); - poison = check_poison_block(&UMM_BLOCK(c)) && check_poison_neighbors(c); + poison = check_poison_block(&UMM_BLOCK(c)) && check_poison_neighbors(_context, c); UMM_CRITICAL_EXIT(id_poison); if (!poison) { @@ -157,17 +161,13 @@ size_t umm_block_size( void ) { } #endif -#if (!defined(UMM_INLINE_METRICS) && defined(UMM_STATS)) || defined(UMM_STATS_FULL) -UMM_STATISTICS ummStats; -#endif - #if defined(UMM_STATS) || defined(UMM_STATS_FULL) // Keep complete call path in IRAM size_t umm_free_heap_size_lw( void ) { - if (umm_heap == NULL) { - umm_init(); - } - return (size_t)UMM_FREE_BLOCKS * sizeof(umm_block); + UMM_INIT_HEAP; + + umm_heap_context_t *_context = umm_get_current_heap(); + return (size_t)_context->UMM_FREE_BLOCKS * sizeof(umm_block); } #endif @@ -186,14 +186,17 @@ size_t xPortGetFreeHeapSize(void) __attribute__ ((alias("umm_free_heap_size"))); #endif #if defined(UMM_STATS) || defined(UMM_STATS_FULL) -void print_stats(int force) { +void umm_print_stats(int force) { + umm_heap_context_t *_context = umm_get_current_heap(); + DBGLOG_FORCE( force, "umm heap statistics:\n"); - DBGLOG_FORCE( force, " Raw Free Space %5u\n", UMM_FREE_BLOCKS * sizeof(umm_block)); - DBGLOG_FORCE( force, " OOM Count %5u\n", UMM_OOM_COUNT); + DBGLOG_FORCE( force, " Heap ID %5u\n", _context->id); + DBGLOG_FORCE( force, " Free Space %5u\n", _context->UMM_FREE_BLOCKS * sizeof(umm_block)); + DBGLOG_FORCE( force, " OOM Count %5u\n", _context->UMM_OOM_COUNT); #if defined(UMM_STATS_FULL) - DBGLOG_FORCE( force, " Low Watermark %5u\n", ummStats.free_blocks_min * sizeof(umm_block)); - DBGLOG_FORCE( force, " Low Watermark ISR %5u\n", ummStats.free_blocks_isr_min * sizeof(umm_block)); - DBGLOG_FORCE( force, " MAX Alloc Request %5u\n", ummStats.alloc_max_size); + DBGLOG_FORCE( force, " Low Watermark %5u\n", _context->stats.free_blocks_min * sizeof(umm_block)); + DBGLOG_FORCE( force, " Low Watermark ISR %5u\n", _context->stats.free_blocks_isr_min * sizeof(umm_block)); + DBGLOG_FORCE( force, " MAX Alloc Request %5u\n", _context->stats.alloc_max_size); #endif DBGLOG_FORCE( force, " Size of umm_block %5u\n", sizeof(umm_block)); DBGLOG_FORCE( force, "+--------------------------------------------------------------+\n" ); @@ -215,4 +218,85 @@ int ICACHE_FLASH_ATTR umm_info_safe_printf_P(const char *fmt, ...) { return result; } +#if defined(UMM_STATS) || defined(UMM_STATS_FULL) +size_t ICACHE_FLASH_ATTR umm_get_oom_count( void ) { + umm_heap_context_t *_context = umm_get_current_heap(); + return _context->UMM_OOM_COUNT; +} +#endif + +#ifdef UMM_STATS_FULL +// TODO - Did I mix something up +// +// umm_free_heap_size_min is the same code as +// umm_free_heap_size_lw_min +// +// If this is correct use alias. +// +size_t ICACHE_FLASH_ATTR umm_free_heap_size_lw_min( void ) { + umm_heap_context_t *_context = umm_get_current_heap(); + return _context->stats.free_blocks_min * umm_block_size(); +} + +size_t ICACHE_FLASH_ATTR umm_free_heap_size_min_reset( void ) { + umm_heap_context_t *_context = umm_get_current_heap(); + _context->stats.free_blocks_min = _context->UMM_FREE_BLOCKS; + return _context->stats.free_blocks_min * umm_block_size(); +} + +#if 0 // TODO - Don't understand this why do both umm_free_heap_size_(lw_)min exist +size_t umm_free_heap_size_min(void) __attribute__ ((alias("umm_free_heap_size_lw_min"))); +#else +size_t ICACHE_FLASH_ATTR umm_free_heap_size_min( void ) { + umm_heap_context_t *_context = umm_get_current_heap(); + return _context->stats.free_blocks_min * umm_block_size(); +} +#endif + +size_t ICACHE_FLASH_ATTR umm_free_heap_size_isr_min( void ) { + umm_heap_context_t *_context = umm_get_current_heap(); + return _context->stats.free_blocks_isr_min * umm_block_size(); +} + +size_t ICACHE_FLASH_ATTR umm_get_max_alloc_size( void ) { + umm_heap_context_t *_context = umm_get_current_heap(); + return _context->stats.alloc_max_size; +} + +size_t ICACHE_FLASH_ATTR umm_get_last_alloc_size( void ) { + umm_heap_context_t *_context = umm_get_current_heap(); + return _context->stats.last_alloc_size; +} + +size_t ICACHE_FLASH_ATTR umm_get_malloc_count( void ) { + umm_heap_context_t *_context = umm_get_current_heap(); + return _context->stats.id_malloc_count; +} + +size_t ICACHE_FLASH_ATTR umm_get_malloc_zero_count( void ) { + umm_heap_context_t *_context = umm_get_current_heap(); + return _context->stats.id_malloc_zero_count; +} + +size_t ICACHE_FLASH_ATTR umm_get_realloc_count( void ) { + umm_heap_context_t *_context = umm_get_current_heap(); + return _context->stats.id_realloc_count; +} + +size_t ICACHE_FLASH_ATTR umm_get_realloc_zero_count( void ) { + umm_heap_context_t *_context = umm_get_current_heap(); + return _context->stats.id_realloc_zero_count; +} + +size_t ICACHE_FLASH_ATTR umm_get_free_count( void ) { + umm_heap_context_t *_context = umm_get_current_heap(); + return _context->stats.id_free_count; +} + +size_t ICACHE_FLASH_ATTR umm_get_free_null_count( void ) { + umm_heap_context_t *_context = umm_get_current_heap(); + return _context->stats.id_free_null_count; +} +#endif // UMM_STATS_FULL + #endif // BUILD_UMM_MALLOC_C diff --git a/cores/esp8266/umm_malloc/umm_local.h b/cores/esp8266/umm_malloc/umm_local.h index bb9f0bd4e3..cb72d669c1 100644 --- a/cores/esp8266/umm_malloc/umm_local.h +++ b/cores/esp8266/umm_malloc/umm_local.h @@ -37,12 +37,12 @@ #if defined(UMM_POISON_CHECK_LITE) -static bool check_poison_neighbors( uint16_t cur ); +static bool check_poison_neighbors( umm_heap_context_t *_context, uint16_t cur ); #endif #if defined(UMM_STATS) || defined(UMM_STATS_FULL) -void ICACHE_FLASH_ATTR print_stats(int force); +void ICACHE_FLASH_ATTR umm_print_stats(int force); #endif @@ -51,4 +51,21 @@ int ICACHE_FLASH_ATTR umm_info_safe_printf_P(const char *fmt, ...) __attribute__ #define UMM_INFO_PRINTF(fmt, ...) umm_info_safe_printf_P(PSTR4(fmt), ##__VA_ARGS__) // use PSTR4() instead of PSTR() to ensure 4-bytes alignment in Flash, whatever the default alignment of PSTR_ALIGN + +typedef struct umm_block_t umm_block; + +struct UMM_HEAP_CONTEXT { + umm_block *heap; + void *heap_end; +#if (!defined(UMM_INLINE_METRICS) && defined(UMM_STATS)) || defined(UMM_STATS_FULL) + UMM_STATISTICS stats; +#endif +#ifdef UMM_INFO + UMM_HEAP_INFO info; +#endif + unsigned short int numblocks; + unsigned char id; +}; + + #endif diff --git a/cores/esp8266/umm_malloc/umm_malloc.cpp b/cores/esp8266/umm_malloc/umm_malloc.cpp index 7f14ed942f..8c6cd5758a 100644 --- a/cores/esp8266/umm_malloc/umm_malloc.cpp +++ b/cores/esp8266/umm_malloc/umm_malloc.cpp @@ -63,6 +63,11 @@ extern "C" { #define DBGLOG_LEVEL 0 #endif +// Save 104 bytes by calling umm_init() early once from app_entry() +// Some minor UMM_CRITICAL_METRICS counts will be lost through CRT0 init. +// #define UMM_INIT_HEAP if (!umm_heap) { umm_init(); } +#define UMM_INIT_HEAP (void)0 + #include "dbglog/dbglog.h" //C This change is new in upstream umm_malloc.I think this would have created a @@ -101,24 +106,146 @@ UMM_H_ATTPACKPRE typedef struct umm_block_t { #define UMM_BLOCKNO_MASK ((uint16_t)(0x7FFF)) /* ------------------------------------------------------------------------- */ +umm_heap_context_t heap_context[UMM_NUM_HEAPS] __attribute__((section(".noinit"))); +// void *umm_heap = NULL; + +/* A stack allowing push/popping of heaps for library use */ +#if (UMM_NUM_HEAPS == 1) + +#else +static size_t umm_heap_cur = UMM_HEAP_DRAM; +static int umm_heap_stack_ptr = 0; +static unsigned char umm_heap_stack[UMM_HEAP_STACK_DEPTH]; +#endif +/* ------------------------------------------------------------------------ */ +/* + * Methods to get heap id or context + * + */ + +#if (UMM_NUM_HEAPS == 1) +size_t umm_get_current_heap_id(void) { + return 0; +} + +umm_heap_context_t *umm_get_current_heap(void) { + return &heap_context[0]; +} + +static umm_heap_context_t *umm_get_heap_by_id( size_t which ) { + (void)which; + return &heap_context[0]; +} + +umm_heap_context_t *umm_set_heap_by_id( size_t which ) { + (void)which; + return &heap_context[0]; +} + +#else +size_t umm_get_current_heap_id(void) { + return umm_heap_cur; +} + +umm_heap_context_t *umm_get_current_heap(void) { + return &heap_context[umm_heap_cur]; +} + +static umm_heap_context_t *umm_get_heap_by_id( size_t which ) { + if (which < UMM_NUM_HEAPS) { + return &heap_context[which]; + } + return NULL; +} + +umm_heap_context_t *umm_set_heap_by_id( size_t which ) { + umm_heap_context_t *_context = umm_get_heap_by_id(which); + if (_context && _context->heap) { + umm_heap_cur = which; + return _context; + } + return NULL; +} +#endif + +#if (UMM_NUM_HEAPS == 1) +umm_heap_context_t *umm_push_heap( size_t which ) { + (void)which; + return &heap_context[0]; +} + +umm_heap_context_t *umm_pop_heap( void ) { + return &heap_context[0]; +} + +int umm_get_heap_stack_index( void ) { + return 0; +} +#else +/* ------------------------------------------------------------------------ */ + +umm_heap_context_t *umm_push_heap( size_t which ) { + if (umm_heap_stack_ptr < UMM_HEAP_STACK_DEPTH) { + umm_heap_stack[umm_heap_stack_ptr++] = umm_heap_cur; + return umm_set_heap_by_id( which ); + } + return NULL; +} + +/* ------------------------------------------------------------------------ */ + +umm_heap_context_t *umm_pop_heap( void ) { + if (umm_heap_stack_ptr > 0 ) { + return umm_set_heap_by_id(umm_heap_stack[--umm_heap_stack_ptr]); + } + return NULL; +} -umm_block *umm_heap = NULL; -uint16_t umm_numblocks = 0; +// Intended for diagnosic use +int umm_get_heap_stack_index( void ) { + return umm_heap_stack_ptr; +} +#endif +/* ------------------------------------------------------------------------ */ +/* + * Returns the correct heap context for a given pointer. Useful for + * realloc or free since you may not be in the right heap to handle it. + * + */ +static bool test_ptr_context( size_t which, void *ptr ) { + return + heap_context[which].heap && + ptr >= (void *)heap_context[which].heap && + ptr < heap_context[which].heap_end; +} -#define UMM_NUMBLOCKS (umm_numblocks) +static umm_heap_context_t *umm_get_ptr_context(void *ptr) { + for (size_t i = 0; i < UMM_NUM_HEAPS; i++) { + if (test_ptr_context( i, ptr ) ) { + return umm_get_heap_by_id( i ); + } + } + + panic(); + return NULL; +} + +#define UMM_NUMBLOCKS (_context->numblocks) #define UMM_BLOCK_LAST (UMM_NUMBLOCKS - 1) /* ------------------------------------------------------------------------- * These macros evaluate to the address of the block and data respectively */ -#define UMM_BLOCK(b) (umm_heap[b]) +#define UMM_BLOCK(b) (_context->heap[b]) #define UMM_DATA(b) (UMM_BLOCK(b).body.data) /* ------------------------------------------------------------------------- * These macros evaluate to the index of the block - NOT the address!!! */ +/* ------------------------------------------------------------------------ */ + #define UMM_NBLOCK(b) (UMM_BLOCK(b).header.used.next) #define UMM_PBLOCK(b) (UMM_BLOCK(b).header.used.prev) #define UMM_NFREE(b) (UMM_BLOCK(b).body.free.next) @@ -172,7 +299,9 @@ static uint16_t umm_blocks( size_t size ) { * * Note that free pointers are NOT modified by this function. */ -static void umm_split_block( uint16_t c, +static void umm_split_block( + umm_heap_context_t *_context, + uint16_t c, uint16_t blocks, uint16_t new_freemask ) { @@ -185,7 +314,7 @@ static void umm_split_block( uint16_t c, /* ------------------------------------------------------------------------ */ -static void umm_disconnect_from_free_list( uint16_t c ) { +static void umm_disconnect_from_free_list( umm_heap_context_t *_context, uint16_t c ) { /* Disconnect this block from the FREE list */ UMM_NFREE(UMM_PFREE(c)) = UMM_NFREE(c); @@ -202,7 +331,7 @@ static void umm_disconnect_from_free_list( uint16_t c ) { * next block is free. */ -static void umm_assimilate_up( uint16_t c ) { +static void umm_assimilate_up( umm_heap_context_t *_context, uint16_t c ) { if( UMM_NBLOCK(UMM_NBLOCK(c)) & UMM_FREELIST_MASK ) { @@ -217,7 +346,7 @@ static void umm_assimilate_up( uint16_t c ) { /* Disconnect the next block from the FREE list */ - umm_disconnect_from_free_list( UMM_NBLOCK(c) ); + umm_disconnect_from_free_list( _context, UMM_NBLOCK(c) ); /* Assimilate the next block with this one */ @@ -232,7 +361,7 @@ static void umm_assimilate_up( uint16_t c ) { * up before assimilating down. */ -static uint16_t umm_assimilate_down( uint16_t c, uint16_t freemask ) { +static uint16_t umm_assimilate_down( umm_heap_context_t *_context, uint16_t c, uint16_t freemask ) { // We are going to assimilate down to the previous block because // it was free, so remove it from the fragmentation metric @@ -257,23 +386,18 @@ static uint16_t umm_assimilate_down( uint16_t c, uint16_t freemask ) { /* ------------------------------------------------------------------------- */ -void umm_init( void ) { - /* init heap pointer and size, and memset it to 0 */ - umm_heap = (umm_block *)UMM_MALLOC_CFG_HEAP_ADDR; - umm_numblocks = (UMM_MALLOC_CFG_HEAP_SIZE / sizeof(umm_block)); - memset(umm_heap, 0x00, UMM_MALLOC_CFG_HEAP_SIZE); - +static void umm_init_stage_2( umm_heap_context_t *_context ) { /* setup initial blank heap structure */ UMM_FRAGMENTATION_METRIC_INIT(); - /* init ummStats.free_blocks */ + /* init stats.free_blocks */ #if defined(UMM_STATS) || defined(UMM_STATS_FULL) #if defined(UMM_STATS_FULL) - ummStats.free_blocks_min = - ummStats.free_blocks_isr_min = UMM_NUMBLOCKS - 2; + _context->stats.free_blocks_min = + _context->stats.free_blocks_isr_min = UMM_NUMBLOCKS - 2; #endif #ifndef UMM_INLINE_METRICS - ummStats.free_blocks = UMM_NUMBLOCKS - 2; + _context->stats.free_blocks = UMM_NUMBLOCKS - 2; #endif #endif @@ -314,15 +438,89 @@ void umm_init( void ) { UMM_PBLOCK(UMM_BLOCK_LAST) = 1; } + +void umm_init_common( size_t id, void *start_addr, size_t size, bool zero ) { + /* Preserve internal setup */ + umm_heap_context_t *_context = umm_get_heap_by_id(id); + if (NULL == start_addr || NULL == _context || _context->heap) { + return; + } + + /* init heap pointer and size, and memset it to 0 */ + _context->id = id; + _context->heap = (umm_block *)start_addr; + _context->heap_end = (void *)((uintptr_t)start_addr + size); + _context->numblocks = (size / sizeof(umm_block)); + + // An option for blocking the zeroing of extra heaps allows for performing + // post-crash discovery. + if (zero) { + memset(_context->heap, 0x00, size); +#if (!defined(UMM_INLINE_METRICS) && defined(UMM_STATS)) || defined(UMM_STATS_FULL) + memset(&_context->stats, 0x00, sizeof(_context->stats)); +#endif + + /* Set up internal data structures */ + umm_init_stage_2(_context); + } +} + +void umm_init( void ) { + // if (umm_heap) { + // return; + // } + for (size_t i = 0; i < UMM_NUM_HEAPS; i++) { + heap_context[i].heap = NULL; + } + memset(&heap_context[0], 0, sizeof(heap_context)); + umm_init_common( UMM_HEAP_DRAM, (void *)UMM_MALLOC_CFG_HEAP_ADDR, UMM_MALLOC_CFG_HEAP_SIZE, true ); + // umm_heap = (void *)&heap_context; +} + +#ifdef UMM_HEAP_IRAM +void umm_init_iram_ex( void *addr, unsigned int size, bool zero ) { + /* We need the main, internal heap set up first */ + UMM_INIT_HEAP; + + umm_init_common(UMM_HEAP_IRAM, addr, size, zero); +} + +void _text_end(void); +void umm_init_iram(void) __attribute__((weak)); + +/* + By using a weak link, it is possible to reduce the IRAM heap size with a + user-supplied init function. This would allow the creation of a block of IRAM + dedicated to a sketch and possibly used/preserved across reboots. + */ +void umm_init_iram(void) { + umm_init_iram_ex(mmu_sec_heap(), mmu_sec_heap_size(), true); +} +#endif // #ifdef UMM_HEAP_IRAM + +#ifdef UMM_HEAP_EXTERNAL +void umm_init_vm( void *vmaddr, unsigned int vmsize ) { + /* We need the main, internal (DRAM) heap set up first */ + UMM_INIT_HEAP; + + umm_init_common(UMM_HEAP_EXTERNAL, vmaddr, vmsize, true); +} +#endif + /* ------------------------------------------------------------------------ * Must be called only from within critical sections guarded by * UMM_CRITICAL_ENTRY() and UMM_CRITICAL_EXIT(). */ -static void umm_free_core( void *ptr ) { +static void umm_free_core( umm_heap_context_t *_context, void *ptr ) { uint16_t c; + if (NULL == _context) { + panic(); + return; + } + STATS__FREE_REQUEST(id_free); /* * FIXME: At some point it might be a good idea to add a check to make sure @@ -335,7 +533,7 @@ static void umm_free_core( void *ptr ) { /* Figure out which block we're in. Note the use of truncated division... */ - c = (((uintptr_t)ptr)-(uintptr_t)(&(umm_heap[0])))/sizeof(umm_block); + c = (((uintptr_t)ptr)-(uintptr_t)(&(_context->heap[0])))/sizeof(umm_block); DBGLOG_DEBUG( "Freeing block %6d\n", c ); @@ -344,7 +542,7 @@ static void umm_free_core( void *ptr ) { /* Now let's assimilate this block with the next one if possible. */ - umm_assimilate_up( c ); + umm_assimilate_up( _context, c ); /* Then assimilate with the previous block if possible */ @@ -352,7 +550,7 @@ static void umm_free_core( void *ptr ) { DBGLOG_DEBUG( "Assimilate down to previous block, which is FREE\n" ); - c = umm_assimilate_down(c, UMM_FREELIST_MASK); + c = umm_assimilate_down(_context, c, UMM_FREELIST_MASK); } else { /* * The previous block is not a free block, so add this one to the head @@ -376,9 +574,7 @@ static void umm_free_core( void *ptr ) { void umm_free( void *ptr ) { UMM_CRITICAL_DECL(id_free); - if (umm_heap == NULL) { - umm_init(); - } + UMM_INIT_HEAP; /* If we're being asked to free a NULL pointer, well that's just silly! */ @@ -393,7 +589,8 @@ void umm_free( void *ptr ) { UMM_CRITICAL_ENTRY(id_free); - umm_free_core( ptr ); + /* Need to be in the heap in which this block lives */ + umm_free_core( umm_get_ptr_context( ptr ), ptr ); UMM_CRITICAL_EXIT(id_free); } @@ -403,7 +600,7 @@ void umm_free( void *ptr ) { * UMM_CRITICAL_ENTRY() and UMM_CRITICAL_EXIT(). */ -static void *umm_malloc_core( size_t size ) { +static void *umm_malloc_core( umm_heap_context_t *_context, size_t size ) { uint16_t blocks; uint16_t blockSize = 0; @@ -414,6 +611,11 @@ static void *umm_malloc_core( size_t size ) { STATS__ALLOC_REQUEST(id_malloc, size); + if (NULL == _context) { + panic(); + return NULL; + } + blocks = umm_blocks( size ); /* @@ -474,7 +676,8 @@ static void *umm_malloc_core( size_t size ) { /* Disconnect this block from the FREE list */ - umm_disconnect_from_free_list( cf ); + umm_disconnect_from_free_list( _context, cf ); + } else { /* It's not an exact fit and we need to split off a block. */ @@ -484,7 +687,7 @@ static void *umm_malloc_core( size_t size ) { * split current free block `cf` into two blocks. The first one will be * returned to user, so it's not free, and the second one will be free. */ - umm_split_block( cf, blocks, UMM_FREELIST_MASK /*new block is free*/ ); + umm_split_block( _context, cf, blocks, UMM_FREELIST_MASK /*new block is free*/ ); UMM_FRAGMENTATION_METRIC_ADD(UMM_NBLOCK(cf)); @@ -525,9 +728,61 @@ void *umm_malloc( size_t size ) { void *ptr = NULL; - if (umm_heap == NULL) { - umm_init(); - } + UMM_INIT_HEAP; + + /* + * "Is it safe" + * + * Is it safe to call from an ISR? Is there a point during a malloc that a + * an interrupt and subsequent call to malloc result in undesired results? + * + * Heap selection in managed by the functions umm_push_heap, umm_pop_heap, + * umm_get_current_heap_id, and umm_set_heap_by_id. These functions are + * responsible for getting/setting the module static variable umm_heap_cur. + * The umm_heap_cur variable is an index that is used to select the current + * heap context. Depending on the situation this selection can be overriddened. + * + * All variables for a specific Heap are in a single structure. `heap_context` + * is an array of these structures. Each heap API function uses a function + * local variable `_context` to hold a pointer to the selected heap structure. + * This local pointer is referenced for all the "selected heap" operations. + * Coupled with critical sections around global data should allow the API + * functions to be reentrant. + * + * Using the `_context` name throughout made it easy to incorporate the + * context into existing macros. + * + * For allocating APIs `umm_heap_cur` is used to index and select a value for + * `_context`. If an allocation is made from an ISR, this value is ignored and + * the heap context for DRAM is loaded. For APIs that require operating on an + * existing allcation such as realloc and free, the heap context selected is + * done by matching the allocation's address with that of one of the heap + * address ranges. + * + * I think we are safe with multiple heaps when the non32-bit exception + * handler is used, as long as interrupts don't get enabled. There was a + * window in the Boot ROM "C" Exception Wrapper that would enable interrupts + * when running our non32-exception handler; however, that should be resolved + * by our replacement wrapper. For more information on exception handling + * issues for IRAM see comments above `_set_exception_handler_wrapper()` in + * `core_esp8266_non32xfer.cpp`. + * + * ISRs should not try and change heaps. umm_malloc will ignore the change. + * All should be fine as long as the caller puts the heap back the way it was. + * On return, everything must be the same. The foreground thread will continue + * with the same information that was there before the interrupt. All malloc() + * requests made from an ISR are fulfilled with DRAM. + * + * For umm_malloc, heap selection involves changing a single variable that is + * on the calling context stack. From the umm_mallac side, that variable is + * used to load a context pointer by index, heap ID. While an umm_malloc API + * function is running, all heap related variables are in the context variable + * pointer, registers, or the current stack as the request is processed. With + * a single variable to reference for heap selection, I think it is unlikely + * that umm_malloc can be called, with things in an unusable transition state. + */ + + umm_heap_context_t *_context = umm_get_current_heap(); /* * the very first thing we do is figure out if we're being asked to allocate @@ -547,7 +802,21 @@ void *umm_malloc( size_t size ) { UMM_CRITICAL_ENTRY(id_malloc); - ptr = umm_malloc_core( size ); + /* + * We handle the realloc of an existing IRAM allocation from an ISR with IRAM, + * while a new malloc from an ISR will always supply DRAM. That said, realloc + * from an ISR is not generally safe without special locking mechanisms and is + * not formally supported. + * + * Additionally, to avoid extending the IRQs disabled period, it is best to + * use DRAM for an ISR. Each 16-bit access to IRAM that umm_malloc has to make + * requires a pass through the exception handling logic. + */ + if (UMM_CRITICAL_WITHINISR(id_malloc)) { + _context = umm_get_heap_by_id(UMM_HEAP_DRAM); + } + + ptr = umm_malloc_core( _context, size ); UMM_CRITICAL_EXIT(id_malloc); @@ -568,9 +837,7 @@ void *umm_realloc( void *ptr, size_t size ) { size_t curSize; - if (umm_heap == NULL) { - umm_init(); - } + UMM_INIT_HEAP; /* * This code looks after the case of a NULL value for ptr. The ANSI C @@ -592,6 +859,13 @@ void *umm_realloc( void *ptr, size_t size ) { * we should operate the same as free. */ + /* Need to be in the heap in which this block lives */ + umm_heap_context_t *_context = umm_get_ptr_context( ptr ); + if (NULL == _context) { + panic(); + return NULL; + } + if( 0 == size ) { DBGLOG_DEBUG( "realloc to 0 size, just free the block\n" ); STATS__ZERO_ALLOC_REQUEST(id_realloc, size); @@ -616,7 +890,7 @@ void *umm_realloc( void *ptr, size_t size ) { /* Figure out which block we're in. Note the use of truncated division... */ - c = (((uintptr_t)ptr)-(uintptr_t)(&(umm_heap[0])))/sizeof(umm_block); + c = (((uintptr_t)ptr)-(uintptr_t)(&(_context->heap[0])))/sizeof(umm_block); /* Figure out how big this block is ... the free bit is not set :-) */ @@ -713,15 +987,15 @@ void *umm_realloc( void *ptr, size_t size ) { // Case 3 - prev block NOT free and block + next block fits } else if ((0 == prevBlockSize) && (blockSize + nextBlockSize) >= blocks) { DBGLOG_DEBUG( "realloc using next block - %i\n", blocks ); - umm_assimilate_up( c ); + umm_assimilate_up( _context, c ); STATS__FREE_BLOCKS_UPDATE( - nextBlockSize ); blockSize += nextBlockSize; // Case 4 - prev block + block fits } else if ((prevBlockSize + blockSize) >= blocks) { DBGLOG_DEBUG( "realloc using prev block - %i\n", blocks ); - umm_disconnect_from_free_list( UMM_PBLOCK(c) ); - c = umm_assimilate_down(c, 0); + umm_disconnect_from_free_list( _context, UMM_PBLOCK(c) ); + c = umm_assimilate_down(_context, c, 0); STATS__FREE_BLOCKS_UPDATE( - prevBlockSize ); STATS__FREE_BLOCKS_ISR_MIN(); blockSize += prevBlockSize; @@ -732,14 +1006,14 @@ void *umm_realloc( void *ptr, size_t size ) { // Case 5 - prev block + block + next block fits } else if ((prevBlockSize + blockSize + nextBlockSize) >= blocks) { DBGLOG_DEBUG( "realloc using prev and next block - %d\n", blocks ); - umm_assimilate_up( c ); - umm_disconnect_from_free_list( UMM_PBLOCK(c) ); - c = umm_assimilate_down(c, 0); + umm_assimilate_up( _context, c ); + umm_disconnect_from_free_list( _context, UMM_PBLOCK(c) ); + c = umm_assimilate_down(_context, c, 0); STATS__FREE_BLOCKS_UPDATE( - prevBlockSize - nextBlockSize ); #ifdef UMM_LIGHTWEIGHT_CPU if ((prevBlockSize + blockSize + nextBlockSize) > blocks) { - umm_split_block( c, blocks, 0 ); - umm_free_core( (void *)&UMM_DATA(c+blocks) ); + umm_split_block( _context, c, blocks, 0 ); + umm_free_core( _context, (void *)&UMM_DATA(c+blocks) ); } STATS__FREE_BLOCKS_ISR_MIN(); blockSize = blocks; @@ -755,12 +1029,12 @@ void *umm_realloc( void *ptr, size_t size ) { } else { DBGLOG_DEBUG( "realloc a completely new block %i\n", blocks ); void *oldptr = ptr; - if( (ptr = umm_malloc_core( size )) ) { + if( (ptr = umm_malloc_core( _context, size )) ) { DBGLOG_DEBUG( "realloc %i to a bigger block %i, copy, and free the old\n", blockSize, blocks ); UMM_CRITICAL_SUSPEND(id_realloc); memcpy( ptr, oldptr, curSize ); UMM_CRITICAL_RESUME(id_realloc); - umm_free_core( oldptr ); + umm_free_core( _context, oldptr ); } else { DBGLOG_DEBUG( "realloc %i to a bigger block %i failed - return NULL and leave the old block!\n", blockSize, blocks ); /* This space intentionally left blnk */ @@ -798,8 +1072,8 @@ void *umm_realloc( void *ptr, size_t size ) { * requested number of blocks and add what's left to the free list. */ if (prevBlockSize && (prevBlockSize + blockSize + nextBlockSize) >= blocks) { // 1 - umm_disconnect_from_free_list( UMM_PBLOCK(c) ); - c = umm_assimilate_down(c, 0); + umm_disconnect_from_free_list( _context, UMM_PBLOCK(c) ); + c = umm_assimilate_down( _context, c, 0 ); STATS__FREE_BLOCKS_UPDATE( - prevBlockSize ); blockSize += prevBlockSize; if (blockSize >= blocks) { @@ -807,13 +1081,13 @@ void *umm_realloc( void *ptr, size_t size ) { STATS__FREE_BLOCKS_ISR_MIN(); } else { DBGLOG_DEBUG( "realloc using prev and next block - %d\n", blocks ); - umm_assimilate_up( c ); + umm_assimilate_up( _context, c ); STATS__FREE_BLOCKS_UPDATE( - nextBlockSize ); blockSize += nextBlockSize; #ifdef UMM_LIGHTWEIGHT_CPU if (blockSize > blocks) { - umm_split_block( c, blocks, 0 ); - umm_free_core( (void *)&UMM_DATA(c+blocks) ); + umm_split_block( _context, c, blocks, 0 ); + umm_free_core( _context, (void *)&UMM_DATA(c+blocks) ); } STATS__FREE_BLOCKS_ISR_MIN(); blockSize = blocks; @@ -828,18 +1102,18 @@ void *umm_realloc( void *ptr, size_t size ) { /* This space intentionally left blank */ } else if ((blockSize + nextBlockSize) >= blocks) { // 3 DBGLOG_DEBUG( "realloc using next block - %d\n", blocks ); - umm_assimilate_up( c ); + umm_assimilate_up( _context, c ); STATS__FREE_BLOCKS_UPDATE( - nextBlockSize ); blockSize += nextBlockSize; } else { // 4 DBGLOG_DEBUG( "realloc a completely new block %d\n", blocks ); void *oldptr = ptr; - if( (ptr = umm_malloc_core( size )) ) { + if( (ptr = umm_malloc_core( _context, size )) ) { DBGLOG_DEBUG( "realloc %d to a bigger block %d, copy, and free the old\n", blockSize, blocks ); UMM_CRITICAL_SUSPEND(id_realloc); memcpy( ptr, oldptr, curSize ); UMM_CRITICAL_RESUME(id_realloc); - umm_free_core( oldptr); + umm_free_core( _context, oldptr); } else { DBGLOG_DEBUG( "realloc %d to a bigger block %d failed - return NULL and leave the old block!\n", blockSize, blocks ); /* This space intentionally left blnk */ @@ -859,12 +1133,12 @@ void *umm_realloc( void *ptr, size_t size ) { } else { DBGLOG_DEBUG( "realloc a completely new block %d\n", blocks ); void *oldptr = ptr; - if( (ptr = umm_malloc_core( size )) ) { + if( (ptr = umm_malloc_core( _context, size )) ) { DBGLOG_DEBUG( "realloc %d to a bigger block %d, copy, and free the old\n", blockSize, blocks ); UMM_CRITICAL_SUSPEND(id_realloc); memcpy( ptr, oldptr, curSize ); UMM_CRITICAL_RESUME(id_realloc); - umm_free_core( oldptr ); + umm_free_core( _context, oldptr ); } else { DBGLOG_DEBUG( "realloc %d to a bigger block %d failed - return NULL and leave the old block!\n", blockSize, blocks ); /* This space intentionally left blnk */ @@ -882,8 +1156,8 @@ void *umm_realloc( void *ptr, size_t size ) { if (blockSize > blocks ) { DBGLOG_DEBUG( "split and free %d blocks from %d\n", blocks, blockSize ); - umm_split_block( c, blocks, 0 ); - umm_free_core( (void *)&UMM_DATA(c+blocks) ); + umm_split_block( _context, c, blocks, 0 ); + umm_free_core( _context, (void *)&UMM_DATA(c+blocks) ); } STATS__FREE_BLOCKS_MIN(); diff --git a/cores/esp8266/umm_malloc/umm_malloc.h b/cores/esp8266/umm_malloc/umm_malloc.h index 82cfd91431..f7dcc72c1b 100644 --- a/cores/esp8266/umm_malloc/umm_malloc.h +++ b/cores/esp8266/umm_malloc/umm_malloc.h @@ -17,6 +17,14 @@ extern "C" { #endif + +#ifdef UMM_HEAP_EXTERNAL +extern void umm_init_vm( void *vmaddr, unsigned int vmsize ); +#endif +#ifdef UMM_HEAP_IRAM +extern void umm_init_iram(void); +extern void umm_init_iram_ex( void *addr, unsigned int size, bool zero ); +#endif /* ------------------------------------------------------------------------ */ extern void umm_init( void ); @@ -27,6 +35,13 @@ extern void umm_free( void *ptr ); /* ------------------------------------------------------------------------ */ +extern umm_heap_context_t *umm_push_heap( size_t heap_number ); +extern umm_heap_context_t *umm_pop_heap( void ); +extern int umm_get_heap_stack_index( void ); +extern umm_heap_context_t *umm_set_heap_by_id( size_t which ); +extern size_t umm_get_current_heap_id( void ); +extern umm_heap_context_t *umm_get_current_heap( void ); + #ifdef __cplusplus } #endif diff --git a/cores/esp8266/umm_malloc/umm_malloc_cfg.h b/cores/esp8266/umm_malloc/umm_malloc_cfg.h index 2db2444753..1e5d7f8003 100644 --- a/cores/esp8266/umm_malloc/umm_malloc_cfg.h +++ b/cores/esp8266/umm_malloc/umm_malloc_cfg.h @@ -19,6 +19,7 @@ #include #include +#include #include "../debug.h" #include "../esp8266_undocumented.h" @@ -32,6 +33,46 @@ extern "C" { #include "c_types.h" +/* + * Define active Heaps + */ +#if defined(MMU_IRAM_HEAP) +#define UMM_HEAP_IRAM +#else +#undef UMM_HEAP_IRAM +#endif + +// #define UMM_HEAP_EXTERNAL + +/* + * Assign IDs to active Heaps and tally. DRAM is always active. + */ +#define UMM_HEAP_DRAM 0 +#define UMM_HEAP_DRAM_DEFINED 1 + +#ifdef UMM_HEAP_IRAM +#undef UMM_HEAP_IRAM +#define UMM_HEAP_IRAM_DEFINED 1 +#define UMM_HEAP_IRAM UMM_HEAP_DRAM_DEFINED +#else +#define UMM_HEAP_IRAM_DEFINED 0 +#endif + +#ifdef UMM_HEAP_EXTERNAL +#undef UMM_HEAP_EXTERNAL +#define UMM_HEAP_EXTERNAL_DEFINED 1 +#define UMM_HEAP_EXTERNAL (UMM_HEAP_DRAM_DEFINED + UMM_HEAP_IRAM_DEFINED) +#else +#define UMM_HEAP_EXTERNAL_DEFINED 0 +#endif + +#define UMM_NUM_HEAPS (UMM_HEAP_DRAM_DEFINED + UMM_HEAP_IRAM_DEFINED + UMM_HEAP_EXTERNAL_DEFINED) + +#if (UMM_NUM_HEAPS == 1) +#else +#define UMM_HEAP_STACK_DEPTH 32 +#endif + /* * There are a number of defines you can set at compile time that affect how * the memory allocator will operate. @@ -134,8 +175,9 @@ extern char test_umm_heap[]; #else /* Start addresses and the size of the heap */ extern char _heap_start[]; +#define UMM_HEAP_END_ADDR 0x3FFFC000UL #define UMM_MALLOC_CFG_HEAP_ADDR ((uint32_t)&_heap_start[0]) -#define UMM_MALLOC_CFG_HEAP_SIZE ((size_t)(0x3fffc000 - UMM_MALLOC_CFG_HEAP_ADDR)) +#define UMM_MALLOC_CFG_HEAP_SIZE ((size_t)(UMM_HEAP_END_ADDR - UMM_MALLOC_CFG_HEAP_ADDR)) #endif /* A couple of macros to make packing structures less compiler dependent */ @@ -158,9 +200,9 @@ extern char _heap_start[]; /* -------------------------------------------------------------------------- */ #ifdef UMM_INLINE_METRICS - #define UMM_FRAGMENTATION_METRIC_INIT() umm_fragmentation_metric_init() - #define UMM_FRAGMENTATION_METRIC_ADD(c) umm_fragmentation_metric_add(c) - #define UMM_FRAGMENTATION_METRIC_REMOVE(c) umm_fragmentation_metric_remove(c) + #define UMM_FRAGMENTATION_METRIC_INIT() umm_fragmentation_metric_init(_context) + #define UMM_FRAGMENTATION_METRIC_ADD(c) umm_fragmentation_metric_add(_context, c) + #define UMM_FRAGMENTATION_METRIC_REMOVE(c) umm_fragmentation_metric_remove(_context, c) #ifndef UMM_INFO #define UMM_INFO #endif @@ -194,14 +236,16 @@ extern char _heap_start[]; unsigned int freeBlocksSquared; #ifdef UMM_INLINE_METRICS size_t oom_count; - #define UMM_OOM_COUNT ummHeapInfo.oom_count - #define UMM_FREE_BLOCKS ummHeapInfo.freeBlocks + #define UMM_OOM_COUNT info.oom_count + #define UMM_FREE_BLOCKS info.freeBlocks #endif unsigned int maxFreeContiguousBlocks; } UMM_HEAP_INFO; - extern UMM_HEAP_INFO ummHeapInfo; + // extern UMM_HEAP_INFO ummHeapInfo; +struct UMM_HEAP_CONTEXT; +typedef struct UMM_HEAP_CONTEXT umm_heap_context_t; extern ICACHE_FLASH_ATTR void *umm_info( void *ptr, bool force ); #ifdef UMM_INLINE_METRICS @@ -213,12 +257,19 @@ extern char _heap_start[]; extern ICACHE_FLASH_ATTR size_t umm_max_block_size( void ); extern ICACHE_FLASH_ATTR int umm_usage_metric( void ); extern ICACHE_FLASH_ATTR int umm_fragmentation_metric( void ); + extern ICACHE_FLASH_ATTR size_t umm_free_heap_size_core( umm_heap_context_t *_context ); + extern ICACHE_FLASH_ATTR size_t umm_max_block_size_core( umm_heap_context_t *_context ); + extern ICACHE_FLASH_ATTR int umm_usage_metric_core( umm_heap_context_t *_context ); + extern ICACHE_FLASH_ATTR int umm_fragmentation_metric_core( umm_heap_context_t *_context ); #else #define umm_info(p,b) #define umm_free_heap_size() (0) #define umm_max_block_size() (0) #define umm_fragmentation_metric() (0) #define umm_usage_metric() (0) + #define umm_free_heap_size_core() (0) + #define umm_max_block_size_core() (0) + #define umm_fragmentation_metric_core() (0) #endif /* @@ -263,8 +314,8 @@ typedef struct UMM_STATISTICS_t { // Otherwise it is defined here. size_t free_blocks; size_t oom_count; - #define UMM_OOM_COUNT ummStats.oom_count - #define UMM_FREE_BLOCKS ummStats.free_blocks + #define UMM_OOM_COUNT stats.oom_count + #define UMM_FREE_BLOCKS stats.free_blocks #endif #ifdef UMM_STATS_FULL size_t free_blocks_min; @@ -280,21 +331,17 @@ typedef struct UMM_STATISTICS_t { #endif } UMM_STATISTICS; -extern UMM_STATISTICS ummStats; #ifdef UMM_INLINE_METRICS #define STATS__FREE_BLOCKS_UPDATE(s) (void)(s) #else -#define STATS__FREE_BLOCKS_UPDATE(s) ummStats.free_blocks += (s) +#define STATS__FREE_BLOCKS_UPDATE(s) _context->stats.free_blocks += (s) #endif -#define STATS__OOM_UPDATE() UMM_OOM_COUNT += 1 +#define STATS__OOM_UPDATE() _context->UMM_OOM_COUNT += 1 extern size_t umm_free_heap_size_lw( void ); - -static inline size_t ICACHE_FLASH_ATTR umm_get_oom_count( void ) { - return UMM_OOM_COUNT; -} +extern size_t umm_get_oom_count( void ); #else // not UMM_STATS or UMM_STATS_FULL #define STATS__FREE_BLOCKS_UPDATE(s) (void)(s) @@ -308,87 +355,53 @@ size_t ICACHE_FLASH_ATTR umm_block_size( void ); #ifdef UMM_STATS_FULL #define STATS__FREE_BLOCKS_MIN() \ do { \ - if (UMM_FREE_BLOCKS < ummStats.free_blocks_min) \ - ummStats.free_blocks_min = UMM_FREE_BLOCKS; \ + if (_context->UMM_FREE_BLOCKS < _context->stats.free_blocks_min) \ + _context->stats.free_blocks_min = _context->UMM_FREE_BLOCKS; \ } while(false) #define STATS__FREE_BLOCKS_ISR_MIN() \ do { \ - if (UMM_FREE_BLOCKS < ummStats.free_blocks_isr_min) \ - ummStats.free_blocks_isr_min = UMM_FREE_BLOCKS; \ + if (_context->UMM_FREE_BLOCKS < _context->stats.free_blocks_isr_min) \ + _context->stats.free_blocks_isr_min = _context->UMM_FREE_BLOCKS; \ } while(false) #define STATS__ALLOC_REQUEST(tag, s) \ do { \ - ummStats.tag##_count += 1; \ - ummStats.last_alloc_size = s; \ - if (ummStats.alloc_max_size < s) \ - ummStats.alloc_max_size = s; \ + _context->stats.tag##_count += 1; \ + _context->stats.last_alloc_size = s; \ + if (_context->stats.alloc_max_size < s) \ + _context->stats.alloc_max_size = s; \ } while(false) #define STATS__ZERO_ALLOC_REQUEST(tag, s) \ do { \ - ummStats.tag##_zero_count += 1; \ + _context->stats.tag##_zero_count += 1; \ } while(false) #define STATS__NULL_FREE_REQUEST(tag) \ do { \ - ummStats.tag##_null_count += 1; \ + umm_heap_context_t *_context = umm_get_current_heap(); \ + _context->stats.tag##_null_count += 1; \ } while(false) #define STATS__FREE_REQUEST(tag) \ do { \ - ummStats.tag##_count += 1; \ + _context->stats.tag##_count += 1; \ } while(false) -static inline size_t ICACHE_FLASH_ATTR umm_free_heap_size_lw_min( void ) { - return (size_t)ummStats.free_blocks_min * umm_block_size(); -} - -static inline size_t ICACHE_FLASH_ATTR umm_free_heap_size_min_reset( void ) { - ummStats.free_blocks_min = UMM_FREE_BLOCKS; - return (size_t)ummStats.free_blocks_min * umm_block_size(); -} - -static inline size_t ICACHE_FLASH_ATTR umm_free_heap_size_min( void ) { - return ummStats.free_blocks_min * umm_block_size(); -} - -static inline size_t ICACHE_FLASH_ATTR umm_free_heap_size_isr_min( void ) { - return ummStats.free_blocks_isr_min * umm_block_size(); -} - -static inline size_t ICACHE_FLASH_ATTR umm_get_max_alloc_size( void ) { - return ummStats.alloc_max_size; -} - -static inline size_t ICACHE_FLASH_ATTR umm_get_last_alloc_size( void ) { - return ummStats.last_alloc_size; -} -static inline size_t ICACHE_FLASH_ATTR umm_get_malloc_count( void ) { - return ummStats.id_malloc_count; -} - -static inline size_t ICACHE_FLASH_ATTR umm_get_malloc_zero_count( void ) { - return ummStats.id_malloc_zero_count; -} - -static inline size_t ICACHE_FLASH_ATTR umm_get_realloc_count( void ) { - return ummStats.id_realloc_count; -} - -static inline size_t ICACHE_FLASH_ATTR umm_get_realloc_zero_count( void ) { - return ummStats.id_realloc_zero_count; -} - -static inline size_t ICACHE_FLASH_ATTR umm_get_free_count( void ) { - return ummStats.id_free_count; -} - -static inline size_t ICACHE_FLASH_ATTR umm_get_free_null_count( void ) { - return ummStats.id_free_null_count; -} +size_t umm_free_heap_size_lw_min( void ); +size_t umm_free_heap_size_min_reset( void ); +size_t umm_free_heap_size_min( void ); +size_t umm_free_heap_size_isr_min( void ); +size_t umm_get_max_alloc_size( void ); +size_t umm_get_last_alloc_size( void ); +size_t umm_get_malloc_count( void ); +size_t umm_get_malloc_zero_count( void ); +size_t umm_get_realloc_count( void ); +size_t umm_get_realloc_zero_count( void ); +size_t umm_get_free_count( void ); +size_t umm_get_free_null_count( void ); #else // Not UMM_STATS_FULL #define STATS__FREE_BLOCKS_MIN() (void)0 @@ -492,6 +505,7 @@ static inline void _critical_exit(UMM_TIME_STAT *p, uint32_t *saved_ps) { #define UMM_CRITICAL_DECL(tag) uint32_t _saved_ps_##tag #define UMM_CRITICAL_ENTRY(tag)_critical_entry(&time_stats.tag, &_saved_ps_##tag) #define UMM_CRITICAL_EXIT(tag) _critical_exit(&time_stats.tag, &_saved_ps_##tag) + #define UMM_CRITICAL_WITHINISR(tag) (0 != (_saved_ps_##tag & 0x0F)) #else // ! UMM_CRITICAL_METRICS // This method preserves the intlevel on entry and restores the @@ -499,6 +513,7 @@ static inline void _critical_exit(UMM_TIME_STAT *p, uint32_t *saved_ps) { #define UMM_CRITICAL_DECL(tag) uint32_t _saved_ps_##tag #define UMM_CRITICAL_ENTRY(tag) _saved_ps_##tag = xt_rsil(DEFAULT_CRITICAL_SECTION_INTLEVEL) #define UMM_CRITICAL_EXIT(tag) xt_wsr_ps(_saved_ps_##tag) + #define UMM_CRITICAL_WITHINISR(tag) (0 != (_saved_ps_##tag & 0x0F)) #endif #endif @@ -666,7 +681,7 @@ static inline void _critical_exit(UMM_TIME_STAT *p, uint32_t *saved_ps) { # define POISON_CHECK() 1 # define POISON_CHECK_NEIGHBORS(c) \ do {\ - if(!check_poison_neighbors(c)) \ + if(!check_poison_neighbors(_context, c)) \ panic();\ } while(false) #else @@ -759,6 +774,9 @@ void free_loc (void* p, const char* file, int line); #else // !defined(ESP_DEBUG_OOM) #endif + + + #ifdef __cplusplus } #endif diff --git a/cores/esp8266/umm_malloc/umm_poison.c b/cores/esp8266/umm_malloc/umm_poison.c index 5bfb5ce0a8..01122c1a73 100644 --- a/cores/esp8266/umm_malloc/umm_poison.c +++ b/cores/esp8266/umm_malloc/umm_poison.c @@ -137,8 +137,13 @@ static void *get_unpoisoned( void *vptr ) { ptr -= (sizeof(UMM_POISONED_BLOCK_LEN_TYPE) + UMM_POISON_SIZE_BEFORE); + umm_heap_context_t *_context = umm_get_ptr_context( vptr ); + if (NULL == _context) { + panic(); + return NULL; + } /* Figure out which block we're in. Note the use of truncated division... */ - c = (ptr - (uintptr_t)(&(umm_heap[0])))/sizeof(umm_block); + c = (ptr - (uintptr_t)(&(_context->heap[0])))/sizeof(umm_block); check_poison_block(&UMM_BLOCK(c)); } @@ -214,11 +219,10 @@ bool umm_poison_check(void) { bool ok = true; uint16_t cur; - if (umm_heap == NULL) { - umm_init(); - } + UMM_INIT_HEAP; UMM_CRITICAL_ENTRY(id_poison); + umm_heap_context_t *_context = umm_get_current_heap(); /* Now iterate through the blocks list */ cur = UMM_NBLOCK(0) & UMM_BLOCKNO_MASK; diff --git a/cores/esp8266/xtruntime-frames.h b/cores/esp8266/xtruntime-frames.h new file mode 100644 index 0000000000..8b5a7463ab --- /dev/null +++ b/cores/esp8266/xtruntime-frames.h @@ -0,0 +1,162 @@ +/* xtruntime-frames.h - exception stack frames for single-threaded run-time */ +/* $Id: //depot/rel/Eaglenest/Xtensa/OS/include/xtensa/xtruntime-frames.h#1 $ */ + +/* + * Copyright (c) 2002-2012 Tensilica Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _XTRUNTIME_FRAMES_H_ +#define _XTRUNTIME_FRAMES_H_ + +#include + +/* Macros that help define structures for both C and assembler: */ +#if defined(_ASMLANGUAGE) || defined(__ASSEMBLER__) +#define STRUCT_BEGIN .pushsection .text; .struct 0 +#define STRUCT_FIELD(ctype,size,pre,name) pre##name: .space size +#define STRUCT_AFIELD(ctype,size,pre,name,n) pre##name: .if n ; .space (size)*(n) ; .endif +#define STRUCT_AFIELD_A(ctype,size,align,pre,name,n) .balign align ; pre##name: .if n ; .space (size)*(n) ; .endif +#define STRUCT_END(sname) sname##Size:; .popsection +#else /*_ASMLANGUAGE||__ASSEMBLER__*/ +#define STRUCT_BEGIN typedef struct { +#define STRUCT_FIELD(ctype,size,pre,name) ctype name; +#define STRUCT_AFIELD(ctype,size,pre,name,n) ctype name[n]; +#define STRUCT_AFIELD_A(ctype,size,align,pre,name,n) ctype name[n] __attribute__((aligned(align))); +#define STRUCT_END(sname) } sname; +#endif /*_ASMLANGUAGE||__ASSEMBLER__*/ + + +/* + * Kernel vector mode exception stack frame. + * + * NOTE: due to the limited range of addi used in the current + * kernel exception vector, and the fact that historically + * the vector is limited to 12 bytes, the size of this + * stack frame is limited to 128 bytes (currently at 64). + */ +STRUCT_BEGIN +STRUCT_FIELD (long,4,KEXC_,pc) /* "parm" */ +STRUCT_FIELD (long,4,KEXC_,ps) +STRUCT_AFIELD(long,4,KEXC_,areg, 4) /* a12 .. a15 */ +STRUCT_FIELD (long,4,KEXC_,sar) /* "save" */ +#if XCHAL_HAVE_LOOPS +STRUCT_FIELD (long,4,KEXC_,lcount) +STRUCT_FIELD (long,4,KEXC_,lbeg) +STRUCT_FIELD (long,4,KEXC_,lend) +#endif +#if XCHAL_HAVE_MAC16 +STRUCT_FIELD (long,4,KEXC_,acclo) +STRUCT_FIELD (long,4,KEXC_,acchi) +STRUCT_AFIELD(long,4,KEXC_,mr, 4) +#endif +STRUCT_END(KernelFrame) + + +/* + * User vector mode exception stack frame: + * + * WARNING: if you modify this structure, you MUST modify the + * computation of the pad size (ALIGNPAD) accordingly. + */ +STRUCT_BEGIN +STRUCT_FIELD (long,4,UEXC_,pc) +STRUCT_FIELD (long,4,UEXC_,ps) +STRUCT_FIELD (long,4,UEXC_,sar) +STRUCT_FIELD (long,4,UEXC_,vpri) +#ifdef __XTENSA_CALL0_ABI__ +STRUCT_FIELD (long,4,UEXC_,a0) +#endif +STRUCT_FIELD (long,4,UEXC_,a2) +STRUCT_FIELD (long,4,UEXC_,a3) +STRUCT_FIELD (long,4,UEXC_,a4) +STRUCT_FIELD (long,4,UEXC_,a5) +#ifdef __XTENSA_CALL0_ABI__ +STRUCT_FIELD (long,4,UEXC_,a6) +STRUCT_FIELD (long,4,UEXC_,a7) +STRUCT_FIELD (long,4,UEXC_,a8) +STRUCT_FIELD (long,4,UEXC_,a9) +STRUCT_FIELD (long,4,UEXC_,a10) +STRUCT_FIELD (long,4,UEXC_,a11) +STRUCT_FIELD (long,4,UEXC_,a12) +STRUCT_FIELD (long,4,UEXC_,a13) +STRUCT_FIELD (long,4,UEXC_,a14) +STRUCT_FIELD (long,4,UEXC_,a15) +#endif +STRUCT_FIELD (long,4,UEXC_,exccause) /* NOTE: can probably rid of this one (pass direct) */ +#if XCHAL_HAVE_LOOPS +STRUCT_FIELD (long,4,UEXC_,lcount) +STRUCT_FIELD (long,4,UEXC_,lbeg) +STRUCT_FIELD (long,4,UEXC_,lend) +#endif +#if XCHAL_HAVE_MAC16 +STRUCT_FIELD (long,4,UEXC_,acclo) +STRUCT_FIELD (long,4,UEXC_,acchi) +STRUCT_AFIELD(long,4,UEXC_,mr, 4) +#endif +/* ALIGNPAD is the 16-byte alignment padding. */ +#ifdef __XTENSA_CALL0_ABI__ +# define CALL0_ABI 1 +#else +# define CALL0_ABI 0 +#endif +#define ALIGNPAD ((3 + XCHAL_HAVE_LOOPS*1 + XCHAL_HAVE_MAC16*2 + CALL0_ABI*1) & 3) +#if ALIGNPAD +STRUCT_AFIELD(long,4,UEXC_,pad, ALIGNPAD) /* 16-byte alignment padding */ +#endif +/*STRUCT_AFIELD_A(char,1,XCHAL_CPEXTRA_SA_ALIGN,UEXC_,ureg, (XCHAL_CPEXTRA_SA_SIZE+3)&-4)*/ /* not used */ +STRUCT_END(UserFrame) + + +#if defined(_ASMLANGUAGE) || defined(__ASSEMBLER__) + + +/* Check for UserFrameSize small enough not to require rounding...: */ + /* Skip 16-byte save area, then 32-byte space for 8 regs of call12 + * (which overlaps with 16-byte GCC nested func chaining area), + * then exception stack frame: */ + .set UserFrameTotalSize, 16+32+UserFrameSize + /* Greater than 112 bytes? (max range of ADDI, both signs, when aligned to 16 bytes): */ + .ifgt UserFrameTotalSize-112 + /* Round up to 256-byte multiple to accelerate immediate adds: */ + .set UserFrameTotalSize, ((UserFrameTotalSize+255) & 0xFFFFFF00) + .endif +# define ESF_TOTALSIZE UserFrameTotalSize + +#endif /* _ASMLANGUAGE || __ASSEMBLER__ */ + + +#if XCHAL_NUM_CONTEXTS > 1 +/* Structure of info stored on new context's stack for setup: */ +STRUCT_BEGIN +STRUCT_FIELD (long,4,INFO_,sp) +STRUCT_FIELD (long,4,INFO_,arg1) +STRUCT_FIELD (long,4,INFO_,funcpc) +STRUCT_FIELD (long,4,INFO_,prevps) +STRUCT_END(SetupInfo) +#endif + + +#define KERNELSTACKSIZE 1024 + + +#endif /* _XTRUNTIME_FRAMES_H_ */ + diff --git a/doc/index.rst b/doc/index.rst index 9fdba0f1de..dcc5f69628 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -13,10 +13,11 @@ Welcome to ESP8266 Arduino Core's documentation! OTA Updates PROGMEM Using GDB to debug + MMU Boards FAQ - + Exception causes Debugging Stack Dump diff --git a/doc/mmu.rst b/doc/mmu.rst new file mode 100644 index 0000000000..41446cf76c --- /dev/null +++ b/doc/mmu.rst @@ -0,0 +1,237 @@ +MMU - Adjust the Ratio of ICACHE to IRAM +======================================== + +Overview +-------- + +The ESP8266 has a total of 64K of instruction memory, IRAM. This 64K of +IRAM is composed of one dedicated 32K block of IRAM and two 16K blocks +of IRAM. The last two 16K blocks of IRAM are flexible in the sense that +it can be used as a transparent cache for external flash memory. These +blocks can either be used for IRAM or an instruction cache for executing +code out of flash, ICACHE. + +The code generated for a sketch is divided up into two groups, ICACHE +and IRAM. IRAM offers faster execution. It is used for interrupt service +routines, exception handling, and time-critical code. The ICACHE allows +for the execution of up to 1MB of code stored in flash. On a cache miss, +a delay occurs as the instructions are read from flash via the SPI bus. + +There is 98KB of DRAM space. This memory can be accessed as byte, short, +or a 32-bit word. Access must be aligned according to the data type +size. A 16bit short must be on a multiple of 2-byte address boundary. +Likewise, a 32-bit word must be on a multiple of 4-byte address +boundary. In contrast, data access in IRAM or ICACHE must always be a +full 32-bit word and aligned. We will discuss a non32-bit exception +handler for this later. + +Option Summary +-------------- + +The Arduino IDE Tools menu option, ``MMU`` has the following selections: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +1. ``32KB cache + 32KB IRAM (balanced)`` + + - This is the legacy ratio. + - Try this option 1st. + +2. ``16KB cache + 48KB IRAM (IRAM)`` + + - With just 16KB cache, execution of code out of flash may be slowed + by more cache misses when compared to 32KB. The slowness will vary + with the sketch. + - Use this if you need a little more IRAM space, and you have enough + DRAM space. + +3. ``16KB cache + 48KB IRAM and 2nd Heap (shared)`` + + - This option builds on the previous option and creates a 2nd Heap + made with IRAM. + - The 2nd Heap size will vary with free IRAM. + - This option is flexible. IRAM usage for code can overflow into the + additional 16KB IRAM region, shrinking the 2nd Heap below 16KB. Or + IRAM can be under 32KB, allowing the 2nd Heap to be larger than + 16KB. + - Installs a Non-32-Bit Access handler for IRAM. This allows for + byte and 16-bit aligned short access. + - This 2nd Heap is supported by the standard ``malloc`` APIs. + - Heap selection is handled through a ``HeapSelect`` class. This + allows a specific heap selection for the duration of a scope. + - Use this option, if you are still running out of DRAM space after + you have moved as many of your constant strings/data elements that + you can to PROGMEM. + +4. ``16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared)`` + + - Not managed by the ``umm_malloc`` heap library + - If required, non-32-Bit Access for IRAM must be enabled + separately. + - Enables a 16KB block of unmanaged IRAM memory + - Data persist across reboots, but not deep sleep. + - Works well for when you need a simple large chunk of memory. This + option will reduce the resources required to support a shared 2nd + Heap. + +MMU related build defines and possible values. These values change as +indicated with the menu options above: + ++-------------------------+--------------+--------------+------------------------------------+------------------------------+ +| ``#define`` | balanced | IRAM | shared (IRAM and Heap) | not shared (IRAM and Heap) | ++=========================+==============+==============+====================================+==============================+ +| ``MMU_IRAM_SIZE`` | ``0x8000`` | ``0xC000`` | ``0xC000`` | ``0x8000`` | ++-------------------------+--------------+--------------+------------------------------------+------------------------------+ +| ``MMU_ICACHE_SIZE`` | ``0x8000`` | ``0x4000`` | ``0x4000`` | ``0x4000`` | ++-------------------------+--------------+--------------+------------------------------------+------------------------------+ +| ``MMU_IRAM_HEAP`` | -- | -- | defined, enables\ ``umm_malloc`` | -- | ++-------------------------+--------------+--------------+------------------------------------+------------------------------+ +| ``MMU_SEC_HEAP`` | -- | \*\* | \*\* | ``0x40108000`` | ++-------------------------+--------------+--------------+------------------------------------+------------------------------+ +| ``MMU_SEC_HEAP_SIZE`` | -- | \*\* | \*\* | ``0x4000`` | ++-------------------------+--------------+--------------+------------------------------------+------------------------------+ + +\*\* This define is to an inline function that calculates the value, +based on unused code space, requires ``#include ``. + +The Arduino IDE Tools menu option, ``Non-32-Bit Access`` has the following selections: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- ``Use pgm_read macros for IRAM/PROGMEM`` +- ``Byte/Word access to IRAM/PROGMEM (very slow)`` + + - This option adds a non32-bit exception handler to your build. + - Handles read/writes to IRAM and reads to ICACHE. + - Supports short and byte access to IRAM + - Not recommended for high-frequency access data, use DRAM if you + can. + - Expect it to be slower than DRAM, each character access, will + require a complete save and restore of all 16+ registers. + - Processing an exception uses 256 bytes of stack space just to get + started. The actual handler will add a little more. + - This option is implicitly enabled and required when you select MMU + option ``16KB cache + 48KB IRAM and 2nd Heap (shared)``. + +IRAM, unlike DRAM, must be accessed as aligned full 32-bit words, no +byte or short access. The pgm\_read macros are an option; however, the +store operation remains an issue. For a block copy, ets\_memcpy appears +to work well as long as the byte count is rounded up to be evenly +divided by 4, and source and destination addresses are 4 bytes aligned. + +A word of caution, I have seen one case with the new toolchain 10.1 +where code that reads a 32-bit word to extract a byte was optimized away +to be a byte read. Using ``volatile`` on the pointer stopped the +over-optimization. + +To get a sense of how memory access time is effected, see examples +``MMU48K`` and ``irammem`` in ``ESP8266``. + +Miscellaneous +------------- + +For calls to ``umm_malloc`` with interrupts disabled. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- ``malloc`` will always allocate from the ``DRAM`` heap when called + with interrupts disabled. +- ``realloc`` with a NULL pointer will use ``malloc`` and return a + ``DRAM`` heap allocation. Note, calling ``realloc`` with interrupts + disabled is **not** officially supported. You are on your own if you + do this. +- If you must use IRAM memory in your ISR, allocate the memory in your + init code. To reduce the time spent in the ISR, avoid non32-bit + access that would trigger the exception handler. For short or byte + access, consider using the inline functions described in section + "Performance Functions" below. + +How to Select Heap +~~~~~~~~~~~~~~~~~~ + +The ``MMU`` selection ``16KB cache + 48KB IRAM and 2nd Heap (shared)`` +allows you to use the standard heap API function calls (``malloc``, +``calloc``, ``free``, ... ). to allocate memory from DRAM or IRAM. This +selection can be made by instantiating the class ``HeapSelectIram`` or +``HeapSelectDram``. The usage is similar to that of the +``InterruptLock`` class. The default/initial heap source is DRAM. The +class is in ``umm_malloc/umm_heap_select.h``. + +:: + + ... + char *bufferDram; + bufferDram = (char *)malloc(33); + char *bufferIram; + { + HeapSelectIram ephemeral; + bufferIram = (char *)malloc(33); + } + ... + free(bufferIram); + free(bufferDram); + ... + +``free`` will always return memory to the correct heap. There is no need +for tracking and selecting before freeing. + +``realloc`` with a non-NULL pointer will always resize the allocation +from the original heap it was allocated from. When the supplied pointer +is NULL, then the current heap selection is used. + +Low-level primitives for selecting a heap. These are used by the above +Classes: + +- ``umm_get_current_heap_id()`` +- ``umm_set_heap_by_id( ID value )`` +- Possible ID values +- ``UMM_HEAP_DRAM`` +- ``UMM_HEAP_IRAM`` + +Also, an alternate stack select method API is available. This is not as +easy as the class method; however, for some small set of cases, it may +provide some additional control: + +- ``ESP.setIramHeap()`` Pushes current heap ID onto a stack and sets + Heap API for an IRAM selection. +- ``ESP.setDramHeap()`` Pushes current heap ID onto a stack and sets + Heap API for a DRAM selection. +- ``ESP.resetHeap()`` Restores previously pushed heap. ### Identify + Memory + +These always inlined functions can be used to determine the resource of +a pointer: + +.. code:: cpp + + bool mmu_is_iram(const void *addr); + bool mmu_is_dram(const void *addr); + bool mmu_is_icache(const void *addr); + +Performance Functions +~~~~~~~~~~~~~~~~~~~~~ + +While these always inlined functions, will bypass the need for the +exception handler reducing execution time and stack use, it comes at the +cost of increased code size. + +These are an alternative to the ``pgm_read`` macros for reading from +IRAM. When compiled with 'Debug Level: core' range checks are performed +on the pointer value to make sure you are reading from the address range +of IRAM, DRAM, or ICACHE. + +.. code:: cpp + + uint8_t mmu_get_uint8(const void *p8); + uint16_t mmu_get_uint16(const uint16_t *p16); + int16_t mmu_get_int16(const int16_t *p16); + +While these functions are intended for writing to IRAM, they will work +with DRAM. When compiled with 'Debug Level: core', range checks are +performed on the pointer value to make sure you are writing to the +address range of IRAM or DRAM. + +.. code:: cpp + + uint8_t mmu_set_uint8(void *p8, const uint8_t val); + uint16_t mmu_set_uint16(uint16_t *p16, const uint16_t val); + int16_t mmu_set_int16(int16_t *p16, const int16_t val); + +:: diff --git a/libraries/ESP8266WebServer/examples/HelloServerBearSSL/HelloServerBearSSL.ino b/libraries/ESP8266WebServer/examples/HelloServerBearSSL/HelloServerBearSSL.ino index 4b786dc1a5..ab36e54805 100644 --- a/libraries/ESP8266WebServer/examples/HelloServerBearSSL/HelloServerBearSSL.ino +++ b/libraries/ESP8266WebServer/examples/HelloServerBearSSL/HelloServerBearSSL.ino @@ -13,6 +13,8 @@ #include #include #include +#include +#include #ifndef STASSID #define STASSID "your-ssid" @@ -142,7 +144,68 @@ void setup(void){ Serial.println("HTTPS server started"); } +extern "C" void stack_thunk_dump_stack(); + +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\n"), ESP.getFreeHeap()); + } + break; + } + case 'P': + out.println(F("Calling stack_thunk_dump_stack();")); + stack_thunk_dump_stack(); + 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 + ")); + 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(" p - call stack_thunk_dump_stack();")); + 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){ server.handleClient(); MDNS.update(); + if (Serial.available() > 0) { + int hotKey = Serial.read(); + processKey(Serial, hotKey); + } } diff --git a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp index c2161cdb4d..4a0d63af6e 100644 --- a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp +++ b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp @@ -25,6 +25,7 @@ #include #include #include +#include extern "C" { #include "osapi.h" @@ -44,6 +45,9 @@ extern "C" { #include #include "c_types.h" #include "coredecls.h" +#include +#include +#include #if !CORE_MOCK @@ -426,17 +430,17 @@ int WiFiClientSecureCtx::_run_until(unsigned target, bool blocking) { DEBUG_BSSL("_run_until: Not connected\n"); return -1; } - + esp8266::polledTimeout::oneShotMs loopTimeout(_timeout); - - for (int no_work = 0; blocking || no_work < 2;) { + + for (int no_work = 0; blocking || no_work < 2;) { optimistic_yield(100); - + if (loopTimeout) { DEBUG_BSSL("_run_until: Timeout\n"); return -1; } - + int state; state = br_ssl_engine_current_state(_eng); if (state & BR_SSL_CLOSED) { @@ -459,15 +463,15 @@ int WiFiClientSecureCtx::_run_until(unsigned target, bool blocking) { buf = br_ssl_engine_sendrec_buf(_eng, &len); availForWrite = WiFiClient::availableForWrite(); - + if (!blocking && len > availForWrite) { - /* + /* writes on WiFiClient will block if len > availableForWrite() this is needed to prevent available() calls from blocking - on dropped connections + on dropped connections */ len = availForWrite; - } + } wlen = WiFiClient::write(buf, len); if (wlen <= 0) { /* @@ -1055,8 +1059,17 @@ bool WiFiClientSecureCtx::_connectSSL(const char* hostName) { _sc = std::make_shared(); _eng = &_sc->eng; // Allocation/deallocation taken care of by the _sc shared_ptr - _iobuf_in = std::shared_ptr(new (std::nothrow) unsigned char[_iobuf_in_size], std::default_delete()); - _iobuf_out = std::shared_ptr(new (std::nothrow) unsigned char[_iobuf_out_size], std::default_delete()); + //C This was borrowed from @earlephilhower PoC, to exemplify the use of IRAM. + //C Is this something we want to keep in the final release? + { // ESP.setIramHeap(); would be an alternative to using a class to set a scope for IRAM usage. + HeapSelectIram ephemeral; + _iobuf_in = std::shared_ptr(new (std::nothrow) unsigned char[_iobuf_in_size], std::default_delete()); + _iobuf_out = std::shared_ptr(new (std::nothrow) unsigned char[_iobuf_out_size], std::default_delete()); + DBG_MMU_PRINTF("\n_iobuf_in: %p\n", _iobuf_in.get()); + DBG_MMU_PRINTF( "_iobuf_out: %p\n", _iobuf_out.get()); + DBG_MMU_PRINTF( "_iobuf_in_size: %u\n", _iobuf_in_size); + DBG_MMU_PRINTF( "_iobuf_out_size: %u\n", _iobuf_out_size); + } // ESP.resetHeap(); if (!_sc || !_iobuf_in || !_iobuf_out) { _freeSSL(); // Frees _sc, _iobuf* @@ -1171,8 +1184,15 @@ bool WiFiClientSecureCtx::_connectSSLServerRSA(const X509List *chain, _oom_err = false; _sc_svr = std::make_shared(); _eng = &_sc_svr->eng; // Allocation/deallocation taken care of by the _sc shared_ptr - _iobuf_in = std::shared_ptr(new (std::nothrow) unsigned char[_iobuf_in_size], std::default_delete()); - _iobuf_out = std::shared_ptr(new (std::nothrow) unsigned char[_iobuf_out_size], std::default_delete()); + { // ESP.setIramHeap(); + HeapSelectIram ephemeral; + _iobuf_in = std::shared_ptr(new (std::nothrow) unsigned char[_iobuf_in_size], std::default_delete()); + _iobuf_out = std::shared_ptr(new (std::nothrow) unsigned char[_iobuf_out_size], std::default_delete()); + DBG_MMU_PRINTF("\n_iobuf_in: %p\n", _iobuf_in.get()); + DBG_MMU_PRINTF( "_iobuf_out: %p\n", _iobuf_out.get()); + DBG_MMU_PRINTF( "_iobuf_in_size: %u\n", _iobuf_in_size); + DBG_MMU_PRINTF( "_iobuf_out_size: %u\n", _iobuf_out_size); + } // ESP.resetHeap(); if (!_sc_svr || !_iobuf_in || !_iobuf_out) { _freeSSL(); @@ -1184,7 +1204,7 @@ bool WiFiClientSecureCtx::_connectSSLServerRSA(const X509List *chain, br_ssl_server_base_init(_sc_svr.get(), suites_server_rsa_P, sizeof(suites_server_rsa_P) / sizeof(suites_server_rsa_P[0])); br_ssl_server_set_single_rsa(_sc_svr.get(), chain ? chain->getX509Certs() : nullptr, chain ? chain->getCount() : 0, sk ? sk->getRSA() : nullptr, BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN, - br_rsa_private_get_default(), br_rsa_pkcs1_sign_get_default()); + br_rsa_private_get_default(), br_rsa_pkcs1_sign_get_default()); br_ssl_engine_set_buffers_bidi(_eng, _iobuf_in.get(), _iobuf_in_size, _iobuf_out.get(), _iobuf_out_size); if (client_CA_ta && !_installServerX509Validator(client_CA_ta)) { DEBUG_BSSL("_connectSSLServerRSA: Can't install serverX509check\n"); @@ -1208,8 +1228,15 @@ bool WiFiClientSecureCtx::_connectSSLServerEC(const X509List *chain, _oom_err = false; _sc_svr = std::make_shared(); _eng = &_sc_svr->eng; // Allocation/deallocation taken care of by the _sc shared_ptr - _iobuf_in = std::shared_ptr(new (std::nothrow) unsigned char[_iobuf_in_size], std::default_delete()); - _iobuf_out = std::shared_ptr(new (std::nothrow) unsigned char[_iobuf_out_size], std::default_delete()); + { // ESP.setIramHeap(); + HeapSelectIram ephemeral; + _iobuf_in = std::shared_ptr(new (std::nothrow) unsigned char[_iobuf_in_size], std::default_delete()); + _iobuf_out = std::shared_ptr(new (std::nothrow) unsigned char[_iobuf_out_size], std::default_delete()); + DBG_MMU_PRINTF("\n_iobuf_in: %p\n", _iobuf_in.get()); + DBG_MMU_PRINTF( "_iobuf_out: %p\n", _iobuf_out.get()); + DBG_MMU_PRINTF( "_iobuf_in_size: %u\n", _iobuf_in_size); + DBG_MMU_PRINTF( "_iobuf_out_size: %u\n", _iobuf_out_size); + } // ESP.resetHeap(); if (!_sc_svr || !_iobuf_in || !_iobuf_out) { _freeSSL(); @@ -1401,7 +1428,7 @@ bool WiFiClientSecure::probeMaxFragmentLength(IPAddress ip, uint16_t port, uint1 0x00, 26 + 14 + 6 + 5, // Extension length 0x00, 0x0d, 0x00, 0x16, 0x00, 0x14, 0x04, 0x03, 0x03, 0x03, 0x05, 0x03, 0x06, 0x03, 0x02, 0x03, 0x04, 0x01, 0x03, 0x01, 0x05, 0x01, 0x06, - 0x01, 0x02, 0x01, // Supported signature algorithms + 0x01, 0x02, 0x01, // Supported signature algorithms 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, 0x00, 0x1d, // Supported groups 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, // Supported EC formats diff --git a/libraries/esp8266/examples/HeapMetric/HeapMetric.ino b/libraries/esp8266/examples/HeapMetric/HeapMetric.ino index e56fc0d24e..3119042cac 100644 --- a/libraries/esp8266/examples/HeapMetric/HeapMetric.ino +++ b/libraries/esp8266/examples/HeapMetric/HeapMetric.ino @@ -4,6 +4,7 @@ #include #include +#include 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() { diff --git a/libraries/esp8266/examples/IramReserve/IramReserve.ino b/libraries/esp8266/examples/IramReserve/IramReserve.ino new file mode 100644 index 0000000000..344622f958 --- /dev/null +++ b/libraries/esp8266/examples/IramReserve/IramReserve.ino @@ -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 +#include +#include +#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 diff --git a/libraries/esp8266/examples/IramReserve/ProcessKey.ino b/libraries/esp8266/examples/IramReserve/ProcessKey.ino new file mode 100644 index 0000000000..9c12ac7c1c --- /dev/null +++ b/libraries/esp8266/examples/IramReserve/ProcessKey.ino @@ -0,0 +1,116 @@ +#include +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 + ")); + 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); +} diff --git a/libraries/esp8266/examples/MMU48K/MMU48K.ino b/libraries/esp8266/examples/MMU48K/MMU48K.ino new file mode 100644 index 0000000000..6acb3840f9 --- /dev/null +++ b/libraries/esp8266/examples/MMU48K/MMU48K.ino @@ -0,0 +1,322 @@ +#include +#include +#include +#include + +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 + ")); + 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); +} diff --git a/libraries/esp8266/examples/MMU48K/timed.cpp b/libraries/esp8266/examples/MMU48K/timed.cpp new file mode 100644 index 0000000000..80bd0c30df --- /dev/null +++ b/libraries/esp8266/examples/MMU48K/timed.cpp @@ -0,0 +1,16 @@ +#include +#include + +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); +} diff --git a/libraries/esp8266/examples/irammem/irammem.ino b/libraries/esp8266/examples/irammem/irammem.ino new file mode 100644 index 0000000000..57b1b31b44 --- /dev/null +++ b/libraries/esp8266/examples/irammem/irammem.ino @@ -0,0 +1,364 @@ +/* + This sketch assumes you have selected IRAM as a Second Heap from + the Arduino IDE tools menu. +*/ + +#include +#include +#include + +// #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 + ")); + 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); + } +} diff --git a/libraries/esp8266/keywords.txt b/libraries/esp8266/keywords.txt index 1f100ff0e3..75afea9ade 100644 --- a/libraries/esp8266/keywords.txt +++ b/libraries/esp8266/keywords.txt @@ -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) ####################################### diff --git a/platform.txt b/platform.txt index b3401cd0d0..13f3c051a4 100644 --- a/platform.txt +++ b/platform.txt @@ -32,6 +32,8 @@ build.lwip_flags=-DLWIP_OPEN_SRC build.vtable_flags=-DVTABLES_IN_FLASH build.sslflags= +build.mmuflags= +build.non32xferflags= build.exception_flags=-fno-exceptions build.stdcpp_lib=-lstdc++ @@ -55,18 +57,18 @@ compiler.libc.path={runtime.platform.path}/tools/sdk/libc/xtensa-lx106-elf compiler.cpreprocessor.flags=-D__ets__ -DICACHE_FLASH -U__STRICT_ANSI__ "-I{compiler.sdk.path}/include" "-I{compiler.sdk.path}/{build.lwip_include}" "-I{compiler.libc.path}/include" "-I{build.path}/core" compiler.c.cmd=xtensa-lx106-elf-gcc -compiler.c.flags=-c {compiler.warning_flags} -std=gnu17 {build.stacksmash_flags} -Os -g -Wpointer-arith -Wno-implicit-function-declaration -Wl,-EL -fno-inline-functions -nostdlib -mlongcalls -mtext-section-literals -falign-functions=4 -MMD -ffunction-sections -fdata-sections {build.exception_flags} {build.sslflags} {build.waveform} +compiler.c.flags=-c {compiler.warning_flags} -std=gnu17 {build.stacksmash_flags} -Os -g -Wpointer-arith -Wno-implicit-function-declaration -Wl,-EL -fno-inline-functions -nostdlib -mlongcalls -mtext-section-literals -falign-functions=4 -MMD -ffunction-sections -fdata-sections {build.exception_flags} {build.sslflags} {build.waveform} {build.mmuflags} {build.non32xferflags} compiler.S.cmd=xtensa-lx106-elf-gcc -compiler.S.flags=-c -g -x assembler-with-cpp -MMD -mlongcalls +compiler.S.flags=-c -g -x assembler-with-cpp -MMD -mlongcalls -I{runtime.tools.xtensa-lx106-elf-gcc.path}/include/ -compiler.c.elf.flags=-g {compiler.warning_flags} -Os -nostdlib -Wl,--no-check-sections -u app_entry {build.float} -Wl,-static "-L{compiler.sdk.path}/lib" "-L{compiler.sdk.path}/lib/{build.sdk}" "-L{compiler.sdk.path}/ld" "-L{compiler.libc.path}/lib" "-T{build.flash_ld}" -Wl,--gc-sections -Wl,-wrap,system_restart_local -Wl,-wrap,spi_flash_read +compiler.c.elf.flags= -g {compiler.warning_flags} -Os -nostdlib -Wl,--no-check-sections -u app_entry {build.float} -Wl,-static "-L{compiler.sdk.path}/lib" "-L{compiler.sdk.path}/lib/{build.sdk}" "-L{build.path}" "-L{compiler.libc.path}/lib" "-Tlocal.eagle.flash.ld" -Wl,--gc-sections -Wl,-wrap,system_restart_local -Wl,-wrap,spi_flash_read compiler.c.elf.cmd=xtensa-lx106-elf-gcc compiler.c.elf.libs=-lhal -lphy -lpp -lnet80211 {build.lwip_lib} -lwpa -lcrypto -lmain -lwps -lbearssl -lespnow -lsmartconfig -lairkiss -lwpa2 {build.stdcpp_lib} -lm -lc -lgcc compiler.cpp.cmd=xtensa-lx106-elf-g++ -compiler.cpp.flags=-c {compiler.warning_flags} {build.stacksmash_flags} -Os -g -mlongcalls -mtext-section-literals -fno-rtti -falign-functions=4 {build.stdcpp_level} -MMD -ffunction-sections -fdata-sections {build.exception_flags} {build.sslflags} {build.waveform} +compiler.cpp.flags=-c {compiler.warning_flags} {build.stacksmash_flags} -Os -g -mlongcalls -mtext-section-literals -fno-rtti -falign-functions=4 {build.stdcpp_level} -MMD -ffunction-sections -fdata-sections {build.exception_flags} {build.sslflags} {build.waveform} {build.mmuflags} {build.non32xferflags} compiler.as.cmd=xtensa-lx106-elf-as @@ -97,7 +99,12 @@ recipe.hooks.sketch.prebuild.pattern="{runtime.tools.python3.path}/python3" "{ru recipe.hooks.prebuild.10.pattern="{runtime.tools.python3.path}/python3" "{runtime.tools.makecorever}" --build_path "{build.path}" --platform_path "{runtime.platform.path}" --version "unix-{version}" ## Build the app.ld linker file -recipe.hooks.linking.prelink.1.pattern="{compiler.path}{compiler.c.cmd}" -CC -E -P {build.vtable_flags} "{runtime.platform.path}/tools/sdk/ld/eagle.app.v6.common.ld.h" -o "{build.path}/local.eagle.app.v6.common.ld" +recipe.hooks.linking.prelink.11.pattern.windows=cmd /v:on /e:on /c "if not exist {build.path}/ld_h/ (mkdir {build.path}/ld_h/ )" +recipe.hooks.linking.prelink.12.pattern.windows=copy "{runtime.platform.path}/tools/sdk/ld/{build.flash_ld}" "{build.path}/ld_h/local.eagle.flash.ld.h" +recipe.hooks.linking.prelink.11.pattern=mkdir -p "{build.path}/ld_h/" +recipe.hooks.linking.prelink.12.pattern=cp "{runtime.platform.path}/tools/sdk/ld/{build.flash_ld}" "{build.path}/ld_h/local.eagle.flash.ld.h" +recipe.hooks.linking.prelink.13.pattern="{compiler.path}{compiler.c.cmd}" -CC -E -P {build.vtable_flags} {build.mmuflags} "{build.path}/ld_h/local.eagle.flash.ld.h" -o "{build.path}/local.eagle.flash.ld" +recipe.hooks.linking.prelink.14.pattern="{compiler.path}{compiler.c.cmd}" -CC -E -P {build.vtable_flags} {build.mmuflags} "{runtime.platform.path}/tools/sdk/ld/eagle.app.v6.common.ld.h" -o "{build.path}/local.eagle.app.v6.common.ld" ## Compile c files recipe.c.o.pattern="{compiler.path}{compiler.c.cmd}" {compiler.cpreprocessor.flags} {compiler.c.flags} -D{build.sdk}=1 -DF_CPU={build.f_cpu} {build.lwip_flags} {build.debug_port} {build.debug_level} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} -DARDUINO_BOARD="{build.board}" {build.led} {build.flash_flags} {compiler.c.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}" @@ -106,7 +113,7 @@ recipe.c.o.pattern="{compiler.path}{compiler.c.cmd}" {compiler.cpreprocessor.fla recipe.cpp.o.pattern="{compiler.path}{compiler.cpp.cmd}" {compiler.cpreprocessor.flags} {compiler.cpp.flags} -D{build.sdk}=1 -DF_CPU={build.f_cpu} {build.lwip_flags} {build.debug_port} {build.debug_level} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} -DARDUINO_BOARD="{build.board}" {build.led} {build.flash_flags} {compiler.cpp.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}" ## Compile S files -recipe.S.o.pattern="{compiler.path}{compiler.c.cmd}" {compiler.cpreprocessor.flags} {compiler.S.flags} -D{build.sdk}=1 -DF_CPU={build.f_cpu} {build.lwip_flags} {build.debug_port} {build.debug_level} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} -DARDUINO_BOARD="{build.board}" {build.led} {build.flash_flags} {compiler.c.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}" +recipe.S.o.pattern="{compiler.path}{compiler.c.cmd}" {compiler.cpreprocessor.flags} {compiler.S.flags} -D{build.sdk}=1 -DF_CPU={build.f_cpu} {build.lwip_flags} {build.debug_port} {build.debug_level} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} -DARDUINO_BOARD="{build.board}" {build.led} {build.flash_flags} {compiler.S.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}" ## Create archives recipe.ar.pattern="{compiler.path}{compiler.ar.cmd}" {compiler.ar.flags} {compiler.ar.extra_flags} "{archive_file_path}" "{object_file}" @@ -120,7 +127,7 @@ recipe.objcopy.eep.pattern= ## Create hex recipe.objcopy.hex.1.pattern="{runtime.tools.python3.path}/python3" "{runtime.tools.elf2bin}" --eboot "{runtime.tools.eboot}" --app "{build.path}/{build.project_name}.elf" --flash_mode {build.flash_mode} --flash_freq {build.flash_freq} --flash_size {build.flash_size} --path "{runtime.tools.xtensa-lx106-elf-gcc.path}/bin" --out "{build.path}/{build.project_name}.bin" recipe.objcopy.hex.2.pattern="{runtime.tools.python3.path}/python3" "{runtime.tools.signing}" --mode sign --privatekey "{build.source.path}/private.key" --bin "{build.path}/{build.project_name}.bin" --out "{build.path}/{build.project_name}.bin.signed" --legacy "{build.path}/{build.project_name}.bin.legacy_sig" -recipe.objcopy.hex.3.pattern="{runtime.tools.python3.path}/python3" "{runtime.tools.sizes}" --elf "{build.path}/{build.project_name}.elf" --path "{runtime.tools.xtensa-lx106-elf-gcc.path}/bin" +recipe.objcopy.hex.3.pattern="{runtime.tools.python3.path}/python3" "{runtime.tools.sizes}" --elf "{build.path}/{build.project_name}.elf" --path "{runtime.tools.xtensa-lx106-elf-gcc.path}/bin" --mmu "{build.mmuflags}" ## Save hex recipe.output.tmp_file={build.project_name}.bin diff --git a/tests/host/common/MockEsp.cpp b/tests/host/common/MockEsp.cpp index 4394c41625..f06d829f3e 100644 --- a/tests/host/common/MockEsp.cpp +++ b/tests/host/common/MockEsp.cpp @@ -247,3 +247,19 @@ uint32_t esp_get_cycle_count() gettimeofday(&t, NULL); return (((uint64_t)t.tv_sec) * 1000000 + t.tv_usec) * (F_CPU / 1000000); } + +void EspClass::setDramHeap() +{ +} + +void EspClass::setIramHeap() +{ +} + +void EspClass::setExternalHeap() +{ +} + +void EspClass::resetHeap() +{ +} diff --git a/tools/boards.txt.py b/tools/boards.txt.py index d0fcc45854..2fa6f5c97a 100755 --- a/tools/boards.txt.py +++ b/tools/boards.txt.py @@ -956,7 +956,7 @@ }) ]) - + ################################################################ @@ -1200,6 +1200,27 @@ ( '.menu.ssl.basic.build.sslflags', '-DBEARSSL_SSL_BASIC'), ]), + ####################### mmu + + 'mmu_menu': collections.OrderedDict([ + ( '.menu.mmu.3232', '32KB cache + 32KB IRAM (balanced)' ), + ( '.menu.mmu.3232.build.mmuflags', '-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000'), + ( '.menu.mmu.4816', '16KB cache + 48KB IRAM (IRAM)' ), + ( '.menu.mmu.4816.build.mmuflags', '-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000' ), + ( '.menu.mmu.4816H', '16KB cache + 48KB IRAM and 2nd Heap (shared)' ), + ( '.menu.mmu.4816H.build.mmuflags', '-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP' ), + ( '.menu.mmu.3216', '16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared)' ), + ( '.menu.mmu.3216.build.mmuflags', '-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000' ), + ]), + + ######################## Non 32-bit load/store exception handler + + 'non32xfer_menu': collections.OrderedDict([ + ('.menu.non32xfer.fast', 'Use pgm_read macros for IRAM/PROGMEM' ), + ('.menu.non32xfer.fast.build.non32xferflags', ''), + ('.menu.non32xfer.safe', 'Byte/Word access to IRAM/PROGMEM (very slow)' ), + ('.menu.non32xfer.safe.build.non32xferflags', '-DNON32XFER_HANDLER'), + ]) } ################################################################ @@ -1370,7 +1391,8 @@ def flash_map (flashsize_kb, fs_kb = 0): print("{") print(" dport0_0_seg : org = 0x3FF00000, len = 0x10") print(" dram0_0_seg : org = 0x3FFE8000, len = 0x14000") - print(" iram1_0_seg : org = 0x40100000, len = 0x8000") + # Moved to ld/eagle.app.v6.common.ld.h as a 2nd MEMORY command. + # print(" iram1_0_seg : org = 0x40100000, len = MMU_IRAM_SIZE") print(" irom0_0_seg : org = 0x40201010, len = 0x%x" % max_upload_size) print("}") print("") @@ -1575,6 +1597,8 @@ def all_boards (): print('menu.sdk=Espressif FW') print('menu.ssl=SSL Support') print('menu.waveform=Waveform Flavour') + print('menu.mmu=MMU') + print('menu.non32xfer=Non-32-Bit Access') print('') missingboards = [] @@ -1595,7 +1619,7 @@ def all_boards (): print(id + optname + '=' + board['opts'][optname]) # macros - macrolist = [ 'defaults', 'cpufreq_menu', 'vtable_menu', 'exception_menu', 'stacksmash_menu', 'ssl_cipher_menu', 'waveform' ] + macrolist = [ 'defaults', 'cpufreq_menu', 'vtable_menu', 'exception_menu', 'stacksmash_menu', 'ssl_cipher_menu', 'waveform', 'mmu_menu', 'non32xfer_menu' ] if 'macro' in board: macrolist += board['macro'] macrolist += [ 'lwip', 'debug_menu', 'flash_erase_menu' ] diff --git a/tools/platformio-build.py b/tools/platformio-build.py index 0c893c2ccb..679d021a7f 100644 --- a/tools/platformio-build.py +++ b/tools/platformio-build.py @@ -116,7 +116,8 @@ def scons_patched_match_splitext(path, suffixes=None): join(FRAMEWORK_DIR, "tools", "sdk", "include"), join(FRAMEWORK_DIR, "tools", "sdk", "libc", "xtensa-lx106-elf", "include"), - join(FRAMEWORK_DIR, "cores", env.BoardConfig().get("build.core")) + join(FRAMEWORK_DIR, "cores", env.BoardConfig().get("build.core")), + join(platform.get_package_dir("toolchain-xtensa"), "include") ], LIBPATH=[ @@ -265,17 +266,31 @@ def scons_patched_match_splitext(path, suffixes=None): env.Append(CPPDEFINES=[current_vtables]) assert current_vtables +current_mmu_iram_size = None +for flag in env["CPPDEFINES"]: + try: + d, val = flag + if str(d).startswith("MMU_IRAM_SIZE"): + current_mmu_iram_size = "{}={}".format(d, val) + except ValueError: + continue +if not current_mmu_iram_size: + current_mmu_iram_size = "MMU_IRAM_SIZE=0x8000" + env.Append(CPPDEFINES=[current_mmu_iram_size]) +assert current_mmu_iram_size + + # Build the eagle.app.v6.common.ld linker file app_ld = env.Command( join("$BUILD_DIR", "ld", "local.eagle.app.v6.common.ld"), join(FRAMEWORK_DIR, "tools", "sdk", "ld", "eagle.app.v6.common.ld.h"), env.VerboseAction( - "$CC -CC -E -P -D%s %s $SOURCE -o $TARGET" % (current_vtables, fp_in_irom), + "$CC -CC -E -P -D%s -D%s %s $SOURCE -o $TARGET" % (current_vtables, current_mmu_iram_size, fp_in_irom), "Generating LD script $TARGET")) env.Depends("$BUILD_DIR/$PROGNAME$PROGSUFFIX", app_ld) if not env.BoardConfig().get("build.ldscript", ""): - env.Replace(LDSCRIPT_PATH=env.BoardConfig().get("build.arduino.ldscript", "")) + env.Replace(LDSCRIPT_PATH=env.BoardConfig().get("build.arduino.ldscript", "")) # # Dynamic core_version.h for staging builds diff --git a/tools/sdk/ld/eagle.app.v6.common.ld.h b/tools/sdk/ld/eagle.app.v6.common.ld.h index e18d5f731a..c51de98408 100644 --- a/tools/sdk/ld/eagle.app.v6.common.ld.h +++ b/tools/sdk/ld/eagle.app.v6.common.ld.h @@ -1,6 +1,15 @@ /* This linker script generated from xt-genldscripts.tpp for LSP . */ /* Linker Script for ld -N */ +/* The restriction to one MEMORY command, appears to be a restriction in + past versions. https://stackoverflow.com/a/55673816 + This 2nd MEMORY command appears to work fine. +*/ +MEMORY +{ + iram1_0_seg : org = 0x40100000, len = MMU_IRAM_SIZE +} + PHDRS { dport0_0_phdr PT_LOAD; @@ -88,7 +97,7 @@ SECTIONS { *(.noinit) } >dram0_0_seg :dram0_0_phdr - + #ifdef VTABLES_IN_DRAM #include "eagle.app.v6.common.ld.vtables.h" #endif diff --git a/tools/sdk/ld/eagle.flash.16m14m.ld b/tools/sdk/ld/eagle.flash.16m14m.ld index a67ef97d15..13c1243fef 100644 --- a/tools/sdk/ld/eagle.flash.16m14m.ld +++ b/tools/sdk/ld/eagle.flash.16m14m.ld @@ -10,7 +10,6 @@ MEMORY { dport0_0_seg : org = 0x3FF00000, len = 0x10 dram0_0_seg : org = 0x3FFE8000, len = 0x14000 - iram1_0_seg : org = 0x40100000, len = 0x8000 irom0_0_seg : org = 0x40201010, len = 0xfeff0 } diff --git a/tools/sdk/ld/eagle.flash.16m15m.ld b/tools/sdk/ld/eagle.flash.16m15m.ld index bca117f761..ad689f0e9f 100644 --- a/tools/sdk/ld/eagle.flash.16m15m.ld +++ b/tools/sdk/ld/eagle.flash.16m15m.ld @@ -10,7 +10,6 @@ MEMORY { dport0_0_seg : org = 0x3FF00000, len = 0x10 dram0_0_seg : org = 0x3FFE8000, len = 0x14000 - iram1_0_seg : org = 0x40100000, len = 0x8000 irom0_0_seg : org = 0x40201010, len = 0xfeff0 } diff --git a/tools/sdk/ld/eagle.flash.1m.ld b/tools/sdk/ld/eagle.flash.1m.ld index 57c35adbc0..65c4e06ac5 100644 --- a/tools/sdk/ld/eagle.flash.1m.ld +++ b/tools/sdk/ld/eagle.flash.1m.ld @@ -10,7 +10,6 @@ MEMORY { dport0_0_seg : org = 0x3FF00000, len = 0x10 dram0_0_seg : org = 0x3FFE8000, len = 0x14000 - iram1_0_seg : org = 0x40100000, len = 0x8000 irom0_0_seg : org = 0x40201010, len = 0xf9ff0 } diff --git a/tools/sdk/ld/eagle.flash.1m128.ld b/tools/sdk/ld/eagle.flash.1m128.ld index f518f08fa0..26de470135 100644 --- a/tools/sdk/ld/eagle.flash.1m128.ld +++ b/tools/sdk/ld/eagle.flash.1m128.ld @@ -10,7 +10,6 @@ MEMORY { dport0_0_seg : org = 0x3FF00000, len = 0x10 dram0_0_seg : org = 0x3FFE8000, len = 0x14000 - iram1_0_seg : org = 0x40100000, len = 0x8000 irom0_0_seg : org = 0x40201010, len = 0xd9ff0 } diff --git a/tools/sdk/ld/eagle.flash.1m144.ld b/tools/sdk/ld/eagle.flash.1m144.ld index 4a4f986511..cb3df4da48 100644 --- a/tools/sdk/ld/eagle.flash.1m144.ld +++ b/tools/sdk/ld/eagle.flash.1m144.ld @@ -10,7 +10,6 @@ MEMORY { dport0_0_seg : org = 0x3FF00000, len = 0x10 dram0_0_seg : org = 0x3FFE8000, len = 0x14000 - iram1_0_seg : org = 0x40100000, len = 0x8000 irom0_0_seg : org = 0x40201010, len = 0xd5ff0 } diff --git a/tools/sdk/ld/eagle.flash.1m160.ld b/tools/sdk/ld/eagle.flash.1m160.ld index 289c0ad0f3..b4a26c4a78 100644 --- a/tools/sdk/ld/eagle.flash.1m160.ld +++ b/tools/sdk/ld/eagle.flash.1m160.ld @@ -10,7 +10,6 @@ MEMORY { dport0_0_seg : org = 0x3FF00000, len = 0x10 dram0_0_seg : org = 0x3FFE8000, len = 0x14000 - iram1_0_seg : org = 0x40100000, len = 0x8000 irom0_0_seg : org = 0x40201010, len = 0xd1ff0 } diff --git a/tools/sdk/ld/eagle.flash.1m192.ld b/tools/sdk/ld/eagle.flash.1m192.ld index 2b71f41acd..0999e501da 100644 --- a/tools/sdk/ld/eagle.flash.1m192.ld +++ b/tools/sdk/ld/eagle.flash.1m192.ld @@ -10,7 +10,6 @@ MEMORY { dport0_0_seg : org = 0x3FF00000, len = 0x10 dram0_0_seg : org = 0x3FFE8000, len = 0x14000 - iram1_0_seg : org = 0x40100000, len = 0x8000 irom0_0_seg : org = 0x40201010, len = 0xc9ff0 } diff --git a/tools/sdk/ld/eagle.flash.1m256.ld b/tools/sdk/ld/eagle.flash.1m256.ld index 6a90e9fcb9..1e07141235 100644 --- a/tools/sdk/ld/eagle.flash.1m256.ld +++ b/tools/sdk/ld/eagle.flash.1m256.ld @@ -10,7 +10,6 @@ MEMORY { dport0_0_seg : org = 0x3FF00000, len = 0x10 dram0_0_seg : org = 0x3FFE8000, len = 0x14000 - iram1_0_seg : org = 0x40100000, len = 0x8000 irom0_0_seg : org = 0x40201010, len = 0xb9ff0 } diff --git a/tools/sdk/ld/eagle.flash.1m512.ld b/tools/sdk/ld/eagle.flash.1m512.ld index e3c6db39ee..1e3b42f1b3 100644 --- a/tools/sdk/ld/eagle.flash.1m512.ld +++ b/tools/sdk/ld/eagle.flash.1m512.ld @@ -10,7 +10,6 @@ MEMORY { dport0_0_seg : org = 0x3FF00000, len = 0x10 dram0_0_seg : org = 0x3FFE8000, len = 0x14000 - iram1_0_seg : org = 0x40100000, len = 0x8000 irom0_0_seg : org = 0x40201010, len = 0x79ff0 } diff --git a/tools/sdk/ld/eagle.flash.1m64.ld b/tools/sdk/ld/eagle.flash.1m64.ld index 87032bd029..9fa4bae54a 100644 --- a/tools/sdk/ld/eagle.flash.1m64.ld +++ b/tools/sdk/ld/eagle.flash.1m64.ld @@ -10,7 +10,6 @@ MEMORY { dport0_0_seg : org = 0x3FF00000, len = 0x10 dram0_0_seg : org = 0x3FFE8000, len = 0x14000 - iram1_0_seg : org = 0x40100000, len = 0x8000 irom0_0_seg : org = 0x40201010, len = 0xe9ff0 } diff --git a/tools/sdk/ld/eagle.flash.2m.ld b/tools/sdk/ld/eagle.flash.2m.ld index e39377b28b..125f20278b 100644 --- a/tools/sdk/ld/eagle.flash.2m.ld +++ b/tools/sdk/ld/eagle.flash.2m.ld @@ -10,7 +10,6 @@ MEMORY { dport0_0_seg : org = 0x3FF00000, len = 0x10 dram0_0_seg : org = 0x3FFE8000, len = 0x14000 - iram1_0_seg : org = 0x40100000, len = 0x8000 irom0_0_seg : org = 0x40201010, len = 0xfeff0 } diff --git a/tools/sdk/ld/eagle.flash.2m128.ld b/tools/sdk/ld/eagle.flash.2m128.ld index fd71ce4587..e15a7896f1 100644 --- a/tools/sdk/ld/eagle.flash.2m128.ld +++ b/tools/sdk/ld/eagle.flash.2m128.ld @@ -10,7 +10,6 @@ MEMORY { dport0_0_seg : org = 0x3FF00000, len = 0x10 dram0_0_seg : org = 0x3FFE8000, len = 0x14000 - iram1_0_seg : org = 0x40100000, len = 0x8000 irom0_0_seg : org = 0x40201010, len = 0xfeff0 } diff --git a/tools/sdk/ld/eagle.flash.2m1m.ld b/tools/sdk/ld/eagle.flash.2m1m.ld index 1726367401..1190e54d74 100644 --- a/tools/sdk/ld/eagle.flash.2m1m.ld +++ b/tools/sdk/ld/eagle.flash.2m1m.ld @@ -10,7 +10,6 @@ MEMORY { dport0_0_seg : org = 0x3FF00000, len = 0x10 dram0_0_seg : org = 0x3FFE8000, len = 0x14000 - iram1_0_seg : org = 0x40100000, len = 0x8000 irom0_0_seg : org = 0x40201010, len = 0xfeff0 } diff --git a/tools/sdk/ld/eagle.flash.2m256.ld b/tools/sdk/ld/eagle.flash.2m256.ld index 6b72dc1673..51259864e7 100644 --- a/tools/sdk/ld/eagle.flash.2m256.ld +++ b/tools/sdk/ld/eagle.flash.2m256.ld @@ -10,7 +10,6 @@ MEMORY { dport0_0_seg : org = 0x3FF00000, len = 0x10 dram0_0_seg : org = 0x3FFE8000, len = 0x14000 - iram1_0_seg : org = 0x40100000, len = 0x8000 irom0_0_seg : org = 0x40201010, len = 0xfeff0 } diff --git a/tools/sdk/ld/eagle.flash.2m512.ld b/tools/sdk/ld/eagle.flash.2m512.ld index eab32089f6..1c495fca01 100644 --- a/tools/sdk/ld/eagle.flash.2m512.ld +++ b/tools/sdk/ld/eagle.flash.2m512.ld @@ -10,7 +10,6 @@ MEMORY { dport0_0_seg : org = 0x3FF00000, len = 0x10 dram0_0_seg : org = 0x3FFE8000, len = 0x14000 - iram1_0_seg : org = 0x40100000, len = 0x8000 irom0_0_seg : org = 0x40201010, len = 0xfeff0 } diff --git a/tools/sdk/ld/eagle.flash.2m64.ld b/tools/sdk/ld/eagle.flash.2m64.ld index 61a94b26dd..5d4fcddc8a 100644 --- a/tools/sdk/ld/eagle.flash.2m64.ld +++ b/tools/sdk/ld/eagle.flash.2m64.ld @@ -10,7 +10,6 @@ MEMORY { dport0_0_seg : org = 0x3FF00000, len = 0x10 dram0_0_seg : org = 0x3FFE8000, len = 0x14000 - iram1_0_seg : org = 0x40100000, len = 0x8000 irom0_0_seg : org = 0x40201010, len = 0xfeff0 } diff --git a/tools/sdk/ld/eagle.flash.4m.ld b/tools/sdk/ld/eagle.flash.4m.ld index 31c0598072..36e71f64a2 100644 --- a/tools/sdk/ld/eagle.flash.4m.ld +++ b/tools/sdk/ld/eagle.flash.4m.ld @@ -10,7 +10,6 @@ MEMORY { dport0_0_seg : org = 0x3FF00000, len = 0x10 dram0_0_seg : org = 0x3FFE8000, len = 0x14000 - iram1_0_seg : org = 0x40100000, len = 0x8000 irom0_0_seg : org = 0x40201010, len = 0xfeff0 } diff --git a/tools/sdk/ld/eagle.flash.4m1m.ld b/tools/sdk/ld/eagle.flash.4m1m.ld index cf6bd7aa1e..5b0c692e70 100644 --- a/tools/sdk/ld/eagle.flash.4m1m.ld +++ b/tools/sdk/ld/eagle.flash.4m1m.ld @@ -10,7 +10,6 @@ MEMORY { dport0_0_seg : org = 0x3FF00000, len = 0x10 dram0_0_seg : org = 0x3FFE8000, len = 0x14000 - iram1_0_seg : org = 0x40100000, len = 0x8000 irom0_0_seg : org = 0x40201010, len = 0xfeff0 } diff --git a/tools/sdk/ld/eagle.flash.4m2m.ld b/tools/sdk/ld/eagle.flash.4m2m.ld index 9723b9490c..554237fb97 100644 --- a/tools/sdk/ld/eagle.flash.4m2m.ld +++ b/tools/sdk/ld/eagle.flash.4m2m.ld @@ -10,7 +10,6 @@ MEMORY { dport0_0_seg : org = 0x3FF00000, len = 0x10 dram0_0_seg : org = 0x3FFE8000, len = 0x14000 - iram1_0_seg : org = 0x40100000, len = 0x8000 irom0_0_seg : org = 0x40201010, len = 0xfeff0 } diff --git a/tools/sdk/ld/eagle.flash.4m3m.ld b/tools/sdk/ld/eagle.flash.4m3m.ld index a85a1da02c..47cf1f93e6 100644 --- a/tools/sdk/ld/eagle.flash.4m3m.ld +++ b/tools/sdk/ld/eagle.flash.4m3m.ld @@ -10,7 +10,6 @@ MEMORY { dport0_0_seg : org = 0x3FF00000, len = 0x10 dram0_0_seg : org = 0x3FFE8000, len = 0x14000 - iram1_0_seg : org = 0x40100000, len = 0x8000 irom0_0_seg : org = 0x40201010, len = 0xfeff0 } diff --git a/tools/sdk/ld/eagle.flash.512k.ld b/tools/sdk/ld/eagle.flash.512k.ld index 44ecf057d5..df8d6fd3dc 100644 --- a/tools/sdk/ld/eagle.flash.512k.ld +++ b/tools/sdk/ld/eagle.flash.512k.ld @@ -10,7 +10,6 @@ MEMORY { dport0_0_seg : org = 0x3FF00000, len = 0x10 dram0_0_seg : org = 0x3FFE8000, len = 0x14000 - iram1_0_seg : org = 0x40100000, len = 0x8000 irom0_0_seg : org = 0x40201010, len = 0x79ff0 } diff --git a/tools/sdk/ld/eagle.flash.512k128.ld b/tools/sdk/ld/eagle.flash.512k128.ld index 63c5a4c6e4..86132a9cf6 100644 --- a/tools/sdk/ld/eagle.flash.512k128.ld +++ b/tools/sdk/ld/eagle.flash.512k128.ld @@ -10,7 +10,6 @@ MEMORY { dport0_0_seg : org = 0x3FF00000, len = 0x10 dram0_0_seg : org = 0x3FFE8000, len = 0x14000 - iram1_0_seg : org = 0x40100000, len = 0x8000 irom0_0_seg : org = 0x40201010, len = 0x59ff0 } diff --git a/tools/sdk/ld/eagle.flash.512k32.ld b/tools/sdk/ld/eagle.flash.512k32.ld index ef031f4cd2..12042444ac 100644 --- a/tools/sdk/ld/eagle.flash.512k32.ld +++ b/tools/sdk/ld/eagle.flash.512k32.ld @@ -10,7 +10,6 @@ MEMORY { dport0_0_seg : org = 0x3FF00000, len = 0x10 dram0_0_seg : org = 0x3FFE8000, len = 0x14000 - iram1_0_seg : org = 0x40100000, len = 0x8000 irom0_0_seg : org = 0x40201010, len = 0x71ff0 } diff --git a/tools/sdk/ld/eagle.flash.512k64.ld b/tools/sdk/ld/eagle.flash.512k64.ld index 3cd80b5da1..47a8b1fd1d 100644 --- a/tools/sdk/ld/eagle.flash.512k64.ld +++ b/tools/sdk/ld/eagle.flash.512k64.ld @@ -10,7 +10,6 @@ MEMORY { dport0_0_seg : org = 0x3FF00000, len = 0x10 dram0_0_seg : org = 0x3FFE8000, len = 0x14000 - iram1_0_seg : org = 0x40100000, len = 0x8000 irom0_0_seg : org = 0x40201010, len = 0x69ff0 } diff --git a/tools/sdk/ld/eagle.flash.8m6m.ld b/tools/sdk/ld/eagle.flash.8m6m.ld index 1077454228..f3febe2085 100644 --- a/tools/sdk/ld/eagle.flash.8m6m.ld +++ b/tools/sdk/ld/eagle.flash.8m6m.ld @@ -10,7 +10,6 @@ MEMORY { dport0_0_seg : org = 0x3FF00000, len = 0x10 dram0_0_seg : org = 0x3FFE8000, len = 0x14000 - iram1_0_seg : org = 0x40100000, len = 0x8000 irom0_0_seg : org = 0x40201010, len = 0xfeff0 } diff --git a/tools/sdk/ld/eagle.flash.8m7m.ld b/tools/sdk/ld/eagle.flash.8m7m.ld index 099f801f1d..ee4de71084 100644 --- a/tools/sdk/ld/eagle.flash.8m7m.ld +++ b/tools/sdk/ld/eagle.flash.8m7m.ld @@ -10,7 +10,6 @@ MEMORY { dport0_0_seg : org = 0x3FF00000, len = 0x10 dram0_0_seg : org = 0x3FFE8000, len = 0x14000 - iram1_0_seg : org = 0x40100000, len = 0x8000 irom0_0_seg : org = 0x40201010, len = 0xfeff0 } diff --git a/tools/sdk/ld/eagle.rom.addr.v6.ld b/tools/sdk/ld/eagle.rom.addr.v6.ld index 84c5e3acf5..e2cff957a9 100644 --- a/tools/sdk/ld/eagle.rom.addr.v6.ld +++ b/tools/sdk/ld/eagle.rom.addr.v6.ld @@ -79,6 +79,11 @@ PROVIDE ( _xtos_l1int_handler = 0x4000048c ); PROVIDE ( _xtos_p_none = 0x4000dbf8 ); PROVIDE ( _xtos_restore_intlevel = 0x4000056c ); PROVIDE ( _xtos_return_from_exc = 0x4000dc54 ); + +/* Added to support replacing the ROM _xtos_c_wrapper_handler */ +PROVIDE ( _xtos_c_handler_table = 0x3fffc100 ); +PROVIDE ( _xtos_exc_handler_table = 0x3fffc000 ); + PROVIDE ( _xtos_set_exception_handler = 0x40000454 ); PROVIDE ( _xtos_set_interrupt_handler = 0x4000bd70 ); PROVIDE ( _xtos_set_interrupt_handler_arg = 0x4000bd28 ); diff --git a/tools/sizes.py b/tools/sizes.py index f633f8a6a5..470e526c7a 100755 --- a/tools/sizes.py +++ b/tools/sizes.py @@ -23,10 +23,11 @@ import subprocess import sys -def get_segment_hints(): +def get_segment_hints(iram): hints = {} + hints['ICACHE'] = ' - flash instruction cache' hints['IROM'] = ' - code in flash (default or ICACHE_FLASH_ATTR)' - hints['IRAM'] = ' / 32768 - code in IRAM (ICACHE_RAM_ATTR, ISRs...)' + hints['IRAM'] = ' / ' + str(iram) + ' - code in IRAM (ICACHE_RAM_ATTR, ISRs...)' hints['DATA'] = ') - initialized variables (global, static) in RAM/HEAP' hints['RODATA'] = ') / 81920 - constants (global, static) in RAM/HEAP' hints['BSS'] = ') - zeroed variables (global, static) in RAM/HEAP' @@ -34,6 +35,7 @@ def get_segment_hints(): def get_segment_sizes(elf, path): sizes = {} + sizes['ICACHE'] = 0 sizes['IROM'] = 0 sizes['IRAM'] = 0 sizes['DATA'] = 0 @@ -55,14 +57,28 @@ def get_segment_sizes(elf, path): sizes['BSS'] = sizes['BSS'] + int(words[1]) return sizes +def get_mmu_sizes(mmu, sizes): + iram = 0x8000 + sizes['ICACHE'] = 0x8000 + lines = mmu.split(' ') + for line in lines: + words = line.split('=') + if line.startswith('-DMMU_IRAM_SIZE'): + iram = int(words[1], 16) + elif line.startswith('-DMMU_ICACHE_SIZE'): + sizes['ICACHE'] = int(words[1], 16) + return [iram, sizes] + def main(): parser = argparse.ArgumentParser(description='Report the different segment sizes of a compiled ELF file') parser.add_argument('-e', '--elf', action='store', required=True, help='Path to the Arduino sketch ELF') parser.add_argument('-p', '--path', action='store', required=True, help='Path to Xtensa toolchain binaries') + parser.add_argument('-i', '--mmu', action='store', required=False, help='MMU build options') args = parser.parse_args() sizes = get_segment_sizes(args.elf, args.path) - hints = get_segment_hints() + [iram, sizes] = get_mmu_sizes(args.mmu, sizes) + hints = get_segment_hints(iram) sys.stderr.write("Executable segment sizes:" + os.linesep) for k in sizes.keys():