Skip to content
Open
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
38 changes: 38 additions & 0 deletions .github/workflows/host_tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
name: Host Tests

on:
push:

jobs:
host-tests:
runs-on: ubuntu-24.04

steps:
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive

- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
build-essential \
cmake \
ninja-build \
ruby

- name: Cache CMake build
uses: actions/cache@v4
with:
path: unittests/host/build
key: ${{ runner.os }}-cmake-${{ hashFiles('unittests/host/CMakeLists.txt', 'unittests/host/cmock_config.yml') }}
restore-keys: |
${{ runner.os }}-cmake-

- name: Run host tests
run: |-
cd unittests/host
chmod +x run-tests.sh
./run-tests.sh
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
build*/
toolchains/
venv/
__pycache__
9 changes: 9 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
[submodule "esp-stub-lib"]
path = esp-stub-lib
url = https://github.com/espressif/esp-stub-lib
shallow = true
[submodule "unittests/CMock"]
path = unittests/CMock
url = https://github.com/ThrowTheSwitch/CMock.git
shallow = true
[submodule "unittests/Unity"]
path = unittests/Unity
url = https://github.com/ThrowTheSwitch/Unity.git
shallow = true
77 changes: 29 additions & 48 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,70 +1,51 @@
cmake_minimum_required(VERSION 3.28)

# Validate TARGET_CHIP parameter
option(TARGET_CHIP "Target ESP chip" "OFF")

if("${TARGET_CHIP}" STREQUAL "OFF")
message(FATAL_ERROR "Please set target chip via -DTARGET_CHIP.")
endif()

# Detect toolchain prefix and flags early
set(EXTRA_RISCV_FLAGS -march=rv32imc -mabi=ilp32 -msmall-data-limit=0)
set(EXTRA_XTENSA_FLAGS -mtext-section-literals -mlongcalls)

set(ESP8266_FLAGS "-DESP8266" ${EXTRA_XTENSA_FLAGS})
set(XTENSA_FLAGS "-DXTENSA" ${EXTRA_XTENSA_FLAGS})
set(RISCV_FLAGS "-DRISCV" ${EXTRA_RISCV_FLAGS})
# Set up ESP_TARGET for toolchain and build system
set(ESP_TARGET ${TARGET_CHIP} CACHE STRING "Target ESP chip" FORCE)

set(ESP32_XTENSA_CHIPS esp32 esp32s2 esp32s3)

if(TARGET_CHIP IN_LIST ESP32_XTENSA_CHIPS)
set(TOOLCHAIN_EXECUTABLE_PREFIX "xtensa-${TARGET_CHIP}-elf-")
set(CHIP_FLAGS "${XTENSA_FLAGS}")
elseif(TARGET_CHIP STREQUAL "esp8266")
set(TOOLCHAIN_EXECUTABLE_PREFIX "xtensa-lx106-elf-")
set(CHIP_FLAGS "${ESP8266_FLAGS}")
# The lx106 toolchain is of version 8.4 which doesn't support the --dependency-file linker option.
set(CMAKE_LINK_DEPENDS_USE_LINKER FALSE) # requires CMake 3.27 or higher
else()
set(TOOLCHAIN_EXECUTABLE_PREFIX "riscv32-esp-elf-")
set(CHIP_FLAGS "${RISCV_FLAGS}")
endif()

# Set compilers BEFORE project() to avoid host detection (e.g., -arch arm64 or -isysroot)
set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_C_COMPILER ${TOOLCHAIN_EXECUTABLE_PREFIX}gcc)
set(CMAKE_LINKER ${TOOLCHAIN_EXECUTABLE_PREFIX}gcc)
set(CMAKE_EXECUTABLE_SUFFIX_C ".elf")
# Configure ESP toolchain
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
include(esp-targets)
configure_esp_toolchain(${TARGET_CHIP})

project(flasher-stub C)

# Configure ESP firmware build
get_esp_target_flags(${TARGET_CHIP} TARGET_FLAGS)

# C standard set based on the GCC version of the toolchain for ESP8266 (oldest one)
set(COMMON_FLAGS -std=gnu17 -Wall -Wextra -Werror -Wshadow -Wundef -Wconversion -Os -fno-common -nostdlib -fno-builtin -ffunction-sections)
# Firmware build flags
set(ESP_FIRMWARE_C_FLAGS
-std=gnu17
-Wall -Wextra -Werror -Wshadow -Wundef -Wconversion
-fno-common -ffunction-sections
-Os # Optimize for size
-nostdlib -fno-builtin # No standard library
)

add_compile_options(${COMMON_FLAGS})
add_compile_options(${CHIP_FLAGS})
set(ESP_FIRMWARE_LINKER_FLAGS
-Wl,-static -Wl,--gc-sections
-nostdlib # No standard library linking
)

set(LINKER_FLAGS -Wl,-static -Wl,--gc-sections)
# Create executable
set(TARGET_NAME stub-${TARGET_CHIP} CACHE STRING "Target name")
message(STATUS "Building for ${TARGET_CHIP}")
add_executable(${TARGET_NAME})

# Set up linker script
set(LINKER_SCRIPTS_DIR "${CMAKE_SOURCE_DIR}/src/ld")
set(CHIP_LINKER_SCRIPT "${LINKER_SCRIPTS_DIR}/${TARGET_CHIP}.ld")

target_link_options(${TARGET_NAME} PRIVATE
${LINKER_FLAGS}
${CHIP_FLAGS}
"-T${CHIP_LINKER_SCRIPT}"
-Wl,-Map=${CMAKE_BINARY_DIR}/${TARGET_NAME}.map
)

add_subdirectory(${CMAKE_SOURCE_DIR}/src)
# Add esp-stub-lib
add_compile_options(${ESP_FIRMWARE_C_FLAGS})
add_compile_options(${TARGET_FLAGS})

set(ESP_TARGET ${TARGET_CHIP} CACHE INTERNAL "Pass TARGET_CHIP as ESP_TARGET")
add_subdirectory(${CMAKE_SOURCE_DIR}/esp-stub-lib)
target_link_libraries(${TARGET_NAME} PRIVATE esp-stub-lib)

add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
COMMAND ${CMAKE_SOURCE_DIR}/tools/elf2json.py ${CMAKE_BINARY_DIR}/${TARGET_NAME}${CMAKE_EXECUTABLE_SUFFIX_C} ${CMAKE_BINARY_DIR}/${TARGET_CHIP}.json
COMMENT "Running elf2json.py to produce a JSON file output"
)
# Add stub target
add_subdirectory(${CMAKE_SOURCE_DIR}/src)
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,18 @@ Then run the following export script in every terminal where the project is used

### Esptool

[Esptool](https://github.com/espressif/esptool/) is needed for ELF file analysis. Run the following commands in order to install it:
[Esptool](https://github.com/espressif/esptool/) is needed for ELF file analysis.

**Virtual Environment Requirement**: It is strongly recommended to use a virtual environment for installing esptool to avoid conflicts with system packages and ensure reproducible builds. The virtual environment isolates the Python dependencies and prevents version conflicts that could affect the build process.

Run the following commands in order to install it:
```sh
python -m venv venv
source venv/bin/activate
pip install esptool
```

Run the following command in every terminal where the project is used:
**Important**: Run the following command in every terminal where the project is used to activate the virtual environment:
```sh
source venv/bin/activate
```
Expand Down
66 changes: 66 additions & 0 deletions cmake/esp-targets.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# ESP Target Definitions and Utilities
# Shared across the entire esp-flasher-stub project

# Define supported ESP targets
set(ESP8266_TARGET esp8266)
set(XTENSA_TARGETS esp32 esp32s2 esp32s3)
set(RISCV_TARGETS esp32c2 esp32c3 esp32c5 esp32c6 esp32c61 esp32h2 esp32h21 esp32h4 esp32p4)
set(ALL_ESP_TARGETS ${ESP8266_TARGET} ${XTENSA_TARGETS} ${RISCV_TARGETS})

# Function to validate ESP target
function(validate_esp_target TARGET_CHIP)
if(NOT TARGET_CHIP IN_LIST ALL_ESP_TARGETS)
message(FATAL_ERROR "Invalid TARGET_CHIP '${TARGET_CHIP}'. Must be one of: ${ALL_ESP_TARGETS}")
endif()
endfunction()

# Function to get toolchain prefix for target
function(get_esp_toolchain_prefix TARGET_CHIP OUTPUT_VAR)
validate_esp_target(${TARGET_CHIP})

if(TARGET_CHIP IN_LIST XTENSA_TARGETS)
set(${OUTPUT_VAR} "xtensa-${TARGET_CHIP}-elf-" PARENT_SCOPE)
elseif(TARGET_CHIP STREQUAL "esp8266")
set(${OUTPUT_VAR} "xtensa-lx106-elf-" PARENT_SCOPE)
else()
set(${OUTPUT_VAR} "riscv32-esp-elf-" PARENT_SCOPE)
endif()
endfunction()

# Function to get target-specific compile flags
function(get_esp_target_flags TARGET_CHIP OUTPUT_VAR)
validate_esp_target(${TARGET_CHIP})

# Architecture-specific flags
set(EXTRA_RISCV_FLAGS -march=rv32imc -mabi=ilp32 -msmall-data-limit=0)
set(EXTRA_XTENSA_FLAGS -mtext-section-literals -mlongcalls)

if(TARGET_CHIP IN_LIST XTENSA_TARGETS)
set(${OUTPUT_VAR} "-DXTENSA" ${EXTRA_XTENSA_FLAGS} PARENT_SCOPE)
elseif(TARGET_CHIP STREQUAL "esp8266")
set(${OUTPUT_VAR} "-DESP8266" ${EXTRA_XTENSA_FLAGS} PARENT_SCOPE)
else()
set(${OUTPUT_VAR} "-DRISCV" ${EXTRA_RISCV_FLAGS} PARENT_SCOPE)
endif()
endfunction()

# Function to configure ESP toolchain (must be called before project())
function(configure_esp_toolchain TARGET_CHIP)
validate_esp_target(${TARGET_CHIP})
get_esp_toolchain_prefix(${TARGET_CHIP} TOOLCHAIN_PREFIX)

# Set system and compiler before project() to avoid host detection
set(CMAKE_SYSTEM_NAME Generic PARENT_SCOPE)
set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}gcc PARENT_SCOPE)
set(CMAKE_LINKER ${TOOLCHAIN_PREFIX}gcc PARENT_SCOPE)
set(CMAKE_EXECUTABLE_SUFFIX_C ".elf" PARENT_SCOPE)

# ESP8266 specific handling
if(TARGET_CHIP STREQUAL "esp8266")
set(CMAKE_LINK_DEPENDS_USE_LINKER FALSE PARENT_SCOPE)
endif()

message(STATUS "ESP toolchain configured for ${TARGET_CHIP}")
endfunction()

message(STATUS "ESP targets loaded: ${ALL_ESP_TARGETS}")
25 changes: 23 additions & 2 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,27 @@
set(COMMON_SOURCES "main.c" "slip.c" "command_handler.c")
add_executable(${TARGET_NAME}
"main.c"
"slip.c"
)

target_sources(${TARGET_NAME} PRIVATE ${COMMON_SOURCES})
target_compile_options(${TARGET_NAME} PRIVATE
${ESP_FIRMWARE_C_FLAGS}
${TARGET_FLAGS}
)

target_link_options(${TARGET_NAME} PRIVATE
${ESP_FIRMWARE_LINKER_FLAGS}
${TARGET_FLAGS}
"-T${CHIP_LINKER_SCRIPT}"
-Wl,-Map=${CMAKE_BINARY_DIR}/${TARGET_NAME}.map
)

target_link_libraries(${TARGET_NAME} PRIVATE esp-stub-lib)

# Add post-build JSON generation
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
COMMAND ${CMAKE_SOURCE_DIR}/tools/elf2json.py ${CMAKE_BINARY_DIR}/src/${TARGET_NAME}${CMAKE_EXECUTABLE_SUFFIX_C} ${CMAKE_BINARY_DIR}/${TARGET_CHIP}.json
COMMENT "Running elf2json.py to produce a JSON file output"
)

if(${TARGET_CHIP} STREQUAL "esp8266")
# zlib library will be available in esp-stub-lib, so miniz probably won't be needed.
Expand Down
1 change: 1 addition & 0 deletions unittests/CMock
Submodule CMock added at 94dcd3
Loading