diff --git a/CMakeLists.txt b/CMakeLists.txt index d42f742..d7b65c3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,7 +41,12 @@ endif() include(configs/mcu/${TARGET}.cmake) # Include toolchain configuration before project() to set compilers -include(${DMOD_DIR}/configs/arch/${DMBOOT_ARCH}/${DMBOOT_ARCH_FAMILY}/tools-cfg.cmake) +# Check for a local override first, then fall back to the dmod library +if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/configs/arch/${DMBOOT_ARCH}/${DMBOOT_ARCH_FAMILY}/tools-cfg.cmake") + include("${CMAKE_CURRENT_SOURCE_DIR}/configs/arch/${DMBOOT_ARCH}/${DMBOOT_ARCH_FAMILY}/tools-cfg.cmake") +else() + include(${DMOD_DIR}/configs/arch/${DMBOOT_ARCH}/${DMBOOT_ARCH_FAMILY}/tools-cfg.cmake) +endif() # ====================================================================== # DMOD Boot Project Configuration @@ -115,7 +120,7 @@ set(MODULES_DMP_OBJECT "${CMAKE_BINARY_DIR}/__modules_dmp.o") add_custom_command( OUTPUT "${MODULES_DMP_OBJECT}" COMMAND ${CMAKE_COMMAND} -E echo "Checking for modules.dmp..." - COMMAND bash -c "if [ -f ${MODULES_DMP_FILE} ]; then echo 'Embedding modules.dmp'; ${CMAKE_OBJCOPY} --input-target=binary --output-target=elf32-littlearm --binary-architecture=arm --rename-section .data=.embedded.modules_dmp,alloc,load,readonly,data,contents ${MODULES_DMP_FILE} ${MODULES_DMP_OBJECT}; else echo 'No modules.dmp found, creating empty object'; ${CMAKE_OBJCOPY} --input-target=binary --output-target=elf32-littlearm --binary-architecture=arm --rename-section .data=.embedded.modules_dmp,alloc,load,readonly,data,contents /dev/null ${MODULES_DMP_OBJECT}; fi" + COMMAND bash -c "if [ -f ${MODULES_DMP_FILE} ]; then echo 'Embedding modules.dmp'; ${CMAKE_OBJCOPY} --input-target=binary --output-target=${DMBOOT_OBJCOPY_OUTPUT_FORMAT} --binary-architecture=${DMBOOT_OBJCOPY_BINARY_ARCH} --rename-section .data=.embedded.modules_dmp,alloc,load,readonly,data,contents ${MODULES_DMP_FILE} ${MODULES_DMP_OBJECT}; else echo 'No modules.dmp found, creating empty object'; ${CMAKE_OBJCOPY} --input-target=binary --output-target=${DMBOOT_OBJCOPY_OUTPUT_FORMAT} --binary-architecture=${DMBOOT_OBJCOPY_BINARY_ARCH} --rename-section .data=.embedded.modules_dmp,alloc,load,readonly,data,contents /dev/null ${MODULES_DMP_OBJECT}; fi" DEPENDS prepare_modules_dmp COMMENT "Conditionally embedding modules.dmp if it exists" VERBATIM diff --git a/README.md b/README.md index c48b6f6..46a230d 100644 --- a/README.md +++ b/README.md @@ -93,7 +93,7 @@ cmake -DTARGET=STM32F407G -S . -B build cmake --build build ``` -Supported values: `STM32F746xG`, `STM32F407G`, `STM32F746ZG`. +Supported values: `STM32F746xG`, `STM32F407G`, `STM32F746ZG`, `ESP32S3`. **Using the `BOARD` parameter** – select a ready-made board configuration that automatically sets `TARGET` and other board-specific settings: @@ -104,6 +104,43 @@ cmake --build build Board configuration files are located in `configs/board//board.cmake`. When `BOARD` is set, the `TARGET` parameter is ignored. +### Building for LILYGO T-Deck Pro + +The T-Deck Pro is based on the **ESP32-S3FN16R8** (Xtensa LX7 dual-core, 240 MHz, 16 MB flash, 8 MB PSRAM). + +**Prerequisites:** + +- [Espressif ESP32-S3 toolchain](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/get-started/linux-macos-setup.html) (`xtensa-esp32s3-elf-gcc`) +- [Espressif OpenOCD fork](https://github.com/espressif/openocd-esp32) (supports the built-in USB JTAG of the ESP32-S3) + +**Build using the board preset:** + +```bash +cmake -DBOARD=t-deck-pro -S . -B build +cmake --build build +``` + +**Or specify the target directly:** + +```bash +cmake -DTARGET=ESP32S3 -S . -B build +cmake --build build +``` + +**Flash via OpenOCD** (T-Deck Pro has a built-in USB JTAG, no external probe needed): + +```bash +cmake --build build --target install-firmware +``` + +This runs: +``` +openocd -f interface/esp_usb_jtag.cfg -f target/esp32s3.cfg \ + -c "program dmboot.elf verify reset exit" +``` + +> **Note:** The firmware is loaded directly into IRAM/DRAM, bypassing the ROM bootloader's cache setup. This makes the development cycle fast and requires no partition table or bootloader binary. + ### Building for Renode Simulation To build for Renode simulation (useful for automated testing without hardware): @@ -134,8 +171,8 @@ cmake --build build ``` **Build Parameters:** -- `TARGET` (optional, defaults to `STM32F746xG`) - Target microcontroller. Supported values: `STM32F746xG`, `STM32F407G`, `STM32F746ZG`. Ignored when `BOARD` is set. -- `BOARD` (optional) - Board name. When set, `TARGET` and other board-specific settings are automatically derived from `configs/board//board.cmake`, so you don't need to set `TARGET` manually. Example: `-DBOARD=stm32f746g-disco`. +- `TARGET` (optional, defaults to `STM32F746xG`) - Target microcontroller. Supported values: `STM32F746xG`, `STM32F407G`, `STM32F746ZG`, `ESP32S3`. Ignored when `BOARD` is set. +- `BOARD` (optional) - Board name. When set, `TARGET` and other board-specific settings are automatically derived from `configs/board//board.cmake`, so you don't need to set `TARGET` manually. Examples: `-DBOARD=stm32f746g-disco`, `-DBOARD=t-deck-pro`. - `STARTUP_DMP_FILE` (optional) - Path to a startup package file (`.dmp`) that will be automatically loaded using `Dmod_AddPackageBuffer` at boot - `USER_DATA_FILE` (optional) - Path to a user data file that will be embedded in ROM, with its address and size available via environment variables `USER_DATA_ADDR` and `USER_DATA_SIZE` - `DMBOOT_CONFIG_DIR` (optional, defaults to `build/configs`) - Path to a directory that will be converted to a dmffs filesystem image and mounted at `/configs/` at boot time. By default, configuration files from `modules.dmd` are downloaded to this directory. diff --git a/configs/arch/armv7/cfg.cmake b/configs/arch/armv7/cfg.cmake index e69de29..44a6bc8 100644 --- a/configs/arch/armv7/cfg.cmake +++ b/configs/arch/armv7/cfg.cmake @@ -0,0 +1,5 @@ +# ====================================================================== +# ARMv7 objcopy format configuration +# ====================================================================== +set(DMBOOT_OBJCOPY_OUTPUT_FORMAT "elf32-littlearm" CACHE STRING "objcopy output target format for ARMv7") +set(DMBOOT_OBJCOPY_BINARY_ARCH "arm" CACHE STRING "objcopy binary architecture for ARMv7") diff --git a/configs/arch/xtensa/arch.ld b/configs/arch/xtensa/arch.ld new file mode 100644 index 0000000..88eb4a4 --- /dev/null +++ b/configs/arch/xtensa/arch.ld @@ -0,0 +1,3 @@ +OUTPUT_FORMAT("elf32-xtensa-le"); +OUTPUT_ARCH(xtensa); +TARGET("elf32-xtensa-le"); diff --git a/configs/arch/xtensa/cfg.cmake b/configs/arch/xtensa/cfg.cmake new file mode 100644 index 0000000..43ffcf0 --- /dev/null +++ b/configs/arch/xtensa/cfg.cmake @@ -0,0 +1,5 @@ +# ====================================================================== +# Xtensa objcopy format configuration +# ====================================================================== +set(DMBOOT_OBJCOPY_OUTPUT_FORMAT "elf32-xtensa-le" CACHE STRING "objcopy output target format for Xtensa") +set(DMBOOT_OBJCOPY_BINARY_ARCH "xtensa" CACHE STRING "objcopy binary architecture for Xtensa") diff --git a/configs/arch/xtensa/lx7/cfg.cmake b/configs/arch/xtensa/lx7/cfg.cmake new file mode 100644 index 0000000..715d1d7 --- /dev/null +++ b/configs/arch/xtensa/lx7/cfg.cmake @@ -0,0 +1,4 @@ +# ====================================================================== +# DMOD Tools Configuration – Xtensa LX7 +# ====================================================================== +set(DMOD_TOOLS_NAME "arch/xtensa/lx7" CACHE STRING "Name of the tools configuration") diff --git a/configs/arch/xtensa/lx7/tools-cfg.cmake b/configs/arch/xtensa/lx7/tools-cfg.cmake new file mode 100644 index 0000000..412a44f --- /dev/null +++ b/configs/arch/xtensa/lx7/tools-cfg.cmake @@ -0,0 +1,32 @@ +# ====================================================================== +# Toolchain configuration for Xtensa LX7 (ESP32-S3) +# +# This file is a local override that is consulted by CMakeLists.txt +# before falling back to the dmod library's tools-cfg.cmake. It sets +# the cross-compilation toolchain for the Espressif xtensa-esp32s3-elf +# toolchain. +# ====================================================================== + +set(CMAKE_SYSTEM_NAME Generic) +set(CMAKE_SYSTEM_PROCESSOR xtensa) + +set(_XTENSA_TOOLCHAIN_PREFIX "xtensa-esp32s3-elf-") + +set(CMAKE_C_COMPILER ${_XTENSA_TOOLCHAIN_PREFIX}gcc) +set(CMAKE_CXX_COMPILER ${_XTENSA_TOOLCHAIN_PREFIX}g++) +set(CMAKE_ASM_COMPILER ${_XTENSA_TOOLCHAIN_PREFIX}gcc) + +find_program(CMAKE_OBJCOPY ${_XTENSA_TOOLCHAIN_PREFIX}objcopy REQUIRED) +find_program(CMAKE_OBJDUMP ${_XTENSA_TOOLCHAIN_PREFIX}objdump REQUIRED) +find_program(CMAKE_SIZE ${_XTENSA_TOOLCHAIN_PREFIX}size REQUIRED) + +# GDB binary – assigned to ARM_GDB to stay compatible with scripts/targets.cmake +find_program(ARM_GDB ${_XTENSA_TOOLCHAIN_PREFIX}gdb REQUIRED) + +# Use call0 ABI (no register-window management) and long-call support +set(CMAKE_C_FLAGS_INIT "-mabi=call0 -mlongcalls" CACHE INTERNAL "") +set(CMAKE_CXX_FLAGS_INIT "-mabi=call0 -mlongcalls" CACHE INTERNAL "") +set(CMAKE_ASM_FLAGS_INIT "-mabi=call0 -mlongcalls" CACHE INTERNAL "") + +# Prevent CMake from testing the compiler with a simple executable +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY CACHE INTERNAL "") diff --git a/configs/board/t-deck-pro/board.cmake b/configs/board/t-deck-pro/board.cmake new file mode 100644 index 0000000..a50d28b --- /dev/null +++ b/configs/board/t-deck-pro/board.cmake @@ -0,0 +1,3 @@ +# Board configuration for LILYGO T-Deck Pro +# MCU: ESP32-S3FN16R8 (Xtensa LX7 dual-core, 240 MHz, 16 MB flash, 8 MB PSRAM) +set(TARGET "ESP32S3" CACHE STRING "Target microcontroller" FORCE) diff --git a/configs/mcu/ESP32S3.cmake b/configs/mcu/ESP32S3.cmake new file mode 100644 index 0000000..42a0fbe --- /dev/null +++ b/configs/mcu/ESP32S3.cmake @@ -0,0 +1,34 @@ +# ====================================================================== +# Configuration +# ====================================================================== +set(DMBOOT_MCU_NAME "esp32s3fn16r8" CACHE STRING "Name of the target microcontroller") +set(DMBOOT_MCU_SERIES "esp32s3" CACHE STRING "Series of the target microcontroller") +set(DMBOOT_ARCH "xtensa" CACHE STRING "Architecture of the target microcontroller") +set(DMBOOT_ARCH_FAMILY "lx7" CACHE STRING "Microcontroller family") + +# ====================================================================== +# RTOS Clock Configuration +# ====================================================================== +set(DMOSI_CPU_CLOCK_HZ 240000000 CACHE STRING "CPU clock frequency for dmosi-freertos configuration") + +# ====================================================================== +# OpenOCD Configuration +# ====================================================================== +# OpenOCD is only required when not using emulation mode +if(NOT DMBOOT_EMULATION) + find_program(OPENOCD openocd REQUIRED) +endif() +# ESP32-S3 has a built-in USB JTAG interface (no external probe required) +set(OPENOCD_INTERFACE "interface/esp_usb_jtag.cfg" CACHE STRING "OpenOCD interface configuration file") +set(OPENOCD_TARGET "target/esp32s3.cfg" CACHE STRING "OpenOCD target configuration file") + +# ====================================================================== +# DMOD Configuration +# ====================================================================== +set(DMOD_CPU_NAME ${DMBOOT_MCU_NAME} CACHE STRING "Name of the target cpu, if empty, the target is generic") + +# ====================================================================== +# Include architecture configuration +# ====================================================================== +include(configs/arch/${DMBOOT_ARCH}/cfg.cmake) +include(configs/arch/${DMBOOT_ARCH}/${DMBOOT_ARCH_FAMILY}/cfg.cmake) diff --git a/configs/mcu/esp32s3/esp32s3fn16r8/flash.dmd b/configs/mcu/esp32s3/esp32s3fn16r8/flash.dmd new file mode 100644 index 0000000..4126482 --- /dev/null +++ b/configs/mcu/esp32s3/esp32s3fn16r8/flash.dmd @@ -0,0 +1,5 @@ +# +# ESP32-S3FN16R8 flash modules – MCU-specific modules for flash +# +dmclk +dmgpio diff --git a/configs/mcu/esp32s3/esp32s3fn16r8/mcu.ld b/configs/mcu/esp32s3/esp32s3fn16r8/mcu.ld new file mode 100644 index 0000000..b91a6a7 --- /dev/null +++ b/configs/mcu/esp32s3/esp32s3fn16r8/mcu.ld @@ -0,0 +1,25 @@ +/* + * ESP32-S3FN16R8 memory layout (bare-metal / direct OpenOCD load): + * + * IRAM (instruction RAM, no cache needed): + * 0x40374000 – 0x403BFFFF 320 KB – application code executed directly + * + * DRAM (data RAM, internal SRAM1 as data): + * 0x3FC88000 – 0x3FCEFFFF 416 KB – application data / BSS / stack / heap + * + * External flash (SPI, 16 MB) – available after cache is enabled: + * IROM (instruction): 0x42000000 + offset + * DROM (read-only data): 0x3C000000 + offset + * + * For development (direct OpenOCD load without the ROM bootloader cache + * setup), all code and data are placed in the internal IRAM / DRAM regions + * so that the firmware runs without any cache initialisation. + */ + +MEMORY +{ + rom (rx) : ORIGIN = 0x40374000, LENGTH = 320K + ram (rwx) : ORIGIN = 0x3FC88000, LENGTH = 416K + ext (rwx) : ORIGIN = 0x3C000000, LENGTH = 0 + dma (rwx) : ORIGIN = 0x3FC88000, LENGTH = 0 +} diff --git a/configs/mcu/esp32s3/esp32s3fn16r8/sdcard.dmd b/configs/mcu/esp32s3/esp32s3fn16r8/sdcard.dmd new file mode 100644 index 0000000..ce3c1a9 --- /dev/null +++ b/configs/mcu/esp32s3/esp32s3fn16r8/sdcard.dmd @@ -0,0 +1,3 @@ +# +# ESP32-S3FN16R8 sdcard modules – MCU-specific modules for sdcard +# diff --git a/docs/cmake-options.md b/docs/cmake-options.md index bd45439..b32789d 100644 --- a/docs/cmake-options.md +++ b/docs/cmake-options.md @@ -8,8 +8,8 @@ These options are defined in the top-level `CMakeLists.txt` and control the over | Option | Type | Default | Description | |--------|------|---------|-------------| -| `TARGET` | STRING | `STM32F746xG` | Target microcontroller. Supported values: `STM32F746xG`, `STM32F407G`. Ignored when `BOARD` is set. | -| `BOARD` | STRING | *(empty)* | Board name (optional). When set, `TARGET` is derived from `configs/board//board.cmake`. Example: `stm32f746g-disco`. | +| `TARGET` | STRING | `STM32F746xG` | Target microcontroller. Supported values: `STM32F746xG`, `STM32F407G`, `STM32F746ZG`, `ESP32S3`. Ignored when `BOARD` is set. | +| `BOARD` | STRING | *(empty)* | Board name (optional). When set, `TARGET` is derived from `configs/board//board.cmake`. Examples: `stm32f746g-disco`, `t-deck-pro`. | | `STARTUP_DMP_FILE` | FILEPATH | *(empty)* | Path to an optional `.dmp` startup package file to embed in ROM. Loaded using `Dmod_AddPackageBuffer` at boot. | | `USER_DATA_FILE` | FILEPATH | *(empty)* | Path to an optional user data file to embed in ROM. Its address and size are accessible via `USER_DATA_ADDR` and `USER_DATA_SIZE` environment variables. | | `DMBOOT_CONFIG_DIR` | PATH | `/configs` | Path to a directory that will be converted to a dmffs filesystem image and mounted at `/configs/` at boot time. | @@ -117,6 +117,15 @@ cmake -DBOARD=stm32f746g-disco -S . -B build cmake --build build ``` +### Build for LILYGO T-Deck Pro (ESP32-S3) + +```bash +cmake -DBOARD=t-deck-pro -S . -B build +cmake --build build +# Flash via the built-in USB JTAG using Espressif OpenOCD +cmake --build build --target install-firmware +``` + ### Build for Renode emulation ```bash diff --git a/scripts/embed_binary.cmake b/scripts/embed_binary.cmake index bd25970..f925ae5 100644 --- a/scripts/embed_binary.cmake +++ b/scripts/embed_binary.cmake @@ -63,8 +63,8 @@ function(embed_binary_file) OUTPUT "${EMBED_OUTPUT_OBJECT}" COMMAND ${CMAKE_OBJCOPY} --input-target=binary - --output-target=elf32-littlearm - --binary-architecture=arm + --output-target=${DMBOOT_OBJCOPY_OUTPUT_FORMAT} + --binary-architecture=${DMBOOT_OBJCOPY_BINARY_ARCH} --rename-section .data=${EMBED_SECTION_NAME},alloc,load,readonly,data,contents "${EMBED_INPUT_FILE_ABS}" "${EMBED_OUTPUT_OBJECT}" diff --git a/scripts/romfs.cmake b/scripts/romfs.cmake index 2506217..33c93a4 100644 --- a/scripts/romfs.cmake +++ b/scripts/romfs.cmake @@ -60,8 +60,8 @@ if(DMBOOT_CONFIG_DIR) OUTPUT "${CONFIG_FS_OBJECT}" COMMAND ${CMAKE_OBJCOPY} --input-target=binary - --output-target=elf32-littlearm - --binary-architecture=arm + --output-target=${DMBOOT_OBJCOPY_OUTPUT_FORMAT} + --binary-architecture=${DMBOOT_OBJCOPY_BINARY_ARCH} --rename-section .data=.embedded.config_fs,alloc,load,readonly,data,contents "${CONFIG_FS_IMAGE}" "${CONFIG_FS_OBJECT}" diff --git a/src/arch/xtensa/CMakeLists.txt b/src/arch/xtensa/CMakeLists.txt new file mode 100644 index 0000000..cf64cbc --- /dev/null +++ b/src/arch/xtensa/CMakeLists.txt @@ -0,0 +1,13 @@ +# ====================================================================== +# Xtensa common library +# ====================================================================== +add_library(dmboot_arch STATIC + dmod_critical.c +) + +target_link_libraries(dmboot_arch + PRIVATE + dmod +) + +add_subdirectory(${DMBOOT_ARCH_FAMILY}) diff --git a/src/arch/xtensa/dmod_critical.c b/src/arch/xtensa/dmod_critical.c new file mode 100644 index 0000000..b1a590f --- /dev/null +++ b/src/arch/xtensa/dmod_critical.c @@ -0,0 +1,105 @@ +/** + * MIT License + * + * Copyright (c) 2024 Patryk Kubiak + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * @brief Critical section implementation for Xtensa LX7 (ESP32-S3) architecture + * @date 2024-11-06 + * @author Patryk Kubiak + * + * Implements nested critical sections by saving/restoring the PS (Processor + * State) register's interrupt level. On entry the interrupt level is raised + * to 15 (all interrupts masked); on exit the original PS is restored when the + * outermost critical section is left. + * + * Using the Xtensa RSIL (Read and Set Interrupt Level) instruction ensures + * that the read-modify-write is atomic. + */ + +#include "dmod.h" +#include + +static uint32_t critical_nesting = 0; +static uint32_t saved_ps = 0; + +/** + * @brief Assertion handler for critical section functions + */ +void Dmod_Assert( int Condition, const char* Message, const char* File, + int Line, const char* Function ) +{ + if( !Condition ) + { + DMOD_LOG_ERROR( "Assertion failed: %s, at %s:%d in function %s\n", + Message, File, Line, Function ); + __asm__ volatile ( "break 1, 15" ); /* Xtensa debug breakpoint */ + while( 1 ); + } +} + +/** + * @brief Enter critical section + * + * Raises PS.INTLEVEL to 15, masking all interrupts. Saves the original PS + * only for the outermost nesting level so that it can be restored on exit. + */ +void Dmod_EnterCritical( void ) +{ + uint32_t ps; + __asm__ volatile ( + "rsil %0, 15\n" /* read PS into ps, then set INTLEVEL = 15 */ + : "=a"( ps ) + : + : "memory" + ); + if( critical_nesting == 0 ) + { + saved_ps = ps; + } + critical_nesting++; +} + +/** + * @brief Exit critical section + * + * Decrements the nesting counter. When the counter reaches zero the original + * PS is written back, re-enabling interrupts at the level they had before the + * outermost Dmod_EnterCritical() call. + */ +void Dmod_ExitCritical( void ) +{ + DMOD_ASSERT( critical_nesting > 0 ); + + if( critical_nesting > 0 ) + { + critical_nesting--; + if( critical_nesting == 0 ) + { + __asm__ volatile ( + "wsr %0, PS\n" + "rsync\n" + : + : "a"( saved_ps ) + : "memory" + ); + } + } +} diff --git a/src/arch/xtensa/lx7/CMakeLists.txt b/src/arch/xtensa/lx7/CMakeLists.txt new file mode 100644 index 0000000..4d61608 --- /dev/null +++ b/src/arch/xtensa/lx7/CMakeLists.txt @@ -0,0 +1,9 @@ +# ====================================================================== +# Startup library – Xtensa LX7 +# ====================================================================== +add_library(dmboot_startup STATIC + startup.S +) + +# Assembler files need a C-compatible linker language hint +set_target_properties(dmboot_startup PROPERTIES LINKER_LANGUAGE C) diff --git a/src/arch/xtensa/lx7/startup.S b/src/arch/xtensa/lx7/startup.S new file mode 100644 index 0000000..69eff45 --- /dev/null +++ b/src/arch/xtensa/lx7/startup.S @@ -0,0 +1,138 @@ +/******************************************************************************* + * Xtensa LX7 (ESP32-S3) startup code + * + * chip: Xtensa LX7 (ESP32-S3) + * ABI: call0 (no register-window overhead; compiled with -mabi=call0) + * compiler: xtensa-esp32s3-elf-gcc + * + * Startup sequence mirrors the ARMv7-M startup so that the rest of the + * project's C runtime initialisation conventions are preserved: + * 1. Disable all interrupts (PS.INTLEVEL = 15) + * 2. Set up the stack pointer + * 3. Call low_level_init_0() (.data / .bss NOT yet initialised) + * 4. Copy .data from ROM image to RAM + * 5. Zero-initialise .bss + * 6. Call low_level_init_1() + * 7. Call main(0, NULL) + * 8. Loop forever on return + *******************************************************************************/ + +/*============================================================================== + Reset vector – placed in the .vectors section so the linker script can + position it at the start of the ROM (IRAM) region. +==============================================================================*/ + +.section .vectors, "ax" +.balign 4 +.global Reset_Handler +.type Reset_Handler, @function + +Reset_Handler: + +/*------------------------------------------------------------------------------ + Disable all interrupts by setting PS.INTLEVEL to 15 (maximum level). + Using RSIL to read the old PS value (discarded) and set the new level. +------------------------------------------------------------------------------*/ + rsil a0, 15 /* PS.INTLEVEL = 15 – all interrupts masked */ + rsync + +/*------------------------------------------------------------------------------ + Set up the process stack pointer. + __stack_end__ is defined by the linker script (top of the .stack section). +------------------------------------------------------------------------------*/ + movi a1, __stack_end__ + +/*------------------------------------------------------------------------------ + Call low_level_init_0() – hardware clock / watchdog setup that must happen + before .data / .bss are initialised. Uses call0 ABI: return address in a0, + callee must preserve a1 (sp). +------------------------------------------------------------------------------*/ + call0 low_level_init_0 + +/*------------------------------------------------------------------------------ + Copy initialised data (.data section) from its load address in ROM to its + run-time address in RAM. + + Registers used (caller-saved in call0 ABI, safe to clobber here): + a0 – source pointer (__data_init_start__) + a2 – destination pointer (__data_start__) + a3 – end of destination (__data_end__) + a4 – temporary word value +------------------------------------------------------------------------------*/ + movi a0, __data_init_start__ + movi a2, __data_start__ + movi a3, __data_end__ + bgeu a2, a3, .Ldata_done /* skip if .data is empty */ +.Ldata_loop: + l32i a4, a0, 0 + s32i a4, a2, 0 + addi a0, a0, 4 + addi a2, a2, 4 + bltu a2, a3, .Ldata_loop +.Ldata_done: + +/*------------------------------------------------------------------------------ + Zero-initialise .bss section. + + Registers used: + a0 – zero constant + a2 – current pointer (__bss_start__) + a3 – end pointer (__bss_end__) +------------------------------------------------------------------------------*/ + movi a0, 0 + movi a2, __bss_start__ + movi a3, __bss_end__ + bgeu a2, a3, .Lbss_done /* skip if .bss is empty */ +.Lbss_loop: + s32i a0, a2, 0 + addi a2, a2, 4 + bltu a2, a3, .Lbss_loop +.Lbss_done: + +/*------------------------------------------------------------------------------ + Re-load the stack pointer (may have been modified during init). +------------------------------------------------------------------------------*/ + movi a1, __stack_end__ + +/*------------------------------------------------------------------------------ + Call low_level_init_1() – second-stage initialisation with .data / .bss + already set up (e.g. peripheral and RTOS clock initialisation). +------------------------------------------------------------------------------*/ + call0 low_level_init_1 + +/*------------------------------------------------------------------------------ + Call main(argc=0, argv=NULL). + In the call0 ABI the first two arguments are passed in a2 and a3. +------------------------------------------------------------------------------*/ + movi a2, 0 /* argc = 0 */ + movi a3, 0 /* argv = NULL */ + call0 main + +/*------------------------------------------------------------------------------ + Loop forever if main() returns (should never happen in a bootloader). +------------------------------------------------------------------------------*/ +.Lforever: + j .Lforever + +/*============================================================================== + Default (weak) implementations of low_level_init_0 / low_level_init_1. + A board-specific file may override them by providing a strong symbol. +==============================================================================*/ + +.balign 4 +.global __default_low_level_init +.type __default_low_level_init, @function +__default_low_level_init: + ret.n + +.weak low_level_init_0 +.global low_level_init_0 +.set low_level_init_0, __default_low_level_init + +.weak low_level_init_1 +.global low_level_init_1 +.set low_level_init_1, __default_low_level_init + +/******************************************************************************* + * END OF FILE + *******************************************************************************/