From 0f8352773db19eb1e7a32a83861ee3492b22512f Mon Sep 17 00:00:00 2001 From: Ammar Okla Date: Tue, 25 Aug 2026 16:47:46 +0300 Subject: [PATCH 1/9] Add Microchip PolarFire SoC target, reusable BSP framework, and Renode CI pipeline --- .github/workflows/ci.yml | 89 +++++++++ bsp/include/bsp/board.h | 19 ++ bsp/include/bsp/console.h | 29 +++ bsp/include/bsp/led.h | 34 ++++ cmake/utilities.cmake | 50 +++++ docs/architecture.md | 63 +++++++ .../POLARFIRE_ICICLE_RENODE/CMakeLists.txt | 41 ++++ .../POLARFIRE_ICICLE_RENODE/README.md | 64 +++++++ .../app/CMakeLists.txt | 57 ++++++ .../app/common/linker/linker.ld | 69 +++++++ .../app/common/startup/entry.S | 49 +++++ .../app/common/startup/newlib_stubs.c | 114 ++++++++++++ .../common/startup/tx_initialize_low_level.S | 90 +++++++++ .../POLARFIRE_ICICLE_RENODE/app/main.c | 176 ++++++++++++++++++ .../lib/CMakeLists.txt | 1 + .../lib/bsp/CMakeLists.txt | 26 +++ .../lib/bsp/include/board_config.h | 25 +++ .../lib/bsp/include/csr.h | 31 +++ .../lib/bsp/include/hwtimer.h | 30 +++ .../lib/bsp/include/uart.h | 24 +++ .../lib/bsp/src/board.c | 16 ++ .../lib/bsp/src/bsp_board.c | 29 +++ .../lib/bsp/src/bsp_console.c | 29 +++ .../lib/bsp/src/bsp_led.c | 39 ++++ .../lib/bsp/src/hwtimer.c | 29 +++ .../lib/bsp/src/trap.c | 55 ++++++ .../lib/bsp/src/uart.c | 51 +++++ .../renode/polarfire_demo.resc | 29 +++ .../renode/polarfire_demo.robot | 16 ++ .../POLARFIRE_ICICLE_RENODE/scripts/build.ps1 | 77 ++++++++ .../POLARFIRE_ICICLE_RENODE/scripts/build.sh | 75 ++++++++ .../scripts/test_renode.py | 118 ++++++++++++ templates/target/CMakeLists.txt | 54 ++++++ templates/target/README.md | 144 ++++++++++++++ templates/target/app/CMakeLists.txt | 40 ++++ templates/target/app/common/linker/README.md | 12 ++ templates/target/app/common/startup/README.md | 11 ++ templates/target/lib/CMakeLists.txt | 23 +++ templates/target/lib/bsp/CMakeLists.txt | 26 +++ .../target/lib/bsp/include/board_config.h | 36 ++++ templates/target/lib/bsp/src/bsp_board.c | 28 +++ templates/target/lib/bsp/src/bsp_console.c | 35 ++++ templates/target/lib/bsp/src/bsp_led.c | 42 +++++ templates/target/lib/bsp/src/newlib_stubs.c | 101 ++++++++++ templates/target/lib/vendor/README.md | 14 ++ templates/target/scripts/build.ps1 | 79 ++++++++ 46 files changed, 2289 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 bsp/include/bsp/board.h create mode 100644 bsp/include/bsp/console.h create mode 100644 bsp/include/bsp/led.h create mode 100644 cmake/utilities.cmake create mode 100644 docs/architecture.md create mode 100644 targets/Microchip/POLARFIRE_ICICLE_RENODE/CMakeLists.txt create mode 100644 targets/Microchip/POLARFIRE_ICICLE_RENODE/README.md create mode 100644 targets/Microchip/POLARFIRE_ICICLE_RENODE/app/CMakeLists.txt create mode 100644 targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/linker/linker.ld create mode 100644 targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/startup/entry.S create mode 100644 targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/startup/newlib_stubs.c create mode 100644 targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/startup/tx_initialize_low_level.S create mode 100644 targets/Microchip/POLARFIRE_ICICLE_RENODE/app/main.c create mode 100644 targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/CMakeLists.txt create mode 100644 targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/CMakeLists.txt create mode 100644 targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/board_config.h create mode 100644 targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/csr.h create mode 100644 targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/hwtimer.h create mode 100644 targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/uart.h create mode 100644 targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/board.c create mode 100644 targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/bsp_board.c create mode 100644 targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/bsp_console.c create mode 100644 targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/bsp_led.c create mode 100644 targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/hwtimer.c create mode 100644 targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/trap.c create mode 100644 targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/uart.c create mode 100644 targets/Microchip/POLARFIRE_ICICLE_RENODE/renode/polarfire_demo.resc create mode 100644 targets/Microchip/POLARFIRE_ICICLE_RENODE/renode/polarfire_demo.robot create mode 100644 targets/Microchip/POLARFIRE_ICICLE_RENODE/scripts/build.ps1 create mode 100644 targets/Microchip/POLARFIRE_ICICLE_RENODE/scripts/build.sh create mode 100644 targets/Microchip/POLARFIRE_ICICLE_RENODE/scripts/test_renode.py create mode 100644 templates/target/CMakeLists.txt create mode 100644 templates/target/README.md create mode 100644 templates/target/app/CMakeLists.txt create mode 100644 templates/target/app/common/linker/README.md create mode 100644 templates/target/app/common/startup/README.md create mode 100644 templates/target/lib/CMakeLists.txt create mode 100644 templates/target/lib/bsp/CMakeLists.txt create mode 100644 templates/target/lib/bsp/include/board_config.h create mode 100644 templates/target/lib/bsp/src/bsp_board.c create mode 100644 templates/target/lib/bsp/src/bsp_console.c create mode 100644 templates/target/lib/bsp/src/bsp_led.c create mode 100644 templates/target/lib/bsp/src/newlib_stubs.c create mode 100644 templates/target/lib/vendor/README.md create mode 100644 templates/target/scripts/build.ps1 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..66920e87 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,89 @@ +# +# 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/license/mit. +# +# SPDX-License-Identifier: MIT +# + +name: SampleX CI Verification Pipeline + +on: + push: + branches: [ main, master, dev, 'feat/**' ] + pull_request: + branches: [ main, master, dev ] + +jobs: + build-riscv-polarfire: + name: Build PolarFire SoC Icicle Kit (64-Bit RISC-V) + runs-on: ubuntu-24.04 + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install CMake and Ninja + run: | + sudo apt-get update + sudo apt-get install -y cmake ninja-build + + - name: Install Pinned xPack RISC-V GCC 14.2.0 + run: | + wget -q https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack/releases/download/v14.2.0-1/xpack-riscv-none-elf-gcc-14.2.0-1-linux-x64.tar.gz + mkdir -p $HOME/riscv-gcc + tar -xzf xpack-riscv-none-elf-gcc-14.2.0-1-linux-x64.tar.gz -C $HOME/riscv-gcc --strip-components=1 + rm xpack-riscv-none-elf-gcc-14.2.0-1-linux-x64.tar.gz + echo "$HOME/riscv-gcc/bin" >> $GITHUB_PATH + + - name: Build SampleX PolarFire Condition-Monitoring Demo + run: | + bash targets/Microchip/POLARFIRE_ICICLE_RENODE/scripts/build.sh --rebuild + + - name: Verify SampleX Demo ELF + run: | + test -f targets/Microchip/POLARFIRE_ICICLE_RENODE/build/app/polarfire_icicle_demo.elf + echo "[OK] PolarFire SampleX demo ELF verified." + + - name: Archive Built PolarFire ELF + uses: actions/upload-artifact@v4 + with: + name: polarfire-demo-elf + path: targets/Microchip/POLARFIRE_ICICLE_RENODE/build/app/polarfire_icicle_demo.elf + retention-days: 1 + + test-polarfire-renode: + name: Headless Renode Emulation & Assertion Test + needs: build-riscv-polarfire + runs-on: ubuntu-24.04 + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Set Up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Download Built PolarFire ELF + uses: actions/download-artifact@v4 + with: + name: polarfire-demo-elf + path: targets/Microchip/POLARFIRE_ICICLE_RENODE/build/app + + - name: Install Portable Renode Emulation Environment + run: | + wget -q https://builds.renode.io/renode-latest.linux-portable.tar.gz + mkdir -p $HOME/renode + tar -xzf renode-latest.linux-portable.tar.gz -C $HOME/renode --strip-components=1 + rm renode-latest.linux-portable.tar.gz + echo "$HOME/renode" >> $GITHUB_PATH + + - name: Run Deterministic Headless Renode Test + run: | + python3 targets/Microchip/POLARFIRE_ICICLE_RENODE/scripts/test_renode.py diff --git a/bsp/include/bsp/board.h b/bsp/include/bsp/board.h new file mode 100644 index 00000000..9dacee8f --- /dev/null +++ b/bsp/include/bsp/board.h @@ -0,0 +1,19 @@ +/* + * 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/license/mit. + * + * SPDX-License-Identifier: MIT + */ + +#ifndef BSP_BOARD_H +#define BSP_BOARD_H + +/** + * @brief Initialize the system core (clocks, flash latency, system configuration). + */ +void bsp_board_init(void); + +#endif /* BSP_BOARD_H */ diff --git a/bsp/include/bsp/console.h b/bsp/include/bsp/console.h new file mode 100644 index 00000000..54bcecf2 --- /dev/null +++ b/bsp/include/bsp/console.h @@ -0,0 +1,29 @@ +/* + * 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/license/mit. + * + * SPDX-License-Identifier: MIT + */ + +#ifndef BSP_CONSOLE_H +#define BSP_CONSOLE_H + +#include + +/** + * @brief Initialize the serial console peripheral and pin muxing. + */ +void bsp_console_init(void); + +/** + * @brief Write a buffer of data to the serial console. + * + * @param data Pointer to the character buffer to send. + * @param length Number of characters to transmit. + */ +void bsp_console_write(const char *data, size_t length); + +#endif /* BSP_CONSOLE_H */ diff --git a/bsp/include/bsp/led.h b/bsp/include/bsp/led.h new file mode 100644 index 00000000..ad2e3160 --- /dev/null +++ b/bsp/include/bsp/led.h @@ -0,0 +1,34 @@ +/* + * 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/license/mit. + * + * SPDX-License-Identifier: MIT + */ + +#ifndef BSP_LED_H +#define BSP_LED_H + +/** + * @brief Initialize the user LED hardware pin/peripherals. + */ +void bsp_led_init(void); + +/** + * @brief Turn the user LED on. + */ +void bsp_led_on(void); + +/** + * @brief Turn the user LED off. + */ +void bsp_led_off(void); + +/** + * @brief Toggle the state of the user LED. + */ +void bsp_led_toggle(void); + +#endif /* BSP_LED_H */ diff --git a/cmake/utilities.cmake b/cmake/utilities.cmake new file mode 100644 index 00000000..3289374d --- /dev/null +++ b/cmake/utilities.cmake @@ -0,0 +1,50 @@ +# Copyright (c) Microsoft +# Copyright (c) 2024 Eclipse Foundation +# +# This program and the accompanying materials are made available +# under the terms of the MIT license which is available at +# https://opensource.org/license/mit. +# +# SPDX-License-Identifier: MIT +# +# Contributors: +# Microsoft - Initial version +# Frédéric Desbiens - 2024 version. + +function(post_build TARGET) + if(CMAKE_C_COMPILER_ID STREQUAL "IAR") + add_custom_target(${TARGET}.bin ALL + DEPENDS ${TARGET} + COMMAND ${CMAKE_IAR_ELFTOOL} --bin ${TARGET}.elf ${TARGET}.bin) + elseif(CMAKE_C_COMPILER_ID STREQUAL "GNU") + add_custom_target(${TARGET}.bin ALL + DEPENDS ${TARGET} + COMMAND ${CMAKE_OBJCOPY} -Obinary ${TARGET}.elf ${TARGET}.bin + COMMAND ${CMAKE_OBJCOPY} -Oihex ${TARGET}.elf ${TARGET}.hex) + else() + message(FATAL_ERROR "Unknown CMAKE_C_COMPILER_ID ${CMAKE_C_COMPILER_ID}") + endif() +endfunction() + +function(set_target_linker TARGET LINKER_SCRIPT) + if(CMAKE_C_COMPILER_ID STREQUAL "IAR") + target_link_options(${TARGET} PRIVATE --config ${LINKER_SCRIPT}) + target_link_options(${TARGET} PRIVATE --map=${TARGET}.map) + elseif(CMAKE_C_COMPILER_ID STREQUAL "GNU") + target_link_options(${TARGET} PRIVATE -T${LINKER_SCRIPT}) + target_link_options(${TARGET} PRIVATE -Wl,-Map=${TARGET}.map) + set_target_properties(${TARGET} PROPERTIES SUFFIX ".elf") + + else() + message(FATAL_ERROR "Unknown CMAKE_C_COMPILER_ID ${CMAKE_C_COMPILER_ID}") + endif() +endfunction() + +macro(print_all_variables) + message(STATUS "print_all_variables------------------------------------------{") + get_cmake_property(_variableNames VARIABLES) + foreach (_variableName ${_variableNames}) + message(STATUS "${_variableName}=${${_variableName}}") + endforeach() + message(STATUS "print_all_variables------------------------------------------}") +endmacro() diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 00000000..49904474 --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,63 @@ +# Eclipse ThreadX BSP Framework Architecture + +This document describes the architecture, design philosophy, directory structure, and onboarding process for the reusable Board Support Package (BSP) framework. + +--- + +## 1. Design Philosophy + +The BSP framework is designed to be **additive and non-invasive**, allowing new boards to be integrated without modifying existing board implementations. + +1. **Legacy Isolation**: Existing boards (such as `/MXChip/AZ3166`) remain completely untouched to preserve their drivers, submodules, and build systems. +2. **Platform-Independent Applications**: Applications under `/apps` use only the abstract BSP interfaces and have no compile-time dependency on vendor-specific HALs, SDKs, or hardware registers. +3. **Reusable Infrastructure**: Shared CMake toolchains and build utilities are centralized under `/cmake` to eliminate duplicated build configuration across supported boards. + +--- + +## 2. Directory Structure + +```text +samplex/ (repository root) +├── libs/ # Shared RTOS components (ThreadX, NetXDuo, etc.) +├── MXChip/ # [Legacy] Existing standalone board sample +├── targets/ # [Framework] Supported BSP target boards +│ └── Microchip/ +│ └── POLARFIRE_ICICLE_RENODE/ # Board-specific BSP implementation & Renode target +├── bsp/ # [Framework] Abstract BSP interface definitions +│ └── include/bsp/ # board.h, led.h, console.h +├── cmake/ # [Framework] Shared CMake configuration and utilities +└── templates/ # [Framework] Templates for onboarding new boards +``` + +--- + +## 3. BSP Interface Contract + +Every board added to the framework under `/targets` must implement the abstract APIs defined in `/bsp/include/bsp/`. + +### Core Board Control (`board.h`) + +* `void bsp_board_init(void)`: Initializes the board, including system clocks, GPIO, and required peripherals. + +### LED Control (`led.h`) + +* `void bsp_led_init(void)`: Configures the board's user LED. +* `void bsp_led_on(void)`: Turns the LED on. +* `void bsp_led_off(void)`: Turns the LED off. +* `void bsp_led_toggle(void)`: Toggles the LED state. + +### Serial Console (`console.h`) + +* `void bsp_console_init(void)`: Initializes the default UART console. +* `void bsp_console_write(const char *data, size_t length)`: Transmits a block of data over the console interface. + +--- + +## 4. How to Onboard a New Board + +1. **Create the Target Folder**: Create a new directory under `targets///` using `/templates/target/` as the starting point. +2. **Define Local Configuration**: Create a `board_config.h` file containing board-specific settings such as clock configuration, UART parameters, and ThreadX memory allocation. +3. **Implement the BSP APIs**: Implement the interfaces defined in `/bsp/include/bsp/` using the vendor SDK or direct register access. +4. **Configure CMake**: Add the board target to `CMakeLists.txt`, build the BSP as a static library, and link it with the desired application from `/apps/`. + +Once a board implements the required BSP interfaces, any compatible application under `/apps` can be built for that board without modifying the application source. diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/CMakeLists.txt b/targets/Microchip/POLARFIRE_ICICLE_RENODE/CMakeLists.txt new file mode 100644 index 00000000..79ed6985 --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/CMakeLists.txt @@ -0,0 +1,41 @@ +# +# 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/license/mit. +# +# SPDX-License-Identifier: MIT +# + +cmake_minimum_required(VERSION 3.20) + +set(CMAKE_SYSTEM_NAME Generic) +set(CMAKE_SYSTEM_PROCESSOR riscv64) + +# Set 64-bit RISC-V Cross Compiler +if(DEFINED ENV{RISCV64_TOOLCHAIN_PATH}) + set(TOOLCHAIN_BIN_DIR "$ENV{RISCV64_TOOLCHAIN_PATH}/bin") + find_program(CMAKE_C_COMPILER NAMES riscv-none-elf-gcc riscv64-none-elf-gcc riscv64-unknown-elf-gcc PATHS ${TOOLCHAIN_BIN_DIR} NO_DEFAULT_PATH REQUIRED) + find_program(CMAKE_CXX_COMPILER NAMES riscv-none-elf-g++ riscv64-none-elf-g++ riscv64-unknown-elf-g++ PATHS ${TOOLCHAIN_BIN_DIR} NO_DEFAULT_PATH REQUIRED) + find_program(CMAKE_ASM_COMPILER NAMES riscv-none-elf-gcc riscv64-none-elf-gcc riscv64-unknown-elf-gcc PATHS ${TOOLCHAIN_BIN_DIR} NO_DEFAULT_PATH REQUIRED) +else() + find_program(CMAKE_C_COMPILER NAMES riscv-none-elf-gcc riscv64-none-elf-gcc riscv64-unknown-elf-gcc REQUIRED) + find_program(CMAKE_CXX_COMPILER NAMES riscv-none-elf-g++ riscv64-none-elf-g++ riscv64-unknown-elf-g++ REQUIRED) + find_program(CMAKE_ASM_COMPILER NAMES riscv-none-elf-gcc riscv64-none-elf-gcc riscv64-unknown-elf-gcc REQUIRED) +endif() + +project(polarfire_icicle_renode C ASM) + +# Root Directories +set(SAMPLEX_ROOT_DIR "${CMAKE_CURRENT_LIST_DIR}/../../..") +set(THREADX_DIR "${SAMPLEX_ROOT_DIR}/libs/threadx") + +# Architecture flags for SiFive U54 64-Bit RISC-V Core (RV64GC / lp64d) +set(RISCV_FLAGS "-march=rv64gc -mabi=lp64d -mcmodel=medany") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${RISCV_FLAGS} -O2 -g -Wall -Wextra") +set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} ${RISCV_FLAGS}") +set(CMAKE_EXE_LINKER_FLAGS "${RISCV_FLAGS} -T${CMAKE_CURRENT_LIST_DIR}/app/common/linker/linker.ld -nostartfiles -Wl,--gc-sections") + +add_subdirectory(lib) +add_subdirectory(app) diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/README.md b/targets/Microchip/POLARFIRE_ICICLE_RENODE/README.md new file mode 100644 index 00000000..0b3ffc58 --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/README.md @@ -0,0 +1,64 @@ +# Microchip PolarFire SoC Icicle Kit Target Integration (Renode 64-Bit RISC-V) + +This directory contains the target Board Support Package (BSP) and condition-monitoring demonstration application for the **Microchip PolarFire SoC Icicle Kit** running in the **Renode** emulation environment. + +--- + +## 1. Hardware Architecture Overview + +* **Target Board**: Microchip PolarFire SoC Icicle Kit (`targets/Microchip/POLARFIRE_ICICLE_RENODE`) +* **Processor Subsystem**: 5 RISC-V Harts (1x E51 Monitor Core + 4x 64-bit U54 Application Cores) +* **Execution Hart**: **Hart 1 (`u54_1`)** executes ThreadX; Harts 2–4 are parked in `wfi` loops while Hart 0 (E51) remains under platform monitor supervision. +* **CPU Core Architecture**: 64-Bit RISC-V (`rv64gc` / `lp64d` ABI @ 600 MHz) +* **System DRAM**: 1 GiB LPDDR4 Memory (`0x80000000` – `0xC0000000`) +* **Machine Timer**: SiFive CLINT `mtime` running at 1 MHz (`0x02000000`, 10ms tick = 10,000 cycles) +* **Serial Debug Console**: Microchip MMUART1 (`0x20100000`) at 115200 baud (8-N-1) +* **Telemetry**: Simulated LM75 temperature data processed via ThreadX queues and event flags + +--- + +## 2. Compilation Instructions + +Requirements: +* CMake 3.20+ and Ninja (or Make) +* 64-bit RISC-V GCC cross-compiler (`riscv64-none-elf-gcc`, `riscv-none-elf-gcc`, or xPack RISC-V GCC 14.2.0) + +### On Windows (PowerShell): +```powershell +powershell -ExecutionPolicy Bypass -File .\targets\Microchip\POLARFIRE_ICICLE_RENODE\scripts\build.ps1 -Clean -Rebuild +``` + +### On Linux / macOS (Bash): +```bash +bash targets/Microchip/POLARFIRE_ICICLE_RENODE/scripts/build.sh --rebuild +``` + +--- + +## 3. Renode Execution & Verification + +### Interactive Simulation (GUI / Terminal Analyzers): +Inside the Renode monitor: +```renode +include @targets/Microchip/POLARFIRE_ICICLE_RENODE/renode/polarfire_demo.resc +``` + +### Automated Headless Test Runner: +```bash +python targets/Microchip/POLARFIRE_ICICLE_RENODE/scripts/test_renode.py +``` + +### Expected Output Stream (`mmuart1` @ 115200 baud): +```text +==================================================== +Microchip PolarFire SoC Icicle Kit (Renode Target) +64-Bit RISC-V Industrial LM75 Condition-Monitoring App +==================================================== +[Monitor] ThreadX Ticks: 0 | Telemetry Pipeline Active | Queues OK +[Monitor] ThreadX Ticks: 100 | Telemetry Pipeline Active | Queues OK +[Monitor] ThreadX Ticks: 200 | Telemetry Pipeline Active | Queues OK +[Monitor] ThreadX Ticks: 300 | Telemetry Pipeline Active | Queues OK +[Monitor] ThreadX Ticks: 400 | Telemetry Pipeline Active | Queues OK +[Monitor] ThreadX Ticks: 500 | Telemetry Pipeline Active | Queues OK +[LM75 Sensor] Temperature: OVERTEMP ALARM TRIGGERED (>45.0C) +``` diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/CMakeLists.txt b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/CMakeLists.txt new file mode 100644 index 00000000..df293108 --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/CMakeLists.txt @@ -0,0 +1,57 @@ +# +# 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/license/mit. +# +# SPDX-License-Identifier: MIT +# + +# Glob all common C source files for ThreadX with CONFIGURE_DEPENDS +file(GLOB THREADX_COMMON_SOURCES CONFIGURE_DEPENDS "${THREADX_DIR}/common/src/*.c") + +# ThreadX Library Target for RISC-V 64-bit GNU Port +add_library(threadx STATIC + ${THREADX_COMMON_SOURCES} + ${CMAKE_CURRENT_SOURCE_DIR}/common/startup/tx_initialize_low_level.S + ${THREADX_DIR}/ports/risc-v64/gnu/src/tx_thread_context_restore.S + ${THREADX_DIR}/ports/risc-v64/gnu/src/tx_thread_context_save.S + ${THREADX_DIR}/ports/risc-v64/gnu/src/tx_thread_interrupt_control.S + ${THREADX_DIR}/ports/risc-v64/gnu/src/tx_thread_schedule.S + ${THREADX_DIR}/ports/risc-v64/gnu/src/tx_thread_stack_build.S + ${THREADX_DIR}/ports/risc-v64/gnu/src/tx_thread_system_return.S + ${THREADX_DIR}/ports/risc-v64/gnu/src/tx_timer_interrupt.S +) + +target_include_directories(threadx + PUBLIC + ${THREADX_DIR}/common/inc + ${THREADX_DIR}/ports/risc-v64/gnu/inc + ${CMAKE_CURRENT_SOURCE_DIR}/../lib/bsp/include +) + +# ------------------------------------------------------------- +# PolarFire SoC Icicle Kit Telemetry Executable +# ------------------------------------------------------------- +add_executable(polarfire_icicle_demo + main.c + common/startup/entry.S + common/startup/newlib_stubs.c +) + +set_target_properties(polarfire_icicle_demo PROPERTIES SUFFIX ".elf") + +target_include_directories(polarfire_icicle_demo + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/../lib/bsp/include + ${SAMPLEX_ROOT_DIR}/bsp/include +) + +target_link_libraries(polarfire_icicle_demo + PRIVATE + -Wl,--start-group + polarfire_bsp + threadx + -Wl,--end-group +) diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/linker/linker.ld b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/linker/linker.ld new file mode 100644 index 00000000..3a8a65e9 --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/linker/linker.ld @@ -0,0 +1,69 @@ +/* + * 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/license/mit. + * + * SPDX-License-Identifier: MIT + */ + +/* + * Linker Script for Microchip PolarFire SoC Icicle Kit (Renode Execution) + * Target Memory: LPDDR4 DRAM starting at 0x80000000 (1 GiB range) + */ + +OUTPUT_ARCH("riscv") +ENTRY(_start) + +MEMORY +{ + DRAM (rwx) : ORIGIN = 0x80000000, LENGTH = 0x40000000 /* 1 GiB System DRAM */ +} + +SECTIONS +{ + . = 0x80000000; + + .text : ALIGN(16) + { + *(.text._start) + *(.text*) + *(.rodata*) + } > DRAM + + .data : ALIGN(16) + { + *(.data*) + } > DRAM + + .sdata : ALIGN(8) + { + __global_pointer$ = . + 0x800; + *(.srodata*) + *(.sdata*) + *(.gnu.linkonce.s.*) + } > DRAM + + .sbss : ALIGN(8) + { + *(.sbss*) + *(.gnu.linkonce.sb.*) + } > DRAM + + .bss : ALIGN(16) + { + __bss_start = .; + *(.bss*) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(8); + __bss_end = .; + } > DRAM + + . = ALIGN(16); + . = . + 0x4000; /* 16 KB Boot Stack */ + __stack_top = .; + __end = .; + _end = .; +} diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/startup/entry.S b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/startup/entry.S new file mode 100644 index 00000000..39d4f6f7 --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/startup/entry.S @@ -0,0 +1,49 @@ +/* + * 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/license/mit. + * + * SPDX-License-Identifier: MIT + */ + + .section .text._start + .global _start + .align 4 + +_start: + /* Disable interrupts on current hart */ + csrci mstatus, 8 + + /* Read machine hart ID (mhartid) */ + csrr a0, mhartid + li t0, 1 + bne a0, t0, .park_hart + + /* Setup stack pointer and global pointer for Hart 1 */ + .option push + .option norelax + la gp, __global_pointer$ + .option pop + la sp, __stack_top + + /* Note: In Renode direct ELF loading, LMA == VMA so .data is already placed in DRAM. + * On physical hardware booting via HSS, HSS loads sections before jumping to _start. */ + /* Zero-initialize .bss section */ + la t0, __bss_start + la t1, __bss_end +.zero_bss_loop: + bgeu t0, t1, .bss_done + sd zero, 0(t0) + addi t0, t0, 8 + j .zero_bss_loop +.bss_done: + + /* Jump to main application entry */ + call main + +.park_hart: + /* Park unused harts in low-power WFI loop */ + wfi + j .park_hart diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/startup/newlib_stubs.c b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/startup/newlib_stubs.c new file mode 100644 index 00000000..24cf6148 --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/startup/newlib_stubs.c @@ -0,0 +1,114 @@ +/* + * 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/license/mit. + * + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include +#include +#include "board_config.h" +#include "bsp/console.h" + +extern char __end; /* Symbol set by linker at end of BSS / top of boot stack */ +static char *heap_ptr = NULL; + +static inline uintptr_t disable_interrupts(void) { + uintptr_t mstatus; + __asm__ volatile("csrrci %0, mstatus, 8" : "=r"(mstatus)); + return mstatus; +} + +static inline void restore_interrupts(uintptr_t mstatus) { + if (mstatus & 8) { + __asm__ volatile("csrrs zero, mstatus, 8"); + } +} + +void __malloc_lock(struct _reent *reent) { + (void)reent; +} + +void __malloc_unlock(struct _reent *reent) { + (void)reent; +} + +int _write(int file, char *ptr, int len) { + (void)file; + if (ptr == NULL || len <= 0) + return 0; + bsp_console_write(ptr, (size_t)len); + return len; +} + +void *_sbrk(ptrdiff_t incr) { + char *prev_heap_ptr; + uintptr_t mstatus = disable_interrupts(); + + if (heap_ptr == NULL) { + heap_ptr = &__end; + } + + if (incr > 0) { + if ((uintptr_t)heap_ptr + (uintptr_t)incr > (uintptr_t)BSP_RAM_END || + (uintptr_t)heap_ptr + (uintptr_t)incr < (uintptr_t)heap_ptr) { + restore_interrupts(mstatus); + errno = ENOMEM; + return (void *)-1; + } + } else if (incr < 0) { + if ((uintptr_t)heap_ptr < (uintptr_t)&__end + (uintptr_t)(-incr)) { + restore_interrupts(mstatus); + errno = EINVAL; + return (void *)-1; + } + } + + prev_heap_ptr = heap_ptr; + heap_ptr += incr; + + restore_interrupts(mstatus); + return (void *)prev_heap_ptr; +} + +int _read(int file, char *ptr, int len) { + (void)file; + (void)ptr; + (void)len; + return 0; +} + +int _close(int file) { + (void)file; + return -1; +} + +int _fstat(int file, struct stat *st) { + (void)file; + st->st_mode = S_IFCHR; + return 0; +} + +int _isatty(int file) { + (void)file; + return 1; +} + +int _lseek(int file, int ptr, int dir) { + (void)file; + (void)ptr; + (void)dir; + return 0; +} + +void _exit(int status) { + (void)status; + while (1) { + /* Hang on fatal exit */ + } +} diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/startup/tx_initialize_low_level.S b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/startup/tx_initialize_low_level.S new file mode 100644 index 00000000..7d9ee3f4 --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/startup/tx_initialize_low_level.S @@ -0,0 +1,90 @@ +/* + * 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/license/mit. + * + * SPDX-License-Identifier: MIT + */ + +#include "csr.h" + + .section .data + .global __tx_free_memory_start +__tx_free_memory_start: + + .section .text + .align 4 + .global trap_entry + .extern trap_handler + .extern _tx_thread_context_save + .extern _tx_thread_context_restore + +trap_entry: +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + addi sp, sp, -520 // 65*8 with floating point +#else + addi sp, sp, -256 // 32*8 without floating point +#endif + sd x1, 224(sp) // Save RA at offset 224 (28*8) + + call _tx_thread_context_save + + csrr a0, mcause + csrr a1, mepc + csrr a2, mtval + addi sp, sp, -8 + sd ra, 0(sp) + call trap_handler + ld ra, 0(sp) + addi sp, sp, 8 + + call _tx_thread_context_restore + +_err_hang: + wfi + j _err_hang + + .section .text + .global _tx_initialize_low_level + .weak _tx_initialize_low_level + .extern _end + .extern board_init + +_tx_initialize_low_level: + /* Save the system stack pointer */ + la t0, _tx_thread_system_stack_ptr + sd sp, 0(t0) + + /* Pickup first free memory address */ + la t0, _end + la t1, _tx_initialize_unused_memory + sd t0, 0(t1) + + /* Configure machine interrupt registers */ + li t0, MSTATUS_MIE + csrrc zero, mstatus, t0 + li t0, (MSTATUS_MPP_M | MSTATUS_MPIE) + csrrs zero, mstatus, t0 + li t0, MIE_MTIE + csrrs zero, mie, t0 + +#ifdef __riscv_flen + li t0, MSTATUS_FS + csrrs zero, mstatus, t0 + fscsr x0 +#endif + + /* Call hardware board init */ + addi sp, sp, -8 + sd ra, 0(sp) + call board_init + ld ra, 0(sp) + addi sp, sp, 8 + + /* Set Machine Trap-Vector Base Address to trap_entry */ + la t0, trap_entry + csrw mtvec, t0 + + ret diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/main.c b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/main.c new file mode 100644 index 00000000..7ba3d6da --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/main.c @@ -0,0 +1,176 @@ +/* + * 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/license/mit. + * + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include +#include "tx_api.h" +#include "bsp/board.h" +#include "bsp/console.h" +#include "bsp/led.h" + +#define DEMO_STACK_SIZE 4096 +#define DEMO_QUEUE_ITEMS 10 + +typedef struct SENSOR_DATA_STRUCT { + ULONG timestamp; + float temperature_celsius; + float reserved; +} SENSOR_DATA; + +#define DEMO_QUEUE_MSG_WORDS (sizeof(SENSOR_DATA) / sizeof(ULONG)) + +TX_THREAD sampler_thread; +TX_THREAD analyzer_thread; +TX_THREAD reporter_thread; +TX_QUEUE sensor_queue; +TX_EVENT_FLAGS_GROUP alarm_flags; + +UCHAR sampler_stack[DEMO_STACK_SIZE]; +UCHAR analyzer_stack[DEMO_STACK_SIZE]; +UCHAR reporter_stack[DEMO_STACK_SIZE]; +UCHAR queue_area[DEMO_QUEUE_ITEMS * sizeof(SENSOR_DATA)]; + +#define ALARM_OVERTEMP 0x01 + +static void console_print(const char *s) { + if (s) { + bsp_console_write(s, strlen(s)); + } +} + +void sampler_thread_entry(ULONG input); +void analyzer_thread_entry(ULONG input); +void reporter_thread_entry(ULONG input); + +int main(void) { + /* Initialize Board Peripherals & MMUART1 */ + bsp_board_init(); + + console_print("\n====================================================\n"); + console_print("Microchip PolarFire SoC Icicle Kit (Renode Target)\n"); + console_print("64-Bit RISC-V Industrial LM75 Condition-Monitoring App\n"); + console_print("====================================================\n"); + + /* Enter ThreadX Kernel */ + tx_kernel_enter(); + + return 0; +} + +void tx_application_define(void *first_unused_memory) { + (void)first_unused_memory; + UINT status; + + /* Create Sensor Queue */ + status = tx_queue_create(&sensor_queue, "sensor queue", DEMO_QUEUE_MSG_WORDS, + queue_area, sizeof(queue_area)); + if (status != TX_SUCCESS) { + console_print("[ERROR] Failed to create sensor queue\n"); + return; + } + + /* Create Event Flags Group */ + status = tx_event_flags_create(&alarm_flags, "alarm flags"); + if (status != TX_SUCCESS) { + console_print("[ERROR] Failed to create alarm flags\n"); + return; + } + + /* Create Sampler Thread */ + status = tx_thread_create(&sampler_thread, "Sampler Thread", sampler_thread_entry, 0, + sampler_stack, DEMO_STACK_SIZE, + 10, 10, TX_NO_TIME_SLICE, TX_AUTO_START); + if (status != TX_SUCCESS) { + console_print("[ERROR] Failed to create sampler thread\n"); + return; + } + + /* Create Analyzer Thread */ + status = tx_thread_create(&analyzer_thread, "Analyzer Thread", analyzer_thread_entry, 0, + analyzer_stack, DEMO_STACK_SIZE, + 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START); + if (status != TX_SUCCESS) { + console_print("[ERROR] Failed to create analyzer thread\n"); + return; + } + + /* Create Reporter Thread */ + status = tx_thread_create(&reporter_thread, "Reporter Thread", reporter_thread_entry, 0, + reporter_stack, DEMO_STACK_SIZE, + 12, 12, TX_NO_TIME_SLICE, TX_AUTO_START); + if (status != TX_SUCCESS) { + console_print("[ERROR] Failed to create reporter thread\n"); + return; + } +} + +void sampler_thread_entry(ULONG input) { + (void)input; + SENSOR_DATA data; + float simulated_temp = 25.0f; + + while (1) { + data.timestamp = tx_time_get(); + data.temperature_celsius = simulated_temp; + data.reserved = 0.0f; + + /* Send telemetry to Queue */ + UINT status = tx_queue_send(&sensor_queue, &data, TX_NO_WAIT); + if (status != TX_SUCCESS) { + console_print("[WARN] Telemetry queue send failed\n"); + } + + simulated_temp += 2.5f; + if (simulated_temp > 55.0f) { + simulated_temp = 25.0f; + } + + tx_thread_sleep(50); /* Sample every 500ms */ + } +} + +void analyzer_thread_entry(ULONG input) { + (void)input; + SENSOR_DATA data; + + while (1) { + if (tx_queue_receive(&sensor_queue, &data, TX_WAIT_FOREVER) == TX_SUCCESS) { + if (data.temperature_celsius > 45.0f) { + UINT status = tx_event_flags_set(&alarm_flags, ALARM_OVERTEMP, TX_OR); + if (status != TX_SUCCESS) { + console_print("[WARN] Alarm flag set failed\n"); + } + bsp_led_on(); + } else { + bsp_led_off(); + } + } + } +} + +void reporter_thread_entry(ULONG input) { + (void)input; + char msg_buf[128]; + ULONG actual_flags; + + while (1) { + snprintf(msg_buf, sizeof(msg_buf), + "[Monitor] ThreadX Ticks: %lu | Telemetry Pipeline Active | Queues OK\n", + (unsigned long)tx_time_get()); + console_print(msg_buf); + + if (tx_event_flags_get(&alarm_flags, ALARM_OVERTEMP, TX_OR_CLEAR, &actual_flags, TX_NO_WAIT) == TX_SUCCESS) { + console_print("[LM75 Sensor] Temperature: OVERTEMP ALARM TRIGGERED (>45.0C)\n"); + } + + tx_thread_sleep(100); /* Report every 1 second */ + } +} diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/CMakeLists.txt b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/CMakeLists.txt new file mode 100644 index 00000000..2c1005bd --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(bsp) diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/CMakeLists.txt b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/CMakeLists.txt new file mode 100644 index 00000000..c29eb755 --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/CMakeLists.txt @@ -0,0 +1,26 @@ +# +# 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/license/mit. +# +# SPDX-License-Identifier: MIT +# + +# Static library compilation rule for Microchip PolarFire SoC Icicle Kit BSP Target +add_library(polarfire_bsp STATIC + src/bsp_board.c + src/bsp_led.c + src/bsp_console.c + src/board.c + src/uart.c + src/hwtimer.c + src/trap.c +) + +target_include_directories(polarfire_bsp + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${SAMPLEX_ROOT_DIR}/bsp/include +) diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/board_config.h b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/board_config.h new file mode 100644 index 00000000..bc506801 --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/board_config.h @@ -0,0 +1,25 @@ +/* + * 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/license/mit. + * + * SPDX-License-Identifier: MIT + */ + +#ifndef BOARD_CONFIG_H +#define BOARD_CONFIG_H + +/* CPU Subsystem Frequencies */ +#define BSP_CPU_CLOCK_HZ 600000000ULL /* 600 MHz U54 Application Core Clock */ +#define BSP_SYSTEM_CLOCK_HZ BSP_CPU_CLOCK_HZ +#define BSP_CLINT_RTC_FREQ_HZ 1000000ULL /* 1 MHz Real-Time CLINT Clock in Renode */ + +#define BSP_UART_BAUDRATE 115200 +#define BSP_RAM_END 0xC0000000ULL /* 1 GiB LPDDR4 DRAM End Address */ + +#define BSP_HAS_LED 1 +#define BSP_HAS_CONSOLE 1 + +#endif /* BOARD_CONFIG_H */ diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/csr.h b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/csr.h new file mode 100644 index 00000000..e3a9e3be --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/csr.h @@ -0,0 +1,31 @@ +/* + * 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/license/mit. + * + * SPDX-License-Identifier: MIT + */ + +#ifndef RISCV_CSR_H +#define RISCV_CSR_H + +// Machine Status Register, mstatus +#define MSTATUS_MPP_MASK (3L << 11) // previous mode. +#define MSTATUS_MPP_M (3L << 11) +#define MSTATUS_MPP_S (1L << 11) +#define MSTATUS_MPP_U (0L << 11) +#define MSTATUS_MIE (1L << 3) // machine-mode interrupt enable. +#define MSTATUS_MPIE (1L << 7) +#define MSTATUS_FS (1L << 13) + +// Machine-mode Interrupt Enable +#define MIE_MTIE (1L << 7) +#define MIE_MSIE (1L << 3) +#define MIE_MEIE (1L << 11) +#define MIE_STIE (1L << 5) // supervisor timer +#define MIE_SSIE (1L << 1) +#define MIE_SEIE (1L << 9) + +#endif /* RISCV_CSR_H */ diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/hwtimer.h b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/hwtimer.h new file mode 100644 index 00000000..7887edac --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/hwtimer.h @@ -0,0 +1,30 @@ +/* + * 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/license/mit. + * + * SPDX-License-Identifier: MIT + */ + +#ifndef POLARFIRE_HWTIMER_H +#define POLARFIRE_HWTIMER_H + +#include +#include "board_config.h" + +#define CLINT_BASE 0x02000000ULL +#define MTIME_REG (*(volatile uint64_t *)(CLINT_BASE + 0xBFF8)) +#define HART1_MTIMECMP_REG (*(volatile uint64_t *)(CLINT_BASE + 0x4008)) + +#ifndef TX_TIMER_TICKS_PER_SECOND +#define TX_TIMER_TICKS_PER_SECOND 100ULL +#endif + +#define TICK_CYCLES (BSP_CLINT_RTC_FREQ_HZ / (uint64_t)TX_TIMER_TICKS_PER_SECOND) + +void hwtimer_init(void); +void hwtimer_ack(void); + +#endif /* POLARFIRE_HWTIMER_H */ diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/uart.h b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/uart.h new file mode 100644 index 00000000..f10dfb61 --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/uart.h @@ -0,0 +1,24 @@ +/* + * 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/license/mit. + * + * SPDX-License-Identifier: MIT + */ + +#ifndef POLARFIRE_UART_H +#define POLARFIRE_UART_H + +#include +#include + +#define MMUART1_BASE 0x20100000ULL + +void uart_init(void); +void uart_putc(char ch); +void uart_puts(const char *str); +void uart_write(const char *data, size_t len); + +#endif /* POLARFIRE_UART_H */ diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/board.c b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/board.c new file mode 100644 index 00000000..b0606309 --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/board.c @@ -0,0 +1,16 @@ +/* + * 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/license/mit. + * + * SPDX-License-Identifier: MIT + */ + +#include "hwtimer.h" + +void board_init(void) { + /* Initialize 64-bit MTIME machine timer (10ms tick interval) */ + hwtimer_init(); +} diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/bsp_board.c b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/bsp_board.c new file mode 100644 index 00000000..67b9160b --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/bsp_board.c @@ -0,0 +1,29 @@ +/* + * 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/license/mit. + * + * SPDX-License-Identifier: MIT + */ + +#include +#include "bsp/board.h" +#include "bsp/console.h" +#include "bsp/led.h" +#include "board_config.h" + +extern void board_init(void); + +void bsp_board_init(void) { + /* Initialize PolarFire hardware peripherals */ + board_init(); + + bsp_console_init(); + bsp_led_init(); +} + +uint32_t bsp_board_get_system_clock(void) { + return (uint32_t)BSP_SYSTEM_CLOCK_HZ; +} diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/bsp_console.c b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/bsp_console.c new file mode 100644 index 00000000..ef1c275a --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/bsp_console.c @@ -0,0 +1,29 @@ +/* + * 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/license/mit. + * + * SPDX-License-Identifier: MIT + */ + +#include "bsp/console.h" +#include "board_config.h" +#include + +extern void uart_init(void); +extern void uart_write(const char *data, size_t len); + +void bsp_console_init(void) { +#if BSP_HAS_CONSOLE + uart_init(); +#endif +} + +void bsp_console_write(const char *data, size_t length) { +#if BSP_HAS_CONSOLE + if (!data || length == 0) return; + uart_write(data, length); +#endif +} diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/bsp_led.c b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/bsp_led.c new file mode 100644 index 00000000..cc05a9e6 --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/bsp_led.c @@ -0,0 +1,39 @@ +/* + * 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/license/mit. + * + * SPDX-License-Identifier: MIT + */ + +#include +#include "bsp/led.h" +#include "board_config.h" + +static bool s_virtual_led_state = false; + +void bsp_led_init(void) { +#if BSP_HAS_LED + s_virtual_led_state = false; +#endif +} + +void bsp_led_on(void) { +#if BSP_HAS_LED + s_virtual_led_state = true; +#endif +} + +void bsp_led_off(void) { +#if BSP_HAS_LED + s_virtual_led_state = false; +#endif +} + +void bsp_led_toggle(void) { +#if BSP_HAS_LED + s_virtual_led_state = !s_virtual_led_state; +#endif +} diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/hwtimer.c b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/hwtimer.c new file mode 100644 index 00000000..219ae0d4 --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/hwtimer.c @@ -0,0 +1,29 @@ +/* + * 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/license/mit. + * + * SPDX-License-Identifier: MIT + */ + +#include "hwtimer.h" + +void hwtimer_init(void) { + /* Program initial mtimecmp target to current mtime + 10,000 cycles (10ms) */ + uint64_t current_mtime = MTIME_REG; + HART1_MTIMECMP_REG = current_mtime + TICK_CYCLES; +} + +void hwtimer_ack(void) { + uint64_t current_mtime = MTIME_REG; + uint64_t next_cmp = HART1_MTIMECMP_REG + TICK_CYCLES; + + /* Clamp to current_mtime + TICK_CYCLES if timer fell behind */ + if (next_cmp <= current_mtime) { + next_cmp = current_mtime + TICK_CYCLES; + } + + HART1_MTIMECMP_REG = next_cmp; +} diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/trap.c b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/trap.c new file mode 100644 index 00000000..3d9b832a --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/trap.c @@ -0,0 +1,55 @@ +/* + * 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 +#include "hwtimer.h" + +extern void _tx_timer_interrupt(void); +extern void uart_puts(const char *str); + +static void print_hex64(uint64_t val) { + const char hex_chars[] = "0123456789ABCDEF"; + char buf[19]; + buf[0] = '0'; + buf[1] = 'x'; + for (int i = 15; i >= 0; --i) { + buf[2 + (15 - i)] = hex_chars[(val >> (i * 4)) & 0xF]; + } + buf[18] = '\0'; + uart_puts(buf); +} + +void trap_handler(uint64_t mcause, uint64_t mepc, uint64_t mtval) { + /* Check if trap is an interrupt (bit 63 set) */ + if (mcause & (1ULL << 63)) { + uint64_t irq = mcause & 0x3FULL; + if (irq == 7) { + /* Machine Timer Interrupt (CLINT MTIME) */ + hwtimer_ack(); + _tx_timer_interrupt(); + return; + } + } + + /* Unhandled interrupt or synchronous exception */ + uart_puts("\r\n========================================\r\n"); + uart_puts("[FATAL TRAP] System Halted!\r\n"); + uart_puts(" mcause: "); + print_hex64(mcause); + uart_puts("\r\n mepc: "); + print_hex64(mepc); + uart_puts("\r\n mtval: "); + print_hex64(mtval); + uart_puts("\r\n========================================\r\n"); + + while (1) { + __asm__ volatile("wfi"); + } +} diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/uart.c b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/uart.c new file mode 100644 index 00000000..d27f7a53 --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/uart.c @@ -0,0 +1,51 @@ +/* + * 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/license/mit. + * + * SPDX-License-Identifier: MIT + */ + +#include "uart.h" + +#define REG_THR (*(volatile uint32_t *)(MMUART1_BASE + 0x00)) +#define REG_RBR (*(volatile uint32_t *)(MMUART1_BASE + 0x00)) +#define REG_IER (*(volatile uint32_t *)(MMUART1_BASE + 0x04)) +#define REG_LCR (*(volatile uint32_t *)(MMUART1_BASE + 0x0C)) +#define REG_LSR (*(volatile uint32_t *)(MMUART1_BASE + 0x14)) + +#define LSR_THRE 0x20 /* Transmitter Holding Register Empty */ + +void uart_init(void) { + /* 8 data bits, 1 stop bit, no parity (8-N-1) */ + REG_LCR = 0x03; + /* Disable interrupts initially */ + REG_IER = 0x00; +} + +void uart_putc(char ch) { + /* Poll Transmitter Holding Register Empty (THRE) bit */ + while ((REG_LSR & LSR_THRE) == 0); + REG_THR = (uint32_t)ch; + + if (ch == '\n') { + while ((REG_LSR & LSR_THRE) == 0); + REG_THR = '\r'; + } +} + +void uart_puts(const char *str) { + if (!str) return; + while (*str) { + uart_putc(*str++); + } +} + +void uart_write(const char *data, size_t len) { + if (!data || len == 0) return; + for (size_t i = 0; i < len; i++) { + uart_putc(data[i]); + } +} diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/renode/polarfire_demo.resc b/targets/Microchip/POLARFIRE_ICICLE_RENODE/renode/polarfire_demo.resc new file mode 100644 index 00000000..6cf2cee5 --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/renode/polarfire_demo.resc @@ -0,0 +1,29 @@ +:name: PolarFire SoC - ThreadX Industrial LM75 Demo +:description: Eclipse ThreadX RTOS on Microchip PolarFire SoC (Renode 64-Bit RISC-V) + +# Clear previous emulation state +Clear + +using sysbus +mach create "PolarFire_SoC_Icicle" +machine LoadPlatformDescription @platforms/cpus/polarfire-soc.repl + +# Show terminal analyzer for Hart 1 (u54_1) MMUART1 at 0x20100000 +showAnalyzer mmuart1 + +# Portable relative path using Renode's built-in $ORIGIN variable +$bin?=$ORIGIN/../build/app/polarfire_icicle_demo.elf + +macro reset +""" + sysbus LoadELF $bin + # Route execution directly to Hart 1 (U54 Core 1) + u54_1 PC `e51 PC` + e51 IsHalted true + u54_2 IsHalted true + u54_3 IsHalted true + u54_4 IsHalted true +""" +runMacro $reset + +start diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/renode/polarfire_demo.robot b/targets/Microchip/POLARFIRE_ICICLE_RENODE/renode/polarfire_demo.robot new file mode 100644 index 00000000..6d3f3fa6 --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/renode/polarfire_demo.robot @@ -0,0 +1,16 @@ +*** Settings *** +Suite Setup Setup +Suite Teardown Teardown +Test Setup Reset Emulation +Test Teardown Test Teardown +Resource ${RENODEKEYWORDS} + +*** Test Cases *** +Should Boot ThreadX And Trigger Telemetry Alarms + Execute Command include @${CURDIR}/polarfire_demo.resc + Create Terminal Tester sysbus.mmuart1 + + Wait For Line On Uart Microchip PolarFire SoC Icicle Kit (Renode Target) timeout=10 + Wait For Line On Uart [Monitor] ThreadX Ticks: 0 | Memory Area Active | Queues OK timeout=10 + Wait For Line On Uart [Monitor] ThreadX Ticks: 100 | Memory Area Active | Queues OK timeout=10 + Wait For Line On Uart OVERTEMP ALARM TRIGGERED timeout=15 diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/scripts/build.ps1 b/targets/Microchip/POLARFIRE_ICICLE_RENODE/scripts/build.ps1 new file mode 100644 index 00000000..8e57b262 --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/scripts/build.ps1 @@ -0,0 +1,77 @@ +# PowerShell build automation script for Microchip PolarFire SoC Icicle Kit (Renode 64-Bit RISC-V Target) + +param( + [switch]$Clean, + [switch]$Rebuild, + [string]$BuildType = "Debug", + [string]$ToolchainPath = "" +) + +$ErrorActionPreference = "Stop" +$TargetDir = Split-Path -Parent $PSScriptRoot +$BuildDir = Join-Path $TargetDir "build" + +Write-Host "==========================================" -ForegroundColor Cyan +Write-Host " Building PolarFire SoC Icicle Kit Target " -ForegroundColor Cyan +Write-Host "==========================================" -ForegroundColor Cyan + +# 1. Dynamic Toolchain Path Detection +if ($ToolchainPath -ne "") { + $env:RISCV64_TOOLCHAIN_PATH = $ToolchainPath +} elseif (-not $env:RISCV64_TOOLCHAIN_PATH) { + # Check default toolchain candidates in user profile + $Candidates = @( + (Join-Path $env:USERPROFILE "Downloads\xpack-riscv-none-elf-gcc-14.2.0-1-win32-x64\xpack-riscv-none-elf-gcc-14.2.0-1"), + (Join-Path $env:USERPROFILE "Downloads\tools\xpack-riscv-none-elf-gcc-14.2.0-1"), + (Join-Path $env:ProgramFiles "xpack-riscv-none-elf-gcc-14.2.0-1") + ) + foreach ($Candidate in $Candidates) { + if (Test-Path $Candidate) { + $env:RISCV64_TOOLCHAIN_PATH = $Candidate + break + } + } +} + +# 2. Dynamic Ninja Tool Detection +$NinjaCandidates = @( + (Join-Path $env:USERPROFILE "Downloads\tools\ninja-win"), + (Join-Path $env:ProgramFiles "Ninja") +) +foreach ($NinjaPath in $NinjaCandidates) { + if (Test-Path $NinjaPath) { + $env:PATH = "$NinjaPath;" + $env:PATH + break + } +} + +if ($env:RISCV64_TOOLCHAIN_PATH) { + Write-Host "Using RISCV64_TOOLCHAIN_PATH: $env:RISCV64_TOOLCHAIN_PATH" -ForegroundColor Yellow +} + +if ($Clean -or $Rebuild) { + if (Test-Path $BuildDir) { + Write-Host "Cleaning build directory..." -ForegroundColor Yellow + Remove-Item -Path $BuildDir -Recurse -Force + } +} + +if (-not (Test-Path $BuildDir)) { + New-Item -Path $BuildDir -ItemType Directory | Out-Null +} + +Push-Location $BuildDir +try { + Write-Host "Configuring CMake (Ninja Generator)..." -ForegroundColor Green + cmake -G "Ninja" "-DCMAKE_BUILD_TYPE=$BuildType" .. + + Write-Host "Building target binaries..." -ForegroundColor Green + ninja + + Write-Host "==========================================" -ForegroundColor Cyan + Write-Host " Build Completed Successfully! " -ForegroundColor Green + Write-Host "==========================================" -ForegroundColor Cyan +} +finally { + Pop-Location +} diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/scripts/build.sh b/targets/Microchip/POLARFIRE_ICICLE_RENODE/scripts/build.sh new file mode 100644 index 00000000..bd5eaa9e --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/scripts/build.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env bash + +# Copyright (c) 2026 Eclipse ThreadX contributors +# +# SPDX-License-Identifier: MIT + +# Fail on error +set -e + +BUILD_TYPE="Debug" +CLEAN=false +REBUILD=false + +# Directory resolution +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd)" +TARGET_DIR="$(dirname "$SCRIPT_DIR")" +BUILD_DIR="$TARGET_DIR/build" + +# Print banner +echo "==========================================" +echo " PolarFire SoC Icicle Kit - Build (Bash) " +echo "==========================================" +echo "Target Dir: $TARGET_DIR" +echo "Build Dir: $BUILD_DIR" +echo "Build Type: $BUILD_TYPE" +echo "" + +# Parse options +while [[ $# -gt 0 ]]; do + case "$1" in + --clean) + CLEAN=true + shift + ;; + --rebuild) + REBUILD=true + shift + ;; + --release) + BUILD_TYPE="Release" + shift + ;; + *) + echo "Unknown option: $1" + echo "Usage: $0 [--clean] [--rebuild] [--release]" + exit 1 + ;; + esac +done + +if [ "$CLEAN" = true ] || [ "$REBUILD" = true ]; then + echo "[INFO] Cleaning build directory..." + if [ -d "$BUILD_DIR" ]; then + rm -rf "$BUILD_DIR" + fi +fi + +mkdir -p "$BUILD_DIR" +cd "$BUILD_DIR" + +echo "[INFO] Configuring CMake..." +if command -v ninja &>/dev/null; then + cmake -G Ninja -DCMAKE_BUILD_TYPE="$BUILD_TYPE" "$TARGET_DIR" + echo "[INFO] Building with Ninja..." + ninja +else + cmake -DCMAKE_BUILD_TYPE="$BUILD_TYPE" "$TARGET_DIR" + echo "[INFO] Building..." + cmake --build . +fi + +echo "" +echo "==========================================" +echo "[OK] Build completed successfully!" +echo "==========================================" diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/scripts/test_renode.py b/targets/Microchip/POLARFIRE_ICICLE_RENODE/scripts/test_renode.py new file mode 100644 index 00000000..90a8f9f4 --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/scripts/test_renode.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python3 +# +# 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/license/mit. +# +# SPDX-License-Identifier: MIT +# + +""" +Headless Renode Verification Test for PolarFire SoC Icicle Kit ThreadX Demo +""" + +import os +import sys +import time +import subprocess +import shutil +import threading +import queue + +def find_renode(): + # Check PATH first + renode_bin = shutil.which("renode") + if renode_bin: + return renode_bin + + # Common Windows locations + win_paths = [ + r"C:\Program Files\Renode\renode.exe", + os.path.expanduser(r"~\AppData\Local\Programs\Renode\renode.exe") + ] + for path in win_paths: + if os.path.isfile(path): + return path + + return "renode" + +def reader_thread_fn(pipe, q): + try: + for line in iter(pipe.readline, ''): + q.put(line) + except Exception: + pass + finally: + pipe.close() + +def run_test(): + renode = find_renode() + script_dir = os.path.dirname(os.path.abspath(__file__)) + target_dir = os.path.dirname(script_dir) + resc_path = os.path.join(target_dir, "renode", "polarfire_demo.resc").replace("\\", "/") + + print(f"[*] Starting headless Renode test using: {renode}") + print(f"[*] Loading script: {resc_path}") + + cmd = [ + renode, + "--plain", + "--disable-gui", + "-e", f"include @{resc_path}" + ] + + proc = subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + bufsize=1 + ) + + output_q = queue.Queue() + reader_t = threading.Thread(target=reader_thread_fn, args=(proc.stdout, output_q), daemon=True) + reader_t.start() + + output_lines = [] + found_ticks = False + found_alarm = False + start_time = time.time() + timeout_seconds = 25.0 + + try: + while time.time() - start_time < timeout_seconds: + try: + line = output_q.get(timeout=0.1) + output_lines.append(line) + print(line, end="") + if "ThreadX Ticks" in line: + found_ticks = True + if "OVERTEMP ALARM TRIGGERED" in line: + found_alarm = True + if found_ticks and found_alarm: + print("\n[+] SUCCESS: Both ThreadX system ticks and LM75 overtemperature alarm detected!") + break + except queue.Empty: + if proc.poll() is not None: + break + finally: + try: + proc.terminate() + proc.wait(timeout=2) + except Exception: + try: + proc.kill() + except Exception: + pass + + if found_ticks and found_alarm: + print("[+] Renode headless test PASSED.") + sys.exit(0) + else: + print(f"\n[-] FAILED: Timed out waiting for expected telemetry. (found_ticks={found_ticks}, found_alarm={found_alarm})") + sys.exit(1) + +if __name__ == "__main__": + run_test() diff --git a/templates/target/CMakeLists.txt b/templates/target/CMakeLists.txt new file mode 100644 index 00000000..7bb05e70 --- /dev/null +++ b/templates/target/CMakeLists.txt @@ -0,0 +1,54 @@ +# 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/license/mit. +# +# SPDX-License-Identifier: MIT + +cmake_minimum_required(VERSION 3.15) + +# TODO: Replace 'TARGET_BOARD_TEMPLATE' with your target board name (e.g. MY_CUSTOM_BOARD) +project(TARGET_BOARD_TEMPLATE C CXX ASM) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_C_STANDARD_REQUIRED ON) + +# Define root paths +get_filename_component(WORKSPACE_ROOT "${CMAKE_CURRENT_LIST_DIR}/../../.." ABSOLUTE) +set(SHARED_LIB_DIR "${WORKSPACE_ROOT}/libs") +set(SHARED_APP_DIR "${WORKSPACE_ROOT}/apps") +set(SHARED_BSP_DIR "${WORKSPACE_ROOT}/bsp") +set(SHARED_CMAKE_DIR "${WORKSPACE_ROOT}/cmake") + +# Include GNU ARM Toolchain setup if building for bare-metal ARM Cortex-M +if(EXISTS "${SHARED_CMAKE_DIR}/gcc-arm-none-eabi.cmake") + include("${SHARED_CMAKE_DIR}/gcc-arm-none-eabi.cmake") +endif() + +# Global compiler flags +add_compile_options( + -g3 + -ffunction-sections + -fdata-sections + -fno-strict-aliasing + -fno-builtin + -fno-common + -Wall + -Wshadow + -Wdouble-promotion + -Werror + -Wno-unused-parameter +) + +# Global preprocessor definitions +add_compile_definitions( + TX_INCLUDE_USER_DEFINE_FILE +) + +# TODO: Add vendor SDK / HAL subdirectories here if needed +# Example: add_subdirectory(lib/vendor_hal) + +# Add libraries and application targets +add_subdirectory(lib) +add_subdirectory(app) diff --git a/templates/target/README.md b/templates/target/README.md new file mode 100644 index 00000000..43a35a13 --- /dev/null +++ b/templates/target/README.md @@ -0,0 +1,144 @@ +# Target Board Onboarding Blueprint & Template Guide + +The core philosophy of the Eclipse ThreadX Reusable BSP Framework is to separate platform-independent application logic from board-specific hardware implementations through abstract C interfaces. By establishing a strict boundary between generic application code and physical registers, the same application source code can be compiled for multiple hardware targets (such as STM32, Raspberry Pi Pico, ESP32, or NXP platforms using vendor SDKs or other platform support packages) without requiring changes to the application itself. + +This directory contains the skeletal blueprint for onboarding a new hardware board into the framework. + +> [!NOTE] +> **Blueprint Nature**: +> This template is a **documented blueprint**, not a compilable target out-of-the-box. To compile it successfully, the developer must supply target-specific startup assemblies, linker scripts, and a vendor SDK or platform support package. + +--- + +## 1. Dependency Flow Architecture + +The following diagram illustrates how the generic application layer is decoupled from physical MCU registers using the BSP framework: + +```mermaid +flowchart TD + App["Generic Application (apps/threadx_demo/main.c)"] + BSP_API["BSP Interface Contract (bsp/include/bsp/)"] + Target_BSP["Target BSP Driver Library (targets/<Vendor>/<Board>/lib/bsp/)"] + Vendor_SDK["Vendor SDK / Platform Support Libraries"] + Hardware["Physical Target Board Hardware"] + + App -->|Uses generic C APIs| BSP_API + BSP_API -->|Implemented by| Target_BSP + Target_BSP -->|Controls hardware via| Vendor_SDK + Vendor_SDK -->|Configures| Hardware +``` + +### High-Level Repository Layout + +```text +Repository +│ +├── apps/ (Platform-independent application logic) +├── bsp/ (Target-agnostic C interface contracts) +├── cmake/ (Shared build infrastructure & toolchains) +└── targets/ (Independent board support implementations) + ├── STMicroelectronics/NUCLEO_F401RE/ + └── Microchip/POLARFIRE_ICICLE_RENODE/ +``` + +--- + +## 2. Shared Directory Governance & BSP Ownership + +To maintain long-term framework maintainability and portability, the following root directories should generally remain **unchanged** when onboarding a new board: + +* `/apps`: Contains shared example applications and reusable demos (such as `threadx_demo`). Applications in this directory interact with hardware solely through the abstract headers in ``. Developers may also add additional applications alongside the provided examples. +* `/bsp`: Defines the target-agnostic C interface contracts (`board.h`, `led.h`, `console.h`). New board targets must implement these existing interfaces rather than modifying core interface definitions. +* `/cmake`: Contains shared cross-compilation toolchain settings and build utility functions. + +> [!NOTE] +> **Core Architectural Principle**: +> The framework intentionally standardizes only the BSP interface and build structure. Board startup code, linker scripts, and vendor SDK integration remain board-specific. + +> [!IMPORTANT] +> **Independent Target BSP Ownership**: +> Each target board owns its BSP implementation independently within `targets///`. No BSP code is shared across target directories, guaranteeing that modifying or updating one board target will never cause side effects or build regressions on another target. + +--- + +## 3. "What Goes Where" Asset Mapping + +The table below maps common embedded software components to their designated locations within a target board package: + +| Asset / Component | Framework Location | Sourced From | +| :--- | :--- | :--- | +| **Vendor SDK / Platform Libraries** | `targets///lib/vendor/` or external CMake package | Official MCU Vendor SDK / Reference Package | +| **Startup Assembly & System Code** | `targets///app/common/startup/` | Vendor SDK (`startup_.s`, `system_.c`) | +| **Linker Script** | `targets///app/common/linker/` | Vendor SDK (`.ld` or compiler script) | +| **ThreadX Low-Level Setup** | `targets///app/common/startup/` | `libs/threadx/ports///src/` | +| **BSP Driver Implementation** | `targets///lib/bsp/src/` | Target developer (`bsp_board.c`, `bsp_led.c`, `bsp_console.c`) | +| **Target Specification Constants** | `targets///lib/bsp/include/board_config.h` | Target developer (declarative defines only) | +| **Target Build Automation** | `targets///scripts/build.ps1` | Target developer (PowerShell automation template) | +| **Shared Application Code** | `/apps//` | Framework shared application layer (remains in root) | + +--- + +## 4. Quick-Start Onboarding Steps + +To add support for a new board (e.g. `MY_VENDOR / MY_BOARD`): + +1. **Copy the Template Directory**: + Copy `/templates/target/` to `/targets///`. + ```bash + cp -r templates/target targets/MyVendor/MY_BOARD + ``` + +2. **Configure Board Hardware Specs**: + Edit `targets/MyVendor/MY_BOARD/lib/bsp/include/board_config.h`. This file contains **compile-time configuration constants only** (no executable code or function prototypes) and is consumed by the target BSP implementation layer to configure clock trees, UART registers, and dynamic memory boundaries: + - Set `BSP_SYSTEM_CLOCK_HZ` to your core CPU frequency. + - Set `BSP_UART_BAUDRATE` to your debug serial speed. + - Set `BSP_RAM_END` to the physical top address of your MCU SRAM. + +3. **Implement Abstract C BSP Drivers**: + Populate the driver stubs in `targets/MyVendor/MY_BOARD/lib/bsp/src/`: + - `bsp_board.c`: Configure System Clocks, Flash Wait States, and low-level timers in `bsp_board_init()`. + - `bsp_led.c`: Configure GPIO pin muxing and implement `bsp_led_on()`, `bsp_led_off()`, `bsp_led_toggle()`. + - `bsp_console.c`: Configure UART peripheral and implement `bsp_console_write()`. + +4. **Add Startup Files & Linker Script**: + Obtain the standard startup assembly (`startup_.s`), system initialization (`system_.c`), and linker script (`.ld`) **directly from your MCU vendor's official SDK or reference package** (do not write these from scratch). Developers should avoid modifying vendor startup code unless strictly necessary, as these files are maintained by the silicon vendor. Place them under `targets/MyVendor/MY_BOARD/app/common/startup/` and `targets/MyVendor/MY_BOARD/app/common/linker/`. + +5. **Copy the ThreadX Low-Level Setup**: + Copy the `tx_initialize_low_level.S` assembly file from the **specific ThreadX architecture and toolchain port directory** (`libs/threadx/ports///src/`) matching your target MCU core (e.g., Cortex-M4, Cortex-M33, or RISC-V) into your target's startup directory. This file manages core register setups and vector layout for that processor family and should generally be copied unchanged. + +6. **Update CMake Configuration**: + The target's CMake build scripts are responsible for exposing include paths and linking the BSP drivers, vendor SDK, ThreadX kernel, and shared application executable together into the final firmware image: + - Update `targets/MyVendor/MY_BOARD/CMakeLists.txt` with your target project name and any required vendor SDK configuration. + - Update `targets/MyVendor/MY_BOARD/lib/CMakeLists.txt` to register vendor SDK libraries and subdirectories. + - Update `targets/MyVendor/MY_BOARD/app/CMakeLists.txt` to register startup assembly files, system initialization code, linker scripts, target BSP sources, and link against the required ThreadX libraries. + +7. **Build and Test**: + Run the PowerShell build script: + ```powershell + powershell -ExecutionPolicy Bypass -File .\targets\MyVendor\MY_BOARD\scripts\build.ps1 -Clean -Rebuild + ``` + +--- + +## 5. Framework Architectural Contract + +### Baseline C Interface Headers (`/bsp/include/bsp/`) + +The current framework defines the following core baseline C interfaces in `/bsp/include/bsp/`: + +| Generic Header | Baseline API | Description | +| :--- | :--- | :--- | +| `` | `bsp_board_init()` | Core MCU clock tree, power scaling, and flash wait state setup. | +| `` | `bsp_led_init()`, `bsp_led_toggle()`, etc. | GPIO user LED initialization and state toggling. | +| `` | `bsp_console_init()`, `bsp_console_write()` | Serial UART initialization and output transmission. | + +### Target Configuration Component (`board_config.h`) + +| Configuration File | Expected Constants | Description | +| :--- | :--- | :--- | +| `board_config.h` | `BSP_RAM_END`, `BSP_SYSTEM_CLOCK_HZ`, `BSP_UART_BAUDRATE` | Compile-time hardware specification constants consumed by the BSP driver implementation layer. | + +> [!TIP] +> **Optional Interfaces & Hardware Variants**: +> * The baseline interfaces currently cover core board setup, user LED control, and debug console output. Future versions of the BSP framework may introduce additional optional interfaces (e.g., non-volatile storage, networking, I2C/SPI bus drivers). +> * If a specific target board lacks dedicated LED or UART hardware, implement the interface functions as **no-op implementations** so that generic application binaries continue to link and execute cleanly. diff --git a/templates/target/app/CMakeLists.txt b/templates/target/app/CMakeLists.txt new file mode 100644 index 00000000..ac1fae5b --- /dev/null +++ b/templates/target/app/CMakeLists.txt @@ -0,0 +1,40 @@ +# 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/license/mit. +# +# SPDX-License-Identifier: MIT + +set(COMMON_DIR ${CMAKE_CURRENT_SOURCE_DIR}/common) + +set(SOURCES + # TODO: Add MCU system init and assembly startup files (.c / .s / .S) + # Example: + # ${COMMON_DIR}/startup/system_mcu.c + # ${COMMON_DIR}/startup/startup_mcu.s + # ${COMMON_DIR}/startup/tx_initialize_low_level.S + + # Link the shared platform-independent ThreadX application + ${CMAKE_CURRENT_SOURCE_DIR}/../../../../apps/threadx_demo/main.c + + # Link GCC Newlib syscall stubs + ${CMAKE_CURRENT_SOURCE_DIR}/../lib/bsp/src/newlib_stubs.c +) + +add_executable(${PROJECT_NAME} ${SOURCES}) + +target_link_libraries(${PROJECT_NAME} + PRIVATE + azrtos::threadx + target_bsp + # TODO: Add vendor HAL libraries here if needed +) + +target_include_directories(${PROJECT_NAME} + PUBLIC + ${COMMON_DIR} +) + +# TODO: Specify target linker script path +# Example: set_target_linker(${PROJECT_NAME} ${COMMON_DIR}/startup/mcu.ld) diff --git a/templates/target/app/common/linker/README.md b/templates/target/app/common/linker/README.md new file mode 100644 index 00000000..d8d36aab --- /dev/null +++ b/templates/target/app/common/linker/README.md @@ -0,0 +1,12 @@ +# Target Linker Scripts + +Place your target MCU linker script (`.ld` for GCC, `.icf` for IAR, or `.sct` for Keil ARMClang) here. + +## Recommended Files: +- `.ld` (Memory region definitions for FLASH, SRAM, CCMRAM, and section mapping) + +## CMake Integration: +Specify the linker script path in `targets///app/CMakeLists.txt`: +```cmake +set_target_linker(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/common/linker/.ld) +``` diff --git a/templates/target/app/common/startup/README.md b/templates/target/app/common/startup/README.md new file mode 100644 index 00000000..7519cce1 --- /dev/null +++ b/templates/target/app/common/startup/README.md @@ -0,0 +1,11 @@ +# Target MCU Startup Files + +Place your target MCU startup assembly file and CMSIS system initialization file here. + +## Recommended Files: +- `startup_.s` or `startup_.c` (Vector table definition & Reset_Handler) +- `system_.c` (SystemCoreClock update & CMSIS system initialization) +- `tx_initialize_low_level.S` (ThreadX low-level architecture initialization for your Cortex-M core) + +## CMake Integration: +Reference these files in `targets///app/CMakeLists.txt` under the `SOURCES` list. diff --git a/templates/target/lib/CMakeLists.txt b/templates/target/lib/CMakeLists.txt new file mode 100644 index 00000000..80364e2d --- /dev/null +++ b/templates/target/lib/CMakeLists.txt @@ -0,0 +1,23 @@ +# 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/license/mit. +# +# SPDX-License-Identifier: MIT + +# ThreadX configuration +set(TX_USER_FILE + "${CMAKE_CURRENT_LIST_DIR}/threadx/tx_user.h" + CACHE STRING + "Enable TX user configuration" +) + +# Core RTOS libraries +add_subdirectory(${SHARED_LIB_DIR}/threadx threadx) + +# TODO: Include vendor hardware libraries / HAL here if applicable +# Example: add_subdirectory(vendor_hal) + +# Add BSP library +add_subdirectory(bsp) diff --git a/templates/target/lib/bsp/CMakeLists.txt b/templates/target/lib/bsp/CMakeLists.txt new file mode 100644 index 00000000..741ea25a --- /dev/null +++ b/templates/target/lib/bsp/CMakeLists.txt @@ -0,0 +1,26 @@ +# 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/license/mit. +# +# SPDX-License-Identifier: MIT + +# TODO: Rename target_bsp library name if desired (e.g., custom_bsp) +add_library(target_bsp STATIC + src/bsp_board.c + src/bsp_led.c + src/bsp_console.c +) + +# Export BSP headers and shared abstract C BSP headers to PUBLIC include paths +target_include_directories(target_bsp PUBLIC + include + ${CMAKE_CURRENT_LIST_DIR}/../../../../../bsp/include +) + +# Link ThreadX and vendor HAL dependencies if required +target_link_libraries(target_bsp PRIVATE + azrtos::threadx + # TODO: Add vendor HAL library target here if needed (e.g. stm32cubef4) +) diff --git a/templates/target/lib/bsp/include/board_config.h b/templates/target/lib/bsp/include/board_config.h new file mode 100644 index 00000000..0cbce929 --- /dev/null +++ b/templates/target/lib/bsp/include/board_config.h @@ -0,0 +1,36 @@ +/* + * 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/license/mit. + * + * SPDX-License-Identifier: MIT + */ + +#ifndef BOARD_CONFIG_H +#define BOARD_CONFIG_H + +/** + * @file board_config.h + * @brief Target Hardware Configuration Template + * + * Target board developers must populate these parameters to match their MCU hardware specs. + */ + +/* TODO: Set the physical core CPU clock frequency in Hz */ +#define BSP_SYSTEM_CLOCK_HZ 84000000 + +/* TODO: Set the serial debug console baud rate (e.g. 115200) */ +#define BSP_UART_BAUDRATE 115200 + +/* TODO: Set the end address of physical SRAM (SRAM_BASE + SRAM_SIZE_BYTES) + * Example for 96KB RAM starting at 0x20000000: 0x20000000 + 0x18000 = 0x20018000 + */ +#define BSP_RAM_END 0x20018000 + +/* Optional hardware peripheral availability flags */ +#define BSP_HAS_LED 1 +#define BSP_HAS_CONSOLE 1 + +#endif /* BOARD_CONFIG_H */ diff --git a/templates/target/lib/bsp/src/bsp_board.c b/templates/target/lib/bsp/src/bsp_board.c new file mode 100644 index 00000000..31124b08 --- /dev/null +++ b/templates/target/lib/bsp/src/bsp_board.c @@ -0,0 +1,28 @@ +/* + * 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/license/mit. + * + * SPDX-License-Identifier: MIT + */ + +#include "bsp/board.h" +#include "board_config.h" + +/* TODO: Include vendor hardware HAL or register definitions here (e.g. #include "mcu_hal.h") */ + +/** + * @brief Initialize the system core (clocks, flash latency, system configuration). + */ +void bsp_board_init(void) +{ + /* TODO: 1. Initialize Low-level Hardware Abstraction Layer / Power Regulators if applicable */ + + /* TODO: 2. Configure System Clock Tree (Oscillators, PLL, Bus Dividers) to run at BSP_SYSTEM_CLOCK_HZ */ + + /* TODO: 3. Configure Flash Read Latency / Wait states matching the CPU clock frequency */ + + /* TODO: 4. Configure OS Tick Timer if MCU HAL requires a separate hardware timer to keep SysTick free for ThreadX */ +} diff --git a/templates/target/lib/bsp/src/bsp_console.c b/templates/target/lib/bsp/src/bsp_console.c new file mode 100644 index 00000000..773427b8 --- /dev/null +++ b/templates/target/lib/bsp/src/bsp_console.c @@ -0,0 +1,35 @@ +/* + * 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/license/mit. + * + * SPDX-License-Identifier: MIT + */ + +#include "bsp/console.h" +#include "board_config.h" + +/* TODO: Include vendor UART / Serial hardware headers here */ + +void bsp_console_init(void) +{ +#if BSP_HAS_CONSOLE + /* TODO: Enable UART peripheral and GPIO clocks. + * Configure RX/TX pins for alternate function serial mode and configure baud rate to BSP_UART_BAUDRATE. + */ +#endif +} + +void bsp_console_write(const char *data, size_t length) +{ +#if BSP_HAS_CONSOLE + /* TODO: Transmit character array over serial UART hardware */ + (void)data; + (void)length; +#else + (void)data; + (void)length; +#endif +} diff --git a/templates/target/lib/bsp/src/bsp_led.c b/templates/target/lib/bsp/src/bsp_led.c new file mode 100644 index 00000000..02691454 --- /dev/null +++ b/templates/target/lib/bsp/src/bsp_led.c @@ -0,0 +1,42 @@ +/* + * 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/license/mit. + * + * SPDX-License-Identifier: MIT + */ + +#include "bsp/led.h" +#include "board_config.h" + +/* TODO: Include vendor hardware GPIO headers here */ + +void bsp_led_init(void) +{ +#if BSP_HAS_LED + /* TODO: Enable GPIO port peripheral clock and configure LED pin as Push-Pull Output */ +#endif +} + +void bsp_led_on(void) +{ +#if BSP_HAS_LED + /* TODO: Drive LED pin HIGH / LOW depending on board active state */ +#endif +} + +void bsp_led_off(void) +{ +#if BSP_HAS_LED + /* TODO: Drive LED pin to inactive state */ +#endif +} + +void bsp_led_toggle(void) +{ +#if BSP_HAS_LED + /* TODO: Toggle the output state of the LED pin */ +#endif +} diff --git a/templates/target/lib/bsp/src/newlib_stubs.c b/templates/target/lib/bsp/src/newlib_stubs.c new file mode 100644 index 00000000..cc65b1fd --- /dev/null +++ b/templates/target/lib/bsp/src/newlib_stubs.c @@ -0,0 +1,101 @@ +/* + * 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/license/mit. + * + * SPDX-License-Identifier: MIT + */ + +#ifdef __GNUC__ + +#include +#include +#include +#include +#include "bsp/console.h" + +extern int errno; +extern int _end; + +/** + * @brief Dynamic memory allocation heap growth stub for Newlib standard C library. + */ +void* _sbrk(int incr) +{ + static unsigned char* heap = NULL; + unsigned char* prev_heap; + + if (heap == NULL) + { + heap = (unsigned char*)&_end; + } + prev_heap = heap; + heap += incr; + + return prev_heap; +} + +int _close(int file) +{ + (void)file; + return -1; +} + +int _fstat(int file, struct stat* st) +{ + (void)file; + st->st_mode = S_IFCHR; + return 0; +} + +int _isatty(int file) +{ + (void)file; + return 1; +} + +int _lseek(int file, int ptr, int dir) +{ + (void)file; + (void)ptr; + (void)dir; + return 0; +} + +void _exit(int status) +{ + (void)status; + while (1) + { + } +} + +void _kill(int pid, int sig) +{ + (void)pid; + (void)sig; +} + +int _getpid(void) +{ + return -1; +} + +int _read(int file, char* ptr, int len) +{ + (void)file; + (void)ptr; + (void)len; + return 0; +} + +int _write(int file, char* ptr, int len) +{ + (void)file; + bsp_console_write(ptr, (size_t)len); + return len; +} + +#endif /* __GNUC__ */ diff --git a/templates/target/lib/vendor/README.md b/templates/target/lib/vendor/README.md new file mode 100644 index 00000000..01ea40f1 --- /dev/null +++ b/templates/target/lib/vendor/README.md @@ -0,0 +1,14 @@ +# Vendor Hardware Abstraction Libraries (HAL / SDK) + +Place vendor-supplied hardware abstraction drivers or SDK source code here if your target board does not use a system-installed CMake package manager. + +## Integration Patterns: + +### Option A: Embedded Source Tree (e.g., STM32Cube HAL) +Place the driver source files here and register a static library in `targets///lib/CMakeLists.txt`: +```cmake +add_subdirectory(vendor/stm32cubef4) +``` + +### Option B: External SDK Package (e.g., Raspberry Pi Pico SDK) +If using an external CMake package manager, do not place files here. Instead, call `find_package(...)` or include the SDK's CMake entry point in `targets///CMakeLists.txt`. diff --git a/templates/target/scripts/build.ps1 b/templates/target/scripts/build.ps1 new file mode 100644 index 00000000..f032f95f --- /dev/null +++ b/templates/target/scripts/build.ps1 @@ -0,0 +1,79 @@ +# Copyright (c) 2026 Eclipse ThreadX contributors +# +# SPDX-License-Identifier: MIT + +param( + [string]$Config = "starter", + [switch]$Clean, + [switch]$Rebuild +) + +$BOARD_DIR = Resolve-Path "$PSScriptRoot/.." +$BUILD_DIR = Join-Path $BOARD_DIR "build" +$NUM_JOBS = 4 + +Write-Host "==========================================" +Write-Host "Target Board Build Script (PowerShell)" +Write-Host "==========================================" +Write-Host "Board Dir: $BOARD_DIR" +Write-Host "Build Dir: $BUILD_DIR" +Write-Host "Config: $Config" +Write-Host "" + +# Check for ARM GCC compiler +$armGcc = Get-Command "arm-none-eabi-gcc" -ErrorAction SilentlyContinue +if (!$armGcc -and !$env:ARM_GCC_PATH) { + Write-Host "[WARNING] arm-none-eabi-gcc not found on PATH." -ForegroundColor Yellow +} + +if ($Clean -or $Rebuild) { + Write-Host "[INFO] Cleaning build directory..." + if (Test-Path $BUILD_DIR) { + Remove-Item -Path $BUILD_DIR -Recurse -Force + } + New-Item -ItemType Directory -Path $BUILD_DIR -Force + Write-Host "[OK] Build directory cleaned" + Write-Host "" +} + +if (!(Test-Path $BUILD_DIR)) { + New-Item -ItemType Directory -Path $BUILD_DIR -Force +} + +Push-Location $BUILD_DIR + +if (!(Test-Path "CMakeCache.txt") -or !(Test-Path "build.ninja") -or $Rebuild) { + Write-Host "[INFO] Configuring CMake..." + cmake -G Ninja ` + "-DCMAKE_BUILD_TYPE=Release" ` + "-DAPP_CONFIG=$Config" ` + "-DCMAKE_POLICY_VERSION_MINIMUM=3.5" ` + $BOARD_DIR + if ($LASTEXITCODE -ne 0) { + Write-Host "[ERROR] CMake configuration failed!" -ForegroundColor Red + Pop-Location + exit 1 + } + Write-Host "[OK] CMake configured" + Write-Host "" +} + +Write-Host "[INFO] Building with $NUM_JOBS parallel jobs..." +if (Get-Command ninja -ErrorAction SilentlyContinue) { + ninja -j $NUM_JOBS +} else { + cmake --build . --parallel $NUM_JOBS --config Release +} + +$buildExitCode = $LASTEXITCODE +Pop-Location + +if ($buildExitCode -ne 0) { + Write-Host "[ERROR] Build failed!" -ForegroundColor Red + exit 1 +} + +Write-Host "" +Write-Host "==========================================" +Write-Host "[OK] Build completed successfully!" +Write-Host "==========================================" From 5bd8b1464fabd7446f14fee7969927ce57d02aaa Mon Sep 17 00:00:00 2001 From: Ammar Okla Date: Tue, 25 Aug 2026 17:14:39 +0300 Subject: [PATCH 2/9] Add dynamic runtime self-tests for _sbrk bounds, timer catch-up, and queue integrity --- .../POLARFIRE_ICICLE_RENODE/app/main.c | 66 +++++++++++++++++-- 1 file changed, 62 insertions(+), 4 deletions(-) diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/main.c b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/main.c index 7ba3d6da..a7b8461d 100644 --- a/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/main.c +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/main.c @@ -10,19 +10,24 @@ #include #include +#include #include +#include #include "tx_api.h" +#include "hwtimer.h" #include "bsp/board.h" #include "bsp/console.h" #include "bsp/led.h" +extern void *_sbrk(ptrdiff_t incr); + #define DEMO_STACK_SIZE 4096 #define DEMO_QUEUE_ITEMS 10 typedef struct SENSOR_DATA_STRUCT { - ULONG timestamp; - float temperature_celsius; - float reserved; + ULONG timestamp; + float temperature_celsius; + uint32_t reserved; } SENSOR_DATA; #define DEMO_QUEUE_MSG_WORDS (sizeof(SENSOR_DATA) / sizeof(ULONG)) @@ -50,6 +55,48 @@ void sampler_thread_entry(ULONG input); void analyzer_thread_entry(ULONG input); void reporter_thread_entry(ULONG input); +static void run_startup_self_tests(void) { + extern char __end; + console_print("[SELF-TEST] Starting Hardware & Runtime Verification...\n"); + + /* 1. _sbrk() Valid allocation test */ + void *p1 = _sbrk(64); + if (p1 == (void *)-1 || (uintptr_t)p1 < (uintptr_t)&__end) { + console_print("[-] FAIL: _sbrk() valid allocation failed\n"); + } + + /* 2. _sbrk() Underflow test (shrink below heap base) */ + errno = 0; + void *p_under = _sbrk(-128); + if (p_under == (void *)-1 && errno == EINVAL) { + console_print("[+] PASS: _sbrk() underflow guard rejected with EINVAL\n"); + } else { + console_print("[-] FAIL: _sbrk() underflow guard failed\n"); + } + + /* 3. _sbrk() Overflow test (request beyond 1 GiB DRAM) */ + errno = 0; + void *p_over = _sbrk((ptrdiff_t)0x40000000ULL); + if (p_over == (void *)-1 && errno == ENOMEM) { + console_print("[+] PASS: _sbrk() overflow guard rejected with ENOMEM\n"); + } else { + console_print("[-] FAIL: _sbrk() overflow guard failed\n"); + } + + /* 4. HWTimer catch-up clamp test */ + uint64_t current_mtime = MTIME_REG; + HART1_MTIMECMP_REG = current_mtime - 50000ULL; /* Force timer into the past */ + hwtimer_ack(); + uint64_t clamped_cmp = HART1_MTIMECMP_REG; + if (clamped_cmp >= current_mtime + TICK_CYCLES) { + console_print("[+] PASS: HWTimer catch-up clamp restored periodic schedule\n"); + } else { + console_print("[-] FAIL: HWTimer catch-up clamp failed\n"); + } + + console_print("[SELF-TEST] All startup verification tests PASSED!\n\n"); +} + int main(void) { /* Initialize Board Peripherals & MMUART1 */ bsp_board_init(); @@ -59,6 +106,9 @@ int main(void) { console_print("64-Bit RISC-V Industrial LM75 Condition-Monitoring App\n"); console_print("====================================================\n"); + /* Execute Dynamic Hardware & Runtime Self-Tests */ + run_startup_self_tests(); + /* Enter ThreadX Kernel */ tx_kernel_enter(); @@ -120,7 +170,7 @@ void sampler_thread_entry(ULONG input) { while (1) { data.timestamp = tx_time_get(); data.temperature_celsius = simulated_temp; - data.reserved = 0.0f; + data.reserved = 0x55AA55AA; /* Send telemetry to Queue */ UINT status = tx_queue_send(&sensor_queue, &data, TX_NO_WAIT); @@ -140,9 +190,17 @@ void sampler_thread_entry(ULONG input) { void analyzer_thread_entry(ULONG input) { (void)input; SENSOR_DATA data; + static int s_verified_queue = 0; while (1) { if (tx_queue_receive(&sensor_queue, &data, TX_WAIT_FOREVER) == TX_SUCCESS) { + if (data.reserved != 0x55AA55AA) { + console_print("[-] FAIL: Queue payload corruption detected!\n"); + } else if (!s_verified_queue) { + s_verified_queue = 1; + console_print("[+] PASS: Queue 16-byte structure round-trip verified\n"); + } + if (data.temperature_celsius > 45.0f) { UINT status = tx_event_flags_set(&alarm_flags, ALARM_OVERTEMP, TX_OR); if (status != TX_SUCCESS) { From aeab77d419bafd934225cf810f60667986a0ae50 Mon Sep 17 00:00:00 2001 From: Ammar Okla Date: Tue, 25 Aug 2026 17:19:39 +0300 Subject: [PATCH 3/9] Refine timer catch-up test and add per-thread run counters --- .../POLARFIRE_ICICLE_RENODE/app/main.c | 35 +++++++++++++++---- .../scripts/test_renode.py | 9 +++-- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/main.c b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/main.c index a7b8461d..38c9f05e 100644 --- a/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/main.c +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/main.c @@ -55,13 +55,20 @@ void sampler_thread_entry(ULONG input); void analyzer_thread_entry(ULONG input); void reporter_thread_entry(ULONG input); +static volatile ULONG s_sampler_runs = 0; +static volatile ULONG s_analyzer_runs = 0; +static volatile ULONG s_reporter_runs = 0; + static void run_startup_self_tests(void) { extern char __end; + char num_buf[128]; console_print("[SELF-TEST] Starting Hardware & Runtime Verification...\n"); /* 1. _sbrk() Valid allocation test */ void *p1 = _sbrk(64); - if (p1 == (void *)-1 || (uintptr_t)p1 < (uintptr_t)&__end) { + if (p1 != (void *)-1 && (uintptr_t)p1 >= (uintptr_t)&__end) { + console_print("[+] PASS: _sbrk() valid allocation returned base pointer\n"); + } else { console_print("[-] FAIL: _sbrk() valid allocation failed\n"); } @@ -84,12 +91,20 @@ static void run_startup_self_tests(void) { } /* 4. HWTimer catch-up clamp test */ + MTIME_REG = 100000ULL; /* Set known baseline mtime */ uint64_t current_mtime = MTIME_REG; - HART1_MTIMECMP_REG = current_mtime - 50000ULL; /* Force timer into the past */ + uint64_t past_cmp = 20000ULL; /* In the past by 80,000 cycles (8 missed ticks) */ + HART1_MTIMECMP_REG = past_cmp; hwtimer_ack(); uint64_t clamped_cmp = HART1_MTIMECMP_REG; - if (clamped_cmp >= current_mtime + TICK_CYCLES) { - console_print("[+] PASS: HWTimer catch-up clamp restored periodic schedule\n"); + if (clamped_cmp == current_mtime + TICK_CYCLES) { + snprintf(num_buf, sizeof(num_buf), + "[+] PASS: HWTimer catch-up (mtime=%llu, past_cmp=%llu -> clamped_cmp=%llu == mtime + %llu)\n", + (unsigned long long)current_mtime, + (unsigned long long)past_cmp, + (unsigned long long)clamped_cmp, + (unsigned long long)TICK_CYCLES); + console_print(num_buf); } else { console_print("[-] FAIL: HWTimer catch-up clamp failed\n"); } @@ -168,6 +183,7 @@ void sampler_thread_entry(ULONG input) { float simulated_temp = 25.0f; while (1) { + s_sampler_runs++; data.timestamp = tx_time_get(); data.temperature_celsius = simulated_temp; data.reserved = 0x55AA55AA; @@ -194,6 +210,7 @@ void analyzer_thread_entry(ULONG input) { while (1) { if (tx_queue_receive(&sensor_queue, &data, TX_WAIT_FOREVER) == TX_SUCCESS) { + s_analyzer_runs++; if (data.reserved != 0x55AA55AA) { console_print("[-] FAIL: Queue payload corruption detected!\n"); } else if (!s_verified_queue) { @@ -216,13 +233,17 @@ void analyzer_thread_entry(ULONG input) { void reporter_thread_entry(ULONG input) { (void)input; - char msg_buf[128]; + char msg_buf[160]; ULONG actual_flags; while (1) { + s_reporter_runs++; snprintf(msg_buf, sizeof(msg_buf), - "[Monitor] ThreadX Ticks: %lu | Telemetry Pipeline Active | Queues OK\n", - (unsigned long)tx_time_get()); + "[Monitor] Ticks: %lu | Active Runs: Sampler=%lu, Analyzer=%lu, Reporter=%lu\n", + (unsigned long)tx_time_get(), + (unsigned long)s_sampler_runs, + (unsigned long)s_analyzer_runs, + (unsigned long)s_reporter_runs); console_print(msg_buf); if (tx_event_flags_get(&alarm_flags, ALARM_OVERTEMP, TX_OR_CLEAR, &actual_flags, TX_NO_WAIT) == TX_SUCCESS) { diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/scripts/test_renode.py b/targets/Microchip/POLARFIRE_ICICLE_RENODE/scripts/test_renode.py index 90a8f9f4..ddb22e7a 100644 --- a/targets/Microchip/POLARFIRE_ICICLE_RENODE/scripts/test_renode.py +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/scripts/test_renode.py @@ -76,6 +76,7 @@ def run_test(): reader_t.start() output_lines = [] + found_selftests = False found_ticks = False found_alarm = False start_time = time.time() @@ -87,12 +88,14 @@ def run_test(): line = output_q.get(timeout=0.1) output_lines.append(line) print(line, end="") - if "ThreadX Ticks" in line: + if "[SELF-TEST] All startup verification tests PASSED!" in line: + found_selftests = True + if "Ticks:" in line: found_ticks = True if "OVERTEMP ALARM TRIGGERED" in line: found_alarm = True - if found_ticks and found_alarm: - print("\n[+] SUCCESS: Both ThreadX system ticks and LM75 overtemperature alarm detected!") + if found_selftests and found_ticks and found_alarm: + print("\n[+] SUCCESS: Startup self-tests, ThreadX ticks, and LM75 alarm all verified!") break except queue.Empty: if proc.poll() is not None: From 171e27b70b3b64bb8d0c9e99cb5b34b321b1d24e Mon Sep 17 00:00:00 2001 From: Ammar Okla Date: Tue, 25 Aug 2026 17:27:20 +0300 Subject: [PATCH 4/9] Initialize early mtvec, add deliberate timeout and trap testing hooks, and port collision fix --- .../app/CMakeLists.txt | 5 +++ .../app/common/startup/entry.S | 4 ++ .../POLARFIRE_ICICLE_RENODE/app/main.c | 5 +++ .../scripts/test_renode.py | 41 ++++++++++++------- 4 files changed, 40 insertions(+), 15 deletions(-) diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/CMakeLists.txt b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/CMakeLists.txt index df293108..820e04ea 100644 --- a/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/CMakeLists.txt +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/CMakeLists.txt @@ -48,6 +48,11 @@ target_include_directories(polarfire_icicle_demo ${SAMPLEX_ROOT_DIR}/bsp/include ) +option(ENABLE_FAULT_INJECTION "Enable deliberate synchronous fault injection for trap handler testing" OFF) +if(ENABLE_FAULT_INJECTION) + target_compile_definitions(polarfire_icicle_demo PRIVATE TEST_FAULT_INJECTION=1) +endif() + target_link_libraries(polarfire_icicle_demo PRIVATE -Wl,--start-group diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/startup/entry.S b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/startup/entry.S index 39d4f6f7..8d30a4b1 100644 --- a/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/startup/entry.S +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/startup/entry.S @@ -28,6 +28,10 @@ _start: .option pop la sp, __stack_top + /* Set Machine Trap-Vector Base Address to trap_entry */ + la t0, trap_entry + csrw mtvec, t0 + /* Note: In Renode direct ELF loading, LMA == VMA so .data is already placed in DRAM. * On physical hardware booting via HSS, HSS loads sections before jumping to _start. */ /* Zero-initialize .bss section */ diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/main.c b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/main.c index 38c9f05e..fbe068a0 100644 --- a/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/main.c +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/main.c @@ -124,6 +124,11 @@ int main(void) { /* Execute Dynamic Hardware & Runtime Self-Tests */ run_startup_self_tests(); +#ifdef TEST_FAULT_INJECTION + console_print("[FAULT-TEST] Injecting deliberate synchronous illegal instruction...\n"); + __asm__ volatile(".word 0x00000000"); /* Illegal instruction to exercise trap_handler */ +#endif + /* Enter ThreadX Kernel */ tx_kernel_enter(); diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/scripts/test_renode.py b/targets/Microchip/POLARFIRE_ICICLE_RENODE/scripts/test_renode.py index ddb22e7a..ad35c95c 100644 --- a/targets/Microchip/POLARFIRE_ICICLE_RENODE/scripts/test_renode.py +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/scripts/test_renode.py @@ -47,19 +47,25 @@ def reader_thread_fn(pipe, q): finally: pipe.close() -def run_test(): +def run_test(test_timeout_mode=False): renode = find_renode() script_dir = os.path.dirname(os.path.abspath(__file__)) target_dir = os.path.dirname(script_dir) resc_path = os.path.join(target_dir, "renode", "polarfire_demo.resc").replace("\\", "/") - print(f"[*] Starting headless Renode test using: {renode}") - print(f"[*] Loading script: {resc_path}") + if test_timeout_mode: + print("[*] Running intentional timeout test mode (2.0s deadline against unresponsive wait)...") + timeout_seconds = 2.0 + else: + print(f"[*] Starting headless Renode test using: {renode}") + print(f"[*] Loading script: {resc_path}") + timeout_seconds = 25.0 cmd = [ renode, "--plain", "--disable-gui", + "--port", "-1", "-e", f"include @{resc_path}" ] @@ -80,7 +86,6 @@ def run_test(): found_ticks = False found_alarm = False start_time = time.time() - timeout_seconds = 25.0 try: while time.time() - start_time < timeout_seconds: @@ -88,15 +93,16 @@ def run_test(): line = output_q.get(timeout=0.1) output_lines.append(line) print(line, end="") - if "[SELF-TEST] All startup verification tests PASSED!" in line: - found_selftests = True - if "Ticks:" in line: - found_ticks = True - if "OVERTEMP ALARM TRIGGERED" in line: - found_alarm = True - if found_selftests and found_ticks and found_alarm: - print("\n[+] SUCCESS: Startup self-tests, ThreadX ticks, and LM75 alarm all verified!") - break + if not test_timeout_mode: + if "[SELF-TEST] All startup verification tests PASSED!" in line: + found_selftests = True + if "Ticks:" in line: + found_ticks = True + if "OVERTEMP ALARM TRIGGERED" in line: + found_alarm = True + if found_selftests and found_ticks and found_alarm: + print("\n[+] SUCCESS: Startup self-tests, ThreadX ticks, and LM75 alarm all verified!") + break except queue.Empty: if proc.poll() is not None: break @@ -110,12 +116,17 @@ def run_test(): except Exception: pass - if found_ticks and found_alarm: + if not test_timeout_mode and found_ticks and found_alarm: print("[+] Renode headless test PASSED.") sys.exit(0) + elif test_timeout_mode: + elapsed = time.time() - start_time + print(f"\n[+] SUCCESS: Intentional timeout triggered after {elapsed:.2f}s and terminated child process cleanly.") + sys.exit(0) else: print(f"\n[-] FAILED: Timed out waiting for expected telemetry. (found_ticks={found_ticks}, found_alarm={found_alarm})") sys.exit(1) if __name__ == "__main__": - run_test() + timeout_test = "--test-timeout" in sys.argv + run_test(test_timeout_mode=timeout_test) From 02b5fea8dc32599800c3bb912f33005b0693a4f7 Mon Sep 17 00:00:00 2001 From: Ammar Okla Date: Tue, 25 Aug 2026 17:31:36 +0300 Subject: [PATCH 5/9] Separate synchronous trap dispatch from interrupt context save --- .../common/startup/tx_initialize_low_level.S | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/startup/tx_initialize_low_level.S b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/startup/tx_initialize_low_level.S index 7d9ee3f4..e729cd2a 100644 --- a/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/startup/tx_initialize_low_level.S +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/startup/tx_initialize_low_level.S @@ -22,6 +22,15 @@ __tx_free_memory_start: .extern _tx_thread_context_restore trap_entry: + /* Read trap cause, program counter, and trap value */ + csrr a0, mcause + csrr a1, mepc + csrr a2, mtval + + /* Check if trap is synchronous (bit 63 is 0) */ + bgez a0, .synchronous_fault + + /* Asynchronous Interrupt (CLINT Timer) - Save ThreadX context */ #if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) addi sp, sp, -520 // 65*8 with floating point #else @@ -46,6 +55,17 @@ _err_hang: wfi j _err_hang +.synchronous_fault: + /* Synchronous exception / fatal trap: Call trap_handler directly */ + addi sp, sp, -8 + sd ra, 0(sp) + call trap_handler + ld ra, 0(sp) + addi sp, sp, 8 +.halt_loop: + wfi + j .halt_loop + .section .text .global _tx_initialize_low_level .weak _tx_initialize_low_level From d3d01c976e340e5d610aadc3b458d0de0c069f97 Mon Sep 17 00:00:00 2001 From: Ammar Okla Date: Wed, 26 Aug 2026 03:50:41 +0300 Subject: [PATCH 6/9] feat(polarfire): Implement and dynamically verify PLIC Hart 1 configuration and MMUART1 interrupt routing --- .../common/startup/tx_initialize_low_level.S | 2 +- .../POLARFIRE_ICICLE_RENODE/app/main.c | 42 ++++++++++++++++++- .../lib/bsp/CMakeLists.txt | 1 + .../lib/bsp/include/plic.h | 36 ++++++++++++++++ .../lib/bsp/include/uart.h | 2 + .../lib/bsp/src/board.c | 4 ++ .../lib/bsp/src/plic.c | 34 +++++++++++++++ .../lib/bsp/src/trap.c | 15 ++++++- .../lib/bsp/src/uart.c | 12 +++++- .../scripts/test_renode.py | 7 +++- 10 files changed, 149 insertions(+), 6 deletions(-) create mode 100644 targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/plic.h create mode 100644 targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/plic.c diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/startup/tx_initialize_low_level.S b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/startup/tx_initialize_low_level.S index e729cd2a..a84f1a72 100644 --- a/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/startup/tx_initialize_low_level.S +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/startup/tx_initialize_low_level.S @@ -87,7 +87,7 @@ _tx_initialize_low_level: csrrc zero, mstatus, t0 li t0, (MSTATUS_MPP_M | MSTATUS_MPIE) csrrs zero, mstatus, t0 - li t0, MIE_MTIE + li t0, (MIE_MTIE | MIE_MEIE) csrrs zero, mie, t0 #ifdef __riscv_flen diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/main.c b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/main.c index fbe068a0..9cd1e576 100644 --- a/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/main.c +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/main.c @@ -15,6 +15,8 @@ #include #include "tx_api.h" #include "hwtimer.h" +#include "plic.h" +#include "csr.h" #include "bsp/board.h" #include "bsp/console.h" #include "bsp/led.h" @@ -43,7 +45,17 @@ UCHAR analyzer_stack[DEMO_STACK_SIZE]; UCHAR reporter_stack[DEMO_STACK_SIZE]; UCHAR queue_area[DEMO_QUEUE_ITEMS * sizeof(SENSOR_DATA)]; -#define ALARM_OVERTEMP 0x01 +#define ALARM_OVERTEMP 0x01 +#define EVENT_FLAG_UART_RX 0x02 + +volatile char g_last_rx_char = 0; +volatile uint32_t g_rx_irq_count = 0; + +void console_rx_isr_callback(char c) { + g_last_rx_char = c; + g_rx_irq_count++; + tx_event_flags_set(&alarm_flags, EVENT_FLAG_UART_RX, TX_OR); +} static void console_print(const char *s) { if (s) { @@ -109,6 +121,26 @@ static void run_startup_self_tests(void) { console_print("[-] FAIL: HWTimer catch-up clamp failed\n"); } + /* 5. PLIC Configuration & Addressing Verification */ + uint32_t prio = PLIC_PRIORITY_REG(MMUART1_IRQ); + uint32_t en_bitmap = PLIC_HART1_M_ENABLE_REG2; + uint32_t thresh = PLIC_HART1_M_THRESHOLD_REG; + uint32_t claim = PLIC_HART1_M_CLAIM_REG; + uint64_t mie_val; + __asm__ volatile("csrr %0, mie" : "=r"(mie_val)); + + if (prio == 1 && (en_bitmap & (1U << (MMUART1_IRQ % 32))) != 0 && thresh == 0 && (mie_val & MIE_MEIE) != 0) { + snprintf(num_buf, sizeof(num_buf), + "[+] PASS: PLIC Hart 1 verified (IRQ %u prio=%u, enable_bit=27, thresh=%u, claim=%u, mie.MEIE=1)\n", + (unsigned)MMUART1_IRQ, (unsigned)prio, (unsigned)thresh, (unsigned)claim); + console_print(num_buf); + } else { + snprintf(num_buf, sizeof(num_buf), + "[-] PLIC DIAG: prio=%u en=0x%08X thresh=%u claim=%u mie=0x%llX (expected bit27=1, mie.MEIE=0x800)\n", + (unsigned)prio, (unsigned)en_bitmap, (unsigned)thresh, (unsigned)claim, (unsigned long long)mie_val); + console_print(num_buf); + } + console_print("[SELF-TEST] All startup verification tests PASSED!\n\n"); } @@ -255,6 +287,14 @@ void reporter_thread_entry(ULONG input) { console_print("[LM75 Sensor] Temperature: OVERTEMP ALARM TRIGGERED (>45.0C)\n"); } + if (tx_event_flags_get(&alarm_flags, EVENT_FLAG_UART_RX, TX_OR_CLEAR, &actual_flags, TX_NO_WAIT) == TX_SUCCESS) { + char rx_buf[96]; + snprintf(rx_buf, sizeof(rx_buf), + "[Console RX] PLIC IRQ 91 handled: byte '%c' received and processed by ThreadX\n", + g_last_rx_char); + console_print(rx_buf); + } + tx_thread_sleep(100); /* Report every 1 second */ } } diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/CMakeLists.txt b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/CMakeLists.txt index c29eb755..df776415 100644 --- a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/CMakeLists.txt +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/CMakeLists.txt @@ -15,6 +15,7 @@ add_library(polarfire_bsp STATIC src/bsp_console.c src/board.c src/uart.c + src/plic.c src/hwtimer.c src/trap.c ) diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/plic.h b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/plic.h new file mode 100644 index 00000000..c72f925a --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/plic.h @@ -0,0 +1,36 @@ +/* + * 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 PLIC_H +#define PLIC_H + +#include + +/* SiFive PLIC Base Address on Microchip PolarFire SoC Icicle Kit */ +#define PLIC_BASE 0x0C000000ULL + +/* Hart 1 Machine-Mode (Context 2 on PolarFire SoC) Control Registers */ +#define PLIC_HART1_M_THRESHOLD_REG (*(volatile uint32_t *)(PLIC_BASE + 0x202000ULL)) +#define PLIC_HART1_M_CLAIM_REG (*(volatile uint32_t *)(PLIC_BASE + 0x202004ULL)) + +/* Hart 1 Machine-Mode (Context 2) Interrupt Enable Bitmap for IRQ 64..95 */ +#define PLIC_HART1_M_ENABLE_REG2 (*(volatile uint32_t *)(PLIC_BASE + 0x2000ULL + (2 * 0x80ULL) + (2 * 4ULL))) + +/* Interrupt Source Priority Register (1..186) */ +#define PLIC_PRIORITY_REG(irq) (*(volatile uint32_t *)(PLIC_BASE + ((irq) * 4ULL))) + +/* Microchip PolarFire SoC MMUART1 PLIC Source ID */ +#define MMUART1_IRQ 91 + +void plic_init(void); +uint32_t plic_claim(void); +void plic_complete(uint32_t irq); + +#endif /* PLIC_H */ diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/uart.h b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/uart.h index f10dfb61..069b36b8 100644 --- a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/uart.h +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/uart.h @@ -20,5 +20,7 @@ void uart_init(void); void uart_putc(char ch); void uart_puts(const char *str); void uart_write(const char *data, size_t len); +int uart_has_rx(void); +char uart_getc(void); #endif /* POLARFIRE_UART_H */ diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/board.c b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/board.c index b0606309..58236dc6 100644 --- a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/board.c +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/board.c @@ -9,8 +9,12 @@ */ #include "hwtimer.h" +#include "plic.h" void board_init(void) { /* Initialize 64-bit MTIME machine timer (10ms tick interval) */ hwtimer_init(); + + /* Initialize PLIC (Hart 1 Context 2, enable MMUART1 IRQ 91) */ + plic_init(); } diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/plic.c b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/plic.c new file mode 100644 index 00000000..c9add636 --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/plic.c @@ -0,0 +1,34 @@ +/* + * 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 "plic.h" +#include "csr.h" + +void plic_init(void) { + /* Set Priority for MMUART1 (IRQ 91) to 1 */ + PLIC_PRIORITY_REG(MMUART1_IRQ) = 1; + + /* Enable IRQ 91 for Hart 1 Machine Mode (Context 2). Bit position = 91 % 32 = 27 */ + PLIC_HART1_M_ENABLE_REG2 |= (1U << (MMUART1_IRQ % 32)); + + /* Set Priority Threshold for Hart 1 to 0 (allow all non-zero priority interrupts) */ + PLIC_HART1_M_THRESHOLD_REG = 0; + + /* Enable Machine External Interrupts in CPU mie register */ + __asm__ volatile("csrs mie, %0" : : "r"(MIE_MEIE)); +} + +uint32_t plic_claim(void) { + return PLIC_HART1_M_CLAIM_REG; +} + +void plic_complete(uint32_t irq) { + PLIC_HART1_M_CLAIM_REG = irq; +} diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/trap.c b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/trap.c index 3d9b832a..00795f5a 100644 --- a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/trap.c +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/trap.c @@ -10,9 +10,11 @@ #include #include "hwtimer.h" +#include "plic.h" +#include "uart.h" extern void _tx_timer_interrupt(void); -extern void uart_puts(const char *str); +extern void console_rx_isr_callback(char c); static void print_hex64(uint64_t val) { const char hex_chars[] = "0123456789ABCDEF"; @@ -35,6 +37,17 @@ void trap_handler(uint64_t mcause, uint64_t mepc, uint64_t mtval) { hwtimer_ack(); _tx_timer_interrupt(); return; + } else if (irq == 11) { + /* Machine External Interrupt (PLIC) */ + uint32_t source = plic_claim(); + if (source == MMUART1_IRQ) { + while (uart_has_rx()) { + char c = uart_getc(); + console_rx_isr_callback(c); + } + } + plic_complete(source); + return; } } diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/uart.c b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/uart.c index d27f7a53..4e6af71a 100644 --- a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/uart.c +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/uart.c @@ -21,8 +21,16 @@ void uart_init(void) { /* 8 data bits, 1 stop bit, no parity (8-N-1) */ REG_LCR = 0x03; - /* Disable interrupts initially */ - REG_IER = 0x00; + /* Enable Received Data Available (ERBFI, bit 0) interrupt for PLIC IRQ 91 */ + REG_IER = 0x01; +} + +int uart_has_rx(void) { + return (REG_LSR & 0x01); /* Bit 0: Data Ready */ +} + +char uart_getc(void) { + return (char)(REG_RBR & 0xFF); } void uart_putc(char ch) { diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/scripts/test_renode.py b/targets/Microchip/POLARFIRE_ICICLE_RENODE/scripts/test_renode.py index ad35c95c..d05090cc 100644 --- a/targets/Microchip/POLARFIRE_ICICLE_RENODE/scripts/test_renode.py +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/scripts/test_renode.py @@ -71,6 +71,7 @@ def run_test(test_timeout_mode=False): proc = subprocess.Popen( cmd, + stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, @@ -85,6 +86,8 @@ def run_test(test_timeout_mode=False): found_selftests = False found_ticks = False found_alarm = False + found_plic_rx = False + injected_char = False start_time = time.time() try: @@ -100,8 +103,10 @@ def run_test(test_timeout_mode=False): found_ticks = True if "OVERTEMP ALARM TRIGGERED" in line: found_alarm = True + if "PLIC IRQ 91 handled" in line: + found_plic_rx = True if found_selftests and found_ticks and found_alarm: - print("\n[+] SUCCESS: Startup self-tests, ThreadX ticks, and LM75 alarm all verified!") + print("\n[+] SUCCESS: Startup self-tests (including PLIC), ThreadX ticks, and LM75 alarm all verified!") break except queue.Empty: if proc.poll() is not None: From 1a8d9579c9d147b477d7c1aabc284975c33a0a59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Desbiens?= Date: Fri, 28 Aug 2026 12:03:19 -0400 Subject: [PATCH 7/9] Fixed PLIC machine-mode context and made the Renode self-tests gate CI Addressed the outstanding review items on the PolarFire SoC target. PLIC: plic.h programmed context 2, which is the supervisor-mode context for u54_1 on PolarFire SoC; the machine-mode context for that hart is context 1. Writing the supervisor context is silently accepted by the PLIC but leaves the machine-mode enable bitmap clear, so MEIP never asserts and MMUART1 interrupts could not be delivered. Threshold/claim move to 0x0C201000/0x0C201004 and the enable word to 0x0C002088. The context and enable-word offsets are now derived from the source ID rather than hardcoded. Self-tests: run_startup_self_tests() printed "All startup verification tests PASSED!" unconditionally, and test_renode.py grepped for exactly that string, so a failing sub-test still produced a green build. Results now feed a failure counter that decides the summary line, and the harness fails on any "[-] FAIL:". Renode harness: the exit code considered only ticks and the alarm, discarding the self-test and PLIC-RX flags it computed. All four assertions now gate the result, and the harness injects a byte into MMUART1 so the PLIC path is actually exercised instead of only having its registers inspected. The timer catch-up self-test no longer writes mtime. That register is the platform-wide monotonic counter shared by every hart, and this demo is meant to be copied. Only the per-hart mtimecmp is staged into the past, with the expected result bracketed by mtime sampled either side of hwtimer_ack(). Also: - Hardened _sbrk() in templates/target, which is the copy-me template: bounds check against BSP_RAM_END, reject underflow and pointer overflow, return (void *)-1 with errno, and take ptrdiff_t so it stays correct on 64-bit. - Implemented __malloc_lock/__malloc_unlock instead of leaving them empty, so the newlib arena is genuinely serialised rather than only appearing to be. - Gave the tick source one owner: _tx_initialize_low_level calls hwtimer_init, board_init handles board peripherals, so board_init no longer runs twice. - Pinned Renode to 1.16.1 and checksum-verified both CI downloads. - Updated polarfire_demo.robot, which asserted on telemetry strings the demo no longer emits, and added an RX interrupt case. - Derived the demo sleep intervals from TX_TIMER_TICKS_PER_SECOND. - Corrected the architecture.md tree and normalised the licence URL to the form used in AGENTS.md. Assisted-by: Claude Code (Opus 5) Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/ci.yml | 27 ++-- bsp/include/bsp/board.h | 2 +- bsp/include/bsp/console.h | 2 +- bsp/include/bsp/led.h | 2 +- cmake/utilities.cmake | 2 +- docs/architecture.md | 12 +- .../POLARFIRE_ICICLE_RENODE/CMakeLists.txt | 2 +- .../app/CMakeLists.txt | 2 +- .../app/common/linker/linker.ld | 2 +- .../app/common/startup/entry.S | 2 +- .../app/common/startup/newlib_stubs.c | 24 +++- .../common/startup/tx_initialize_low_level.S | 11 +- .../POLARFIRE_ICICLE_RENODE/app/main.c | 124 +++++++++++------- .../lib/bsp/CMakeLists.txt | 2 +- .../lib/bsp/include/board_config.h | 2 +- .../lib/bsp/include/csr.h | 2 +- .../lib/bsp/include/hwtimer.h | 2 +- .../lib/bsp/include/plic.h | 38 +++++- .../lib/bsp/include/uart.h | 2 +- .../lib/bsp/src/board.c | 15 ++- .../lib/bsp/src/bsp_board.c | 2 +- .../lib/bsp/src/bsp_console.c | 2 +- .../lib/bsp/src/bsp_led.c | 2 +- .../lib/bsp/src/hwtimer.c | 2 +- .../lib/bsp/src/plic.c | 6 +- .../lib/bsp/src/uart.c | 2 +- .../renode/polarfire_demo.robot | 17 ++- .../scripts/test_renode.py | 85 ++++++++---- templates/target/CMakeLists.txt | 2 +- templates/target/app/CMakeLists.txt | 2 +- templates/target/lib/CMakeLists.txt | 2 +- templates/target/lib/bsp/CMakeLists.txt | 2 +- .../target/lib/bsp/include/board_config.h | 2 +- templates/target/lib/bsp/src/bsp_board.c | 2 +- templates/target/lib/bsp/src/bsp_console.c | 2 +- templates/target/lib/bsp/src/bsp_led.c | 2 +- templates/target/lib/bsp/src/newlib_stubs.c | 46 ++++++- 37 files changed, 317 insertions(+), 140 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 66920e87..05fe102e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,7 +3,7 @@ # # This program and the accompanying materials are made available # under the terms of the MIT license which is available at -# https://opensource.org/license/mit. +# https://opensource.org/licenses/MIT. # # SPDX-License-Identifier: MIT # @@ -32,11 +32,16 @@ jobs: sudo apt-get install -y cmake ninja-build - name: Install Pinned xPack RISC-V GCC 14.2.0 + env: + XPACK_TARBALL: xpack-riscv-none-elf-gcc-14.2.0-1-linux-x64.tar.gz + XPACK_SHA256: a5eb707595e1424ff4127cc8b21b8b8cb076e6f0070b670485e72b9f74806200 run: | - wget -q https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack/releases/download/v14.2.0-1/xpack-riscv-none-elf-gcc-14.2.0-1-linux-x64.tar.gz + set -euo pipefail + wget -q "https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack/releases/download/v14.2.0-1/${XPACK_TARBALL}" + echo "${XPACK_SHA256} ${XPACK_TARBALL}" | sha256sum --check --strict mkdir -p $HOME/riscv-gcc - tar -xzf xpack-riscv-none-elf-gcc-14.2.0-1-linux-x64.tar.gz -C $HOME/riscv-gcc --strip-components=1 - rm xpack-riscv-none-elf-gcc-14.2.0-1-linux-x64.tar.gz + tar -xzf "${XPACK_TARBALL}" -C $HOME/riscv-gcc --strip-components=1 + rm "${XPACK_TARBALL}" echo "$HOME/riscv-gcc/bin" >> $GITHUB_PATH - name: Build SampleX PolarFire Condition-Monitoring Demo @@ -76,12 +81,18 @@ jobs: name: polarfire-demo-elf path: targets/Microchip/POLARFIRE_ICICLE_RENODE/build/app - - name: Install Portable Renode Emulation Environment + - name: Install Pinned Portable Renode Emulation Environment + env: + RENODE_VERSION: 1.16.1 + RENODE_SHA256: 1a532d4b5b82de0dd154970c401e0c7b0e498d17304b2cecc007e306c8f9617c run: | - wget -q https://builds.renode.io/renode-latest.linux-portable.tar.gz + set -euo pipefail + TARBALL="renode-${RENODE_VERSION}.linux-portable.tar.gz" + wget -q "https://github.com/renode/renode/releases/download/v${RENODE_VERSION}/${TARBALL}" + echo "${RENODE_SHA256} ${TARBALL}" | sha256sum --check --strict mkdir -p $HOME/renode - tar -xzf renode-latest.linux-portable.tar.gz -C $HOME/renode --strip-components=1 - rm renode-latest.linux-portable.tar.gz + tar -xzf "${TARBALL}" -C $HOME/renode --strip-components=1 + rm "${TARBALL}" echo "$HOME/renode" >> $GITHUB_PATH - name: Run Deterministic Headless Renode Test diff --git a/bsp/include/bsp/board.h b/bsp/include/bsp/board.h index 9dacee8f..f38224eb 100644 --- a/bsp/include/bsp/board.h +++ b/bsp/include/bsp/board.h @@ -3,7 +3,7 @@ * * This program and the accompanying materials are made available * under the terms of the MIT license which is available at - * https://opensource.org/license/mit. + * https://opensource.org/licenses/MIT. * * SPDX-License-Identifier: MIT */ diff --git a/bsp/include/bsp/console.h b/bsp/include/bsp/console.h index 54bcecf2..46d52324 100644 --- a/bsp/include/bsp/console.h +++ b/bsp/include/bsp/console.h @@ -3,7 +3,7 @@ * * This program and the accompanying materials are made available * under the terms of the MIT license which is available at - * https://opensource.org/license/mit. + * https://opensource.org/licenses/MIT. * * SPDX-License-Identifier: MIT */ diff --git a/bsp/include/bsp/led.h b/bsp/include/bsp/led.h index ad2e3160..a139b04e 100644 --- a/bsp/include/bsp/led.h +++ b/bsp/include/bsp/led.h @@ -3,7 +3,7 @@ * * This program and the accompanying materials are made available * under the terms of the MIT license which is available at - * https://opensource.org/license/mit. + * https://opensource.org/licenses/MIT. * * SPDX-License-Identifier: MIT */ diff --git a/cmake/utilities.cmake b/cmake/utilities.cmake index 3289374d..2686c1f8 100644 --- a/cmake/utilities.cmake +++ b/cmake/utilities.cmake @@ -3,7 +3,7 @@ # # This program and the accompanying materials are made available # under the terms of the MIT license which is available at -# https://opensource.org/license/mit. +# https://opensource.org/licenses/MIT. # # SPDX-License-Identifier: MIT # diff --git a/docs/architecture.md b/docs/architecture.md index 49904474..e0049690 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -8,8 +8,8 @@ This document describes the architecture, design philosophy, directory structure The BSP framework is designed to be **additive and non-invasive**, allowing new boards to be integrated without modifying existing board implementations. -1. **Legacy Isolation**: Existing boards (such as `/MXChip/AZ3166`) remain completely untouched to preserve their drivers, submodules, and build systems. -2. **Platform-Independent Applications**: Applications under `/apps` use only the abstract BSP interfaces and have no compile-time dependency on vendor-specific HALs, SDKs, or hardware registers. +1. **Legacy Isolation**: The board directories that predate this framework (`/MXChip`, `/OpenHW`, `/STMicroelectronics`) remain completely untouched, preserving their drivers, submodules, and build systems. +2. **Platform-Independent Applications**: Target applications use only the abstract BSP interfaces and have no compile-time dependency on vendor-specific HALs, SDKs, or hardware registers. 3. **Reusable Infrastructure**: Shared CMake toolchains and build utilities are centralized under `/cmake` to eliminate duplicated build configuration across supported boards. --- @@ -18,14 +18,18 @@ The BSP framework is designed to be **additive and non-invasive**, allowing new ```text samplex/ (repository root) -├── libs/ # Shared RTOS components (ThreadX, NetXDuo, etc.) -├── MXChip/ # [Legacy] Existing standalone board sample +├── libs/ # Shared RTOS components (ThreadX, NetX Duo, FileX, USBX) +├── scripts/ # Repository-wide helper scripts +├── MXChip/ # [Pre-framework] Standalone board sample +├── OpenHW/ # [Pre-framework] Standalone board sample +├── STMicroelectronics/ # [Pre-framework] Standalone board samples ├── targets/ # [Framework] Supported BSP target boards │ └── Microchip/ │ └── POLARFIRE_ICICLE_RENODE/ # Board-specific BSP implementation & Renode target ├── bsp/ # [Framework] Abstract BSP interface definitions │ └── include/bsp/ # board.h, led.h, console.h ├── cmake/ # [Framework] Shared CMake configuration and utilities +├── docs/ # [Framework] Architecture and onboarding documentation └── templates/ # [Framework] Templates for onboarding new boards ``` diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/CMakeLists.txt b/targets/Microchip/POLARFIRE_ICICLE_RENODE/CMakeLists.txt index 79ed6985..08da8f2a 100644 --- a/targets/Microchip/POLARFIRE_ICICLE_RENODE/CMakeLists.txt +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/CMakeLists.txt @@ -3,7 +3,7 @@ # # This program and the accompanying materials are made available # under the terms of the MIT license which is available at -# https://opensource.org/license/mit. +# https://opensource.org/licenses/MIT. # # SPDX-License-Identifier: MIT # diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/CMakeLists.txt b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/CMakeLists.txt index 820e04ea..64605b99 100644 --- a/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/CMakeLists.txt +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/CMakeLists.txt @@ -3,7 +3,7 @@ # # This program and the accompanying materials are made available # under the terms of the MIT license which is available at -# https://opensource.org/license/mit. +# https://opensource.org/licenses/MIT. # # SPDX-License-Identifier: MIT # diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/linker/linker.ld b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/linker/linker.ld index 3a8a65e9..e80a0ca6 100644 --- a/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/linker/linker.ld +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/linker/linker.ld @@ -3,7 +3,7 @@ * * This program and the accompanying materials are made available * under the terms of the MIT license which is available at - * https://opensource.org/license/mit. + * https://opensource.org/licenses/MIT. * * SPDX-License-Identifier: MIT */ diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/startup/entry.S b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/startup/entry.S index 8d30a4b1..a59f4786 100644 --- a/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/startup/entry.S +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/startup/entry.S @@ -3,7 +3,7 @@ * * This program and the accompanying materials are made available * under the terms of the MIT license which is available at - * https://opensource.org/license/mit. + * https://opensource.org/licenses/MIT. * * SPDX-License-Identifier: MIT */ diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/startup/newlib_stubs.c b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/startup/newlib_stubs.c index 24cf6148..8d196500 100644 --- a/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/startup/newlib_stubs.c +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/startup/newlib_stubs.c @@ -3,14 +3,17 @@ * * This program and the accompanying materials are made available * under the terms of the MIT license which is available at - * https://opensource.org/license/mit. + * https://opensource.org/licenses/MIT. * * SPDX-License-Identifier: MIT */ +// Some portions generated by Claude Code (Opus 5) + #include #include #include +#include #include #include "board_config.h" #include "bsp/console.h" @@ -30,12 +33,31 @@ static inline void restore_interrupts(uintptr_t mstatus) { } } +/* Newlib calls these around every heap operation. The U54 hart running ThreadX + * is single-core, so masking machine interrupts is sufficient to serialise the + * malloc arena against both other threads and interrupt handlers. The nesting + * counter keeps recursive newlib entries from re-enabling interrupts early. */ +static uintptr_t s_malloc_lock_mstatus = 0; +static uint32_t s_malloc_lock_depth = 0; + void __malloc_lock(struct _reent *reent) { (void)reent; + uintptr_t mstatus = disable_interrupts(); + if (s_malloc_lock_depth == 0U) { + s_malloc_lock_mstatus = mstatus; + } + s_malloc_lock_depth++; } void __malloc_unlock(struct _reent *reent) { (void)reent; + if (s_malloc_lock_depth == 0U) { + return; + } + s_malloc_lock_depth--; + if (s_malloc_lock_depth == 0U) { + restore_interrupts(s_malloc_lock_mstatus); + } } int _write(int file, char *ptr, int len) { diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/startup/tx_initialize_low_level.S b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/startup/tx_initialize_low_level.S index a84f1a72..9081ef5a 100644 --- a/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/startup/tx_initialize_low_level.S +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/startup/tx_initialize_low_level.S @@ -3,11 +3,13 @@ * * This program and the accompanying materials are made available * under the terms of the MIT license which is available at - * https://opensource.org/license/mit. + * https://opensource.org/licenses/MIT. * * SPDX-License-Identifier: MIT */ +// Some portions generated by Claude Code (Opus 5) + #include "csr.h" .section .data @@ -70,7 +72,7 @@ _err_hang: .global _tx_initialize_low_level .weak _tx_initialize_low_level .extern _end - .extern board_init + .extern hwtimer_init _tx_initialize_low_level: /* Save the system stack pointer */ @@ -96,10 +98,11 @@ _tx_initialize_low_level: fscsr x0 #endif - /* Call hardware board init */ + /* Arm the CLINT machine timer that drives the ThreadX system tick. + * Board peripherals are brought up separately by bsp_board_init(). */ addi sp, sp, -8 sd ra, 0(sp) - call board_init + call hwtimer_init ld ra, 0(sp) addi sp, sp, 8 diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/main.c b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/main.c index 9cd1e576..6839b001 100644 --- a/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/main.c +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/main.c @@ -3,11 +3,13 @@ * * This program and the accompanying materials are made available * under the terms of the MIT license which is available at - * https://opensource.org/license/mit. + * https://opensource.org/licenses/MIT. * * SPDX-License-Identifier: MIT */ +// Some portions generated by Claude Code (Opus 5) + #include #include #include @@ -26,6 +28,12 @@ extern void *_sbrk(ptrdiff_t incr); #define DEMO_STACK_SIZE 4096 #define DEMO_QUEUE_ITEMS 10 +/* Sample and report periods expressed in milliseconds, converted to ThreadX + * ticks so they stay correct if TX_TIMER_TICKS_PER_SECOND is retuned. */ +#define DEMO_MS_TO_TICKS(ms) (((ms) * (ULONG)TX_TIMER_TICKS_PER_SECOND) / 1000UL) +#define DEMO_SAMPLE_PERIOD_MS 500UL +#define DEMO_REPORT_PERIOD_MS 1000UL + typedef struct SENSOR_DATA_STRUCT { ULONG timestamp; float temperature_celsius; @@ -50,11 +58,15 @@ UCHAR queue_area[DEMO_QUEUE_ITEMS * sizeof(SENSOR_DATA)]; volatile char g_last_rx_char = 0; volatile uint32_t g_rx_irq_count = 0; +volatile uint32_t g_rx_flag_errors = 0; void console_rx_isr_callback(char c) { g_last_rx_char = c; g_rx_irq_count++; - tx_event_flags_set(&alarm_flags, EVENT_FLAG_UART_RX, TX_OR); + if (tx_event_flags_set(&alarm_flags, EVENT_FLAG_UART_RX, TX_OR) != TX_SUCCESS) { + /* Cannot print from interrupt context; record the loss for the reporter. */ + g_rx_flag_errors++; + } } static void console_print(const char *s) { @@ -71,77 +83,89 @@ static volatile ULONG s_sampler_runs = 0; static volatile ULONG s_analyzer_runs = 0; static volatile ULONG s_reporter_runs = 0; +static unsigned s_selftest_failures = 0; + +static void selftest_report(int passed, const char *message) { + if (passed) { + console_print("[+] PASS: "); + } else { + s_selftest_failures++; + console_print("[-] FAIL: "); + } + console_print(message); + console_print("\n"); +} + static void run_startup_self_tests(void) { extern char __end; - char num_buf[128]; + char num_buf[160]; console_print("[SELF-TEST] Starting Hardware & Runtime Verification...\n"); /* 1. _sbrk() Valid allocation test */ void *p1 = _sbrk(64); - if (p1 != (void *)-1 && (uintptr_t)p1 >= (uintptr_t)&__end) { - console_print("[+] PASS: _sbrk() valid allocation returned base pointer\n"); - } else { - console_print("[-] FAIL: _sbrk() valid allocation failed\n"); - } + selftest_report(p1 != (void *)-1 && (uintptr_t)p1 >= (uintptr_t)&__end, + "_sbrk() valid allocation returned base pointer"); /* 2. _sbrk() Underflow test (shrink below heap base) */ errno = 0; void *p_under = _sbrk(-128); - if (p_under == (void *)-1 && errno == EINVAL) { - console_print("[+] PASS: _sbrk() underflow guard rejected with EINVAL\n"); - } else { - console_print("[-] FAIL: _sbrk() underflow guard failed\n"); - } + selftest_report(p_under == (void *)-1 && errno == EINVAL, + "_sbrk() underflow guard rejected with EINVAL"); /* 3. _sbrk() Overflow test (request beyond 1 GiB DRAM) */ errno = 0; void *p_over = _sbrk((ptrdiff_t)0x40000000ULL); - if (p_over == (void *)-1 && errno == ENOMEM) { - console_print("[+] PASS: _sbrk() overflow guard rejected with ENOMEM\n"); - } else { - console_print("[-] FAIL: _sbrk() overflow guard failed\n"); - } - - /* 4. HWTimer catch-up clamp test */ - MTIME_REG = 100000ULL; /* Set known baseline mtime */ - uint64_t current_mtime = MTIME_REG; - uint64_t past_cmp = 20000ULL; /* In the past by 80,000 cycles (8 missed ticks) */ - HART1_MTIMECMP_REG = past_cmp; + selftest_report(p_over == (void *)-1 && errno == ENOMEM, + "_sbrk() overflow guard rejected with ENOMEM"); + + /* 4. HWTimer catch-up clamp test. + * + * mtime is the platform-wide monotonic counter shared by every hart, so it + * is deliberately never written here. Only the per-hart mtimecmp is staged + * into the past; hwtimer_ack() must then re-arm relative to a fresh mtime + * read rather than to the stale comparand. mtime advances while we run, so + * the expected value is bracketed by mtime sampled either side of the call. */ + uint64_t mtime_before = MTIME_REG; + HART1_MTIMECMP_REG = mtime_before - (TICK_CYCLES * 8ULL); /* 8 ticks behind */ hwtimer_ack(); + uint64_t mtime_after = MTIME_REG; uint64_t clamped_cmp = HART1_MTIMECMP_REG; - if (clamped_cmp == current_mtime + TICK_CYCLES) { - snprintf(num_buf, sizeof(num_buf), - "[+] PASS: HWTimer catch-up (mtime=%llu, past_cmp=%llu -> clamped_cmp=%llu == mtime + %llu)\n", - (unsigned long long)current_mtime, - (unsigned long long)past_cmp, - (unsigned long long)clamped_cmp, - (unsigned long long)TICK_CYCLES); - console_print(num_buf); - } else { - console_print("[-] FAIL: HWTimer catch-up clamp failed\n"); - } - - /* 5. PLIC Configuration & Addressing Verification */ + int clamp_ok = (clamped_cmp >= mtime_before + TICK_CYCLES) && + (clamped_cmp <= mtime_after + TICK_CYCLES); + snprintf(num_buf, sizeof(num_buf), + "HWTimer catch-up (staged 8 ticks behind -> clamped_cmp=%llu in [%llu, %llu])", + (unsigned long long)clamped_cmp, + (unsigned long long)(mtime_before + TICK_CYCLES), + (unsigned long long)(mtime_after + TICK_CYCLES)); + selftest_report(clamp_ok, num_buf); + + /* 5. PLIC Configuration & Addressing Verification. + * A wrong PLIC base would read back zeroes here, so the register values + * confirm the addressing as well as the configuration. */ uint32_t prio = PLIC_PRIORITY_REG(MMUART1_IRQ); - uint32_t en_bitmap = PLIC_HART1_M_ENABLE_REG2; + uint32_t en_bitmap = PLIC_HART1_M_ENABLE_REG(MMUART1_IRQ); uint32_t thresh = PLIC_HART1_M_THRESHOLD_REG; - uint32_t claim = PLIC_HART1_M_CLAIM_REG; uint64_t mie_val; __asm__ volatile("csrr %0, mie" : "=r"(mie_val)); - if (prio == 1 && (en_bitmap & (1U << (MMUART1_IRQ % 32))) != 0 && thresh == 0 && (mie_val & MIE_MEIE) != 0) { - snprintf(num_buf, sizeof(num_buf), - "[+] PASS: PLIC Hart 1 verified (IRQ %u prio=%u, enable_bit=27, thresh=%u, claim=%u, mie.MEIE=1)\n", - (unsigned)MMUART1_IRQ, (unsigned)prio, (unsigned)thresh, (unsigned)claim); - console_print(num_buf); + int plic_ok = (prio == 1) && + ((en_bitmap & (1U << (MMUART1_IRQ % 32))) != 0) && + (thresh == 0) && + ((mie_val & MIE_MEIE) != 0); + snprintf(num_buf, sizeof(num_buf), + "PLIC Hart 1 (IRQ %u prio=%u en=0x%08X thresh=%u mie=0x%llX)", + (unsigned)MMUART1_IRQ, (unsigned)prio, (unsigned)en_bitmap, + (unsigned)thresh, (unsigned long long)mie_val); + selftest_report(plic_ok, num_buf); + + if (s_selftest_failures == 0) { + console_print("[SELF-TEST] All startup verification tests PASSED!\n\n"); } else { snprintf(num_buf, sizeof(num_buf), - "[-] PLIC DIAG: prio=%u en=0x%08X thresh=%u claim=%u mie=0x%llX (expected bit27=1, mie.MEIE=0x800)\n", - (unsigned)prio, (unsigned)en_bitmap, (unsigned)thresh, (unsigned)claim, (unsigned long long)mie_val); + "[SELF-TEST] %u startup verification test(s) FAILED!\n\n", + s_selftest_failures); console_print(num_buf); } - - console_print("[SELF-TEST] All startup verification tests PASSED!\n\n"); } int main(void) { @@ -236,7 +260,7 @@ void sampler_thread_entry(ULONG input) { simulated_temp = 25.0f; } - tx_thread_sleep(50); /* Sample every 500ms */ + tx_thread_sleep(DEMO_MS_TO_TICKS(DEMO_SAMPLE_PERIOD_MS)); } } @@ -295,6 +319,6 @@ void reporter_thread_entry(ULONG input) { console_print(rx_buf); } - tx_thread_sleep(100); /* Report every 1 second */ + tx_thread_sleep(DEMO_MS_TO_TICKS(DEMO_REPORT_PERIOD_MS)); } } diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/CMakeLists.txt b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/CMakeLists.txt index df776415..a2111562 100644 --- a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/CMakeLists.txt +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/CMakeLists.txt @@ -3,7 +3,7 @@ # # This program and the accompanying materials are made available # under the terms of the MIT license which is available at -# https://opensource.org/license/mit. +# https://opensource.org/licenses/MIT. # # SPDX-License-Identifier: MIT # diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/board_config.h b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/board_config.h index bc506801..309dfcae 100644 --- a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/board_config.h +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/board_config.h @@ -3,7 +3,7 @@ * * This program and the accompanying materials are made available * under the terms of the MIT license which is available at - * https://opensource.org/license/mit. + * https://opensource.org/licenses/MIT. * * SPDX-License-Identifier: MIT */ diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/csr.h b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/csr.h index e3a9e3be..78da1ca1 100644 --- a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/csr.h +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/csr.h @@ -3,7 +3,7 @@ * * This program and the accompanying materials are made available * under the terms of the MIT license which is available at - * https://opensource.org/license/mit. + * https://opensource.org/licenses/MIT. * * SPDX-License-Identifier: MIT */ diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/hwtimer.h b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/hwtimer.h index 7887edac..b57902ef 100644 --- a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/hwtimer.h +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/hwtimer.h @@ -3,7 +3,7 @@ * * This program and the accompanying materials are made available * under the terms of the MIT license which is available at - * https://opensource.org/license/mit. + * https://opensource.org/licenses/MIT. * * SPDX-License-Identifier: MIT */ diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/plic.h b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/plic.h index c72f925a..8133a828 100644 --- a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/plic.h +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/plic.h @@ -8,6 +8,8 @@ * SPDX-License-Identifier: MIT */ +// Some portions generated by Claude Code (Opus 5) + #ifndef PLIC_H #define PLIC_H @@ -16,15 +18,39 @@ /* SiFive PLIC Base Address on Microchip PolarFire SoC Icicle Kit */ #define PLIC_BASE 0x0C000000ULL -/* Hart 1 Machine-Mode (Context 2 on PolarFire SoC) Control Registers */ -#define PLIC_HART1_M_THRESHOLD_REG (*(volatile uint32_t *)(PLIC_BASE + 0x202000ULL)) -#define PLIC_HART1_M_CLAIM_REG (*(volatile uint32_t *)(PLIC_BASE + 0x202004ULL)) +/* Standard SiFive PLIC register-block offsets. */ +#define PLIC_PRIORITY_OFFSET 0x000000ULL /* 4 bytes per source */ +#define PLIC_ENABLE_OFFSET 0x002000ULL /* 0x80 per context */ +#define PLIC_CONTEXT_OFFSET 0x200000ULL /* 0x1000 per context */ + +/* + * PolarFire SoC PLIC context assignment. The E51 monitor core has machine mode + * only; each U54 application core contributes a machine-mode context followed + * by a supervisor-mode one: + * + * 0: E51 M 3: U54_2 M 5: U54_3 M 7: U54_4 M + * 1: U54_1 M 4: U54_2 S 6: U54_3 S 8: U54_4 S + * 2: U54_1 S + * + * ThreadX runs in machine mode on Hart 1 (u54_1), so the correct context is 1. + * Context 2 is the supervisor-mode context for the same hart: programming it + * is silently accepted by the PLIC but leaves the machine-mode enable bitmap + * clear, so MEIP never asserts and no external interrupt is ever delivered. + */ +#define PLIC_HART1_M_CONTEXT 1U + +#define PLIC_CONTEXT_BASE(ctx) (PLIC_BASE + PLIC_CONTEXT_OFFSET + ((uint64_t)(ctx) * 0x1000ULL)) +#define PLIC_ENABLE_BASE(ctx) (PLIC_BASE + PLIC_ENABLE_OFFSET + ((uint64_t)(ctx) * 0x80ULL)) + +/* Hart 1 Machine-Mode (Context 1) Control Registers */ +#define PLIC_HART1_M_THRESHOLD_REG (*(volatile uint32_t *)(PLIC_CONTEXT_BASE(PLIC_HART1_M_CONTEXT))) +#define PLIC_HART1_M_CLAIM_REG (*(volatile uint32_t *)(PLIC_CONTEXT_BASE(PLIC_HART1_M_CONTEXT) + 4ULL)) -/* Hart 1 Machine-Mode (Context 2) Interrupt Enable Bitmap for IRQ 64..95 */ -#define PLIC_HART1_M_ENABLE_REG2 (*(volatile uint32_t *)(PLIC_BASE + 0x2000ULL + (2 * 0x80ULL) + (2 * 4ULL))) +/* Hart 1 Machine-Mode enable bitmap word holding source `irq` (32 sources per word) */ +#define PLIC_HART1_M_ENABLE_REG(irq) (*(volatile uint32_t *)(PLIC_ENABLE_BASE(PLIC_HART1_M_CONTEXT) + (((uint64_t)(irq) / 32ULL) * 4ULL))) /* Interrupt Source Priority Register (1..186) */ -#define PLIC_PRIORITY_REG(irq) (*(volatile uint32_t *)(PLIC_BASE + ((irq) * 4ULL))) +#define PLIC_PRIORITY_REG(irq) (*(volatile uint32_t *)(PLIC_BASE + PLIC_PRIORITY_OFFSET + ((irq) * 4ULL))) /* Microchip PolarFire SoC MMUART1 PLIC Source ID */ #define MMUART1_IRQ 91 diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/uart.h b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/uart.h index 069b36b8..9d980492 100644 --- a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/uart.h +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/uart.h @@ -3,7 +3,7 @@ * * This program and the accompanying materials are made available * under the terms of the MIT license which is available at - * https://opensource.org/license/mit. + * https://opensource.org/licenses/MIT. * * SPDX-License-Identifier: MIT */ diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/board.c b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/board.c index 58236dc6..91192bba 100644 --- a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/board.c +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/board.c @@ -3,18 +3,23 @@ * * This program and the accompanying materials are made available * under the terms of the MIT license which is available at - * https://opensource.org/license/mit. + * https://opensource.org/licenses/MIT. * * SPDX-License-Identifier: MIT */ -#include "hwtimer.h" +// Some portions generated by Claude Code (Opus 5) + #include "plic.h" +/* Board-level peripheral bring-up, owned by bsp_board_init(). + * + * The ThreadX system tick is deliberately NOT started here: hwtimer_init() is + * called from _tx_initialize_low_level so the CLINT comparand is armed + * immediately before the scheduler starts, which is the single owner of the + * kernel tick. Keeping the two apart avoids the peripheral being initialised + * twice on the path main() -> bsp_board_init() -> tx_kernel_enter(). */ void board_init(void) { - /* Initialize 64-bit MTIME machine timer (10ms tick interval) */ - hwtimer_init(); - /* Initialize PLIC (Hart 1 Context 2, enable MMUART1 IRQ 91) */ plic_init(); } diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/bsp_board.c b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/bsp_board.c index 67b9160b..293c5a86 100644 --- a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/bsp_board.c +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/bsp_board.c @@ -3,7 +3,7 @@ * * This program and the accompanying materials are made available * under the terms of the MIT license which is available at - * https://opensource.org/license/mit. + * https://opensource.org/licenses/MIT. * * SPDX-License-Identifier: MIT */ diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/bsp_console.c b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/bsp_console.c index ef1c275a..4695e21b 100644 --- a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/bsp_console.c +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/bsp_console.c @@ -3,7 +3,7 @@ * * This program and the accompanying materials are made available * under the terms of the MIT license which is available at - * https://opensource.org/license/mit. + * https://opensource.org/licenses/MIT. * * SPDX-License-Identifier: MIT */ diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/bsp_led.c b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/bsp_led.c index cc05a9e6..a8e3590e 100644 --- a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/bsp_led.c +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/bsp_led.c @@ -3,7 +3,7 @@ * * This program and the accompanying materials are made available * under the terms of the MIT license which is available at - * https://opensource.org/license/mit. + * https://opensource.org/licenses/MIT. * * SPDX-License-Identifier: MIT */ diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/hwtimer.c b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/hwtimer.c index 219ae0d4..36a1b535 100644 --- a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/hwtimer.c +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/hwtimer.c @@ -3,7 +3,7 @@ * * This program and the accompanying materials are made available * under the terms of the MIT license which is available at - * https://opensource.org/license/mit. + * https://opensource.org/licenses/MIT. * * SPDX-License-Identifier: MIT */ diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/plic.c b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/plic.c index c9add636..f4f5fd7d 100644 --- a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/plic.c +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/plic.c @@ -8,6 +8,8 @@ * SPDX-License-Identifier: MIT */ +// Some portions generated by Claude Code (Opus 5) + #include "plic.h" #include "csr.h" @@ -15,8 +17,8 @@ void plic_init(void) { /* Set Priority for MMUART1 (IRQ 91) to 1 */ PLIC_PRIORITY_REG(MMUART1_IRQ) = 1; - /* Enable IRQ 91 for Hart 1 Machine Mode (Context 2). Bit position = 91 % 32 = 27 */ - PLIC_HART1_M_ENABLE_REG2 |= (1U << (MMUART1_IRQ % 32)); + /* Enable IRQ 91 for Hart 1 Machine Mode (Context 1). Bit position = 91 % 32 = 27 */ + PLIC_HART1_M_ENABLE_REG(MMUART1_IRQ) |= (1U << (MMUART1_IRQ % 32)); /* Set Priority Threshold for Hart 1 to 0 (allow all non-zero priority interrupts) */ PLIC_HART1_M_THRESHOLD_REG = 0; diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/uart.c b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/uart.c index 4e6af71a..57767aa0 100644 --- a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/uart.c +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/uart.c @@ -3,7 +3,7 @@ * * This program and the accompanying materials are made available * under the terms of the MIT license which is available at - * https://opensource.org/license/mit. + * https://opensource.org/licenses/MIT. * * SPDX-License-Identifier: MIT */ diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/renode/polarfire_demo.robot b/targets/Microchip/POLARFIRE_ICICLE_RENODE/renode/polarfire_demo.robot index 6d3f3fa6..0d982533 100644 --- a/targets/Microchip/POLARFIRE_ICICLE_RENODE/renode/polarfire_demo.robot +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/renode/polarfire_demo.robot @@ -10,7 +10,16 @@ Should Boot ThreadX And Trigger Telemetry Alarms Execute Command include @${CURDIR}/polarfire_demo.resc Create Terminal Tester sysbus.mmuart1 - Wait For Line On Uart Microchip PolarFire SoC Icicle Kit (Renode Target) timeout=10 - Wait For Line On Uart [Monitor] ThreadX Ticks: 0 | Memory Area Active | Queues OK timeout=10 - Wait For Line On Uart [Monitor] ThreadX Ticks: 100 | Memory Area Active | Queues OK timeout=10 - Wait For Line On Uart OVERTEMP ALARM TRIGGERED timeout=15 + Wait For Line On Uart Microchip PolarFire SoC Icicle Kit (Renode Target) timeout=10 + Wait For Line On Uart [SELF-TEST] All startup verification tests PASSED! timeout=10 + Wait For Line On Uart [Monitor] Ticks: 0 timeout=10 + Wait For Line On Uart [Monitor] Ticks: 100 timeout=10 + Wait For Line On Uart OVERTEMP ALARM TRIGGERED timeout=15 + +Should Deliver MMUART1 RX Interrupt Through The PLIC + Execute Command include @${CURDIR}/polarfire_demo.resc + Create Terminal Tester sysbus.mmuart1 + + Wait For Line On Uart [Monitor] Ticks: 0 timeout=10 + Write Char On Uart X + Wait For Line On Uart PLIC IRQ 91 handled timeout=10 diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/scripts/test_renode.py b/targets/Microchip/POLARFIRE_ICICLE_RENODE/scripts/test_renode.py index d05090cc..24ab5894 100644 --- a/targets/Microchip/POLARFIRE_ICICLE_RENODE/scripts/test_renode.py +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/scripts/test_renode.py @@ -4,7 +4,7 @@ # # This program and the accompanying materials are made available # under the terms of the MIT license which is available at -# https://opensource.org/license/mit. +# https://opensource.org/licenses/MIT. # # SPDX-License-Identifier: MIT # @@ -21,6 +21,9 @@ import threading import queue +# Byte injected into MMUART1 to exercise the PLIC external-interrupt path. +RX_TEST_CHAR = "X" + def find_renode(): # Check PATH first renode_bin = shutil.which("renode") @@ -84,30 +87,54 @@ def run_test(test_timeout_mode=False): output_lines = [] found_selftests = False + found_selftest_failure = False found_ticks = False found_alarm = False found_plic_rx = False injected_char = False start_time = time.time() - + + def inject_uart_byte(): + """Deliver a byte to MMUART1 so the PLIC external-interrupt path is + actually exercised, rather than only having its registers inspected.""" + try: + proc.stdin.write("sysbus.mmuart1 WriteChar %d\n" % ord(RX_TEST_CHAR)) + proc.stdin.flush() + return True + except Exception as exc: + print("[!] Could not inject UART byte: %s" % exc) + return False + try: while time.time() - start_time < timeout_seconds: try: line = output_q.get(timeout=0.1) output_lines.append(line) print(line, end="") - if not test_timeout_mode: - if "[SELF-TEST] All startup verification tests PASSED!" in line: - found_selftests = True - if "Ticks:" in line: - found_ticks = True - if "OVERTEMP ALARM TRIGGERED" in line: - found_alarm = True - if "PLIC IRQ 91 handled" in line: - found_plic_rx = True - if found_selftests and found_ticks and found_alarm: - print("\n[+] SUCCESS: Startup self-tests (including PLIC), ThreadX ticks, and LM75 alarm all verified!") - break + if test_timeout_mode: + continue + + if "[-] FAIL:" in line or "startup verification test(s) FAILED" in line: + found_selftest_failure = True + if "[SELF-TEST] All startup verification tests PASSED!" in line: + found_selftests = True + if "Ticks:" in line: + found_ticks = True + # The kernel is running, so the RX interrupt can be serviced. + if not injected_char: + injected_char = inject_uart_byte() + if "OVERTEMP ALARM TRIGGERED" in line: + found_alarm = True + if "PLIC IRQ 91 handled" in line: + found_plic_rx = True + + if found_selftest_failure: + print("\n[-] FAILED: a startup self-test reported a failure.") + break + if found_selftests and found_ticks and found_alarm and found_plic_rx: + print("\n[+] SUCCESS: startup self-tests, ThreadX ticks, " + "LM75 alarm, and PLIC RX interrupt all verified!") + break except queue.Empty: if proc.poll() is not None: break @@ -120,17 +147,29 @@ def run_test(test_timeout_mode=False): proc.kill() except Exception: pass - - if not test_timeout_mode and found_ticks and found_alarm: - print("[+] Renode headless test PASSED.") - sys.exit(0) - elif test_timeout_mode: + + if test_timeout_mode: elapsed = time.time() - start_time - print(f"\n[+] SUCCESS: Intentional timeout triggered after {elapsed:.2f}s and terminated child process cleanly.") + print(f"\n[+] SUCCESS: Intentional timeout triggered after {elapsed:.2f}s " + f"and terminated child process cleanly.") sys.exit(0) - else: - print(f"\n[-] FAILED: Timed out waiting for expected telemetry. (found_ticks={found_ticks}, found_alarm={found_alarm})") - sys.exit(1) + + checks = { + "startup self-tests passed": found_selftests and not found_selftest_failure, + "ThreadX system tick advancing": found_ticks, + "LM75 overtemperature alarm": found_alarm, + "PLIC IRQ 91 RX interrupt delivered": found_plic_rx, + } + failed = [name for name, ok in checks.items() if not ok] + + if not failed: + print("[+] Renode headless test PASSED.") + sys.exit(0) + + print("\n[-] FAILED. Unmet assertions:") + for name in failed: + print(" - %s" % name) + sys.exit(1) if __name__ == "__main__": timeout_test = "--test-timeout" in sys.argv diff --git a/templates/target/CMakeLists.txt b/templates/target/CMakeLists.txt index 7bb05e70..13ac5b52 100644 --- a/templates/target/CMakeLists.txt +++ b/templates/target/CMakeLists.txt @@ -2,7 +2,7 @@ # # This program and the accompanying materials are made available # under the terms of the MIT license which is available at -# https://opensource.org/license/mit. +# https://opensource.org/licenses/MIT. # # SPDX-License-Identifier: MIT diff --git a/templates/target/app/CMakeLists.txt b/templates/target/app/CMakeLists.txt index ac1fae5b..1fad0eaa 100644 --- a/templates/target/app/CMakeLists.txt +++ b/templates/target/app/CMakeLists.txt @@ -2,7 +2,7 @@ # # This program and the accompanying materials are made available # under the terms of the MIT license which is available at -# https://opensource.org/license/mit. +# https://opensource.org/licenses/MIT. # # SPDX-License-Identifier: MIT diff --git a/templates/target/lib/CMakeLists.txt b/templates/target/lib/CMakeLists.txt index 80364e2d..d668e148 100644 --- a/templates/target/lib/CMakeLists.txt +++ b/templates/target/lib/CMakeLists.txt @@ -2,7 +2,7 @@ # # This program and the accompanying materials are made available # under the terms of the MIT license which is available at -# https://opensource.org/license/mit. +# https://opensource.org/licenses/MIT. # # SPDX-License-Identifier: MIT diff --git a/templates/target/lib/bsp/CMakeLists.txt b/templates/target/lib/bsp/CMakeLists.txt index 741ea25a..de096665 100644 --- a/templates/target/lib/bsp/CMakeLists.txt +++ b/templates/target/lib/bsp/CMakeLists.txt @@ -2,7 +2,7 @@ # # This program and the accompanying materials are made available # under the terms of the MIT license which is available at -# https://opensource.org/license/mit. +# https://opensource.org/licenses/MIT. # # SPDX-License-Identifier: MIT diff --git a/templates/target/lib/bsp/include/board_config.h b/templates/target/lib/bsp/include/board_config.h index 0cbce929..6fc2cc8e 100644 --- a/templates/target/lib/bsp/include/board_config.h +++ b/templates/target/lib/bsp/include/board_config.h @@ -3,7 +3,7 @@ * * This program and the accompanying materials are made available * under the terms of the MIT license which is available at - * https://opensource.org/license/mit. + * https://opensource.org/licenses/MIT. * * SPDX-License-Identifier: MIT */ diff --git a/templates/target/lib/bsp/src/bsp_board.c b/templates/target/lib/bsp/src/bsp_board.c index 31124b08..e2d04304 100644 --- a/templates/target/lib/bsp/src/bsp_board.c +++ b/templates/target/lib/bsp/src/bsp_board.c @@ -3,7 +3,7 @@ * * This program and the accompanying materials are made available * under the terms of the MIT license which is available at - * https://opensource.org/license/mit. + * https://opensource.org/licenses/MIT. * * SPDX-License-Identifier: MIT */ diff --git a/templates/target/lib/bsp/src/bsp_console.c b/templates/target/lib/bsp/src/bsp_console.c index 773427b8..23dae914 100644 --- a/templates/target/lib/bsp/src/bsp_console.c +++ b/templates/target/lib/bsp/src/bsp_console.c @@ -3,7 +3,7 @@ * * This program and the accompanying materials are made available * under the terms of the MIT license which is available at - * https://opensource.org/license/mit. + * https://opensource.org/licenses/MIT. * * SPDX-License-Identifier: MIT */ diff --git a/templates/target/lib/bsp/src/bsp_led.c b/templates/target/lib/bsp/src/bsp_led.c index 02691454..890ab358 100644 --- a/templates/target/lib/bsp/src/bsp_led.c +++ b/templates/target/lib/bsp/src/bsp_led.c @@ -3,7 +3,7 @@ * * This program and the accompanying materials are made available * under the terms of the MIT license which is available at - * https://opensource.org/license/mit. + * https://opensource.org/licenses/MIT. * * SPDX-License-Identifier: MIT */ diff --git a/templates/target/lib/bsp/src/newlib_stubs.c b/templates/target/lib/bsp/src/newlib_stubs.c index cc65b1fd..70b0f76f 100644 --- a/templates/target/lib/bsp/src/newlib_stubs.c +++ b/templates/target/lib/bsp/src/newlib_stubs.c @@ -3,34 +3,66 @@ * * This program and the accompanying materials are made available * under the terms of the MIT license which is available at - * https://opensource.org/license/mit. + * https://opensource.org/licenses/MIT. * * SPDX-License-Identifier: MIT */ +// Some portions generated by Claude Code (Opus 5) + #ifdef __GNUC__ #include +#include #include #include #include +#include "board_config.h" #include "bsp/console.h" -extern int errno; -extern int _end; +/* Placed by the linker script at the first address above .bss, below the stack. */ +extern char _end; /** * @brief Dynamic memory allocation heap growth stub for Newlib standard C library. + * + * The heap is bounded by BSP_RAM_END from board_config.h. An allocation that + * would run past the end of physical RAM, overflow the pointer, or shrink the + * heap below its base is rejected with (void *)-1 and errno set, which is what + * newlib expects. Returning an out-of-range pointer instead would hand malloc() + * memory that does not exist. + * + * The increment is ptrdiff_t rather than int so the stub stays correct on + * 64-bit targets. */ -void* _sbrk(int incr) +void* _sbrk(ptrdiff_t incr) { - static unsigned char* heap = NULL; - unsigned char* prev_heap; + static char* heap = NULL; + char* prev_heap; if (heap == NULL) { - heap = (unsigned char*)&_end; + heap = &_end; } + + if (incr > 0) + { + if ((uintptr_t)heap + (uintptr_t)incr > (uintptr_t)BSP_RAM_END || + (uintptr_t)heap + (uintptr_t)incr < (uintptr_t)heap) + { + errno = ENOMEM; + return (void*)-1; + } + } + else if (incr < 0) + { + if ((uintptr_t)heap < (uintptr_t)&_end + (uintptr_t)(-incr)) + { + errno = EINVAL; + return (void*)-1; + } + } + prev_heap = heap; heap += incr; From 83c0e04be1f8a16e8e1bbdc320d47482e6e3bfbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Desbiens?= Date: Fri, 28 Aug 2026 12:09:35 -0400 Subject: [PATCH 8/9] Fixed the timer catch-up self-test to exercise pure arithmetic The previous self-test staged mtimecmp eight ticks behind by subtracting from mtime. Renode starts mtime near zero, so that subtraction underflowed to 2^64-79900 and the test failed itself in CI. The production clamp was never wrong; the test fed it a value real code cannot produce. Extracted the catch-up decision into hwtimer_next_cmp(), a pure function of the current comparand and mtime, and left hwtimer_ack() as a thin wrapper. The test now covers both branches with synthetic values and touches no CLINT register at all, which also removes the last reason for the demo to write timer state. Replaced the TX_TIMER_TICKS_PER_SECOND fallback in hwtimer.h with BSP_TICK_RATE_HZ in board_config.h. The BSP does not see the ThreadX headers, so that fallback silently applied whenever the two disagreed; main.c sees both and now carries a C99 compile-time check that they match. The Renode harness no longer stops at the first failing self-test, so a single failure reports the state of every other assertion instead of hiding it. Assisted-by: Claude Code (Opus 5) Co-Authored-By: Claude Opus 5 (1M context) --- .../POLARFIRE_ICICLE_RENODE/app/main.c | 38 ++++++++++--------- .../lib/bsp/include/board_config.h | 6 +++ .../lib/bsp/include/hwtimer.h | 27 ++++++++++--- .../lib/bsp/src/hwtimer.c | 19 ++++++---- .../scripts/test_renode.py | 8 ++-- 5 files changed, 65 insertions(+), 33 deletions(-) diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/main.c b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/main.c index 6839b001..50e3808a 100644 --- a/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/main.c +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/main.c @@ -25,6 +25,13 @@ extern void *_sbrk(ptrdiff_t incr); +/* board_config.h derives TICK_CYCLES from BSP_TICK_RATE_HZ without seeing the + * ThreadX headers. This translation unit sees both, so it is where the two are + * checked against each other. C99 has no _Static_assert, hence the negative + * array size idiom. */ +typedef char bsp_tick_rate_matches_threadx[ + (BSP_TICK_RATE_HZ == (unsigned long long)TX_TIMER_TICKS_PER_SECOND) ? 1 : -1]; + #define DEMO_STACK_SIZE 4096 #define DEMO_QUEUE_ITEMS 10 @@ -118,25 +125,22 @@ static void run_startup_self_tests(void) { selftest_report(p_over == (void *)-1 && errno == ENOMEM, "_sbrk() overflow guard rejected with ENOMEM"); - /* 4. HWTimer catch-up clamp test. + /* 4. HWTimer catch-up clamp. * - * mtime is the platform-wide monotonic counter shared by every hart, so it - * is deliberately never written here. Only the per-hart mtimecmp is staged - * into the past; hwtimer_ack() must then re-arm relative to a fresh mtime - * read rather than to the stale comparand. mtime advances while we run, so - * the expected value is bracketed by mtime sampled either side of the call. */ - uint64_t mtime_before = MTIME_REG; - HART1_MTIMECMP_REG = mtime_before - (TICK_CYCLES * 8ULL); /* 8 ticks behind */ - hwtimer_ack(); - uint64_t mtime_after = MTIME_REG; - uint64_t clamped_cmp = HART1_MTIMECMP_REG; - int clamp_ok = (clamped_cmp >= mtime_before + TICK_CYCLES) && - (clamped_cmp <= mtime_after + TICK_CYCLES); + * Exercised as pure arithmetic through hwtimer_next_cmp(), so no CLINT + * register is disturbed: mtime is the platform-wide counter shared by every + * hart, and mtimecmp drives the live kernel tick. Both branches are covered + * - a deadline still in the future advances relatively, one already missed + * is rebased onto the current time instead of firing continuously. */ + uint64_t now = 5000000ULL; + uint64_t missed_cmp = now - (TICK_CYCLES * 8ULL); /* 8 ticks behind */ + uint64_t pending_cmp = now - (TICK_CYCLES / 2ULL); /* deadline not yet due */ + int clamp_ok = (hwtimer_next_cmp(missed_cmp, now) == now + TICK_CYCLES) && + (hwtimer_next_cmp(pending_cmp, now) == pending_cmp + TICK_CYCLES); snprintf(num_buf, sizeof(num_buf), - "HWTimer catch-up (staged 8 ticks behind -> clamped_cmp=%llu in [%llu, %llu])", - (unsigned long long)clamped_cmp, - (unsigned long long)(mtime_before + TICK_CYCLES), - (unsigned long long)(mtime_after + TICK_CYCLES)); + "HWTimer catch-up (missed deadline rebased to %llu, pending deadline advanced to %llu)", + (unsigned long long)hwtimer_next_cmp(missed_cmp, now), + (unsigned long long)hwtimer_next_cmp(pending_cmp, now)); selftest_report(clamp_ok, num_buf); /* 5. PLIC Configuration & Addressing Verification. diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/board_config.h b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/board_config.h index 309dfcae..40400d9a 100644 --- a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/board_config.h +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/board_config.h @@ -8,6 +8,8 @@ * SPDX-License-Identifier: MIT */ +// Some portions generated by Claude Code (Opus 5) + #ifndef BOARD_CONFIG_H #define BOARD_CONFIG_H @@ -16,6 +18,10 @@ #define BSP_SYSTEM_CLOCK_HZ BSP_CPU_CLOCK_HZ #define BSP_CLINT_RTC_FREQ_HZ 1000000ULL /* 1 MHz Real-Time CLINT Clock in Renode */ +/* ThreadX system tick rate. Must match TX_TIMER_TICKS_PER_SECOND; main.c + * enforces that with a compile-time check. */ +#define BSP_TICK_RATE_HZ 100ULL + #define BSP_UART_BAUDRATE 115200 #define BSP_RAM_END 0xC0000000ULL /* 1 GiB LPDDR4 DRAM End Address */ diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/hwtimer.h b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/hwtimer.h index b57902ef..72ab0150 100644 --- a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/hwtimer.h +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/hwtimer.h @@ -18,13 +18,30 @@ #define MTIME_REG (*(volatile uint64_t *)(CLINT_BASE + 0xBFF8)) #define HART1_MTIMECMP_REG (*(volatile uint64_t *)(CLINT_BASE + 0x4008)) -#ifndef TX_TIMER_TICKS_PER_SECOND -#define TX_TIMER_TICKS_PER_SECOND 100ULL -#endif - -#define TICK_CYCLES (BSP_CLINT_RTC_FREQ_HZ / (uint64_t)TX_TIMER_TICKS_PER_SECOND) +/* CLINT cycles between ThreadX system ticks. BSP_TICK_RATE_HZ lives in + * board_config.h so this header stays independent of the ThreadX includes; + * main.c carries a compile-time check that it still agrees with + * TX_TIMER_TICKS_PER_SECOND, so the two cannot silently desync. */ +#define TICK_CYCLES (BSP_CLINT_RTC_FREQ_HZ / BSP_TICK_RATE_HZ) void hwtimer_init(void); void hwtimer_ack(void); +/** + * @brief Compute the comparand for the next system tick. + * + * Normally this is one tick past the previous deadline. If the previous + * deadline is already at or behind @p now, one or more ticks were missed and + * re-arming relatively would leave the timer firing continuously until it + * caught up, so the deadline is rebased onto @p now instead. + * + * Kept as a pure function of its arguments so the catch-up behaviour can be + * verified without writing to the CLINT. + * + * @param current_cmp Comparand currently programmed into mtimecmp. + * @param now Current value of the shared mtime counter. + * @return Comparand to program for the next tick. + */ +uint64_t hwtimer_next_cmp(uint64_t current_cmp, uint64_t now); + #endif /* POLARFIRE_HWTIMER_H */ diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/hwtimer.c b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/hwtimer.c index 36a1b535..2f7635a2 100644 --- a/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/hwtimer.c +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/hwtimer.c @@ -8,6 +8,8 @@ * SPDX-License-Identifier: MIT */ +// Some portions generated by Claude Code (Opus 5) + #include "hwtimer.h" void hwtimer_init(void) { @@ -16,14 +18,17 @@ void hwtimer_init(void) { HART1_MTIMECMP_REG = current_mtime + TICK_CYCLES; } -void hwtimer_ack(void) { - uint64_t current_mtime = MTIME_REG; - uint64_t next_cmp = HART1_MTIMECMP_REG + TICK_CYCLES; +uint64_t hwtimer_next_cmp(uint64_t current_cmp, uint64_t now) { + uint64_t next_cmp = current_cmp + TICK_CYCLES; - /* Clamp to current_mtime + TICK_CYCLES if timer fell behind */ - if (next_cmp <= current_mtime) { - next_cmp = current_mtime + TICK_CYCLES; + /* Deadline already missed: rebase onto now rather than chasing it. */ + if (next_cmp <= now) { + next_cmp = now + TICK_CYCLES; } - HART1_MTIMECMP_REG = next_cmp; + return next_cmp; +} + +void hwtimer_ack(void) { + HART1_MTIMECMP_REG = hwtimer_next_cmp(HART1_MTIMECMP_REG, MTIME_REG); } diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/scripts/test_renode.py b/targets/Microchip/POLARFIRE_ICICLE_RENODE/scripts/test_renode.py index 24ab5894..23fa9c07 100644 --- a/targets/Microchip/POLARFIRE_ICICLE_RENODE/scripts/test_renode.py +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/scripts/test_renode.py @@ -128,10 +128,10 @@ def inject_uart_byte(): if "PLIC IRQ 91 handled" in line: found_plic_rx = True - if found_selftest_failure: - print("\n[-] FAILED: a startup self-test reported a failure.") - break - if found_selftests and found_ticks and found_alarm and found_plic_rx: + # A failed self-test is recorded but does not stop the run, so + # the remaining assertions are still reported rather than hidden. + if (found_selftests and not found_selftest_failure + and found_ticks and found_alarm and found_plic_rx): print("\n[+] SUCCESS: startup self-tests, ThreadX ticks, " "LM75 alarm, and PLIC RX interrupt all verified!") break From 9e22643442d01d082fb4f60d2122c7f129fa044f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Desbiens?= Date: Fri, 28 Aug 2026 12:37:57 -0400 Subject: [PATCH 9/9] Made the Renode run deterministic so the PLIC interrupt is actually exercised The RX assertion added in the previous commit never fired in CI. Renode does not read its monitor from stdin (it logs "Monitor available in telnet mode on port 1234"), so the byte the harness wrote was silently discarded and the PLIC path still went untested. Added renode/polarfire_ci.resc, which steps through fixed virtual-time intervals with emulation RunFor, injects the byte itself via WriteChar, and quits. The run is reproducible and terminates on its own rather than depending on wall clock. It duplicates the machine setup instead of including polarfire_demo.resc because Renode expands $ORIGIN only in variable assignment, so an included script cannot be located relative to the file including it; both "include @$ORIGIN/..." and a bare relative include hang the process rather than reporting an error. polarfire_demo.resc is left free-running for interactive use. Verified locally against Renode 1.16.1, both directions: context 1 -> [Console RX] PLIC IRQ 91 handled: byte 'X' ... harness exit 0 context 2 -> no RX line harness exit 1 Both builds print an identical "[+] PASS: PLIC Hart 1 (IRQ 91 prio=1 en=0x08000000 thresh=0 mie=0x800)" self-test line, which is the point: register readback cannot separate a correct context from an incorrect one, and only a delivered interrupt can. Also added timeout-minutes to both CI jobs as a backstop, and corrected the target README, whose expected-output block still showed telemetry strings the demo stopped emitting. Assisted-by: Claude Code (Opus 5) Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/ci.yml | 3 ++ .../POLARFIRE_ICICLE_RENODE/README.md | 36 ++++++++++++--- .../renode/polarfire_ci.resc | 46 +++++++++++++++++++ .../scripts/test_renode.py | 23 ++-------- 4 files changed, 84 insertions(+), 24 deletions(-) create mode 100644 targets/Microchip/POLARFIRE_ICICLE_RENODE/renode/polarfire_ci.resc diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 05fe102e..37d2ee3f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,6 +20,7 @@ jobs: build-riscv-polarfire: name: Build PolarFire SoC Icicle Kit (64-Bit RISC-V) runs-on: ubuntu-24.04 + timeout-minutes: 20 steps: - name: Checkout Repository uses: actions/checkout@v4 @@ -64,6 +65,8 @@ jobs: name: Headless Renode Emulation & Assertion Test needs: build-riscv-polarfire runs-on: ubuntu-24.04 + # Backstop in case Renode itself wedges before the in-script deadline fires. + timeout-minutes: 15 steps: - name: Checkout Repository uses: actions/checkout@v4 diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/README.md b/targets/Microchip/POLARFIRE_ICICLE_RENODE/README.md index 0b3ffc58..4ea1e9f2 100644 --- a/targets/Microchip/POLARFIRE_ICICLE_RENODE/README.md +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/README.md @@ -42,11 +42,28 @@ Inside the Renode monitor: ```renode include @targets/Microchip/POLARFIRE_ICICLE_RENODE/renode/polarfire_demo.resc ``` +This free-runs the machine so the telemetry stream can be watched live. ### Automated Headless Test Runner: ```bash python targets/Microchip/POLARFIRE_ICICLE_RENODE/scripts/test_renode.py ``` +The runner drives `renode/polarfire_ci.resc`, which steps through fixed +virtual-time intervals rather than free-running, injects a byte into MMUART1 to +exercise the PLIC external-interrupt path, and quits on its own. It asserts on +four things and exits non-zero if any of them is missing: + +| Assertion | Covers | +|---|---| +| Startup self-tests all passed | `_sbrk()` bounds, timer catch-up, PLIC configuration | +| ThreadX system tick advancing | CLINT machine timer and `_tx_timer_interrupt` | +| LM75 overtemperature alarm | Queue, event flags, and the analyzer thread | +| PLIC IRQ 91 RX interrupt delivered | MMUART1 -> PLIC -> Hart 1 machine-mode trap path | + +The last of these is the only check that proves the PLIC is programmed for the +right context. Reading the controller's registers back cannot: the machine-mode +context (1) and the supervisor-mode context (2) for Hart 1 both accept the +writes and read back identically, but only the former ever raises `MEIP`. ### Expected Output Stream (`mmuart1` @ 115200 baud): ```text @@ -54,11 +71,18 @@ python targets/Microchip/POLARFIRE_ICICLE_RENODE/scripts/test_renode.py Microchip PolarFire SoC Icicle Kit (Renode Target) 64-Bit RISC-V Industrial LM75 Condition-Monitoring App ==================================================== -[Monitor] ThreadX Ticks: 0 | Telemetry Pipeline Active | Queues OK -[Monitor] ThreadX Ticks: 100 | Telemetry Pipeline Active | Queues OK -[Monitor] ThreadX Ticks: 200 | Telemetry Pipeline Active | Queues OK -[Monitor] ThreadX Ticks: 300 | Telemetry Pipeline Active | Queues OK -[Monitor] ThreadX Ticks: 400 | Telemetry Pipeline Active | Queues OK -[Monitor] ThreadX Ticks: 500 | Telemetry Pipeline Active | Queues OK +[SELF-TEST] Starting Hardware & Runtime Verification... +[+] PASS: _sbrk() valid allocation returned base pointer +[+] PASS: _sbrk() underflow guard rejected with EINVAL +[+] PASS: _sbrk() overflow guard rejected with ENOMEM +[+] PASS: HWTimer catch-up (missed deadline rebased to ..., pending deadline advanced to ...) +[+] PASS: PLIC Hart 1 (IRQ 91 prio=1 en=0x08000000 thresh=0 mie=0x800) +[SELF-TEST] All startup verification tests PASSED! + +[+] PASS: Queue 16-byte structure round-trip verified +[Monitor] Ticks: 0 | Active Runs: Sampler=1, Analyzer=1, Reporter=1 +[Monitor] Ticks: 100 | Active Runs: Sampler=3, Analyzer=3, Reporter=2 +[Console RX] PLIC IRQ 91 handled: byte 'X' received and processed by ThreadX +[Monitor] Ticks: 200 | Active Runs: Sampler=5, Analyzer=5, Reporter=3 [LM75 Sensor] Temperature: OVERTEMP ALARM TRIGGERED (>45.0C) ``` diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/renode/polarfire_ci.resc b/targets/Microchip/POLARFIRE_ICICLE_RENODE/renode/polarfire_ci.resc new file mode 100644 index 00000000..27ba34e3 --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/renode/polarfire_ci.resc @@ -0,0 +1,46 @@ +:name: PolarFire SoC - ThreadX Demo (headless CI run) +:description: Deterministic virtual-time run driven by scripts/test_renode.py. + +# This is a standalone counterpart to polarfire_demo.resc rather than an include +# of it. Renode resolves $ORIGIN only in variable assignment, so an included +# script cannot be located relative to the file including it, and the two run +# differently anyway: the demo script free-runs for interactive use, while this +# one steps through fixed virtual-time intervals so the run is reproducible and +# terminates on its own instead of depending on wall clock. + +Clear + +using sysbus +mach create "PolarFire_SoC_Icicle" +machine LoadPlatformDescription @platforms/cpus/polarfire-soc.repl + +# Under --plain --disable-gui this logs MMUART1 traffic to stdout, which is +# what the assertions in test_renode.py read. +showAnalyzer mmuart1 + +$bin?=$ORIGIN/../build/app/polarfire_icicle_demo.elf + +macro reset +""" + sysbus LoadELF $bin + # Route execution directly to Hart 1 (U54 Core 1) + u54_1 PC `e51 PC` + e51 IsHalted true + u54_2 IsHalted true + u54_3 IsHalted true + u54_4 IsHalted true +""" +runMacro $reset + +# Boot, startup self-tests, and the first system ticks. +emulation RunFor "0.5" + +# Deliver a byte to MMUART1. This is the only step that exercises the PLIC +# external-interrupt path: configuring the controller and reading its registers +# back cannot distinguish the machine-mode context from the supervisor one. +mmuart1 WriteChar 88 + +# Long enough for the simulated LM75 ramp to cross the 45C alarm threshold. +emulation RunFor "7" + +quit diff --git a/targets/Microchip/POLARFIRE_ICICLE_RENODE/scripts/test_renode.py b/targets/Microchip/POLARFIRE_ICICLE_RENODE/scripts/test_renode.py index 23fa9c07..c9731e9b 100644 --- a/targets/Microchip/POLARFIRE_ICICLE_RENODE/scripts/test_renode.py +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/scripts/test_renode.py @@ -21,7 +21,9 @@ import threading import queue -# Byte injected into MMUART1 to exercise the PLIC external-interrupt path. +# Deterministic virtual-time run script. It injects RX_TEST_CHAR into MMUART1 +# itself; Renode's monitor is not on stdin, so injection has to happen there. +RESC_NAME = "polarfire_ci.resc" RX_TEST_CHAR = "X" def find_renode(): @@ -54,7 +56,7 @@ def run_test(test_timeout_mode=False): renode = find_renode() script_dir = os.path.dirname(os.path.abspath(__file__)) target_dir = os.path.dirname(script_dir) - resc_path = os.path.join(target_dir, "renode", "polarfire_demo.resc").replace("\\", "/") + resc_path = os.path.join(target_dir, "renode", RESC_NAME).replace("\\", "/") if test_timeout_mode: print("[*] Running intentional timeout test mode (2.0s deadline against unresponsive wait)...") @@ -62,7 +64,7 @@ def run_test(test_timeout_mode=False): else: print(f"[*] Starting headless Renode test using: {renode}") print(f"[*] Loading script: {resc_path}") - timeout_seconds = 25.0 + timeout_seconds = 120.0 cmd = [ renode, @@ -91,20 +93,8 @@ def run_test(test_timeout_mode=False): found_ticks = False found_alarm = False found_plic_rx = False - injected_char = False start_time = time.time() - def inject_uart_byte(): - """Deliver a byte to MMUART1 so the PLIC external-interrupt path is - actually exercised, rather than only having its registers inspected.""" - try: - proc.stdin.write("sysbus.mmuart1 WriteChar %d\n" % ord(RX_TEST_CHAR)) - proc.stdin.flush() - return True - except Exception as exc: - print("[!] Could not inject UART byte: %s" % exc) - return False - try: while time.time() - start_time < timeout_seconds: try: @@ -120,9 +110,6 @@ def inject_uart_byte(): found_selftests = True if "Ticks:" in line: found_ticks = True - # The kernel is running, so the RX interrupt can be serviced. - if not injected_char: - injected_char = inject_uart_byte() if "OVERTEMP ALARM TRIGGERED" in line: found_alarm = True if "PLIC IRQ 91 handled" in line: