mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-07-26 06:38:42 +00:00
esp32: implement flash XIP (execute-in-place) support
Add full flash XIP support for ESP32, enabling code and read-only data to execute/load directly from flash via the MMU cache rather than consuming precious SRAM. This increases available RAM from ~328KB to effectively unlimited for code/rodata, while keeping ~121KB for the Go heap. Changes: - src/device/esp/esp32.S: Add MMU initialization in call_start_cpu0 - Call ROM bootloader mmu_init() and cache_flash_mmu_set() to map DROM/IROM - Enable flash cache via ROM Cache_Read_Enable() - Fix tinygo_scanCurrentStack to spill all register windows for GC - targets/esp32-interrupts.S: Add exception diagnostics - targets/esp32.ld: Major linker script restructure for XIP - Add DROM (4MB @ 0x3F400000) and IROM (4MB @ 0x400D0000) regions - Move .rodata to DROM, main .text to IROM (both flash-mapped) - Keep boot code, vectors, and WiFi blob IRAM sections in SRAM0 - Create WiFi arena in SRAM1 pool 7/6 (64KB @ 0x3FFF0000) - Move .bss and heap to SRAM2 (200KB @ 0x3FFAE000), avoiding ROM/MAC regions - Add _drom_flash_addr variable (patched by builder with flash offset) - targets/esp32.json: Add linker wrap flags for malloc/free and WiFi functions Signed-off-by: deadprogram <ron@hybridgroup.com>
This commit is contained in:
+239
-4
@@ -10,6 +10,49 @@
|
||||
.section .text.call_start_cpu0
|
||||
1:
|
||||
.long _stack_top
|
||||
.Lmain_addr:
|
||||
.long main
|
||||
.Lrom_mmu_init:
|
||||
.long 0x400095a4 // mmu_init(int cpu_no)
|
||||
.Lrom_cache_flash_mmu_set:
|
||||
.long 0x400095e0 // cache_flash_mmu_set(cpu, pid, vaddr, paddr, pgsz, pgcnt)
|
||||
.Lrom_Cache_Read_Enable:
|
||||
.long 0x40009a84 // Cache_Read_Enable(int cpu_no)
|
||||
.Lrom_Cache_Read_Disable:
|
||||
.long 0x40009ab8 // Cache_Read_Disable(int cpu_no)
|
||||
.Lrom_Cache_Flush:
|
||||
.long 0x40009a14 // Cache_Flush(int cpu_no)
|
||||
.Lrodata_start:
|
||||
.long _rodata_start
|
||||
.Lrodata_end:
|
||||
.long _rodata_end
|
||||
.Ltext_start:
|
||||
.long _text_start
|
||||
.Ltext_end:
|
||||
.long _text_end
|
||||
.Ldport_pro_cache_ctrl1:
|
||||
.long 0x3FF00044 // DPORT_PRO_CACHE_CTRL1_REG
|
||||
.Ldrom_paddr_ptr:
|
||||
.long _drom_flash_addr // pointer to builder-patched DROM flash offset
|
||||
.Ldrom_vaddr:
|
||||
.long 0x3F400000 // DROM virtual base address
|
||||
.Lirom_vaddr:
|
||||
.long 0x400D0000 // IROM virtual base address
|
||||
.Lmmu_table_base:
|
||||
.long 0x3FF10000 // PRO CPU Flash MMU table
|
||||
.Lrtc_wdt_protect:
|
||||
.long 0x3FF480A4 // RTC_CNTL_WDTWPROTECT_REG
|
||||
.Lrtc_wdt_key:
|
||||
.long 0x50D83AA1 // WDT write-protect key
|
||||
.Lrtc_wdt_config0:
|
||||
.long 0x3FF4808C // RTC_CNTL_WDTCONFIG0_REG
|
||||
.Ltimg0_wdt_protect:
|
||||
.long 0x3FF5F064 // TIMG0_WDTWPROTECT_REG
|
||||
.Ltimg0_wdt_config0:
|
||||
.long 0x3FF5F048 // TIMG0_WDTCONFIG0_REG
|
||||
.Lvector_table:
|
||||
.long _vector_table
|
||||
|
||||
.global call_start_cpu0
|
||||
call_start_cpu0:
|
||||
// We need to set the stack pointer to a different value. This is somewhat
|
||||
@@ -47,11 +90,203 @@ call_start_cpu0:
|
||||
wsr.cpenable a2
|
||||
rsync
|
||||
|
||||
// Jump to the runtime start function written in Go.
|
||||
call4 main
|
||||
// Disable the RTC and TIMG0 watchdogs before configuring the flash cache.
|
||||
// The ROM bootloader leaves them running; a fault during cache setup would
|
||||
// otherwise reset the chip. The Go runtime re-disables them once it starts.
|
||||
l32r a2, .Lrtc_wdt_protect
|
||||
l32r a3, .Lrtc_wdt_key
|
||||
s32i a3, a2, 0 // unlock WDT write-protect
|
||||
memw
|
||||
l32r a2, .Lrtc_wdt_config0
|
||||
movi a3, 0
|
||||
s32i a3, a2, 0 // disable WDT (write 0 to config0)
|
||||
memw
|
||||
|
||||
// Disable TG0 WDT (Timer Group 0 Main Watchdog).
|
||||
// TIMG0_WDTWPROTECT_REG = 0x3FF5F064, TIMG0_WDTCONFIG0_REG = 0x3FF5F048
|
||||
l32r a2, .Ltimg0_wdt_protect
|
||||
l32r a3, .Lrtc_wdt_key // same unlock key 0x50D83AA1
|
||||
s32i a3, a2, 0
|
||||
memw
|
||||
l32r a2, .Ltimg0_wdt_config0
|
||||
movi a3, 0
|
||||
s32i a3, a2, 0
|
||||
memw
|
||||
|
||||
// Set VECBASE to our vector table. Must happen before any callx4 so that
|
||||
// register-window overflow exceptions route to our handlers.
|
||||
l32r a2, .Lvector_table
|
||||
wsr.vecbase a2
|
||||
rsync
|
||||
|
||||
// Clear PS.EXCM so window overflow exceptions work properly.
|
||||
rsr.ps a2
|
||||
movi a3, ~0x1F
|
||||
and a2, a2, a3
|
||||
movi a3, 0x20 // PS.UM = 1
|
||||
or a2, a2, a3
|
||||
wsr.ps a2
|
||||
rsync
|
||||
|
||||
// ---- Configure flash cache and MMU ----
|
||||
// The ROM bootloader only loaded IRAM/DRAM segments. We must now set up
|
||||
// the ICache MMU to map DROM (.rodata) and IROM (.text) from flash.
|
||||
|
||||
// 0. Disable cache and flush before reconfiguring MMU.
|
||||
// The ROM bootloader may leave the cache enabled.
|
||||
movi a6, 0 // cpu_no = 0
|
||||
mov a5, a1
|
||||
l32r a4, .Lrom_Cache_Read_Disable
|
||||
callx4 a4
|
||||
|
||||
movi a6, 0 // cpu_no = 0
|
||||
mov a5, a1
|
||||
l32r a4, .Lrom_Cache_Flush
|
||||
callx4 a4
|
||||
|
||||
// 1. Reset MMU tables for PRO CPU (clear all entries to invalid).
|
||||
movi a6, 0 // cpu_no = 0 (PRO)
|
||||
mov a5, a1
|
||||
l32r a4, .Lrom_mmu_init
|
||||
callx4 a4
|
||||
|
||||
// 2. Map DROM pages via ROM cache_flash_mmu_set:
|
||||
// vaddr=0x3F400000, paddr=0x10000, page_size=64KB, count=ceil(size/64KB)
|
||||
l32r a2, .Lrodata_end
|
||||
l32r a3, .Lrodata_start
|
||||
sub a2, a2, a3 // a2 = rodata size in bytes
|
||||
beqz a2, .Lskip_drom // skip if no rodata
|
||||
addi a2, a2, -1
|
||||
srli a2, a2, 16
|
||||
addi a2, a2, 1 // a2 = drom page count
|
||||
|
||||
// cache_flash_mmu_set(cpu=0, pid=0, vaddr, paddr, psize=64, num)
|
||||
movi a6, 0
|
||||
movi a7, 0
|
||||
l32r a8, .Ldrom_vaddr // 0x3F400000
|
||||
l32r a9, .Ldrom_paddr_ptr
|
||||
l32i a9, a9, 0 // a9 = DROM flash offset (builder-patched)
|
||||
movi a10, 64
|
||||
mov a11, a2
|
||||
mov a5, a1
|
||||
l32r a4, .Lrom_cache_flash_mmu_set
|
||||
callx4 a4
|
||||
.Lskip_drom:
|
||||
|
||||
// 3. Map IROM pages via ROM cache_flash_mmu_set:
|
||||
// vaddr=0x400D0000, paddr=0x10000 + drom_pages*64KB
|
||||
l32r a2, .Ltext_end
|
||||
l32r a3, .Ltext_start
|
||||
sub a2, a2, a3 // a2 = text size in bytes
|
||||
beqz a2, .Lskip_irom // skip if no text
|
||||
addi a2, a2, -1
|
||||
srli a2, a2, 16
|
||||
addi a2, a2, 1 // a2 = irom page count
|
||||
|
||||
// Compute IROM paddr into a3 = DROM flash offset + drom_pages * 64KB
|
||||
l32r a9, .Lrodata_end
|
||||
l32r a3, .Lrodata_start
|
||||
sub a9, a9, a3 // a9 = rodata size
|
||||
l32r a3, .Ldrom_paddr_ptr
|
||||
l32i a3, a3, 0 // a3 = DROM flash offset (builder-patched)
|
||||
beqz a9, .Lirom_paddr_ready
|
||||
addi a9, a9, -1
|
||||
srli a9, a9, 16
|
||||
addi a9, a9, 1 // a9 = drom page count
|
||||
slli a9, a9, 16 // a9 = drom_pages * 64KB
|
||||
add a3, a3, a9 // a3 = drom_flash_addr + drom_pages * 64KB
|
||||
.Lirom_paddr_ready:
|
||||
|
||||
movi a6, 0
|
||||
movi a7, 0
|
||||
l32r a8, .Lirom_vaddr // 0x400D0000
|
||||
mov a9, a3 // irom paddr
|
||||
movi a10, 64
|
||||
mov a11, a2
|
||||
mov a5, a1
|
||||
l32r a4, .Lrom_cache_flash_mmu_set
|
||||
callx4 a4
|
||||
.Lskip_irom:
|
||||
|
||||
// 4. Unmask DROM0 and IRAM0 cache buses in DPORT_PRO_CACHE_CTRL1_REG:
|
||||
// bit 4: PRO_CACHE_MASK_DROM0 (data flash, 0x3F400000)
|
||||
// bit 0: PRO_CACHE_MASK_IRAM0 (instruction flash, 0x400D0000 window)
|
||||
l32r a2, .Ldport_pro_cache_ctrl1
|
||||
l32i a3, a2, 0
|
||||
movi a4, ~0x11 // clear bit 0 (IRAM0) and bit 4 (DROM0)
|
||||
and a3, a3, a4
|
||||
s32i a3, a2, 0
|
||||
memw
|
||||
|
||||
// 5. Enable flash cache for PRO CPU.
|
||||
movi a6, 0 // cpu_no = 0
|
||||
mov a5, a1
|
||||
l32r a4, .Lrom_Cache_Read_Enable
|
||||
callx4 a4
|
||||
|
||||
isync
|
||||
|
||||
// ---- Jump to main (in IROM/flash, now accessible) ----
|
||||
mov a5, a1
|
||||
l32r a4, .Lmain_addr
|
||||
callx4 a4
|
||||
|
||||
// If main returns, loop forever.
|
||||
1: j 1b
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// tinygo_scanCurrentStack — Spill all Xtensa register windows to the
|
||||
// stack, then call tinygo_scanstack(sp) so the conservative GC can
|
||||
// discover live heap pointers that are currently in physical registers.
|
||||
//
|
||||
// On RISC-V / ARM the equivalent function pushes callee-saved registers
|
||||
// before the call. On Xtensa windowed ABI the same effect is achieved
|
||||
// by forcing hardware window-overflow for every occupied pane: each
|
||||
// overflow saves the four registers in that pane to the stack frame
|
||||
// pointed to by the pane's a1 (sp). After all panes are flushed, a
|
||||
// scan from the current sp to stackTop covers every live value.
|
||||
//
|
||||
// Without this spill the conservative GC misses heap pointers held only
|
||||
// in physical registers, frees live objects, and later crashes jumping
|
||||
// through a freed/garbage function pointer (e.g. a goroutine trampoline).
|
||||
// -----------------------------------------------------------------------
|
||||
.section .text.tinygo_scanCurrentStack
|
||||
|
||||
.global tinygo_scanCurrentStack
|
||||
tinygo_scanCurrentStack:
|
||||
// TODO: save callee saved registers on the stack
|
||||
j tinygo_scanstack
|
||||
entry a1, 48
|
||||
|
||||
// Disable interrupts while flushing register windows.
|
||||
rsr a4, PS
|
||||
s32i a4, a1, 0 // save PS for later restore
|
||||
rsil a4, 3 // XCHAL_EXCM_LEVEL
|
||||
|
||||
// Flush all register windows using recursive call4.
|
||||
// For NAREG=64 (16 panes), 15 recursive levels cover all panes
|
||||
// except the current one (which is kept active).
|
||||
movi a6, 15
|
||||
call4 .Lscan_spill
|
||||
|
||||
// Restore interrupts.
|
||||
l32i a4, a1, 0
|
||||
wsr.ps a4
|
||||
rsync
|
||||
|
||||
// Pass current sp to tinygo_scanstack.
|
||||
// call4 maps caller's a5→callee's a1 (stack ptr for callee's entry)
|
||||
// and caller's a6→callee's a2 (first argument = sp).
|
||||
mov a5, a1 // callee's a1 = valid stack pointer
|
||||
mov a6, a1 // callee's a2 = sp argument
|
||||
call4 tinygo_scanstack
|
||||
|
||||
retw
|
||||
|
||||
.balign 4
|
||||
.Lscan_spill:
|
||||
entry a1, 16
|
||||
beqz a2, .Lscan_spill_done
|
||||
addi a2, a2, -1
|
||||
mov a6, a2
|
||||
call4 .Lscan_spill
|
||||
.Lscan_spill_done:
|
||||
retw
|
||||
|
||||
@@ -364,8 +364,75 @@ _handle_level1:
|
||||
rfe
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Exception halt: infinite loop for unhandled exceptions.
|
||||
// Exception halt: dump EXCCAUSE and EPC1 over UART0, then loop forever.
|
||||
// Helps diagnose unhandled CPU exceptions (crashes) which would otherwise
|
||||
// look like a silent freeze (especially with the watchdogs disabled).
|
||||
// -----------------------------------------------------------------------
|
||||
.Lexc_uart_fifo:
|
||||
.long 0x3FF40000 // UART0 FIFO
|
||||
.Lexc_uart_stat:
|
||||
.long 0x3FF4001C // UART0 STATUS (TXFIFO_CNT bits 23:16)
|
||||
.Lexception_halt:
|
||||
// Emit marker "\nEXC " then EXCCAUSE (8 hex), ' ', EPC1 (8 hex), '\n'.
|
||||
l32r a4, .Lexc_uart_fifo
|
||||
l32r a5, .Lexc_uart_stat
|
||||
movi a6, 10 // '\n'
|
||||
call0 .Lexc_putc
|
||||
movi a6, 'E'
|
||||
call0 .Lexc_putc
|
||||
movi a6, 'X'
|
||||
call0 .Lexc_putc
|
||||
movi a6, 'C'
|
||||
call0 .Lexc_putc
|
||||
movi a6, ' '
|
||||
call0 .Lexc_putc
|
||||
rsr a7, EXCCAUSE
|
||||
call0 .Lexc_puthex
|
||||
movi a6, ' '
|
||||
call0 .Lexc_putc
|
||||
rsr a7, EPC1
|
||||
call0 .Lexc_puthex
|
||||
movi a6, ' '
|
||||
call0 .Lexc_putc
|
||||
rsr a7, EXCVADDR
|
||||
call0 .Lexc_puthex
|
||||
movi a6, ' '
|
||||
call0 .Lexc_putc
|
||||
l32i a7, a1, 0 // saved a0 (return address of faulting frame)
|
||||
call0 .Lexc_puthex
|
||||
movi a6, 10 // '\n'
|
||||
call0 .Lexc_putc
|
||||
1:
|
||||
waiti 0
|
||||
j .Lexception_halt
|
||||
j 1b
|
||||
|
||||
// Emit char in a6 (clobbers a3). a4=fifo, a5=status. Returns via a0 (call0).
|
||||
.align 4
|
||||
.Lexc_putc:
|
||||
s32i a6, a4, 0
|
||||
2: l32i a3, a5, 0
|
||||
extui a3, a3, 16, 8
|
||||
bnez a3, 2b
|
||||
ret
|
||||
|
||||
// Emit 32-bit value in a7 as 8 hex chars (clobbers a6,a8,a9,a3).
|
||||
// Uses a10 as return save since call0 to .Lexc_putc clobbers a0.
|
||||
.align 4
|
||||
.Lexc_puthex:
|
||||
mov a10, a0 // save return address
|
||||
movi a9, 28 // shift
|
||||
3:
|
||||
ssr a9
|
||||
srl a8, a7
|
||||
extui a8, a8, 0, 4 // nibble
|
||||
movi a6, 10
|
||||
bge a8, a6, 4f
|
||||
addi a6, a8, '0'
|
||||
j 5f
|
||||
4: addi a6, a8, 'a' - 10
|
||||
5: call0 .Lexc_putc
|
||||
addi a9, a9, -4
|
||||
bgez a9, 3b
|
||||
mov a0, a10 // restore return address
|
||||
ret
|
||||
|
||||
|
||||
@@ -7,6 +7,13 @@
|
||||
"scheduler": "tasks",
|
||||
"serial": "uart",
|
||||
"linker": "ld.lld",
|
||||
"ldflags": [
|
||||
"--wrap=malloc",
|
||||
"--wrap=calloc",
|
||||
"--wrap=free",
|
||||
"--wrap=realloc",
|
||||
"--wrap=ppCheckTxConnTrafficIdle"
|
||||
],
|
||||
"default-stack-size": 8192,
|
||||
"rtlib": "compiler-rt",
|
||||
"libc": "picolibc",
|
||||
|
||||
+181
-74
@@ -1,34 +1,141 @@
|
||||
/* Linker script for the ESP32 */
|
||||
/* Linker script for the ESP32 (flash XIP)
|
||||
*
|
||||
* The ESP32 has 520KB of internal SRAM:
|
||||
* - SRAM0 (192KB): 0x40070000-0x4009FFFF — upper 64KB used by ICache when
|
||||
* flash XIP is active. Usable IRAM: 0x40080000 (128KB).
|
||||
* - SRAM1 (128KB): 0x3FFE0000-0x3FFFFFFF — data only.
|
||||
* - SRAM2 (200KB): 0x3FFAE000-0x3FFDFFFF — data only.
|
||||
*
|
||||
* Flash is memory-mapped via the cache:
|
||||
* - DROM (read-only data): 0x3F400000, up to 4MB (64KB pages via MMU)
|
||||
* - IROM (executable code): 0x400D0000, up to 4MB (64KB pages via MMU)
|
||||
* The startup code in IRAM configures the MMU to map flash pages.
|
||||
* The TinyGo builder places DROM/IROM content at page-aligned flash offsets.
|
||||
*/
|
||||
|
||||
MEMORY
|
||||
{
|
||||
/* Data RAM. Allows byte access.
|
||||
* There are various data RAM regions:
|
||||
* SRAM2: 0x3FFA_E000..0x3FFD_FFFF (72 + 128 = 200K)
|
||||
* SRAM1: 0x3FFE_0000..0x3FFF_FFFF (128K)
|
||||
* This gives us 328K of contiguous RAM, which is the largest span possible.
|
||||
* SRAM1 has other addresses as well but the datasheet seems to indicate
|
||||
* these are aliases.
|
||||
*/
|
||||
DRAM (rw) : ORIGIN = 0x3FFAE000, LENGTH = 200K + 128K /* Internal SRAM 1 + 2 */
|
||||
/* SRAM2 (0x3FFAE000-0x3FFE0000, 200K): a single safe contiguous block used
|
||||
* for the stack, initialized .data, .bss, and the whole Go GC heap. */
|
||||
DRAM (rw) : ORIGIN = 0x3FFAE000, LENGTH = 200K
|
||||
|
||||
/* Instruction RAM. */
|
||||
IRAM (x) : ORIGIN = 0x40080000, LENGTH = 128K /* Internal SRAM 0 */
|
||||
/* Top of SRAM1 (0x3FFF0000-0x40000000, 64K): pool 7 + pool 6. This is the
|
||||
* ONLY part of SRAM1 that is safe for us to use:
|
||||
* - 0x3FFE0000-0x3FFE0440 : ROM PRO data (ROM writes it) — avoid
|
||||
* - 0x3FFE0440-0x3FFE8000 : ROM boot-stack area — NOT free during WiFi;
|
||||
* putting the arena here corrupts DMA and breaks
|
||||
* association ("auth expired") — avoid
|
||||
* - 0x3FFE8000-0x3FFF0000 : pool 8, "used for MAC dump" by the WiFi MAC —
|
||||
* corrupts anything placed here — avoid
|
||||
* - 0x3FFF0000-0x40000000 : pool 7/6 — safe (verified: arena + .bss both
|
||||
* work here). Used for the WiFi arena.
|
||||
* The arena (DMA buffers) owns this whole 64K block; .bss stays in SRAM2. */
|
||||
DRAM1 (rw) : ORIGIN = 0x3FFF0000, LENGTH = 64K
|
||||
|
||||
IRAM (x) : ORIGIN = 0x40080000, LENGTH = 128K /* SRAM0 (usable portion) */
|
||||
|
||||
DROM (r) : ORIGIN = 0x3F400000, LENGTH = 4M /* Flash data bus (page-aligned) */
|
||||
IROM (rx) : ORIGIN = 0x400D0000, LENGTH = 4M /* Flash instruction bus (page-aligned) */
|
||||
}
|
||||
|
||||
/* The entry point. It is set in the image flashed to the chip, so must be
|
||||
* defined.
|
||||
*/
|
||||
ENTRY(call_start_cpu0)
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
/* Constant literals and code. Loaded into IRAM for now. Eventually, most
|
||||
* code should be executed directly from flash.
|
||||
* Note that literals must be before code for the l32r instruction to work.
|
||||
/* Constant global variables, stored in flash (DROM).
|
||||
* The builder places this at a page-aligned flash offset.
|
||||
*/
|
||||
.text : ALIGN(4)
|
||||
.rodata : ALIGN(4)
|
||||
{
|
||||
_rodata_start = ABSOLUTE(.);
|
||||
*(.rodata*)
|
||||
. = ALIGN (4);
|
||||
_rodata_end = ABSOLUTE(.);
|
||||
} >DROM
|
||||
|
||||
/* Put the stack at the bottom of DRAM, so that the application will
|
||||
* crash on stack overflow instead of silently corrupting memory.
|
||||
*/
|
||||
.stack (NOLOAD) :
|
||||
{
|
||||
. = ALIGN(16);
|
||||
. += _stack_size;
|
||||
_stack_top = .;
|
||||
} >DRAM
|
||||
|
||||
/* Global variables that are mutable and zero-initialized. Kept in SRAM2
|
||||
* (DRAM): the WiFi blob's .bss holds structures the MAC/DMA touches, and
|
||||
* SRAM1 has no room for both .bss and an adequately-sized arena in its only
|
||||
* safe region (pool 7/6, 64K) — so .bss stays in SRAM2 and the arena owns
|
||||
* the SRAM1 block. */
|
||||
.bss (NOLOAD) : ALIGN(4)
|
||||
{
|
||||
. = ALIGN (4);
|
||||
_sbss = ABSOLUTE(.);
|
||||
*(.bss .bss.*)
|
||||
. = ALIGN (4);
|
||||
_ebss = ABSOLUTE(.);
|
||||
} >DRAM
|
||||
|
||||
/* Reserved zero-initialized storage for WiFi blob global variables.
|
||||
* On ESP32-S3 these live at fixed ROM DRAM addresses; on ESP32 the blob
|
||||
* doesn't export them so we allocate space here. */
|
||||
.wifi_bss (NOLOAD) : ALIGN(4)
|
||||
{
|
||||
_wifi_bss_start = ABSOLUTE(.);
|
||||
/* Function pointers patched by netif.c (6 × 4 bytes) */
|
||||
PROVIDE(g_config_func = ABSOLUTE(.)); . += 4;
|
||||
PROVIDE(g_net80211_tx_func = ABSOLUTE(.)); . += 4;
|
||||
PROVIDE(g_timer_func = ABSOLUTE(.)); . += 4;
|
||||
PROVIDE(s_michael_mic_failure_cb = ABSOLUTE(.)); . += 4;
|
||||
PROVIDE(g_tx_done_cb_func = ABSOLUTE(.)); . += 4;
|
||||
PROVIDE(s_encap_amsdu_func = ABSOLUTE(.)); . += 4;
|
||||
/* Pointer-sized globals used by pp/lmac (3 × 4 bytes) */
|
||||
PROVIDE(pp_wdev_funcs = ABSOLUTE(.)); . += 4;
|
||||
PROVIDE(our_tx_eb = ABSOLUTE(.)); . += 4;
|
||||
PROVIDE(our_wait_eb = ABSOLUTE(.)); . += 4;
|
||||
PROVIDE(lmacConfMib_ptr = ABSOLUTE(.)); . += 4;
|
||||
. = ALIGN(4);
|
||||
_wifi_bss_end = ABSOLUTE(.);
|
||||
} >DRAM
|
||||
|
||||
/* WiFi blob arena (DMA buffers): owns all of DRAM1 (SRAM1 pool 7/6,
|
||||
* 0x3FFF0000-0x40000000, 64K) — the only SRAM1 region safe from ROM/MAC
|
||||
* writes. Outside the Go GC heap; DMA-capable; isolated from GC objects. */
|
||||
.arena (NOLOAD) : ALIGN(16)
|
||||
{
|
||||
_arena_start = ABSOLUTE(.);
|
||||
} >DRAM1
|
||||
_arena_end = ORIGIN(DRAM1) + LENGTH(DRAM1);
|
||||
ASSERT((_arena_end - _arena_start) >= 32K, "arena region < 32K")
|
||||
|
||||
/* Mutable global variables, initialized by the ROM bootloader. */
|
||||
.data : ALIGN(4)
|
||||
{
|
||||
. = ALIGN (4);
|
||||
_sdata = ABSOLUTE(.);
|
||||
*(.data .data.*)
|
||||
*(.dram*)
|
||||
. = ALIGN (4);
|
||||
/* DROM flash physical address, patched by the TinyGo ESP image builder
|
||||
* with the flash offset where the .rodata (DROM) segment was placed.
|
||||
* The startup code reads this to program the flash cache MMU. */
|
||||
_drom_flash_addr = ABSOLUTE(.);
|
||||
LONG(0x10000);
|
||||
. = ALIGN (4);
|
||||
_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.
|
||||
*/
|
||||
ASSERT(_edata < 0x3ffe1320, "the .data section overlaps with the stack used by the boot ROM, possibly causing corruption at startup")
|
||||
|
||||
/* IRAM segment: boot code, interrupt vectors, and WiFi/BLE blob code
|
||||
* that must run from RAM. */
|
||||
.iram : ALIGN(4)
|
||||
{
|
||||
/* Boot entry point and its literals */
|
||||
*(.literal.call_start_cpu0)
|
||||
*(.text.call_start_cpu0)
|
||||
|
||||
@@ -40,69 +147,49 @@ SECTIONS
|
||||
*(.literal._handle_level1)
|
||||
*(.text._handle_level1)
|
||||
|
||||
*(.literal .text)
|
||||
*(.literal.* .text.*)
|
||||
/* WiFi/BLE blob IRAM sections */
|
||||
*(.iram*)
|
||||
*(.wifislprxiram*)
|
||||
*(.wifiextrairam*)
|
||||
*(.wifi0iram*)
|
||||
*(.wifislpiram*)
|
||||
*(.wifirxiram*)
|
||||
*(.wifiorslpiram*)
|
||||
*(.iram1*)
|
||||
*(.coexiram*)
|
||||
|
||||
. = ALIGN(4);
|
||||
_iram_end = .;
|
||||
} >IRAM
|
||||
|
||||
/* Put the stack at the bottom of DRAM, so that the application will
|
||||
* crash on stack overflow instead of silently corrupting memory.
|
||||
* See: http://blog.japaric.io/stack-overflow-protection/ */
|
||||
.stack (NOLOAD) :
|
||||
{
|
||||
. = ALIGN(16);
|
||||
. += _stack_size;
|
||||
_stack_top = .;
|
||||
} >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).
|
||||
/* IROM segment: main code executed from flash via MMU cache.
|
||||
* The builder places this at a page-aligned flash offset.
|
||||
*/
|
||||
.rodata : ALIGN(4)
|
||||
.text : ALIGN(4)
|
||||
{
|
||||
*(.rodata)
|
||||
*(.rodata.*)
|
||||
} >DRAM
|
||||
_text_start = ABSOLUTE(.);
|
||||
*(.literal .text)
|
||||
*(.literal.* .text.*)
|
||||
*(.phyiram*)
|
||||
. = ALIGN(4);
|
||||
_text_end = ABSOLUTE(.);
|
||||
} >IROM
|
||||
|
||||
/* Mutable global variables.
|
||||
*/
|
||||
.data : ALIGN(4)
|
||||
/DISCARD/ :
|
||||
{
|
||||
_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)
|
||||
{
|
||||
. = ALIGN (4);
|
||||
_sbss = ABSOLUTE(.);
|
||||
*(.bss)
|
||||
*(.bss.*)
|
||||
. = ALIGN (4);
|
||||
_ebss = ABSOLUTE(.);
|
||||
} >DRAM
|
||||
*(.eh_frame)
|
||||
}
|
||||
}
|
||||
|
||||
/* For the garbage collector.
|
||||
*/
|
||||
_globals_start = _sdata;
|
||||
_globals_end = _ebss;
|
||||
_heap_start = _ebss;
|
||||
_heap_end = ORIGIN(DRAM) + LENGTH(DRAM);
|
||||
/* For the garbage collector. */
|
||||
_globals_start = _sbss;
|
||||
_globals_end = _wifi_bss_end;
|
||||
_heap_start = _edata;
|
||||
/* Go GC heap = the rest of SRAM2 above .bss (~121KB), a single safe contiguous
|
||||
* block. The WiFi arena lives in DRAM1 (SRAM1 pool 7/6), so its ~64KB does not
|
||||
* consume Go heap and its DMA is isolated from GC objects. The heap never
|
||||
* touches SRAM1's ROM/MAC-dump regions (0x3FFE0000-0x3FFF0000, left unused). */
|
||||
_heap_end = 0x3FFE0000;
|
||||
|
||||
_stack_size = 4K;
|
||||
|
||||
@@ -209,3 +296,23 @@ __umodsi3 = 0x4000c7d0;
|
||||
__umulsidi3 = 0x4000c7d8;
|
||||
__unorddf2 = 0x400637f4;
|
||||
__unordsf2 = 0x40063478;
|
||||
|
||||
/* From ESP-IDF:
|
||||
* components/esp_rom/esp32/ld/esp32.rom.ld
|
||||
* ROM functions needed for WiFi/PHY support.
|
||||
*/
|
||||
PROVIDE(ets_delay_us = 0x40008534);
|
||||
PROVIDE(ets_printf = 0x40007d54);
|
||||
PROVIDE(ets_install_putc1 = 0x40007d18);
|
||||
PROVIDE(ets_install_uart_printf = 0x40007d28);
|
||||
PROVIDE(intr_matrix_set = 0x4000681c);
|
||||
PROVIDE(rtc_get_reset_reason = 0x400081d4);
|
||||
PROVIDE(phy_get_romfuncs = 0x40004100);
|
||||
PROVIDE(uart_tx_one_char = 0x40009200);
|
||||
PROVIDE(uart_rx_one_char = 0x400092d0);
|
||||
PROVIDE(ets_intr_lock = 0x400067b0);
|
||||
PROVIDE(ets_intr_unlock = 0x400067c4);
|
||||
PROVIDE(ets_isr_attach = 0x400067ec);
|
||||
PROVIDE(ets_isr_mask = 0x400067fc);
|
||||
PROVIDE(ets_isr_unmask = 0x40006808);
|
||||
PROVIDE(roundup2 = 0x4000ab7c);
|
||||
|
||||
Reference in New Issue
Block a user