From b2dba0ebd3f69a98ff4104a9e181315ddc635383 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Thu, 12 Dec 2024 18:39:13 +0000 Subject: [PATCH 01/19] Add pico_set_modified_binary_type function Allows creating binaries using an existing binary type & linker script, but with modified RAM/SCRATCH addresses For example, to only use SRAM1 so you can power down SRAM0 in your binary you could use `pico_set_modified_binary_type( no_flash RAM "0x20040000" "256k"` --- .../pico_standard_link/CMakeLists.txt | 45 +++++++++++++++++++ .../memmap_script.template.cmake | 5 +++ 2 files changed, 50 insertions(+) create mode 100644 src/rp2_common/pico_standard_link/memmap_script.template.cmake diff --git a/src/rp2_common/pico_standard_link/CMakeLists.txt b/src/rp2_common/pico_standard_link/CMakeLists.txt index c16968bba..4ea5b3a95 100644 --- a/src/rp2_common/pico_standard_link/CMakeLists.txt +++ b/src/rp2_common/pico_standard_link/CMakeLists.txt @@ -78,6 +78,51 @@ if (NOT TARGET pico_standard_link) set_target_properties(${TARGET} PROPERTIES PICO_TARGET_BINARY_TYPE ${TYPE}) endfunction() + function(pico_set_modified_binary_type TARGET TYPE) + set(multiValueArgs RAM SCRATCH_X SCRATCH_Y) + cmake_parse_arguments(PARSE_ARGV 0 args + "" "" "${multiValueArgs}" + ) + + pico_set_binary_type(${TARGET} ${TYPE}) + + # 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 "string(REGEX REPLACE \"RAM\\\\(rwx\\\\) *: *ORIGIN *= *[0-9xX]*, *LENGTH *= *[0-9kKmM]*\\n\" \"RAM(rwx) : ORIGIN = ${ORIGIN}, LENGTH = ${LENGTH}\\n\" LINKER_SCRIPT \"\${LINKER_SCRIPT}\")\n") + endif() + if (args_SCRATCH_X) + list(POP_FRONT args_SCRATCH_X ORIGIN LENGTH) + set(SCRATCH_X "string(REGEX REPLACE \"SCRATCH_X\\\\(rwx\\\\) *: *ORIGIN *= *[0-9xX]*, *LENGTH *= *[0-9kKmM]*\\n\" \"SCRATCH_X(rwx) : ORIGIN = ${ORIGIN}, LENGTH = ${LENGTH}\\n\" LINKER_SCRIPT \"\${LINKER_SCRIPT}\")\n") + endif() + if (args_SCRATCH_Y) + list(POP_FRONT args_SCRATCH_Y ORIGIN LENGTH) + set(SCRATCH_Y "string(REGEX REPLACE \"SCRATCH_Y\\\\(rwx\\\\) *: *ORIGIN *= *[0-9xX]*, *LENGTH *= *[0-9kKmM]*\\n\" \"SCRATCH_Y(rwx) : ORIGIN = ${ORIGIN}, LENGTH = ${LENGTH}\\n\" LINKER_SCRIPT \"\${LINKER_SCRIPT}\")\n") + 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 or memmap_${TYPE}.ld 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 ${PICO_LINKER_SCRIPT_PATH}/memmap_${TYPE}.ld ${LINKER_CMAKE_SCRIPT}) + 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) + 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..533dbd2a5 --- /dev/null +++ b/src/rp2_common/pico_standard_link/memmap_script.template.cmake @@ -0,0 +1,5 @@ +file(READ ${PICO_LINKER_SCRIPT_PATH}/memmap_@TYPE@.ld LINKER_SCRIPT) +@RAM@ +@SCRATCH_X@ +@SCRATCH_Y@ +file(WRITE ${output_file} "${LINKER_SCRIPT}") From 44305028a4dd8b6d828d28d7fb35aacf2842ec58 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Mon, 1 Sep 2025 14:53:41 +0100 Subject: [PATCH 02/19] Create new linker scripts rather than modifying existing New template flash and sram linker scripts, to avoid need for parsing the file Adds a test to kitchen_sink that these new linker scripts produce the same defaults, to ensure modifications to linker scripts are propogated --- .../pico_crt0/rp2040/memmap_flash.template.ld | 286 +++++++++++++++++ .../pico_crt0/rp2040/memmap_sram.template.ld | 249 +++++++++++++++ .../pico_crt0/rp2350/memmap_flash.template.ld | 302 ++++++++++++++++++ .../pico_crt0/rp2350/memmap_sram.template.ld | 256 +++++++++++++++ .../pico_standard_link/CMakeLists.txt | 60 +++- .../memmap_script.template.cmake | 11 +- test/kitchen_sink/CMakeLists.txt | 47 +++ test/kitchen_sink/memmap_check.cmake | 8 + 8 files changed, 1208 insertions(+), 11 deletions(-) create mode 100644 src/rp2_common/pico_crt0/rp2040/memmap_flash.template.ld create mode 100644 src/rp2_common/pico_crt0/rp2040/memmap_sram.template.ld create mode 100644 src/rp2_common/pico_crt0/rp2350/memmap_flash.template.ld create mode 100644 src/rp2_common/pico_crt0/rp2350/memmap_sram.template.ld create mode 100644 test/kitchen_sink/memmap_check.cmake 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..cc7cb9d8c --- /dev/null +++ b/src/rp2_common/pico_crt0/rp2040/memmap_flash.template.ld @@ -0,0 +1,286 @@ +/* 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 +{ + INCLUDE "pico_flash_region.ld" + 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@ +} + +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 + + .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_sram.template.ld b/src/rp2_common/pico_crt0/rp2040/memmap_sram.template.ld new file mode 100644 index 000000000..50283d213 --- /dev/null +++ b/src/rp2_common/pico_crt0/rp2040/memmap_sram.template.ld @@ -0,0 +1,249 @@ +/* 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@ +} + +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)) + *(.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*) + . = 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); + + .data : { + __data_start__ = .; + *(vtable) + *(.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_flash.template.ld b/src/rp2_common/pico_crt0/rp2350/memmap_flash.template.ld new file mode 100644 index 000000000..c16fab215 --- /dev/null +++ b/src/rp2_common/pico_crt0/rp2350/memmap_flash.template.ld @@ -0,0 +1,302 @@ +/* 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 +{ + INCLUDE "pico_flash_region.ld" + 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@ +} + +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 + + .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_sram.template.ld b/src/rp2_common/pico_crt0/rp2350/memmap_sram.template.ld new file mode 100644 index 000000000..d5569d8f1 --- /dev/null +++ b/src/rp2_common/pico_crt0/rp2350/memmap_sram.template.ld @@ -0,0 +1,256 @@ +/* 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@ +} + +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); + + .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_standard_link/CMakeLists.txt b/src/rp2_common/pico_standard_link/CMakeLists.txt index 4ea5b3a95..66d5d1552 100644 --- a/src/rp2_common/pico_standard_link/CMakeLists.txt +++ b/src/rp2_common/pico_standard_link/CMakeLists.txt @@ -78,13 +78,42 @@ if (NOT TARGET pico_standard_link) set_target_properties(${TARGET} PROPERTIES PICO_TARGET_BINARY_TYPE ${TYPE}) endfunction() + # pico_set_modified_binary_type(TARGET TYPE [RAM ] [SCRATCH_X ] [SCRATCH_Y ]) + # \brief\ Set the binary type for the target with modified RAM, SCRATCH_X and SCRATCH_Y regions + # + # \param\ TYPE The type of binary to use (flash or 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 function(pico_set_modified_binary_type TARGET TYPE) set(multiValueArgs RAM SCRATCH_X SCRATCH_Y) cmake_parse_arguments(PARSE_ARGV 0 args "" "" "${multiValueArgs}" ) - pico_set_binary_type(${TARGET} ${TYPE}) + if (NOT (TYPE STREQUAL "sram" OR TYPE STREQUAL "flash")) + message(FATAL_ERROR "Invalid binary type for pico_set_modified_binary_type: ${TYPE}") + endif() + + # Default RAM, SCRATCH_X and SCRATCH_Y regions + if (PICO_RP2040) + set(PICO_DEFAULT_RAM_ORIGIN 0x20000000) + set(PICO_DEFAULT_RAM_LENGTH 256k) + set(PICO_DEFAULT_SCRATCH_X_ORIGIN 0x20040000) + set(PICO_DEFAULT_SCRATCH_X_LENGTH 4k) + set(PICO_DEFAULT_SCRATCH_Y_ORIGIN 0x20041000) + set(PICO_DEFAULT_SCRATCH_Y_LENGTH 4k) + elseif (PICO_RP2350) + set(PICO_DEFAULT_RAM_ORIGIN 0x20000000) + set(PICO_DEFAULT_RAM_LENGTH 512k) + set(PICO_DEFAULT_SCRATCH_X_ORIGIN 0x20080000) + set(PICO_DEFAULT_SCRATCH_X_LENGTH 4k) + set(PICO_DEFAULT_SCRATCH_Y_ORIGIN 0x20081000) + set(PICO_DEFAULT_SCRATCH_Y_LENGTH 4k) + endif() # Scripts that will be created by this function set(LINKER_CMAKE_SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/memmap_${TARGET}.cmake") @@ -93,15 +122,27 @@ if (NOT TARGET pico_standard_link) # Configure memmap_${TARGET}.cmake script file to create memmap_${TARGET}.ld if (args_RAM) list(POP_FRONT args_RAM ORIGIN LENGTH) - set(RAM "string(REGEX REPLACE \"RAM\\\\(rwx\\\\) *: *ORIGIN *= *[0-9xX]*, *LENGTH *= *[0-9kKmM]*\\n\" \"RAM(rwx) : ORIGIN = ${ORIGIN}, LENGTH = ${LENGTH}\\n\" LINKER_SCRIPT \"\${LINKER_SCRIPT}\")\n") + 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 "string(REGEX REPLACE \"SCRATCH_X\\\\(rwx\\\\) *: *ORIGIN *= *[0-9xX]*, *LENGTH *= *[0-9kKmM]*\\n\" \"SCRATCH_X(rwx) : ORIGIN = ${ORIGIN}, LENGTH = ${LENGTH}\\n\" LINKER_SCRIPT \"\${LINKER_SCRIPT}\")\n") + 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 "string(REGEX REPLACE \"SCRATCH_Y\\\\(rwx\\\\) *: *ORIGIN *= *[0-9xX]*, *LENGTH *= *[0-9kKmM]*\\n\" \"SCRATCH_Y(rwx) : ORIGIN = ${ORIGIN}, LENGTH = ${LENGTH}\\n\" LINKER_SCRIPT \"\${LINKER_SCRIPT}\")\n") + 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 (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") # CMAKE_CURRENT_FUNCTION_LIST_DIR added in 3.17.0 @@ -110,17 +151,24 @@ if (NOT TARGET pico_standard_link) 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 or memmap_${TYPE}.ld changes + # 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 ${PICO_LINKER_SCRIPT_PATH}/memmap_${TYPE}.ld ${LINKER_CMAKE_SCRIPT}) + DEPENDS ${LINKER_CMAKE_SCRIPT}) 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) + 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 diff --git a/src/rp2_common/pico_standard_link/memmap_script.template.cmake b/src/rp2_common/pico_standard_link/memmap_script.template.cmake index 533dbd2a5..8cff236ae 100644 --- a/src/rp2_common/pico_standard_link/memmap_script.template.cmake +++ b/src/rp2_common/pico_standard_link/memmap_script.template.cmake @@ -1,5 +1,6 @@ -file(READ ${PICO_LINKER_SCRIPT_PATH}/memmap_@TYPE@.ld LINKER_SCRIPT) -@RAM@ -@SCRATCH_X@ -@SCRATCH_Y@ -file(WRITE ${output_file} "${LINKER_SCRIPT}") +# These lines are configured to be set(RAM_ORIGIN xxx) set(RAM_LENGTH xxx) etc. +@RAM@ +@SCRATCH_X@ +@SCRATCH_Y@ + +configure_file("${PICO_LINKER_SCRIPT_PATH}/memmap_@TYPE@.template.ld" "${output_file}" @ONLY) diff --git a/test/kitchen_sink/CMakeLists.txt b/test/kitchen_sink/CMakeLists.txt index 1d6110f4c..337813e45 100644 --- a/test/kitchen_sink/CMakeLists.txt +++ b/test/kitchen_sink/CMakeLists.txt @@ -206,6 +206,33 @@ 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 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) + if (PICO_RP2040) # RP2040 has blocked ram add_executable(kitchen_sink_blocked_ram ${CMAKE_CURRENT_LIST_DIR}/kitchen_sink.c) @@ -213,6 +240,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() From 3aca4b634226a27ce029e50ccd841ae5605abf83 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Thu, 4 Sep 2025 10:31:38 +0100 Subject: [PATCH 03/19] Fix PICO_USE_SW_SPIN_LOCKS=0 on RP2350 Use spinlock IDs that are unaffected by E2 --- .../include/hardware/sync/spin_lock.h | 42 ++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) 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 From 7201ebc0d08230bf8bb8707de1952c25336bf9f4 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Thu, 4 Sep 2025 11:14:29 +0100 Subject: [PATCH 04/19] Add support for xip_sram binary type --- src/rp2_common/pico_standard_link/CMakeLists.txt | 10 ++++++++++ test/CMakeLists.txt | 1 + test/pico_xip_sram_test/CMakeLists.txt | 7 +++++++ test/pico_xip_sram_test/pico_xip_sram_test.c | 8 ++++++++ 4 files changed, 26 insertions(+) create mode 100644 test/pico_xip_sram_test/CMakeLists.txt create mode 100644 test/pico_xip_sram_test/pico_xip_sram_test.c diff --git a/src/rp2_common/pico_standard_link/CMakeLists.txt b/src/rp2_common/pico_standard_link/CMakeLists.txt index 66d5d1552..10782fb25 100644 --- a/src/rp2_common/pico_standard_link/CMakeLists.txt +++ b/src/rp2_common/pico_standard_link/CMakeLists.txt @@ -75,6 +75,16 @@ 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") + if (PICO_RP2350) + pico_set_modified_binary_type(${TARGET} sram RAM 0x13ffc000 12k SCRATCH_X 0x13fff000 2k SCRATCH_Y 0x13fff800 2k) + target_compile_definitions(${TARGET} PRIVATE PICO_USE_SW_SPIN_LOCKS=0 PICO_USE_XIP_SRAM=1) # exclusives only work in main SRAM on RP2350 + else() + message(FATAL_ERROR "xip_sram binary type is only supported on RP2350") + endif() + return() + endif() set_target_properties(${TARGET} PROPERTIES PICO_TARGET_BINARY_TYPE ${TYPE}) endfunction() 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/pico_xip_sram_test/CMakeLists.txt b/test/pico_xip_sram_test/CMakeLists.txt new file mode 100644 index 000000000..6a0f68daf --- /dev/null +++ b/test/pico_xip_sram_test/CMakeLists.txt @@ -0,0 +1,7 @@ +if (NOT PICO_RP2040) + 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_package_uf2_output(pico_xip_sram_test) + pico_add_extra_outputs(pico_xip_sram_test) +endif() 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..4f8846b9b --- /dev/null +++ b/test/pico_xip_sram_test/pico_xip_sram_test.c @@ -0,0 +1,8 @@ +#include +#include "pico/stdlib.h" + + +int main(void) { + stdio_init_all(); + printf("pico_xip_sram_test begins\n"); +} From eb6524078635b1d48efecc319ac6f20b091cec0d Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Thu, 11 Sep 2025 12:59:28 +0100 Subject: [PATCH 05/19] Add copy_flash_sram modified binary type, and allow data_cpy in no_flash builds Also modify linker scripts to add templating additional regions, and callback to pico_set_modified_binary_type to set additional variables --- src/rp2_common/pico_crt0/crt0.S | 21 +- .../rp2040/memmap_copy_flash_sram.template.ld | 290 ++++++++++++++++ .../pico_crt0/rp2040/memmap_copy_to_ram.ld | 3 + .../pico_crt0/rp2040/memmap_default.ld | 3 + .../pico_crt0/rp2040/memmap_flash.template.ld | 5 +- .../pico_crt0/rp2040/memmap_no_flash.ld | 3 + .../pico_crt0/rp2040/memmap_sram.template.ld | 3 + .../rp2350/memmap_copy_flash_sram.template.ld | 312 ++++++++++++++++++ .../pico_crt0/rp2350/memmap_copy_to_ram.ld | 3 + .../pico_crt0/rp2350/memmap_default.ld | 3 + .../pico_crt0/rp2350/memmap_flash.template.ld | 5 +- .../pico_crt0/rp2350/memmap_no_flash.ld | 3 + .../pico_crt0/rp2350/memmap_sram.template.ld | 3 + .../pico_standard_link/CMakeLists.txt | 25 +- .../memmap_script.template.cmake | 6 + test/kitchen_sink/CMakeLists.txt | 12 + 16 files changed, 689 insertions(+), 11 deletions(-) create mode 100644 src/rp2_common/pico_crt0/rp2040/memmap_copy_flash_sram.template.ld create mode 100644 src/rp2_common/pico_crt0/rp2350/memmap_copy_flash_sram.template.ld diff --git a/src/rp2_common/pico_crt0/crt0.S b/src/rp2_common/pico_crt0/crt0.S index ea3b99a5a..34c301ee4 100644 --- a/src/rp2_common/pico_crt0/crt0.S +++ b/src/rp2_common/pico_crt0/crt0.S @@ -20,6 +20,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 @@ -477,7 +482,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 +527,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 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 index cc7cb9d8c..999b76530 100644 --- a/src/rp2_common/pico_crt0/rp2040/memmap_flash.template.ld +++ b/src/rp2_common/pico_crt0/rp2040/memmap_flash.template.ld @@ -23,10 +23,11 @@ MEMORY { - INCLUDE "pico_flash_region.ld" + @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) @@ -149,6 +150,8 @@ SECTIONS *(.uninitialized_data*) } > RAM +@ADDITIONAL_PRE_DATA@ + .data : { __data_start__ = .; *(vtable) 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..62bc3023b 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) @@ -102,6 +103,8 @@ SECTIONS __binary_info_end = .; . = ALIGN(4); + + .data : { __data_start__ = .; *(vtable) diff --git a/src/rp2_common/pico_crt0/rp2040/memmap_sram.template.ld b/src/rp2_common/pico_crt0/rp2040/memmap_sram.template.ld index 50283d213..3c3a126e7 100644 --- a/src/rp2_common/pico_crt0/rp2040/memmap_sram.template.ld +++ b/src/rp2_common/pico_crt0/rp2040/memmap_sram.template.ld @@ -26,6 +26,7 @@ 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) @@ -102,6 +103,8 @@ SECTIONS __binary_info_end = .; . = ALIGN(4); +@ADDITIONAL_PRE_DATA@ + .data : { __data_start__ = .; *(vtable) 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 index c16fab215..241304727 100644 --- a/src/rp2_common/pico_crt0/rp2350/memmap_flash.template.ld +++ b/src/rp2_common/pico_crt0/rp2350/memmap_flash.template.ld @@ -23,10 +23,11 @@ MEMORY { - INCLUDE "pico_flash_region.ld" + @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) @@ -161,6 +162,8 @@ SECTIONS *(.uninitialized_data*) } > RAM +@ADDITIONAL_PRE_DATA@ + .data : { __data_start__ = .; *(vtable) 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 index d5569d8f1..ce54be99c 100644 --- a/src/rp2_common/pico_crt0/rp2350/memmap_sram.template.ld +++ b/src/rp2_common/pico_crt0/rp2350/memmap_sram.template.ld @@ -26,6 +26,7 @@ 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) @@ -104,6 +105,8 @@ SECTIONS __binary_info_end = .; . = ALIGN(4); +@ADDITIONAL_PRE_DATA@ + .data : { __data_start__ = .; *(vtable) diff --git a/src/rp2_common/pico_standard_link/CMakeLists.txt b/src/rp2_common/pico_standard_link/CMakeLists.txt index 10782fb25..5597ec711 100644 --- a/src/rp2_common/pico_standard_link/CMakeLists.txt +++ b/src/rp2_common/pico_standard_link/CMakeLists.txt @@ -88,23 +88,25 @@ if (NOT TARGET pico_standard_link) set_target_properties(${TARGET} PROPERTIES PICO_TARGET_BINARY_TYPE ${TYPE}) endfunction() - # pico_set_modified_binary_type(TARGET TYPE [RAM ] [SCRATCH_X ] [SCRATCH_Y ]) - # \brief\ Set the binary type for the target with modified RAM, SCRATCH_X and SCRATCH_Y regions + # 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 or sram) + # \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 - "" "" "${multiValueArgs}" + "" "${oneValueArgs}" "${multiValueArgs}" ) - if (NOT (TYPE STREQUAL "sram" OR TYPE STREQUAL "flash")) + 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() @@ -154,6 +156,15 @@ if (NOT TARGET pico_standard_link) 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) @@ -167,7 +178,7 @@ if (NOT TARGET pico_standard_link) -DPICO_LINKER_SCRIPT_PATH:PATH=${PICO_LINKER_SCRIPT_PATH} -Doutput_file:FILEPATH=${LINKER_LD_SCRIPT} -P "${LINKER_CMAKE_SCRIPT}" - DEPENDS ${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) @@ -176,6 +187,8 @@ if (NOT TARGET pico_standard_link) # 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() diff --git a/src/rp2_common/pico_standard_link/memmap_script.template.cmake b/src/rp2_common/pico_standard_link/memmap_script.template.cmake index 8cff236ae..4c8b1fcdd 100644 --- a/src/rp2_common/pico_standard_link/memmap_script.template.cmake +++ b/src/rp2_common/pico_standard_link/memmap_script.template.cmake @@ -2,5 +2,11 @@ @RAM@ @SCRATCH_X@ @SCRATCH_Y@ +@ADDITIONAL_CHANGES@ + +# 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/kitchen_sink/CMakeLists.txt b/test/kitchen_sink/CMakeLists.txt index 337813e45..354f82b59 100644 --- a/test/kitchen_sink/CMakeLists.txt +++ b/test/kitchen_sink/CMakeLists.txt @@ -219,6 +219,12 @@ if (NOT KITCHEN_SINK_NO_BINARY_TYPE_VARIANTS) 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} @@ -232,6 +238,12 @@ if (NOT KITCHEN_SINK_NO_BINARY_TYPE_VARIANTS) -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 From 398b71e9f6e91c87dea969dde2b234ba0c65fa9e Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Thu, 11 Sep 2025 14:09:20 +0100 Subject: [PATCH 06/19] Add support for xip_sram binaries on RP2040 and RP2350 On RP2040 they are copy to xip sram, as the bootrom doesn't support direct entry into xip sram --- src/rp2_common/pico_crt0/crt0.S | 9 +++++++++ .../pico_crt0/embedded_start_block.inc.S | 14 ++++++++++++++ src/rp2_common/pico_standard_link/CMakeLists.txt | 9 +++++++-- test/pico_xip_sram_test/CMakeLists.txt | 13 ++++++------- test/pico_xip_sram_test/pico_xip_sram_test.c | 8 ++++++++ 5 files changed, 44 insertions(+), 9 deletions(-) diff --git a/src/rp2_common/pico_crt0/crt0.S b/src/rp2_common/pico_crt0/crt0.S index 34c301ee4..d88cca121 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" @@ -462,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 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_standard_link/CMakeLists.txt b/src/rp2_common/pico_standard_link/CMakeLists.txt index 5597ec711..46a022164 100644 --- a/src/rp2_common/pico_standard_link/CMakeLists.txt +++ b/src/rp2_common/pico_standard_link/CMakeLists.txt @@ -77,9 +77,14 @@ if (NOT TARGET pico_standard_link) function(pico_set_binary_type TARGET TYPE) # catch special cases if (TYPE STREQUAL "xip_sram") - if (PICO_RP2350) + if (PICO_RP2040) + # Use copy_flash_sram, as RP2040 bootrom cannot directly access XIP SRAM + pico_set_modified_binary_type(${TARGET} copy_flash_sram RAM 0x15000000 12k SCRATCH_X 0x15003000 2k SCRATCH_Y 0x15003800 2k) + target_compile_definitions(${TARGET} PRIVATE PICO_USE_XIP_CACHE_AS_RAM=1) + elseif (PICO_RP2350) pico_set_modified_binary_type(${TARGET} sram RAM 0x13ffc000 12k SCRATCH_X 0x13fff000 2k SCRATCH_Y 0x13fff800 2k) - target_compile_definitions(${TARGET} PRIVATE PICO_USE_SW_SPIN_LOCKS=0 PICO_USE_XIP_SRAM=1) # exclusives only work in main SRAM on 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) else() message(FATAL_ERROR "xip_sram binary type is only supported on RP2350") endif() diff --git a/test/pico_xip_sram_test/CMakeLists.txt b/test/pico_xip_sram_test/CMakeLists.txt index 6a0f68daf..59d04de3f 100644 --- a/test/pico_xip_sram_test/CMakeLists.txt +++ b/test/pico_xip_sram_test/CMakeLists.txt @@ -1,7 +1,6 @@ -if (NOT PICO_RP2040) - 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_package_uf2_output(pico_xip_sram_test) - pico_add_extra_outputs(pico_xip_sram_test) -endif() +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) +target_compile_definitions(pico_xip_sram_test PRIVATE PICO_HEAP_SIZE=0x400) +pico_add_extra_outputs(pico_xip_sram_test) diff --git a/test/pico_xip_sram_test/pico_xip_sram_test.c b/test/pico_xip_sram_test/pico_xip_sram_test.c index 4f8846b9b..aa3610843 100644 --- a/test/pico_xip_sram_test/pico_xip_sram_test.c +++ b/test/pico_xip_sram_test/pico_xip_sram_test.c @@ -5,4 +5,12 @@ 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; } From f169711ab2f9f6aea7aa3024514c66a36aad3665 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Thu, 11 Sep 2025 17:23:09 +0100 Subject: [PATCH 07/19] Add no_flash_using_xip_ram and copy_to_ram_using_xip_ram binary types Time critical functions are placed in xip sram for those types --- src/rp2_common/pico_crt0/crt0.S | 4 ++ .../include/pico/platform/sections.h | 4 +- .../pico_standard_link/CMakeLists.txt | 35 +++++++++++++++- .../memmap_script.template.cmake | 3 +- test/pico_xip_sram_test/CMakeLists.txt | 17 +++++++- test/pico_xip_sram_test/pico_xip_sram_test.c | 42 +++++++++++++++++++ 6 files changed, 99 insertions(+), 6 deletions(-) diff --git a/src/rp2_common/pico_crt0/crt0.S b/src/rp2_common/pico_crt0/crt0.S index d88cca121..38b717dfe 100644 --- a/src/rp2_common/pico_crt0/crt0.S +++ b/src/rp2_common/pico_crt0/crt0.S @@ -584,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_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 46a022164..596e79d13 100644 --- a/src/rp2_common/pico_standard_link/CMakeLists.txt +++ b/src/rp2_common/pico_standard_link/CMakeLists.txt @@ -86,7 +86,40 @@ if (NOT TARGET pico_standard_link) # 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) else() - message(FATAL_ERROR "xip_sram binary type is only supported on RP2350") + message(FATAL_ERROR "unknown chip for xip_sram binary type") + endif() + 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) + 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 AT> FLASH + __xip_ram_text_source__ = LOADADDR(.xip_text);\")" PARENT_SCOPE) + + if (PICO_RP2040) + set(ADDITIONAL_MEMORY "set(ADDITIONAL_MEMORY \" XIP_RAM(rwx) : ORIGIN = 0x15000000, LENGTH = 16k\")" PARENT_SCOPE) + elseif(PICO_RP2350) + set(ADDITIONAL_MEMORY "set(ADDITIONAL_MEMORY \" XIP_RAM(rwx) : ORIGIN = 0x13ffc000, LENGTH = 16k\")" PARENT_SCOPE) + else() + message(FATAL_ERROR "unknown chip for xxx_using_xip_ram binary type") + endif() + 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(WRITE ${CMAKE_CURRENT_BINARY_DIR}/data_cpy_xip_text.S " +.word __xip_ram_text_source__ +.word __xip_ram_text_start__ +.word __xip_ram_text_end__ +") + 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) + 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) endif() return() endif() diff --git a/src/rp2_common/pico_standard_link/memmap_script.template.cmake b/src/rp2_common/pico_standard_link/memmap_script.template.cmake index 4c8b1fcdd..ad6e4d047 100644 --- a/src/rp2_common/pico_standard_link/memmap_script.template.cmake +++ b/src/rp2_common/pico_standard_link/memmap_script.template.cmake @@ -2,7 +2,8 @@ @RAM@ @SCRATCH_X@ @SCRATCH_Y@ -@ADDITIONAL_CHANGES@ +@ADDITIONAL_MEMORY@ +@ADDITIONAL_PRE_DATA@ # Set some defaults if (NOT FLASH_REGION) diff --git a/test/pico_xip_sram_test/CMakeLists.txt b/test/pico_xip_sram_test/CMakeLists.txt index 59d04de3f..3480a4098 100644 --- a/test/pico_xip_sram_test/CMakeLists.txt +++ b/test/pico_xip_sram_test/CMakeLists.txt @@ -1,6 +1,19 @@ +# 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) +target_link_libraries(pico_xip_sram_test PRIVATE pico_stdlib pico_multicore) 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) -target_compile_definitions(pico_xip_sram_test PRIVATE PICO_HEAP_SIZE=0x400) +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_xip_sram_test.c) +target_link_libraries(pico_critical_xip_sram_test PRIVATE pico_stdlib pico_multicore) +pico_set_binary_type(pico_critical_xip_sram_test copy_to_ram_using_xip_ram) +pico_add_extra_outputs(pico_critical_xip_sram_test) + +# Same as above, but without XIP SRAM, to show the difference +add_executable(pico_critical_no_xip_sram_test pico_xip_sram_test.c) +target_link_libraries(pico_critical_no_xip_sram_test PRIVATE pico_stdlib pico_multicore) +pico_set_binary_type(pico_critical_no_xip_sram_test copy_to_ram) +pico_add_extra_outputs(pico_critical_no_xip_sram_test) diff --git a/test/pico_xip_sram_test/pico_xip_sram_test.c b/test/pico_xip_sram_test/pico_xip_sram_test.c index aa3610843..52f050b76 100644 --- a/test/pico_xip_sram_test/pico_xip_sram_test.c +++ b/test/pico_xip_sram_test/pico_xip_sram_test.c @@ -1,13 +1,55 @@ #include #include "pico/stdlib.h" +#include "pico/multicore.h" +#include "hardware/structs/systick.h" +#include "hardware/structs/busctrl.h" + + +int __time_critical_func(test_func)(void) { + systick_hw->rvr = 0x00ffffff; + systick_hw->cvr = 0; + + volatile uint32_t i = 0; + i += 4; + i += i; + + return systick_hw->rvr - systick_hw->cvr; +} + + +void core1_entry() { + // 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" + ); +} int main(void) { stdio_init_all(); printf("pico_xip_sram_test begins\n"); + multicore_launch_core1(core1_entry); + + systick_hw->csr = 0x4 | 0x1; // clock source and enable + + // Give core1 high priority + hw_set_bits(&busctrl_hw->priority, BUSCTRL_BUS_PRIORITY_PROC1_BITS); + for (int i = 0; i < 5; i++) { printf("running... %d\n", i); + printf("test_func: %d\n", test_func()); sleep_ms(500); } From 102edabc3ee5341077c9ef38c835e97f5ec7c4ae Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Thu, 11 Sep 2025 17:41:25 +0100 Subject: [PATCH 08/19] Add DMA to slow down accesses --- test/pico_xip_sram_test/CMakeLists.txt | 6 +++--- test/pico_xip_sram_test/pico_xip_sram_test.c | 21 ++++++++++++++++++-- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/test/pico_xip_sram_test/CMakeLists.txt b/test/pico_xip_sram_test/CMakeLists.txt index 3480a4098..7baca73b6 100644 --- a/test/pico_xip_sram_test/CMakeLists.txt +++ b/test/pico_xip_sram_test/CMakeLists.txt @@ -1,6 +1,6 @@ # 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_multicore) +target_link_libraries(pico_xip_sram_test PRIVATE pico_stdlib pico_multicore hardware_dma) 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) target_compile_definitions(pico_xip_sram_test PRIVATE PICO_HEAP_SIZE=0x200) @@ -8,12 +8,12 @@ pico_add_extra_outputs(pico_xip_sram_test) # Use XIP SRAM for time critical functions add_executable(pico_critical_xip_sram_test pico_xip_sram_test.c) -target_link_libraries(pico_critical_xip_sram_test PRIVATE pico_stdlib pico_multicore) +target_link_libraries(pico_critical_xip_sram_test PRIVATE pico_stdlib pico_multicore hardware_dma) pico_set_binary_type(pico_critical_xip_sram_test copy_to_ram_using_xip_ram) pico_add_extra_outputs(pico_critical_xip_sram_test) # Same as above, but without XIP SRAM, to show the difference add_executable(pico_critical_no_xip_sram_test pico_xip_sram_test.c) -target_link_libraries(pico_critical_no_xip_sram_test PRIVATE pico_stdlib pico_multicore) +target_link_libraries(pico_critical_no_xip_sram_test PRIVATE pico_stdlib pico_multicore hardware_dma) pico_set_binary_type(pico_critical_no_xip_sram_test copy_to_ram) pico_add_extra_outputs(pico_critical_no_xip_sram_test) diff --git a/test/pico_xip_sram_test/pico_xip_sram_test.c b/test/pico_xip_sram_test/pico_xip_sram_test.c index 52f050b76..af2fa7c28 100644 --- a/test/pico_xip_sram_test/pico_xip_sram_test.c +++ b/test/pico_xip_sram_test/pico_xip_sram_test.c @@ -1,6 +1,7 @@ #include #include "pico/stdlib.h" #include "pico/multicore.h" +#include "hardware/dma.h" #include "hardware/structs/systick.h" #include "hardware/structs/busctrl.h" @@ -36,6 +37,20 @@ void core1_entry() { } +void trigger_dma(void) { + 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 + ((SRAM_STRIPED_END - SRAM_BASE) / 2); + uint32_t size = ((SRAM_STRIPED_END - SRAM_BASE) / 2) / 4; + dma_channel_configure(chan, &c, (uint32_t*)to, (uint32_t*)from, size, true); + dma_channel_unclaim(chan); +} + + int main(void) { stdio_init_all(); printf("pico_xip_sram_test begins\n"); @@ -44,11 +59,13 @@ int main(void) { systick_hw->csr = 0x4 | 0x1; // clock source and enable - // Give core1 high priority - hw_set_bits(&busctrl_hw->priority, BUSCTRL_BUS_PRIORITY_PROC1_BITS); + // 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); for (int i = 0; i < 5; i++) { printf("running... %d\n", i); + trigger_dma(); printf("test_func: %d\n", test_func()); sleep_ms(500); } From 5398b0e940e9ba8e96d0eddf0c74114f50b05cea Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Thu, 11 Sep 2025 18:23:49 +0100 Subject: [PATCH 09/19] Add pico_get_addressmap_value function Parse addressmap.h for sram locations, and set them as target properties on pico_standard_link for storage --- .../pico_standard_link/CMakeLists.txt | 95 +++++++++++++------ 1 file changed, 68 insertions(+), 27 deletions(-) diff --git a/src/rp2_common/pico_standard_link/CMakeLists.txt b/src/rp2_common/pico_standard_link/CMakeLists.txt index 596e79d13..220f7ff2e 100644 --- a/src/rp2_common/pico_standard_link/CMakeLists.txt +++ b/src/rp2_common/pico_standard_link/CMakeLists.txt @@ -5,6 +5,53 @@ if (NOT TARGET pico_standard_link) target_link_libraries(pico_standard_link INTERFACE boot_stage2_headers) endif() + 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 + 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 + ) + 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() + + # pico_get_addressmap_value(VAR [FALLBACK_VAR ]) + # \brief\ Get the value of a variable from the addressmap.h file + # + # \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(oneValueArgs FALLBACK_VAR) + cmake_parse_arguments(PARSE_ARGV 0 args + "" "${oneValueArgs}" "" + ) + get_target_property(TMP_VAR pico_standard_link ${VAR}) + if (TMP_VAR) + set(${VAR} ${TMP_VAR} PARENT_SCOPE) + elseif (args_FALLBACK_VAR) + get_target_property(TMP_VAR pico_standard_link ${args_FALLBACK_VAR}) + if (TMP_VAR) + set(${VAR} ${TMP_VAR} PARENT_SCOPE) + else() + message(FATAL_ERROR "Variable ${VAR} and fallback ${args_FALLBACK_VAR} not found in addressmap.h") + endif() + else() + message(FATAL_ERROR "Variable ${VAR} not found in addressmap.h") + endif() + endfunction() + # pico_add_link_depend(TARGET dependency) # \brief\ Add a link time dependency to the target # @@ -77,17 +124,18 @@ if (NOT TARGET pico_standard_link) 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 - pico_set_modified_binary_type(${TARGET} copy_flash_sram RAM 0x15000000 12k SCRATCH_X 0x15003000 2k SCRATCH_Y 0x15003800 2k) - target_compile_definitions(${TARGET} PRIVATE PICO_USE_XIP_CACHE_AS_RAM=1) + set(TYPE "copy_flash_sram") elseif (PICO_RP2350) - pico_set_modified_binary_type(${TARGET} sram RAM 0x13ffc000 12k SCRATCH_X 0x13fff000 2k SCRATCH_Y 0x13fff800 2k) # 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) - else() - message(FATAL_ERROR "unknown chip for xip_sram binary type") 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) @@ -101,13 +149,9 @@ if (NOT TARGET pico_standard_link) } > XIP_RAM AT> FLASH __xip_ram_text_source__ = LOADADDR(.xip_text);\")" PARENT_SCOPE) - if (PICO_RP2040) - set(ADDITIONAL_MEMORY "set(ADDITIONAL_MEMORY \" XIP_RAM(rwx) : ORIGIN = 0x15000000, LENGTH = 16k\")" PARENT_SCOPE) - elseif(PICO_RP2350) - set(ADDITIONAL_MEMORY "set(ADDITIONAL_MEMORY \" XIP_RAM(rwx) : ORIGIN = 0x13ffc000, LENGTH = 16k\")" PARENT_SCOPE) - else() - message(FATAL_ERROR "unknown chip for xxx_using_xip_ram binary type") - endif() + 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() target_compile_definitions(${TARGET} PRIVATE PICO_DATA_COPY_EXTRA_SECTIONS_FILE="data_cpy_xip_text.S" PICO_USE_XIP_CACHE_AS_RAM=1) file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/data_cpy_xip_text.S " @@ -149,21 +193,18 @@ if (NOT TARGET pico_standard_link) endif() # Default RAM, SCRATCH_X and SCRATCH_Y regions - if (PICO_RP2040) - set(PICO_DEFAULT_RAM_ORIGIN 0x20000000) - set(PICO_DEFAULT_RAM_LENGTH 256k) - set(PICO_DEFAULT_SCRATCH_X_ORIGIN 0x20040000) - set(PICO_DEFAULT_SCRATCH_X_LENGTH 4k) - set(PICO_DEFAULT_SCRATCH_Y_ORIGIN 0x20041000) - set(PICO_DEFAULT_SCRATCH_Y_LENGTH 4k) - elseif (PICO_RP2350) - set(PICO_DEFAULT_RAM_ORIGIN 0x20000000) - set(PICO_DEFAULT_RAM_LENGTH 512k) - set(PICO_DEFAULT_SCRATCH_X_ORIGIN 0x20080000) - set(PICO_DEFAULT_SCRATCH_X_LENGTH 4k) - set(PICO_DEFAULT_SCRATCH_Y_ORIGIN 0x20081000) - set(PICO_DEFAULT_SCRATCH_Y_LENGTH 4k) - endif() + 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) + + set(PICO_DEFAULT_RAM_ORIGIN ${SRAM_BASE}) + set(PICO_DEFAULT_RAM_LENGTH ${SRAM_STRIPED_END}-${SRAM_BASE}) + set(PICO_DEFAULT_SCRATCH_X_ORIGIN ${SRAM_SCRATCH_X_BASE}) + set(PICO_DEFAULT_SCRATCH_X_LENGTH ${SRAM_SCRATCH_Y_BASE}-${SRAM_SCRATCH_X_BASE}) + set(PICO_DEFAULT_SCRATCH_Y_ORIGIN ${SRAM_SCRATCH_Y_BASE}) + set(PICO_DEFAULT_SCRATCH_Y_LENGTH ${SRAM_END}-${SRAM_SCRATCH_Y_BASE}) # Scripts that will be created by this function set(LINKER_CMAKE_SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/memmap_${TARGET}.cmake") From e78bb05738e0e9ab7b34b86279fad90212a39b75 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Thu, 11 Sep 2025 18:29:17 +0100 Subject: [PATCH 10/19] Bazel checks ignore pico_xip_sram_test, as no bazel support --- tools/check_source_files_in_bazel_build.py | 2 ++ 1 file changed, 2 insertions(+) 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/*", ) From 7172d259f631e12ac8cb989709ae03c2718c86e7 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Thu, 11 Sep 2025 18:57:28 +0100 Subject: [PATCH 11/19] Fix linker script checks --- src/rp2_common/pico_standard_link/CMakeLists.txt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/rp2_common/pico_standard_link/CMakeLists.txt b/src/rp2_common/pico_standard_link/CMakeLists.txt index 220f7ff2e..d04153584 100644 --- a/src/rp2_common/pico_standard_link/CMakeLists.txt +++ b/src/rp2_common/pico_standard_link/CMakeLists.txt @@ -199,12 +199,16 @@ if (NOT TARGET pico_standard_link) 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}) - set(PICO_DEFAULT_RAM_LENGTH ${SRAM_STRIPED_END}-${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}) - set(PICO_DEFAULT_SCRATCH_X_LENGTH ${SRAM_SCRATCH_Y_BASE}-${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}) - set(PICO_DEFAULT_SCRATCH_Y_LENGTH ${SRAM_END}-${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") From 0add4b4d8390ec60e7ca3b4678e689390df09066 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Thu, 11 Sep 2025 19:01:06 +0100 Subject: [PATCH 12/19] Add newlines --- src/rp2_common/pico_crt0/rp2040/memmap_blocked_ram.ld | 3 +++ 1 file changed, 3 insertions(+) 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) From 3216c320d87541b933398395737700c7ebacf55b Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Fri, 12 Sep 2025 10:57:09 +0100 Subject: [PATCH 13/19] Improve time_critical test More DMA, and check the cycles after - now works on RP2040 and RP2350 --- test/pico_xip_sram_test/CMakeLists.txt | 10 +- .../pico_critical_xip_sram_test.c | 106 ++++++++++++++++++ test/pico_xip_sram_test/pico_xip_sram_test.c | 59 ---------- 3 files changed, 108 insertions(+), 67 deletions(-) create mode 100644 test/pico_xip_sram_test/pico_critical_xip_sram_test.c diff --git a/test/pico_xip_sram_test/CMakeLists.txt b/test/pico_xip_sram_test/CMakeLists.txt index 7baca73b6..2cca158b1 100644 --- a/test/pico_xip_sram_test/CMakeLists.txt +++ b/test/pico_xip_sram_test/CMakeLists.txt @@ -1,19 +1,13 @@ # 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_multicore hardware_dma) +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) 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_xip_sram_test.c) +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) pico_set_binary_type(pico_critical_xip_sram_test copy_to_ram_using_xip_ram) pico_add_extra_outputs(pico_critical_xip_sram_test) - -# Same as above, but without XIP SRAM, to show the difference -add_executable(pico_critical_no_xip_sram_test pico_xip_sram_test.c) -target_link_libraries(pico_critical_no_xip_sram_test PRIVATE pico_stdlib pico_multicore hardware_dma) -pico_set_binary_type(pico_critical_no_xip_sram_test copy_to_ram) -pico_add_extra_outputs(pico_critical_no_xip_sram_test) 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..5fe365abf --- /dev/null +++ b/test/pico_xip_sram_test/pico_critical_xip_sram_test.c @@ -0,0 +1,106 @@ +#include +#include "pico/stdlib.h" +#include "pico/multicore.h" +#include "hardware/dma.h" +#include "hardware/structs/systick.h" +#include "hardware/structs/busctrl.h" + + +int __time_critical_func(test_func_xip)(void) { + systick_hw->rvr = 0x00ffffff; + systick_hw->cvr = 0; + + volatile uint32_t i = 0; + i += 4; + i += i; + + return systick_hw->rvr - systick_hw->cvr; +} + +int __not_in_flash_func(test_func_sram)(void) { + systick_hw->rvr = 0x00ffffff; + systick_hw->cvr = 0; + + volatile uint32_t i = 0; + i += 4; + i += i; + + return systick_hw->rvr - systick_hw->cvr; +} + + +void core1_entry() { + // 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" + ); +} + + +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); + + systick_hw->csr = 0x4 | 0x1; // clock source and enable + + // 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 index af2fa7c28..aa3610843 100644 --- a/test/pico_xip_sram_test/pico_xip_sram_test.c +++ b/test/pico_xip_sram_test/pico_xip_sram_test.c @@ -1,72 +1,13 @@ #include #include "pico/stdlib.h" -#include "pico/multicore.h" -#include "hardware/dma.h" -#include "hardware/structs/systick.h" -#include "hardware/structs/busctrl.h" - - -int __time_critical_func(test_func)(void) { - systick_hw->rvr = 0x00ffffff; - systick_hw->cvr = 0; - - volatile uint32_t i = 0; - i += 4; - i += i; - - return systick_hw->rvr - systick_hw->cvr; -} - - -void core1_entry() { - // 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" - ); -} - - -void trigger_dma(void) { - 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 + ((SRAM_STRIPED_END - SRAM_BASE) / 2); - uint32_t size = ((SRAM_STRIPED_END - SRAM_BASE) / 2) / 4; - dma_channel_configure(chan, &c, (uint32_t*)to, (uint32_t*)from, size, true); - dma_channel_unclaim(chan); -} int main(void) { stdio_init_all(); printf("pico_xip_sram_test begins\n"); - multicore_launch_core1(core1_entry); - - systick_hw->csr = 0x4 | 0x1; // clock source and enable - - // 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); - for (int i = 0; i < 5; i++) { printf("running... %d\n", i); - trigger_dma(); - printf("test_func: %d\n", test_func()); sleep_ms(500); } From edbad2f03a04b6eec7e313c8bb7208fa68a7e19e Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Fri, 12 Sep 2025 11:39:52 +0100 Subject: [PATCH 14/19] Tidy up pico_get_addressmap_value Add pico_read_addressmap_value function to do actual reading, called by pico_get_addressmap_value when not found --- .../pico_standard_link/CMakeLists.txt | 78 +++++++++++++------ 1 file changed, 54 insertions(+), 24 deletions(-) diff --git a/src/rp2_common/pico_standard_link/CMakeLists.txt b/src/rp2_common/pico_standard_link/CMakeLists.txt index d04153584..2d60e01cb 100644 --- a/src/rp2_common/pico_standard_link/CMakeLists.txt +++ b/src/rp2_common/pico_standard_link/CMakeLists.txt @@ -5,50 +5,80 @@ if (NOT TARGET pico_standard_link) target_link_libraries(pico_standard_link INTERFACE boot_stage2_headers) endif() - 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 - 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 - ) - 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() + # 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() - 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 - "" "${oneValueArgs}" "" + "${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) - else() - message(FATAL_ERROR "Variable ${VAR} and fallback ${args_FALLBACK_VAR} not found in addressmap.h") + 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} not found in addressmap.h") + message(FATAL_ERROR "Variable ${VAR} and fallback ${args_FALLBACK_VAR} not found in addressmap.h") endif() endfunction() From f714343cfdf9d2b2808c6a889b123a7dbb482142 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Fri, 12 Sep 2025 11:41:06 +0100 Subject: [PATCH 15/19] REVERT THIS: Use xip-sram picotool branch --- pico_sdk_version.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 8880cab1c3674754fbfbc186dc2b8be43fffae26 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Tue, 16 Sep 2025 14:57:20 +0100 Subject: [PATCH 16/19] Fix Risc-V build --- src/rp2_common/pico_crt0/crt0_riscv.S | 24 ++++++++- .../pico_standard_link/CMakeLists.txt | 4 +- test/pico_xip_sram_test/CMakeLists.txt | 5 +- .../pico_critical_xip_sram_test.c | 52 +++++++++++++++++++ 4 files changed, 80 insertions(+), 5 deletions(-) diff --git a/src/rp2_common/pico_crt0/crt0_riscv.S b/src/rp2_common/pico_crt0/crt0_riscv.S index e9fc7d94c..48ab6bd6c 100644 --- a/src/rp2_common/pico_crt0/crt0_riscv.S +++ b/src/rp2_common/pico_crt0/crt0_riscv.S @@ -30,6 +30,11 @@ #define PICO_CRT0_INCLUDE_PICOBIN_END_BLOCK (PICO_CRT0_INCLUDE_PICOBIN_BLOCK && !PICO_NO_FLASH) #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 + // 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 +345,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 +384,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 +428,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_standard_link/CMakeLists.txt b/src/rp2_common/pico_standard_link/CMakeLists.txt index 2d60e01cb..7066db94a 100644 --- a/src/rp2_common/pico_standard_link/CMakeLists.txt +++ b/src/rp2_common/pico_standard_link/CMakeLists.txt @@ -184,11 +184,11 @@ if (NOT TARGET pico_standard_link) set(ADDITIONAL_MEMORY "set(ADDITIONAL_MEMORY \" XIP_RAM(rwx) : ORIGIN = ${XIP_SRAM_BASE}, LENGTH = ${XIP_SRAM_END} - ${XIP_SRAM_BASE}\")" 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(WRITE ${CMAKE_CURRENT_BINARY_DIR}/data_cpy_xip_text.S " + 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) diff --git a/test/pico_xip_sram_test/CMakeLists.txt b/test/pico_xip_sram_test/CMakeLists.txt index 2cca158b1..5edca566d 100644 --- a/test/pico_xip_sram_test/CMakeLists.txt +++ b/test/pico_xip_sram_test/CMakeLists.txt @@ -2,12 +2,15 @@ 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) +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_add_extra_outputs(pico_critical_xip_sram_test) 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 index 5fe365abf..5daefe862 100644 --- a/test/pico_xip_sram_test/pico_critical_xip_sram_test.c +++ b/test/pico_xip_sram_test/pico_critical_xip_sram_test.c @@ -2,34 +2,57 @@ #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" @@ -45,6 +68,29 @@ void core1_entry() { "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 } @@ -74,7 +120,13 @@ int main(void) { 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); From cc7850707079f108daef5881cdc2b6d3b6699b0c Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Thu, 18 Sep 2025 12:41:01 +0100 Subject: [PATCH 17/19] Save/restore XIP cache state around flash functions --- src/rp2_common/hardware_flash/flash.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) 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 From f0b5966e00a57ab986228ba61fb8137525fb563c Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Thu, 18 Sep 2025 13:17:17 +0100 Subject: [PATCH 18/19] Fix config check --- src/rp2_common/pico_crt0/crt0_riscv.S | 1 - 1 file changed, 1 deletion(-) diff --git a/src/rp2_common/pico_crt0/crt0_riscv.S b/src/rp2_common/pico_crt0/crt0_riscv.S index 48ab6bd6c..80278dff1 100644 --- a/src/rp2_common/pico_crt0/crt0_riscv.S +++ b/src/rp2_common/pico_crt0/crt0_riscv.S @@ -30,7 +30,6 @@ #define PICO_CRT0_INCLUDE_PICOBIN_END_BLOCK (PICO_CRT0_INCLUDE_PICOBIN_BLOCK && !PICO_NO_FLASH) #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 From d07fe6558de61f0bb01a92c9c600ccc80109e229 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Thu, 18 Sep 2025 17:43:19 +0100 Subject: [PATCH 19/19] Fix no_flash build Move default time_critical section, and store correctly --- .../pico_crt0/rp2040/memmap_no_flash.ld | 4 +++- .../pico_crt0/rp2040/memmap_sram.template.ld | 4 +++- .../pico_standard_link/CMakeLists.txt | 18 ++++++++++++++---- test/pico_xip_sram_test/CMakeLists.txt | 11 +++++++++++ 4 files changed, 31 insertions(+), 6 deletions(-) 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 62bc3023b..2874d8a91 100644 --- a/src/rp2_common/pico_crt0/rp2040/memmap_no_flash.ld +++ b/src/rp2_common/pico_crt0/rp2040/memmap_no_flash.ld @@ -51,7 +51,6 @@ SECTIONS __embedded_block_end = .; . = ALIGN(256); KEEP (*(.vectors)) - *(.time_critical*) *(.text*) . = ALIGN(4); *(.init) @@ -108,6 +107,9 @@ SECTIONS .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 index 3c3a126e7..266d6a17c 100644 --- a/src/rp2_common/pico_crt0/rp2040/memmap_sram.template.ld +++ b/src/rp2_common/pico_crt0/rp2040/memmap_sram.template.ld @@ -51,7 +51,6 @@ SECTIONS __embedded_block_end = .; . = ALIGN(256); KEEP (*(.vectors)) - *(.time_critical*) *(.text*) . = ALIGN(4); *(.init) @@ -108,6 +107,9 @@ SECTIONS .data : { __data_start__ = .; *(vtable) + + *(.time_critical*) + *(.data*) . = ALIGN(4); diff --git a/src/rp2_common/pico_standard_link/CMakeLists.txt b/src/rp2_common/pico_standard_link/CMakeLists.txt index 7066db94a..006ced2a2 100644 --- a/src/rp2_common/pico_standard_link/CMakeLists.txt +++ b/src/rp2_common/pico_standard_link/CMakeLists.txt @@ -168,7 +168,7 @@ if (NOT TARGET pico_standard_link) 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) + function(pico_internal_add_xip_text_section STORE_AT) set(ADDITIONAL_PRE_DATA "set(ADDITIONAL_PRE_DATA \" .xip_text : { __xip_ram_text_start__ = .; @@ -176,13 +176,23 @@ if (NOT TARGET pico_standard_link) *(.time_critical.text*) . = ALIGN(4); __xip_ram_text_end__ = .; - } > XIP_RAM AT> FLASH + } > 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__ @@ -191,9 +201,9 @@ if (NOT TARGET pico_standard_link) " @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) + 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) + pico_set_modified_binary_type(${TARGET} copy_flash_sram EXTRAS pico_internal_add_xip_text_section_copy_to_ram) endif() return() endif() diff --git a/test/pico_xip_sram_test/CMakeLists.txt b/test/pico_xip_sram_test/CMakeLists.txt index 5edca566d..6a92bcdcd 100644 --- a/test/pico_xip_sram_test/CMakeLists.txt +++ b/test/pico_xip_sram_test/CMakeLists.txt @@ -13,4 +13,15 @@ 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)