For the pic branch of github/Fishwaldo/pico-sdk/

This commit is contained in:
Justin Hammond 2022-12-07 22:45:36 +08:00
commit 902f03c761
20 changed files with 2203 additions and 0 deletions

18
.vscode/c_cpp_properties.json vendored Normal file
View file

@ -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
}

30
.vscode/launch.json vendored Normal file
View file

@ -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"
]
}
]
}

23
.vscode/settings.json vendored Normal file
View file

@ -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
}

38
CMakeLists.txt Normal file
View file

@ -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)

35
blink.c Normal file
View file

@ -0,0 +1,35 @@
/**
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stdio.h>
#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
}

View file

@ -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
}

30
bootloader/.vscode/launch.json vendored Normal file
View file

@ -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"
]
}
]
}

24
bootloader/.vscode/settings.json vendored Normal file
View file

@ -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
}

147
bootloader/CMakeLists.txt Normal file
View file

@ -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 $<TARGET_FILE:${NAME}> ${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)

254
bootloader/bootloader.ld Normal file
View file

@ -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 */
}

View file

@ -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}")

260
bootloader/combined.ld Normal file
View file

@ -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 */
}

59
bootloader/gen_imghdr.py Executable file
View file

@ -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))

773
bootloader/main.c Normal file
View file

@ -0,0 +1,773 @@
/**
* Copyright (c) 2021 Brian Starkey <stark3y@gmail.com>
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stdio.h>
#include <string.h>
#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 <stdio.h>
#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 <stark3y@gmail.com>
// 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;
}

53
bootloader/mkasm.py Normal file
View file

@ -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))

View file

@ -0,0 +1,62 @@
# This is a copy of <PICO_SDK_PATH>/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})

243
bootloader/standalone.ld Normal file
View file

@ -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 */
}

4
image_info.c Normal file
View file

@ -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;

9
image_info.h Normal file
View file

@ -0,0 +1,9 @@
#ifndef _IMAGE_INFO_H_
#define _IMAGE_INFO_H_
#include <stdint.h>
// Sometimes hovering might show false results in debugger, but these still have right values.
extern uint32_t __firmwareOffset;
#endif // #define _IMAGE_INFO_H_

73
pico_sdk_import.cmake Normal file
View file

@ -0,0 +1,73 @@
# This is a copy of <PICO_SDK_PATH>/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})