mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-16 10:43:29 +00:00
esp32s3: add flash XIP boot assembly with cache/MMU init
The ESP32-S3 ROM bootloader loads IRAM/DRAM into SRAM but does not configure the flash cache or MMU. Previously the target incorrectly reused the ESP32 boot assembly (esp32.S) which lacks flash XIP support. Add a dedicated esp32s3.S boot assembly that: - Sets up windowed-ABI registers, stack, and FPU - Disables all watchdog timers (RTC, TIMG0, TIMG1, Super WDT) - Configures VECBASE and clears PS.EXCM before any callx4 - Calls ROM functions to configure cache modes: rom_config_instruction_cache_mode (16KB, 8-way, 32B line) rom_config_data_cache_mode (32KB, 8-way, 32B line) - Initializes MMU, maps flash page 0 for IROM and DROM, clears bus-shut bits, and enables both caches - Jumps to runtime.main in IROM (flash) Update the linker script (esp32s3.ld) to place .text and .rodata in flash-mapped regions (IROM/DROM) with proper alignment for the MMU page size. Update esp32s3-interrupts.S with proper exception vector handlers. Point esp32s3.json at the new esp32s3.S instead of esp32.S. Signed-off-by: deadprogram <ron@hybridgroup.com>
This commit is contained in:
@@ -0,0 +1,317 @@
|
|||||||
|
// Startup code for the ESP32-S3 (Xtensa LX7, windowed ABI).
|
||||||
|
//
|
||||||
|
// The ROM bootloader loads IRAM/DRAM segments into SRAM but does NOT
|
||||||
|
// configure flash cache/MMU. We must:
|
||||||
|
// 1. Set up the windowed-ABI register file and stack pointer.
|
||||||
|
// 2. Set VECBASE and clear PS.EXCM (needed for callx4 window overflows).
|
||||||
|
// 3. Disable watchdog timers.
|
||||||
|
// 4. Configure the flash cache and MMU so IROM/DROM are accessible.
|
||||||
|
// 5. Jump to runtime.main (in IROM).
|
||||||
|
//
|
||||||
|
// Cache/MMU init sequence (from NuttX esp_loader.c / ESP-IDF bootloader / esp-hal):
|
||||||
|
// Phase A — configure cache modes:
|
||||||
|
// a. rom_config_instruction_cache_mode(16KB, 8-way, 32B)
|
||||||
|
// b. rom_Cache_Suspend_DCache()
|
||||||
|
// c. rom_config_data_cache_mode(32KB, 8-way, 32B)
|
||||||
|
// d. Cache_Resume_DCache(0)
|
||||||
|
// Phase B — map flash pages:
|
||||||
|
// e. Disable caches
|
||||||
|
// f. Cache_MMU_Init() — reset all MMU entries to invalid
|
||||||
|
// g. Cache_Set_IDROM_MMU_Size() — set IROM/DROM entry split
|
||||||
|
// h. Write MMU entries mapping flash page 0 for IROM and DROM
|
||||||
|
// i. Clear bus-shut bits
|
||||||
|
// j. Enable caches + isync
|
||||||
|
|
||||||
|
#define PS_WOE 0x00040000
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Boot entry point — placed in IRAM by the linker.
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
.section .text.call_start_cpu0
|
||||||
|
.literal_position
|
||||||
|
.align 4
|
||||||
|
.Lstack_top_addr:
|
||||||
|
.long _stack_top
|
||||||
|
.Lmain_addr:
|
||||||
|
.long main
|
||||||
|
.Lvector_table_addr:
|
||||||
|
.long _vector_table
|
||||||
|
// WDT register addresses
|
||||||
|
.Lwdt_key:
|
||||||
|
.long 0x50D83AA1
|
||||||
|
.Lrtc_wdt_protect:
|
||||||
|
.long 0x600080B0
|
||||||
|
.Lrtc_wdt_config0:
|
||||||
|
.long 0x60008098
|
||||||
|
.Ltimg0_wdt_protect:
|
||||||
|
.long 0x6001F064
|
||||||
|
.Ltimg0_wdt_config0:
|
||||||
|
.long 0x6001F048
|
||||||
|
.Ltimg1_wdt_protect:
|
||||||
|
.long 0x60020064
|
||||||
|
.Ltimg1_wdt_config0:
|
||||||
|
.long 0x60020048
|
||||||
|
.Lswd_protect:
|
||||||
|
.long 0x600080B8
|
||||||
|
.Lswd_key:
|
||||||
|
.long 0x8F1D312A
|
||||||
|
.Lswd_conf:
|
||||||
|
.long 0x600080B4
|
||||||
|
.Lswd_disable:
|
||||||
|
.long 0x40000000
|
||||||
|
// ROM function addresses (from ESP-IDF esp32s3.rom.ld)
|
||||||
|
.Lrom_config_icache:
|
||||||
|
.long 0x40001a1c
|
||||||
|
.Lrom_config_dcache:
|
||||||
|
.long 0x40001a28
|
||||||
|
.Lrom_suspend_dcache:
|
||||||
|
.long 0x400018b4
|
||||||
|
.LCache_Resume_DCache:
|
||||||
|
.long 0x400018c0
|
||||||
|
.LCache_Disable_ICache:
|
||||||
|
.long 0x4000186c
|
||||||
|
.LCache_Disable_DCache:
|
||||||
|
.long 0x40001884
|
||||||
|
.LCache_MMU_Init:
|
||||||
|
.long 0x40001998
|
||||||
|
.LCache_Set_IDROM_MMU_Size:
|
||||||
|
.long 0x40001914
|
||||||
|
.LCache_Enable_ICache:
|
||||||
|
.long 0x40001878
|
||||||
|
.LCache_Enable_DCache:
|
||||||
|
.long 0x40001890
|
||||||
|
// Cache/MMU register addresses
|
||||||
|
.Lmmu_table_base:
|
||||||
|
.long 0x600C5000
|
||||||
|
.Licache_ctrl1_reg:
|
||||||
|
.long 0x600C4064
|
||||||
|
.Ldcache_ctrl1_reg:
|
||||||
|
.long 0x600C4004
|
||||||
|
|
||||||
|
.global call_start_cpu0
|
||||||
|
call_start_cpu0:
|
||||||
|
|
||||||
|
// ---- 1. Windowed-ABI register file setup ----
|
||||||
|
|
||||||
|
// Disable WOE so we can safely manipulate WINDOWSTART.
|
||||||
|
rsr.ps a2
|
||||||
|
movi a3, ~(PS_WOE)
|
||||||
|
and a2, a2, a3
|
||||||
|
wsr.ps a2
|
||||||
|
rsync
|
||||||
|
|
||||||
|
// Set WINDOWSTART to 1 << WINDOWBASE (mark only current window as valid).
|
||||||
|
rsr.windowbase a2
|
||||||
|
ssl a2
|
||||||
|
movi a2, 1
|
||||||
|
sll a2, a2
|
||||||
|
wsr.windowstart a2
|
||||||
|
rsync
|
||||||
|
|
||||||
|
// Load stack pointer.
|
||||||
|
l32r a1, .Lstack_top_addr
|
||||||
|
|
||||||
|
// Re-enable WOE.
|
||||||
|
rsr.ps a2
|
||||||
|
movi a3, PS_WOE
|
||||||
|
or a2, a2, a3
|
||||||
|
wsr.ps a2
|
||||||
|
rsync
|
||||||
|
|
||||||
|
// Enable FPU (coprocessor 0).
|
||||||
|
movi a2, 1
|
||||||
|
wsr.cpenable a2
|
||||||
|
rsync
|
||||||
|
|
||||||
|
// ---- 2. Disable all watchdog timers (IMMEDIATELY, before any delay) ----
|
||||||
|
l32r a3, .Lwdt_key
|
||||||
|
movi a4, 0
|
||||||
|
|
||||||
|
// RTC WDT
|
||||||
|
l32r a2, .Lrtc_wdt_protect
|
||||||
|
memw
|
||||||
|
s32i a3, a2, 0
|
||||||
|
l32r a5, .Lrtc_wdt_config0
|
||||||
|
memw
|
||||||
|
s32i a4, a5, 0
|
||||||
|
memw
|
||||||
|
s32i a4, a2, 0
|
||||||
|
|
||||||
|
// TIMG0 WDT
|
||||||
|
l32r a2, .Ltimg0_wdt_protect
|
||||||
|
memw
|
||||||
|
s32i a3, a2, 0
|
||||||
|
l32r a5, .Ltimg0_wdt_config0
|
||||||
|
memw
|
||||||
|
s32i a4, a5, 0
|
||||||
|
memw
|
||||||
|
s32i a4, a2, 0
|
||||||
|
|
||||||
|
// TIMG1 WDT
|
||||||
|
l32r a2, .Ltimg1_wdt_protect
|
||||||
|
memw
|
||||||
|
s32i a3, a2, 0
|
||||||
|
l32r a5, .Ltimg1_wdt_config0
|
||||||
|
memw
|
||||||
|
s32i a4, a5, 0
|
||||||
|
memw
|
||||||
|
s32i a4, a2, 0
|
||||||
|
|
||||||
|
// Super WDT
|
||||||
|
l32r a2, .Lswd_protect
|
||||||
|
l32r a3, .Lswd_key
|
||||||
|
memw
|
||||||
|
s32i a3, a2, 0
|
||||||
|
l32r a5, .Lswd_conf
|
||||||
|
l32r a6, .Lswd_disable
|
||||||
|
memw
|
||||||
|
s32i a6, a5, 0
|
||||||
|
memw
|
||||||
|
s32i a4, a2, 0
|
||||||
|
|
||||||
|
// ---- 3. Set VECBASE and clear PS.EXCM ----
|
||||||
|
// VECBASE must be set before any callx4 so that window overflow
|
||||||
|
// exceptions (triggered by register window rotation) route to our
|
||||||
|
// handlers in IRAM, not the ROM's default vectors.
|
||||||
|
l32r a8, .Lvector_table_addr
|
||||||
|
wsr.vecbase a8
|
||||||
|
rsync
|
||||||
|
|
||||||
|
// Clear PS.EXCM (bit 4) and PS.INTLEVEL (bits 0-3).
|
||||||
|
// The ROM bootloader may leave EXCM=1; with EXCM set any callx4
|
||||||
|
// window overflow would become a double exception.
|
||||||
|
// Set PS.UM (bit 5) so level-1 exceptions route to User vector.
|
||||||
|
rsr.ps a2
|
||||||
|
movi a3, ~0x1F
|
||||||
|
and a2, a2, a3
|
||||||
|
movi a3, 0x20
|
||||||
|
or a2, a2, a3
|
||||||
|
wsr.ps a2
|
||||||
|
rsync
|
||||||
|
|
||||||
|
// ---- 4. Configure flash cache and MMU ----
|
||||||
|
//
|
||||||
|
// ROM function calls use callx4 (windowed ABI):
|
||||||
|
// a4 = target address (overwritten with return addr by call mechanism)
|
||||||
|
// a5 = stack pointer for callee (becomes callee's a1 via entry)
|
||||||
|
// a6 = first argument (becomes callee's a2)
|
||||||
|
// a7 = second argument (becomes callee's a3)
|
||||||
|
// a8 = third argument (becomes callee's a4)
|
||||||
|
// Registers a0-a3 are preserved across callx4; a4-a11 may be clobbered.
|
||||||
|
|
||||||
|
// Phase A: Configure cache modes (required for cache hardware to function).
|
||||||
|
// Without this, the cache doesn't know its size/associativity/line-size
|
||||||
|
// and cannot service flash accesses.
|
||||||
|
|
||||||
|
// 4a. Configure ICache mode: 16KB, 8-way, 32-byte line
|
||||||
|
movi a6, 0x4000 // cache_size = 16KB
|
||||||
|
movi a7, 8 // ways = 8
|
||||||
|
movi a8, 32 // line_size = 32
|
||||||
|
mov a5, a1
|
||||||
|
l32r a4, .Lrom_config_icache
|
||||||
|
callx4 a4
|
||||||
|
|
||||||
|
// 4b. Suspend DCache before configuring it
|
||||||
|
mov a5, a1
|
||||||
|
l32r a4, .Lrom_suspend_dcache
|
||||||
|
callx4 a4
|
||||||
|
|
||||||
|
// 4c. Configure DCache mode: 32KB, 8-way, 32-byte line
|
||||||
|
movi a6, 0x8000 // cache_size = 32KB
|
||||||
|
movi a7, 8 // ways = 8
|
||||||
|
movi a8, 32 // line_size = 32
|
||||||
|
mov a5, a1
|
||||||
|
l32r a4, .Lrom_config_dcache
|
||||||
|
callx4 a4
|
||||||
|
|
||||||
|
// 4d. Resume DCache
|
||||||
|
movi a6, 0
|
||||||
|
mov a5, a1
|
||||||
|
l32r a4, .LCache_Resume_DCache
|
||||||
|
callx4 a4
|
||||||
|
|
||||||
|
// Phase B: Map flash pages into MMU.
|
||||||
|
|
||||||
|
// 4e. Disable ICache
|
||||||
|
mov a5, a1
|
||||||
|
l32r a4, .LCache_Disable_ICache
|
||||||
|
callx4 a4
|
||||||
|
|
||||||
|
// 4f. Disable DCache
|
||||||
|
mov a5, a1
|
||||||
|
l32r a4, .LCache_Disable_DCache
|
||||||
|
callx4 a4
|
||||||
|
|
||||||
|
// 4g. Initialize MMU (resets all 512 entries to invalid = 0x4000)
|
||||||
|
mov a5, a1
|
||||||
|
l32r a4, .LCache_MMU_Init
|
||||||
|
callx4 a4
|
||||||
|
|
||||||
|
// 4h. Set IDROM MMU size: even 256/256 split.
|
||||||
|
// Each entry is 4 bytes, so 256 entries = 0x400 bytes per region.
|
||||||
|
movi a6, 0x400 // irom_mmu_size (256 entries × 4 bytes)
|
||||||
|
movi a7, 0x400 // drom_mmu_size (256 entries × 4 bytes)
|
||||||
|
mov a5, a1
|
||||||
|
l32r a4, .LCache_Set_IDROM_MMU_Size
|
||||||
|
callx4 a4
|
||||||
|
|
||||||
|
// 4i. Write MMU entries: map flash page 0 for both IROM and DROM.
|
||||||
|
// MMU table at 0x600C5000: entries 0-255 = ICache, 256-511 = DCache.
|
||||||
|
// Entry value 0 = flash page 0, valid (SOC_MMU_VALID = 0).
|
||||||
|
// Our .text (IROM, VMA 0x42000xxx) and .rodata (DROM, VMA 0x3C000xxx)
|
||||||
|
// both reside in flash page 0 (first 64KB).
|
||||||
|
l32r a8, .Lmmu_table_base // a8 = 0x600C5000
|
||||||
|
movi a9, 0 // flash page 0
|
||||||
|
s32i a9, a8, 0 // Entry 0: ICache VMA 0x42000000
|
||||||
|
addmi a10, a8, 0x400 // a10 = 0x600C5400 (entry 256)
|
||||||
|
s32i a9, a10, 0 // Entry 256: DCache VMA 0x3C000000
|
||||||
|
memw
|
||||||
|
|
||||||
|
// 4j. Clear bus-shut bits so core 0 can access ICache and DCache buses.
|
||||||
|
l32r a8, .Licache_ctrl1_reg // 0x600C4064
|
||||||
|
movi a9, 0
|
||||||
|
s32i a9, a8, 0 // Clear all ICACHE_CTRL1 shut bits
|
||||||
|
l32r a8, .Ldcache_ctrl1_reg // 0x600C4004
|
||||||
|
s32i a9, a8, 0 // Clear all DCACHE_CTRL1 shut bits
|
||||||
|
memw
|
||||||
|
|
||||||
|
// 4k. Enable ICache (arg: autoload = 0)
|
||||||
|
movi a6, 0
|
||||||
|
mov a5, a1
|
||||||
|
l32r a4, .LCache_Enable_ICache
|
||||||
|
callx4 a4
|
||||||
|
|
||||||
|
// 4l. Enable DCache (arg: autoload = 0)
|
||||||
|
movi a6, 0
|
||||||
|
mov a5, a1
|
||||||
|
l32r a4, .LCache_Enable_DCache
|
||||||
|
callx4 a4
|
||||||
|
|
||||||
|
// Flush instruction pipeline so new cache/MMU config takes effect.
|
||||||
|
isync
|
||||||
|
|
||||||
|
// ---- 5. Jump to main (in IROM) ----
|
||||||
|
// Re-clear PS.EXCM in case ROM calls changed processor state.
|
||||||
|
rsr.ps a2
|
||||||
|
movi a3, ~0x1F
|
||||||
|
and a2, a2, a3
|
||||||
|
movi a3, 0x20
|
||||||
|
or a2, a2, a3
|
||||||
|
wsr.ps a2
|
||||||
|
rsync
|
||||||
|
|
||||||
|
mov a5, a1
|
||||||
|
l32r a4, .Lmain_addr
|
||||||
|
callx4 a4
|
||||||
|
|
||||||
|
// If main returns, loop forever.
|
||||||
|
1: j 1b
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// tinygo_scanCurrentStack — tail-jump to tinygo_scanstack.
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
.section .text.tinygo_scanCurrentStack
|
||||||
|
|
||||||
|
.global tinygo_scanCurrentStack
|
||||||
|
tinygo_scanCurrentStack:
|
||||||
|
j tinygo_scanstack
|
||||||
@@ -172,11 +172,12 @@ _nmi_vector:
|
|||||||
j _nmi_vector
|
j _nmi_vector
|
||||||
|
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
// Offset 0x300 — Kernel exception (stub — loops forever)
|
// Offset 0x300 — Kernel exception
|
||||||
|
// Diagnostic: steady 1s ON / 1s OFF blink.
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
.org _vector_table + 0x300
|
.org _vector_table + 0x300
|
||||||
_kernel_vector:
|
_kernel_vector:
|
||||||
j _kernel_vector
|
j _handle_kernel_exc // jump to handler below table
|
||||||
|
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
// Offset 0x340 — User exception / level-1 interrupt
|
// Offset 0x340 — User exception / level-1 interrupt
|
||||||
@@ -190,11 +191,18 @@ _level1_vector:
|
|||||||
j _handle_level1 // jump to full handler (PC-relative, no literal pool)
|
j _handle_level1 // jump to full handler (PC-relative, no literal pool)
|
||||||
|
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
// Offset 0x3C0 — Double exception (stub — loops forever)
|
// Offset 0x3C0 — Double exception
|
||||||
|
// Diagnostic: solid LED ON (no blinking).
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
.org _vector_table + 0x3C0
|
.org _vector_table + 0x3C0
|
||||||
_double_vector:
|
_double_vector:
|
||||||
j _double_vector
|
movi a0, 1
|
||||||
|
slli a0, a0, 21
|
||||||
|
movi a1, 6
|
||||||
|
slli a1, a1, 28
|
||||||
|
addmi a1, a1, 0x4000
|
||||||
|
s32i a0, a1, 0x0C // LED ON permanently
|
||||||
|
1: j 1b // spin forever
|
||||||
|
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
// Level-1 interrupt handler — lives outside the vector table so there
|
// Level-1 interrupt handler — lives outside the vector table so there
|
||||||
@@ -225,6 +233,36 @@ _double_vector:
|
|||||||
.LhandleInterrupt_addr:
|
.LhandleInterrupt_addr:
|
||||||
.word handleInterrupt
|
.word handleInterrupt
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Kernel exception handler (out-of-table).
|
||||||
|
// a3 = EXCCAUSE+1 (set in _kernel_vector stub).
|
||||||
|
// Blinks GPIO21 a3 times, long pause, repeat forever.
|
||||||
|
// 1 blink = cause 0 (Illegal instruction)
|
||||||
|
// 3 blinks = cause 2 (Instruction fetch error)
|
||||||
|
// 6 blinks = cause 5 (Alloca / window check in entry)
|
||||||
|
// 29 blinks = cause 28 (LoadProhibitedCause)
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
_handle_kernel_exc:
|
||||||
|
// Steady 1-second ON / 1-second OFF blink.
|
||||||
|
// This is the KERNEL exception handler.
|
||||||
|
movi a0, 1
|
||||||
|
slli a0, a0, 21 // a0 = GPIO21 bit
|
||||||
|
movi a1, 6
|
||||||
|
slli a1, a1, 28
|
||||||
|
addmi a1, a1, 0x4000 // a1 = 0x60004000
|
||||||
|
_hke_blink:
|
||||||
|
s32i a0, a1, 0x0C // LED ON
|
||||||
|
movi a2, 5
|
||||||
|
slli a2, a2, 23 // ~1s @40MHz
|
||||||
|
1: addi a2, a2, -1
|
||||||
|
bnez a2, 1b
|
||||||
|
s32i a0, a1, 0x08 // LED OFF
|
||||||
|
movi a2, 5
|
||||||
|
slli a2, a2, 23 // ~1s @40MHz
|
||||||
|
1: addi a2, a2, -1
|
||||||
|
bnez a2, 1b
|
||||||
|
j _hke_blink
|
||||||
|
|
||||||
.global _handle_level1
|
.global _handle_level1
|
||||||
_handle_level1:
|
_handle_level1:
|
||||||
// --- allocate 96-byte exception frame on the interrupted stack ---
|
// --- allocate 96-byte exception frame on the interrupted stack ---
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
"libc": "picolibc",
|
"libc": "picolibc",
|
||||||
"linkerscript": "targets/esp32s3.ld",
|
"linkerscript": "targets/esp32s3.ld",
|
||||||
"extra-files": [
|
"extra-files": [
|
||||||
"src/device/esp/esp32.S",
|
"src/device/esp/esp32s3.S",
|
||||||
"targets/esp32s3-interrupts.S",
|
"targets/esp32s3-interrupts.S",
|
||||||
"src/internal/task/task_stack_esp32.S"
|
"src/internal/task/task_stack_esp32.S"
|
||||||
],
|
],
|
||||||
|
|||||||
+124
-78
@@ -1,15 +1,25 @@
|
|||||||
/* Linker script for the ESP32-S3 */
|
/* Linker script for the ESP32-S3 (flash XIP)
|
||||||
|
*
|
||||||
|
* The ESP32-S3 has 512KB of internal SRAM:
|
||||||
|
* - SRAM0 (32KB): 0x40370000-0x40377FFF — used by ICache when flash XIP is
|
||||||
|
* active, so we MUST NOT place code here.
|
||||||
|
* - SRAM1 (416KB): dual-mapped as IRAM 0x40378000-0x403DFFFF and
|
||||||
|
* DRAM 0x3FC88000-0x3FCEFFFF.
|
||||||
|
*
|
||||||
|
* Flash is memory-mapped via the cache:
|
||||||
|
* - DROM (read-only data): 0x3C000000, up to 32MB
|
||||||
|
* - IROM (executable code): 0x42000000, up to 32MB
|
||||||
|
* The MMU uses 64KB pages, so the bottom 16 bits of the virtual address
|
||||||
|
* and the flash offset must match. Dummy sections handle this alignment.
|
||||||
|
*/
|
||||||
|
|
||||||
MEMORY
|
MEMORY
|
||||||
{
|
{
|
||||||
/* Note: DRAM and IRAM below are actually in the same 416K address space. */
|
DRAM (rw) : ORIGIN = 0x3FC88000, LENGTH = 416K
|
||||||
DRAM (rw) : ORIGIN = 0x3FC88000, LENGTH = 416K /* Internal SRAM 1 (data bus) */
|
IRAM (x) : ORIGIN = 0x40378000, LENGTH = 416K /* SRAM1 only (SRAM0 used by ICache) */
|
||||||
IRAM (x) : ORIGIN = 0x40370000, LENGTH = 416K /* Internal SRAM 1 (instruction bus) */
|
|
||||||
|
|
||||||
/* Note: DROM and IROM below are actually in the same 32M address space. */
|
DROM (r) : ORIGIN = 0x3C000000, LENGTH = 32M /* Flash data bus (read-only) */
|
||||||
DROM (r) : ORIGIN = 0x3C000000, LENGTH = 32M /* Data bus (read-only) */
|
IROM (rx) : ORIGIN = 0x42000000, LENGTH = 32M /* Flash instruction bus */
|
||||||
IROM (rx) : ORIGIN = 0x42000000, LENGTH = 32M /* Instruction bus */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The entry point. It is set in the image flashed to the chip, so must be
|
/* The entry point. It is set in the image flashed to the chip, so must be
|
||||||
@@ -19,9 +29,25 @@ ENTRY(call_start_cpu0)
|
|||||||
|
|
||||||
SECTIONS
|
SECTIONS
|
||||||
{
|
{
|
||||||
|
/* Dummy section so that .rodata starts right after the image header
|
||||||
|
* and DROM segment header in the flash image.
|
||||||
|
*/
|
||||||
|
.rodata_dummy (NOLOAD): ALIGN(4)
|
||||||
|
{
|
||||||
|
. += 0x18; /* esp_image_header_t at start of flash */
|
||||||
|
. += 0x8; /* DROM segment header (8 bytes) */
|
||||||
|
} > DROM
|
||||||
|
|
||||||
|
/* Constant global variables, stored in flash (DROM). */
|
||||||
|
.rodata : ALIGN(4)
|
||||||
|
{
|
||||||
|
*(.rodata*)
|
||||||
|
. = ALIGN (4);
|
||||||
|
} >DROM
|
||||||
|
|
||||||
/* Put the stack at the bottom of DRAM, so that the application will
|
/* Put the stack at the bottom of DRAM, so that the application will
|
||||||
* crash on stack overflow instead of silently corrupting memory.
|
* crash on stack overflow instead of silently corrupting memory.
|
||||||
* See: http://blog.japaric.io/stack-overflow-protection/ */
|
*/
|
||||||
.stack (NOLOAD) :
|
.stack (NOLOAD) :
|
||||||
{
|
{
|
||||||
. = ALIGN(16);
|
. = ALIGN(16);
|
||||||
@@ -29,86 +55,106 @@ SECTIONS
|
|||||||
_stack_top = .;
|
_stack_top = .;
|
||||||
} >DRAM
|
} >DRAM
|
||||||
|
|
||||||
/* Constant literals and code. Loaded into IRAM for now. Eventually, most
|
/* Global variables that are mutable and zero-initialized. */
|
||||||
* code should be executed directly from flash.
|
|
||||||
* Note that literals must be before code for the l32r instruction to work.
|
|
||||||
*/
|
|
||||||
.text.call_start_cpu0 : ALIGN(4)
|
|
||||||
{
|
|
||||||
*(.literal.call_start_cpu0)
|
|
||||||
*(.text.call_start_cpu0)
|
|
||||||
} >IRAM AT >DRAM
|
|
||||||
|
|
||||||
/* Xtensa exception/interrupt vector table — must be 0x400-aligned. */
|
|
||||||
.text.exception_vectors : ALIGN(0x400)
|
|
||||||
{
|
|
||||||
*(.text.exception_vectors)
|
|
||||||
} >IRAM AT >DRAM
|
|
||||||
|
|
||||||
/* Level-1 interrupt handler (called from the vector stub). */
|
|
||||||
.text._handle_level1 : ALIGN(4)
|
|
||||||
{
|
|
||||||
*(.literal._handle_level1)
|
|
||||||
*(.text._handle_level1)
|
|
||||||
} >IRAM AT >DRAM
|
|
||||||
|
|
||||||
/* All other code and literals */
|
|
||||||
.text : ALIGN(4)
|
|
||||||
{
|
|
||||||
*(.literal .text)
|
|
||||||
*(.literal.* .text.*)
|
|
||||||
*(.text)
|
|
||||||
*(.text.*)
|
|
||||||
} >IRAM AT >DRAM
|
|
||||||
|
|
||||||
/* Constant global variables.
|
|
||||||
* They are loaded in DRAM for ease of use. Eventually they should be stored
|
|
||||||
* in flash and loaded directly from there but they're kept in RAM to make
|
|
||||||
* sure they can always be accessed (even in interrupts).
|
|
||||||
*/
|
|
||||||
.rodata : ALIGN(4)
|
|
||||||
{
|
|
||||||
*(.rodata)
|
|
||||||
*(.rodata.*)
|
|
||||||
} >DRAM
|
|
||||||
|
|
||||||
/* Mutable global variables.
|
|
||||||
*/
|
|
||||||
.data : ALIGN(4)
|
|
||||||
{
|
|
||||||
_sdata = ABSOLUTE(.);
|
|
||||||
*(.data)
|
|
||||||
*(.data.*)
|
|
||||||
_edata = ABSOLUTE(.);
|
|
||||||
} >DRAM
|
|
||||||
|
|
||||||
/* Check that the boot ROM stack (for the APP CPU) does not overlap with the
|
|
||||||
* data that is loaded by the boot ROM. There may be ways to avoid this
|
|
||||||
* issue if it occurs in practice.
|
|
||||||
* The magic value here is _stack_sentry in the boot ROM ELF file.
|
|
||||||
*/
|
|
||||||
ASSERT(_edata < 0x3ffe1320, "the .data section overlaps with the stack used by the boot ROM, possibly causing corruption at startup")
|
|
||||||
|
|
||||||
/* Global variables that are mutable and zero-initialized.
|
|
||||||
* These must be zeroed at startup (unlike data, which is loaded by the
|
|
||||||
* bootloader).
|
|
||||||
*/
|
|
||||||
.bss (NOLOAD) : ALIGN(4)
|
.bss (NOLOAD) : ALIGN(4)
|
||||||
{
|
{
|
||||||
. = ALIGN (4);
|
. = ALIGN (4);
|
||||||
_sbss = ABSOLUTE(.);
|
_sbss = ABSOLUTE(.);
|
||||||
*(.bss)
|
*(.bss .bss.*)
|
||||||
*(.bss.*)
|
|
||||||
. = ALIGN (4);
|
. = ALIGN (4);
|
||||||
_ebss = ABSOLUTE(.);
|
_ebss = ABSOLUTE(.);
|
||||||
} >DRAM
|
} >DRAM
|
||||||
|
|
||||||
|
/* Mutable global variables, initialized by the ROM bootloader. */
|
||||||
|
.data : ALIGN(4)
|
||||||
|
{
|
||||||
|
. = ALIGN (4);
|
||||||
|
_sdata = ABSOLUTE(.);
|
||||||
|
*(.data .data.*)
|
||||||
|
*(.dram*)
|
||||||
|
. = ALIGN (4);
|
||||||
|
_edata = ABSOLUTE(.);
|
||||||
|
} >DRAM
|
||||||
|
|
||||||
|
/* Dummy section to skip past stack+bss+data in IRAM (dual-mapped with DRAM). */
|
||||||
|
.iram_dummy (NOLOAD): ALIGN(4)
|
||||||
|
{
|
||||||
|
. += SIZEOF(.stack);
|
||||||
|
. += SIZEOF(.bss);
|
||||||
|
. += SIZEOF(.data);
|
||||||
|
} > IRAM
|
||||||
|
|
||||||
|
/* IRAM segment: boot code, interrupt vectors, and any code that must
|
||||||
|
* run from RAM. Loaded into SRAM by the ROM bootloader.
|
||||||
|
*/
|
||||||
|
.iram : ALIGN(4)
|
||||||
|
{
|
||||||
|
/* Boot entry point and its literals */
|
||||||
|
*(.literal.call_start_cpu0)
|
||||||
|
*(.text.call_start_cpu0)
|
||||||
|
|
||||||
|
/* Xtensa exception/interrupt vector table — must be 0x400-aligned */
|
||||||
|
. = ALIGN(0x400);
|
||||||
|
*(.text.exception_vectors)
|
||||||
|
|
||||||
|
/* Level-1 interrupt handler */
|
||||||
|
*(.literal._handle_level1)
|
||||||
|
*(.text._handle_level1)
|
||||||
|
|
||||||
|
/* WiFi/BLE blob IRAM sections */
|
||||||
|
*(.iram*)
|
||||||
|
*(.wifislprxiram*)
|
||||||
|
*(.wifiextrairam*)
|
||||||
|
*(.wifi0iram*)
|
||||||
|
*(.wifislpiram*)
|
||||||
|
*(.wifirxiram*)
|
||||||
|
*(.wifiorslpiram*)
|
||||||
|
*(.iram1*)
|
||||||
|
*(.coexiram*)
|
||||||
|
|
||||||
|
/* Init code — reclaimed for heap after startup */
|
||||||
|
__init_start = .;
|
||||||
|
*(.init)
|
||||||
|
__init_end = .;
|
||||||
|
. = ALIGN(4);
|
||||||
|
_iram_end = .;
|
||||||
|
} >IRAM
|
||||||
|
|
||||||
|
/* Dummy section to put the IROM segment at the correct flash offset. */
|
||||||
|
.text_dummy (NOLOAD): ALIGN(4)
|
||||||
|
{
|
||||||
|
. += 0x18; /* esp_image_header_t */
|
||||||
|
. += SIZEOF(.rodata) + ((SIZEOF(.rodata) != 0) ? 0x8 : 0); /* DROM segment (optional) */
|
||||||
|
. += SIZEOF(.data) + ((SIZEOF(.data) != 0) ? 0x8 : 0); /* DRAM segment (optional) */
|
||||||
|
. += SIZEOF(.iram) + 0x8; /* IRAM segment */
|
||||||
|
. += 0x8; /* IROM segment header */
|
||||||
|
} > IROM
|
||||||
|
|
||||||
|
/* IROM segment: main code executed from flash via cache. */
|
||||||
|
.text : ALIGN(4)
|
||||||
|
{
|
||||||
|
*(.literal .text)
|
||||||
|
*(.literal.* .text.*)
|
||||||
|
_irom_end = .;
|
||||||
|
} >IROM
|
||||||
|
|
||||||
|
/DISCARD/ :
|
||||||
|
{
|
||||||
|
*(.eh_frame)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* For the garbage collector.
|
/* For the garbage collector.
|
||||||
|
* _heap_start must be after the DRAM shadow of the IRAM section.
|
||||||
|
* IRAM and DRAM share the same physical SRAM1, with addresses offset by
|
||||||
|
* (IRAM_ORIGIN - DRAM_ORIGIN) = 0x6F0000. We use _iram_end (the actual
|
||||||
|
* end of .iram in IRAM space) converted to DRAM to avoid counting any
|
||||||
|
* alignment gaps between .iram_dummy and .iram.
|
||||||
|
* Init code at the end of .iram can be reclaimed for heap.
|
||||||
*/
|
*/
|
||||||
_globals_start = _sdata;
|
_globals_start = _sbss;
|
||||||
_globals_end = _ebss;
|
_globals_end = _edata;
|
||||||
_heap_start = _ebss;
|
_heap_start = _iram_end - 0x6F0000 - (__init_end - __init_start);
|
||||||
_heap_end = ORIGIN(DRAM) + LENGTH(DRAM);
|
_heap_end = ORIGIN(DRAM) + LENGTH(DRAM);
|
||||||
|
|
||||||
_stack_size = 4K;
|
_stack_size = 4K;
|
||||||
|
|||||||
Reference in New Issue
Block a user