diff --git a/ports/risc-v32/gnu/example_build/esp32c6/CMakeLists.txt b/ports/risc-v32/gnu/example_build/esp32c6/CMakeLists.txt new file mode 100644 index 000000000..d061898c5 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/esp32c6/CMakeLists.txt @@ -0,0 +1,158 @@ +# +# Copyright (c) 2026 Eclipse ThreadX contributors +# +# This program and the accompanying materials are made available under the +# terms of the MIT License which is available at +# https://opensource.org/licenses/MIT. +# +# SPDX-License-Identifier: MIT +# + +cmake_minimum_required(VERSION 3.16) + +# Generate compile_commands.json for clangd and IDE language servers +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Enforce Cross-Compilation Toolchain if not explicitly specified +if(NOT CMAKE_TOOLCHAIN_FILE) + set(CMAKE_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/cmake/esp32c6-toolchain.cmake") +endif() + +project(esp32c6_threadx_example C ASM) + +# ------------------------------------------------------------------------------ +# 1. ESP-IDF SDK Path Resolution (Cross-Platform: Windows & Linux) +# ------------------------------------------------------------------------------ +if(ESP_IDF_PATH) + # User specified -DESP_IDF_PATH=/path/to/esp-idf on CMake command line + set(ESP_IDF_PATH "${ESP_IDF_PATH}") +elseif(DEFINED ENV{IDF_PATH}) + # Standard Espressif environment variable set via export.sh / export.ps1 + set(ESP_IDF_PATH "$ENV{IDF_PATH}") +else() + # Search standard Espressif installation directories across OSes without hardcoding version numbers + file(GLOB ESP_IDF_DEFAULT_PATHS + "C:/esp/*/esp-idf" + "C:/esp/esp-idf" + "$ENV{USERPROFILE}/esp/*/esp-idf" + "$ENV{USERPROFILE}/esp/esp-idf" + "$ENV{HOME}/esp/*/esp-idf" + "$ENV{HOME}/esp/esp-idf" + ) + list(LENGTH ESP_IDF_DEFAULT_PATHS PATH_COUNT) + if(PATH_COUNT GREATER 0) + list(GET ESP_IDF_DEFAULT_PATHS 0 ESP_IDF_PATH) + else() + message(FATAL_ERROR "ESP-IDF SDK path not found. Please set the IDF_PATH environment variable or pass -DESP_IDF_PATH=/path/to/esp-idf.") + endif() +endif() + +message(STATUS "Using ESP-IDF hardware headers from: ${ESP_IDF_PATH}") + +# ------------------------------------------------------------------------------ +# 2. Include Directories +# ------------------------------------------------------------------------------ +# ESP32-C6 Hardware & SoC Component Headers +set(ESP32C6_IDF_INCLUDES + "${ESP_IDF_PATH}/components/soc/esp32c6/include" + "${ESP_IDF_PATH}/components/soc/esp32c6/register" + "${ESP_IDF_PATH}/components/soc/include" + "${ESP_IDF_PATH}/components/hal/include" + "${ESP_IDF_PATH}/components/hal/esp32c6/include" + "${ESP_IDF_PATH}/components/hal/platform_port/include" + "${ESP_IDF_PATH}/components/esp_hal_uart/include" + "${ESP_IDF_PATH}/components/esp_hal_uart/esp32c6/include" + "${ESP_IDF_PATH}/components/esp_rom/include" + "${ESP_IDF_PATH}/components/esp_rom/esp32c6/include" + "${ESP_IDF_PATH}/components/esp_common/include" + "${ESP_IDF_PATH}/components/esp_app_format/include" + "${ESP_IDF_PATH}/components/bootloader_support/include" + "${ESP_IDF_PATH}/components/riscv/include" +) + +# ThreadX Core & Port Include Directories +set(THREADX_INCLUDES + "${CMAKE_CURRENT_SOURCE_DIR}/../../../../../common/inc" + "${CMAKE_CURRENT_SOURCE_DIR}/../../inc" + "${CMAKE_CURRENT_SOURCE_DIR}/include" +) + +include_directories( + ${ESP32C6_IDF_INCLUDES} + ${THREADX_INCLUDES} +) + +# ------------------------------------------------------------------------------ +# 3. ThreadX Static Library Target +# ------------------------------------------------------------------------------ +file(GLOB THREADX_COMMON_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/../../../../common/src/*.c") +file(GLOB THREADX_PORT_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/../../src/*.S") + +add_library(threadx STATIC + ${THREADX_COMMON_SOURCES} + ${THREADX_PORT_SOURCES} +) + +target_include_directories(threadx PUBLIC ${THREADX_INCLUDES}) + +# ------------------------------------------------------------------------------ +# 4. ESP32-C6 Board Support Object Library (Assembly Startup & Pinned HAL) +# ------------------------------------------------------------------------------ +set(ESP32C6_BOARD_BSP_SOURCES + "${CMAKE_CURRENT_SOURCE_DIR}/entry.S" + "${CMAKE_CURRENT_SOURCE_DIR}/vectors.S" + "${CMAKE_CURRENT_SOURCE_DIR}/tx_initialize_low_level.S" + "${CMAKE_CURRENT_SOURCE_DIR}/platform/console.c" + "${CMAKE_CURRENT_SOURCE_DIR}/platform/interrupt.c" + "${CMAKE_CURRENT_SOURCE_DIR}/platform/systimer.c" + "${CMAKE_CURRENT_SOURCE_DIR}/platform/startup.c" + "${ESP_IDF_PATH}/components/hal/systimer_hal.c" +) + +add_library(esp32c6_bsp OBJECT + ${ESP32C6_BOARD_BSP_SOURCES} +) +target_include_directories(esp32c6_bsp PUBLIC ${THREADX_INCLUDES} ${ESP32C6_IDF_INCLUDES}) + +# ------------------------------------------------------------------------------ +# 5. Linker Configuration +# ------------------------------------------------------------------------------ +set(ESP32C6_LINKER_SEARCH_PATHS + "${ESP_IDF_PATH}/components/soc/esp32c6/ld" + "${ESP_IDF_PATH}/components/esp_rom/esp32c6/ld" +) + +set(ESP32C6_LINK_FLAGS "-T${CMAKE_CURRENT_SOURCE_DIR}/link.ld") +foreach(LD_PATH ${ESP32C6_LINKER_SEARCH_PATHS}) + string(APPEND ESP32C6_LINK_FLAGS " -L${LD_PATH}") +endforeach() + +# ------------------------------------------------------------------------------ +# 6. Executable Firmware Target & Image Generation (.elf -> .bin) +# ------------------------------------------------------------------------------ +add_executable(esp32c6_demo.elf + "${CMAKE_CURRENT_SOURCE_DIR}/demo_threadx.c" + $ +) + +target_include_directories(esp32c6_demo.elf PUBLIC ${THREADX_INCLUDES} ${ESP32C6_IDF_INCLUDES}) + +target_link_options(esp32c6_demo.elf PRIVATE + "-T${CMAKE_CURRENT_SOURCE_DIR}/link.ld" + "-L${ESP_IDF_PATH}/components/soc/esp32c6/ld" + "-L${ESP_IDF_PATH}/components/esp_rom/esp32c6/ld" +) + +target_link_libraries(esp32c6_demo.elf PRIVATE threadx) + +# Locate Python interpreter for esptool.py image conversion +find_package(Python3 COMPONENTS Interpreter REQUIRED) + +add_custom_command(TARGET esp32c6_demo.elf POST_BUILD + COMMAND ${Python3_EXECUTABLE} "${ESP_IDF_PATH}/components/esptool_py/esptool/esptool.py" + --chip esp32c6 elf2image -o "${CMAKE_CURRENT_BINARY_DIR}/esp32c6_demo.bin" + "${CMAKE_CURRENT_BINARY_DIR}/esp32c6_demo.elf" + COMMENT "Generating bootable ESP32-C6 binary image: esp32c6_demo.bin" +) + +message(STATUS "ESP32-C6 ThreadX complete build system ready.") diff --git a/ports/risc-v32/gnu/example_build/esp32c6/NOTICE.md b/ports/risc-v32/gnu/example_build/esp32c6/NOTICE.md new file mode 100644 index 000000000..6a1063ab6 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/esp32c6/NOTICE.md @@ -0,0 +1,40 @@ +# Notices for Eclipse ThreadX ESP32-C6 Port & Example + +This component contains software, hardware register definitions, linker scripts, and architectural patterns derived from third parties under open-source licenses. + +--- + +## 1. Eclipse ThreadX + +* **Copyright**: Copyright (c) 2024-present Microsoft Corporation; Copyright (c) 2026 Eclipse ThreadX contributors +* **License**: [MIT License](https://opensource.org/licenses/MIT) +* **Homepage**: https://github.com/eclipse-threadx/threadx + +--- + +## 2. Espressif Systems (ESP-IDF) + +This component incorporates hardware headers, linker script sections, ROM API definitions, and HAL drivers developed by Espressif Systems (Shanghai) CO LTD. + +* **Copyright**: Copyright (c) 2018-2024 Espressif Systems (Shanghai) CO LTD +* **License**: [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) +* **Homepage**: https://github.com/espressif/esp-idf + +--- + +## 3. 10xEngineers (RISC-V32 ThreadX Port) + +This component incorporates RISC-V32 port assembly templates and initialization hooks developed by 10xEngineers. + +* **Copyright**: Copyright (c) 2025 10xEngineers +* **License**: [MIT License](https://opensource.org/licenses/MIT) + +--- + +## 4. Zephyr RTOS Project + +Architectural patterns and RISC-V PLIC claim/complete dispatch design concepts were informed by the Zephyr RTOS project. + +* **Copyright**: Copyright (c) 2016-2024 Zephyr Project Contributors +* **License**: [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) +* **Homepage**: https://zephyrproject.org diff --git a/ports/risc-v32/gnu/example_build/esp32c6/README.md b/ports/risc-v32/gnu/example_build/esp32c6/README.md new file mode 100644 index 000000000..992b6e167 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/esp32c6/README.md @@ -0,0 +1,109 @@ + + +# ESP32-C6 (RISC-V32) Bare-Metal ThreadX Example Build + +This directory contains the **Eclipse ThreadX** bare-metal bring-up example for the **Espressif ESP32-C6** microcontroller board (`ESP32-C6-DevKitC-1`). + +--- + +> [!WARNING] +> ### Current Project Status & Known Issues +> +> **This example build is currently INCOMPLETE and has active hardware bring-up issues:** +> +> 1. **Serial Output & Reset Loop**: When flashed to physical hardware (`write_flash 0x10000 build/esp32c6_demo.bin`), the serial monitor shows **no console output** and the board continuously resets every few seconds (watchdog / trap reset loop). +> 2. **Timer Interrupt Freeze**: When stepping through JTAG with OpenOCD/GDB, execution successfully reaches `thread_0_entry`. However, after calling `tx_thread_sleep()`, the ThreadX scheduler gets **stuck inside `_tx_thread_schedule_loop()`** at `wfi` because the 100 Hz SYSTIMER hardware tick interrupt is not advancing `_tx_timer_system_clock`. +> +> **Where to start for the next developer**: +> * Refer to [`threadx_ESP32C6_native_WIFI_plan.md`](threadx_ESP32C6_native_WIFI_plan.md) for the architectural blueprint. + +--- + +## Architecture Overview + +* **Target CPU**: ESP32-C6 High-Performance RISC-V32 Core (`rv32imac_zicsr_zifencei`, 160 MHz) +* **RTOS Kernel**: Eclipse ThreadX RISC-V32 GNU Port +* **Boot Chain**: Espressif Mask ROM $\rightarrow$ 2nd-Stage Bootloader $\rightarrow$ ThreadX Application Entry (`_start`) +* **Drivers Included**: + * Console UART Output (`esp_rom_output_tx_one_char`) + * RISC-V PLIC & ESP32-C6 Interrupt Matrix Router (`interrupt.c`) + * Hardware SYSTIMER 100 Hz Kernel Tick Driver (`systimer.c` using pinned `systimer_hal.c`) + +--- + +## Build Prerequisites + +Ensure the following tools are installed and present in your system `PATH`: + +1. **Toolchain**: `riscv32-esp-elf-gcc` (Espressif RISC-V GCC 14+) +2. **Build System**: CMake 3.16+ and Ninja (or Make) +3. **Flash Tool**: `esptool.py` (Installed via `pip install esptool`) + +--- + +## Building the Example Firmware + +Run the following commands from this directory: + +```bash +# Configure CMake build system +cmake -B build -DCMAKE_TOOLCHAIN_FILE=cmake/esp32c6-toolchain.cmake + +# Build executable ELF and bootable binary image +cmake --build build +``` + +Upon successful compilation, the output artifacts will be placed under `build/`: +* `build/esp32c6_demo.elf`: Symbol-rich executable for OpenOCD/GDB debugging. +* `build/esp32c6_demo.bin`: Bootable binary image formatted for Espressif 2nd-stage bootloader. + +--- + +## Flashing to ESP32-C6 Hardware + +Connect your ESP32-C6 board via USB and run `esptool.py`: + +```bash +esptool.py --chip esp32c6 -p COM3 -b 460800 write_flash 0x10000 build/esp32c6_demo.bin +``` + +*(Replace `COM3` with your operating system's serial port path, e.g. `/dev/ttyUSB0` on Linux).* + +--- + +## Monitoring Serial Diagnostics + +Open any serial terminal emulator (such as PuTTY, TeraTerm, or `idf_monitor`) configured for: +* **Baudrate**: 115200 +* **Data bits**: 8 +* **Stop bits**: 1 +* **Parity**: None + +--- + +## Hardware JTAG Debugging with OpenOCD & GDB + +1. **Launch OpenOCD**: + ```bash + openocd -f board/esp32c6-builtin.cfg + ``` +2. **Launch GDB**: + ```bash + riscv32-esp-elf-gdb build/esp32c6_demo.elf + ``` +3. **Connect and Debug in GDB**: + ```gdb + target remote :3333 + mon reset halt + load + hbreak thread_0_entry + continue + ``` diff --git a/ports/risc-v32/gnu/example_build/esp32c6/cmake/esp32c6-toolchain.cmake b/ports/risc-v32/gnu/example_build/esp32c6/cmake/esp32c6-toolchain.cmake new file mode 100644 index 000000000..bfd6e9084 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/esp32c6/cmake/esp32c6-toolchain.cmake @@ -0,0 +1,37 @@ +# +# Copyright (c) 2026 Eclipse ThreadX contributors +# +# This program and the accompanying materials are made available under the +# terms of the MIT License which is available at +# https://opensource.org/licenses/MIT. +# +# SPDX-License-Identifier: MIT +# + +set(CMAKE_SYSTEM_NAME Generic) +set(CMAKE_SYSTEM_PROCESSOR riscv32) + +# Prevent CMake from executing default compiler test executables during configuration +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) + +# Cross-Compilation Toolchain Binary Names +set(CMAKE_C_COMPILER "riscv32-esp-elf-gcc") +set(CMAKE_CXX_COMPILER "riscv32-esp-elf-g++") +set(CMAKE_ASM_COMPILER "riscv32-esp-elf-gcc") +set(CMAKE_AR "riscv32-esp-elf-ar") +set(CMAKE_OBJCOPY "riscv32-esp-elf-objcopy") +set(CMAKE_OBJDUMP "riscv32-esp-elf-objdump") +set(CMAKE_SIZE "riscv32-esp-elf-size") + +# Target Architecture Flags for ESP32-C6 (RV32IMAC Zicsr Zifencei) +set(ESP32C6_TARGET_FLAGS "-march=rv32imac_zicsr_zifencei -mabi=ilp32 -mcmodel=medany -ffunction-sections -fdata-sections") + +set(CMAKE_C_FLAGS_INIT "${ESP32C6_TARGET_FLAGS} -Wall -Wextra") +set(CMAKE_CXX_FLAGS_INIT "${ESP32C6_TARGET_FLAGS} -Wall -Wextra") +set(CMAKE_ASM_FLAGS_INIT "${ESP32C6_TARGET_FLAGS}") + +# Search mode policies for bare-metal toolchains +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) diff --git a/ports/risc-v32/gnu/example_build/esp32c6/demo_threadx.c b/ports/risc-v32/gnu/example_build/esp32c6/demo_threadx.c new file mode 100644 index 000000000..5b6133389 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/esp32c6/demo_threadx.c @@ -0,0 +1,140 @@ +/*************************************************************************** + * Copyright (c) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + +#include "esp32c6_example.h" + +#define DEMO_STACK_SIZE 2048 + +static TX_THREAD thread_0; +static TX_THREAD thread_1; +static TX_MUTEX demo_mutex; + +/* 16-byte aligned static stack buffers guaranteed for RISC-V ABI */ +static uint8_t thread_0_stack[DEMO_STACK_SIZE] __attribute__((aligned(16))); +static uint8_t thread_1_stack[DEMO_STACK_SIZE] __attribute__((aligned(16))); + +static ULONG thread_0_counter = 0; +static ULONG thread_1_counter = 0; + +/* ------------------------------------------------------------------------------ + * Thread Functions + * ------------------------------------------------------------------------------ + */ + +static void thread_0_entry(ULONG thread_input) +{ + (void)thread_input; + + esp32c6_console_write("[ThreadX Demo] Thread 0 Started.\r\n"); + + while (1) { + /* Acquire Mutex */ + if (tx_mutex_get(&demo_mutex, TX_WAIT_FOREVER) == TX_SUCCESS) { + thread_0_counter++; + esp32c6_console_write("[Thread 0] Acquired Mutex, Counter: "); + + /* Simple decimal print */ + char buf[16]; + uint32_t val = (uint32_t)thread_0_counter; + int idx = 0; + if (val == 0) { + buf[idx++] = '0'; + } else { + char temp[16]; + int t_idx = 0; + while (val > 0) { + temp[t_idx++] = '0' + (val % 10); + val /= 10; + } + while (t_idx > 0) { + buf[idx++] = temp[--t_idx]; + } + } + buf[idx++] = '\r'; + buf[idx++] = '\n'; + buf[idx] = '\0'; + esp32c6_console_write(buf); + + /* Release Mutex */ + tx_mutex_put(&demo_mutex); + } + + /* Sleep for 50 ticks (500 ms) */ + tx_thread_sleep(50); + } +} + +static void thread_1_entry(ULONG thread_input) +{ + (void)thread_input; + + esp32c6_console_write("[ThreadX Demo] Thread 1 Started.\r\n"); + + while (1) { + /* Acquire Mutex */ + if (tx_mutex_get(&demo_mutex, TX_WAIT_FOREVER) == TX_SUCCESS) { + thread_1_counter++; + esp32c6_console_write("[Thread 1] Acquired Mutex, Counter: "); + + char buf[16]; + uint32_t val = (uint32_t)thread_1_counter; + int idx = 0; + if (val == 0) { + buf[idx++] = '0'; + } else { + char temp[16]; + int t_idx = 0; + while (val > 0) { + temp[t_idx++] = '0' + (val % 10); + val /= 10; + } + while (t_idx > 0) { + buf[idx++] = temp[--t_idx]; + } + } + buf[idx++] = '\r'; + buf[idx++] = '\n'; + buf[idx] = '\0'; + esp32c6_console_write(buf); + + /* Release Mutex */ + tx_mutex_put(&demo_mutex); + } + + /* Sleep for 100 ticks (1000 ms) */ + tx_thread_sleep(100); + } +} + +/* ------------------------------------------------------------------------------ + * ThreadX Application Definition Entry Point + * ------------------------------------------------------------------------------ + */ +void tx_application_define(void *first_unused_memory) +{ + (void)first_unused_memory; + + esp32c6_console_write("\r\n======================================================\r\n"); + esp32c6_console_write(" Eclipse ThreadX RTOS on ESP32-C6 (RISC-V32 Bare-Metal)\r\n"); + esp32c6_console_write("======================================================\r\n\r\n"); + + /* Create ThreadX Mutex */ + tx_mutex_create(&demo_mutex, "Demo Mutex", TX_NO_INHERIT); + + /* Create Thread 0 (Priority 16) with 16-byte aligned static stack */ + tx_thread_create(&thread_0, "Thread 0", thread_0_entry, 0, + thread_0_stack, DEMO_STACK_SIZE, + 16, 16, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Create Thread 1 (Priority 17) with 16-byte aligned static stack */ + tx_thread_create(&thread_1, "Thread 1", thread_1_entry, 0, + thread_1_stack, DEMO_STACK_SIZE, + 17, 17, TX_NO_TIME_SLICE, TX_AUTO_START); +} diff --git a/ports/risc-v32/gnu/example_build/esp32c6/entry.S b/ports/risc-v32/gnu/example_build/esp32c6/entry.S new file mode 100644 index 000000000..8c32d4b67 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/esp32c6/entry.S @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2025 10xEngineers + * Copyright (c) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + */ + +.section .iram0.text, "ax" +.global _start +.extern _esp32c6_vector_table +.extern c6_startup + +_start: + /* Force Direct Mode (Mode 0) by clearing bottom 2 mode bits of mtvec */ + la t0, _esp32c6_vector_table + andi t0, t0, -4 + csrw mtvec, t0 + + /* Clear MIE bit in mstatus during boot */ + csrc mstatus, 8 + + /* Set up global pointer and stack pointer */ + la gp, __global_pointer$ + la sp, _sp + + /* Clear BSS Section */ + la t0, _bss_start + la t1, _bss_end +1: + bgeu t0, t1, 2f + sw zero, 0(t0) + addi t0, t0, 4 + j 1b +2: + /* Call C startup entry point */ + call c6_startup + + /* Safety infinite loop if c6_startup returns */ +3: + j 3b diff --git a/ports/risc-v32/gnu/example_build/esp32c6/include/esp32c6_example.h b/ports/risc-v32/gnu/example_build/esp32c6/include/esp32c6_example.h new file mode 100644 index 000000000..4008880ba --- /dev/null +++ b/ports/risc-v32/gnu/example_build/esp32c6/include/esp32c6_example.h @@ -0,0 +1,120 @@ +/*************************************************************************** + * Copyright (c) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + +#ifndef ESP32C6_EXAMPLE_H +#define ESP32C6_EXAMPLE_H + +#include +#include +#include + +/* ThreadX API Header Inclusion */ +#include "tx_api.h" + +/* ESP-IDF ROM & Register Header Inclusions */ +#include "soc/soc.h" +#include "soc/interrupts.h" +#include "soc/reg_base.h" +#include "soc/plic_reg.h" +#include "hal/interrupt_plic_ll.h" +#include "esp_rom_sys.h" + +/* ------------------------------------------------------------------------------ + * RISC-V Register Bitmasks & Constants + * ------------------------------------------------------------------------------ + */ +#define RISCV_MCAUSE_INTERRUPT_FLAG (1UL << 31) /* Highest bit indicates hardware interrupt */ +#define RISCV_MCAUSE_EXCEPTION_CODE_MASK (0x7FFFFFFFUL)/* Low bits indicate interrupt/exception vector */ + +#define RISCV_MIE_MTIE (1UL << 7) /* Machine Timer Interrupt Enable */ +#define RISCV_MIE_MEIE (1UL << 11) /* Machine External Interrupt Enable */ + +/* ------------------------------------------------------------------------------ + * Hardware & Clock Constants + * ------------------------------------------------------------------------------ + */ +#define ESP32C6_CPU_CLK_FREQ_HZ (160000000UL) /* CPU Clock: 160 MHz */ +#define ESP32C6_SYSTIMER_CLK_FREQ_HZ (16000000UL) /* SYSTIMER Counter Clock: 16 MHz */ +#define ESP32C6_CONSOLE_BAUDRATE (115200) /* Default Console Baudrate */ + +/* ------------------------------------------------------------------------------ + * System Interrupt Assignment Mapping (RISC-V Machine Mode PLIC) + * ------------------------------------------------------------------------------ + */ +/* Hardware peripheral interrupt sources from ESP-IDF interrupts.h */ +#define ESP32C6_SYSTIMER_TICK_INTR_SRC ETS_SYSTIMER_TARGET0_EDGE_INTR_SOURCE + +/* CPU Interrupt Vector Line Numbers (Allocated for CPU Machine Interrupts) */ +#define ESP32C6_SYSTIMER_CPU_INTR_NUM 1 /* Dedicated CPU Line for ThreadX 100Hz Tick */ +#define ESP32C6_UART_CPU_INTR_NUM 2 /* Dedicated CPU Line for Console UART */ + +/* ------------------------------------------------------------------------------ + * Platform Hardware Driver API Prototypes + * ------------------------------------------------------------------------------ + */ + +/** + * @brief Early hardware platform initialization (Console, Interrupt Matrix, SYSTIMER). + * Called during startup before launching the ThreadX kernel. + */ +void esp32c6_platform_init(void); + +/** + * @brief Initializes the ESP32-C6 UART console for serial diagnostic logging. + */ +void esp32c6_console_init(void); + +/** + * @brief Outputs a single character to the console UART. + * @param c Character to output. + */ +void esp32c6_console_putc(char c); + +/** + * @brief Outputs a null-terminated string to the console UART. + * @param str Pointer to string. + */ +void esp32c6_console_write(const char *str); + +/** + * @brief Initializes the ESP32-C6 64-bit SYSTIMER Alarm 0 for periodic 100 Hz interrupts. + */ +void esp32c6_systimer_init(void); + +/** + * @brief Clears the SYSTIMER Alarm 0 interrupt flag. + */ +void esp32c6_systimer_clear_interrupt(void); + +/** + * @brief Initializes the ESP32-C6 Interrupt Matrix and PLIC for RISC-V M-mode. + */ +void esp32c6_interrupt_init(void); + +/** + * @brief Attaches a handler to a specific CPU interrupt vector line. + * @param cpu_intr_num CPU interrupt line (1-31). + * @param intr_source Hardware peripheral interrupt source (ETS_*_INTR_SOURCE). + * @param handler Function pointer to interrupt handler. + * @param arg Pointer to user parameter passed to handler. + */ +typedef void (*esp32c6_isr_handler_t)(void *arg); +void esp32c6_interrupt_attach(uint32_t cpu_intr_num, + uint32_t intr_source, + esp32c6_isr_handler_t handler, + void *arg); + +/** + * @brief Main interrupt dispatcher called from vectors.S. + * Queries RISC-V mcause / PLIC and invokes the attached handler. + */ +void esp32c6_interrupt_dispatch(void); + +#endif /* ESP32C6_EXAMPLE_H */ diff --git a/ports/risc-v32/gnu/example_build/esp32c6/include/sdkconfig.h b/ports/risc-v32/gnu/example_build/esp32c6/include/sdkconfig.h new file mode 100644 index 000000000..af9ad2101 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/esp32c6/include/sdkconfig.h @@ -0,0 +1,19 @@ +/* + * Target Configuration Header for ESP32-C6 ThreadX BSP + * + * SPDX-License-Identifier: MIT + */ + +#ifndef SDKCONFIG_H +#define SDKCONFIG_H + +/* Target SoC Architecture & Family Definitions */ +#define CONFIG_IDF_TARGET_ESP32C6 1 +#define CONFIG_IDF_TARGET "esp32c6" +#define CONFIG_IDF_TARGET_ARCH_RISCV 1 +#define CONFIG_IDF_FIRMWARE_CHIP_ID 0x000D + +/* Clock Configuration */ +#define CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ 160 + +#endif /* SDKCONFIG_H */ diff --git a/ports/risc-v32/gnu/example_build/esp32c6/include/tx_user.h b/ports/risc-v32/gnu/example_build/esp32c6/include/tx_user.h new file mode 100644 index 000000000..85db1f4c8 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/esp32c6/include/tx_user.h @@ -0,0 +1,48 @@ +/* + * Eclipse ThreadX User Configuration Header for ESP32-C6 (RISC-V32) + * + * SPDX-License-Identifier: MIT + */ + +#ifndef TX_USER_H +#define TX_USER_H + +/* ------------------------------------------------------------------------------ + * Kernel Timer & Tick Configuration + * ------------------------------------------------------------------------------ + * System timer tick frequency in Hz. + * 100 Ticks/Second = 10ms per tick interval. + */ +#ifndef TX_TIMER_TICKS_PER_SECOND +#define TX_TIMER_TICKS_PER_SECOND ((ULONG)100) +#endif + +/* ------------------------------------------------------------------------------ + * Thread Priority & Scheduling Settings + * ------------------------------------------------------------------------------ + * Number of thread priority levels (0 to TX_MAX_PRIORITIES - 1). + * 32 priority levels (0-31) provides an optimal balance between scheduler + * flexibility and low RAM usage. + */ +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + +/* Minimum stack size in bytes for ThreadX threads */ +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 512 +#endif + +/* ------------------------------------------------------------------------------ + * Safety & Debugging Options + * ------------------------------------------------------------------------------ + * Enable runtime stack checking to detect stack overflow conditions early. + */ +#ifndef TX_ENABLE_STACK_CHECKING +#define TX_ENABLE_STACK_CHECKING +#endif + +/* Enable event trace capability if needed for telemetry (disabled by default) */ +/* #define TX_ENABLE_EVENT_TRACE */ + +#endif /* TX_USER_H */ diff --git a/ports/risc-v32/gnu/example_build/esp32c6/link.ld b/ports/risc-v32/gnu/example_build/esp32c6/link.ld new file mode 100644 index 000000000..dc2e72608 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/esp32c6/link.ld @@ -0,0 +1,99 @@ +/* + * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD + * Modifications Copyright (c) 2026 Eclipse ThreadX contributors + * + * SPDX-License-Identifier: Apache-2.0 + */ + +ENTRY(_start) + +/* ------------------------------------------------------------------------------ + * Vendor ROM & Peripheral Symbol Linker Script Inclusions + * Reuses Espressif ROM API symbols and peripheral base register definitions + * ------------------------------------------------------------------------------ + */ +INCLUDE "esp32c6.peripherals.ld" +INCLUDE "esp32c6.rom.ld" +INCLUDE "esp32c6.rom.api.ld" +INCLUDE "esp32c6.rom.libgcc.ld" +INCLUDE "esp32c6.rom.newlib.ld" + +/* ------------------------------------------------------------------------------ + * Memory Map Definition + * ------------------------------------------------------------------------------ + * ESP32-C6 512 KB Internal SRAM (shared between IRAM and DRAM) + */ +MEMORY +{ + ram_seg (RWAX) : ORIGIN = 0x40800000, LENGTH = 0x80000 +} + +SECTIONS +{ + /* Boot Assembly Entry Point, Vendor App Descriptor, & RISC-V Vector Table */ + .iram0.text : + { + _text_start = .; + + /* + * Espressif 2nd-stage Bootloader Requirement: + * App description header (.rodata_desc) MUST be at the very top of segment 0. + * KEEP ensures --gc-sections does not discard the app descriptor. + */ + KEEP(*(.rodata_desc .rodata_desc.*)) + KEEP(*(.app_desc .app_desc.*)) + + KEEP(*(.iram0.text)) + KEEP(*(.vectors)) + KEEP(*(.entry)) + *(.text .text.*) + _text_end = .; + } > ram_seg + + /* Read-Only Data (String Literals & Constants) */ + .rodata : + { + . = ALIGN(4); + _rodata_start = .; + *(.rodata .rodata.*) + *(.srodata .srodata.*) + . = ALIGN(4); + _rodata_end = .; + } > ram_seg + + /* Initialized Global Data */ + .data : + { + . = ALIGN(4); + _data_start = .; + PROVIDE(__global_pointer$ = . + 0x800); + *(.data .data.*) + *(.sdata .sdata.*) + . = ALIGN(4); + _data_end = .; + } > ram_seg + + /* Uninitialized Global Data (BSS - Zeroed by entry.S) */ + .bss : + { + . = ALIGN(4); + _bss_start = .; + *(.bss .bss.*) + *(.sbss .sbss.*) + *(COMMON) + . = ALIGN(4); + _bss_end = .; + } > ram_seg + + /* Early System Initialization Stack (16-Byte Aligned for RISC-V C ABI) */ + .stack : + { + . = ALIGN(16); + . += 0x1000; /* 4 KB Stack */ + _sp = .; + } > ram_seg + + /* First available RAM address for ThreadX System Byte Pool */ + . = ALIGN(16); + _tx_initialize_unused_memory = .; +} diff --git a/ports/risc-v32/gnu/example_build/esp32c6/platform/console.c b/ports/risc-v32/gnu/example_build/esp32c6/platform/console.c new file mode 100644 index 000000000..a55967ca5 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/esp32c6/platform/console.c @@ -0,0 +1,32 @@ +/* + * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD + * Modifications Copyright (c) 2026 Eclipse ThreadX contributors + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp32c6_example.h" +#include "esp_rom_serial_output.h" + +void esp32c6_console_init(void) +{ + /* Console UART is initialized by the Espressif 2nd-stage bootloader */ +} + +void esp32c6_console_putc(char c) +{ + if (c == '\n') { + esp_rom_output_tx_one_char('\r'); + } + esp_rom_output_tx_one_char(c); +} + +void esp32c6_console_write(const char *str) +{ + if (!str) { + return; + } + while (*str) { + esp32c6_console_putc(*str++); + } +} diff --git a/ports/risc-v32/gnu/example_build/esp32c6/platform/interrupt.c b/ports/risc-v32/gnu/example_build/esp32c6/platform/interrupt.c new file mode 100644 index 000000000..2b6642826 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/esp32c6/platform/interrupt.c @@ -0,0 +1,87 @@ +/*************************************************************************** + * Copyright (c) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + * + * Inspired by and derived in part from architectural patterns in: + * - Espressif Systems ESP-IDF (Apache-2.0) + * - Zephyr RTOS Project (Apache-2.0) + **************************************************************************/ + +#include "esp32c6_example.h" + +#define MAX_CPU_INTERRUPTS 32 + +static esp32c6_isr_handler_t s_isr_table[MAX_CPU_INTERRUPTS]; +static void *s_isr_arg[MAX_CPU_INTERRUPTS]; + +void esp32c6_interrupt_init(void) +{ + for (uint32_t i = 0; i < MAX_CPU_INTERRUPTS; i++) { + s_isr_table[i] = NULL; + s_isr_arg[i] = NULL; + } + + /* Enable PCR INTMTX bus clock (bit 0 of 0x60096090) & clear reset */ + REG_WRITE(0x60096090, 1); + + /* Disable all PLIC CPU interrupt lines & clear pending interrupts on boot */ + REG_WRITE(PLIC_MXINT_ENABLE_REG, 0); + REG_WRITE(PLIC_MXINT_CLEAR_REG, 0xFFFFFFFF); + REG_WRITE(PLIC_MXINT_THRESH_REG, 0); + + /* Enable Machine External & Timer Interrupts in RISC-V mie CSR */ + uint32_t mie_mask = RISCV_MIE_MEIE | RISCV_MIE_MTIE; + __asm__ __volatile__ ("csrs mie, %0" :: "r"(mie_mask)); +} + +void esp32c6_interrupt_attach(uint32_t cpu_intr_num, + uint32_t intr_source, + esp32c6_isr_handler_t handler, + void *arg) +{ + if (cpu_intr_num == 0 || cpu_intr_num >= MAX_CPU_INTERRUPTS || handler == NULL) { + return; + } + + s_isr_table[cpu_intr_num] = handler; + s_isr_arg[cpu_intr_num] = arg; + + /* Route hardware peripheral interrupt source to CPU interrupt line */ + interrupt_plic_ll_route(intr_source, cpu_intr_num); + + /* Set default priority (level 1) */ + REG_WRITE(PLIC_MXINT_PRI_REG(cpu_intr_num), 1); + + /* Enable CPU interrupt line in PLIC */ + REG_WRITE(PLIC_MXINT_ENABLE_REG, REG_READ(PLIC_MXINT_ENABLE_REG) | (1UL << cpu_intr_num)); +} + +void esp32c6_interrupt_dispatch(void) +{ + uint32_t mcause_val; + __asm__ __volatile__ ("csrr %0, mcause" : "=r"(mcause_val)); + + /* Check if cause is a RISC-V Machine Hardware Interrupt (highest bit set) */ + if (mcause_val & RISCV_MCAUSE_INTERRUPT_FLAG) { + /* + * RISC-V PLIC Hardware Dispatch: + * Claim active CPU interrupt line from PLIC_MXINT_CLAIM_REG (0x20001094). + * For PLIC Machine External Interrupts (mcause = 11), PLIC Claim Register returns + * the active CPU Interrupt Line Number (1-31). + */ + uint32_t cpu_intr_num = REG_READ(PLIC_MXINT_CLAIM_REG); + + if (cpu_intr_num > 0 && cpu_intr_num < MAX_CPU_INTERRUPTS) { + if (s_isr_table[cpu_intr_num] != NULL) { + s_isr_table[cpu_intr_num](s_isr_arg[cpu_intr_num]); + } + /* Complete PLIC claim transaction by writing claimed line back to PLIC */ + REG_WRITE(PLIC_MXINT_CLAIM_REG, cpu_intr_num); + } + } +} diff --git a/ports/risc-v32/gnu/example_build/esp32c6/platform/startup.c b/ports/risc-v32/gnu/example_build/esp32c6/platform/startup.c new file mode 100644 index 000000000..dd6a412fc --- /dev/null +++ b/ports/risc-v32/gnu/example_build/esp32c6/platform/startup.c @@ -0,0 +1,59 @@ +/*************************************************************************** + * Copyright (c) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * Inspired by and derived in part from architectural patterns in: + * - Espressif Systems ESP-IDF (Apache-2.0) + * - Zephyr RTOS Project (Apache-2.0) + **************************************************************************/ + +#include "esp32c6_example.h" +#include "esp_app_desc.h" + +/* + * Espressif 2nd-Stage Bootloader Application Description Header + * Placed at the very top of segment 0 (.rodata_desc) via link.ld. + */ +const esp_app_desc_t esp_app_desc __attribute__((section(".rodata_desc"))) = { + .magic_word = ESP_APP_DESC_MAGIC_WORD, + .secure_version = 0, + .version = "1.0.0", + .project_name = "esp32c6_threadx", + .time = __TIME__, + .date = __DATE__, + .idf_ver = "v6.0.1", + .app_elf_sha256 = {0}, + .reserv2 = {0} +}; + +static void esp32c6_disable_wdt(void) +{ + /* Unlock LP_WDT write protection using key 0x50D83AA1 */ + REG_WRITE(0x600B1C1C, 0x50D83AA1); + /* Disable LP_WDT to prevent standalone hardware reset loops */ + REG_WRITE(0x600B1C00, 0); +} + +void esp32c6_platform_init(void) +{ + /* 0. Disable hardware LP Watchdog Timer */ + esp32c6_disable_wdt(); + + /* 1. Initialize Console UART */ + esp32c6_console_init(); + + /* 2. Initialize Interrupt Matrix & PLIC */ + esp32c6_interrupt_init(); + + /* 3. Initialize SYSTIMER */ + esp32c6_systimer_init(); +} + +void c6_startup(void) +{ + /* Hand off directly to ThreadX kernel initialization */ + tx_kernel_enter(); +} diff --git a/ports/risc-v32/gnu/example_build/esp32c6/platform/systimer.c b/ports/risc-v32/gnu/example_build/esp32c6/platform/systimer.c new file mode 100644 index 000000000..11b06e9b1 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/esp32c6/platform/systimer.c @@ -0,0 +1,70 @@ +/* + * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD + * Modifications Copyright (c) 2026 Eclipse ThreadX contributors + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp32c6_example.h" +#include "hal/systimer_hal.h" +#include "hal/systimer_ll.h" + +extern void _tx_timer_interrupt(void); + +#define SYSTIMER_COUNTER_ID 0 +#define SYSTIMER_ALARM_ID 0 +#define SYSTIMER_TICKS_PER_10MS (ESP32C6_SYSTIMER_CLK_FREQ_HZ / TX_TIMER_TICKS_PER_SECOND) + +static systimer_hal_context_t s_systimer_hal; +static uint64_t s_next_alarm_target = 0; + +static void esp32c6_systimer_isr(void *arg) +{ + (void)arg; + + /* Clear hardware alarm interrupt status bit */ + systimer_ll_clear_alarm_int(&SYSTIMER, SYSTIMER_ALARM_ID); + + /* Advance next periodic 10ms target & set hardware comparator */ + s_next_alarm_target += SYSTIMER_TICKS_PER_10MS; + systimer_ll_enable_alarm(&SYSTIMER, SYSTIMER_ALARM_ID, false); + systimer_ll_set_alarm_target(&SYSTIMER, SYSTIMER_ALARM_ID, s_next_alarm_target); + systimer_ll_apply_alarm_value(&SYSTIMER, SYSTIMER_ALARM_ID); + systimer_ll_enable_alarm(&SYSTIMER, SYSTIMER_ALARM_ID, true); + + /* Invoke ThreadX kernel timer tick handler */ + _tx_timer_interrupt(); +} + +void esp32c6_systimer_init(void) +{ + /* Initialize Pinned ESP-IDF SYSTIMER HAL */ + systimer_hal_init(&s_systimer_hal); + + /* Enable Counter 0 & connect Alarm 0 to Counter 0 */ + systimer_ll_enable_counter(&SYSTIMER, SYSTIMER_COUNTER_ID, true); + systimer_ll_connect_alarm_counter(&SYSTIMER, SYSTIMER_ALARM_ID, SYSTIMER_COUNTER_ID); + + /* Configure Alarm 0 initial target for 10ms period */ + uint64_t now = systimer_hal_get_counter_value(&s_systimer_hal, SYSTIMER_COUNTER_ID); + s_next_alarm_target = now + SYSTIMER_TICKS_PER_10MS; + + systimer_ll_enable_alarm_period(&SYSTIMER, SYSTIMER_ALARM_ID); + systimer_ll_set_alarm_target(&SYSTIMER, SYSTIMER_ALARM_ID, s_next_alarm_target); + systimer_ll_apply_alarm_value(&SYSTIMER, SYSTIMER_ALARM_ID); + + /* Attach ISR to CPU Interrupt Line 1 */ + esp32c6_interrupt_attach(ESP32C6_SYSTIMER_CPU_INTR_NUM, + ESP32C6_SYSTIMER_TICK_INTR_SRC, + esp32c6_systimer_isr, + NULL); + + /* Enable Alarm 0 hardware & interrupt */ + systimer_ll_enable_alarm_int(&SYSTIMER, SYSTIMER_ALARM_ID, true); + systimer_ll_enable_alarm(&SYSTIMER, SYSTIMER_ALARM_ID, true); +} + +void esp32c6_systimer_clear_interrupt(void) +{ + systimer_ll_clear_alarm_int(&SYSTIMER, SYSTIMER_ALARM_ID); +} diff --git a/ports/risc-v32/gnu/example_build/esp32c6/threadx_ESP32C6_native_WIFI_plan.md b/ports/risc-v32/gnu/example_build/esp32c6/threadx_ESP32C6_native_WIFI_plan.md new file mode 100644 index 000000000..a71ed6a86 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/esp32c6/threadx_ESP32C6_native_WIFI_plan.md @@ -0,0 +1,1243 @@ +# ESP32-C6 ThreadX Bring-Up and SampleX Native Wi-Fi Integration + +## 1. Objective + +Deliver ESP32-C6 support at the appropriate level in two Eclipse ThreadX +repositories: + +- A basic ESP32-C6 ThreadX bring-up example under the generic RV32 GNU port's + `example_build` directory in the ThreadX repository. +- A complete ESP32-C6 sample integration in SampleX. +- The ESP32-C6 integrated Wi-Fi radio. +- A native ThreadX adaptation of the Espressif Wi-Fi OS interface. +- NetX Duo as the only TCP/IP stack. +- No FreeRTOS runtime, lwIP, `esp_netif`, `esp_event`, or stock ESP-IDF + `esp_timer` runtime component. +- No external network or USB controller hardware. +- No USBX. + +The Espressif Wi-Fi and PHY binary libraries remain required. Reimplementing the +closed MAC/PHY functionality is outside the scope of the project. "ThreadX-native +Wi-Fi" therefore means that the vendor libraries run on ThreadX services directly, +without a FreeRTOS compatibility layer. + +The ThreadX repository will not ship a complete ESP32-C6 BSP or the vendor Wi-Fi +integration. Its ESP32-C6 content will be limited to the minimum board adaptation +needed to demonstrate and validate the generic RISC-V32 port. SampleX will own the +full board integration, ESP-IDF dependency, Wi-Fi adaptation, NetX Duo driver, and +network application. + +Selected ESP-IDF timer sources are an intentional exception to treating ESP-IDF +only as a hardware reference. SampleX will maintain a pinned, narrowly patched +import of the `esp_timer` core, its ESP32-C6 SYSTIMER backend, and the ETS +compatibility layer. All operating-system integration in that import will use +native ThreadX services; it will not pull in the ESP-IDF component manager, +startup framework, FreeRTOS, or interrupt allocator. + +## 2. Architectural decision + +ESP-IDF will be used as a silicon reference and vendor SDK, not as the application +framework. A conventional ESP-IDF application is unsuitable because its startup +flow and many of its components assume FreeRTOS. + +Repository ownership is deliberately split: + +| Repository | Owns | Does not own | +|---|---|---| +| ThreadX | Generic RV32 port fixes and a minimal ESP32-C6 example under `ports/risc-v32/gnu/example_build/esp32c6` | Full BSP productization, ESP-IDF integration, Wi-Fi, NetX Duo, or application features | +| SampleX | Complete Espressif/ESP32-C6 sample, board support needed by that sample, pinned ESP-IDF integration, native Wi-Fi OS adapter, WPA support, NetX Duo link driver, and demo application | Generic ThreadX kernel/port implementation | + +SampleX consumes ThreadX and NetX Duo through its existing `libs/threadx` and +`libs/netxduo` submodules. It should reuse the basic ESP32-C6 bring-up support from +the pinned ThreadX submodule where practical, then layer the vendor-specific Wi-Fi +services on top. + +The target architecture is: + +```text +ESP32-C6 mask ROM + | + v +Espressif second-stage bootloader + | + v +ThreadX-native application and board support + +-- startup, memory, cache, and flash conventions + +-- RISC-V trap entry and C6 PLIC handling + +-- ThreadX SYSTIMER tick + +-- ThreadX-adapted IDF high-resolution timer and ETS service + +-- console, reset, random, eFuse, and MAC services + | + v +ThreadX + +-- application threads + +-- Wi-Fi threads created through the native OS adapter + +-- NetX Duo threads + | + v +Espressif Wi-Fi OS adapter implemented with ThreadX + | + v +Espressif Wi-Fi, PHY, and WPA supplicant code + | + v +ESP32-C6 integrated radio + +Wi-Fi Ethernet frames <--> NetX Duo IP driver +``` + +This boundary minimizes FreeRTOS emulation, gives ThreadX ownership of interrupt +and scheduling semantics, and keeps NetX Duo in direct control of IP networking. +The minimal version of the path through `tx_kernel_enter()` is proven first by the +ThreadX `example_build`; the complete path shown above is delivered by SampleX. + +## 3. ESP-IDF reuse boundary + +### 3.1 Reuse + +Reuse the following from an exactly pinned ESP-IDF revision: + +- `riscv32-esp-elf` compiler, assembler, linker, and binary utilities. +- The Espressif second-stage bootloader. +- ESP application image and partition-table formats. +- `esptool.py` image generation and flashing support. +- Espressif OpenOCD configuration. +- ESP32-C6 ROM symbol linker scripts. +- ESP32-C6 register definitions and capability headers. +- Selected RTOS-independent HAL and LL headers and implementations. +- eFuse and base-MAC-address access logic. +- PHY initialization data and calibration behavior. +- Prebuilt ESP32-C6 Wi-Fi and PHY archives. +- The minimum required Espressif WPA supplicant sources. +- Small RTOS-independent portions of clock, modem, reset, and peripheral setup. +- The task-dispatch portions of `esp_timer`, the ESP32-C6 SYSTIMER timer backend, + and `ets_timer_legacy`, maintained as a small SampleX-local adaptation. + +The stock second-stage bootloader should be retained initially. Replacing it would +add flash setup, validation, partition, and security work without advancing the +ThreadX or networking demonstration. + +### 3.2 Do not reuse as frameworks + +Do not include these subsystems in the ThreadX application: + +- FreeRTOS or the ThreadX FreeRTOS compatibility layer. +- The normal `esp_system` application startup sequence. +- `esp_event`. +- The stock `esp_timer` component build, startup hooks, FreeRTOS integration, + profiling, ISR-dispatch mode, ETM, wall-clock integration, and sleep-time + compensation. Only the explicitly listed timer sources are adapted. +- `esp_netif`. +- lwIP. +- VFS and pthread integration. +- IDF task and interrupt watchdog services initially. +- IDF power-management, light-sleep, and retention frameworks. +- The default ESP-IDF Wi-Fi OS adapter. +- Bluetooth, IEEE 802.15.4, and coexistence initially. + +High-level ESP-IDF drivers may be considered later only after verifying that they +do not introduce FreeRTOS or other excluded dependencies. The minimal ThreadX +example and the fuller SampleX board integration should prefer ROM calls, SoC +headers, and HAL/LL operations. + +## 4. Source and version policy + +Pin exact commits for: + +- ESP-IDF. +- ThreadX. +- NetX Duo. + +ThreadX and SampleX changes should be developed as separate, ordered changes: + +1. Land any genuinely generic RV32 fixes and the minimal ESP32-C6 + `example_build` in ThreadX. +2. Update the SampleX `libs/threadx` submodule to that ThreadX revision. +3. Add the complete ESP32-C6 Wi-Fi/NetX Duo sample in SampleX. + +ESP32-C6-specific source must not be added to the generic ThreadX port's `inc` or +`src` directories unless it exposes and fixes a genuinely architecture-generic +problem. Board startup, linker, kernel-tick timer, PLIC, and console files belong +below the ESP32-C6 `example_build` directory. High-resolution/ETS timers, Wi-Fi, +PHY, WPA, ESP-IDF integration, and NetX link code belong only in SampleX. + +The Wi-Fi OS interface, internal Layer-2 APIs, binary archive dependencies, ROM +symbols, and linker fragments can change between ESP-IDF revisions. The build must +fail if the expected Wi-Fi OS adapter version changes. + +Follow the useful integration pattern demonstrated by Zephyr's Espressif HAL: + +- Pin one matching ESP-IDF/HAL revision rather than consume the developer's + installed SDK implicitly. +- Compile only an explicit source manifest. +- Keep RTOS substitutions in a small, reviewable patch series or adaptation + layer rather than modifying a full imported SDK tree by hand. +- Use native kernel objects and native interrupt dispatch instead of emulating a + FreeRTOS runtime. +- Retain vendor data structures and behavioral semantics at the binary-library + boundary. +- Make patch application fail when its expected source context changes. + +The current ESP-IDF Wi-Fi adapter declares `ESP_WIFI_OS_ADAPTER_VERSION` in: + +```text +components/esp_wifi/include/esp_private/wifi_os_adapter.h +``` + +Record the following in a machine-readable manifest: + +- ESP-IDF commit. +- ThreadX and NetX Duo commits. +- Wi-Fi OS adapter version. +- List and checksums of linked vendor archives. +- Compiler version and target flags. +- Required private Espressif symbols. +- Imported timer source paths, hashes, configuration, and adaptation patch level. + +Preserve all Espressif copyright and Apache-2.0 notices when adapting source. +Outside the explicit imported-source manifests, prefer vendor headers or behavior +implemented from public specifications over copying source bodies. + +## 5. Proposed repository layouts + +### 5.1 ThreadX repository + +```text +threadx/ +`-- ports/risc-v32/gnu/ + |-- inc/ # Generic RV32 GNU port only + |-- src/ # Generic RV32 GNU port only + `-- example_build/ + `-- esp32c6/ + |-- CMakeLists.txt + |-- README.md + |-- cmake/ + | `-- esp32c6-toolchain.cmake + |-- platform/ + | |-- startup.c + | |-- interrupt.c + | |-- systimer.c + | |-- console.c + | `-- gpio.c + |-- include/ + | |-- esp32c6_example.h + | `-- tx_user.h + |-- entry.S + |-- vectors.S + |-- tx_initialize_low_level.S + |-- link.ld + |-- demo_threadx.c + `-- tests/ +``` + +The example proves only startup, the ThreadX timer tick, context switching, basic +interrupt dispatch, console output, and a simple GPIO or timer demonstration. It +must not link NetX Duo or the Espressif Wi-Fi/PHY libraries. + +### 5.2 SampleX repository + +Follow SampleX's existing vendor/board organization: + +```text +samplex/ +|-- libs/ +| |-- threadx/ # Existing submodule +| `-- netxduo/ # Existing submodule +`-- Espressif/ + `-- ESP32-C6/ + |-- CMakeLists.txt + |-- README.md + |-- NOTICE.md + |-- app/ + | |-- CMakeLists.txt + | |-- common/ + | | |-- board_init.c + | | |-- console.c + | | `-- wifi_manager.c + | `-- starter/ + | `-- main.c + |-- cmake/ + | |-- esp32c6-toolchain.cmake + | |-- espressif-libraries.cmake + | `-- make-image.cmake + |-- deps/ + | `-- esp-idf/ # Pinned dependency or fetched equivalent + |-- vendor/ + | `-- esp_timer/ + | |-- README.md # Origin revision and supported feature set + | |-- sources.cmake # Explicit imported-source manifest + | |-- patches/ # Small ThreadX adaptation patch series + | `-- include/ # Narrow configuration/adaptation headers + |-- lib/ + | |-- CMakeLists.txt + | |-- esp32c6_base/ # Reuses ThreadX example support + | |-- esp32c6_wifi/ + | | |-- esp_wifi_os_adapter.c + | | |-- esp_wifi_platform.c + | | |-- esp_wifi_events.c + | | `-- esp_phy_platform.c + | |-- esp32c6_timer/ + | | |-- esp_timer_threadx.c + | | |-- esp_timer_platform.c + | | `-- esp_timer_platform.h + | |-- netxduo/ + | | |-- nx_user.h + | | |-- nx_driver_esp32c6_wifi.c + | | `-- nx_wifi_manager.c + | `-- threadx/ + | `-- tx_user.h + |-- scripts/ + | |-- build.sh + | `-- deploy.sh + `-- tests/ +``` + +`lib/esp32c6_base` should compile the basic startup, vectors, PLIC, SYSTIMER, and +console support from the pinned ThreadX submodule's ESP32-C6 `example_build` when +the files are suitable for reuse. SampleX-local wrappers may expose a cleaner board +API, but should not fork the generic ThreadX assembly without a documented reason. + +The preferred ESP-IDF dependency model is a pinned SampleX dependency under the +board directory so the sample is reproducible. If repository-size policy rules out +an ESP-IDF submodule, provide a setup script that fetches and verifies an exact +commit; an unconstrained `ESP_IDF_ROOT` from the developer's environment is not +sufficient for CI or release validation. + +Keep the pinned ESP-IDF checkout pristine. At configure time, copy only the files +listed in `vendor/esp_timer/sources.cmake` into a generated build staging directory, +verify their hashes, and apply the ordered adaptation patches there. Patch failure +is a configuration error. If SampleX policy instead requires committed imported +sources, record the same hashes and provide a script that reproduces the import +from the pinned ESP-IDF commit. + +## 6. Build and image strategy + +There are two builds with different purposes. + +### 6.1 ThreadX basic example build + +The ThreadX repository build compiles the generic `risc-v32/gnu` port together with +the files under `example_build/esp32c6`. It produces a minimal application image +that proves the port on hardware. Its CMake files may use the Espressif compiler, +ROM linker symbols, image conversion tool, and stock second-stage bootloader, but +must not acquire a dependency on the ESP-IDF application framework. + +### 6.2 SampleX integration build + +The full standalone CMake and Ninja build lives under +`SampleX/Espressif/ESP32-C6` and is driven by Espressif's toolchain: + +```sh +cd Espressif/ESP32-C6 +source deps/esp-idf/export.sh + +cmake -S . -B build -G Ninja \ + -DCMAKE_TOOLCHAIN_FILE=cmake/esp32c6-toolchain.cmake \ + -DESP_IDF_ROOT=$PWD/deps/esp-idf + +cmake --build build +``` + +The toolchain file must use the C6 ISA, ABI, code model, and linker conventions +from the pinned ESP-IDF revision. Do not guess these flags independently. + +The build should produce: + +- A standard ELF with debug information. +- An Espressif application binary produced with `esptool.py elf2image`. +- A map file. +- A manifest of linked vendor libraries and unresolved-symbol closure. +- Flash arguments for the stock bootloader, partition table, and application. + +Initially disable secure boot and flash encryption. Add them only after the basic +image, reset, and flash paths are stable. + +The application linker script must provide: + +- Correct ESP32-C6 IRAM, DRAM, RTC, and flash mappings. +- Espressif application metadata expected by the bootloader. +- ROM symbol resolution. +- Dedicated startup/system stack space. +- ThreadX code and data placement. +- IRAM placement for interrupt and cache-disabled paths. +- Explicit regions for ThreadX stacks and network packet pools where useful. + +## 7. Basic ESP32-C6 adaptation in the ThreadX RV32 example + +Add the minimum C6-specific interrupt and initialization layer below +`ports/risc-v32/gnu/example_build/esp32c6`. The generic port's interrupt-frame +contract is not directly compatible with ESP-IDF's RISC-V vector implementation, +so the example supplies its own board-level entry and vector code while continuing +to build the generic RV32 GNU port sources unchanged wherever possible. + +### 7.1 Startup + +The application entry path must: + +1. Accept control from the Espressif second-stage bootloader. +2. Establish or verify `gp`, `sp`, and machine-mode state. +3. Initialize `.data`, `.bss`, and any required C runtime state not already handled. +4. Preserve the flash/cache configuration established by the bootloader. +5. Initialize the console and early diagnostics. +6. Install the ThreadX-compatible trap vector in `mtvec`. +7. Initialize the C6 interrupt controller to a known state. +8. Call `tx_kernel_enter()`. + +`tx_application_define()` creates only the simple validation threads and objects +needed by the ThreadX example. The full bootstrap, vendor services, and networking +threads are SampleX responsibilities. + +### 7.2 Interrupt and trap handling + +Implement a C6-specific trap entry around the ThreadX RV32 context contract: + +- Save the interrupted context in the format expected by ThreadX. +- Support machine-mode execution. +- Decode synchronous exceptions separately from interrupts. +- Decode C6 PLIC interrupt numbers. +- Switch to the ThreadX system/interrupt stack on first-level interrupt entry. +- Track nested interrupt state even if nesting is initially disabled. +- Dispatch through a small example-local interrupt table. +- Return through ThreadX context-restore logic so preemption occurs correctly. + +The initial implementation should disable nested interrupts. Add prioritized nesting +only after thread switching, timer interrupts, and peripheral interrupts pass stress +tests. When nesting is enabled, account for both `mstatus.MIE` and the C6 PLIC +threshold. + +Expose only the minimal example-local interrupt API needed to validate the port, +such as: + +```c +typedef void (*c6_example_isr_t)(void *context); + +int c6_example_interrupt_attach(unsigned source, + unsigned priority, + c6_example_isr_t handler, + void *context); +void c6_example_interrupt_enable(unsigned interrupt_number); +void c6_example_interrupt_disable(unsigned interrupt_number); +``` + +### 7.3 ThreadX tick + +Use one C6 SYSTIMER comparator for the ThreadX tick: + +- Configure a periodic alarm. +- Route it through the interrupt matrix and PLIC. +- Clear the comparator interrupt in the ISR. +- Invoke `_tx_timer_interrupt` with the required ThreadX context-save/restore flow. +- Start with a 100 Hz tick. + +Initial timing configuration: + +```c +#define TX_TIMER_TICKS_PER_SECOND 100 +``` + +### 7.4 SampleX extension: ThreadX-adapted IDF timer service + +The minimal ThreadX example does not require the Wi-Fi timer service. SampleX +adds it by adapting the timer implementation from the exact ESP-IDF revision that +supplies the Wi-Fi libraries. This preserves Espressif's ordered expiration, +periodic rearm, callback, cancellation, and deferred-delete semantics instead of +reimplementing them independently. + +The initial source manifest contains only: + +```text +components/esp_timer/src/esp_timer.c +components/esp_timer/src/esp_timer_impl_common.c +components/esp_timer/src/esp_timer_impl_systimer.c +components/esp_timer/src/ets_timer_legacy.c +components/esp_hal_systimer/systimer_hal.c +components/esp_hw_support/port/esp32c6/systimer.c +``` + +Include only the corresponding public/private timer headers, ESP32-C6 SoC +definitions, ROM `ETSTimer` definition, and SYSTIMER HAL/LL headers required by +that manifest. Re-evaluate the list against the pinned revision rather than +assuming paths from another ESP-IDF release. + +Configure the imported timer code for: + +- Single-core ESP32-C6 operation. +- `ESP_TIMER_TASK` dispatch only. +- No profiling, ETM, system-time provider, RTC synchronization, light sleep, or + power-management compensation. +- Internal RAM allocation only. +- Explicit SampleX initialization and no ESP-IDF startup macros. + +Map the RTOS-dependent operations as follows: + +| IDF timer dependency | SampleX ThreadX implementation | +|---|---| +| Timer task | Statically allocated `TX_THREAD` and stack | +| ISR-to-task notification | One-bit `TX_EVENT_FLAGS_GROUP` using `TX_OR`/`TX_OR_CLEAR` | +| `portMUX_TYPE` list/register locks | Saved C6 interrupt posture with nesting-safe restore | +| Current task | `tx_thread_identify()` | +| Tick count and one-tick delay | `tx_time_get()` and `tx_thread_sleep(1)` | +| Internal allocation | Dedicated `TX_BYTE_POOL`, nonblocking allocation plus explicit zeroing for `calloc` semantics | +| ISR-context query | SampleX C6 interrupt-context helper | +| `esp_intr_alloc` | SampleX native interrupt attach/enable API | +| ESP-IDF startup hook | Explicit SampleX board/application initialization | + +Use event flags rather than an unbounded counting semaphore because IDF task +notifications coalesce repeated alarm interrupts. The SYSTIMER ISR clears alarm 2 +and sets the event bit; ThreadX performs any necessary preemption when the ISR +returns through its normal context-restore path. + +Preserve the `ETSTimer` ABI and legacy wrapper behavior. In particular, keep +deletion deferred to the timer thread and store `pfunction` in +`ETSTimer.timer_func` as a defensive compatibility measure used by the Zephyr +adaptation because vendor blobs may inspect that field. + +The adapted code is an internal Espressif compatibility service. Applications +should use ThreadX facilities unless they specifically require the Espressif timer +ABI. + +### 7.5 Shared SYSTIMER ownership and initialization + +Retain ESP-IDF's nonconflicting ESP32-C6 resource allocation: + +| Consumer | Counter | Alarm | +|---|---:|---:| +| ThreadX kernel tick | 1 | 0 | +| Espressif high-resolution timer | 0 | 2 | + +SampleX owns the SYSTIMER peripheral globally. It enables and resets the peripheral +exactly once before either consumer is configured. The adapted +`esp_timer_impl_early_init()` must not reset an already configured SYSTIMER or use +ESP-IDF's peripheral reference-counting service. + +Initialization order is mandatory: + +1. Enable and reset SYSTIMER once in early SampleX platform initialization. +2. Initialize high-resolution counter 0 and one-shot alarm 2. +3. Configure the ThreadX periodic tick on counter 1 and alarm 0. +4. Enter ThreadX. +5. In `tx_application_define()`, create the timer thread and event-flags object, + attach the alarm-2 ISR, and enable its interrupt before Wi-Fi starts. + +Accesses to shared SYSTIMER interrupt-enable and configuration registers must use +the platform interrupt-posture lock. Timer initialization and ThreadX tick +initialization must never independently reset the peripheral. + +### 7.6 Memory ownership + +The ThreadX example uses static internal SRAM for its stacks and objects. SampleX +adds the following, still using internal SRAM during initial development: + +- Statically allocated critical ThreadX stacks and control blocks. +- A ThreadX byte pool for general dynamic objects. +- A separate internal-memory allocator for vendor Wi-Fi allocations. +- Explicit alignment and capability checks for DMA-visible buffers. +- A dedicated NetX packet pool. + +Leave PSRAM disabled until Wi-Fi and NetX are stable. Define clear ownership rules +for every allocation crossing the Wi-Fi/NetX boundary. + +### 7.7 Responsibility boundary + +The ThreadX `example_build` implements: + +- Early console output. +- Reset and reboot. +- PLIC and interrupt-matrix control. +- SYSTIMER kernel tick. +- GPIO output and interrupt support. + +SampleX adds: + +- ThreadX-adapted IDF high-resolution and ETS timers. +- Hardware random-number access. +- eFuse/base-MAC access. +- Flash reads. +- Modem and Wi-Fi clock control. +- PHY/RF power-domain control. +- Cache-safe ISR placement. + +Both deliverables defer sleep, dynamic frequency scaling, watchdog integration, +PSRAM, and generalized peripheral-driver APIs until the full sample is stable. + +## 8. SampleX minimal Wi-Fi feature profile + +All Wi-Fi work from this point forward belongs under +`SampleX/Espressif/ESP32-C6`. None of these sources or dependencies are added to +the ThreadX repository. + +The first working configuration should support only: + +- Station mode. +- WPA2-Personal/PSK. +- IPv4. +- NetX Duo DHCP client. +- NetX Duo DNS client. +- `WIFI_PS_NONE`. + +Disable initially: + +- SoftAP. +- WPA Enterprise. +- WPA3/SAE. +- WPS and SmartConfig. +- ESP-NOW, mesh, NAN, roaming, and FTM. +- Bluetooth and IEEE 802.15.4. +- Radio coexistence. +- Modem sleep, light sleep, MAC/BB retention, and tickless operation. +- PSRAM. +- Wi-Fi NVS persistence. + +The feature configuration must remove source and dependency paths, not merely leave +unused APIs uncalled where that would still pull unwanted initialization code. + +## 9. SampleX Espressif Wi-Fi and PHY linkage + +The pinned ESP-IDF tree supplies ESP32-C6 Wi-Fi archives including: + +```text +components/esp_wifi/lib/esp32c6/libcore.a +components/esp_wifi/lib/esp32c6/libnet80211.a +components/esp_wifi/lib/esp32c6/libpp.a +components/esp_wifi/lib/esp32c6/libespnow.a +components/esp_wifi/lib/esp32c6/libmesh.a +components/esp_wifi/lib/esp32c6/libsmartconfig.a +components/esp_wifi/lib/esp32c6/libwapi.a +``` + +The PHY libraries are under: + +```text +components/esp_phy/lib/esp32c6/ +``` + +Begin with `libcore.a`, `libnet80211.a`, `libpp.a`, and the required PHY library, +then determine the actual closure mechanically. Do not assume that archive names +fully describe their shared modem dependencies. + +Use `riscv32-esp-elf-nm` to generate a report of undefined symbols from every +selected archive. Classify each symbol as: + +- ESP ROM. +- SampleX board/platform hardware service. +- Wi-Fi OS adapter function. +- PHY platform function. +- WPA supplicant function. +- Required vendor support code. +- Disabled feature that should be removed. +- Unsupported or unexplained dependency. + +No unexplained symbols or permissive catch-all stubs should remain at the end of +the closure phase. + +## 10. SampleX native ThreadX Wi-Fi OS adapter + +Implement `g_wifi_osi_funcs` directly with ThreadX. Preserve the exact structure +layout, version, magic value, function signatures, and return conventions expected +by the pinned vendor archives. + +### 10.1 Object mappings + +| Espressif service | Native implementation | +|---|---| +| Task creation/deletion | `tx_thread_create`, terminate, and delete | +| Delay | `tx_thread_sleep` with explicit tick conversion | +| Current task | `tx_thread_identify` | +| Counting/binary semaphore | `TX_SEMAPHORE` wrapper | +| Mutex | `TX_MUTEX` with priority inheritance | +| Recursive mutex | Owner and recursion wrapper around `TX_MUTEX` | +| Queue | Arbitrary-item-size queue wrapper | +| Event group | `TX_EVENT_FLAGS_GROUP` | +| Per-thread semaphore | `TX_THREAD_USER_EXTENSION` or adapter registry | +| Interrupt lock/restore | SampleX C6 board interrupt posture API | +| Software timers | ThreadX-adapted IDF `esp_timer`/ETS service | +| Time in microseconds | Adapted `esp_timer_get_time()` on SYSTIMER counter 0 | +| Dynamic memory | ThreadX byte pool/SampleX internal heap | +| Free heap query | SampleX allocator accounting | +| Event posting | Native ThreadX Wi-Fi event queue | +| Random data | C6 hardware RNG | +| Base MAC | eFuse/ROM service | +| Logging | SampleX board console logger | +| NVS | Disabled adapter initially; native storage later | + +### 10.2 Priority mapping + +Espressif uses FreeRTOS-style priorities where larger numbers are more urgent; +ThreadX uses smaller values for higher priorities. Use a reviewed lookup table, +not a blind arithmetic mapping. + +Example allocation: + +```text +ThreadX 0-3 reserved for kernel/critical deferred work +ThreadX 4-7 Espressif Wi-Fi protocol threads +ThreadX 8-9 Wi-Fi timer and receive-deferred threads +ThreadX 10-14 NetX and application networking threads +ThreadX 20+ background application work +``` + +Validate that every vendor-requested priority maps into a legal ThreadX range. + +### 10.3 Queue adapter + +Espressif queues accept arbitrary item sizes, while a ThreadX queue transfers one +or more `ULONG` words. Implement an adapter object containing: + +- Fixed-size item storage. +- A free-slot pool. +- A ThreadX queue carrying slot pointers or indices. +- Send-to-front and send-to-back support. +- Nonblocking ISR send. +- Accurate waiting-message count. +- Timeout conversion. +- Defined deletion behavior. + +Test ordering, full/empty behavior, finite and infinite waits, ISR posting, and +multiple producers/consumers independently of Wi-Fi. + +### 10.4 Recursive mutexes and thread-local state + +Implement recursive mutex ownership explicitly if the underlying ThreadX mutex +configuration does not provide identical semantics. Store per-Wi-Fi-thread data +through a `TX_THREAD_USER_EXTENSION` field or a protected registry indexed by +`TX_THREAD *`. + +Thread deletion must release adapter-owned thread-local objects without leaking +memory or deleting objects still in use. + +### 10.5 Events + +Replace `esp_event_post` with a bounded native event queue. Copy event payloads +whose lifetime does not extend beyond the vendor callback. + +The vendor callback must only enqueue the event. A dedicated Wi-Fi manager thread +updates state and calls application handlers. + +### 10.6 Power-management and coexistence hooks + +With power saving, Bluetooth, 802.15.4, and coexistence disabled, some hooks may be +valid no-ops. Document every such hook and verify its expected disabled-feature +behavior in the pinned IDF implementation. Do not silently return success for a +hook whose side effects are required by active station mode. + +### 10.7 Vendor-source adaptation discipline + +Use the same general technique that Zephyr applies to Espressif integrations: +retain vendor-facing structures and algorithms while replacing the operating +system boundary with a small native layer. For each imported source group: + +1. Record its exact upstream commit and source hashes. +2. Maintain an explicit source and configuration manifest. +3. Keep ThreadX substitutions in focused patches or wrapper headers. +4. Avoid compatibility macros that pretend ThreadX is FreeRTOS across the whole + application. +5. Compile with warnings enabled and fail on stale patches or ABI assertions. +6. Test the adapted service independently before linking the Wi-Fi archives. + +The ThreadX FreeRTOS compatibility layer may be useful as a behavioral reference, +but it is not linked into the demonstration. Direct ThreadX implementations keep +object ownership, ISR legality, and priority behavior visible and reviewable. + +## 11. SampleX PHY initialization and persistence + +For the first milestone: + +- Embed the normal ESP32-C6 PHY initialization data in the application. +- Disable Wi-Fi NVS persistence in the initialization configuration. +- Perform full PHY calibration at boot if required. +- Accept increased boot time. + +If the vendor interface still calls NVS functions, initially provide a deliberate +in-memory implementation or correctly return "not found"/unsupported according to +the API contract. Do not persist credentials in an ad hoc format during bring-up. + +After networking is stable, add a native flash-backed storage service for: + +- PHY calibration data. +- Wi-Fi configuration values required by the vendor driver. +- Application credentials, if desired. + +Reusing the IDF NVS on-flash format is optional. A smaller ThreadX-native store may +be preferable if interoperability with IDF firmware is not needed. + +## 12. SampleX WPA supplicant strategy + +The Espressif Wi-Fi stack depends on modified WPA supplicant sources in: + +```text +components/wpa_supplicant/ +``` + +Create an explicit source manifest for the minimum WPA2-Personal client path. Do +not import the complete IDF component definition, which enables broad Enterprise, +WPS, WPA3, AP, DPP, and roaming functionality. + +The initial manifest should contain only what is required for: + +- WPA/RSN state handling. +- WPA2-PSK key derivation and four-way handshake. +- Required IEEE 802.11 information-element parsing. +- Espressif station glue. +- The minimal supplicant event loop. +- Required hash, HMAC, AES, and PBKDF2 functions. + +Use either the supplicant's internal crypto subset or a standalone Mbed TLS build. +NetX Secure may later motivate a shared crypto configuration, but that should not +delay basic WPA2, DHCP, and UDP operation. + +## 13. SampleX NetX Duo port and Wi-Fi link driver + +NetX Duo needs a small RISC-V/little-endian `nx_port.h` and a hardware link driver. +Configure NetX for only the protocols used by the demo to control code size. + +### 13.1 Packet pool + +Create at least separate control and receive/transmit considerations, with: + +- Payload capacity for a full Ethernet frame plus NetX headroom. +- At least four-byte alignment. +- Internal SRAM placement initially. +- Sufficient packets to tolerate Wi-Fi RX bursts and TCP retransmission. +- Instrumentation for allocation failures and low-water marks. + +Begin with roughly 1536-byte payloads plus required NetX and Ethernet header space. +Tune counts from measured traffic rather than hiding exhaustion with unbounded +allocation. + +### 13.2 Required driver commands + +Support at least: + +- `NX_LINK_INITIALIZE`. +- `NX_LINK_ENABLE`. +- `NX_LINK_DISABLE`. +- `NX_LINK_PACKET_SEND`. +- `NX_LINK_ARP_SEND`. +- `NX_LINK_ARP_RESPONSE_SEND`. +- `NX_LINK_BROADCAST`. +- `NX_LINK_GET_STATUS`. + +Add multicast commands when required by enabled NetX features. + +### 13.3 Transmit path + +Initial transmit flow: + +1. Accept the NetX driver request. +2. Construct or preserve the Ethernet header required by the command. +3. Linearize chained `NX_PACKET` data into an internal Wi-Fi buffer. +4. Call the pinned Espressif Layer-2 transmit API. +5. Release the NetX packet according to the command contract. +6. Update detailed success, retry, drop, and allocation counters. + +Use copying initially. Investigate zero-copy only after the ownership and completion +semantics of the vendor transmit path are proven. + +### 13.4 Receive path + +Register the Espressif Layer-2 receive callback and: + +1. Validate the frame pointer and length. +2. Allocate an `NX_PACKET` from the receive pool. +3. Copy the complete Ethernet frame. +4. Release the Espressif RX buffer promptly. +5. Queue the NetX packet to a dedicated receive thread. +6. Parse EtherType and deliver to the appropriate NetX IP or ARP receive path. + +Do not run substantial NetX processing in the vendor callback. A dedicated receive +thread provides clear ownership, prevents blocking Wi-Fi internals, and makes queue +overflow behavior observable. + +### 13.5 Link-state manager + +Maintain a native state machine: + +```text +STOPPED + -> STARTED + -> SCANNING + -> ASSOCIATING + -> ASSOCIATED + -> ADDRESSING + -> ONLINE +``` + +On association: + +- Mark the NetX interface link up. +- Start DHCP, or apply a configured static address during early testing. + +On disconnection: + +- Mark link down immediately. +- Stop or restart DHCP as appropriate. +- Invalidate stale address and DNS state. +- Schedule reconnection with bounded exponential backoff. + +Application notifications run from the manager thread, never directly from the +vendor event callback. + +## 14. Implementation phases and exit criteria + +### Phase 0: Generic RV32 audit in ThreadX + +Deliver: + +- An audit of the generic RV32 GNU context, interrupt, timer, and stack-frame + assumptions against the ESP32-C6 CPU. +- Generic RV32 fixes only where the problem applies beyond the ESP32-C6. +- Regression tests for every generic port change. + +Exit criterion: the generic port continues to build and pass its existing QEMU and +other example tests, with new regression coverage for changed behavior. + +Repository: ThreadX. + +### Phase 1: Basic ESP32-C6 `example_build` + +Deliver: + +- `ports/risc-v32/gnu/example_build/esp32c6`. +- Standalone CMake/Ninja build instructions. +- ESP32-C6 startup, linker script, vectors, PLIC, console, and SYSTIMER files + contained under the example directory. +- ELF, map file, application image, and flash instructions. +- ThreadX `tx_application_define` validation application. +- Context switching and preemption. +- 100 Hz kernel tick. +- Console, reset, GPIO, and a peripheral interrupt. + +Exit criterion: sustained thread, timer, queue, mutex, and interrupt stress without +context corruption or timing stalls on an ESP32-C6 board. The example has no NetX +Duo, Wi-Fi, or SampleX application dependency. + +Repository: ThreadX. + +### Phase 2: SampleX ESP32-C6 skeleton + +Deliver: + +- `SampleX/Espressif/ESP32-C6` following the existing SampleX board layout. +- Updated SampleX ThreadX and NetX Duo submodule revisions. +- A pinned ESP-IDF acquisition mechanism. +- CMake/Ninja build, image generation, build/deploy scripts, README, and notice. +- Reuse of the basic ThreadX ESP32-C6 example support through `libs/threadx`. +- Pinned timer-source manifest and reproducible adaptation patch application. +- Shared SYSTIMER ownership with counter 1/alarm 0 for the ThreadX tick and + counter 0/alarm 2 for the high-resolution timer. +- ThreadX-adapted IDF timer thread, event notification, interrupt backend, + allocator, `esp_timer_get_time()`, and ETS compatibility API. + +Exit criterion: the SampleX starter boots, runs ThreadX, and passes the same basic +kernel validation as the ThreadX example without carrying a fork of the generic +RV32 port. High-resolution one-shot, periodic, cancellation, rearm, and deferred +deletion tests pass concurrently with the 100 Hz ThreadX tick. + +Repository: SampleX. + +### Phase 3: SampleX Wi-Fi ABI closure + +Deliver: + +- Selected Wi-Fi/PHY archive manifest. +- Undefined-symbol classification report. +- Native OS adapter skeleton. +- Compile-time adapter version and layout checks. +- Verified `ETSTimer` size, field offsets, callback field, and timer wrapper + signatures for the pinned Wi-Fi libraries. + +Exit criterion: clean link with every required symbol assigned to a documented +implementation and no catch-all stubs. + +Repository: SampleX. + +### Phase 4: SampleX PHY and scanning + +Deliver: + +- Modem clock and RF/PHY initialization. +- Wi-Fi interrupts, threads, queues, mutexes, and timers. +- Station startup and access-point scan. +- SSID, channel, security, and RSSI reporting. + +Exit criterion: repeated scans complete without deadlocks, leaks, timer loss, or +interrupt corruption. This is the primary feasibility gate. + +Repository: SampleX. + +### Phase 5: SampleX WPA2 association + +Deliver: + +- Minimal WPA2-Personal supplicant build. +- Association and disconnection handling. +- Native reconnect state machine. + +Exit criterion: repeated successful association/reassociation cycles with stable +memory use and no leaked ThreadX objects. + +Repository: SampleX. + +### Phase 6: SampleX NetX Layer 2 + +Deliver: + +- NetX Duo library and `nx_port.h`. +- ESP32-C6 Wi-Fi `NX_IP_DRIVER`. +- Static IPv4 configuration initially. +- ARP, ICMP echo, and bidirectional UDP. + +Exit criterion: sustained ping and UDP traffic under deliberate packet loss and +reassociation. + +Repository: SampleX. + +### Phase 7: Complete SampleX network demo + +Deliver: + +- DHCP client. +- DNS client. +- TCP client and/or server. +- A representative application such as HTTP or MQTT. +- NetX Secure TLS if required by the demonstration. + +Exit criterion: unattended operation across access-point restarts, DHCP renewal, +disconnects, and traffic stress. + +Repository: SampleX. + +### Phase 8: Refinement + +Optional follow-on work: + +- Flash-backed PHY calibration and configuration storage. +- WPA3/SAE. +- Wi-Fi power saving. +- Improved packet zero-copy paths. +- Watchdog integration. +- Crash dumps and ThreadX-aware debugger support. +- Secure boot and flash encryption. + +Repository: primarily SampleX. Any issue discovered here should move into the +ThreadX repository only when it is demonstrated to be a generic RV32 port defect. + +### Recommended change sequence + +Keep reviews and repository histories focused by using separate changes: + +1. ThreadX PR: generic RV32 corrections and regression tests, if any. +2. ThreadX PR: minimal ESP32-C6 `example_build` and hardware validation record. +3. SampleX PR: ESP32-C6 starter skeleton and pinned dependency setup. +4. SampleX PR: ThreadX-adapted IDF timer and shared SYSTIMER validation. +5. SampleX PR: PHY initialization and Wi-Fi scan with the native OS adapter. +6. SampleX PR: WPA2 station association. +7. SampleX PR: NetX Duo Layer-2 driver and static-IP traffic. +8. SampleX PR: DHCP, DNS, and the final application demo. + +## 15. Testing strategy + +Testing follows the repository boundary. ThreadX tests prove the generic RV32 port +and the minimal C6 example. SampleX tests prove the reusable board integration, +ESP-IDF archive adaptation, Wi-Fi behavior, and NetX Duo data path. + +### 15.1 ThreadX generic-port and example tests + +Run the existing generic RV32 builds and tests after every generic port change. +Add a regression test for each generic defect corrected. On the ESP32-C6 target, +the `example_build/esp32c6` validation should exercise: + +- Thread create/delete and rapid preemption. +- Nested critical sections. +- ThreadX tick accuracy and wrap behavior. +- Simultaneous SYSTIMER and GPIO interrupts. +- ISR-to-thread semaphore/queue wakeups. +- Long-running stack-watermark checks. + +These tests must not depend on SampleX, ESP-IDF Wi-Fi libraries, NetX Duo, or a +network connection. + +### 15.2 SampleX host tests + +Test RTOS-independent logic on the host where practical: + +- Priority conversion. +- Timeout and tick conversion. +- Queue slot management and ordering. +- Recursive mutex ownership rules. +- Timer expiration ordering and periodic catch-up behavior using a host hardware + backend or extracted timer-list harness. +- ETS timer initialization, callback, arm/disarm, and deferred-delete semantics. +- Wi-Fi event-state transitions. +- NetX packet-chain linearization. +- Ethernet frame validation and EtherType routing. +- Reconnect backoff. + +### 15.3 SampleX on-target platform tests + +Exercise: + +- SampleX bootstrap using the pinned ThreadX revision. +- High-resolution time accuracy, monotonicity, long-range behavior, and conversion + at the C6 SYSTIMER's 16 MHz rate. +- One-shot and periodic expiration, simultaneous expiration ordering, minimum + period behavior, cancellation, rearm, and deferred deletion. +- Timer callbacks that stop, rearm, or delete their own timers. +- Coalesced ISR wakeups when multiple alarms become due before the timer thread + runs. +- Concurrent ThreadX counter-1/alarm-0 tick and counter-0/alarm-2 timer traffic. +- Verification that neither timer initialization path resets the other consumer. +- Wi-Fi interrupt delivery alongside the ThreadX tick. +- SampleX service-thread wakeups and event delivery. +- Allocator exhaustion and recovery behavior. +- Repeated initialization and shutdown where the vendor API supports it. + +### 15.4 SampleX Wi-Fi adapter contract tests + +Before association, test every active OS adapter service independently: + +- Queue full, empty, timeout, front, and ISR cases. +- Semaphore finite/infinite waits and ISR give. +- Mutex priority inheritance and recursion. +- Thread lifecycle and per-thread objects. +- Timer arm, rearm, cancel, and callback races. +- Timer creation/allocation failure and asynchronous deletion reclamation. +- Long-running callbacks and timer-thread priority inversion scenarios. +- Event payload lifetime. +- Internal-memory allocation failures. + +### 15.5 SampleX network tests + +Test: + +- Scan and association repetition. +- Incorrect password and unavailable AP behavior. +- AP restart and channel changes. +- DHCP lease acquisition and renewal. +- RX burst exhaustion. +- TX allocation failure. +- UDP loss and reordering. +- TCP retransmission and reconnect. +- Long-duration traffic with memory and packet-pool telemetry. + +## 16. Diagnostics and observability + +Keep basic interrupt, exception, tick, and stack diagnostics in the ThreadX C6 +example. Provide the fuller SampleX integration counters from the beginning for: + +- Interrupts per source and unhandled interrupts. +- Kernel and high-resolution timer interrupts. +- Timer ISR-to-thread latency, event coalescing, active timer count, late + expirations, callback duration, and allocation failures. +- Wi-Fi OS adapter object creation/deletion. +- Wi-Fi event queue overflows. +- RX frames, drops, allocation failures, and queue high-water mark. +- TX frames, errors, copies, and completion outcomes. +- NetX packet pool availability and low-water mark. +- Association attempts, failures, disconnect reasons, and reconnects. +- Heap current use, peak use, and failed allocations. + +Early failures may occur with caches or normal logging unavailable, so retain a +minimal IRAM-safe diagnostic path and a reset-reason record. + +## 17. Primary risks and mitigations + +### Vendor ABI drift + +Risk: private Wi-Fi APIs, OS adapter fields, and archive dependencies change. + +Mitigation: pin ESP-IDF, check adapter version/layout at compile time, checksum +archives, and regenerate the undefined-symbol report in CI. + +### Queue and timer semantic differences + +Risk: subtle behavioral differences cause intermittent driver failures. + +Mitigation: implement direct native adapters, test their contracts separately, and +instrument all timeout and overflow paths. + +### SYSTIMER ownership and initialization ordering + +Risk: the imported timer's early initialization resets the whole SYSTIMER after +the ThreadX tick has been configured, stopping the kernel tick or corrupting alarm +state. + +Mitigation: make SampleX the sole peripheral owner, perform one early reset, assign +fixed nonoverlapping counters and alarms, and test both interrupt streams under +stress. Remove peripheral reset and IDF RCC acquisition from the adapted backend. + +### Imported timer-source drift + +Risk: an ESP-IDF update changes timer internals, `ETSTimer` layout, configuration +macros, or assumptions made by the Wi-Fi blobs. + +Mitigation: pin the timer sources to the Wi-Fi archive revision, hash imported +files, require clean patch application, assert ABI layout at compile time, retain +license notices, and rerun timer contract tests before accepting an update. + +### Interrupt-context incompatibility + +Risk: mixing ESP-IDF vector assumptions with ThreadX frames corrupts context. + +Mitigation: own the trap entry from the start and design it around the ThreadX RV32 +port contract. Do not link ESP-IDF's FreeRTOS vector hooks unchanged. + +### Private Layer-2 APIs + +Risk: internal transmit/RX callback APIs are not stable public interfaces. + +Mitigation: isolate them behind a small versioned adapter and keep NetX code unaware +of Espressif-private types. + +### PHY/platform dependency expansion + +Risk: enabling an apparently small feature pulls in PM, coexistence, NVS, or other +IDF frameworks. + +Mitigation: begin with station-only, no-power-save configuration and classify the +binary symbol closure before implementing platform hooks. + +## 18. Definition of success + +There are two independently reviewable completion points. + +### 18.1 ThreadX repository completion + +The ThreadX work is successful when the generic RV32 port retains its existing +behavior and `ports/risc-v32/gnu/example_build/esp32c6`: + +1. Builds without SampleX, NetX Duo, or Wi-Fi sources. +2. Boots through the stock Espressif bootloader and enters ThreadX. +3. Demonstrates stable context switching, preemption, synchronization objects, + the ThreadX tick, and at least one non-timer peripheral interrupt. +4. Keeps all ESP32-C6-specific startup, linker, vector, interrupt-controller, and + example code within the ESP32-C6 example directory unless a change is proven + to be a generic RV32 port correction. +5. Documents the required toolchain, image generation, flashing, and supported + ESP32-C6 board configuration. + +This completion point is an architecture-port example, not a supported full BSP, +ESP-IDF integration layer, or networking sample. + +### 18.2 SampleX demonstration completion + +The SampleX demonstration is successful when one ESP32-C6, with no external +peripheral hardware: + +1. Boots through the stock Espressif bootloader into ThreadX. +2. Runs all application and vendor-created threads under ThreadX. +3. Initializes the integrated PHY and scans for access points. +4. Associates with a WPA2-Personal network. +5. Exchanges raw Ethernet frames through a native NetX Duo driver. +6. Acquires an IPv4 address with NetX DHCP. +7. Resolves DNS and completes a TCP or UDP application transaction. +8. Recovers from access-point loss without rebooting. +9. Operates without FreeRTOS, lwIP, `esp_netif`, `esp_event`, or the stock + ESP-IDF `esp_timer` runtime/component. Its pinned timer sources run entirely on + native ThreadX and SampleX platform services. + +This design keeps the hardware self-contained and establishes a clear ownership +boundary: ThreadX contains the generic RV32 port and its minimal C6 adaptation +example; SampleX contains the complete board/demo integration; Espressif supplies +the radio implementation and low-level silicon knowledge; ThreadX supplies all +operating-system services; and NetX Duo supplies all IP networking. diff --git a/ports/risc-v32/gnu/example_build/esp32c6/tx_initialize_low_level.S b/ports/risc-v32/gnu/example_build/esp32c6/tx_initialize_low_level.S new file mode 100644 index 000000000..f7ad58aac --- /dev/null +++ b/ports/risc-v32/gnu/example_build/esp32c6/tx_initialize_low_level.S @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2025 10xEngineers + * Copyright (c) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + */ + +.section .iram0.text, "ax" +.align 4 +.global _tx_initialize_low_level +.extern _tx_initialize_unused_memory +.extern _tx_thread_system_stack_ptr +.extern esp32c6_platform_init + +_tx_initialize_low_level: + /* Allocate stack frame to preserve return address (ra) across C call */ + addi sp, sp, -16 + sw ra, 12(sp) + + /* Save current System Stack Pointer for ThreadX interrupt processing */ + la t0, _tx_thread_system_stack_ptr + sw sp, 0(t0) + + /* Perform C platform driver initialization (Console, PLIC, SYSTIMER) */ + call esp32c6_platform_init + + /* Restore return address and stack frame */ + lw ra, 12(sp) + addi sp, sp, 16 + ret diff --git a/ports/risc-v32/gnu/example_build/esp32c6/vectors.S b/ports/risc-v32/gnu/example_build/esp32c6/vectors.S new file mode 100644 index 000000000..2d96d394c --- /dev/null +++ b/ports/risc-v32/gnu/example_build/esp32c6/vectors.S @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2025 10xEngineers + * Copyright (c) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + */ + +.section .iram0.text, "ax" +.align 4 +.global _esp32c6_vector_table +.extern _tx_thread_context_save +.extern _tx_thread_context_restore +.extern esp32c6_interrupt_dispatch + +_esp32c6_vector_table: + /* Pre-allocate stack frame expected by ThreadX RISC-V context save */ + addi sp, sp, -128 + sw ra, 112(sp) + + /* Call ThreadX interrupt context save */ + call _tx_thread_context_save + + /* Dispatch interrupt to C handler */ + call esp32c6_interrupt_dispatch + + /* Jump to ThreadX interrupt context restore */ + j _tx_thread_context_restore