diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..37d2ee3f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,103 @@ +# +# 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 +# + +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 + timeout-minutes: 20 + 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 + env: + XPACK_TARBALL: xpack-riscv-none-elf-gcc-14.2.0-1-linux-x64.tar.gz + XPACK_SHA256: a5eb707595e1424ff4127cc8b21b8b8cb076e6f0070b670485e72b9f74806200 + run: | + 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_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 + 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 + # Backstop in case Renode itself wedges before the in-script deadline fires. + timeout-minutes: 15 + 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 Pinned Portable Renode Emulation Environment + env: + RENODE_VERSION: 1.16.1 + RENODE_SHA256: 1a532d4b5b82de0dd154970c401e0c7b0e498d17304b2cecc007e306c8f9617c + run: | + 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 "${TARBALL}" -C $HOME/renode --strip-components=1 + rm "${TARBALL}" + 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..f38224eb --- /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/licenses/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..46d52324 --- /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/licenses/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..a139b04e --- /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/licenses/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..2686c1f8 --- /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/licenses/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..e0049690 --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,67 @@ +# 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**: 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. + +--- + +## 2. Directory Structure + +```text +samplex/ (repository root) +├── 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 +``` + +--- + +## 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..08da8f2a --- /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/licenses/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..4ea1e9f2 --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/README.md @@ -0,0 +1,88 @@ +# 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 +``` +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 +==================================================== +Microchip PolarFire SoC Icicle Kit (Renode Target) +64-Bit RISC-V Industrial LM75 Condition-Monitoring App +==================================================== +[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/app/CMakeLists.txt b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/CMakeLists.txt new file mode 100644 index 00000000..64605b99 --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/CMakeLists.txt @@ -0,0 +1,62 @@ +# +# 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 +# + +# 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 +) + +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 + 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..e80a0ca6 --- /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/licenses/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..a59f4786 --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/startup/entry.S @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available + * under the terms of the MIT license which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + */ + + .section .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 + + /* 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 */ + 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..8d196500 --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/startup/newlib_stubs.c @@ -0,0 +1,136 @@ +/* + * 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 + */ + +// Some portions generated by Claude Code (Opus 5) + +#include +#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"); + } +} + +/* 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) { + (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..9081ef5a --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/common/startup/tx_initialize_low_level.S @@ -0,0 +1,113 @@ +/* + * 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 + */ + +// Some portions generated by Claude Code (Opus 5) + +#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: + /* 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 + 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 + +.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 + .extern _end + .extern hwtimer_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 | MIE_MEIE) + csrrs zero, mie, t0 + +#ifdef __riscv_flen + li t0, MSTATUS_FS + csrrs zero, mstatus, t0 + fscsr x0 +#endif + + /* 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 hwtimer_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..50e3808a --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/app/main.c @@ -0,0 +1,328 @@ +/* + * 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 + */ + +// Some portions generated by Claude Code (Opus 5) + +#include +#include +#include +#include +#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" + +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 + +/* 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; + uint32_t 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 +#define EVENT_FLAG_UART_RX 0x02 + +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++; + 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) { + 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); + +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[160]; + console_print("[SELF-TEST] Starting Hardware & Runtime Verification...\n"); + + /* 1. _sbrk() Valid allocation test */ + void *p1 = _sbrk(64); + 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); + 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); + selftest_report(p_over == (void *)-1 && errno == ENOMEM, + "_sbrk() overflow guard rejected with ENOMEM"); + + /* 4. HWTimer catch-up clamp. + * + * 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 (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. + * 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_REG(MMUART1_IRQ); + uint32_t thresh = PLIC_HART1_M_THRESHOLD_REG; + uint64_t mie_val; + __asm__ volatile("csrr %0, mie" : "=r"(mie_val)); + + 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), + "[SELF-TEST] %u startup verification test(s) FAILED!\n\n", + s_selftest_failures); + console_print(num_buf); + } +} + +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"); + + /* 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(); + + 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) { + s_sampler_runs++; + data.timestamp = tx_time_get(); + data.temperature_celsius = simulated_temp; + data.reserved = 0x55AA55AA; + + /* 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(DEMO_MS_TO_TICKS(DEMO_SAMPLE_PERIOD_MS)); + } +} + +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) { + s_analyzer_runs++; + 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) { + 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[160]; + ULONG actual_flags; + + while (1) { + s_reporter_runs++; + snprintf(msg_buf, sizeof(msg_buf), + "[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) { + 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(DEMO_MS_TO_TICKS(DEMO_REPORT_PERIOD_MS)); + } +} 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..a2111562 --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/CMakeLists.txt @@ -0,0 +1,27 @@ +# +# 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 +# + +# 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/plic.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..40400d9a --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/board_config.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/licenses/MIT. + * + * SPDX-License-Identifier: MIT + */ + +// Some portions generated by Claude Code (Opus 5) + +#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 */ + +/* 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 */ + +#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..78da1ca1 --- /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/licenses/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..72ab0150 --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/hwtimer.h @@ -0,0 +1,47 @@ +/* + * 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 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)) + +/* 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/include/plic.h b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/plic.h new file mode 100644 index 00000000..8133a828 --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/plic.h @@ -0,0 +1,62 @@ +/* + * 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 + */ + +// Some portions generated by Claude Code (Opus 5) + +#ifndef PLIC_H +#define PLIC_H + +#include + +/* SiFive PLIC Base Address on Microchip PolarFire SoC Icicle Kit */ +#define PLIC_BASE 0x0C000000ULL + +/* 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 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 + PLIC_PRIORITY_OFFSET + ((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 new file mode 100644 index 00000000..9d980492 --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/include/uart.h @@ -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/licenses/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); +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 new file mode 100644 index 00000000..91192bba --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/board.c @@ -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/licenses/MIT. + * + * SPDX-License-Identifier: MIT + */ + +// 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 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 new file mode 100644 index 00000000..293c5a86 --- /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/licenses/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..4695e21b --- /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/licenses/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..a8e3590e --- /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/licenses/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..2f7635a2 --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/hwtimer.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 + */ + +// Some portions generated by Claude Code (Opus 5) + +#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; +} + +uint64_t hwtimer_next_cmp(uint64_t current_cmp, uint64_t now) { + uint64_t next_cmp = current_cmp + TICK_CYCLES; + + /* Deadline already missed: rebase onto now rather than chasing it. */ + if (next_cmp <= now) { + next_cmp = now + TICK_CYCLES; + } + + 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/lib/bsp/src/plic.c b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/plic.c new file mode 100644 index 00000000..f4f5fd7d --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/plic.c @@ -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 + */ + +// Some portions generated by Claude Code (Opus 5) + +#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 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; + + /* 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 new file mode 100644 index 00000000..00795f5a --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/trap.c @@ -0,0 +1,68 @@ +/* + * 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" +#include "plic.h" +#include "uart.h" + +extern void _tx_timer_interrupt(void); +extern void console_rx_isr_callback(char c); + +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; + } 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; + } + } + + /* 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..57767aa0 --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/lib/bsp/src/uart.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available + * under the terms of the MIT license which is available at + * https://opensource.org/licenses/MIT. + * + * 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; + /* 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) { + /* 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_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/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..0d982533 --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/renode/polarfire_demo.robot @@ -0,0 +1,25 @@ +*** 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 [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/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..c9731e9b --- /dev/null +++ b/targets/Microchip/POLARFIRE_ICICLE_RENODE/scripts/test_renode.py @@ -0,0 +1,163 @@ +#!/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/licenses/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 + +# 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(): + # 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(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", RESC_NAME).replace("\\", "/") + + 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 = 120.0 + + cmd = [ + renode, + "--plain", + "--disable-gui", + "--port", "-1", + "-e", f"include @{resc_path}" + ] + + proc = subprocess.Popen( + cmd, + stdin=subprocess.PIPE, + 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_selftests = False + found_selftest_failure = False + found_ticks = False + found_alarm = False + found_plic_rx = False + start_time = time.time() + + try: + while time.time() - start_time < timeout_seconds: + try: + line = output_q.get(timeout=0.1) + output_lines.append(line) + print(line, end="") + 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 + if "OVERTEMP ALARM TRIGGERED" in line: + found_alarm = True + if "PLIC IRQ 91 handled" in line: + found_plic_rx = True + + # 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 + 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 test_timeout_mode: + elapsed = time.time() - start_time + print(f"\n[+] SUCCESS: Intentional timeout triggered after {elapsed:.2f}s " + f"and terminated child process cleanly.") + sys.exit(0) + + 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 + run_test(test_timeout_mode=timeout_test) diff --git a/templates/target/CMakeLists.txt b/templates/target/CMakeLists.txt new file mode 100644 index 00000000..13ac5b52 --- /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/licenses/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..1fad0eaa --- /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/licenses/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..d668e148 --- /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/licenses/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..de096665 --- /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/licenses/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..6fc2cc8e --- /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/licenses/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..e2d04304 --- /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/licenses/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..23dae914 --- /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/licenses/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..890ab358 --- /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/licenses/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..70b0f76f --- /dev/null +++ b/templates/target/lib/bsp/src/newlib_stubs.c @@ -0,0 +1,133 @@ +/* + * 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 + */ + +// Some portions generated by Claude Code (Opus 5) + +#ifdef __GNUC__ + +#include +#include +#include +#include +#include +#include "board_config.h" +#include "bsp/console.h" + +/* 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(ptrdiff_t incr) +{ + static char* heap = NULL; + char* prev_heap; + + if (heap == NULL) + { + 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; + + 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 "=========================================="