Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rp2_common/pico_standard_link: implement customizable linker script #1547

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from

Conversation

mvds00
Copy link
Contributor

@mvds00 mvds00 commented Nov 13, 2023

Fixes #398

Background:

For cases where modifications to the linker script are required, pico-sdk only provides the option to provide an entire linker script using pico_set_linker_script. This patch adds a function pico_customize_linker_script to create a linker script in the build process, based on a template provided by pico-sdk and local settings provided by the user.

The tool of choice is the C preprocessor, but I am open to suggestions for an alternative.

Use cases for linker script modification include:

  • using the linker script as a rudimentary file system for binary blobs
  • sharing WiFi firmware blobs between bootloader and application (picowota)
  • reducing the number of code duplication in linker scripts (e.g. memmap_blocked_ram.ld which differs in only one line from the default)

In such cases, deriving the linker script from a template may/should lead to code that is easier to maintain.

Implementation:

The template is memmap.ld.in, the user input is provided by specifying a customization file to be included, and the linker script is generated by the C preprocessor. This of course is just one of many template solutions, but this one only requires a C compiler, which is available by definition.

The template exposes a few settings by #defining them before including the customization file, and provides a few hooks to add elements to the template.

Examples and hints for use, based on a working example where cyw43 firmware lives in a separate region in flash:

in CMakeLists.txt:

pico_customize_linker_script(my_target tweaks.h)

tweaks.h:

#undef MAIN_FLASH_LENGTH
#define MAIN_FLASH_LENGTH 256k

#undef ADDITIONAL_FLASH_REGIONS
#define ADDITIONAL_FLASH_REGIONS \
    FLASH_CYWFW(rx):            ORIGIN = 0x10040000, LENGTH = 256k

#undef ADDITIONAL_SECTIONS
#define ADDITIONAL_SECTIONS \
    .fw_cyw43 : {               \\
        . = ALIGN(4k);          \\
        KEEP(*(.fw_cyw43.meta)) \\
        . = ALIGN(512);         \\
        *(.fw_cyw43.blob)       \\
    } > FLASH_CYWFW

Details:

  • The linker script will be a build product named ${TARGET}.ld
  • When using \\ at the end of lines, newlines are inserted in the resulting linker script in a postprocessing step. This is done to improve readability of the resulting linker script.
  • Lines starting with # need to be removed from the output; they are now turned into /*comments*/
  • Values that are to be overridden need to be #undeffed before redefining them, which is a bit awkward; another option is to use #ifdef for each and every variable in the template; yet another option (for integers) is to use linker variables (i.e. simply setting MAIN_FLASH_LENGTH=256k).

TODO:

This was only tested on OS X with makefile builds, so broader testing is probably needed (although this new feature doesn't change/break anything existing).
As mentioned above, at least memmap_blocked_ram.ld (and memmap_default.ld) could easily be derived at runtime from the template. Maybe the other linker scripts can also derive from the template with the right tweaks.

…sing C preprocessor

Background:

For cases where modifications to the linker script are required, pico-sdk only
provides the option to provide an entire linker script using
pico_set_linker_script. This patch adds a function pico_customize_linker_script
to create a linker script in the build process, based on a template provided by
pico-sdk and local settings provided by the user.

Use cases for linker script modification include:
- using the linker script as a rudimentary file system for binary blobs
- sharing WiFi firmware blobs between bootloader and application (picowota)
- reducing the number of code duplication in linker scripts (e.g.
  memmap_blocked_ram.ld which differs in only one line from the default)

In such cases, deriving the linker script from a template may/should lead to
code that is easier to maintain.

Implementation:

The template is memmap.ld.in, the user input is provided by specifying a
customization file to be included, and the linker script is generated by the C
preprocessor.

The template exposes a few settings by #defining them before including the
customization file, and provides a few hooks to add elements to the template.

Examples and hints for use, based on a working example where cyw43 firmware
lives in a separate region in flash:

in CMakeLists.txt:

    pico_customize_linker_script(my_target tweaks.h)

tweaks.h:

    #undef MAIN_FLASH_LENGTH
    #define MAIN_FLASH_LENGTH 256k

    #undef ADDITIONAL_FLASH_REGIONS
    #define ADDITIONAL_FLASH_REGIONS \
        FLASH_CYWFW(rx):            ORIGIN = 0x10040000, LENGTH = 256k

    #undef ADDITIONAL_SECTIONS
    #define ADDITIONAL_SECTIONS \
        .fw_cyw43 : {               \\
            . = ALIGN(4k);          \\
            KEEP(*(.fw_cyw43.meta)) \\
            . = ALIGN(512);         \\
            *(.fw_cyw43.blob)       \\
        } > FLASH_CYWFW

Details:

- The linker script will be a build product named ${TARGET}.ld
- When using \\ at the end of lines, newlines are inserted in the resulting
  linker script in a postprocessing step. This is done to improve readability
  of the resulting linker script.
- Lines starting with # need to be removed from the output; they are now turned
  into /*comments*/
- Values that are to be overridden need to be #undeffed before redefining them,
  which is a bit awkward; another option is to use #ifdef for each and every
  variable in the template; yet another option (for integers) is to use linker
  variables (i.e. simply setting MAIN_FLASH_LENGTH=256k).
@lurch
Copy link
Contributor

lurch commented Dec 20, 2023

I know nothing about linker scripts, but replying to just your comment about #undef and #ifdef - maybe it'd make sense to use the same pattern used by e.g. https://github.com/raspberrypi/pico-sdk/blob/master/src/boards/include/boards/pico.h ?

@mvds00
Copy link
Contributor Author

mvds00 commented Dec 20, 2023

I know nothing about linker scripts, but replying to just your comment about #undef and #ifdef - maybe it'd make sense to use the same pattern used by e.g. https://github.com/raspberrypi/pico-sdk/blob/master/src/boards/include/boards/pico.h ?

I have thought about that, but it hides the default values (they come later, and only #ifndef) so it limits the options, unless you would also #define DEFAULT_PARAM 123 before the include, which would make it all very verbose with 4 times the amount of lines, split over 2 places, compared to the #undef strategy.

@kilograham kilograham modified the milestones: 1.7.0, 1.6.0 May 19, 2024
@kilograham kilograham modified the milestones: 1.6.1, 1.7.0 Jul 19, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants