diff --git a/CMakeLists.txt b/CMakeLists.txt index 421c713..d0033ca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,7 +22,7 @@ # 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) +cmake_minimum_required(VERSION 3.13...4.0) include(pico_sdk_import.cmake) @@ -53,13 +53,21 @@ pico_set_binary_type(bootloader copy_to_ram) set_target_properties(bootloader PROPERTIES COMPILE_FLAGS "-Wall") pico_set_linker_script(bootloader ${CMAKE_CURRENT_SOURCE_DIR}/bootloader.ld) - +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + target_compile_definitions(bootloader PRIVATE DEBUG) + pico_enable_stdio_usb(bootloader 1) +endif() +if(${SKIP_BOOTLOADER_ENTRY_PIN}) + message(STATUS "Skipping bootloader entry pin") + add_definitions(-DSKIP_BOOTLOADER_ENTRY_PIN) +endif() 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 "") @@ -69,15 +77,16 @@ set(BOOTLOADER_DIR "${CMAKE_CURRENT_LIST_DIR}" CACHE INTERNAL "") function(bootloader_define_library) set(NAME bootloader) set(ORIGINAL_BIN ${CMAKE_CURRENT_BINARY_DIR}/${NAME}.bin) + set(NEW_BIN ${CMAKE_CURRENT_BINARY_DIR}/${NAME}_new.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}) + add_custom_command(OUTPUT ${NEW_BIN} DEPENDS ${NAME} COMMAND ${CMAKE_OBJCOPY} -Obinary $ ${NEW_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_custom_command(OUTPUT ${BIN_ASM} DEPENDS ${NEW_BIN} + COMMAND ${Python3_EXECUTABLE} ${BOOTLOADER_DIR}/mkasm.py ${NEW_BIN} ${BIN_ASM} ) add_library(${NAME}_library INTERFACE) @@ -92,36 +101,67 @@ bootloader_define_library() function(bootloader_build_combined NAME) set(APP ${NAME}_app) - set(APP_BIN ${CMAKE_CURRENT_BINARY_DIR}/${APP}.bin) + set(APP_BIN ${CMAKE_CURRENT_BINARY_DIR}/${APP}_app.bin) set(APP_HDR ${CMAKE_CURRENT_BINARY_DIR}/${APP}_hdr.bin) set(COMBINED ${NAME}_combined) target_link_libraries(${NAME} bootloader_library) - - 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}) + if(CMAKE_BUILD_TYPE STREQUAL "Debug") + pico_set_linker_script(${NAME} ${BOOTLOADER_DIR}/combined_debug.ld) + set(SKIP 44) + else() + pico_set_linker_script(${NAME} ${BOOTLOADER_DIR}/combined.ld) + set(SKIP 16) + endif() + MATH(EXPR IMAGE_HDR 0x10000000+${SKIP}*1024) + message(STATUS "IMAGE_HDR: ${IMAGE_HDR}") + + + # pico_add_bin_output(${NAME}) + + add_custom_target(${APP}_bin DEPENDS ${APP_BIN} ${NAME}) add_custom_command(OUTPUT ${APP_BIN} DEPENDS ${NAME}.bin - COMMAND dd ibs=1k iseek=16 if=${NAME}.bin of=${APP_BIN} + COMMAND dd ibs=1k skip=${SKIP} 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 0x10004000 ${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_target(${APP}_hdr DEPENDS ${APP}_bin ${NAME}) + add_custom_command(OUTPUT ${APP_HDR} DEPENDS ${APP_BIN} ${NAME} + COMMAND ${BOOTLOADER_DIR}/gen_imghdr.py -a ${IMAGE_HDR} ${APP_BIN} ${APP_HDR} ) + if(PICO_PLATFORM STREQUAL "rp2040") + set(UF2_FAMILY "0xe48bff56") +elseif(PICO_PLATFORM STREQUAL "rp2350-arm-s") + set(UF2_FAMILY "0xe48bff59") +elseif(PICO_PLATFORM STREQUAL "rp2350-riscv") + set(UF2_FAMILY "0xe48bff5a") +else() + message(FATAL_ERROR "Unknown PICO_PLATFORM: ${PICO_PLATFORM}") +endif() + add_custom_target(${COMBINED} ALL DEPENDS ${APP_HDR} ${NAME}) add_custom_command(TARGET ${COMBINED} POST_BUILD + COMMAND "echo" "Creating ${COMBINED}.elf" + COMMAND ${CMAKE_OBJCOPY} --update-section .app_hdr=${APP_HDR} ${NAME}.elf ${COMBINED}.elf COMMAND ${CMAKE_OBJCOPY} -Obinary ${COMBINED}.elf ${COMBINED}.bin + COMMAND rm ${NAME}.uf2 > /dev/null 2>&1 || true + COMMAND picotool uf2 convert ${COMBINED}.elf ${NAME}.uf2 --family ${UF2_FAMILY} ) +# einde + + + + + # add_custom_command(TARGET ${COMBINED} POST_BUILD + # COMMAND ${CMAKE_OBJCOPY} -Obinary ${COMBINED}.elf ${COMBINED}.bin + # COMMAND picotool uf2 convert ${COMBINED}.elf ${COMBINED}.uf2 --family ${UF2_FAMILY} + # ) + # add_custom_command(TARGET ${COMBINED} POST_BUILD # This uf2 doesn't work as it has the correct ld script, but the rest wrong. + # COMMAND rm ${NAME}.uf2 > /dev/null 2>&1 || true + # ) + + # Assumes picotool is installed to convert the elf to uf2. + # Needs to know the family type to generate the correct uf2. + # add_custom_command(TARGET ${COMBINED} POST_BUILD + # ) endfunction() # Provide a helper to build a standalone target diff --git a/README.md b/README.md index 5df7895..85bf370 100644 --- a/README.md +++ b/README.md @@ -9,3 +9,23 @@ There are currently two tools I know of which can be used to upload code to it: * [serial-flash](https://github.com/usedbytes/serial-flash) - my tool written in `go` * [pico-py-serial-flash](https://github.com/ConfedSolutions/pico-py-serial-flash) - a similar tool written in Python, contributed by another user. + + +# Usage +Add this folder as a submodule to your project + +Add +``` +set(SKIP_BOOTLOADER_ENTRY_PIN 0) # skip bootloader gpio reading if wanted +add_subdirectory(rp2040-serial-bootloader) +bootloader_build_combined($PROJECT_NAME) +``` +to your CMakeLists.txt + +Run make like usual + +Output files will be +$PROJECT_NAME_combined.elf/.uf2/.bin + +The uf2 you can flash like normal with picotool. +The normal uf2 \ No newline at end of file diff --git a/bootloader.ld b/bootloader.ld index c82585d..123798f 100644 --- a/bootloader.ld +++ b/bootloader.ld @@ -21,10 +21,11 @@ __stack (== StackTop) */ -/* Limit flash to 12k, so we can use 12-16k for the image header */ +bootloader_size = 40k; /* 4KB stack size */ +/* Limit flash to 40k, so we can use 40-44k for the image header */ MEMORY { - FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 12k + FLASH(rx) : ORIGIN = 0x10000000, LENGTH = bootloader_size RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k @@ -211,7 +212,7 @@ SECTIONS { __end__ = .; end = __end__; - *(.heap*) + KEEP(*(.heap*)) __HeapLimit = .; } > RAM @@ -226,11 +227,11 @@ SECTIONS */ .stack1_dummy (COPY): { - *(.stack1*) + KEEP(*(.stack1*)) } > SCRATCH_X .stack_dummy (COPY): { - *(.stack*) + KEEP(*(.stack*)) } > SCRATCH_Y .flash_end : { diff --git a/combined.ld b/combined.ld index 761e360..a2472bf 100644 --- a/combined.ld +++ b/combined.ld @@ -21,12 +21,14 @@ __stack (== StackTop) */ +bootloader_size = 12k; /* 4KB stack size */ +header_size = 4k; /* Skip 16kB at the start of flash, that's where our bootloader is */ MEMORY { - FLASH_BL(rx) : ORIGIN = 0x10000000, LENGTH = 12k - FLASH_IMGHDR(rx) : ORIGIN = 0x10000000 + 12k, LENGTH = 4k - FLASH_APP(rx) : ORIGIN = 0x10000000 + 16k, LENGTH = 2048k - 16k + FLASH_BL(rx) : ORIGIN = 0x10000000, LENGTH = bootloader_size + FLASH_IMGHDR(rx) : ORIGIN = 0x10000000 + bootloader_size, LENGTH = 4k + FLASH_APP(rx) : ORIGIN = 0x10000000 + bootloader_size + header_size, LENGTH = 2048k - bootloader_size - header_size RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k @@ -206,7 +208,7 @@ SECTIONS { __end__ = .; end = __end__; - *(.heap*) + KEEP(*(.heap*)) __HeapLimit = .; } > RAM @@ -221,11 +223,11 @@ SECTIONS */ .stack1_dummy (COPY): { - *(.stack1*) + KEEP(*(.stack1*)) } > SCRATCH_X .stack_dummy (COPY): { - *(.stack*) + KEEP(*(.stack*)) } > SCRATCH_Y .flash_end : { diff --git a/combined_debug.ld b/combined_debug.ld new file mode 100644 index 0000000..ab0fe17 --- /dev/null +++ b/combined_debug.ld @@ -0,0 +1,251 @@ +/* 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) +*/ + +bootloader_size = 40k; /* debug: 40k, normal 12k */ +header_size = 4k; +/* Skip 16kB at the start of flash, that's where our bootloader is */ +MEMORY +{ + FLASH_BL(rx) : ORIGIN = 0x10000000, LENGTH = bootloader_size + FLASH_IMGHDR(rx) : ORIGIN = 0x10000000 + bootloader_size, LENGTH = 4k + FLASH_APP(rx) : ORIGIN = 0x10000000 + bootloader_size + header_size, LENGTH = 2048k - bootloader_size - header_size + 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); + + .bss : { + . = ALIGN(4); + __bss_start__ = .; + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*))) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } > RAM + + .heap (COPY): + { + __end__ = .; + end = __end__; + KEEP(*(.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): + { + KEEP(*(.stack1*)) + } > SCRATCH_X + .stack_dummy (COPY): + { + KEEP(*(.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/main.c b/main.c index 86e7fe3..7e2d805 100644 --- a/main.c +++ b/main.c @@ -5,8 +5,21 @@ */ #include #include - +#if PICO_RP2350 +// doesn't work for RP2350 as the bootloader.ld is incorrect for it. +#ifdef __riscv + // +#else +#include "RP2350.h" +#endif +#else #include "RP2040.h" + #endif +// #if PICO_PLATFORM==rp2040 +// #endif +// #if PICO_PLATFORM==rp2350 +// #include "RP2350.h" +// #endif #include "pico/time.h" #include "hardware/dma.h" #include "hardware/flash.h" @@ -16,7 +29,6 @@ #include "hardware/resets.h" #include "hardware/uart.h" #include "hardware/watchdog.h" - #ifdef DEBUG #include #include "pico/stdio_usb.h" @@ -53,7 +65,12 @@ #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 (12 * 1024) +#ifdef DEBUG +#define BOOTLOADER_SECTIONS 40 +#else +#define BOOTLOADER_SECTIONS 12 +#endif +#define IMAGE_HEADER_OFFSET (BOOTLOADER_SECTIONS * 1024) #define WRITE_ADDR_MIN (XIP_BASE + IMAGE_HEADER_OFFSET + FLASH_SECTOR_SIZE) #define ERASE_ADDR_MIN (XIP_BASE + IMAGE_HEADER_OFFSET) @@ -615,7 +632,6 @@ static enum state state_read_args(struct cmd_context *ctx) 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; @@ -676,8 +692,13 @@ static bool should_stay_in_bootloader() { bool wd_says_so = (watchdog_hw->scratch[5] == BOOTLOADER_ENTRY_MAGIC) && (watchdog_hw->scratch[6] == ~BOOTLOADER_ENTRY_MAGIC); + #ifndef SKIP_BOOTLOADER_ENTRY_PIN + bool pin_says_so = !gpio_get(BOOTLOADER_ENTRY_PIN); + #else + bool pin_says_so = false; + #endif - return !gpio_get(BOOTLOADER_ENTRY_PIN) || wd_says_so; + return pin_says_so || wd_says_so; } int main(void) @@ -686,10 +707,11 @@ int main(void) gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); gpio_put(PICO_DEFAULT_LED_PIN, 1); + #ifndef SKIP_BOOTLOADER_ENTRY_PIN gpio_init(BOOTLOADER_ENTRY_PIN); gpio_pull_up(BOOTLOADER_ENTRY_PIN); gpio_set_dir(BOOTLOADER_ENTRY_PIN, 0); - + #endif sleep_ms(10); struct image_header *hdr = (struct image_header *)(XIP_BASE + IMAGE_HEADER_OFFSET); diff --git a/standalone.ld b/standalone.ld index 448f834..fc53dca 100644 --- a/standalone.ld +++ b/standalone.ld @@ -21,7 +21,7 @@ __stack (== StackTop) */ -/* Skip 16kB at the start of flash, that's where our bootloader is */ +/* Skip 16kB at the start of flash, that's where our bootloader is (40 when bootloader is in debug mode) */ MEMORY { FLASH(rx) : ORIGIN = 0x10000000 + 16k, LENGTH = 2048k - 16k