Files
tinygo/targets/esp32c6.ld
T
deadprogram 5f66260c3a machine,targets: add minimal esp32c6 implementation
This adds a minimal esp32c6 implementation, currently only
supporting the examples/serial and examples/blinky1 programs.
It does correctly output the expected "Hello, World" via the
serial port, as well as blink the onboard LED.

In addition, it adds support for the PLIC based IRQ handling
as used on the ESP32C6 processor.

Some parts of this code are loosely based on PR #5252 and #5248

Signed-off-by: deadprogram <ron@hybridgroup.com>
2026-06-25 19:30:24 +02:00

191 lines
6.4 KiB
Plaintext

/* Linker script for the ESP32-C6
*
* The ESP32-C6 has a simpler memory layout than the ESP32-C3:
* - It has 512kB of HP-SRAM. Unlike the C3, IRAM and DRAM share the same
* address space at 0x40800000, so there is no need for separate DRAM/IRAM
* regions.
* - It has 16kB of LP-SRAM at 0x50000000 for low-power / RTC use.
* - DROM and IROM are in separate, non-overlapping 8MB address ranges:
* IROM at 0x42000000 and DROM at 0x42800000.
* - The MMU works in pages of 64kB, which means the bottom 16 bits of the
* address in flash and the address in DROM/IROM need to match.
* - Memory in SRAM is loaded at reset by the ROM bootloader.
*
* The firmware image segments are sorted by virtual address (by esp.go):
* 1. .data (SRAM 0x408...) - loaded by ROM bootloader
* 2. .iram (SRAM 0x408...) - loaded by ROM bootloader
* 3. .text (IROM 0x420...) - flash-mapped via MMU
* 4. .rodata (DROM 0x428...) - flash-mapped via MMU
*
* IMPORTANT: SRAM sections (.data, .iram) are defined BEFORE the dummy
* sections so that SIZEOF() references are backward (not forward). lld may
* not correctly resolve forward SIZEOF() references, which would produce
* wrong dummy sizes and corrupt the firmware image.
*/
MEMORY
{
/* HP-SRAM: unified IRAM/DRAM address space (512K).
* Unlike the C3, IRAM and DRAM share the same address space, so we use
* a single memory region to avoid linker overlap errors.
*/
SRAM (rwx) : ORIGIN = 0x40800000, LENGTH = 512K
/* DROM and IROM are in separate, non-overlapping address ranges. */
DROM (r) : ORIGIN = 0x42800000, LENGTH = 8M /* Data bus (read-only, flash-mapped) */
IROM (rx) : ORIGIN = 0x42000000, LENGTH = 8M /* Instruction bus (flash-mapped) */
}
/* The entry point. It is set in the image flashed to the chip, so must be
* defined.
*/
ENTRY(call_start_cpu0)
SECTIONS
{
/* === SRAM sections (loaded by ROM bootloader) === */
/* Put the stack at the bottom of SRAM, 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 = .;
} >SRAM
/* 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(.);
*(.sbss)
*(.bss .bss.*)
. = ALIGN (4);
_ebss = ABSOLUTE(.);
} >SRAM
/* Mutable global variables. This data (in the SRAM segment) is initialized
* by the ROM bootloader.
*/
.data : ALIGN(4)
{
. = ALIGN (4);
_sdata = ABSOLUTE(.);
*(.sdata)
*(.data .data.*)
*(.dram*)
. = ALIGN (4);
_edata = ABSOLUTE(.);
} >SRAM
/* Code that must run from IRAM (not flash-mapped), for example interrupt
* vectors and code that runs before the flash has been mapped.
* Since IRAM and DRAM share the same address space on ESP32-C6, this is
* placed sequentially after .data in the same SRAM region.
*/
.iram : ALIGN(4)
{
*(.init) /* call_start_cpu0 (before flash is mapped) */
*(.iram*) /* code that must run from IRAM */
*(.text.handleInterruptASM) /* must be in SRAM, within JAL range of _vector_table */
. = ALIGN(256);
*(.text.exception_vectors)
. = ALIGN(4);
} > SRAM
/* Heap starts after IRAM. */
. = ALIGN(4);
_heap_start = ABSOLUTE(.);
_heap_end = ORIGIN(SRAM) + LENGTH(SRAM);
/* === IROM sections (flash-mapped code) === */
/* Dummy section to align .text virtual address with its flash offset.
* In the firmware image, .data and .iram (SRAM) come before .text (IROM).
* SIZEOF(.data) and SIZEOF(.iram) are backward references (defined above).
* .data may be empty (esp.go skips empty sections), so use a ternary.
*/
.irom_dummy (NOLOAD) : ALIGN(0x10000)
{
. += 0x18; /* image header */
. += (SIZEOF(.data) > 0) ? (SIZEOF(.data) + 0x8) : 0; /* data segment + header (if non-empty) */
. += SIZEOF(.iram) + 0x8; /* iram segment + header */
. += 0x8; /* text segment header */
} > IROM
/* Code stored in IROM (flash-mapped).
* This is the main program code.
*/
.text : ALIGN(4)
{
*(.text .text.*)
} > IROM
/* === DROM sections (flash-mapped read-only data) === */
/* Dummy section to align .rodata virtual address with its flash offset.
* Segments in the image are sorted by address: SRAM (.data, .iram),
* IROM (.text), then DROM (.rodata). So .rodata comes last, and we must
* account for all preceding segments.
* All SIZEOF() references are backward (sections defined above).
*/
.rodata_dummy (NOLOAD): ALIGN(4)
{
. += 0x18; /* image header (24 bytes) */
. += (SIZEOF(.data) > 0) ? (SIZEOF(.data) + 0x8) : 0; /* data segment + header (if non-empty) */
. += SIZEOF(.iram) + 0x8; /* iram segment + header */
. += SIZEOF(.text) + 0x8; /* text segment + header */
. += 0x8; /* rodata segment header */
} > DROM
/* Constant global variables, stored in DROM. */
.rodata : ALIGN(4)
{
*(.rodata*)
. = ALIGN (4);
} >DROM
/DISCARD/ :
{
*(.eh_frame) /* we don't do exception handling in C */
}
/* For the garbage collector. */
.tinygo_stacksizes (INFO) :
{
*(.tinygo_stacksizes)
}
.tinygo_arm_entries (INFO) :
{
*(.tinygo_arm_entries)
}
}
/* For the GC and scheduler. */
_globals_start = _sbss;
_globals_end = _edata;
/* Default stack size. */
_stack_size = 4K;
/* ROM function addresses for ESP32-C6.
* Source: ESP-IDF components/esp_rom/esp32c6/ld/esp32c6.rom.ld
*/
PROVIDE( ets_delay_us = 0x40000040 );
/* Cache ROM functions */
Cache_Invalidate_ICache_All = 0x4000064c;
Cache_Suspend_ICache = 0x40000698;
Cache_Resume_ICache = 0x4000069c;
Cache_MMU_Init = 0x400006b4;
Cache_MSPI_MMU_Set = 0x400006b8;
/* PHY ROM data */
phy_param_rom = 0x4087fce8;