Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
43 changes: 40 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand All @@ -104,6 +104,43 @@ cmake --build build

Board configuration files are located in `configs/board/<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):
Expand Down Expand Up @@ -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>/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>/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.
Expand Down
5 changes: 5 additions & 0 deletions configs/arch/armv7/cfg.cmake
Original file line number Diff line number Diff line change
@@ -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")
3 changes: 3 additions & 0 deletions configs/arch/xtensa/arch.ld
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
OUTPUT_FORMAT("elf32-xtensa-le");
OUTPUT_ARCH(xtensa);
TARGET("elf32-xtensa-le");
5 changes: 5 additions & 0 deletions configs/arch/xtensa/cfg.cmake
Original file line number Diff line number Diff line change
@@ -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")
4 changes: 4 additions & 0 deletions configs/arch/xtensa/lx7/cfg.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# ======================================================================
# DMOD Tools Configuration – Xtensa LX7
# ======================================================================
set(DMOD_TOOLS_NAME "arch/xtensa/lx7" CACHE STRING "Name of the tools configuration")
32 changes: 32 additions & 0 deletions configs/arch/xtensa/lx7/tools-cfg.cmake
Original file line number Diff line number Diff line change
@@ -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 "")
3 changes: 3 additions & 0 deletions configs/board/t-deck-pro/board.cmake
Original file line number Diff line number Diff line change
@@ -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)
34 changes: 34 additions & 0 deletions configs/mcu/ESP32S3.cmake
Original file line number Diff line number Diff line change
@@ -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)
5 changes: 5 additions & 0 deletions configs/mcu/esp32s3/esp32s3fn16r8/flash.dmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#
# ESP32-S3FN16R8 flash modules – MCU-specific modules for flash
#
dmclk
dmgpio
25 changes: 25 additions & 0 deletions configs/mcu/esp32s3/esp32s3fn16r8/mcu.ld
Original file line number Diff line number Diff line change
@@ -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
}
3 changes: 3 additions & 0 deletions configs/mcu/esp32s3/esp32s3fn16r8/sdcard.dmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#
# ESP32-S3FN16R8 sdcard modules – MCU-specific modules for sdcard
#
13 changes: 11 additions & 2 deletions docs/cmake-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -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>/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>/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 | `<build>/configs` | Path to a directory that will be converted to a dmffs filesystem image and mounted at `/configs/` at boot time. |
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions scripts/embed_binary.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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}"
Expand Down
4 changes: 2 additions & 2 deletions scripts/romfs.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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}"
Expand Down
13 changes: 13 additions & 0 deletions src/arch/xtensa/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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})
105 changes: 105 additions & 0 deletions src/arch/xtensa/dmod_critical.c
Original file line number Diff line number Diff line change
@@ -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 <patryk.kubiak90@gmail.com>
*
* 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 <stdint.h>

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"
);
}
}
}
9 changes: 9 additions & 0 deletions src/arch/xtensa/lx7/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)
Loading
Loading