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 5843b8fed4..52004fdbb8 100755 Binary files a/bootloaders/eboot/eboot.elf and b/bootloaders/eboot/eboot.elf differ 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():