From b2dba0ebd3f69a98ff4104a9e181315ddc635383 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Thu, 12 Dec 2024 18:39:13 +0000 Subject: [PATCH 1/2] 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 2/2] 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()