diff --git a/pico_sdk_version.cmake b/pico_sdk_version.cmake index aca3c585e..59b8603f3 100644 --- a/pico_sdk_version.cmake +++ b/pico_sdk_version.cmake @@ -10,7 +10,7 @@ if (NOT DEFINED PICO_SDK_VERSION_MAJOR) set(PICO_SDK_VERSION_REVISION 1) # PICO_BUILD_DEFINE: PICO_SDK_VERSION_PRE_RELEASE_ID, Optional SDK pre-release version identifier, default=Current SDK pre-release identifier, type=string, group=pico_base # PICO_CMAKE_CONFIG: PICO_SDK_VERSION_PRE_RELEASE_ID, Optional SDK pre-release version identifier, default=Current SDK pre-release identifier, type=string, group=pico_base - set(PICO_SDK_VERSION_PRE_RELEASE_ID develop) + set(PICO_SDK_VERSION_PRE_RELEASE_ID xip-sram) endif() # PICO_BUILD_DEFINE: PICO_SDK_VERSION_STRING, SDK version string, type=string, default=Current SDK version string, group=pico_base diff --git a/src/rp2_common/hardware_flash/flash.c b/src/rp2_common/hardware_flash/flash.c index fbca8de2c..5526db827 100644 --- a/src/rp2_common/hardware_flash/flash.c +++ b/src/rp2_common/hardware_flash/flash.c @@ -10,6 +10,7 @@ #if PICO_RP2040 #include "hardware/structs/io_qspi.h" #include "hardware/structs/ssi.h" +#include "hardware/structs/xip.h" #else #include "hardware/structs/qmi.h" #include "hardware/regs/otp_data.h" @@ -127,7 +128,9 @@ static void __no_inline_not_in_flash_func(flash_rp2350_restore_qmi_cs1)(const fl typedef struct flash_hardware_save_state { -#if !PICO_RP2040 +#if PICO_RP2040 + uint32_t xip_ctrl; +#else flash_rp2350_qmi_save_state_t qmi_save; #endif uint32_t qspi_pads[count_of(pads_qspi_hw->io)]; @@ -139,7 +142,9 @@ static void __no_inline_not_in_flash_func(flash_save_hardware_state)(flash_hardw for (size_t i = 0; i < count_of(pads_qspi_hw->io); ++i) { state->qspi_pads[i] = pads_qspi_hw->io[i]; } -#if !PICO_RP2040 +#if PICO_RP2040 + state->xip_ctrl = xip_ctrl_hw->ctrl; +#else flash_rp2350_save_qmi_cs1(&state->qmi_save); #endif } @@ -148,7 +153,9 @@ static void __no_inline_not_in_flash_func(flash_restore_hardware_state)(flash_ha for (size_t i = 0; i < count_of(pads_qspi_hw->io); ++i) { pads_qspi_hw->io[i] = state->qspi_pads[i]; } -#if !PICO_RP2040 +#if PICO_RP2040 + xip_ctrl_hw->ctrl = state->xip_ctrl; +#else // Tail call! flash_rp2350_restore_qmi_cs1(&state->qmi_save); #endif diff --git a/src/rp2_common/hardware_sync_spin_lock/include/hardware/sync/spin_lock.h b/src/rp2_common/hardware_sync_spin_lock/include/hardware/sync/spin_lock.h index 2be493d6f..83ba1e6d8 100644 --- a/src/rp2_common/hardware_sync_spin_lock/include/hardware/sync/spin_lock.h +++ b/src/rp2_common/hardware_sync_spin_lock/include/hardware/sync/spin_lock.h @@ -10,7 +10,7 @@ #include "pico.h" #include "hardware/sync.h" -// PICO_CONFIG: PICO_USE_SW_SPIN_LOCKS, Use software implementation for spin locks, type=bool, default=1 on RP2350 due to errata, group=hardware_sync +// PICO_CONFIG: PICO_USE_SW_SPIN_LOCKS, Use software implementation for spin locks, type=bool, default=1 on RP2350 due to errata E2, group=hardware_sync #ifndef PICO_USE_SW_SPIN_LOCKS #if PICO_RP2350 #define PICO_USE_SW_SPIN_LOCKS 1 @@ -19,53 +19,93 @@ // PICO_CONFIG: PICO_SPINLOCK_ID_IRQ, Spinlock ID for IRQ protection, min=0, max=31, default=9, group=hardware_sync #ifndef PICO_SPINLOCK_ID_IRQ +#if PICO_RP2350 && !PICO_USE_SW_SPIN_LOCKS +#define PICO_SPINLOCK_ID_IRQ 5 +#else #define PICO_SPINLOCK_ID_IRQ 9 #endif +#endif // PICO_CONFIG: PICO_SPINLOCK_ID_TIMER, Spinlock ID for Timer protection, min=0, max=31, default=10, group=hardware_sync #ifndef PICO_SPINLOCK_ID_TIMER +#if PICO_RP2350 && !PICO_USE_SW_SPIN_LOCKS +#define PICO_SPINLOCK_ID_TIMER 6 +#else #define PICO_SPINLOCK_ID_TIMER 10 #endif +#endif // PICO_CONFIG: PICO_SPINLOCK_ID_HARDWARE_CLAIM, Spinlock ID for Hardware claim protection, min=0, max=31, default=11, group=hardware_sync #ifndef PICO_SPINLOCK_ID_HARDWARE_CLAIM +#if PICO_RP2350 && !PICO_USE_SW_SPIN_LOCKS +#define PICO_SPINLOCK_ID_HARDWARE_CLAIM 7 +#else #define PICO_SPINLOCK_ID_HARDWARE_CLAIM 11 #endif +#endif // PICO_CONFIG: PICO_SPINLOCK_ID_RAND, Spinlock ID for Random Number Generator, min=0, max=31, default=12, group=hardware_sync #ifndef PICO_SPINLOCK_ID_RAND +#if PICO_RP2350 && !PICO_USE_SW_SPIN_LOCKS +#define PICO_SPINLOCK_ID_RAND 10 +#else #define PICO_SPINLOCK_ID_RAND 12 #endif +#endif // PICO_CONFIG: PICO_SPINLOCK_ID_ATOMIC, Spinlock ID for atomics, min=0, max=31, default=13, group=hardware_sync #ifndef PICO_SPINLOCK_ID_ATOMIC +#if PICO_RP2350 && !PICO_USE_SW_SPIN_LOCKS +#define PICO_SPINLOCK_ID_ATOMIC 11 +#else #define PICO_SPINLOCK_ID_ATOMIC 13 #endif +#endif // PICO_CONFIG: PICO_SPINLOCK_ID_OS1, First Spinlock ID reserved for use by low level OS style software, min=0, max=31, default=14, group=hardware_sync #ifndef PICO_SPINLOCK_ID_OS1 +#if PICO_RP2350 && !PICO_USE_SW_SPIN_LOCKS +#define PICO_SPINLOCK_ID_OS1 18 +#else #define PICO_SPINLOCK_ID_OS1 14 #endif +#endif // PICO_CONFIG: PICO_SPINLOCK_ID_OS2, Second Spinlock ID reserved for use by low level OS style software, min=0, max=31, default=15, group=hardware_sync #ifndef PICO_SPINLOCK_ID_OS2 +#if PICO_RP2350 && !PICO_USE_SW_SPIN_LOCKS +#define PICO_SPINLOCK_ID_OS2 19 +#else #define PICO_SPINLOCK_ID_OS2 15 #endif +#endif // PICO_CONFIG: PICO_SPINLOCK_ID_STRIPED_FIRST, Lowest Spinlock ID in the 'striped' range, min=0, max=31, default=16, group=hardware_sync #ifndef PICO_SPINLOCK_ID_STRIPED_FIRST +#if PICO_RP2350 && !PICO_USE_SW_SPIN_LOCKS +#define PICO_SPINLOCK_ID_STRIPED_FIRST 20 +#else #define PICO_SPINLOCK_ID_STRIPED_FIRST 16 #endif +#endif // PICO_CONFIG: PICO_SPINLOCK_ID_STRIPED_LAST, Highest Spinlock ID in the 'striped' range, min=0, max=31, default=23, group=hardware_sync #ifndef PICO_SPINLOCK_ID_STRIPED_LAST +#if PICO_RP2350 && !PICO_USE_SW_SPIN_LOCKS +#define PICO_SPINLOCK_ID_STRIPED_LAST 25 +#else #define PICO_SPINLOCK_ID_STRIPED_LAST 23 #endif +#endif // PICO_CONFIG: PICO_SPINLOCK_ID_CLAIM_FREE_FIRST, Lowest Spinlock ID in the 'claim free' range, min=0, max=31, default=24, group=hardware_sync #ifndef PICO_SPINLOCK_ID_CLAIM_FREE_FIRST +#if PICO_RP2350 && !PICO_USE_SW_SPIN_LOCKS +#define PICO_SPINLOCK_ID_CLAIM_FREE_FIRST 26 +#else #define PICO_SPINLOCK_ID_CLAIM_FREE_FIRST 24 #endif +#endif #ifdef PICO_SPINLOCK_ID_CLAIM_FREE_END #warning PICO_SPINLOCK_ID_CLAIM_FREE_END has been renamed to PICO_SPINLOCK_ID_CLAIM_FREE_LAST diff --git a/src/rp2_common/pico_crt0/crt0.S b/src/rp2_common/pico_crt0/crt0.S index ea3b99a5a..38b717dfe 100644 --- a/src/rp2_common/pico_crt0/crt0.S +++ b/src/rp2_common/pico_crt0/crt0.S @@ -11,6 +11,7 @@ #include "hardware/regs/addressmap.h" #include "hardware/regs/sio.h" +#include "hardware/regs/xip.h" #include "pico/binary_info/defs.h" #include "boot/picobin.h" #include "pico/bootrom.h" @@ -20,6 +21,11 @@ #define PICO_CRT0_NEAR_CALLS 0 #endif +// PICO_CONFIG: PICO_CRT0_NO_DATA_COPY, Whether crt0 should perform the data copies - usually copying from flash into sram, default=1 for no_flash builds, 0 otherwise, type=bool, group=pico_crt0 +#ifndef PICO_CRT0_NO_DATA_COPY +#define PICO_CRT0_NO_DATA_COPY PICO_NO_FLASH +#endif + #ifdef NDEBUG #ifndef COLLAPSE_IRQS #define COLLAPSE_IRQS @@ -457,6 +463,14 @@ hold_non_core0_in_bootrom: b _enter_vtable_in_r0 1: +#if PICO_RP2040 && PICO_USE_XIP_CACHE_AS_RAM +_disable_xip_cache: + // Disable the XIP cache on RP2040 making its SRAM available for use + ldr r0, =(REG_ALIAS_CLR_BITS + XIP_CTRL_BASE + XIP_CTRL_OFFSET) + movs r1, #XIP_CTRL_EN_BITS + str r1, [r0] +#endif + #if !PICO_RP2040 && PICO_EMBED_XIP_SETUP && !PICO_NO_FLASH // Execute boot2 on the core 0 stack (it also gets copied into BOOTRAM due // to inclusion in the data copy table below). Note the reference @@ -477,7 +491,7 @@ _call_xip_setup: // In a NO_FLASH binary, don't perform .data etc copy, since it's loaded // in-place by the SRAM load. Still need to clear .bss -#if !PICO_NO_FLASH +#if !PICO_CRT0_NO_DATA_COPY adr r4, data_cpy_table // assume there is at least one entry @@ -522,11 +536,21 @@ platform_entry: // symbol for stack traces b 1b -#if !PICO_NO_FLASH +#if !PICO_CRT0_NO_DATA_COPY +#if PICO_NO_FLASH +data_cpy: + // skip copies with same source and destination + cmp r1, r2 + bne data_cpy_start + bx lr +#else + // go straight into the copy + #define data_cpy_start data_cpy +#endif data_cpy_loop: ldm r1!, {r0} stm r2!, {r0} -data_cpy: +data_cpy_start: cmp r2, r3 blo data_cpy_loop bx lr @@ -560,6 +584,10 @@ data_cpy_table: .word __scratch_y_start__ .word __scratch_y_end__ +#ifdef PICO_DATA_COPY_EXTRA_SECTIONS_FILE +#include PICO_DATA_COPY_EXTRA_SECTIONS_FILE +#endif + .word 0 // null terminator // ---------------------------------------------------------------------------- diff --git a/src/rp2_common/pico_crt0/crt0_riscv.S b/src/rp2_common/pico_crt0/crt0_riscv.S index e9fc7d94c..80278dff1 100644 --- a/src/rp2_common/pico_crt0/crt0_riscv.S +++ b/src/rp2_common/pico_crt0/crt0_riscv.S @@ -30,6 +30,10 @@ #define PICO_CRT0_INCLUDE_PICOBIN_END_BLOCK (PICO_CRT0_INCLUDE_PICOBIN_BLOCK && !PICO_NO_FLASH) #endif +#ifndef PICO_CRT0_NO_DATA_COPY +#define PICO_CRT0_NO_DATA_COPY PICO_NO_FLASH +#endif + // If vectors are in RAM, we put them in the .data section, so that they are // preloaded by _reset_handler (assuming this is not a loaded-in-place // binary). @@ -340,7 +344,7 @@ _call_xip_setup: // In a NO_FLASH binary, don't perform .data etc copy, since it's loaded // in-place by the SRAM load. Still need to clear .bss -#if !PICO_NO_FLASH +#if !PICO_CRT0_NO_DATA_COPY la a4, data_cpy_table // assume there is at least one entry @@ -379,14 +383,25 @@ platform_entry: // symbol for stack traces ebreak j 1b +#if !PICO_CRT0_NO_DATA_COPY +#if PICO_NO_FLASH +data_cpy: + // skip copies with same source and destination + bne a0, a1, data_cpy_start + ret +#else + // go straight into the copy + #define data_cpy_start data_cpy +#endif data_cpy_loop: lw a0, (a1) sw a0, (a2) addi a1, a1, 4 addi a2, a2, 4 -data_cpy: +data_cpy_start: bltu a2, a3, data_cpy_loop ret +#endif .align 2 data_cpy_table: @@ -412,6 +427,10 @@ data_cpy_table: .word __scratch_y_start__ .word __scratch_y_end__ +#ifdef PICO_DATA_COPY_EXTRA_SECTIONS_FILE +#include PICO_DATA_COPY_EXTRA_SECTIONS_FILE +#endif + .word 0 // null terminator // ---------------------------------------------------------------------------- diff --git a/src/rp2_common/pico_crt0/embedded_start_block.inc.S b/src/rp2_common/pico_crt0/embedded_start_block.inc.S index 4618a3a94..3b36553dc 100644 --- a/src/rp2_common/pico_crt0/embedded_start_block.inc.S +++ b/src/rp2_common/pico_crt0/embedded_start_block.inc.S @@ -12,6 +12,10 @@ #endif #endif +#ifndef PICO_CRT0_PIN_XIP_SRAM +#define PICO_CRT0_PIN_XIP_SRAM PICO_USE_XIP_CACHE_AS_RAM +#endif + #ifndef PICO_CRT0_INCLUDE_PICOBIN_ENTRY_POINT_ITEM // On RISC-V the default entry point from bootrom is the start of the binary, but // we have our vtable at the start, so we must include an entry point @@ -104,6 +108,16 @@ embedded_block: .word __vectors #endif +#if PICO_CRT0_PIN_XIP_SRAM +.byte PICOBIN_BLOCK_ITEM_LOAD_MAP +.byte 0x04 // word size +.byte 0 // pad +.byte 0x01 // number of entries +.word 0 // clear +.word XIP_SRAM_BASE +.word 0 // size +#endif + .byte PICOBIN_BLOCK_ITEM_2BS_LAST .hword (embedded_block_end - embedded_block - 16 ) / 4 // total size of all .byte 0 diff --git a/src/rp2_common/pico_crt0/rp2040/memmap_blocked_ram.ld b/src/rp2_common/pico_crt0/rp2040/memmap_blocked_ram.ld index 6f5000566..79e91a49f 100644 --- a/src/rp2_common/pico_crt0/rp2040/memmap_blocked_ram.ld +++ b/src/rp2_common/pico_crt0/rp2040/memmap_blocked_ram.ld @@ -27,6 +27,7 @@ MEMORY RAM(rwx) : ORIGIN = 0x21000000, LENGTH = 256k SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k + } ENTRY(_entry_point) @@ -149,6 +150,8 @@ SECTIONS *(.uninitialized_data*) } > RAM + + .data : { __data_start__ = .; *(vtable) diff --git a/src/rp2_common/pico_crt0/rp2040/memmap_copy_flash_sram.template.ld b/src/rp2_common/pico_crt0/rp2040/memmap_copy_flash_sram.template.ld new file mode 100644 index 000000000..a3385c43a --- /dev/null +++ b/src/rp2_common/pico_crt0/rp2040/memmap_copy_flash_sram.template.ld @@ -0,0 +1,290 @@ +/* Based on GCC ARM embedded samples. + Defines the following symbols for use by code: + __exidx_start + __exidx_end + __etext + __data_start__ + __preinit_array_start + __preinit_array_end + __init_array_start + __init_array_end + __fini_array_start + __fini_array_end + __data_end__ + __bss_start__ + __bss_end__ + __end__ + end + __HeapLimit + __StackLimit + __StackTop + __stack (== StackTop) +*/ + +MEMORY +{ + @FLASH_REGION@ + RAM(rwx) : ORIGIN = @RAM_ORIGIN@, LENGTH = @RAM_LENGTH@ + SCRATCH_X(rwx) : ORIGIN = @SCRATCH_X_ORIGIN@, LENGTH = @SCRATCH_X_LENGTH@ + SCRATCH_Y(rwx) : ORIGIN = @SCRATCH_Y_ORIGIN@, LENGTH = @SCRATCH_Y_LENGTH@ +@ADDITIONAL_MEMORY@ +} + +ENTRY(_entry_point) + +SECTIONS +{ + /* Second stage bootloader is prepended to the image. It must be 256 bytes big + and checksummed. It is usually built by the boot_stage2 target + in the Raspberry Pi Pico SDK + */ + + .flash_begin : { + __flash_binary_start = .; + } > FLASH + + .boot2 : { + __boot2_start__ = .; + KEEP (*(.boot2)) + __boot2_end__ = .; + } > FLASH + + ASSERT(__boot2_end__ - __boot2_start__ == 256, + "ERROR: Pico second stage bootloader must be 256 bytes in size") + + /* The second stage will always enter the image at the start of .text. + The debugger will use the ELF entry point, which is the _entry_point + symbol if present, otherwise defaults to start of .text. + This can be used to transfer control back to the bootrom on debugger + launches only, to perform proper flash setup. + */ + + .flashtext : { + __logical_binary_start = .; + KEEP (*(.vectors)) + KEEP (*(.binary_info_header)) + __binary_info_header_end = .; + KEEP (*(.embedded_block)) + __embedded_block_end = .; + KEEP (*(.reset)) + } + + .rodata : { + /* segments not marked as .flashdata are instead pulled into .data (in RAM) to avoid accidental flash accesses */ + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*))) + . = ALIGN(4); + } > FLASH + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > FLASH + + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > FLASH + __exidx_end = .; + + /* Machine inspectable binary information */ + . = ALIGN(4); + __binary_info_start = .; + .binary_info : + { + KEEP(*(.binary_info.keep.*)) + *(.binary_info.*) + } > FLASH + __binary_info_end = .; + . = ALIGN(4); + + /* Vector table goes first in RAM, to avoid large alignment hole */ + .ram_vector_table (NOLOAD): { + *(.ram_vector_table) + } > RAM + + .uninitialized_data (NOLOAD): { + . = ALIGN(4); + *(.uninitialized_data*) + } > RAM + + .text : { + __ram_text_start__ = .; + *(.init) + *(.text*) + *(.fini) + /* Pull all c'tors into .text */ + *crtbegin.o(.ctors) + *crtbegin?.o(.ctors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) + *(SORT(.ctors.*)) + *(.ctors) + /* Followed by destructors */ + *crtbegin.o(.dtors) + *crtbegin?.o(.dtors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) + *(SORT(.dtors.*)) + *(.dtors) + + *(.eh_frame*) + . = ALIGN(4); + __ram_text_end__ = .; + } > RAM AT> FLASH + __ram_text_source__ = LOADADDR(.text); + . = ALIGN(4); + +@ADDITIONAL_PRE_DATA@ + + .data : { + __data_start__ = .; + *(vtable) + + *(.time_critical*) + + . = ALIGN(4); + *(.rodata*) + . = ALIGN(4); + + *(.data*) + + . = ALIGN(4); + *(.after_data.*) + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__mutex_array_start = .); + KEEP(*(SORT(.mutex_array.*))) + KEEP(*(.mutex_array)) + PROVIDE_HIDDEN (__mutex_array_end = .); + + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(SORT(.preinit_array.*))) + KEEP(*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + + . = ALIGN(4); + /* init data */ + PROVIDE_HIDDEN (__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + + . = ALIGN(4); + /* finit data */ + PROVIDE_HIDDEN (__fini_array_start = .); + *(SORT(.fini_array.*)) + *(.fini_array) + PROVIDE_HIDDEN (__fini_array_end = .); + + *(.jcr) + . = ALIGN(4); + } > RAM AT> FLASH + + .tdata : { + . = ALIGN(4); + *(.tdata .tdata.* .gnu.linkonce.td.*) + /* All data end */ + __tdata_end = .; + } > RAM AT> FLASH + PROVIDE(__data_end__ = .); + + /* __etext is (for backwards compatibility) the name of the .data init source pointer (...) */ + __etext = LOADADDR(.data); + + .tbss (NOLOAD) : { + . = ALIGN(4); + __bss_start__ = .; + __tls_base = .; + *(.tbss .tbss.* .gnu.linkonce.tb.*) + *(.tcommon) + + __tls_end = .; + } > RAM + + .bss : { + . = ALIGN(4); + __tbss_end = .; + + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*))) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } > RAM + + .heap (NOLOAD): + { + __end__ = .; + end = __end__; + KEEP(*(.heap*)) + } > RAM + /* historically on GCC sbrk was growing past __HeapLimit to __StackLimit, however + to be more compatible, we now set __HeapLimit explicitly to where the end of the heap is */ + __HeapLimit = ORIGIN(RAM) + LENGTH(RAM); + + /* Start and end symbols must be word-aligned */ + .scratch_x : { + __scratch_x_start__ = .; + *(.scratch_x.*) + . = ALIGN(4); + __scratch_x_end__ = .; + } > SCRATCH_X AT > FLASH + __scratch_x_source__ = LOADADDR(.scratch_x); + + .scratch_y : { + __scratch_y_start__ = .; + *(.scratch_y.*) + . = ALIGN(4); + __scratch_y_end__ = .; + } > SCRATCH_Y AT > FLASH + __scratch_y_source__ = LOADADDR(.scratch_y); + + /* .stack*_dummy section doesn't contains any symbols. It is only + * used for linker to calculate size of stack sections, and assign + * values to stack symbols later + * + * stack1 section may be empty/missing if platform_launch_core1 is not used */ + + /* by default we put core 0 stack at the end of scratch Y, so that if core 1 + * stack is not used then all of SCRATCH_X is free. + */ + .stack1_dummy (NOLOAD): + { + *(.stack1*) + } > SCRATCH_X + .stack_dummy (NOLOAD): + { + KEEP(*(.stack*)) + } > SCRATCH_Y + + .flash_end : { + KEEP(*(.embedded_end_block*)) + PROVIDE(__flash_binary_end = .); + } > FLASH + + /* stack limit is poorly named, but historically is maximum heap ptr */ + __StackLimit = ORIGIN(RAM) + LENGTH(RAM); + __StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X); + __StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y); + __StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy); + __StackBottom = __StackTop - SIZEOF(.stack_dummy); + PROVIDE(__stack = __StackTop); + + /* picolibc and LLVM */ + PROVIDE (__heap_start = __end__); + PROVIDE (__heap_end = __HeapLimit); + PROVIDE( __tls_align = MAX(ALIGNOF(.tdata), ALIGNOF(.tbss)) ); + PROVIDE( __tls_size_align = (__tls_size + __tls_align - 1) & ~(__tls_align - 1)); + PROVIDE( __arm32_tls_tcb_offset = MAX(8, __tls_align) ); + + /* llvm-libc */ + PROVIDE (_end = __end__); + PROVIDE (__llvm_libc_heap_limit = __HeapLimit); + + /* Check if data + heap + stack exceeds RAM limit */ + ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed") + + ASSERT( __binary_info_header_end - __logical_binary_start <= 256, "Binary info must be in first 256 bytes of the binary") + /* todo assert on extra code */ +} + diff --git a/src/rp2_common/pico_crt0/rp2040/memmap_copy_to_ram.ld b/src/rp2_common/pico_crt0/rp2040/memmap_copy_to_ram.ld index 842ebfd3c..ff4deb824 100644 --- a/src/rp2_common/pico_crt0/rp2040/memmap_copy_to_ram.ld +++ b/src/rp2_common/pico_crt0/rp2040/memmap_copy_to_ram.ld @@ -27,6 +27,7 @@ MEMORY RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k + } ENTRY(_entry_point) @@ -132,6 +133,8 @@ SECTIONS __ram_text_source__ = LOADADDR(.text); . = ALIGN(4); + + .data : { __data_start__ = .; *(vtable) diff --git a/src/rp2_common/pico_crt0/rp2040/memmap_default.ld b/src/rp2_common/pico_crt0/rp2040/memmap_default.ld index 51254012d..72d7ef0fe 100644 --- a/src/rp2_common/pico_crt0/rp2040/memmap_default.ld +++ b/src/rp2_common/pico_crt0/rp2040/memmap_default.ld @@ -27,6 +27,7 @@ MEMORY RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k + } ENTRY(_entry_point) @@ -149,6 +150,8 @@ SECTIONS *(.uninitialized_data*) } > RAM + + .data : { __data_start__ = .; *(vtable) diff --git a/src/rp2_common/pico_crt0/rp2040/memmap_flash.template.ld b/src/rp2_common/pico_crt0/rp2040/memmap_flash.template.ld new file mode 100644 index 000000000..999b76530 --- /dev/null +++ b/src/rp2_common/pico_crt0/rp2040/memmap_flash.template.ld @@ -0,0 +1,289 @@ +/* Based on GCC ARM embedded samples. + Defines the following symbols for use by code: + __exidx_start + __exidx_end + __etext + __data_start__ + __preinit_array_start + __preinit_array_end + __init_array_start + __init_array_end + __fini_array_start + __fini_array_end + __data_end__ + __bss_start__ + __bss_end__ + __end__ + end + __HeapLimit + __StackLimit + __StackTop + __stack (== StackTop) +*/ + +MEMORY +{ + @FLASH_REGION@ + RAM(rwx) : ORIGIN = @RAM_ORIGIN@, LENGTH = @RAM_LENGTH@ + SCRATCH_X(rwx) : ORIGIN = @SCRATCH_X_ORIGIN@, LENGTH = @SCRATCH_X_LENGTH@ + SCRATCH_Y(rwx) : ORIGIN = @SCRATCH_Y_ORIGIN@, LENGTH = @SCRATCH_Y_LENGTH@ +@ADDITIONAL_MEMORY@ +} + +ENTRY(_entry_point) + +SECTIONS +{ + /* Second stage bootloader is prepended to the image. It must be 256 bytes big + and checksummed. It is usually built by the boot_stage2 target + in the Raspberry Pi Pico SDK + */ + + .flash_begin : { + __flash_binary_start = .; + } > FLASH + + .boot2 : { + __boot2_start__ = .; + KEEP (*(.boot2)) + __boot2_end__ = .; + } > FLASH + + ASSERT(__boot2_end__ - __boot2_start__ == 256, + "ERROR: Pico second stage bootloader must be 256 bytes in size") + + /* The second stage will always enter the image at the start of .text. + The debugger will use the ELF entry point, which is the _entry_point + symbol if present, otherwise defaults to start of .text. + This can be used to transfer control back to the bootrom on debugger + launches only, to perform proper flash setup. + */ + + .text : { + __logical_binary_start = .; + KEEP (*(.vectors)) + KEEP (*(.binary_info_header)) + __binary_info_header_end = .; + KEEP (*(.embedded_block)) + __embedded_block_end = .; + KEEP (*(.reset)) + /* TODO revisit this now memset/memcpy/float in ROM */ + /* bit of a hack right now to exclude all floating point and time critical (e.g. memset, memcpy) code from + * FLASH ... we will include any thing excluded here in .data below by default */ + *(.init) + *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .text*) + *(.fini) + /* Pull all c'tors into .text */ + *crtbegin.o(.ctors) + *crtbegin?.o(.ctors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) + *(SORT(.ctors.*)) + *(.ctors) + /* Followed by destructors */ + *crtbegin.o(.dtors) + *crtbegin?.o(.dtors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) + *(SORT(.dtors.*)) + *(.dtors) + + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(SORT(.preinit_array.*))) + KEEP(*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + + . = ALIGN(4); + /* init data */ + PROVIDE_HIDDEN (__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + + . = ALIGN(4); + /* finit data */ + PROVIDE_HIDDEN (__fini_array_start = .); + *(SORT(.fini_array.*)) + *(.fini_array) + PROVIDE_HIDDEN (__fini_array_end = .); + + *(.eh_frame*) + . = ALIGN(4); + } > FLASH + + .rodata : { + *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .rodata*) + . = ALIGN(4); + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*))) + . = ALIGN(4); + } > FLASH + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > FLASH + + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > FLASH + __exidx_end = .; + + /* Machine inspectable binary information */ + . = ALIGN(4); + __binary_info_start = .; + .binary_info : + { + KEEP(*(.binary_info.keep.*)) + *(.binary_info.*) + } > FLASH + __binary_info_end = .; + . = ALIGN(4); + + .ram_vector_table (NOLOAD): { + *(.ram_vector_table) + } > RAM + + .uninitialized_data (NOLOAD): { + . = ALIGN(4); + *(.uninitialized_data*) + } > RAM + +@ADDITIONAL_PRE_DATA@ + + .data : { + __data_start__ = .; + *(vtable) + + *(.time_critical*) + + /* remaining .text and .rodata; i.e. stuff we exclude above because we want it in RAM */ + *(.text*) + . = ALIGN(4); + *(.rodata*) + . = ALIGN(4); + + *(.data*) + + . = ALIGN(4); + *(.after_data.*) + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__mutex_array_start = .); + KEEP(*(SORT(.mutex_array.*))) + KEEP(*(.mutex_array)) + PROVIDE_HIDDEN (__mutex_array_end = .); + + . = ALIGN(4); + *(.jcr) + . = ALIGN(4); + } > RAM AT> FLASH + + .tdata : { + . = ALIGN(4); + *(.tdata .tdata.* .gnu.linkonce.td.*) + /* All data end */ + __tdata_end = .; + } > RAM AT> FLASH + PROVIDE(__data_end__ = .); + + /* __etext is (for backwards compatibility) the name of the .data init source pointer (...) */ + __etext = LOADADDR(.data); + + .tbss (NOLOAD) : { + . = ALIGN(4); + __bss_start__ = .; + __tls_base = .; + *(.tbss .tbss.* .gnu.linkonce.tb.*) + *(.tcommon) + + __tls_end = .; + } > RAM + + .bss (NOLOAD) : { + . = ALIGN(4); + __tbss_end = .; + + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*))) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } > RAM + + .heap (NOLOAD): + { + __end__ = .; + end = __end__; + KEEP(*(.heap*)) + } > RAM + /* historically on GCC sbrk was growing past __HeapLimit to __StackLimit, however + to be more compatible, we now set __HeapLimit explicitly to where the end of the heap is */ + __HeapLimit = ORIGIN(RAM) + LENGTH(RAM); + + /* Start and end symbols must be word-aligned */ + .scratch_x : { + __scratch_x_start__ = .; + *(.scratch_x.*) + . = ALIGN(4); + __scratch_x_end__ = .; + } > SCRATCH_X AT > FLASH + __scratch_x_source__ = LOADADDR(.scratch_x); + + .scratch_y : { + __scratch_y_start__ = .; + *(.scratch_y.*) + . = ALIGN(4); + __scratch_y_end__ = .; + } > SCRATCH_Y AT > FLASH + __scratch_y_source__ = LOADADDR(.scratch_y); + + /* .stack*_dummy section doesn't contains any symbols. It is only + * used for linker to calculate size of stack sections, and assign + * values to stack symbols later + * + * stack1 section may be empty/missing if platform_launch_core1 is not used */ + + /* by default we put core 0 stack at the end of scratch Y, so that if core 1 + * stack is not used then all of SCRATCH_X is free. + */ + .stack1_dummy (NOLOAD): + { + *(.stack1*) + } > SCRATCH_X + .stack_dummy (NOLOAD): + { + KEEP(*(.stack*)) + } > SCRATCH_Y + + .flash_end : { + KEEP(*(.embedded_end_block*)) + PROVIDE(__flash_binary_end = .); + } > FLASH + + /* stack limit is poorly named, but historically is maximum heap ptr */ + __StackLimit = ORIGIN(RAM) + LENGTH(RAM); + __StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X); + __StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y); + __StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy); + __StackBottom = __StackTop - SIZEOF(.stack_dummy); + PROVIDE(__stack = __StackTop); + + /* picolibc and LLVM */ + PROVIDE (__heap_start = __end__); + PROVIDE (__heap_end = __HeapLimit); + PROVIDE( __tls_align = MAX(ALIGNOF(.tdata), ALIGNOF(.tbss)) ); + PROVIDE( __tls_size_align = (__tls_size + __tls_align - 1) & ~(__tls_align - 1)); + PROVIDE( __arm32_tls_tcb_offset = MAX(8, __tls_align) ); + + /* llvm-libc */ + PROVIDE (_end = __end__); + PROVIDE (__llvm_libc_heap_limit = __HeapLimit); + + /* Check if data + heap + stack exceeds RAM limit */ + ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed") + + ASSERT( __binary_info_header_end - __logical_binary_start <= 256, "Binary info must be in first 256 bytes of the binary") + /* todo assert on extra code */ +} + diff --git a/src/rp2_common/pico_crt0/rp2040/memmap_no_flash.ld b/src/rp2_common/pico_crt0/rp2040/memmap_no_flash.ld index dbf006a8c..2874d8a91 100644 --- a/src/rp2_common/pico_crt0/rp2040/memmap_no_flash.ld +++ b/src/rp2_common/pico_crt0/rp2040/memmap_no_flash.ld @@ -26,6 +26,7 @@ MEMORY RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k + } ENTRY(_entry_point) @@ -50,7 +51,6 @@ SECTIONS __embedded_block_end = .; . = ALIGN(256); KEEP (*(.vectors)) - *(.time_critical*) *(.text*) . = ALIGN(4); *(.init) @@ -102,9 +102,14 @@ SECTIONS __binary_info_end = .; . = ALIGN(4); + + .data : { __data_start__ = .; *(vtable) + + *(.time_critical*) + *(.data*) . = ALIGN(4); diff --git a/src/rp2_common/pico_crt0/rp2040/memmap_sram.template.ld b/src/rp2_common/pico_crt0/rp2040/memmap_sram.template.ld new file mode 100644 index 000000000..266d6a17c --- /dev/null +++ b/src/rp2_common/pico_crt0/rp2040/memmap_sram.template.ld @@ -0,0 +1,254 @@ +/* Based on GCC ARM embedded samples. + Defines the following symbols for use by code: + __exidx_start + __exidx_end + __etext + __data_start__ + __preinit_array_start + __preinit_array_end + __init_array_start + __init_array_end + __fini_array_start + __fini_array_end + __data_end__ + __bss_start__ + __bss_end__ + __end__ + end + __HeapLimit + __StackLimit + __StackTop + __stack (== StackTop) +*/ + +MEMORY +{ + RAM(rwx) : ORIGIN = @RAM_ORIGIN@, LENGTH = @RAM_LENGTH@ + SCRATCH_X(rwx) : ORIGIN = @SCRATCH_X_ORIGIN@, LENGTH = @SCRATCH_X_LENGTH@ + SCRATCH_Y(rwx) : ORIGIN = @SCRATCH_Y_ORIGIN@, LENGTH = @SCRATCH_Y_LENGTH@ +@ADDITIONAL_MEMORY@ +} + +ENTRY(_entry_point) + +SECTIONS +{ + /* Note in NO_FLASH builds the entry point for both the bootrom, and debugger + entry (ELF entry point), are *first* in the image, and the vector table + follows immediately afterward. This is because the bootrom enters RAM + binaries directly at their lowest address (preferring main RAM over XIP + cache-as-SRAM if both are used). + */ + + .text : { + __logical_binary_start = .; + __reset_start = .; + KEEP (*(.reset)) + __reset_end = .; + KEEP (*(.binary_info_header)) + __binary_info_header_end = .; + KEEP (*(.embedded_block)) + __embedded_block_end = .; + . = ALIGN(256); + KEEP (*(.vectors)) + *(.text*) + . = ALIGN(4); + *(.init) + *(.fini) + /* Pull all c'tors into .text */ + *crtbegin.o(.ctors) + *crtbegin?.o(.ctors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) + *(SORT(.ctors.*)) + *(.ctors) + /* Followed by destructors */ + *crtbegin.o(.dtors) + *crtbegin?.o(.dtors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) + *(SORT(.dtors.*)) + *(.dtors) + + *(.eh_frame*) + } > RAM + + .rodata : { + . = ALIGN(4); + *(.rodata*) + . = ALIGN(4); + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*))) + . = ALIGN(4); + } > RAM + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > RAM + + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > RAM + __exidx_end = .; + + /* Machine inspectable binary information */ + . = ALIGN(4); + __binary_info_start = .; + .binary_info : + { + KEEP(*(.binary_info.keep.*)) + *(.binary_info.*) + } > RAM + __binary_info_end = .; + . = ALIGN(4); + +@ADDITIONAL_PRE_DATA@ + + .data : { + __data_start__ = .; + *(vtable) + + *(.time_critical*) + + *(.data*) + + . = ALIGN(4); + *(.after_data.*) + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__mutex_array_start = .); + KEEP(*(SORT(.mutex_array.*))) + KEEP(*(.mutex_array)) + PROVIDE_HIDDEN (__mutex_array_end = .); + + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(SORT(.preinit_array.*))) + KEEP(*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + + . = ALIGN(4); + /* init data */ + PROVIDE_HIDDEN (__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + + . = ALIGN(4); + /* finit data */ + PROVIDE_HIDDEN (__fini_array_start = .); + *(SORT(.fini_array.*)) + *(.fini_array) + PROVIDE_HIDDEN (__fini_array_end = .); + + *(.jcr) + . = ALIGN(4); + } > RAM + + .tdata : { + . = ALIGN(4); + *(.tdata .tdata.* .gnu.linkonce.td.*) + /* All data end */ + __tdata_end = .; + } > RAM + PROVIDE(__data_end__ = .); + + .uninitialized_data (NOLOAD): { + . = ALIGN(4); + *(.uninitialized_data*) + } > RAM + /* __etext is (for backwards compatibility) the name of the .data init source pointer (...) */ + __etext = LOADADDR(.data); + + .tbss (NOLOAD) : { + . = ALIGN(4); + __bss_start__ = .; + __tls_base = .; + *(.tbss .tbss.* .gnu.linkonce.tb.*) + *(.tcommon) + + __tls_end = .; + } > RAM + + .bss (NOLOAD) : { + . = ALIGN(4); + __tbss_end = .; + + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*))) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } > RAM + + .heap (NOLOAD): + { + __end__ = .; + end = __end__; + KEEP(*(.heap*)) + } > RAM + /* historically on GCC sbrk was growing past __HeapLimit to __StackLimit, however + to be more compatible, we now set __HeapLimit explicitly to where the end of the heap is */ + __HeapLimit = ORIGIN(RAM) + LENGTH(RAM); + + /* Start and end symbols must be word-aligned */ + .scratch_x : { + __scratch_x_start__ = .; + *(.scratch_x.*) + . = ALIGN(4); + __scratch_x_end__ = .; + } > SCRATCH_X + __scratch_x_source__ = LOADADDR(.scratch_x); + + .scratch_y : { + __scratch_y_start__ = .; + *(.scratch_y.*) + . = ALIGN(4); + __scratch_y_end__ = .; + } > SCRATCH_Y + __scratch_y_source__ = LOADADDR(.scratch_y); + + /* .stack*_dummy section doesn't contains any symbols. It is only + * used for linker to calculate size of stack sections, and assign + * values to stack symbols later + * + * stack1 section may be empty/missing if platform_launch_core1 is not used */ + + /* by default we put core 0 stack at the end of scratch Y, so that if core 1 + * stack is not used then all of SCRATCH_X is free. + */ + .stack1_dummy (NOLOAD): + { + *(.stack1*) + } > SCRATCH_X + .stack_dummy (NOLOAD): + { + KEEP(*(.stack*)) + } > SCRATCH_Y + + /* stack limit is poorly named, but historically is maximum heap ptr */ + __StackLimit = ORIGIN(RAM) + LENGTH(RAM); + __StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X); + __StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y); + __StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy); + __StackBottom = __StackTop - SIZEOF(.stack_dummy); + PROVIDE(__stack = __StackTop); + + /* picolibc and LLVM */ + PROVIDE (__heap_start = __end__); + PROVIDE (__heap_end = __HeapLimit); + PROVIDE( __tls_align = MAX(ALIGNOF(.tdata), ALIGNOF(.tbss)) ); + PROVIDE( __tls_size_align = (__tls_size + __tls_align - 1) & ~(__tls_align - 1)); + PROVIDE( __arm32_tls_tcb_offset = MAX(8, __tls_align) ); + + /* llvm-libc */ + PROVIDE (_end = __end__); + PROVIDE (__llvm_libc_heap_limit = __HeapLimit); + + /* Check if data + heap + stack exceeds RAM limit */ + ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed") + + ASSERT( __binary_info_header_end - __logical_binary_start <= 256, "Binary info must be in first 256 bytes of the binary") + /* todo assert on extra code */ +} + diff --git a/src/rp2_common/pico_crt0/rp2350/memmap_copy_flash_sram.template.ld b/src/rp2_common/pico_crt0/rp2350/memmap_copy_flash_sram.template.ld new file mode 100644 index 000000000..2b6e3ca6f --- /dev/null +++ b/src/rp2_common/pico_crt0/rp2350/memmap_copy_flash_sram.template.ld @@ -0,0 +1,312 @@ +/* Based on GCC ARM embedded samples. + Defines the following symbols for use by code: + __exidx_start + __exidx_end + __etext + __data_start__ + __preinit_array_start + __preinit_array_end + __init_array_start + __init_array_end + __fini_array_start + __fini_array_end + __data_end__ + __bss_start__ + __bss_end__ + __end__ + end + __HeapLimit + __StackLimit + __StackTop + __stack (== StackTop) +*/ + +MEMORY +{ + @FLASH_REGION@ + RAM(rwx) : ORIGIN = @RAM_ORIGIN@, LENGTH = @RAM_LENGTH@ + SCRATCH_X(rwx) : ORIGIN = @SCRATCH_X_ORIGIN@, LENGTH = @SCRATCH_X_LENGTH@ + SCRATCH_Y(rwx) : ORIGIN = @SCRATCH_Y_ORIGIN@, LENGTH = @SCRATCH_Y_LENGTH@ +@ADDITIONAL_MEMORY@ +} + +ENTRY(_entry_point) + +SECTIONS +{ + /* On Arm, the bootrom expects a VT at the start of the + image by default; on RISC-V, the default is to enter the image at its + lowest address, so an IMAGE_DEF item is required to specify the + nondefault entry point. */ + + .flash_begin : { + __flash_binary_start = .; + } > FLASH + + /* The bootrom will enter the image at the point indicated in your + IMAGE_DEF, which is usually the reset handler of your vector table. + + The debugger will use the ELF entry point, which is the _entry_point + symbol, and in our case is *different from the bootrom's entry point.* + This is used to go back through the bootrom on debugger launches only, + to perform the same initial flash setup that would be performed on a + cold boot. + */ + + .flashtext : { + __logical_binary_start = .; + KEEP (*(.vectors)) + KEEP (*(.binary_info_header)) + __binary_info_header_end = .; + KEEP (*(.embedded_block)) + __embedded_block_end = .; + KEEP (*(.reset)) + . = ALIGN(4); + } > FLASH + + /* Note the boot2 section is optional, and should be discarded if there is + no reference to it *inside* the binary, as it is not called by the + bootrom. (The bootrom performs a simple best-effort XIP setup and + leaves it to the binary to do anything more sophisticated.) However + there is still a size limit of 256 bytes, to ensure the boot2 can be + stored in boot RAM. + + Really this is a "XIP setup function" -- the name boot2 is historic and + refers to its dual-purpose on RP2040, where it also handled vectoring + from the bootrom into the user image. + */ + + .boot2 : { + __boot2_start__ = .; + *(.boot2) + __boot2_end__ = .; + } > FLASH + + ASSERT(__boot2_end__ - __boot2_start__ <= 256, + "ERROR: Pico second stage bootloader must be no more than 256 bytes in size") + + .rodata : { + /* segments not marked as .flashdata are instead pulled into .data (in RAM) to avoid accidental flash accesses */ + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*))) + . = ALIGN(4); + } > FLASH + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > FLASH + + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > FLASH + __exidx_end = .; + + /* Machine inspectable binary information */ + . = ALIGN(4); + __binary_info_start = .; + .binary_info : + { + KEEP(*(.binary_info.keep.*)) + *(.binary_info.*) + } > FLASH + __binary_info_end = .; + . = ALIGN(4); + + /* Vector table goes first in RAM, to avoid large alignment hole */ + .ram_vector_table (NOLOAD): { + *(.ram_vector_table) + } > RAM + + .uninitialized_data (NOLOAD): { + . = ALIGN(4); + *(.uninitialized_data*) + } > RAM + + .text : { + __ram_text_start__ = .; + *(.init) + *(.text*) + *(.fini) + /* Pull all c'tors into .text */ + *crtbegin.o(.ctors) + *crtbegin?.o(.ctors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) + *(SORT(.ctors.*)) + *(.ctors) + /* Followed by destructors */ + *crtbegin.o(.dtors) + *crtbegin?.o(.dtors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) + *(SORT(.dtors.*)) + *(.dtors) + + *(.eh_frame*) + . = ALIGN(4); + __ram_text_end__ = .; + } > RAM AT> FLASH + __ram_text_source__ = LOADADDR(.text); + . = ALIGN(4); + +@ADDITIONAL_PRE_DATA@ + + .data : { + __data_start__ = .; + *(vtable) + + *(.time_critical*) + + . = ALIGN(4); + *(.rodata*) + *(.srodata*) + . = ALIGN(4); + + *(.data*) + *(.sdata*) + + . = ALIGN(4); + *(.after_data.*) + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__mutex_array_start = .); + KEEP(*(SORT(.mutex_array.*))) + KEEP(*(.mutex_array)) + PROVIDE_HIDDEN (__mutex_array_end = .); + + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(SORT(.preinit_array.*))) + KEEP(*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + + . = ALIGN(4); + /* init data */ + PROVIDE_HIDDEN (__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + + . = ALIGN(4); + /* finit data */ + PROVIDE_HIDDEN (__fini_array_start = .); + *(SORT(.fini_array.*)) + *(.fini_array) + PROVIDE_HIDDEN (__fini_array_end = .); + + *(.jcr) + . = ALIGN(4); + } > RAM AT> FLASH + + .tdata : { + . = ALIGN(4); + *(.tdata .tdata.* .gnu.linkonce.td.*) + /* All data end */ + __tdata_end = .; + } > RAM AT> FLASH + PROVIDE(__data_end__ = .); + + /* __etext is (for backwards compatibility) the name of the .data init source pointer (...) */ + __etext = LOADADDR(.data); + + .tbss (NOLOAD) : { + . = ALIGN(4); + __bss_start__ = .; + __tls_base = .; + *(.tbss .tbss.* .gnu.linkonce.tb.*) + *(.tcommon) + + __tls_end = .; + } > RAM + + .bss : { + . = ALIGN(4); + __tbss_end = .; + + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*))) + *(COMMON) + PROVIDE(__global_pointer$ = . + 2K); + *(.sbss*) + . = ALIGN(4); + __bss_end__ = .; + } > RAM + + .heap (NOLOAD): + { + __end__ = .; + end = __end__; + KEEP(*(.heap*)) + } > RAM + /* historically on GCC sbrk was growing past __HeapLimit to __StackLimit, however + to be more compatible, we now set __HeapLimit explicitly to where the end of the heap is */ + __HeapLimit = ORIGIN(RAM) + LENGTH(RAM); + + /* Start and end symbols must be word-aligned */ + .scratch_x : { + __scratch_x_start__ = .; + *(.scratch_x.*) + . = ALIGN(4); + __scratch_x_end__ = .; + } > SCRATCH_X AT > FLASH + __scratch_x_source__ = LOADADDR(.scratch_x); + + .scratch_y : { + __scratch_y_start__ = .; + *(.scratch_y.*) + . = ALIGN(4); + __scratch_y_end__ = .; + } > SCRATCH_Y AT > FLASH + __scratch_y_source__ = LOADADDR(.scratch_y); + + /* .stack*_dummy section doesn't contains any symbols. It is only + * used for linker to calculate size of stack sections, and assign + * values to stack symbols later + * + * stack1 section may be empty/missing if platform_launch_core1 is not used */ + + /* by default we put core 0 stack at the end of scratch Y, so that if core 1 + * stack is not used then all of SCRATCH_X is free. + */ + .stack1_dummy (NOLOAD): + { + *(.stack1*) + } > SCRATCH_X + .stack_dummy (NOLOAD): + { + KEEP(*(.stack*)) + } > SCRATCH_Y + + .flash_end : { + KEEP(*(.embedded_end_block*)) + PROVIDE(__flash_binary_end = .); + } > FLASH =0xaa + + /* stack limit is poorly named, but historically is maximum heap ptr */ + __StackLimit = ORIGIN(RAM) + LENGTH(RAM); + __StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X); + __StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y); + __StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy); + __StackBottom = __StackTop - SIZEOF(.stack_dummy); + PROVIDE(__stack = __StackTop); + + /* picolibc and LLVM */ + PROVIDE (__heap_start = __end__); + PROVIDE (__heap_end = __HeapLimit); + PROVIDE( __tls_align = MAX(ALIGNOF(.tdata), ALIGNOF(.tbss)) ); + PROVIDE( __tls_size_align = (__tls_size + __tls_align - 1) & ~(__tls_align - 1)); + PROVIDE( __arm32_tls_tcb_offset = MAX(8, __tls_align) ); + + /* llvm-libc */ + PROVIDE (_end = __end__); + PROVIDE (__llvm_libc_heap_limit = __HeapLimit); + + /* Check if data + heap + stack exceeds RAM limit */ + ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed") + + ASSERT( __binary_info_header_end - __logical_binary_start <= 1024, "Binary info must be in first 1024 bytes of the binary") + ASSERT( __embedded_block_end - __logical_binary_start <= 4096, "Embedded block must be in first 4096 bytes of the binary") + + /* todo assert on extra code */ +} + diff --git a/src/rp2_common/pico_crt0/rp2350/memmap_copy_to_ram.ld b/src/rp2_common/pico_crt0/rp2350/memmap_copy_to_ram.ld index 44c69f3b9..9c98354b7 100644 --- a/src/rp2_common/pico_crt0/rp2350/memmap_copy_to_ram.ld +++ b/src/rp2_common/pico_crt0/rp2350/memmap_copy_to_ram.ld @@ -27,6 +27,7 @@ MEMORY RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 512k SCRATCH_X(rwx) : ORIGIN = 0x20080000, LENGTH = 4k SCRATCH_Y(rwx) : ORIGIN = 0x20081000, LENGTH = 4k + } ENTRY(_entry_point) @@ -148,6 +149,8 @@ SECTIONS __ram_text_source__ = LOADADDR(.text); . = ALIGN(4); + + .data : { __data_start__ = .; *(vtable) diff --git a/src/rp2_common/pico_crt0/rp2350/memmap_default.ld b/src/rp2_common/pico_crt0/rp2350/memmap_default.ld index bce316d14..8257a1250 100644 --- a/src/rp2_common/pico_crt0/rp2350/memmap_default.ld +++ b/src/rp2_common/pico_crt0/rp2350/memmap_default.ld @@ -27,6 +27,7 @@ MEMORY RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 512k SCRATCH_X(rwx) : ORIGIN = 0x20080000, LENGTH = 4k SCRATCH_Y(rwx) : ORIGIN = 0x20081000, LENGTH = 4k + } ENTRY(_entry_point) @@ -161,6 +162,8 @@ SECTIONS *(.uninitialized_data*) } > RAM + + .data : { __data_start__ = .; *(vtable) diff --git a/src/rp2_common/pico_crt0/rp2350/memmap_flash.template.ld b/src/rp2_common/pico_crt0/rp2350/memmap_flash.template.ld new file mode 100644 index 000000000..241304727 --- /dev/null +++ b/src/rp2_common/pico_crt0/rp2350/memmap_flash.template.ld @@ -0,0 +1,305 @@ +/* Based on GCC ARM embedded samples. + Defines the following symbols for use by code: + __exidx_start + __exidx_end + __etext + __data_start__ + __preinit_array_start + __preinit_array_end + __init_array_start + __init_array_end + __fini_array_start + __fini_array_end + __data_end__ + __bss_start__ + __bss_end__ + __end__ + end + __HeapLimit + __StackLimit + __StackTop + __stack (== StackTop) +*/ + +MEMORY +{ + @FLASH_REGION@ + RAM(rwx) : ORIGIN = @RAM_ORIGIN@, LENGTH = @RAM_LENGTH@ + SCRATCH_X(rwx) : ORIGIN = @SCRATCH_X_ORIGIN@, LENGTH = @SCRATCH_X_LENGTH@ + SCRATCH_Y(rwx) : ORIGIN = @SCRATCH_Y_ORIGIN@, LENGTH = @SCRATCH_Y_LENGTH@ +@ADDITIONAL_MEMORY@ +} + +ENTRY(_entry_point) + +SECTIONS +{ + .flash_begin : { + __flash_binary_start = .; + } > FLASH + + /* The bootrom will enter the image at the point indicated in your + IMAGE_DEF, which is usually the reset handler of your vector table. + + The debugger will use the ELF entry point, which is the _entry_point + symbol, and in our case is *different from the bootrom's entry point.* + This is used to go back through the bootrom on debugger launches only, + to perform the same initial flash setup that would be performed on a + cold boot. + */ + + .text : { + __logical_binary_start = .; + KEEP (*(.vectors)) + KEEP (*(.binary_info_header)) + __binary_info_header_end = .; + KEEP (*(.embedded_block)) + __embedded_block_end = .; + KEEP (*(.reset)) + /* TODO revisit this now memset/memcpy/float in ROM */ + /* bit of a hack right now to exclude all floating point and time critical (e.g. memset, memcpy) code from + * FLASH ... we will include any thing excluded here in .data below by default */ + *(.init) + *libgcc.a:cmse_nonsecure_call.o + *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .text*) + *(.fini) + /* Pull all c'tors into .text */ + *crtbegin.o(.ctors) + *crtbegin?.o(.ctors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) + *(SORT(.ctors.*)) + *(.ctors) + /* Followed by destructors */ + *crtbegin.o(.dtors) + *crtbegin?.o(.dtors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) + *(SORT(.dtors.*)) + *(.dtors) + + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(SORT(.preinit_array.*))) + KEEP(*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + + . = ALIGN(4); + /* init data */ + PROVIDE_HIDDEN (__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + + . = ALIGN(4); + /* finit data */ + PROVIDE_HIDDEN (__fini_array_start = .); + *(SORT(.fini_array.*)) + *(.fini_array) + PROVIDE_HIDDEN (__fini_array_end = .); + + *(.eh_frame*) + . = ALIGN(4); + } > FLASH + + /* Note the boot2 section is optional, and should be discarded if there is + no reference to it *inside* the binary, as it is not called by the + bootrom. (The bootrom performs a simple best-effort XIP setup and + leaves it to the binary to do anything more sophisticated.) However + there is still a size limit of 256 bytes, to ensure the boot2 can be + stored in boot RAM. + + Really this is a "XIP setup function" -- the name boot2 is historic and + refers to its dual-purpose on RP2040, where it also handled vectoring + from the bootrom into the user image. + */ + + .boot2 : { + __boot2_start__ = .; + *(.boot2) + __boot2_end__ = .; + } > FLASH + + ASSERT(__boot2_end__ - __boot2_start__ <= 256, + "ERROR: Pico second stage bootloader must be no more than 256 bytes in size") + + .rodata : { + *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .rodata*) + *(.srodata*) + . = ALIGN(4); + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*))) + . = ALIGN(4); + } > FLASH + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > FLASH + + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > FLASH + __exidx_end = .; + + /* Machine inspectable binary information */ + . = ALIGN(4); + __binary_info_start = .; + .binary_info : + { + KEEP(*(.binary_info.keep.*)) + *(.binary_info.*) + } > FLASH + __binary_info_end = .; + . = ALIGN(4); + + .ram_vector_table (NOLOAD): { + *(.ram_vector_table) + } > RAM + + .uninitialized_data (NOLOAD): { + . = ALIGN(4); + *(.uninitialized_data*) + } > RAM + +@ADDITIONAL_PRE_DATA@ + + .data : { + __data_start__ = .; + *(vtable) + + *(.time_critical*) + + /* remaining .text and .rodata; i.e. stuff we exclude above because we want it in RAM */ + *(.text*) + . = ALIGN(4); + *(.rodata*) + . = ALIGN(4); + + *(.data*) + *(.sdata*) + + . = ALIGN(4); + *(.after_data.*) + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__mutex_array_start = .); + KEEP(*(SORT(.mutex_array.*))) + KEEP(*(.mutex_array)) + PROVIDE_HIDDEN (__mutex_array_end = .); + + *(.jcr) + . = ALIGN(4); + } > RAM AT> FLASH + + .tdata : { + . = ALIGN(4); + *(.tdata .tdata.* .gnu.linkonce.td.*) + /* All data end */ + __tdata_end = .; + } > RAM AT> FLASH + PROVIDE(__data_end__ = .); + + /* __etext is (for backwards compatibility) the name of the .data init source pointer (...) */ + __etext = LOADADDR(.data); + + .tbss (NOLOAD) : { + . = ALIGN(4); + __bss_start__ = .; + __tls_base = .; + *(.tbss .tbss.* .gnu.linkonce.tb.*) + *(.tcommon) + + __tls_end = .; + } > RAM + + .bss (NOLOAD) : { + . = ALIGN(4); + __tbss_end = .; + + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*))) + *(COMMON) + PROVIDE(__global_pointer$ = . + 2K); + *(.sbss*) + . = ALIGN(4); + __bss_end__ = .; + } > RAM + + .heap (NOLOAD): + { + __end__ = .; + end = __end__; + KEEP(*(.heap*)) + } > RAM + /* historically on GCC sbrk was growing past __HeapLimit to __StackLimit, however + to be more compatible, we now set __HeapLimit explicitly to where the end of the heap is */ + __HeapLimit = ORIGIN(RAM) + LENGTH(RAM); + + /* Start and end symbols must be word-aligned */ + .scratch_x : { + __scratch_x_start__ = .; + *(.scratch_x.*) + . = ALIGN(4); + __scratch_x_end__ = .; + } > SCRATCH_X AT > FLASH + __scratch_x_source__ = LOADADDR(.scratch_x); + + .scratch_y : { + __scratch_y_start__ = .; + *(.scratch_y.*) + . = ALIGN(4); + __scratch_y_end__ = .; + } > SCRATCH_Y AT > FLASH + __scratch_y_source__ = LOADADDR(.scratch_y); + + /* .stack*_dummy section doesn't contains any symbols. It is only + * used for linker to calculate size of stack sections, and assign + * values to stack symbols later + * + * stack1 section may be empty/missing if platform_launch_core1 is not used */ + + /* by default we put core 0 stack at the end of scratch Y, so that if core 1 + * stack is not used then all of SCRATCH_X is free. + */ + .stack1_dummy (NOLOAD): + { + *(.stack1*) + } > SCRATCH_X + .stack_dummy (NOLOAD): + { + KEEP(*(.stack*)) + } > SCRATCH_Y + + .flash_end : { + KEEP(*(.embedded_end_block*)) + PROVIDE(__flash_binary_end = .); + } > FLASH =0xaa + + /* stack limit is poorly named, but historically is maximum heap ptr */ + __StackLimit = ORIGIN(RAM) + LENGTH(RAM); + __StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X); + __StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y); + __StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy); + __StackBottom = __StackTop - SIZEOF(.stack_dummy); + PROVIDE(__stack = __StackTop); + + /* picolibc and LLVM */ + PROVIDE (__heap_start = __end__); + PROVIDE (__heap_end = __HeapLimit); + PROVIDE( __tls_align = MAX(ALIGNOF(.tdata), ALIGNOF(.tbss)) ); + PROVIDE( __tls_size_align = (__tls_size + __tls_align - 1) & ~(__tls_align - 1)); + PROVIDE( __arm32_tls_tcb_offset = MAX(8, __tls_align) ); + + /* llvm-libc */ + PROVIDE (_end = __end__); + PROVIDE (__llvm_libc_heap_limit = __HeapLimit); + + /* Check if data + heap + stack exceeds RAM limit */ + ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed") + + ASSERT( __binary_info_header_end - __logical_binary_start <= 1024, "Binary info must be in first 1024 bytes of the binary") + ASSERT( __embedded_block_end - __logical_binary_start <= 4096, "Embedded block must be in first 4096 bytes of the binary") + + /* todo assert on extra code */ +} + diff --git a/src/rp2_common/pico_crt0/rp2350/memmap_no_flash.ld b/src/rp2_common/pico_crt0/rp2350/memmap_no_flash.ld index 5bedf6d21..1d8d992c5 100644 --- a/src/rp2_common/pico_crt0/rp2350/memmap_no_flash.ld +++ b/src/rp2_common/pico_crt0/rp2350/memmap_no_flash.ld @@ -26,6 +26,7 @@ MEMORY RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 512k SCRATCH_X(rwx) : ORIGIN = 0x20080000, LENGTH = 4k SCRATCH_Y(rwx) : ORIGIN = 0x20081000, LENGTH = 4k + } ENTRY(_entry_point) @@ -104,6 +105,8 @@ SECTIONS __binary_info_end = .; . = ALIGN(4); + + .data : { __data_start__ = .; *(vtable) diff --git a/src/rp2_common/pico_crt0/rp2350/memmap_sram.template.ld b/src/rp2_common/pico_crt0/rp2350/memmap_sram.template.ld new file mode 100644 index 000000000..ce54be99c --- /dev/null +++ b/src/rp2_common/pico_crt0/rp2350/memmap_sram.template.ld @@ -0,0 +1,259 @@ +/* Based on GCC ARM embedded samples. + Defines the following symbols for use by code: + __exidx_start + __exidx_end + __etext + __data_start__ + __preinit_array_start + __preinit_array_end + __init_array_start + __init_array_end + __fini_array_start + __fini_array_end + __data_end__ + __bss_start__ + __bss_end__ + __end__ + end + __HeapLimit + __StackLimit + __StackTop + __stack (== StackTop) +*/ + +MEMORY +{ + RAM(rwx) : ORIGIN = @RAM_ORIGIN@, LENGTH = @RAM_LENGTH@ + SCRATCH_X(rwx) : ORIGIN = @SCRATCH_X_ORIGIN@, LENGTH = @SCRATCH_X_LENGTH@ + SCRATCH_Y(rwx) : ORIGIN = @SCRATCH_Y_ORIGIN@, LENGTH = @SCRATCH_Y_LENGTH@ +@ADDITIONAL_MEMORY@ +} + +ENTRY(_entry_point) + +SECTIONS +{ + /* Note unlike RP2040, we start the image with a vector table even for + NO_FLASH builds. On Arm, the bootrom expects a VT at the start of the + image by default; on RISC-V, the default is to enter the image at its + lowest address, so an IMAGE_DEF item is required to specify the + nondefault entry point. */ + + .text : { + __logical_binary_start = .; + /* Vectors require 512-byte alignment on v8-M when >48 IRQs are used, + so we would waste RAM if the vector table were not at the + start. */ + KEEP (*(.vectors)) + KEEP (*(.binary_info_header)) + __binary_info_header_end = .; + KEEP (*(.embedded_block)) + __embedded_block_end = .; + __reset_start = .; + KEEP (*(.reset)) + __reset_end = .; + *(.time_critical*) + *(.text*) + . = ALIGN(4); + *(.init) + *(.fini) + /* Pull all c'tors into .text */ + *crtbegin.o(.ctors) + *crtbegin?.o(.ctors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) + *(SORT(.ctors.*)) + *(.ctors) + /* Followed by destructors */ + *crtbegin.o(.dtors) + *crtbegin?.o(.dtors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) + *(SORT(.dtors.*)) + *(.dtors) + + *(.eh_frame*) + } > RAM + + .rodata : { + . = ALIGN(4); + *(.rodata*) + *(.srodata*) + . = ALIGN(4); + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*))) + . = ALIGN(4); + } > RAM + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > RAM + + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > RAM + __exidx_end = .; + + /* Machine inspectable binary information */ + . = ALIGN(4); + __binary_info_start = .; + .binary_info : + { + KEEP(*(.binary_info.keep.*)) + *(.binary_info.*) + } > RAM + __binary_info_end = .; + . = ALIGN(4); + +@ADDITIONAL_PRE_DATA@ + + .data : { + __data_start__ = .; + *(vtable) + *(.data*) + *(.sdata*) + + . = ALIGN(4); + *(.after_data.*) + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__mutex_array_start = .); + KEEP(*(SORT(.mutex_array.*))) + KEEP(*(.mutex_array)) + PROVIDE_HIDDEN (__mutex_array_end = .); + + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(SORT(.preinit_array.*))) + KEEP(*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + + . = ALIGN(4); + /* init data */ + PROVIDE_HIDDEN (__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + + . = ALIGN(4); + /* finit data */ + PROVIDE_HIDDEN (__fini_array_start = .); + *(SORT(.fini_array.*)) + *(.fini_array) + PROVIDE_HIDDEN (__fini_array_end = .); + + *(.jcr) + . = ALIGN(4); + } > RAM + + .tdata : { + . = ALIGN(4); + *(.tdata .tdata.* .gnu.linkonce.td.*) + /* All data end */ + __tdata_end = .; + } > RAM + PROVIDE(__data_end__ = .); + + .uninitialized_data (NOLOAD): { + . = ALIGN(4); + *(.uninitialized_data*) + } > RAM + /* __etext is (for backwards compatibility) the name of the .data init source pointer (...) */ + __etext = LOADADDR(.data); + + .tbss (NOLOAD) : { + . = ALIGN(4); + __bss_start__ = .; + __tls_base = .; + *(.tbss .tbss.* .gnu.linkonce.tb.*) + *(.tcommon) + + __tls_end = .; + } > RAM + + .bss (NOLOAD) : { + . = ALIGN(4); + __tbss_end = .; + + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*))) + *(COMMON) + PROVIDE(__global_pointer$ = . + 2K); + *(.sbss*) + . = ALIGN(4); + __bss_end__ = .; + } > RAM + + .heap (NOLOAD): + { + __end__ = .; + end = __end__; + KEEP(*(.heap*)) + } > RAM + /* historically on GCC sbrk was growing past __HeapLimit to __StackLimit, however + to be more compatible, we now set __HeapLimit explicitly to where the end of the heap is */ + __HeapLimit = ORIGIN(RAM) + LENGTH(RAM); + + /* Start and end symbols must be word-aligned */ + .scratch_x : { + __scratch_x_start__ = .; + *(.scratch_x.*) + . = ALIGN(4); + __scratch_x_end__ = .; + } > SCRATCH_X + __scratch_x_source__ = LOADADDR(.scratch_x); + + .scratch_y : { + __scratch_y_start__ = .; + *(.scratch_y.*) + . = ALIGN(4); + __scratch_y_end__ = .; + } > SCRATCH_Y + __scratch_y_source__ = LOADADDR(.scratch_y); + + /* .stack*_dummy section doesn't contains any symbols. It is only + * used for linker to calculate size of stack sections, and assign + * values to stack symbols later + * + * stack1 section may be empty/missing if platform_launch_core1 is not used */ + + /* by default we put core 0 stack at the end of scratch Y, so that if core 1 + * stack is not used then all of SCRATCH_X is free. + */ + .stack1_dummy (NOLOAD): + { + *(.stack1*) + } > SCRATCH_X + .stack_dummy (NOLOAD): + { + KEEP(*(.stack*)) + } > SCRATCH_Y + + /* stack limit is poorly named, but historically is maximum heap ptr */ + __StackLimit = ORIGIN(RAM) + LENGTH(RAM); + __StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X); + __StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y); + __StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy); + __StackBottom = __StackTop - SIZEOF(.stack_dummy); + PROVIDE(__stack = __StackTop); + + /* picolibc and LLVM */ + PROVIDE (__heap_start = __end__); + PROVIDE (__heap_end = __HeapLimit); + PROVIDE( __tls_align = MAX(ALIGNOF(.tdata), ALIGNOF(.tbss)) ); + PROVIDE( __tls_size_align = (__tls_size + __tls_align - 1) & ~(__tls_align - 1)); + PROVIDE( __arm32_tls_tcb_offset = MAX(8, __tls_align) ); + + /* llvm-libc */ + PROVIDE (_end = __end__); + PROVIDE (__llvm_libc_heap_limit = __HeapLimit); + + /* Check if data + heap + stack exceeds RAM limit */ + ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed") + + ASSERT( __binary_info_header_end - __logical_binary_start <= 1024, "Binary info must be in first 1024 bytes of the binary") + ASSERT( __embedded_block_end - __logical_binary_start <= 4096, "Embedded block must be in first 4096 bytes of the binary") + + /* todo assert on extra code */ +} + diff --git a/src/rp2_common/pico_platform_sections/include/pico/platform/sections.h b/src/rp2_common/pico_platform_sections/include/pico/platform/sections.h index e85700295..0f59ff33b 100644 --- a/src/rp2_common/pico_platform_sections/include/pico/platform/sections.h +++ b/src/rp2_common/pico_platform_sections/include/pico/platform/sections.h @@ -145,12 +145,12 @@ * * void __time_critical_func(my_func)(int some_arg) { * - * The function is placed in the `.time_critical.` linker section + * The function is placed in the `.time_critical.text.` linker section * * \see __not_in_flash_func */ #ifndef __time_critical_func -#define __time_critical_func(func_name) __not_in_flash_func(func_name) +#define __time_critical_func(func_name) __noinline __attribute__((section(".time_critical.text." __STRING(func_name)))) func_name #endif /*! \brief Indicate a function should not be stored in flash and should not be inlined diff --git a/src/rp2_common/pico_standard_link/CMakeLists.txt b/src/rp2_common/pico_standard_link/CMakeLists.txt index c16968bba..006ced2a2 100644 --- a/src/rp2_common/pico_standard_link/CMakeLists.txt +++ b/src/rp2_common/pico_standard_link/CMakeLists.txt @@ -5,6 +5,83 @@ if (NOT TARGET pico_standard_link) target_link_libraries(pico_standard_link INTERFACE boot_stage2_headers) endif() + # pico_read_addressmap_value(VARS ...) + # \brief_nodesc\ Read the addressmap.h file and set the values as properties on pico_standard_link + # + # This function is used to read the addressmap.h file and set the values as properties on pico_standard_link, + # for fetching later using pico_get_addressmap_value. + # + # \param\ VARS The variables to read the values of + function(pico_read_addressmap_value) + if (TARGET hardware_regs_headers) + # parse addressmap.h for values, and set them as properties on pico_standard_link + get_target_property(hardware_regs_headers_source_dir hardware_regs_headers SOURCE_DIR) + if (NOT EXISTS ${hardware_regs_headers_source_dir}/include/hardware/regs/addressmap.h) + message(STATUS "addressmap.h not found in ${hardware_regs_headers_source_dir}/include/hardware/regs/") + else() + file(READ ${hardware_regs_headers_source_dir}/include/hardware/regs/addressmap.h hardware_regs_addressmap_h) + foreach (VAR ${ARGV}) + string(REGEX MATCH "#define ${VAR} _u\\(([^)]*)\\)" TMP_VAR "${hardware_regs_addressmap_h}") + if (${CMAKE_MATCH_1}) + set_target_properties(pico_standard_link PROPERTIES ${VAR} ${CMAKE_MATCH_1}) + endif() + endforeach() + endif() + endif() + endfunction() + + # Read all the standard variables from the addressmap.h file + pico_read_addressmap_value( + XIP_SRAM_BASE XIP_SRAM_END + SRAM_BASE SRAM_STRIPED_END + SRAM4_BASE SRAM5_BASE + SRAM_SCRATCH_X_BASE SRAM_SCRATCH_Y_BASE + SRAM_END + ) + + # pico_get_addressmap_value(VAR [FALLBACK_VAR ]) + # \brief\ Get the value of a variable from the addressmap.h file + # + # Use pico_read_addressmap_value to read all the values you need from the addressmap.h + # file before using this function, for best performance. + # + # This function will use the fallback variable if the first variable is not found. + # + # If neither are found, it will call pico_read_addressmap_value to read the values + # from the addressmap.h file, and throw an error if the values are still not found. + # + # \param\ VAR The variable to get the value of + # \param\ FALLBACK_VAR The variable to use if VAR is not found + function(pico_get_addressmap_value VAR) + set(zeroValueArgs NO_RECURSE) + set(oneValueArgs FALLBACK_VAR) + cmake_parse_arguments(PARSE_ARGV 0 args + "${zeroValueArgs}" "${oneValueArgs}" "" + ) + + get_target_property(TMP_VAR pico_standard_link ${VAR}) + if (TMP_VAR) + set(${VAR} ${TMP_VAR} PARENT_SCOPE) + return() + elseif (args_FALLBACK_VAR) + get_target_property(TMP_VAR pico_standard_link ${args_FALLBACK_VAR}) + if (TMP_VAR) + set(${VAR} ${TMP_VAR} PARENT_SCOPE) + return() + endif() + endif() + + # Attempt to read the value from the addressmap.h file + if (NOT args_NO_RECURSE) + pico_read_addressmap_value(${VAR} ${args_FALLBACK_VAR}) + pico_get_addressmap_value(${VAR} NO_RECURSE FALLBACK_VAR ${args_FALLBACK_VAR}) + set(${VAR} ${${VAR}} PARENT_SCOPE) + return() + else() + message(FATAL_ERROR "Variable ${VAR} and fallback ${args_FALLBACK_VAR} not found in addressmap.h") + endif() + endfunction() + # pico_add_link_depend(TARGET dependency) # \brief\ Add a link time dependency to the target # @@ -75,9 +152,171 @@ if (NOT TARGET pico_standard_link) # # \param\ TYPE The binary type to set function(pico_set_binary_type TARGET TYPE) + # catch special cases + if (TYPE STREQUAL "xip_sram") + pico_get_addressmap_value(XIP_SRAM_BASE) + pico_get_addressmap_value(XIP_SRAM_END) + target_compile_definitions(${TARGET} PRIVATE PICO_USE_XIP_CACHE_AS_RAM=1) + set(TYPE "sram") + if (PICO_RP2040) + # Use copy_flash_sram, as RP2040 bootrom cannot directly access XIP SRAM + set(TYPE "copy_flash_sram") + elseif (PICO_RP2350) + # Exclusives only work in main SRAM on RP2350, so cannot use software spin locks + target_compile_definitions(${TARGET} PRIVATE PICO_USE_XIP_CACHE_AS_RAM=1 PICO_USE_SW_SPIN_LOCKS=0) + endif() + pico_set_modified_binary_type(${TARGET} ${TYPE} RAM ${XIP_SRAM_BASE} ${XIP_SRAM_END}-${XIP_SRAM_BASE}-4k SCRATCH_X ${XIP_SRAM_END}-4k 2k SCRATCH_Y ${XIP_SRAM_END}-2k 2k) + return() + elseif (TYPE STREQUAL "no_flash_using_xip_ram" OR TYPE STREQUAL "copy_to_ram_using_xip_ram") + function(pico_internal_add_xip_text_section STORE_AT) + set(ADDITIONAL_PRE_DATA "set(ADDITIONAL_PRE_DATA \" + .xip_text : { + __xip_ram_text_start__ = .; + . = ALIGN(4); + *(.time_critical.text*) + . = ALIGN(4); + __xip_ram_text_end__ = .; + } > XIP_RAM ${STORE_AT} + __xip_ram_text_source__ = LOADADDR(.xip_text);\")" PARENT_SCOPE) + + pico_get_addressmap_value(XIP_SRAM_BASE) + pico_get_addressmap_value(XIP_SRAM_END) + set(ADDITIONAL_MEMORY "set(ADDITIONAL_MEMORY \" XIP_RAM(rwx) : ORIGIN = ${XIP_SRAM_BASE}, LENGTH = ${XIP_SRAM_END} - ${XIP_SRAM_BASE}\")" PARENT_SCOPE) + endfunction() + function(pico_internal_add_xip_text_section_no_flash) + pico_internal_add_xip_text_section("") + set(ADDITIONAL_PRE_DATA ${ADDITIONAL_PRE_DATA} PARENT_SCOPE) + set(ADDITIONAL_MEMORY ${ADDITIONAL_MEMORY} PARENT_SCOPE) + endfunction() + function(pico_internal_add_xip_text_section_copy_to_ram) + pico_internal_add_xip_text_section("AT> FLASH") + set(ADDITIONAL_PRE_DATA ${ADDITIONAL_PRE_DATA} PARENT_SCOPE) + set(ADDITIONAL_MEMORY ${ADDITIONAL_MEMORY} PARENT_SCOPE) + endfunction() + target_compile_definitions(${TARGET} PRIVATE PICO_DATA_COPY_EXTRA_SECTIONS_FILE="data_cpy_xip_text.S" PICO_USE_XIP_CACHE_AS_RAM=1) + file(CONFIGURE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/data_cpy_xip_text.S CONTENT " +.word __xip_ram_text_source__ +.word __xip_ram_text_start__ +.word __xip_ram_text_end__ +" @ONLY) + target_include_directories(${TARGET} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + if (TYPE STREQUAL "no_flash_using_xip_ram") + pico_set_modified_binary_type(${TARGET} sram EXTRAS pico_internal_add_xip_text_section_no_flash) + elseif (TYPE STREQUAL "copy_to_ram_using_xip_ram") + pico_set_modified_binary_type(${TARGET} copy_flash_sram EXTRAS pico_internal_add_xip_text_section_copy_to_ram) + endif() + return() + endif() set_target_properties(${TARGET} PROPERTIES PICO_TARGET_BINARY_TYPE ${TYPE}) endfunction() + # pico_set_modified_binary_type(TARGET TYPE [RAM ] [SCRATCH_X ] [SCRATCH_Y ] [EXTRAS ]) + # \brief\ Set the binary type for the target with modified regions + # + # \param\ TYPE The type of binary to use (flash, sram, or copy_flash_sram) + # \param\ RAM_ORIGIN The origin of the RAM section + # \param\ RAM_LENGTH The length of the RAM section + # \param\ SCRATCH_X_ORIGIN The origin of the SCRATCH_X section + # \param\ SCRATCH_X_LENGTH The length of the SCRATCH_X section + # \param\ SCRATCH_Y_ORIGIN The origin of the SCRATCH_Y section + # \param\ SCRATCH_Y_LENGTH The length of the SCRATCH_Y section + # \param\ EXTRAS A CMake function to call to set additional variables - requires CMake 3.18.0 or later + function(pico_set_modified_binary_type TARGET TYPE) + set(oneValueArgs EXTRAS) + set(multiValueArgs RAM SCRATCH_X SCRATCH_Y) + cmake_parse_arguments(PARSE_ARGV 0 args + "" "${oneValueArgs}" "${multiValueArgs}" + ) + + if (NOT (TYPE STREQUAL "sram" OR TYPE STREQUAL "flash" OR TYPE STREQUAL "copy_flash_sram")) + message(FATAL_ERROR "Invalid binary type for pico_set_modified_binary_type: ${TYPE}") + endif() + + # Default RAM, SCRATCH_X and SCRATCH_Y regions + pico_get_addressmap_value(SRAM_BASE) + pico_get_addressmap_value(SRAM_STRIPED_END) + pico_get_addressmap_value(SRAM_SCRATCH_X_BASE FALLBACK_VAR SRAM4_BASE) # fallback for RP2040 + pico_get_addressmap_value(SRAM_SCRATCH_Y_BASE FALLBACK_VAR SRAM5_BASE) # fallback for RP2040 + pico_get_addressmap_value(SRAM_END) + + # Convert sizes to kibibytes - not necessary, but makes the scripts easier to read + set(PICO_DEFAULT_RAM_ORIGIN ${SRAM_BASE}) + math(EXPR PICO_DEFAULT_RAM_LENGTH "(${SRAM_STRIPED_END}-${SRAM_BASE})/1024") + set(PICO_DEFAULT_RAM_LENGTH "${PICO_DEFAULT_RAM_LENGTH}k") + set(PICO_DEFAULT_SCRATCH_X_ORIGIN ${SRAM_SCRATCH_X_BASE}) + math(EXPR PICO_DEFAULT_SCRATCH_X_LENGTH "(${SRAM_SCRATCH_Y_BASE}-${SRAM_SCRATCH_X_BASE})/1024") + set(PICO_DEFAULT_SCRATCH_X_LENGTH "${PICO_DEFAULT_SCRATCH_X_LENGTH}k") + set(PICO_DEFAULT_SCRATCH_Y_ORIGIN ${SRAM_SCRATCH_Y_BASE}) + math(EXPR PICO_DEFAULT_SCRATCH_Y_LENGTH "(${SRAM_END}-${SRAM_SCRATCH_Y_BASE})/1024") + set(PICO_DEFAULT_SCRATCH_Y_LENGTH "${PICO_DEFAULT_SCRATCH_Y_LENGTH}k") + + # Scripts that will be created by this function + set(LINKER_CMAKE_SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/memmap_${TARGET}.cmake") + set(LINKER_LD_SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/memmap_${TARGET}.ld") + + # Configure memmap_${TARGET}.cmake script file to create memmap_${TARGET}.ld + if (args_RAM) + list(POP_FRONT args_RAM ORIGIN LENGTH) + set(RAM "set(RAM_ORIGIN ${ORIGIN})\nset(RAM_LENGTH ${LENGTH})\n") + elseif (PICO_DEFAULT_RAM_ORIGIN) + set(RAM "set(RAM_ORIGIN ${PICO_DEFAULT_RAM_ORIGIN})\nset(RAM_LENGTH ${PICO_DEFAULT_RAM_LENGTH})\n") + else() + message(FATAL_ERROR "Unknown default RAM region for ${PICO_PLATFORM}") + endif() + if (args_SCRATCH_X) + list(POP_FRONT args_SCRATCH_X ORIGIN LENGTH) + set(SCRATCH_X "set(SCRATCH_X_ORIGIN ${ORIGIN})\nset(SCRATCH_X_LENGTH ${LENGTH})\n") + elseif(PICO_DEFAULT_SCRATCH_X_ORIGIN) + set(SCRATCH_X "set(SCRATCH_X_ORIGIN ${PICO_DEFAULT_SCRATCH_X_ORIGIN})\nset(SCRATCH_X_LENGTH ${PICO_DEFAULT_SCRATCH_X_LENGTH})\n") + else() + message(FATAL_ERROR "Unknown default SCRATCH_X region for ${PICO_PLATFORM}") + endif() + if (args_SCRATCH_Y) + list(POP_FRONT args_SCRATCH_Y ORIGIN LENGTH) + set(SCRATCH_Y "set(SCRATCH_Y_ORIGIN ${ORIGIN})\nset(SCRATCH_Y_LENGTH ${LENGTH})\n") + elseif(PICO_DEFAULT_SCRATCH_Y_ORIGIN) + set(SCRATCH_Y "set(SCRATCH_Y_ORIGIN ${PICO_DEFAULT_SCRATCH_Y_ORIGIN})\nset(SCRATCH_Y_LENGTH ${PICO_DEFAULT_SCRATCH_Y_LENGTH})\n") + else() + message(FATAL_ERROR "Unknown default SCRATCH_Y region for ${PICO_PLATFORM}") + endif() + + if (args_EXTRAS) + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.18.0") + cmake_language(CALL ${args_EXTRAS}) + else() + message(FATAL_ERROR "EXTRAS argument requires CMake 3.18.0 or later") + endif() + endif() + + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + # CMAKE_CURRENT_FUNCTION_LIST_DIR added in 3.17.0 + configure_file(${CMAKE_CURRENT_FUNCTION_LIST_DIR}/memmap_script.template.cmake ${LINKER_CMAKE_SCRIPT} @ONLY) + else() + configure_file(${PICO_SDK_PATH}src/rp2_common/pico_standard_link/memmap_script.template.cmake ${LINKER_CMAKE_SCRIPT} @ONLY) + endif() + + # Add command to run this script whenever it changes + add_custom_command(OUTPUT ${LINKER_LD_SCRIPT} + COMMAND ${CMAKE_COMMAND} + -DPICO_LINKER_SCRIPT_PATH:PATH=${PICO_LINKER_SCRIPT_PATH} + -Doutput_file:FILEPATH=${LINKER_LD_SCRIPT} + -P "${LINKER_CMAKE_SCRIPT}" + DEPENDS ${LINKER_CMAKE_SCRIPT} ${PICO_LINKER_SCRIPT_PATH}/memmap_${TYPE}.template.ld) + add_custom_target(memmap_${TARGET}_ld DEPENDS ${LINKER_LD_SCRIPT}) + add_dependencies(${TARGET} memmap_${TARGET}_ld) + + pico_set_linker_script(${TARGET} ${CMAKE_CURRENT_BINARY_DIR}/memmap_${TARGET}.ld) + + # Set PICO_TARGET_BINARY_TYPE for additional configuration + if (TYPE STREQUAL "sram") + set_target_properties(${TARGET} PROPERTIES PICO_TARGET_BINARY_TYPE no_flash) + elseif (TYPE STREQUAL "copy_flash_sram") + set_target_properties(${TARGET} PROPERTIES PICO_TARGET_BINARY_TYPE copy_to_ram) + else() + set_target_properties(${TARGET} PROPERTIES PICO_TARGET_BINARY_TYPE default) + endif() + endfunction() + # slightly messy as we support both the preferred PICO_DEFAULT_BINARY_TYPE and the individual variables if (NOT PICO_DEFAULT_BINARY_TYPE) if (PICO_NO_FLASH) diff --git a/src/rp2_common/pico_standard_link/memmap_script.template.cmake b/src/rp2_common/pico_standard_link/memmap_script.template.cmake new file mode 100644 index 000000000..ad6e4d047 --- /dev/null +++ b/src/rp2_common/pico_standard_link/memmap_script.template.cmake @@ -0,0 +1,13 @@ +# These lines are configured to be set(RAM_ORIGIN xxx) set(RAM_LENGTH xxx) etc. +@RAM@ +@SCRATCH_X@ +@SCRATCH_Y@ +@ADDITIONAL_MEMORY@ +@ADDITIONAL_PRE_DATA@ + +# Set some defaults +if (NOT FLASH_REGION) + set(FLASH_REGION "INCLUDE \"pico_flash_region.ld\"") +endif() + +configure_file("${PICO_LINKER_SCRIPT_PATH}/memmap_@TYPE@.template.ld" "${output_file}" @ONLY) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 81647ee93..812786eb7 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -13,4 +13,5 @@ if (PICO_ON_DEVICE) add_subdirectory(cmsis_test) add_subdirectory(pico_sem_test) add_subdirectory(pico_sha256_test) + add_subdirectory(pico_xip_sram_test) endif() diff --git a/test/kitchen_sink/CMakeLists.txt b/test/kitchen_sink/CMakeLists.txt index 1d6110f4c..354f82b59 100644 --- a/test/kitchen_sink/CMakeLists.txt +++ b/test/kitchen_sink/CMakeLists.txt @@ -206,6 +206,45 @@ if (NOT KITCHEN_SINK_NO_BINARY_TYPE_VARIANTS) pico_add_extra_outputs(kitchen_sink_no_flash) target_compile_definitions(kitchen_sink_no_flash PRIVATE KITCHEN_SINK_ID="no-flash binary") + # Check that pico_set_modified_binary_type creates same linker script as pico_set_binary_type + add_executable(kitchen_sink_flash ${CMAKE_CURRENT_LIST_DIR}/kitchen_sink.c) + pico_set_modified_binary_type(kitchen_sink_flash flash) + target_link_libraries(kitchen_sink_flash kitchen_sink_libs kitchen_sink_options) + pico_add_extra_outputs(kitchen_sink_flash) + target_compile_definitions(kitchen_sink_flash PRIVATE KITCHEN_SINK_ID="flash binary") + + add_executable(kitchen_sink_sram ${CMAKE_CURRENT_LIST_DIR}/kitchen_sink.c) + pico_set_modified_binary_type(kitchen_sink_sram sram) + target_link_libraries(kitchen_sink_sram kitchen_sink_libs kitchen_sink_options) + pico_add_extra_outputs(kitchen_sink_sram) + target_compile_definitions(kitchen_sink_sram PRIVATE KITCHEN_SINK_ID="sram binary") + + add_executable(kitchen_sink_copy_flash_sram ${CMAKE_CURRENT_LIST_DIR}/kitchen_sink.c) + pico_set_modified_binary_type(kitchen_sink_copy_flash_sram copy_flash_sram) + target_link_libraries(kitchen_sink_copy_flash_sram kitchen_sink_libs kitchen_sink_options) + pico_add_extra_outputs(kitchen_sink_copy_flash_sram) + target_compile_definitions(kitchen_sink_copy_flash_sram PRIVATE KITCHEN_SINK_ID="copy-flash-sram binary") + + # Add command to check files match + add_custom_target(kitchen_sink_flash_ld_match ALL + COMMAND ${CMAKE_COMMAND} + -DDEFAULT_LINKER_SCRIPT:PATH=${PICO_LINKER_SCRIPT_PATH}/memmap_default.ld + -DMODIFIED_LINKER_SCRIPT:PATH=${CMAKE_CURRENT_BINARY_DIR}/memmap_kitchen_sink_flash.ld + -P ${CMAKE_CURRENT_LIST_DIR}/memmap_check.cmake + DEPENDS kitchen_sink_flash) + add_custom_target(kitchen_sink_sram_ld_match ALL + COMMAND ${CMAKE_COMMAND} + -DDEFAULT_LINKER_SCRIPT:PATH=${PICO_LINKER_SCRIPT_PATH}/memmap_no_flash.ld + -DMODIFIED_LINKER_SCRIPT:PATH=${CMAKE_CURRENT_BINARY_DIR}/memmap_kitchen_sink_sram.ld + -P ${CMAKE_CURRENT_LIST_DIR}/memmap_check.cmake + DEPENDS kitchen_sink_sram) + add_custom_target(kitchen_sink_copy_flash_sram_ld_match ALL + COMMAND ${CMAKE_COMMAND} + -DDEFAULT_LINKER_SCRIPT:PATH=${PICO_LINKER_SCRIPT_PATH}/memmap_copy_to_ram.ld + -DMODIFIED_LINKER_SCRIPT:PATH=${CMAKE_CURRENT_BINARY_DIR}/memmap_kitchen_sink_copy_flash_sram.ld + -P ${CMAKE_CURRENT_LIST_DIR}/memmap_check.cmake + DEPENDS kitchen_sink_copy_flash_sram) + if (PICO_RP2040) # RP2040 has blocked ram add_executable(kitchen_sink_blocked_ram ${CMAKE_CURRENT_LIST_DIR}/kitchen_sink.c) @@ -213,6 +252,26 @@ if (NOT KITCHEN_SINK_NO_BINARY_TYPE_VARIANTS) target_link_libraries(kitchen_sink_blocked_ram kitchen_sink_libs kitchen_sink_options) pico_add_extra_outputs(kitchen_sink_blocked_ram) target_compile_definitions(kitchen_sink_blocked_ram PRIVATE KITCHEN_SINK_ID="blocked-ram binary") + + add_executable(kitchen_sink_blocked_ram_modified ${CMAKE_CURRENT_LIST_DIR}/kitchen_sink.c) + pico_set_modified_binary_type(kitchen_sink_blocked_ram_modified flash RAM 0x21000000 256k) + target_link_libraries(kitchen_sink_blocked_ram_modified kitchen_sink_libs kitchen_sink_options) + pico_add_extra_outputs(kitchen_sink_blocked_ram_modified) + target_compile_definitions(kitchen_sink_blocked_ram_modified PRIVATE KITCHEN_SINK_ID="blocked-ram-modified binary") + + add_custom_target(kitchen_sink_blocked_ram_modified_ld_match ALL + COMMAND ${CMAKE_COMMAND} + -DDEFAULT_LINKER_SCRIPT:PATH=${PICO_LINKER_SCRIPT_PATH}/memmap_blocked_ram.ld + -DMODIFIED_LINKER_SCRIPT:PATH=${CMAKE_CURRENT_BINARY_DIR}/memmap_kitchen_sink_blocked_ram_modified.ld + -P ${CMAKE_CURRENT_LIST_DIR}/memmap_check.cmake + DEPENDS kitchen_sink_blocked_ram_modified) + else() + add_executable(kitchen_sink_sram1 ${CMAKE_CURRENT_LIST_DIR}/kitchen_sink.c) + pico_set_modified_binary_type(kitchen_sink_sram1 sram RAM 0x20040000 256k) + target_link_libraries(kitchen_sink_sram1 kitchen_sink_libs kitchen_sink_options) + pico_package_uf2_output(kitchen_sink_sram1) + pico_add_extra_outputs(kitchen_sink_sram1) + target_compile_definitions(kitchen_sink_sram1 PRIVATE KITCHEN_SINK_ID="sram1 binary") endif() endif() diff --git a/test/kitchen_sink/memmap_check.cmake b/test/kitchen_sink/memmap_check.cmake new file mode 100644 index 000000000..9defad498 --- /dev/null +++ b/test/kitchen_sink/memmap_check.cmake @@ -0,0 +1,8 @@ +execute_process(COMMAND ${CMAKE_COMMAND} + -E compare_files + ${DEFAULT_LINKER_SCRIPT} + ${MODIFIED_LINKER_SCRIPT} RESULT_VARIABLE compare_result) + +if (NOT compare_result EQUAL 0) + message(FATAL_ERROR "Generated linker script ${MODIFIED_LINKER_SCRIPT} does not match default linker script ${DEFAULT_LINKER_SCRIPT}") +endif() diff --git a/test/pico_xip_sram_test/CMakeLists.txt b/test/pico_xip_sram_test/CMakeLists.txt new file mode 100644 index 000000000..6a92bcdcd --- /dev/null +++ b/test/pico_xip_sram_test/CMakeLists.txt @@ -0,0 +1,27 @@ +# XIP SRAM only binary +add_executable(pico_xip_sram_test pico_xip_sram_test.c) +target_link_libraries(pico_xip_sram_test PRIVATE pico_stdlib) +pico_set_binary_type(pico_xip_sram_test xip_sram) +pico_minimize_runtime(pico_xip_sram_test INCLUDE PRINTF PRINTF_MINIMAL DEFAULT_ALARM_POOL PANIC FLOAT) +target_compile_definitions(pico_xip_sram_test PRIVATE PICO_HEAP_SIZE=0x200) +pico_add_extra_outputs(pico_xip_sram_test) + +# Use XIP SRAM for time critical functions +add_executable(pico_critical_xip_sram_test pico_critical_xip_sram_test.c) +target_link_libraries(pico_critical_xip_sram_test PRIVATE pico_stdlib pico_multicore hardware_dma) +if (NOT PICO_RP2040) + target_link_libraries(pico_critical_xip_sram_test PRIVATE hardware_riscv_platform_timer) +endif() +pico_set_binary_type(pico_critical_xip_sram_test copy_to_ram_using_xip_ram) +pico_enable_stdio_usb(pico_critical_xip_sram_test 1) # test with USB as that invokes flash functions +pico_add_extra_outputs(pico_critical_xip_sram_test) + +# Use XIP SRAM for time critical functions (no_flash build) +add_executable(pico_critical_xip_sram_test_no_flash pico_critical_xip_sram_test.c) +target_link_libraries(pico_critical_xip_sram_test_no_flash PRIVATE pico_stdlib pico_multicore hardware_dma) +if (NOT PICO_RP2040) + target_link_libraries(pico_critical_xip_sram_test_no_flash PRIVATE hardware_riscv_platform_timer) +endif() +pico_set_binary_type(pico_critical_xip_sram_test_no_flash no_flash_using_xip_ram) +pico_enable_stdio_usb(pico_critical_xip_sram_test 1) +pico_add_extra_outputs(pico_critical_xip_sram_test_no_flash) diff --git a/test/pico_xip_sram_test/pico_critical_xip_sram_test.c b/test/pico_xip_sram_test/pico_critical_xip_sram_test.c new file mode 100644 index 000000000..5daefe862 --- /dev/null +++ b/test/pico_xip_sram_test/pico_critical_xip_sram_test.c @@ -0,0 +1,158 @@ +#include +#include "pico/stdlib.h" +#include "pico/multicore.h" +#include "hardware/dma.h" +#if PICO_RP2040 +#include "hardware/structs/systick.h" +#else +#include "hardware/riscv_platform_timer.h" +#endif +#include "hardware/structs/busctrl.h" + + +int __time_critical_func(test_func_xip)(void) { +#if PICO_RP2040 + systick_hw->rvr = 0x00ffffff; + systick_hw->cvr = 0; +#else + riscv_timer_set_mtimecmp(0xffffffffffffffff); + riscv_timer_set_mtime(0); +#endif + + volatile uint32_t i = 0; + i += 4; + i += i; + +#if PICO_RP2040 + return systick_hw->rvr - systick_hw->cvr; +#else + return riscv_timer_get_mtime(); +#endif +} + +int __not_in_flash_func(test_func_sram)(void) { +#if PICO_RP2040 + systick_hw->rvr = 0x00ffffff; + systick_hw->cvr = 0; +#else + riscv_timer_set_mtimecmp(0xffffffffffffffff); + riscv_timer_set_mtime(0); +#endif + + volatile uint32_t i = 0; + i += 4; + i += i; + +#if PICO_RP2040 + return systick_hw->rvr - systick_hw->cvr; +#else + return riscv_timer_get_mtime(); +#endif +} + + +void core1_entry() { +#ifndef __riscv + // Just read memory repeatedly + pico_default_asm_volatile( + "1:\n" + "ldr r0, =%0\n" + "ldmia r0!, {r1-r4}\n" + "ldmia r0!, {r1-r4}\n" + "ldmia r0!, {r1-r4}\n" + "ldmia r0!, {r1-r4}\n" + "ldmia r0!, {r1-r4}\n" + "ldmia r0!, {r1-r4}\n" + "ldmia r0!, {r1-r4}\n" + "ldmia r0!, {r1-r4}\n" + "b 1b\n" + : : "i" (SRAM_BASE) : "r0", "r1", "r2", "r3", "r4" + ); +#else + pico_default_asm_volatile( + "1:\n" + "li a0, %0\n" + "lw a1, 0(a0)\n" + "lw a2, 4(a0)\n" + "lw a3, 8(a0)\n" + "lw a4, 12(a0)\n" + "addi a0, a0, 16\n" + "lw a1, 0(a0)\n" + "lw a2, 4(a0)\n" + "lw a3, 8(a0)\n" + "lw a4, 12(a0)\n" + "addi a0, a0, 16\n" + "lw a1, 0(a0)\n" + "lw a2, 4(a0)\n" + "lw a3, 8(a0)\n" + "lw a4, 12(a0)\n" + "addi a0, a0, 16\n" + "j 1b\n" + : : "i" (SRAM_BASE) : "a0", "a1", "a2", "a3", "a4" + ); +#endif +} + + +void trigger_dma(void) { + int dat[8]; + for (int i = 0; i < count_of(dat); i++) { + int chan = dma_claim_unused_channel(true); + dma_channel_config c = dma_channel_get_default_config(chan); + channel_config_set_transfer_data_size(&c, DMA_SIZE_32); + channel_config_set_read_increment(&c, true); + channel_config_set_write_increment(&c, true); + uint32_t from = SRAM_BASE; + uint32_t to = SRAM_BASE + ((SRAM4_BASE - SRAM_BASE) / 2); + uint32_t size = ((SRAM4_BASE - SRAM_BASE) / 2) / 4; + dma_channel_configure(chan, &c, (uint32_t*)to, (uint32_t*)from, size, true); + dat[i] = chan; + } + for (int i = 0; i < count_of(dat); i++) { + dma_channel_unclaim(dat[i]); + } +} + + +int main(void) { + stdio_init_all(); + printf("pico_xip_sram_test begins\n"); + + multicore_launch_core1(core1_entry); + +#if PICO_RP2040 + systick_hw->csr = 0x4 | 0x1; // clock source and enable +#else + riscv_timer_set_fullspeed(true); + riscv_timer_set_enabled(true); + riscv_timer_set_mtimecmp(0xffffffffffffffff); +#endif + + // Give core1 and DMA high priority + hw_set_bits(&busctrl_hw->priority, BUSCTRL_BUS_PRIORITY_PROC1_BITS | BUSCTRL_BUS_PRIORITY_DMA_R_BITS | BUSCTRL_BUS_PRIORITY_DMA_W_BITS); + hw_clear_bits(&busctrl_hw->priority, BUSCTRL_BUS_PRIORITY_PROC0_BITS); + + int test_func_xip_cycles = 0; + int test_func_sram_cycles = 0; + for (int i = 0; i < 5; i++) { + printf("running... %d\n", i); + trigger_dma(); + int tmp = test_func_xip(); + test_func_xip_cycles += tmp; + printf("test_func_xip: %d\n", tmp); + tmp = test_func_sram(); + test_func_sram_cycles += tmp; + printf("test_func_sram: %d\n", tmp); + sleep_ms(500); + } + + if (test_func_xip_cycles >= test_func_sram_cycles) { + printf("ERROR: test_func_xip_cycles (%d) >= test_func_sram_cycles (%d)\n", test_func_xip_cycles, test_func_sram_cycles); + return 1; + } else { + printf("SUCCESS: test_func_xip_cycles (%d) < test_func_sram_cycles (%d)\n", test_func_xip_cycles, test_func_sram_cycles); + } + + printf("pico_xip_sram_test ends\n"); + return 0; +} diff --git a/test/pico_xip_sram_test/pico_xip_sram_test.c b/test/pico_xip_sram_test/pico_xip_sram_test.c new file mode 100644 index 000000000..aa3610843 --- /dev/null +++ b/test/pico_xip_sram_test/pico_xip_sram_test.c @@ -0,0 +1,16 @@ +#include +#include "pico/stdlib.h" + + +int main(void) { + stdio_init_all(); + printf("pico_xip_sram_test begins\n"); + + for (int i = 0; i < 5; i++) { + printf("running... %d\n", i); + sleep_ms(500); + } + + printf("pico_xip_sram_test ends\n"); + return 0; +} diff --git a/tools/check_source_files_in_bazel_build.py b/tools/check_source_files_in_bazel_build.py index 5717916d9..b9b455de3 100755 --- a/tools/check_source_files_in_bazel_build.py +++ b/tools/check_source_files_in_bazel_build.py @@ -60,6 +60,8 @@ # Doxygen only files. "**/index.h", "**/doc.h", + # Not supported in Bazel build + "test/pico_xip_sram_test/*", )