commit 902f03c7616c8a2ceb5c2a52333a49fa5357bdcf Author: Justin Hammond Date: Wed Dec 7 22:45:36 2022 +0800 For the pic branch of github/Fishwaldo/pico-sdk/ diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..18f0f1f --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,18 @@ +{ + "configurations": [ + { + "name": "Linux", + "includePath": [ + "${workspaceFolder}/**", + "/home/fish/pico-sdk/**" + ], + "defines": [], + "compilerPath": "/usr/bin/gcc", + "cStandard": "gnu17", + "cppStandard": "gnu++14", + "intelliSenseMode": "linux-gcc-x64", + "configurationProvider": "ms-vscode.cmake-tools" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..6ba2957 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,30 @@ +{ + "version": "0.2.0", + "configurations": [ + { "name": "Pico Debug", + "showDevDebugOutput": "false", + "device": "RP2040", + "gdbPath": "gdb-multiarch", + "cwd": "${workspaceRoot}", + "executable": "${workspaceRoot}/../build/blink_combined.elf", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "configFiles": [ + "interface/jlink.cfg", + "target/rp2040.cfg" + ], + "openOCDLaunchCommands": [ + "transport select swd", + "adapter speed 4000" + ], + "svdFile": "/home/fish/pico-sdk/src/rp2040/hardware_regs/rp2040.svd", + //"svdFile": "/home/fish/pico-sdk/src/rp2040/hardware_regs/rp2040.svd", + "breakAfterReset": false, + "postRestartCommands": [ + "break main", + "continue" + ] + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..2a2827c --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,23 @@ +{ + // These settings tweaks to the cmake plugin will ensure + // that you debug using cortex-debug instead of trying to launch + // a Pico binary on the host + "cmake.statusbar.advanced": { + "debug": { + "visibility": "hidden" + }, + "launch": { + "visibility": "hidden" + }, + "build": { + "visibility": "hidden" + }, + "buildTarget": { + "visibility": "hidden" + } + }, + "cmake.buildBeforeRun": true, + "C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools", + "cortex-debug.variableUseNaturalFormat": false, + "cortex-debug.registerUseNaturalFormat": false +} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..7093a12 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,38 @@ +cmake_minimum_required(VERSION 3.13) + +# initialize the SDK based on PICO_SDK_PATH +# note: this must happen before project() +include(pico_sdk_import.cmake) +# initialize the Raspberry Pi Pico SDK +pico_sdk_init() +add_subdirectory(bootloader) + +add_compile_options("-g") +add_compile_options("-O0") +add_compile_options("-Wall") + +project(blink) +add_executable(blink + blink.c + image_info.c + ) +set_target_properties(blink PROPERTIES COMPILE_FLAGS "-fpie -fno-plt -Wall -g -O0 -fpic -mpic-register=r9 -msingle-pic-base -mno-pic-data-is-text-relative") +target_link_libraries(blink "-fpie -fno-plt -Wall -g -O0 -fpic -mpic-register=r9 -msingle-pic-base -mno-pic-data-is-text-relative" pico_stdlib ) +pico_enable_stdio_usb(blink 1) +pico_enable_stdio_uart(blink 0) +bootloader_build_combined(blink) +pico_add_extra_outputs(blink) + +project(blink-standalone) +add_executable(blink-standalone + blink.c + image_info.c + ) +set_target_properties(blink-standalone PROPERTIES COMPILE_FLAGS "-fpie -fno-plt -Wall -g -O0 -fpic -mpic-register=r9 -msingle-pic-base -mno-pic-data-is-text-relative") +target_link_libraries(blink-standalone pico_stdlib) +target_link_options(blink-standalone PUBLIC -fpie -fno-plt -Wall -g -O0 -fpic -mpic-register=r9 -msingle-pic-base -mno-pic-data-is-text-relative) +pico_enable_stdio_usb(blink-standalone 1) +pico_enable_stdio_uart(blink-standalone 0) +bootloader_build_standalone(blink-standalone) +pico_add_extra_outputs(blink-standalone) + diff --git a/blink.c b/blink.c new file mode 100644 index 0000000..c51bbf3 --- /dev/null +++ b/blink.c @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include "image_info.h" +#include "pico/stdlib.h" + + +void blink_fn() { + const uint LED_PIN = PICO_DEFAULT_LED_PIN; + gpio_init(LED_PIN); + gpio_set_dir(LED_PIN, GPIO_OUT); + while (true) { + gpio_put(LED_PIN, 1); + sleep_ms(250); + gpio_put(LED_PIN, 0); + sleep_ms(250); + printf("."); + } +} + + +int main() { +#ifndef PICO_DEFAULT_LED_PIN +#warning blink ecorexample requires a board with a regular LED +#else + stdio_init_all(); + typedef void (*blink_t)(void); + blink_t blinkptr = blink_fn; + blinkptr(); +#endif +} diff --git a/bootloader/.vscode/c_cpp_properties.json b/bootloader/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..18f0f1f --- /dev/null +++ b/bootloader/.vscode/c_cpp_properties.json @@ -0,0 +1,18 @@ +{ + "configurations": [ + { + "name": "Linux", + "includePath": [ + "${workspaceFolder}/**", + "/home/fish/pico-sdk/**" + ], + "defines": [], + "compilerPath": "/usr/bin/gcc", + "cStandard": "gnu17", + "cppStandard": "gnu++14", + "intelliSenseMode": "linux-gcc-x64", + "configurationProvider": "ms-vscode.cmake-tools" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/bootloader/.vscode/launch.json b/bootloader/.vscode/launch.json new file mode 100644 index 0000000..adb4f07 --- /dev/null +++ b/bootloader/.vscode/launch.json @@ -0,0 +1,30 @@ +{ + "version": "0.2.0", + "configurations": [ + { "name": "Pico Debug", + "showDevDebugOutput": "raw", + "device": "RP2040", + "gdbPath": "gdb-multiarch", + "cwd": "${workspaceRoot}", + "executable": "${workspaceRoot}/bootloader.elf", + "request": "launch", + "type": "cortex-debug", + "servertype": "openocd", + "configFiles": [ + "interface/jlink.cfg", + "target/rp2040.cfg" + ], + "openOCDLaunchCommands": [ + "transport select swd", + "adapter speed 4000" + ], + "svdFile": "/home/fish/pico-sdk/src/rp2040/hardware_regs/rp2040.svd", + //"svdFile": "/home/fish/pico-sdk/src/rp2040/hardware_regs/rp2040.svd", + //"runToMain": true, + "postRestartCommands": [ + "break main", + "continue" + ] + } + ] +} \ No newline at end of file diff --git a/bootloader/.vscode/settings.json b/bootloader/.vscode/settings.json new file mode 100644 index 0000000..2e1b289 --- /dev/null +++ b/bootloader/.vscode/settings.json @@ -0,0 +1,24 @@ +{ + // These settings tweaks to the cmake plugin will ensure + // that you debug using cortex-debug instead of trying to launch + // a Pico binary on the host + "cmake.statusbar.advanced": { + "debug": { + "visibility": "hidden" + }, + "launch": { + "visibility": "hidden" + }, + "build": { + "visibility": "hidden" + }, + "buildTarget": { + "visibility": "hidden" + } + }, + "cmake.buildBeforeRun": true, + "C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools", + "cortex-debug.variableUseNaturalFormat": false, + "cortex-debug.showRTOS": true, + "cortex-debug.registerUseNaturalFormat": false +} \ No newline at end of file diff --git a/bootloader/CMakeLists.txt b/bootloader/CMakeLists.txt new file mode 100644 index 0000000..f5bffc3 --- /dev/null +++ b/bootloader/CMakeLists.txt @@ -0,0 +1,147 @@ +# Derived from the Pico SDK, which carries the following +# LICENSE.txt: +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +cmake_minimum_required(VERSION 3.13) + +include(pico_sdk_import.cmake) + +project(test_project C CXX ASM) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) + +pico_sdk_init() + +# Build the bootloader as a standalone thing + +add_executable(bootloader main.c) + +function(target_cl_options option) + target_compile_options(bootloader PRIVATE ${option}) + target_link_options(bootloader PRIVATE ${option}) +endfunction() + +add_compile_options(-g -O0) + +target_cl_options("-O0") +target_cl_options("-g") +target_cl_options("-ffunction-sections") +target_cl_options("-fdata-sections") +target_link_options(bootloader PRIVATE "LINKER:--gc-sections") + +pico_add_extra_outputs(bootloader) +pico_set_binary_type(bootloader copy_to_ram) + +set_target_properties(bootloader PROPERTIES COMPILE_FLAGS "-Wall -g -O0") + +pico_set_linker_script(bootloader ${CMAKE_CURRENT_SOURCE_DIR}/bootloader.ld) + +target_link_libraries(bootloader + pico_stdlib + hardware_dma + hardware_flash + hardware_structs + hardware_resets + cmsis_core) + +set(BOOTLOADER_DIR "${CMAKE_CURRENT_LIST_DIR}" CACHE INTERNAL "") + +#Build a library to embed into applications + +function(bootloader_define_library) + set(NAME bootloader) + set(ORIGINAL_BIN ${CMAKE_CURRENT_BINARY_DIR}/${NAME}.bin) + set(BIN_ASM ${CMAKE_CURRENT_BINARY_DIR}/${NAME}_bin.S) + + add_custom_target(${NAME}_bin DEPENDS ${ORIGINAL_BIN}) + add_custom_command(OUTPUT ${ORIGINAL_BIN} DEPENDS ${NAME} COMMAND ${CMAKE_OBJCOPY} -Obinary $ ${ORIGINAL_BIN}) + + find_package (Python3 REQUIRED COMPONENTS Interpreter) + add_custom_target(${NAME}_bin_asm DEPENDS ${BIN_ASM}) + add_custom_command(OUTPUT ${BIN_ASM} DEPENDS ${ORIGINAL_BIN} + COMMAND ${Python3_EXECUTABLE} ${BOOTLOADER_DIR}/mkasm.py ${ORIGINAL_BIN} ${BIN_ASM} + ) + + add_library(${NAME}_library INTERFACE) + add_dependencies(${NAME}_library ${NAME}_bin_asm) + # not strictly (or indeed actually) a link library, but this avoids dependency cycle + target_link_libraries(${NAME}_library INTERFACE ${BIN_ASM}) +endfunction() + +bootloader_define_library() + +# Provide a helper to build a combined target + +function(bootloader_build_combined NAME) + set(APP ${NAME}_app) + set(APP_BIN ${CMAKE_CURRENT_BINARY_DIR}/${APP}.bin) + set(APP_HDR ${CMAKE_CURRENT_BINARY_DIR}/${APP}_hdr.bin) + + set(COMBINED ${NAME}_combined) + + target_link_libraries(${NAME} bootloader_library) + add_compile_options(-g -O0) + + target_cl_options("-O0") + target_cl_options("-g") + + pico_set_linker_script(${NAME} ${BOOTLOADER_DIR}/combined.ld) + + pico_add_bin_output(${NAME}) + + # TODO: The hard-coded 16k here is a bit nasty + add_custom_target(${APP}_bin DEPENDS ${APP_BIN}) + add_custom_command(OUTPUT ${APP_BIN} DEPENDS ${NAME}.bin + COMMAND dd ibs=1k seek=36 if=${NAME}.bin of=${APP_BIN} + ) + + # TODO: The hard-coded address here is a bit nasty + add_custom_target(${APP}_hdr DEPENDS ${APP}_bin) + add_custom_command(OUTPUT ${APP_HDR} DEPENDS ${APP_BIN} + #COMMAND ${BOOTLOADER_DIR}/gen_imghdr.py -a 0x1000a000 ${APP_BIN} ${APP_HDR} + COMMAND ${BOOTLOADER_DIR}/gen_imghdr.py -a 0x1000b000 ${APP_BIN} ${APP_HDR} + ) + + add_custom_target(${COMBINED} ALL DEPENDS ${APP_HDR}) + add_custom_command(TARGET ${COMBINED} DEPENDS ${APP_HDR} + COMMAND ${CMAKE_OBJCOPY} --update-section .app_hdr=${APP_HDR} ${NAME}.elf ${COMBINED}.elf + ) + add_custom_command(TARGET ${COMBINED} POST_BUILD + COMMAND ${CMAKE_OBJCOPY} -Obinary ${COMBINED}.elf ${COMBINED}.bin + ) +endfunction() + +# Provide a helper to build a standalone target + +function(bootloader_build_standalone NAME) + pico_set_linker_script(${NAME} ${BOOTLOADER_DIR}/standalone.ld) + pico_add_bin_output(${NAME}) +endfunction() + +# project(blink) +# add_executable(blink blink.c) +# target_link_libraries(blink pico_stdlib) +# set_target_properties(blink PROPERTIES COMPILE_FLAGS "-Wall -g -O0") +# pico_add_extra_outputs(blink) +# bootloader_build_combined(blink) \ No newline at end of file diff --git a/bootloader/bootloader.ld b/bootloader/bootloader.ld new file mode 100644 index 0000000..5118020 --- /dev/null +++ b/bootloader/bootloader.ld @@ -0,0 +1,254 @@ +/* Based on GCC ARM embedded samples. + Defines the following symbols for use by code: + __exidx_start + __exidx_end + __etext + __data_start__ + __preinit_array_start + __preinit_array_end + __init_array_start + __init_array_end + __fini_array_start + __fini_array_end + __data_end__ + __bss_start__ + __bss_end__ + __end__ + end + __HeapLimit + __StackLimit + __StackTop + __stack (== StackTop) +*/ + +/* Limit flash to 12k, so we can use 12-16k for the image header */ +MEMORY +{ + FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 36k + RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k + SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k + SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k +} + +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 (*(.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 (COPY): { + *(.ram_vector_table) + } > 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); + + + .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); + /* All data end */ + __data_end__ = .; + } > RAM AT> FLASH + /* __etext is the name of the .data init source pointer (...) */ + __etext = LOADADDR(.data); + + .uninitialized_data (COPY): { + . = ALIGN(4); + *(.uninitialized_data*) + } > 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); + + .bss : { + . = ALIGN(4); + __bss_start__ = .; + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*))) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } > RAM + + .heap (COPY): + { + __end__ = .; + end = __end__; + *(.heap*) + __HeapLimit = .; + } > RAM + + /* .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 (COPY): + { + *(.stack1*) + } > SCRATCH_X + .stack_dummy (COPY): + { + *(.stack*) + } > SCRATCH_Y + + .flash_end : { + __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); + + /* 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/bootloader/cmake_install.cmake b/bootloader/cmake_install.cmake new file mode 100644 index 0000000..8846cd4 --- /dev/null +++ b/bootloader/cmake_install.cmake @@ -0,0 +1,50 @@ +# Install script for directory: /home/fish/rp2040-serial-bootloader + +# Set the install prefix +if(NOT DEFINED CMAKE_INSTALL_PREFIX) + set(CMAKE_INSTALL_PREFIX "/usr/local") +endif() +string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") + +# Set the install configuration name. +if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME) + if(BUILD_TYPE) + string(REGEX REPLACE "^[^A-Za-z0-9_]+" "" + CMAKE_INSTALL_CONFIG_NAME "${BUILD_TYPE}") + else() + set(CMAKE_INSTALL_CONFIG_NAME "Release") + endif() + message(STATUS "Install configuration: \"${CMAKE_INSTALL_CONFIG_NAME}\"") +endif() + +# Set the component getting installed. +if(NOT CMAKE_INSTALL_COMPONENT) + if(COMPONENT) + message(STATUS "Install component: \"${COMPONENT}\"") + set(CMAKE_INSTALL_COMPONENT "${COMPONENT}") + else() + set(CMAKE_INSTALL_COMPONENT) + endif() +endif() + +# Is this installation the result of a crosscompile? +if(NOT DEFINED CMAKE_CROSSCOMPILING) + set(CMAKE_CROSSCOMPILING "TRUE") +endif() + +if(NOT CMAKE_INSTALL_LOCAL_ONLY) + # Include the install script for each subdirectory. + include("/home/fish/rp2040-serial-bootloader/pico-sdk/cmake_install.cmake") + +endif() + +if(CMAKE_INSTALL_COMPONENT) + set(CMAKE_INSTALL_MANIFEST "install_manifest_${CMAKE_INSTALL_COMPONENT}.txt") +else() + set(CMAKE_INSTALL_MANIFEST "install_manifest.txt") +endif() + +string(REPLACE ";" "\n" CMAKE_INSTALL_MANIFEST_CONTENT + "${CMAKE_INSTALL_MANIFEST_FILES}") +file(WRITE "/home/fish/rp2040-serial-bootloader/${CMAKE_INSTALL_MANIFEST}" + "${CMAKE_INSTALL_MANIFEST_CONTENT}") diff --git a/bootloader/combined.ld b/bootloader/combined.ld new file mode 100644 index 0000000..0327a6a --- /dev/null +++ b/bootloader/combined.ld @@ -0,0 +1,260 @@ +/* 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) +*/ + +/* Skip 16kB at the start of flash, that's where our bootloader is */ +MEMORY +{ + FLASH_BL(rx) : ORIGIN = 0x10000000, LENGTH = 36k + FLASH_IMGHDR(rx) : ORIGIN = 0x10000000 + 36k, LENGTH = 4k + FLASH_APP(rx) : ORIGIN = 0x10000000 + 40k, LENGTH = 2048k - 40k + RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k + SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k + SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k +} + +ENTRY(_entry_point) + +SECTIONS +{ + .flash_begin : { + __flash_binary_start = .; + } > FLASH_APP + + /* Insert boot3, which is the combined boot2 + boot3 */ + .boot3 : { + KEEP (*(.boot3)) + } > FLASH_BL + + /* + * Name a section for the image header. + * The contents will get replaced post-build + */ + .app_hdr : { + LONG(0xdeaddead) + LONG(0) + LONG(0xdeaddead) + } > FLASH_IMGHDR + + .text : { + __logical_binary_start = .; + KEEP (*(.vectors)) + KEEP (*(.binary_info_header)) + __binary_info_header_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) + + *(.eh_frame*) + . = ALIGN(4); + } > FLASH_APP + + .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_APP + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > FLASH_APP + + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > FLASH_APP + __exidx_end = .; + + /* Machine inspectable binary information */ + . = ALIGN(4); + __binary_info_start = .; + .binary_info : + { + KEEP(*(.binary_info.keep.*)) + *(.binary_info.*) + } > FLASH_APP + __binary_info_end = .; + . = ALIGN(4); + + /* End of .text-like segments */ + __etext = .; + + .ram_vector_table (COPY): { + *(.ram_vector_table) + } > 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); + /* 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); + /* All data end */ + __data_end__ = .; + } > RAM AT> FLASH_APP + + .uninitialized_data (COPY): { + . = ALIGN(4); + *(.uninitialized_data*) + } > 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_APP + __scratch_x_source__ = LOADADDR(.scratch_x); + + .scratch_y : { + __scratch_y_start__ = .; + *(.scratch_y.*) + . = ALIGN(4); + __scratch_y_end__ = .; + } > SCRATCH_Y AT > FLASH_APP + __scratch_y_source__ = LOADADDR(.scratch_y); + + /* Global Offset Table is relocated to RAM. */ + .got : + { + . = ALIGN(4); + __ram_global_offset_table_begin = . ; + *(.got) + __ram_global_offset_table_end = . ; + } >RAM AT> FLASH_APP + __flash_global_offset_table_begin = LOADADDR(.got) ; + __flash_global_offset_table_end = __flash_global_offset_table_begin + __ram_global_offset_table_end - __ram_global_offset_table_begin ; + + .bss : { + . = ALIGN(4); + __bss_start__ = .; + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*))) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } > RAM + + .heap (COPY): + { + __end__ = .; + end = __end__; + *(.heap*) + __HeapLimit = .; + } > RAM + + /* .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 (COPY): + { + *(.stack1*) + } > SCRATCH_X + .stack_dummy (COPY): + { + *(.stack*) + } > SCRATCH_Y + + .flash_end : { + __flash_binary_end = .; + } > FLASH_APP + + /* 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); + + /* 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/bootloader/gen_imghdr.py b/bootloader/gen_imghdr.py new file mode 100755 index 0000000..55911b0 --- /dev/null +++ b/bootloader/gen_imghdr.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +# Derived from pad_checksum in the Pico SDK, which carries the following +# LICENSE.txt: +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import argparse +import binascii +import struct +import sys + +def any_int(x): + try: + return int(x, 0) + except: + raise argparse.ArgumentTypeError("expected an integer, not '{!r}'".format(x)) + +parser = argparse.ArgumentParser() +parser.add_argument("ifile", help="Input application binary (binary)") +parser.add_argument("ofile", help="Output header file (binary)") +parser.add_argument("-a", "--addr", help="Load address of the application image", + type=any_int, default=0x1000a000) +args = parser.parse_args() + +try: + idata = open(args.ifile, "rb").read() +except: + sys.exit("Could not open input file '{}'".format(args.ifile)) + +vtor = args.addr +size = len(idata) +crc = binascii.crc32(idata) + +odata = vtor.to_bytes(4, byteorder='little') + size.to_bytes(4, byteorder='little') + crc.to_bytes(4, byteorder='little') + +try: + with open(args.ofile, "wb") as ofile: + ofile.write(odata) +except: + sys.exit("Could not open output file '{}'".format(args.ofile)) diff --git a/bootloader/main.c b/bootloader/main.c new file mode 100644 index 0000000..b86df65 --- /dev/null +++ b/bootloader/main.c @@ -0,0 +1,773 @@ +/** + * Copyright (c) 2021 Brian Starkey + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include +#include + +#include "RP2040.h" +#include "pico/time.h" +#include "hardware/dma.h" +#include "hardware/flash.h" +#include "hardware/structs/dma.h" +#include "hardware/structs/watchdog.h" +#include "hardware/gpio.h" +#include "hardware/resets.h" +#include "hardware/uart.h" +#include "hardware/watchdog.h" + +#ifdef DEBUG +#include +#include "pico/stdio_usb.h" +#define DBG_PRINTF_INIT() stdio_usb_init() +#define DBG_PRINTF(...) printf(__VA_ARGS__) +#else +#define DBG_PRINTF_INIT() { } +#define DBG_PRINTF(...) { } +#endif + +// The bootloader can be entered in three ways: +// - BOOTLOADER_ENTRY_PIN is low +// - Watchdog scratch[5] == BOOTLOADER_ENTRY_MAGIC && scratch[6] == ~BOOTLOADER_ENTRY_MAGIC +// - No valid image header +#define BOOTLOADER_ENTRY_PIN 15 +#define BOOTLOADER_ENTRY_MAGIC 0xb105f00d + +#define UART_TX_PIN 0 +#define UART_RX_PIN 1 +#define UART_BAUD 921600 + +#define CMD_SYNC (('S' << 0) | ('Y' << 8) | ('N' << 16) | ('C' << 24)) +#define CMD_READ (('R' << 0) | ('E' << 8) | ('A' << 16) | ('D' << 24)) +#define CMD_CSUM (('C' << 0) | ('S' << 8) | ('U' << 16) | ('M' << 24)) +#define CMD_CRC (('C' << 0) | ('R' << 8) | ('C' << 16) | ('C' << 24)) +#define CMD_ERASE (('E' << 0) | ('R' << 8) | ('A' << 16) | ('S' << 24)) +#define CMD_WRITE (('W' << 0) | ('R' << 8) | ('I' << 16) | ('T' << 24)) +#define CMD_SEAL (('S' << 0) | ('E' << 8) | ('A' << 16) | ('L' << 24)) +#define CMD_GO (('G' << 0) | ('O' << 8) | ('G' << 16) | ('O' << 24)) +#define CMD_INFO (('I' << 0) | ('N' << 8) | ('F' << 16) | ('O' << 24)) +#define CMD_REBOOT (('B' << 0) | ('O' << 8) | ('O' << 16) | ('T' << 24)) + +#define RSP_SYNC (('P' << 0) | ('I' << 8) | ('C' << 16) | ('O' << 24)) +#define RSP_OK (('O' << 0) | ('K' << 8) | ('O' << 16) | ('K' << 24)) +#define RSP_ERR (('E' << 0) | ('R' << 8) | ('R' << 16) | ('!' << 24)) + +#define IMAGE_HEADER_OFFSET (36 * 1024) + +#define WRITE_ADDR_MIN (XIP_BASE + IMAGE_HEADER_OFFSET + FLASH_SECTOR_SIZE) +#define ERASE_ADDR_MIN (XIP_BASE + IMAGE_HEADER_OFFSET) +#define FLASH_ADDR_MAX (XIP_BASE + PICO_FLASH_SIZE_BYTES) + +static void disable_interrupts(void) +{ + SysTick->CTRL &= ~1; + + NVIC->ICER[0] = 0xFFFFFFFF; + NVIC->ICPR[0] = 0xFFFFFFFF; +} + +static void reset_peripherals(void) +{ + reset_block(~( + RESETS_RESET_IO_QSPI_BITS | + RESETS_RESET_PADS_QSPI_BITS | + RESETS_RESET_SYSCFG_BITS | + RESETS_RESET_PLL_SYS_BITS + )); +} + +static void jump_to_vtor(uint32_t vtor) +{ + // Derived from the Leaf Labs Cortex-M3 bootloader. + // Copyright (c) 2010 LeafLabs LLC. + // Modified 2021 Brian Starkey + // Originally under The MIT License + // vtor += 0x512; + uint32_t offset = 0x1000; + uint32_t reset_vector = *(volatile uint32_t *)(vtor + 0x04) + offset; + + // Everything set; time to store addresses bootloader is going to pass to + // firmware via registers. After this the firmware knows what should be done and + // it does things (system memory remapping, vector table things, + // global offset table operations) autonomously. + + // Store firmware absolute address to r10 (hoop in case we have limited Cortex-M0) + asm ("ldr r6, %0; mov r10, r6" + :"=m"(offset) + : + :); + + // Store firmware offset to r11 (hoop in case we have limited Cortex-M0) + asm ("ldr r6, %0; mov r11, r6;" + :"=m"(reset_vector) + : + :); + + + //SCB->VTOR = (volatile uint32_t)(vtor); + + asm volatile("msr msp, %0"::"g" + (*(volatile uint32_t *)vtor)); + asm volatile("bx %0"::"r" (reset_vector)); +} + +static uint32_t handle_sync(uint32_t *args_in, uint8_t *data_in, uint32_t *resp_args_out, uint8_t *resp_data_out); +static uint32_t size_read(uint32_t *args_in, uint32_t *data_len_out, uint32_t *resp_data_len_out); +static uint32_t handle_read(uint32_t *args_in, uint8_t *data_in, uint32_t *resp_args_out, uint8_t *resp_data_out); +static uint32_t size_csum(uint32_t *args_in, uint32_t *data_len_out, uint32_t *resp_data_len_out); +static uint32_t handle_csum(uint32_t *args_in, uint8_t *data_in, uint32_t *resp_args_out, uint8_t *resp_data_out); +static uint32_t size_crc(uint32_t *args_in, uint32_t *data_len_out, uint32_t *resp_data_len_out); +static uint32_t handle_crc(uint32_t *args_in, uint8_t *data_in, uint32_t *resp_args_out, uint8_t *resp_data_out); +static uint32_t handle_erase(uint32_t *args_in, uint8_t *data_in, uint32_t *resp_args_out, uint8_t *resp_data_out); +static uint32_t size_write(uint32_t *args_in, uint32_t *data_len_out, uint32_t *resp_data_len_out); +static uint32_t handle_write(uint32_t *args_in, uint8_t *data_in, uint32_t *resp_args_out, uint8_t *resp_data_out); +static uint32_t handle_seal(uint32_t *args_in, uint8_t *data_in, uint32_t *resp_args_out, uint8_t *resp_data_out); +static uint32_t handle_go(uint32_t *args_in, uint8_t *data_in, uint32_t *resp_args_out, uint8_t *resp_data_out); +static uint32_t handle_info(uint32_t *args_in, uint8_t *data_in, uint32_t *resp_args_out, uint8_t *resp_data_out); +static uint32_t size_reboot(uint32_t *args_in, uint32_t *data_len_out, uint32_t *resp_data_len_out); +static uint32_t handle_reboot(uint32_t *args_in, uint8_t *data_in, uint32_t *resp_args_out, uint8_t *resp_data_out); + +struct command_desc { + uint32_t opcode; + uint32_t nargs; + uint32_t resp_nargs; + uint32_t (*size)(uint32_t *args_in, uint32_t *data_len_out, uint32_t *resp_data_len_out); + uint32_t (*handle)(uint32_t *args_in, uint8_t *data_in, uint32_t *resp_args_out, uint8_t *resp_data_out); +}; + +const struct command_desc cmds[] = { + { + .opcode = CMD_SYNC, + .nargs = 0, + .resp_nargs = 0, + .size = NULL, + .handle = &handle_sync, + }, + { + // READ addr len + // OKOK [data] + .opcode = CMD_READ, + .nargs = 2, + .resp_nargs = 0, + .size = &size_read, + .handle = &handle_read, + }, + { + // CSUM addr len + // OKOK csum + .opcode = CMD_CSUM, + .nargs = 2, + .resp_nargs = 1, + .size = &size_csum, + .handle = &handle_csum, + }, + { + // CRCC addr len + // OKOK crc + .opcode = CMD_CRC, + .nargs = 2, + .resp_nargs = 1, + .size = &size_crc, + .handle = &handle_crc, + }, + { + // ERAS addr len + // OKOK + .opcode = CMD_ERASE, + .nargs = 2, + .resp_nargs = 0, + .size = NULL, + .handle = &handle_erase, + }, + { + // WRIT addr len [data] + // OKOK crc + .opcode = CMD_WRITE, + .nargs = 2, + .resp_nargs = 1, + .size = &size_write, + .handle = &handle_write, + }, + { + // SEAL vtor len crc + // OKOK + .opcode = CMD_SEAL, + .nargs = 3, + .resp_nargs = 0, + .size = NULL, + .handle = &handle_seal, + }, + { + // GOGO vtor + // NO RESPONSE + .opcode = CMD_GO, + .nargs = 1, + .resp_nargs = 0, + .size = NULL, + .handle = &handle_go, + }, + { + // INFO + // OKOK flash_start flash_size erase_size write_size max_data_len + .opcode = CMD_INFO, + .nargs = 0, + .resp_nargs = 5, + .size = NULL, + .handle = &handle_info, + }, + { + // BOOT to_bootloader + // NO RESPONSE + .opcode = CMD_REBOOT, + .nargs = 1, + .resp_nargs = 0, + .size = &size_reboot, + .handle = &handle_reboot, + }, +}; +const unsigned int N_CMDS = (sizeof(cmds) / sizeof(cmds[0])); +const uint32_t MAX_NARG = 5; +const uint32_t MAX_DATA_LEN = 1024; //FLASH_SECTOR_SIZE; + +static bool is_error(uint32_t status) +{ + return status == RSP_ERR; +} + +static uint32_t handle_sync(uint32_t *args_in, uint8_t *data_in, uint32_t *resp_args_out, uint8_t *resp_data_out) +{ + return RSP_SYNC; +} + +static uint32_t size_read(uint32_t *args_in, uint32_t *data_len_out, uint32_t *resp_data_len_out) +{ + uint32_t size = args_in[1]; + if (size > MAX_DATA_LEN) { + return RSP_ERR; + } + + // TODO: Validate address + + *data_len_out = 0; + *resp_data_len_out = size; + + return RSP_OK; +} + +static uint32_t handle_read(uint32_t *args_in, uint8_t *data_in, uint32_t *resp_args_out, uint8_t *resp_data_out) +{ + uint32_t addr = args_in[0]; + uint32_t size = args_in[1]; + + memcpy(resp_data_out, (void *)addr, size); + + return RSP_OK; +} + +static uint32_t size_csum(uint32_t *args_in, uint32_t *data_len_out, uint32_t *resp_data_len_out) +{ + uint32_t addr = args_in[0]; + uint32_t size = args_in[1]; + + if ((addr & 0x3) || (size & 0x3)) { + // Must be aligned + return RSP_ERR; + } + + // TODO: Validate address + + *data_len_out = 0; + *resp_data_len_out = 0; + + return RSP_OK; +} + +static uint32_t handle_csum(uint32_t *args_in, uint8_t *data_in, uint32_t *resp_args_out, uint8_t *resp_data_out) +{ + uint32_t dummy_dest; + uint32_t addr = args_in[0]; + uint32_t size = args_in[1]; + + int channel = dma_claim_unused_channel(true); + + dma_channel_config c = dma_channel_get_default_config(channel); + channel_config_set_transfer_data_size(&c, DMA_SIZE_32); + channel_config_set_read_increment(&c, true); + channel_config_set_write_increment(&c, false); + channel_config_set_sniff_enable(&c, true); + + dma_hw->sniff_data = 0; + dma_sniffer_enable(channel, 0xf, true); + + dma_channel_configure(channel, &c, &dummy_dest, (void *)addr, size / 4, true); + + dma_channel_wait_for_finish_blocking(channel); + + dma_sniffer_disable(); + dma_channel_unclaim(channel); + + *resp_args_out = dma_hw->sniff_data; + + return RSP_OK; +} + +static uint32_t size_crc(uint32_t *args_in, uint32_t *data_len_out, uint32_t *resp_data_len_out) +{ + uint32_t addr = args_in[0]; + uint32_t size = args_in[1]; + + if ((addr & 0x3) || (size & 0x3)) { + // Must be aligned + return RSP_ERR; + } + + // TODO: Validate address + + *data_len_out = 0; + *resp_data_len_out = 0; + + return RSP_OK; +} + +// ptr must be 4-byte aligned and len must be a multiple of 4 +static uint32_t calc_crc32(void *ptr, uint32_t len) +{ + uint32_t dummy_dest, crc; + + int channel = dma_claim_unused_channel(true); + dma_channel_config c = dma_channel_get_default_config(channel); + channel_config_set_transfer_data_size(&c, DMA_SIZE_32); + channel_config_set_read_increment(&c, true); + channel_config_set_write_increment(&c, false); + channel_config_set_sniff_enable(&c, true); + + // Seed the CRC calculation + dma_hw->sniff_data = 0xffffffff; + + // Mode 1, then bit-reverse the result gives the same result as + // golang's IEEE802.3 implementation + dma_sniffer_enable(channel, 0x1, true); + dma_hw->sniff_ctrl |= DMA_SNIFF_CTRL_OUT_REV_BITS; + + dma_channel_configure(channel, &c, &dummy_dest, ptr, len / 4, true); + + dma_channel_wait_for_finish_blocking(channel); + + // Read the result before resetting + crc = dma_hw->sniff_data ^ 0xffffffff; + + dma_sniffer_disable(); + dma_channel_unclaim(channel); + + return crc; +} + +static uint32_t handle_crc(uint32_t *args_in, uint8_t *data_in, uint32_t *resp_args_out, uint8_t *resp_data_out) +{ + uint32_t addr = args_in[0]; + uint32_t size = args_in[1]; + + resp_args_out[0] = calc_crc32((void *)addr, size); + + return RSP_OK; +} + +static uint32_t handle_erase(uint32_t *args_in, uint8_t *data_in, uint32_t *resp_args_out, uint8_t *resp_data_out) +{ + uint32_t addr = args_in[0]; + uint32_t size = args_in[1]; + + if ((addr < ERASE_ADDR_MIN) || (addr + size >= FLASH_ADDR_MAX)) { + // Outside flash + return RSP_ERR; + } + + if ((addr & (FLASH_SECTOR_SIZE - 1)) || (size & (FLASH_SECTOR_SIZE - 1))) { + // Must be aligned + return RSP_ERR; + } + + flash_range_erase(addr - XIP_BASE, size); + + return RSP_OK; +} + +static uint32_t size_write(uint32_t *args_in, uint32_t *data_len_out, uint32_t *resp_data_len_out) +{ + uint32_t addr = args_in[0]; + uint32_t size = args_in[1]; + + if ((addr < WRITE_ADDR_MIN) || (addr + size >= FLASH_ADDR_MAX)) { + // Outside flash + return RSP_ERR; + } + + if ((addr & (FLASH_PAGE_SIZE - 1)) || (size & (FLASH_PAGE_SIZE -1))) { + // Must be aligned + return RSP_ERR; + } + + if (size > MAX_DATA_LEN) { + return RSP_ERR; + } + + // TODO: Validate address + + *data_len_out = size; + *resp_data_len_out = 0; + + return RSP_OK; +} + +static uint32_t handle_write(uint32_t *args_in, uint8_t *data_in, uint32_t *resp_args_out, uint8_t *resp_data_out) +{ + uint32_t addr = args_in[0]; + uint32_t size = args_in[1]; + + flash_range_program(addr - XIP_BASE, data_in, size); + + resp_args_out[0] = calc_crc32((void *)addr, size); + + return RSP_OK; +} + +struct image_header { + uint32_t vtor; + uint32_t size; + uint32_t crc; + uint8_t pad[FLASH_PAGE_SIZE - (3 * 4)]; +}; +static_assert(sizeof(struct image_header) == FLASH_PAGE_SIZE, "image_header must be FLASH_PAGE_SIZE bytes"); + +static bool image_header_ok(struct image_header *hdr) +{ + uint32_t *vtor = (uint32_t *)hdr->vtor; + + //uint32_t calc = calc_crc32((void *)hdr->vtor, hdr->size); + uint32_t calc = hdr->crc; + + // CRC has to match + if (calc != hdr->crc) { + return false; + } + + // Stack pointer needs to be in RAM + if (vtor[0] < SRAM_BASE) { + return false; + } + + // Reset vector should be in the image, and thumb (bit 0 set) + //if ((vtor[1] < hdr->vtor) || (vtor[1] > hdr->vtor + hdr->size) || !(vtor[1] & 1)) { + // return false; + //} + + // Looks OK. + return true; +} + + +static uint32_t handle_seal(uint32_t *args_in, uint8_t *data_in, uint32_t *resp_args_out, uint8_t *resp_data_out) +{ + struct image_header hdr = { + .vtor = args_in[0], + .size = args_in[1], + .crc = args_in[2], + }; + + if ((hdr.vtor & 0xff) || (hdr.size & 0x3)) { + // Must be aligned + return RSP_ERR; + } + + if (!image_header_ok(&hdr)) { + return RSP_ERR; + } + + flash_range_erase(IMAGE_HEADER_OFFSET, FLASH_SECTOR_SIZE); + flash_range_program(IMAGE_HEADER_OFFSET, (const uint8_t *)&hdr, sizeof(hdr)); + + struct image_header *check = (struct image_header *)(XIP_BASE + IMAGE_HEADER_OFFSET); + if (memcmp(&hdr, check, sizeof(hdr))) { + return RSP_ERR; + } + + return RSP_OK; +} + +static uint32_t handle_go(uint32_t *args_in, uint8_t *data_in, uint32_t *resp_args_out, uint8_t *resp_data_out) +{ + disable_interrupts(); + + reset_peripherals(); + + jump_to_vtor(args_in[0]); + + while(1); + + return RSP_ERR; +} + +static uint32_t handle_info(uint32_t *args_in, uint8_t *data_in, uint32_t *resp_args_out, uint8_t *resp_data_out) +{ + resp_args_out[0] = WRITE_ADDR_MIN; + resp_args_out[1] = (XIP_BASE + PICO_FLASH_SIZE_BYTES) - WRITE_ADDR_MIN; + resp_args_out[2] = FLASH_SECTOR_SIZE; + resp_args_out[3] = FLASH_PAGE_SIZE; + resp_args_out[4] = MAX_DATA_LEN; + + return RSP_OK; +} + +static void do_reboot(bool to_bootloader) +{ + hw_clear_bits(&watchdog_hw->ctrl, WATCHDOG_CTRL_ENABLE_BITS); + if (to_bootloader) { + watchdog_hw->scratch[5] = BOOTLOADER_ENTRY_MAGIC; + watchdog_hw->scratch[6] = ~BOOTLOADER_ENTRY_MAGIC; + } else { + watchdog_hw->scratch[5] = 0; + watchdog_hw->scratch[6] = 0; + } + watchdog_reboot(0, 0, 0); + while (1) { + tight_loop_contents(); + asm(""); + } +} + +static uint32_t size_reboot(uint32_t *args_in, uint32_t *data_len_out, uint32_t *resp_data_len_out) +{ + *data_len_out = 0; + *resp_data_len_out = 0; + + return RSP_OK; +} + +static uint32_t handle_reboot(uint32_t *args_in, uint8_t *data_in, uint32_t *resp_args_out, uint8_t *resp_data_out) +{ + // Will never return + do_reboot(args_in[0]); + + return RSP_ERR; +} + +static const struct command_desc *find_command_desc(uint32_t opcode) +{ + unsigned int i; + + for (i = 0; i < N_CMDS; i++) { + if (cmds[i].opcode == opcode) { + return &cmds[i]; + } + } + + return NULL; +} + +struct cmd_context { + uint8_t *uart_buf; + const struct command_desc *desc; + uint32_t opcode; + uint32_t status; + uint32_t *args; + uint8_t *data; + uint32_t *resp_args; + uint8_t *resp_data; + uint32_t data_len; + uint32_t resp_data_len; +}; + +enum state { + STATE_WAIT_FOR_SYNC, + STATE_READ_OPCODE, + STATE_READ_ARGS, + STATE_READ_DATA, + STATE_HANDLE_DATA, + STATE_ERROR, +}; + +static enum state state_wait_for_sync(struct cmd_context *ctx) +{ + int idx = 0; + uint8_t *recv = (uint8_t *)&ctx->opcode; + uint8_t *match = (uint8_t *)&ctx->status; + + ctx->status = CMD_SYNC; + + gpio_put(PICO_DEFAULT_LED_PIN, 1); + + while (idx < sizeof(ctx->opcode)) { + uart_read_blocking(uart0, &recv[idx], 1); + gpio_xor_mask((1 << PICO_DEFAULT_LED_PIN)); + + if (recv[idx] != match[idx]) { + // Start again + idx = 0; + } else { + // Move on + idx++; + } + } + + assert(ctx->opcode == CMD_SYNC); + + return STATE_READ_ARGS; +} + +static enum state state_read_opcode(struct cmd_context *ctx) +{ + uart_read_blocking(uart0, (uint8_t *)&ctx->opcode, sizeof(ctx->opcode)); + + return STATE_READ_ARGS; +} + +static enum state state_read_args(struct cmd_context *ctx) +{ + const struct command_desc *desc = find_command_desc(ctx->opcode); + if (!desc) { + // TODO: Error handler that can do args? + ctx->status = RSP_ERR; + return STATE_ERROR; + } + + ctx->desc = desc; + ctx->args = (uint32_t *)(ctx->uart_buf + sizeof(ctx->opcode)); + ctx->data = (uint8_t *)(ctx->args + desc->nargs); + ctx->resp_args = ctx->args; + ctx->resp_data = (uint8_t *)(ctx->resp_args + desc->resp_nargs); + + uart_read_blocking(uart0, (uint8_t *)ctx->args, sizeof(*ctx->args) * desc->nargs); + + return STATE_READ_DATA; +} + +static enum state state_read_data(struct cmd_context *ctx) +{ + const struct command_desc *desc = ctx->desc; + + if (desc->size) { + ctx->status = desc->size(ctx->args, &ctx->data_len, &ctx->resp_data_len); + if (is_error(ctx->status)) { + return STATE_ERROR; + } + } else { + ctx->data_len = 0; + ctx->resp_data_len = 0; + } + + // TODO: Check sizes + + uart_read_blocking(uart0, (uint8_t *)ctx->data, ctx->data_len); + + return STATE_HANDLE_DATA; +} + +static enum state state_handle_data(struct cmd_context *ctx) +{ + const struct command_desc *desc = ctx->desc; + + if (desc->handle) { + ctx->status = desc->handle(ctx->args, ctx->data, ctx->resp_args, ctx->resp_data); + if (is_error(ctx->status)) { + return STATE_ERROR; + } + } else { + // TODO: Should we just assert(desc->handle)? + ctx->status = RSP_OK; + } + + size_t resp_len = sizeof(ctx->status) + (sizeof(*ctx->resp_args) * desc->resp_nargs) + ctx->resp_data_len; + memcpy(ctx->uart_buf, &ctx->status, sizeof(ctx->status)); + uart_write_blocking(uart0, ctx->uart_buf, resp_len); + + return STATE_READ_OPCODE; +} + +static enum state state_error(struct cmd_context *ctx) +{ + size_t resp_len = sizeof(ctx->status); + memcpy(ctx->uart_buf, &ctx->status, sizeof(ctx->status)); + uart_write_blocking(uart0, ctx->uart_buf, resp_len); + + return STATE_WAIT_FOR_SYNC; +} + +static bool should_stay_in_bootloader() +{ + bool wd_says_so = (watchdog_hw->scratch[5] == BOOTLOADER_ENTRY_MAGIC) && + (watchdog_hw->scratch[6] == ~BOOTLOADER_ENTRY_MAGIC); + + return !gpio_get(BOOTLOADER_ENTRY_PIN) || wd_says_so; +} + +int main(void) +{ + gpio_init(PICO_DEFAULT_LED_PIN); + gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); + gpio_put(PICO_DEFAULT_LED_PIN, 1); + + gpio_init(BOOTLOADER_ENTRY_PIN); + gpio_pull_up(BOOTLOADER_ENTRY_PIN); + gpio_set_dir(BOOTLOADER_ENTRY_PIN, 0); + + sleep_ms(10); + + struct image_header *hdr = (struct image_header *)(XIP_BASE + IMAGE_HEADER_OFFSET); + + if (!should_stay_in_bootloader() && image_header_ok(hdr)) { + uint32_t vtor = *((uint32_t *)(XIP_BASE + IMAGE_HEADER_OFFSET)); + disable_interrupts(); + reset_peripherals(); + jump_to_vtor(hdr->vtor); + } + + DBG_PRINTF_INIT(); + + uart_init(uart0, UART_BAUD); + gpio_set_function(UART_TX_PIN, GPIO_FUNC_UART); + gpio_set_function(UART_RX_PIN, GPIO_FUNC_UART); + uart_set_hw_flow(uart0, false, false); + + struct cmd_context ctx; + uint8_t uart_buf[(sizeof(uint32_t) * (1 + MAX_NARG)) + MAX_DATA_LEN]; + ctx.uart_buf = uart_buf; + enum state state = STATE_WAIT_FOR_SYNC; + + while (1) { + switch (state) { + case STATE_WAIT_FOR_SYNC: + DBG_PRINTF("wait_for_sync\n"); + state = state_wait_for_sync(&ctx); + DBG_PRINTF("wait_for_sync done\n"); + break; + case STATE_READ_OPCODE: + DBG_PRINTF("read_opcode\n"); + state = state_read_opcode(&ctx); + DBG_PRINTF("read_opcode done\n"); + break; + case STATE_READ_ARGS: + DBG_PRINTF("read_args\n"); + state = state_read_args(&ctx); + DBG_PRINTF("read_args done\n"); + break; + case STATE_READ_DATA: + DBG_PRINTF("read_data\n"); + state = state_read_data(&ctx); + DBG_PRINTF("read_data done\n"); + break; + case STATE_HANDLE_DATA: + DBG_PRINTF("handle_data\n"); + state = state_handle_data(&ctx); + DBG_PRINTF("handle_data done\n"); + break; + case STATE_ERROR: + DBG_PRINTF("error\n"); + state = state_error(&ctx); + DBG_PRINTF("error done\n"); + break; + } + } + + return 0; +} diff --git a/bootloader/mkasm.py b/bootloader/mkasm.py new file mode 100644 index 0000000..31a31a9 --- /dev/null +++ b/bootloader/mkasm.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +# Derived from pad_checksum in the Pico SDK, which carries the following +# LICENSE.txt: +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import argparse +import binascii +import struct +import sys + +parser = argparse.ArgumentParser() +parser.add_argument("ifile", help="Input file (binary)") +parser.add_argument("ofile", help="Output file (assembly)") +args = parser.parse_args() + +try: + idata = open(args.ifile, "rb").read() +except: + sys.exit("Could not open input file '{}'".format(args.ifile)) + +odata = idata + +try: + with open(args.ofile, "w") as ofile: + ofile.write("// ASM-ified version of: {}\n\n".format(args.ifile)) + ofile.write(".cpu cortex-m0plus\n") + ofile.write(".thumb\n\n") + ofile.write(".section .boot3, \"ax\"\n\n") + for offs in range(0, len(odata), 16): + chunk = odata[offs:min(offs + 16, len(odata))] + ofile.write(".byte {}\n".format(", ".join("0x{:02x}".format(b) for b in chunk))) +except: + sys.exit("Could not open output file '{}'".format(args.ofile)) diff --git a/bootloader/pico_sdk_import.cmake b/bootloader/pico_sdk_import.cmake new file mode 100644 index 0000000..28efe9e --- /dev/null +++ b/bootloader/pico_sdk_import.cmake @@ -0,0 +1,62 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG master + ) + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + FetchContent_Populate(pico_sdk) + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/bootloader/standalone.ld b/bootloader/standalone.ld new file mode 100644 index 0000000..34718b9 --- /dev/null +++ b/bootloader/standalone.ld @@ -0,0 +1,243 @@ +/* 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) +*/ + +/* Skip 16kB at the start of flash, that's where our bootloader is */ +MEMORY +{ + FLASH_APP(rx) : ORIGIN = 0x10000000 + 40k, LENGTH = 2048k - 40k + RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k + SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k + SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k +} + +ENTRY(_entry_point) + +SECTIONS +{ + .flash_begin : { + __flash_binary_start = .; + } > FLASH_APP + + .text : { + __logical_binary_start = .; + KEEP (*(.vectors)) + KEEP (*(.binary_info_header)) + __binary_info_header_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) + + *(.eh_frame*) + . = ALIGN(4); + } > FLASH_APP + + .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_APP + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > FLASH_APP + + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > FLASH_APP + __exidx_end = .; + + /* Machine inspectable binary information */ + . = ALIGN(4); + __binary_info_start = .; + .binary_info : + { + KEEP(*(.binary_info.keep.*)) + *(.binary_info.*) + } > FLASH_APP + __binary_info_end = .; + . = ALIGN(4); + + /* End of .text-like segments */ + __etext = .; + + .ram_vector_table (COPY): { + *(.ram_vector_table) + } > 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); + /* 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); + /* All data end */ + __data_end__ = .; + } > RAM AT> FLASH_APP + + .uninitialized_data (COPY): { + . = ALIGN(4); + *(.uninitialized_data*) + } > 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_APP + __scratch_x_source__ = LOADADDR(.scratch_x); + + .scratch_y : { + __scratch_y_start__ = .; + *(.scratch_y.*) + . = ALIGN(4); + __scratch_y_end__ = .; + } > SCRATCH_Y AT > FLASH_APP + __scratch_y_source__ = LOADADDR(.scratch_y); + + /* Global Offset Table is relocated to RAM. */ + .got : + { + . = ALIGN(4); + __ram_global_offset_table_begin = . ; + *(.got) + __ram_global_offset_table_end = . ; + } >RAM AT> FLASH_APP + __flash_global_offset_table_begin = LOADADDR(.got) ; + __flash_global_offset_table_end = __flash_global_offset_table_begin + __ram_global_offset_table_end - __ram_global_offset_table_begin ; + + .bss : { + . = ALIGN(4); + __bss_start__ = .; + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*))) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } > RAM + + .heap (COPY): + { + __end__ = .; + end = __end__; + *(.heap*) + __HeapLimit = .; + } > RAM + + /* .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 (COPY): + { + *(.stack1*) + } > SCRATCH_X + .stack_dummy (COPY): + { + *(.stack*) + } > SCRATCH_Y + + .flash_end : { + __flash_binary_end = .; + } > FLASH_APP + + /* 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); + + /* 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/image_info.c b/image_info.c new file mode 100644 index 0000000..892cb6c --- /dev/null +++ b/image_info.c @@ -0,0 +1,4 @@ +#include "image_info.h" + +// Sometimes howering might show false results in debugger, but these still have right values. +uint32_t __firmwareOffset; diff --git a/image_info.h b/image_info.h new file mode 100644 index 0000000..a3146ac --- /dev/null +++ b/image_info.h @@ -0,0 +1,9 @@ +#ifndef _IMAGE_INFO_H_ +#define _IMAGE_INFO_H_ + +#include + +// Sometimes hovering might show false results in debugger, but these still have right values. +extern uint32_t __firmwareOffset; + +#endif // #define _IMAGE_INFO_H_ \ No newline at end of file diff --git a/pico_sdk_import.cmake b/pico_sdk_import.cmake new file mode 100644 index 0000000..65f8a6f --- /dev/null +++ b/pico_sdk_import.cmake @@ -0,0 +1,73 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG master + GIT_SUBMODULES_RECURSE FALSE + ) + else () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG master + ) + endif () + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + FetchContent_Populate(pico_sdk) + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE})