diff --git a/.vscode/settings.json b/.vscode/settings.json index 1020abe..f10d07f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -41,14 +41,22 @@ "charge_mode.h": "c", "http_rest.h": "c", "motors.h": "c", - "eeprom.h": "c", - "stdbool.h": "c", - "configuration.h": "c", - "uart.h": "c", - "common.h": "c", - "clocks.h": "c", - "pwm.h": "c", - "servo_gate.h": "c", - "semphr.h": "c" + "defs.h": "c", + "code.h": "c", + "binary_info.h": "c", + "structure.h": "c", + "stepper.pio.h": "c", + "app_header.h": "c", + "pico.h": "c", + "critical_section.h": "c", + "*.in": "c", + "vreg.h": "c", + "resets.h": "c", + "address_mapped.h": "c", + "m0plus.h": "c", + "stdint.h": "c", + "rp2040_config.h": "c", + "rp2040.h": "c", + "core_cm0plus.h": "c" } } diff --git a/CMakeLists.txt b/CMakeLists.txt index 83b0e25..34474b0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,6 @@ cmake_minimum_required(VERSION 3.25) # Set project data set(PROJECT_NAME "OpenTricklerController") -set(TARGET_NAME "app") message("CMAKE_SOURCE_DIR: ${CMAKE_SOURCE_DIR}") @@ -41,14 +40,10 @@ set(PICO_BOARD_HEADER_DIRS "${CMAKE_SOURCE_DIR}/targets") # Append compiler flags SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fdata-sections -ffunction-sections -Wall -Werror") SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdata-sections -ffunction-sections -Wall -Werror") -# SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") -# Application -add_executable("${TARGET_NAME}") - -# Include source -include_directories(${SRC_DIRECTORY}) +# Add definitions +add_definitions(-DPICO_NO_BINARY_INFO) # remove binary info from pico-sdk. We will implement our own # Pull in FreeRTOS include(${FREERTOS_SRC_DIRECTORY}/portable/ThirdParty/GCC/RP2040/FreeRTOS_Kernel_import.cmake) @@ -67,8 +62,6 @@ target_sources(trinamic INTERFACE ) target_include_directories(trinamic INTERFACE ${TMC_SRC_DIRECTORY}) - - # Pull in u8g2 mui add_library(u8g2_mui ${U8G2_MUI_DIRECTORY}/mui_u8g2.c @@ -78,47 +71,115 @@ add_library(u8g2_mui ) target_link_libraries(u8g2_mui u8g2) -# Pull in src -add_subdirectory(${SRC_DIRECTORY}) - # Collect all source files file(GLOB SRC ${SRC_DIRECTORY}/*.c ${SRC_DIRECTORY}/*.cpp) -# Include application source file -target_sources("${TARGET_NAME}" PUBLIC - ${SRC} -) -# Include additional headers -target_include_directories("${TARGET_NAME}" PUBLIC ${CMAKE_SOURCE_DIR}/targets) - -# Include libraries1 -target_link_libraries("${TARGET_NAME}" - pico_stdlib - hardware_pio - hardware_spi - hardware_i2c - hardware_pwm +# Pull in source code as library +add_library(app_base + # ${SRC} + "${SRC_DIRECTORY}/app.cpp" + "${SRC_DIRECTORY}/freertos_hooks.c" +) +target_include_directories(app_base PUBLIC + ${SRC_DIRECTORY} + ${SRC_DIRECTORY}/generated + ${PICO_BOARD_HEADER_DIRS} +) +target_link_libraries(app_base FreeRTOS-Kernel FreeRTOS-Kernel-Heap4 u8g2 u8g2_mui trinamic - app_version + pico_stdlib + hardware_pio + hardware_spi + hardware_i2c + hardware_pwm + pico_cyw43_arch_lwip_sys_freertos + pico_lwip_freertos + cmsis_core + + # Source libraries + access_point_mode + button + charge_mode + cleanup_mode + common + http_server + rest_server + eeprom + menu + display + motors + neopixel + wireless + profile + and_scale + steinberg_scale + gng_scale + ussolid_scale + scale + servo_gate ) -if (PICO_BOARD STREQUAL "pico_w" ) - message("Using PICO_W board, link cyw43") - target_link_libraries("${TARGET_NAME}" - pico_cyw43_arch_lwip_sys_freertos - pico_lwip_freertos - # pico_lwip_http - ) -endif() +# Pull in src to generate extra files +add_subdirectory(${SRC_DIRECTORY}) + +# #################################################### +# Build Deployment Application # +###################################################### +add_executable(initial_deployment) -target_link_options("${TARGET_NAME}" PUBLIC -Wl,--gc-sections -Wl,--print-memory-usage) +# Include libraries1 +target_link_libraries(initial_deployment + app_base + app_header_0 +) + +target_link_options(initial_deployment PUBLIC -Wl,--gc-sections -Wl,--print-memory-usage) +set_target_properties(initial_deployment PROPERTIES PICO_TARGET_LINKER_SCRIPT ${SCRIPTS_DIRECTORY}/linker_scripts/initial_deployment.ld) # set( CMAKE_VERBOSE_MAKEFILE on ) # Generate extra outputs -pico_add_extra_outputs("${TARGET_NAME}") +pico_add_extra_outputs(initial_deployment) + + +# #################################################### +# Build Bank0 Application # +###################################################### +add_executable(fw_bank0) + +# Include libraries1 +target_link_libraries(fw_bank0 + app_base + app_header_0 +) + +target_link_options(fw_bank0 PUBLIC -Wl,--gc-sections -Wl,--print-memory-usage) +set_target_properties(fw_bank0 PROPERTIES PICO_TARGET_LINKER_SCRIPT ${SCRIPTS_DIRECTORY}/linker_scripts/bank0.ld) +# set( CMAKE_VERBOSE_MAKEFILE on ) + +# Generate extra outputs +pico_add_extra_outputs(fw_bank0) + + +# #################################################### +# Build Bank1 Application # +###################################################### +add_executable(fw_bank1) + +# Include libraries1 +target_link_libraries(fw_bank1 + app_base + app_header_1 +) + +target_link_options(fw_bank1 PUBLIC -Wl,--gc-sections -Wl,--print-memory-usage) +set_target_properties(fw_bank1 PROPERTIES PICO_TARGET_LINKER_SCRIPT ${SCRIPTS_DIRECTORY}/linker_scripts/bank1.ld) + + +# Generate extra outputs +pico_add_extra_outputs(fw_bank1) diff --git a/scripts/gen_app_header.py b/scripts/gen_app_header.py new file mode 100644 index 0000000..5e36ea3 --- /dev/null +++ b/scripts/gen_app_header.py @@ -0,0 +1,108 @@ +import argparse +import logging +import subprocess +import sys +import re +import os + + +GIT_VERSION_COMMAND = ["git", "describe", "--tags", "--long", "--dirty", "--always"] + +GIT_VERSION_PATTERN_REGEX = r'v(?P\d+)\.(?P\d+)-(?P\d+)-g(?P[a-f0-9]+)(?P-dirty)?' + + +C_SOURCE_TEMPLATE = """// ---------------------------------------------------------------------- // +// This file is auto-generated by gen_application_header.py; do not edit! // +// ---------------------------------------------------------------------- // +#include "app_header.h" + +#ifdef __cplusplus +extern "C" +{{ +#endif + +__attribute__ ((section (".app_header"))) +__attribute__ ((used)) +const app_header_t __app_header = {{ + .app_header_rev = {app_header_rev}, + .flash_partition_idx = {flash_partition_idx}, + .crc = 0, + .vcs_hash = "{vcs_hash}", + .build_type = "{build_type}", + .firmware_version = "{firmware_version}", +}}; + + +const app_header_t * get_app_header(void) +{{ + return &__app_header; +}} + + +#ifdef __cplusplus +}} +#endif +""" + + +def main(output_path, build_type, flash_partition_idx): + output = subprocess.check_output(GIT_VERSION_COMMAND, text=True) + + logging.debug(f'Raw output: {output}') + match = re.match(GIT_VERSION_PATTERN_REGEX, output) + + version_string = "unknown" + hash_string = "" + + if match: + groupdict = match.groupdict() + + major = groupdict['major'] + minor = groupdict['minor'] + patch = groupdict['patch'] + short_hash = groupdict['short_hash'] + dirty = groupdict['dirty'] + + dirty_string = "" + if dirty: # In case the dirty is None + dirty_string = dirty + + version_string = f"{major}.{minor}.{patch}{dirty_string}" + hash_string = short_hash + + c_source_string = C_SOURCE_TEMPLATE.format( + app_header_rev=1, + flash_partition_idx=flash_partition_idx, + vcs_hash=hash_string, + build_type=build_type, + firmware_version=version_string, + ) + + c_source_filepath = os.path.join(output_path, f"app_header_{flash_partition_idx}.c") + with open(c_source_filepath, "w") as fp: + logging.debug(f"Write to {c_source_filepath}") + fp.write(c_source_string) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + + parser.add_argument('-o', '--output_filepath', help="The output filepath that the C source will be written to", required=True) + parser.add_argument('--build-type', help="CMake build type", required=True) + parser.add_argument('--flash-partition-idx', help="Flash partition index, it could be either 0 or 1", required=True) + + parser.add_argument('-v', '--verbose', action='count', default=0) + + + args = parser.parse_args() + + logging_levels = {0: logging.ERROR, + 1: logging.DEBUG, + 2: logging.INFO, + 3: logging.WARNING, + 4: logging.ERROR, + 5: logging.CRITICAL} + + logging.basicConfig(stream=sys.stdout, level=logging_levels[args.verbose]) + + main(args.output_filepath, args.build_type, args.flash_partition_idx) diff --git a/scripts/linker_scripts/bank0.ld b/scripts/linker_scripts/bank0.ld new file mode 100644 index 0000000..7adfd7a --- /dev/null +++ b/scripts/linker_scripts/bank0.ld @@ -0,0 +1,271 @@ +/* Based on GCC ARM embedded samples. + Defines the following symbols for use by code: + __exidx_start + __exidx_end + __etext + __data_start__ + __preinit_array_start + __preinit_array_end + __init_array_start + __init_array_end + __fini_array_start + __fini_array_end + __data_end__ + __bss_start__ + __bss_end__ + __end__ + end + __HeapLimit + __StackLimit + __StackTop + __stack (== StackTop) +*/ + +MEMORY +{ + FLASH_BOOT2(rx) : ORIGIN = 0x10000000, LENGTH = 0x100 + FLASH(rx) : ORIGIN = 0x10000100, LENGTH = 1M - 0x100 - 0x100 + FLASH_APP_HEADER (r): ORIGIN = 0x10100000 - 0x100, LENGTH = 0x100 + RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k + SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k + SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k +} + +ENTRY(_entry_point) + +SECTIONS +{ + /* + * BS2 is removed from the deployment package * + + 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 + + .boot2 : { + __boot2_start__ = .; + KEEP (*(.boot2)) + __boot2_end__ = .; + } > FLASH_BOOT2 + + 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. + */ + + .app_header_section : { + __app_header_start = .; + KEEP (*(.app_header)) + __app_header_end = .; + } > FLASH_APP_HEADER + ASSERT(__app_header_end - __app_header_start < 0x100, + "ERROR: App header must be smaller than 0x100 bytes") + + .flash_begin : { + __flash_binary_start = .; + } > FLASH + + .text : { + __logical_binary_start = .; + KEEP (*(.vectors)) + + /* Remove binary info header + 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 + + .rodata : { + *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .rodata*) + . = ALIGN(4); + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*))) + . = ALIGN(4); + } > FLASH + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > FLASH + + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > FLASH + __exidx_end = .; + + /* Machine inspectable binary information */ + . = ALIGN(4); + __binary_info_start = .; + .binary_info : + { + KEEP(*(.binary_info.keep.*)) + *(.binary_info.*) + } > FLASH + __binary_info_end = .; + . = ALIGN(4); + + .ram_vector_table (NOLOAD): { + *(.ram_vector_table) + } > RAM + + .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 + /* __etext is (for backwards compatibility) the name of the .data init source pointer (...) */ + __etext = LOADADDR(.data); + + .uninitialized_data (NOLOAD): { + . = 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 (NOLOAD): + { + __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 (NOLOAD): + { + *(.stack1*) + } > SCRATCH_X + .stack_dummy (NOLOAD): + { + KEEP(*(.stack*)) + } > SCRATCH_Y + + .flash_end : { + PROVIDE(__flash_binary_end = .); + } > FLASH + + /* stack limit is poorly named, but historically is maximum heap ptr */ + __StackLimit = ORIGIN(RAM) + LENGTH(RAM); + __StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X); + __StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y); + __StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy); + __StackBottom = __StackTop - SIZEOF(.stack_dummy); + PROVIDE(__stack = __StackTop); + + /* Check if data + heap + stack exceeds RAM limit */ + ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed") + + /* Remove binary info header + 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/scripts/linker_scripts/bank1.ld b/scripts/linker_scripts/bank1.ld new file mode 100644 index 0000000..d1201da --- /dev/null +++ b/scripts/linker_scripts/bank1.ld @@ -0,0 +1,271 @@ +/* Based on GCC ARM embedded samples. + Defines the following symbols for use by code: + __exidx_start + __exidx_end + __etext + __data_start__ + __preinit_array_start + __preinit_array_end + __init_array_start + __init_array_end + __fini_array_start + __fini_array_end + __data_end__ + __bss_start__ + __bss_end__ + __end__ + end + __HeapLimit + __StackLimit + __StackTop + __stack (== StackTop) +*/ + +MEMORY +{ + FLASH_BOOT2(rx) : ORIGIN = 0x10000000, LENGTH = 0x100 + FLASH(rx) : ORIGIN = 0x10100000, LENGTH = 1M - 0x100 - 0x100 + FLASH_APP_HEADER (r): ORIGIN = 0x10200000 - 0x100 - 0x100, LENGTH = 0x100 + RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k + SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k + SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k +} + +ENTRY(_entry_point) + +SECTIONS +{ + /* + * BS2 is removed from the deployment package * + + 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 + + .boot2 : { + __boot2_start__ = .; + KEEP (*(.boot2)) + __boot2_end__ = .; + } > FLASH_BOOT2 + + 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. + */ + + .app_header_section : { + __app_header_start = .; + KEEP (*(.app_header)) + __app_header_end = .; + } > FLASH_APP_HEADER + ASSERT(__app_header_end - __app_header_start < 0x100, + "ERROR: App header must be smaller than 0x100 bytes") + + .flash_begin : { + __flash_binary_start = .; + } > FLASH + + .text : { + __logical_binary_start = .; + KEEP (*(.vectors)) + + /* Remove binary info header + 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 + + .rodata : { + *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .rodata*) + . = ALIGN(4); + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*))) + . = ALIGN(4); + } > FLASH + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > FLASH + + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > FLASH + __exidx_end = .; + + /* Machine inspectable binary information */ + . = ALIGN(4); + __binary_info_start = .; + .binary_info : + { + KEEP(*(.binary_info.keep.*)) + *(.binary_info.*) + } > FLASH + __binary_info_end = .; + . = ALIGN(4); + + .ram_vector_table (NOLOAD): { + *(.ram_vector_table) + } > RAM + + .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 + /* __etext is (for backwards compatibility) the name of the .data init source pointer (...) */ + __etext = LOADADDR(.data); + + .uninitialized_data (NOLOAD): { + . = 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 (NOLOAD): + { + __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 (NOLOAD): + { + *(.stack1*) + } > SCRATCH_X + .stack_dummy (NOLOAD): + { + KEEP(*(.stack*)) + } > SCRATCH_Y + + .flash_end : { + PROVIDE(__flash_binary_end = .); + } > FLASH + + /* stack limit is poorly named, but historically is maximum heap ptr */ + __StackLimit = ORIGIN(RAM) + LENGTH(RAM); + __StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X); + __StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y); + __StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy); + __StackBottom = __StackTop - SIZEOF(.stack_dummy); + PROVIDE(__stack = __StackTop); + + /* Check if data + heap + stack exceeds RAM limit */ + ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed") + + /* Remove binary info header + 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/scripts/linker_scripts/initial_deployment.ld b/scripts/linker_scripts/initial_deployment.ld new file mode 100644 index 0000000..1fdee04 --- /dev/null +++ b/scripts/linker_scripts/initial_deployment.ld @@ -0,0 +1,266 @@ +/* Based on GCC ARM embedded samples. + Defines the following symbols for use by code: + __exidx_start + __exidx_end + __etext + __data_start__ + __preinit_array_start + __preinit_array_end + __init_array_start + __init_array_end + __fini_array_start + __fini_array_end + __data_end__ + __bss_start__ + __bss_end__ + __end__ + end + __HeapLimit + __StackLimit + __StackTop + __stack (== StackTop) +*/ + +MEMORY +{ + FLASH_BOOT2(rx) : ORIGIN = 0x10000000, LENGTH = 0x100 + FLASH(rx) : ORIGIN = 0x10000100, LENGTH = 1M - 0x100 - 0x100 + FLASH_APP_HEADER (r): ORIGIN = 0x10100000 - 0x100, LENGTH = 0x100 + 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 + */ + .boot2 : { + __boot2_start__ = .; + KEEP (*(.boot2)) + __boot2_end__ = .; + } > FLASH_BOOT2 + + 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. + */ + + .app_header_section : { + __app_header_start = .; + KEEP (*(.app_header)) + __app_header_end = .; + } > FLASH_APP_HEADER + ASSERT(__app_header_end - __app_header_start < 0x100, + "ERROR: App header must be smaller than 0x100 bytes") + + .flash_begin : { + __flash_binary_start = .; + } > FLASH + + .text : { + __logical_binary_start = .; + KEEP (*(.vectors)) + + /* Remove binary info header + 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 + + .rodata : { + *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .rodata*) + . = ALIGN(4); + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*))) + . = ALIGN(4); + } > FLASH + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > FLASH + + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > FLASH + __exidx_end = .; + + /* Machine inspectable binary information */ + . = ALIGN(4); + __binary_info_start = .; + .binary_info : + { + KEEP(*(.binary_info.keep.*)) + *(.binary_info.*) + } > FLASH + __binary_info_end = .; + . = ALIGN(4); + + .ram_vector_table (NOLOAD): { + *(.ram_vector_table) + } > RAM + + .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 + /* __etext is (for backwards compatibility) the name of the .data init source pointer (...) */ + __etext = LOADADDR(.data); + + .uninitialized_data (NOLOAD): { + . = 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 (NOLOAD): + { + __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 (NOLOAD): + { + *(.stack1*) + } > SCRATCH_X + .stack_dummy (NOLOAD): + { + KEEP(*(.stack*)) + } > SCRATCH_Y + + .flash_end : { + PROVIDE(__flash_binary_end = .); + } > FLASH + + /* stack limit is poorly named, but historically is maximum heap ptr */ + __StackLimit = ORIGIN(RAM) + LENGTH(RAM); + __StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X); + __StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y); + __StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy); + __StackBottom = __StackTop - SIZEOF(.stack_dummy); + PROVIDE(__stack = __StackTop); + + /* Check if data + heap + stack exceeds RAM limit */ + ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed") + + /* Remove binary info header + ASSERT( __binary_info_header_end - __logical_binary_start <= 256, "Binary info must be in first 256 bytes of the binary") + */ + + /* todo assert on extra code */ +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 63f78eb..5d6cc87 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,10 +1,11 @@ # Generate PIO headers -pico_generate_pio_header(app ${SRC_DIRECTORY}/ws2812.pio OUTPUT_DIR ${SRC_DIRECTORY}/generated) +pico_generate_pio_header(app_base ${SRC_DIRECTORY}/ws2812.pio OUTPUT_DIR ${SRC_DIRECTORY}/generated) # Generate PIO headers -pico_generate_pio_header(app ${SRC_DIRECTORY}/stepper.pio OUTPUT_DIR ${SRC_DIRECTORY}/generated) +pico_generate_pio_header(app_base ${SRC_DIRECTORY}/stepper.pio OUTPUT_DIR ${SRC_DIRECTORY}/generated) # Find Python, as Python is required to convert pages into source find_package (Python COMPONENTS Interpreter REQUIRED) +MESSAGE("Python_EXECUTABLE: ${Python_EXECUTABLE}") # Generate HTML header # Reference: https://cliutils.gitlab.io/modern-cmake/chapters/basics/programs.html @@ -22,7 +23,7 @@ add_custom_command( COMMENT "Generating web_portal.html header" ) -add_dependencies("${TARGET_NAME}" generate_web_portal_header) +add_dependencies(app_base generate_web_portal_header) # wizard.html @@ -38,7 +39,7 @@ add_custom_command( COMMENT "Generating wizard.html header" ) -add_dependencies("${TARGET_NAME}" generate_wizard_header) +add_dependencies(app_base generate_wizard_header) # display_mirror.html @@ -54,17 +55,179 @@ add_custom_command( COMMENT "Generating display_mirror.html header" ) -add_dependencies("${TARGET_NAME}" generate_display_mirror) +add_dependencies(app_base generate_display_mirror) + + +# Generate two app headers. The target will select the one to linked against +add_custom_command( + OUTPUT + "${SRC_DIRECTORY}/generated/app_header_0.c" + "non_exist_file0" + COMMAND "${Python_EXECUTABLE}" "${SCRIPTS_DIRECTORY}/gen_app_header.py" -o ${SRC_DIRECTORY}/generated --build-type="${CMAKE_BUILD_TYPE}" --flash-partition-idx=0 + COMMENT "Generating app header 0" +) +add_library(app_header_0) +target_include_directories(app_header_0 PRIVATE ${SRC_DIRECTORY}) +target_sources(app_header_0 PRIVATE "${SRC_DIRECTORY}/generated/app_header_0.c") +target_link_libraries(app_header_0 cmsis_core) -# Generate version -MESSAGE("Python_EXECUTABLE: ${Python_EXECUTABLE}") add_custom_command( OUTPUT - "${SRC_DIRECTORY}/generated/version.c" - "${SRC_DIRECTORY}/generated/version.h" - "non_exist_file" - COMMAND "${Python_EXECUTABLE}" "${SCRIPTS_DIRECTORY}/gen_version.py" -o ${SRC_DIRECTORY}/generated --build-type="${CMAKE_BUILD_TYPE}" - COMMENT "Generating version" + "${SRC_DIRECTORY}/generated/app_header_1.c" + "non_exist_file1" + COMMAND "${Python_EXECUTABLE}" "${SCRIPTS_DIRECTORY}/gen_app_header.py" -o ${SRC_DIRECTORY}/generated --build-type="${CMAKE_BUILD_TYPE}" --flash-partition-idx=1 + COMMENT "Generating app header 1" +) +add_library(app_header_1) +target_include_directories(app_header_1 PRIVATE ${SRC_DIRECTORY}) +target_sources(app_header_1 PRIVATE "${SRC_DIRECTORY}/generated/app_header_1.c") +target_link_libraries(app_header_1 cmsis_core) + + +## Add modules +# Common +add_library(common INTERFACE) +target_include_directories(common INTERFACE ${SRC_DIRECTORY}) +target_sources(common INTERFACE "${SRC_DIRECTORY}/common.c") + +# Access point mode +add_library(access_point_mode INTERFACE) +target_include_directories(access_point_mode INTERFACE ${SRC_DIRECTORY}) +target_sources(access_point_mode INTERFACE "${SRC_DIRECTORY}/access_point_mode.c") + +# G&G Scale +add_library(gng_scale INTERFACE) +target_include_directories(gng_scale INTERFACE ${SRC_DIRECTORY}) +target_sources(gng_scale INTERFACE + "${SRC_DIRECTORY}/gng_scale.c" +) + +# A&D Scale +add_library(and_scale INTERFACE) +target_include_directories(and_scale INTERFACE ${SRC_DIRECTORY}) +target_sources(and_scale INTERFACE + "${SRC_DIRECTORY}/and_scale_calibrate.c" + "${SRC_DIRECTORY}/and_scale.c" +) + +# Steinberg scale +add_library(steinberg_scale INTERFACE) +target_include_directories(steinberg_scale INTERFACE ${SRC_DIRECTORY}) +target_sources(steinberg_scale INTERFACE + "${SRC_DIRECTORY}/steinberg_scale.c" +) + +# U.S.solid scale +add_library(ussolid_scale INTERFACE) +target_include_directories(ussolid_scale INTERFACE ${SRC_DIRECTORY}) +target_sources(ussolid_scale INTERFACE + "${SRC_DIRECTORY}/ussolid_scale.c" +) + +# Scale (abstract class) +add_library(scale INTERFACE) +target_include_directories(scale INTERFACE ${SRC_DIRECTORY}) +target_sources(scale INTERFACE + "${SRC_DIRECTORY}/scale.c" +) + +# Button +add_library(button INTERFACE) +target_include_directories(button INTERFACE ${SRC_DIRECTORY}) +target_sources(button INTERFACE + "${SRC_DIRECTORY}/button.c" + "${SRC_DIRECTORY}/rotary_button.cpp" +) + +# Charge mode +add_library(charge_mode INTERFACE) +target_include_directories(charge_mode INTERFACE ${SRC_DIRECTORY}) +target_sources(charge_mode INTERFACE + "${SRC_DIRECTORY}/charge_mode.cpp" + "${SRC_DIRECTORY}/FloatRingBuffer.cpp" +) + +# Cleanup mode +add_library(cleanup_mode INTERFACE) +target_include_directories(cleanup_mode INTERFACE ${SRC_DIRECTORY}) +target_sources(cleanup_mode INTERFACE + "${SRC_DIRECTORY}/cleanup_mode.cpp" +) + +# HTTP server +add_library(http_server INTERFACE) +target_include_directories(http_server INTERFACE ${SRC_DIRECTORY}) +target_sources(http_server INTERFACE + "${SRC_DIRECTORY}/dhcpserver.c" + "${SRC_DIRECTORY}/dnsserver.c" + "${SRC_DIRECTORY}/http_rest.c" +) + +# REST server +add_library(rest_server INTERFACE) +target_include_directories(rest_server INTERFACE ${SRC_DIRECTORY}) +target_sources(rest_server INTERFACE + "${SRC_DIRECTORY}/rest_endpoints.c" + "${SRC_DIRECTORY}/rest_app_control.c" + +) + +# EEPROM +add_library(eeprom INTERFACE) +target_include_directories(eeprom INTERFACE ${SRC_DIRECTORY}) +target_sources(eeprom INTERFACE + "${SRC_DIRECTORY}/eeprom.c" + "${SRC_DIRECTORY}/cat24c256_eeprom.c" +) + +# Menu +add_library(menu INTERFACE) +target_include_directories(menu INTERFACE ${SRC_DIRECTORY}) +target_sources(menu INTERFACE + "${SRC_DIRECTORY}/menu.cpp" + "${SRC_DIRECTORY}/mui_menu.c" +) + +# Display +add_library(display INTERFACE) +target_include_directories(display INTERFACE ${SRC_DIRECTORY}) +target_sources(display INTERFACE + "${SRC_DIRECTORY}/display.c" + "${SRC_DIRECTORY}/uc1701_display.c" +) + +# Motors +add_library(motors INTERFACE) +target_include_directories(motors INTERFACE ${SRC_DIRECTORY}) +target_sources(motors INTERFACE + "${SRC_DIRECTORY}/motors.c" +) + +# Neopixel +add_library(neopixel INTERFACE) +target_include_directories(neopixel INTERFACE ${SRC_DIRECTORY}) +target_sources(neopixel INTERFACE + "${SRC_DIRECTORY}/neopixel_led.c" +) + +# Profile +add_library(profile INTERFACE) +target_include_directories(profile INTERFACE ${SRC_DIRECTORY}) +target_sources(profile INTERFACE + "${SRC_DIRECTORY}/profile.c" +) + +# Wireless +add_library(wireless INTERFACE) +target_include_directories(wireless INTERFACE ${SRC_DIRECTORY}) +target_sources(wireless INTERFACE + "${SRC_DIRECTORY}/wireless.c" +) + +# Servo Gate +add_library(servo_gate INTERFACE) +target_include_directories(servo_gate INTERFACE ${SRC_DIRECTORY}) +target_sources(servo_gate INTERFACE + "${SRC_DIRECTORY}/servo_gate.c" ) -add_library(app_version "${SRC_DIRECTORY}/generated/version.c") diff --git a/src/app.cpp b/src/app.cpp index 33bfca8..e09df19 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -28,7 +28,6 @@ #include "profile.h" #include "servo_gate.h" - uint8_t software_reboot() { watchdog_reboot(0, 0, 0); diff --git a/src/app_header.h b/src/app_header.h new file mode 100644 index 0000000..5ec7cf6 --- /dev/null +++ b/src/app_header.h @@ -0,0 +1,28 @@ +#ifndef APP_HEADER_H_ +#define APP_HEADER_H_ + +#include + +typedef struct { + uint32_t app_header_rev; + uint32_t flash_partition_idx; + uint32_t crc; + char vcs_hash[16]; + char build_type[16]; + char firmware_version[32]; + +} app_header_t; + +#ifdef __cplusplus +extern "C" +{ +#endif + +const app_header_t * get_app_header(); + +#ifdef __cplusplus +} +#endif + + +#endif // APP_HEADER_H_ diff --git a/src/boot3/CMakeLists.txt b/src/boot3/CMakeLists.txt new file mode 100644 index 0000000..e69de29 diff --git a/src/boot3/bl_crt0.S b/src/boot3/bl_crt0.S new file mode 100644 index 0000000..d061108 --- /dev/null +++ b/src/boot3/bl_crt0.S @@ -0,0 +1,337 @@ +/* + * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "pico.h" +#include "hardware/regs/m0plus.h" +#include "hardware/regs/addressmap.h" +#include "hardware/regs/sio.h" +#include "pico/asm_helper.S" +#include "pico/binary_info/defs.h" + +#ifdef NDEBUG +#ifndef COLLAPSE_IRQS +#define COLLAPSE_IRQS +#endif +#endif + +.syntax unified +.cpu cortex-m0plus +.thumb + +.section .vectors, "ax" +.align 2 + +.global __vectors, __VECTOR_TABLE +__VECTOR_TABLE: +__vectors: +.word __StackTop +.word _reset_handler +.word isr_nmi +.word isr_hardfault +.word isr_invalid // Reserved, should never fire +.word isr_invalid // Reserved, should never fire +.word isr_invalid // Reserved, should never fire +.word isr_invalid // Reserved, should never fire +.word isr_invalid // Reserved, should never fire +.word isr_invalid // Reserved, should never fire +.word isr_invalid // Reserved, should never fire +.word isr_svcall +.word isr_invalid // Reserved, should never fire +.word isr_invalid // Reserved, should never fire +.word isr_pendsv +.word isr_systick +.word isr_irq0 +.word isr_irq1 +.word isr_irq2 +.word isr_irq3 +.word isr_irq4 +.word isr_irq5 +.word isr_irq6 +.word isr_irq7 +.word isr_irq8 +.word isr_irq9 +.word isr_irq10 +.word isr_irq11 +.word isr_irq12 +.word isr_irq13 +.word isr_irq14 +.word isr_irq15 +.word isr_irq16 +.word isr_irq17 +.word isr_irq18 +.word isr_irq19 +.word isr_irq20 +.word isr_irq21 +.word isr_irq22 +.word isr_irq23 +.word isr_irq24 +.word isr_irq25 +.word isr_irq26 +.word isr_irq27 +.word isr_irq28 +.word isr_irq29 +.word isr_irq30 +.word isr_irq31 + +// all default exception handlers do nothing, and we can check for them being set to our +// default values by seeing if they point to somewhere between __defaults_isrs_start and __default_isrs_end +.global __default_isrs_start +__default_isrs_start: + +// Declare a weak symbol for each ISR. +// By default, they will fall through to the undefined IRQ handler below (breakpoint), +// but can be overridden by C functions with correct name. + +.macro decl_isr_bkpt name +.weak \name +.type \name,%function +.thumb_func +\name: + bkpt #0 +.endm + +// these are separated out for clarity +decl_isr_bkpt isr_invalid +decl_isr_bkpt isr_nmi +decl_isr_bkpt isr_hardfault +decl_isr_bkpt isr_svcall +decl_isr_bkpt isr_pendsv +decl_isr_bkpt isr_systick + +.global __default_isrs_end +__default_isrs_end: + +.macro decl_isr name +.weak \name +.type \name,%function +.thumb_func +\name: +.endm + +decl_isr isr_irq0 +decl_isr isr_irq1 +decl_isr isr_irq2 +decl_isr isr_irq3 +decl_isr isr_irq4 +decl_isr isr_irq5 +decl_isr isr_irq6 +decl_isr isr_irq7 +decl_isr isr_irq8 +decl_isr isr_irq9 +decl_isr isr_irq10 +decl_isr isr_irq11 +decl_isr isr_irq12 +decl_isr isr_irq13 +decl_isr isr_irq14 +decl_isr isr_irq15 +decl_isr isr_irq16 +decl_isr isr_irq17 +decl_isr isr_irq18 +decl_isr isr_irq19 +decl_isr isr_irq20 +decl_isr isr_irq21 +decl_isr isr_irq22 +decl_isr isr_irq23 +decl_isr isr_irq24 +decl_isr isr_irq25 +decl_isr isr_irq26 +decl_isr isr_irq27 +decl_isr isr_irq28 +decl_isr isr_irq29 +decl_isr isr_irq30 +decl_isr isr_irq31 + +// All unhandled USER IRQs fall through to here +.global __unhandled_user_irq +.thumb_func +__unhandled_user_irq: + mrs r0, ipsr + subs r0, #16 +.global unhandled_user_irq_num_in_r0 +unhandled_user_irq_num_in_r0: + bkpt #0 + +// ---------------------------------------------------------------------------- + +.section .binary_info_header, "a" + +// Header must be in first 256 bytes of main image (i.e. excluding flash boot2). +// For flash builds we put it immediately after vector table; for NO_FLASH the +// vectors are at a +0x100 offset because the bootrom enters RAM images directly +// at their lowest address, so we put the header in the VTOR alignment hole. + +#if !PICO_NO_BINARY_INFO +binary_info_header: +.word BINARY_INFO_MARKER_START +.word __binary_info_start +.word __binary_info_end +.word data_cpy_table // we may need to decode pointers that are in RAM at runtime. +.word BINARY_INFO_MARKER_END +#endif + +// ---------------------------------------------------------------------------- + +.section .reset, "ax" + +// On flash builds, the vector table comes first in the image (conventional). +// On NO_FLASH builds, the reset handler section comes first, as the entry +// point is at offset 0 (fixed due to bootrom), and VTOR is highly-aligned. +// Image is entered in various ways: +// +// - NO_FLASH builds are entered from beginning by UF2 bootloader +// +// - Flash builds vector through the table into _reset_handler from boot2 +// +// - Either type can be entered via _entry_point by the debugger, and flash builds +// must then be sent back round the boot sequence to properly initialise flash + +// ELF entry point: +.type _entry_point,%function +.thumb_func +.global _entry_point +_entry_point: + +#if PICO_NO_FLASH + // Vector through our own table (SP, VTOR will not have been set up at + // this point). Same path for debugger entry and bootloader entry. + ldr r0, =__vectors +#else + // Debugger tried to run code after loading, so SSI is in 03h-only mode. + // Go back through bootrom + boot2 to properly initialise flash. + movs r0, #0 +#endif + ldr r1, =(PPB_BASE + M0PLUS_VTOR_OFFSET) + str r0, [r1] + ldmia r0!, {r1, r2} + msr msp, r1 + bx r2 + +// Reset handler: +// - initialises .data +// - clears .bss +// - calls runtime_init +// - calls main +// - calls exit (which should eventually hang the processor via _exit) + +.type _reset_handler,%function +.thumb_func +_reset_handler: + // Only core 0 should run the C runtime startup code; core 1 is normally + // sleeping in the bootrom at this point but check to be sure + ldr r0, =(SIO_BASE + SIO_CPUID_OFFSET) + ldr r0, [r0] + cmp r0, #0 + bne hold_non_core0_in_bootrom + + // In a NO_FLASH binary, don't perform .data copy, since it's loaded + // in-place by the SRAM load. Still need to clear .bss +#if !PICO_NO_FLASH + adr r4, data_cpy_table + + // assume there is at least one entry +1: + ldmia r4!, {r1-r3} + cmp r1, #0 + beq 2f + bl data_cpy + b 1b +2: +#endif + + // Zero out the BSS + ldr r1, =__bss_start__ + ldr r2, =__bss_end__ + movs r0, #0 + b bss_fill_test +bss_fill_loop: + stm r1!, {r0} +bss_fill_test: + cmp r1, r2 + bne bss_fill_loop + +platform_entry: // symbol for stack traces + // Use 32-bit jumps, in case these symbols are moved out of branch range + // (e.g. if main is in SRAM and crt0 in flash) + ldr r1, =runtime_init + blx r1 + ldr r1, =main + blx r1 + ldr r1, =exit + blx r1 + // exit should not return. If it does, hang the core. + // (fall thru into our hang _exit impl +1: // separate label because _exit can be moved out of branch range + bkpt #0 + b 1b + +#if !PICO_NO_FLASH +data_cpy_loop: + ldm r1!, {r0} + stm r2!, {r0} +data_cpy: + cmp r2, r3 + blo data_cpy_loop + bx lr +#endif + +// Note the data copy table is still included for NO_FLASH builds, even though +// we skip the copy, because it is listed in binary info + +.align 2 +data_cpy_table: +#if PICO_COPY_TO_RAM +.word __ram_text_source__ +.word __ram_text_start__ +.word __ram_text_end__ +#endif +.word __etext +.word __data_start__ +.word __data_end__ + +.word __scratch_x_source__ +.word __scratch_x_start__ +.word __scratch_x_end__ + +.word __scratch_y_source__ +.word __scratch_y_start__ +.word __scratch_y_end__ + +.word 0 // null terminator + +// ---------------------------------------------------------------------------- +// Provide safe defaults for _exit and runtime_init +// Full implementations usually provided by platform.c + +.weak runtime_init +.type runtime_init,%function +.thumb_func +runtime_init: + bx lr + +// ---------------------------------------------------------------------------- +// If core 1 somehow gets into crt0 due to a spectacular VTOR mishap, we need to +// catch it and send back to the sleep-and-launch code in the bootrom. Shouldn't +// happen (it should sleep in the ROM until given an entry point via the +// cross-core FIFOs) but it's good to be defensive. + +hold_non_core0_in_bootrom: + ldr r0, = 'W' | ('V' << 8) + bl rom_func_lookup + bx r0 + +// ---------------------------------------------------------------------------- +// Stack/heap dummies to set size + +.section .stack +// align to allow for memory protection (although this alignment is pretty much ignored by linker script) +.align 5 + .equ StackSize, PICO_STACK_SIZE +.space StackSize + +.section .heap +.align 2 + .equ HeapSize, PICO_HEAP_SIZE +.space HeapSize diff --git a/src/boot3/bootloader.c b/src/boot3/bootloader.c new file mode 100644 index 0000000..f00d673 --- /dev/null +++ b/src/boot3/bootloader.c @@ -0,0 +1,4 @@ + +void main(void) { + +} \ No newline at end of file diff --git a/src/eeprom.c b/src/eeprom.c index b8867ef..91d0a9c 100644 --- a/src/eeprom.c +++ b/src/eeprom.c @@ -19,8 +19,10 @@ #include "app.h" #include "neopixel_led.h" #include "rotary_button.h" -#include "version.h" #include "profile.h" +#include "app_header.h" + +extern const app_header_t __app_header; extern bool cat24c256_eeprom_erase(); @@ -228,12 +230,14 @@ bool http_rest_system_control(struct fs_file *file, int num_params, char *params } // Response + const app_header_t * app_header = get_app_header(); + snprintf(eeprom_config_json_buffer, sizeof(eeprom_config_json_buffer), "%s" "{\"s0\":\"%s\",\"s1\":\"%s\",\"s2\":\"%s\",\"s3\":\"%s\",\"s4\":%s,\"s5\":%s,\"s6\":%s}", http_json_header, - metadata.unique_id, version_string, vcs_hash, build_type, + metadata.unique_id, app_header->firmware_version, app_header->vcs_hash, app_header->build_type, boolean_to_string(save_to_eeprom_flag), boolean_to_string(erase_eeprom_flag), boolean_to_string(software_reset_flag)); diff --git a/src/mui_menu.c b/src/mui_menu.c index 23095ae..da4da6c 100644 --- a/src/mui_menu.c +++ b/src/mui_menu.c @@ -10,9 +10,9 @@ #include "scale.h" #include "charge_mode.h" -#include "version.h" #include "common.h" #include "profile.h" +#include "app_header.h" #include "servo_gate.h" @@ -51,21 +51,22 @@ uint8_t render_version_page(mui_t * ui, uint8_t msg) { switch (msg) { case MUIF_MSG_DRAW: { + const app_header_t * app_header = get_app_header(); u8g2_uint_t x = mui_get_x(ui); u8g2_uint_t y = mui_get_y(ui); u8g2_t *u8g2 = mui_get_U8g2(ui); - char buf[32]; + char buf[40]; u8g2_SetFont(u8g2, u8g2_font_profont11_tf); - snprintf(buf, sizeof(buf), "Ver: %s", version_string); + snprintf(buf, sizeof(buf), "Ver: %s", app_header->firmware_version); u8g2_DrawStr(u8g2, x, y, buf); - snprintf(buf, sizeof(buf), "VCS: %s", vcs_hash); + snprintf(buf, sizeof(buf), "VCS: %s", app_header->vcs_hash); u8g2_DrawStr(u8g2, x, y + 10, buf); - snprintf(buf, sizeof(buf), "Build: %s", build_type); + snprintf(buf, sizeof(buf), "Build: %s", app_header->build_type); u8g2_DrawStr(u8g2, x, y + 20, buf); break;