From f10fbbffab1cfe4b25d2a2c1963046aee0ba41c0 Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Mon, 9 Mar 2026 15:49:15 +0100 Subject: [PATCH 01/79] adds small changes to make tx_port for non-smp consistent with the one that implements smp --- port/threadx/inc/tx_port.h | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/port/threadx/inc/tx_port.h b/port/threadx/inc/tx_port.h index c0ac1cd..1e57546 100644 --- a/port/threadx/inc/tx_port.h +++ b/port/threadx/inc/tx_port.h @@ -72,11 +72,6 @@ #else /*not __ASSEMBLER__ */ -/* Include for memset. */ -#include -/* include for strtoul*/ -#include - /* Determine if the optional ThreadX user define file should be used. */ #ifdef TX_INCLUDE_USER_DEFINE_FILE @@ -90,6 +85,9 @@ /* Define compiler library include files. */ +#include +#include + /* Define ThreadX basic types for this port. */ #define VOID void -- 2.49.1 From e5020b78de9409e72d1c8e13a8cdaf3fb74d7878 Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Mon, 9 Mar 2026 19:37:34 +0100 Subject: [PATCH 02/79] WIP: build system --- port/threadx_smp/CMakeLists.txt | 36 +++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 port/threadx_smp/CMakeLists.txt diff --git a/port/threadx_smp/CMakeLists.txt b/port/threadx_smp/CMakeLists.txt new file mode 100644 index 0000000..3927344 --- /dev/null +++ b/port/threadx_smp/CMakeLists.txt @@ -0,0 +1,36 @@ +set(THREADX_COMMON_SMP_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../third-party/threadx/common_smp) +if(NOT EXISTS "${THREADX_COMMON_SMP_DIR}") + message(FATAL_ERROR "could not find ThreadX SMP sources, is the submodule checked out?") +endif() + +set(THREADX_SMP_CUSTOM_INC ${CMAKE_CURRENT_SOURCE_DIR}/inc ${CMAKE_CURRENT_SOURCE_DIR}/../moonlight) +set(THREADX_SMP_CUSTOM_SRC + src/tx_thread_smp_core_preempt.c + src/tx_initialize_low_level.S +) + +file(GLOB THREADX_SMP_SOURCES ${THREADX_COMMON_SMP_DIR}/src/*.c) +add_library(threadx_smp STATIC) +target_sources(threadx_smp PRIVATE + ${THREADX_SMP_SOURCES} + ${THREADX_SMP_CUSTOM_SRC} +) +target_include_directories(threadx_smp PUBLIC + ${THREADX_COMMON_SMP_DIR}/inc + ${THREADX_SMP_CUSTOM_INC} +) +target_compile_definitions(threadx_smp PRIVATE -DTX_QUEUE_MESSAGE_MAX_SIZE=16) #This is addressed in PR #503 + + +#Definition smp_demo +project(smp_demo LANGUAGES C ASM) +cmake_minimum_required(VERSION 3.24) +add_executable(smp_demo) +target_include_directories(smp_demo PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/../moonlight +) +target_link_libraries(smp_demo PRIVATE threadx_smp) + +target_sources(smp_demo PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../src/thread_demo/main.c) + +#cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE=$PWD/../../cmake/rv32gc_gnu.cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=YES \ No newline at end of file -- 2.49.1 From 70bbde8502ffe4a97284040417082038293372a4 Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Tue, 10 Mar 2026 20:57:47 +0100 Subject: [PATCH 03/79] adds initial smp scheduler --- port/threadx_smp/src/tx_thread_schedule.S | 337 ++++++++++++++++++++++ 1 file changed, 337 insertions(+) create mode 100644 port/threadx_smp/src/tx_thread_schedule.S diff --git a/port/threadx_smp/src/tx_thread_schedule.S b/port/threadx_smp/src/tx_thread_schedule.S new file mode 100644 index 0000000..eb3fa53 --- /dev/null +++ b/port/threadx_smp/src/tx_thread_schedule.S @@ -0,0 +1,337 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * 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 Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#include "tx_port.h" + + .section .text +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule RISC-V64/GNU */ +/* 6.2.1 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 03-08-2023 Scott Larson Initial Version 6.2.1 */ +/* */ +/**************************************************************************/ +/* VOID _tx_thread_schedule(VOID) +{ */ + .global _tx_thread_schedule +_tx_thread_schedule: + + /* Enable interrupts. */ + csrsi mstatus, 0x08 // Enable interrupts + csrr t4, mhartid // Pickup the current hart ID + slli t4, t4, LOG_REGBYTES // Build per-hart byte offset + + /* Wait for a thread to execute. */ + /* do + { */ + + la t0, _tx_thread_execute_ptr // Pickup address of execute ptr + add t0, t0, t4 // Select this hart's execute slot +_tx_thread_schedule_loop: +#ifdef TX_ENABLE_WFI + csrci mstatus, 0x08 // Lockout interrupts + LOAD t1, 0(t0) // Pickup next thread to execute + bnez t1, _tx_thread_schedule_thread // If non-NULL, continue + csrsi mstatus, 0x08 // Enable interrupts + wfi // Wait for interrupt + j _tx_thread_schedule_loop // Keep looking for a thread +#else + csrci mstatus, 0x08 // Lockout interrupts + LOAD t1, 0(t0) // Pickup next thread to execute + bnez t1, _tx_thread_schedule_thread // If non-NULL, continue + csrsi mstatus, 0x08 // Enable interrupts + j _tx_thread_schedule_loop // Keep looking for a thread +#endif +_tx_thread_schedule_thread: + + /* Atomically claim the thread's ready token so only one hart can + dispatch this TCB at a time. */ + addi t2, t1, TX_THREAD_SMP_LOCK_READY_BIT_OFFSET // Pickup lock/ready-bit address + amoswap.w.aq t3, x0, (t2) // Clear it and fetch prior state + beqz t3, _tx_thread_schedule // If not ready, retry scheduling + + /* } + while(_tx_thread_execute_ptr == TX_NULL); */ + + /* Publish the current thread pointer for this hart, then re-check the + execute slot to avoid missing a concurrent execute-pointer update. */ + + la t5, _tx_thread_current_ptr // Pickup current thread pointer address + add t5, t5, t4 // Select this hart's current slot + STORE t1, 0(t5) // Set current thread pointer + fence rw, rw // Publish current thread pointer before re-check + LOAD t6, 0(t0) // Reload execute pointer for this hart + beq t1, t6, _execute_pointer_did_not_change // If unchanged, continue + + /* Another core changed the execute slot after this hart claimed the + thread but before current-thread publication completed. Roll back + and restart so the new selection is not missed. */ + STORE x0, 0(t5) // Clear current thread pointer + li t3, 1 // Rebuild ready token + amoswap.w.rl x0, t3, (t2) // Restore ready token with release ordering + j _tx_thread_schedule_loop // Restart scheduling + +_execute_pointer_did_not_change: + + /* Increment the run count for this thread. */ + /* _tx_thread_current_ptr -> tx_thread_run_count++; */ + + LOAD t2, 1*REGBYTES(t1) // Pickup run count + LOAD t3, 6*REGBYTES(t1) // Pickup time slice value + addi t2, t2, 1 // Increment run count + STORE t2, 1*REGBYTES(t1) // Store new run count + + /* Setup time-slice for this hart, if present. */ + + la t2, _tx_timer_time_slice // Pickup time-slice variable address + add t2, t2, t4 // Select this hart's time-slice slot + + /* Switch to the thread's stack. */ + /* SP = _tx_thread_execute_ptr -> tx_thread_stack_ptr; */ + + LOAD sp, 2*REGBYTES(t1) // Switch to thread's stack + STORE t3, 0(t2) // Store new time-slice + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + call _tx_execution_thread_enter // Call the thread execution enter function +#endif + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LOAD t2, 0(sp) // Pickup stack type + beqz t2, _tx_thread_synch_return // If 0, solicited thread return + + /* Determine if floating point registers need to be recovered. */ + +#if defined(__riscv_float_abi_single) + flw f0, 31*REGBYTES(sp) // Recover ft0 + flw f1, 32*REGBYTES(sp) // Recover ft1 + flw f2, 33*REGBYTES(sp) // Recover ft2 + flw f3, 34*REGBYTES(sp) // Recover ft3 + flw f4, 35*REGBYTES(sp) // Recover ft4 + flw f5, 36*REGBYTES(sp) // Recover ft5 + flw f6, 37*REGBYTES(sp) // Recover ft6 + flw f7, 38*REGBYTES(sp) // Recover ft7 + flw f8, 39*REGBYTES(sp) // Recover fs0 + flw f9, 40*REGBYTES(sp) // Recover fs1 + flw f10,41*REGBYTES(sp) // Recover fa0 + flw f11,42*REGBYTES(sp) // Recover fa1 + flw f12,43*REGBYTES(sp) // Recover fa2 + flw f13,44*REGBYTES(sp) // Recover fa3 + flw f14,45*REGBYTES(sp) // Recover fa4 + flw f15,46*REGBYTES(sp) // Recover fa5 + flw f16,47*REGBYTES(sp) // Recover fa6 + flw f17,48*REGBYTES(sp) // Recover fa7 + flw f18,49*REGBYTES(sp) // Recover fs2 + flw f19,50*REGBYTES(sp) // Recover fs3 + flw f20,51*REGBYTES(sp) // Recover fs4 + flw f21,52*REGBYTES(sp) // Recover fs5 + flw f22,53*REGBYTES(sp) // Recover fs6 + flw f23,54*REGBYTES(sp) // Recover fs7 + flw f24,55*REGBYTES(sp) // Recover fs8 + flw f25,56*REGBYTES(sp) // Recover fs9 + flw f26,57*REGBYTES(sp) // Recover fs10 + flw f27,58*REGBYTES(sp) // Recover fs11 + flw f28,59*REGBYTES(sp) // Recover ft8 + flw f29,60*REGBYTES(sp) // Recover ft9 + flw f30,61*REGBYTES(sp) // Recover ft10 + flw f31,62*REGBYTES(sp) // Recover ft11 + LOAD t0, 63*REGBYTES(sp) // Recover fcsr + csrw fcsr, t0 // +#elif defined(__riscv_float_abi_double) + fld f0, 31*REGBYTES(sp) // Recover ft0 + fld f1, 32*REGBYTES(sp) // Recover ft1 + fld f2, 33*REGBYTES(sp) // Recover ft2 + fld f3, 34*REGBYTES(sp) // Recover ft3 + fld f4, 35*REGBYTES(sp) // Recover ft4 + fld f5, 36*REGBYTES(sp) // Recover ft5 + fld f6, 37*REGBYTES(sp) // Recover ft6 + fld f7, 38*REGBYTES(sp) // Recover ft7 + fld f8, 39*REGBYTES(sp) // Recover fs0 + fld f9, 40*REGBYTES(sp) // Recover fs1 + fld f10,41*REGBYTES(sp) // Recover fa0 + fld f11,42*REGBYTES(sp) // Recover fa1 + fld f12,43*REGBYTES(sp) // Recover fa2 + fld f13,44*REGBYTES(sp) // Recover fa3 + fld f14,45*REGBYTES(sp) // Recover fa4 + fld f15,46*REGBYTES(sp) // Recover fa5 + fld f16,47*REGBYTES(sp) // Recover fa6 + fld f17,48*REGBYTES(sp) // Recover fa7 + fld f18,49*REGBYTES(sp) // Recover fs2 + fld f19,50*REGBYTES(sp) // Recover fs3 + fld f20,51*REGBYTES(sp) // Recover fs4 + fld f21,52*REGBYTES(sp) // Recover fs5 + fld f22,53*REGBYTES(sp) // Recover fs6 + fld f23,54*REGBYTES(sp) // Recover fs7 + fld f24,55*REGBYTES(sp) // Recover fs8 + fld f25,56*REGBYTES(sp) // Recover fs9 + fld f26,57*REGBYTES(sp) // Recover fs10 + fld f27,58*REGBYTES(sp) // Recover fs11 + fld f28,59*REGBYTES(sp) // Recover ft8 + fld f29,60*REGBYTES(sp) // Recover ft9 + fld f30,61*REGBYTES(sp) // Recover ft10 + fld f31,62*REGBYTES(sp) // Recover ft11 + LOAD t0, 63*REGBYTES(sp) // Recover fcsr +#endif + + /* Recover standard registers. */ + + LOAD t0, 30*REGBYTES(sp) // Recover mepc + csrw mepc, t0 // Store mepc + li t0, 0x1880 // Prepare MPIP +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + li t1, 1<<13 + or t0, t1, t0 +#endif + csrw mstatus, t0 // Enable MPIP + + LOAD x1, 28*REGBYTES(sp) // Recover RA + LOAD x5, 19*REGBYTES(sp) // Recover t0 + LOAD x6, 18*REGBYTES(sp) // Recover t1 + LOAD x7, 17*REGBYTES(sp) // Recover t2 + LOAD x8, 12*REGBYTES(sp) // Recover s0 + LOAD x9, 11*REGBYTES(sp) // Recover s1 + LOAD x10, 27*REGBYTES(sp) // Recover a0 + LOAD x11, 26*REGBYTES(sp) // Recover a1 + LOAD x12, 25*REGBYTES(sp) // Recover a2 + LOAD x13, 24*REGBYTES(sp) // Recover a3 + LOAD x14, 23*REGBYTES(sp) // Recover a4 + LOAD x15, 22*REGBYTES(sp) // Recover a5 + LOAD x16, 21*REGBYTES(sp) // Recover a6 + LOAD x17, 20*REGBYTES(sp) // Recover a7 + LOAD x18, 10*REGBYTES(sp) // Recover s2 + LOAD x19, 9*REGBYTES(sp) // Recover s3 + LOAD x20, 8*REGBYTES(sp) // Recover s4 + LOAD x21, 7*REGBYTES(sp) // Recover s5 + LOAD x22, 6*REGBYTES(sp) // Recover s6 + LOAD x23, 5*REGBYTES(sp) // Recover s7 + LOAD x24, 4*REGBYTES(sp) // Recover s8 + LOAD x25, 3*REGBYTES(sp) // Recover s9 + LOAD x26, 2*REGBYTES(sp) // Recover s10 + LOAD x27, 1*REGBYTES(sp) // Recover s11 + LOAD x28, 16*REGBYTES(sp) // Recover t3 + LOAD x29, 15*REGBYTES(sp) // Recover t4 + LOAD x30, 14*REGBYTES(sp) // Recover t5 + LOAD x31, 13*REGBYTES(sp) // Recover t6 + +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + addi sp, sp, 65*REGBYTES // Recover stack frame - with floating point registers +#else + addi sp, sp, 32*REGBYTES // Recover stack frame - without floating point registers +#endif + mret // Return to point of interrupt + +_tx_thread_synch_return: + +#if defined(__riscv_float_abi_single) + flw f8, 15*REGBYTES(sp) // Recover fs0 + flw f9, 16*REGBYTES(sp) // Recover fs1 + flw f18,17*REGBYTES(sp) // Recover fs2 + flw f19,18*REGBYTES(sp) // Recover fs3 + flw f20,19*REGBYTES(sp) // Recover fs4 + flw f21,20*REGBYTES(sp) // Recover fs5 + flw f22,21*REGBYTES(sp) // Recover fs6 + flw f23,22*REGBYTES(sp) // Recover fs7 + flw f24,23*REGBYTES(sp) // Recover fs8 + flw f25,24*REGBYTES(sp) // Recover fs9 + flw f26,25*REGBYTES(sp) // Recover fs10 + flw f27,26*REGBYTES(sp) // Recover fs11 + LOAD t0, 27*REGBYTES(sp) // Recover fcsr + csrw fcsr, t0 // +#elif defined(__riscv_float_abi_double) + fld f8, 15*REGBYTES(sp) // Recover fs0 + fld f9, 16*REGBYTES(sp) // Recover fs1 + fld f18,17*REGBYTES(sp) // Recover fs2 + fld f19,18*REGBYTES(sp) // Recover fs3 + fld f20,19*REGBYTES(sp) // Recover fs4 + fld f21,20*REGBYTES(sp) // Recover fs5 + fld f22,21*REGBYTES(sp) // Recover fs6 + fld f23,22*REGBYTES(sp) // Recover fs7 + fld f24,23*REGBYTES(sp) // Recover fs8 + fld f25,24*REGBYTES(sp) // Recover fs9 + fld f26,25*REGBYTES(sp) // Recover fs10 + fld f27,26*REGBYTES(sp) // Recover fs11 + LOAD t0, 27*REGBYTES(sp) // Recover fcsr + csrw fcsr, t0 // +#endif + + /* Recover standard preserved registers. */ + /* Recover standard registers. */ + + LOAD x1, 13*REGBYTES(sp) // Recover RA + LOAD x8, 12*REGBYTES(sp) // Recover s0 + LOAD x9, 11*REGBYTES(sp) // Recover s1 + LOAD x18, 10*REGBYTES(sp) // Recover s2 + LOAD x19, 9*REGBYTES(sp) // Recover s3 + LOAD x20, 8*REGBYTES(sp) // Recover s4 + LOAD x21, 7*REGBYTES(sp) // Recover s5 + LOAD x22, 6*REGBYTES(sp) // Recover s6 + LOAD x23, 5*REGBYTES(sp) // Recover s7 + LOAD x24, 4*REGBYTES(sp) // Recover s8 + LOAD x25, 3*REGBYTES(sp) // Recover s9 + LOAD x26, 2*REGBYTES(sp) // Recover s10 + LOAD x27, 1*REGBYTES(sp) // Recover s11 + LOAD t0, 14*REGBYTES(sp) // Recover mstatus + csrw mstatus, t0 // Store mstatus, enables interrupt +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + addi sp, sp, 29*REGBYTES // Recover stack frame +#else + addi sp, sp, 16*REGBYTES // Recover stack frame +#endif + ret // Return to thread + +/* } */ -- 2.49.1 From 2e955b934236b4be7f7802d9c3ebb3edbaab493a Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Tue, 10 Mar 2026 20:59:58 +0100 Subject: [PATCH 04/79] adds the generation of the offsetof defines to CMakeLists.txt As the offsets into the TX_THREAD are needed to access certain fields and the members for smp are quite difficult to obtain this was the safest option --- port/threadx_smp/CMakeLists.txt | 40 ++++++++++++++++++- .../cmake/GenerateAsmOffsets.cmake | 22 ++++++++++ port/threadx_smp/src/tx_asm_offsets.c | 14 +++++++ 3 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 port/threadx_smp/cmake/GenerateAsmOffsets.cmake create mode 100644 port/threadx_smp/src/tx_asm_offsets.c diff --git a/port/threadx_smp/CMakeLists.txt b/port/threadx_smp/CMakeLists.txt index 3927344..87419a1 100644 --- a/port/threadx_smp/CMakeLists.txt +++ b/port/threadx_smp/CMakeLists.txt @@ -3,11 +3,19 @@ if(NOT EXISTS "${THREADX_COMMON_SMP_DIR}") message(FATAL_ERROR "could not find ThreadX SMP sources, is the submodule checked out?") endif() -set(THREADX_SMP_CUSTOM_INC ${CMAKE_CURRENT_SOURCE_DIR}/inc ${CMAKE_CURRENT_SOURCE_DIR}/../moonlight) +set(THREADX_SMP_CUSTOM_INC + ${CMAKE_CURRENT_SOURCE_DIR}/inc + ${CMAKE_CURRENT_SOURCE_DIR}/../moonlight +) set(THREADX_SMP_CUSTOM_SRC src/tx_thread_smp_core_preempt.c src/tx_initialize_low_level.S + src/tx_thread_schedule.S ) +set(THREADX_SMP_GENERATED_INC_DIR ${CMAKE_CURRENT_BINARY_DIR}/generated) +set(THREADX_SMP_OFFSET_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/src/tx_asm_offsets.c) +set(THREADX_SMP_OFFSET_ASM ${THREADX_SMP_GENERATED_INC_DIR}/tx_asm_offsets.s) +set(THREADX_SMP_OFFSET_INC ${THREADX_SMP_GENERATED_INC_DIR}/tx_asm_offsets.inc) file(GLOB THREADX_SMP_SOURCES ${THREADX_COMMON_SMP_DIR}/src/*.c) add_library(threadx_smp STATIC) @@ -19,8 +27,36 @@ target_include_directories(threadx_smp PUBLIC ${THREADX_COMMON_SMP_DIR}/inc ${THREADX_SMP_CUSTOM_INC} ) +target_include_directories(threadx_smp PRIVATE + ${THREADX_SMP_GENERATED_INC_DIR} +) target_compile_definitions(threadx_smp PRIVATE -DTX_QUEUE_MESSAGE_MAX_SIZE=16) #This is addressed in PR #503 +add_custom_command( + OUTPUT ${THREADX_SMP_OFFSET_INC} + COMMAND ${CMAKE_COMMAND} -E make_directory ${THREADX_SMP_GENERATED_INC_DIR} + COMMAND ${CMAKE_C_COMPILER} + $<$>:$,PREPEND,-I>> + $<$>:$,PREPEND,-D>> + -S + -o ${THREADX_SMP_OFFSET_ASM} + ${THREADX_SMP_OFFSET_SOURCE} + COMMAND ${CMAKE_COMMAND} + -DINPUT=${THREADX_SMP_OFFSET_ASM} + -DOUTPUT=${THREADX_SMP_OFFSET_INC} + -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/GenerateAsmOffsets.cmake + DEPENDS + ${THREADX_SMP_OFFSET_SOURCE} + ${CMAKE_CURRENT_SOURCE_DIR}/cmake/GenerateAsmOffsets.cmake + ${CMAKE_CURRENT_SOURCE_DIR}/inc/tx_port.h + ${THREADX_COMMON_SMP_DIR}/inc/tx_api.h + COMMAND_EXPAND_LISTS + VERBATIM +) + +add_custom_target(threadx_smp_offsets DEPENDS ${THREADX_SMP_OFFSET_INC}) +add_dependencies(threadx_smp threadx_smp_offsets) + #Definition smp_demo project(smp_demo LANGUAGES C ASM) @@ -33,4 +69,4 @@ target_link_libraries(smp_demo PRIVATE threadx_smp) target_sources(smp_demo PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../src/thread_demo/main.c) -#cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE=$PWD/../../cmake/rv32gc_gnu.cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=YES \ No newline at end of file +#cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE=$PWD/../../cmake/rv32gc_gnu.cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=YES diff --git a/port/threadx_smp/cmake/GenerateAsmOffsets.cmake b/port/threadx_smp/cmake/GenerateAsmOffsets.cmake new file mode 100644 index 0000000..9657e20 --- /dev/null +++ b/port/threadx_smp/cmake/GenerateAsmOffsets.cmake @@ -0,0 +1,22 @@ +if(NOT DEFINED INPUT) + message(FATAL_ERROR "INPUT is required") +endif() + +if(NOT DEFINED OUTPUT) + message(FATAL_ERROR "OUTPUT is required") +endif() + +file(STRINGS "${INPUT}" OFFSET_LINES REGEX "->") + +set(OFFSET_CONTENT "/* Generated by GenerateAsmOffsets.cmake. */\n") + +foreach(LINE IN LISTS OFFSET_LINES) + string(REGEX MATCH "->([A-Za-z0-9_]+)[^0-9-]*(-?[0-9]+)" _ "${LINE}") + if(NOT CMAKE_MATCH_1) + continue() + endif() + + string(APPEND OFFSET_CONTENT "#define ${CMAKE_MATCH_1} ${CMAKE_MATCH_2}\n") +endforeach() + +file(WRITE "${OUTPUT}" "${OFFSET_CONTENT}") diff --git a/port/threadx_smp/src/tx_asm_offsets.c b/port/threadx_smp/src/tx_asm_offsets.c new file mode 100644 index 0000000..1ec59fc --- /dev/null +++ b/port/threadx_smp/src/tx_asm_offsets.c @@ -0,0 +1,14 @@ +#include + +#include "tx_api.h" + + +#define TX_ASM_OFFSET(symbol, value) __asm__ volatile("\n.ascii \"->" #symbol " %c0\\n\"" : : "i" (value)) + + +void tx_asm_offsets_generate(void) +{ + TX_ASM_OFFSET(TX_THREAD_STACK_END_OFFSET, offsetof(TX_THREAD, tx_thread_stack_end)); + TX_ASM_OFFSET(TX_THREAD_TIME_SLICE_OFFSET, offsetof(TX_THREAD, tx_thread_time_slice)); + TX_ASM_OFFSET(TX_THREAD_SMP_LOCK_READY_BIT_OFFSET, offsetof(TX_THREAD, tx_thread_smp_lock_ready_bit)); +} -- 2.49.1 From f83e96fbcd934c54309ff9b5379fe67cd4f7aa84 Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Tue, 10 Mar 2026 21:01:01 +0100 Subject: [PATCH 05/79] adds initial low_level_init --- .../threadx_smp/src/tx_initialize_low_level.S | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 port/threadx_smp/src/tx_initialize_low_level.S diff --git a/port/threadx_smp/src/tx_initialize_low_level.S b/port/threadx_smp/src/tx_initialize_low_level.S new file mode 100644 index 0000000..0602ea9 --- /dev/null +++ b/port/threadx_smp/src/tx_initialize_low_level.S @@ -0,0 +1,84 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * 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 "csr.h" +#include "tx_port.h" + .section .text +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level RISC-V64/GNU */ +/* 6.2.1 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for any low-level processor */ +/* initialization, including setting up interrupt vectors, setting */ +/* up a periodic timer interrupt source, saving the system stack */ +/* pointer for use in ISR processing later, and finding the first */ +/* available RAM memory address for tx_application_define. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 03-08-2023 Scott Larson Initial Version 6.2.1 */ +/* */ +/**************************************************************************/ +/* VOID _tx_initialize_low_level(VOID) +*/ + .global _tx_initialize_low_level + .weak _tx_initialize_low_level + .extern __heap_start + .extern board_init +_tx_initialize_low_level: + STORE sp, _tx_thread_system_stack_ptr, t0 // Save system stack pointer + + la t0, __heap_start // Pickup first free address + STORE t0, _tx_initialize_unused_memory, t1 // Save unused memory address + li t0, MSTATUS_MIE + csrrc zero, mstatus, t0 // clear MSTATUS_MIE bit + li t0, (MSTATUS_MPP_M | MSTATUS_MPIE ) + csrrs zero, mstatus, t0 // set MSTATUS_MPP, MPIE bit + li t0, (MIE_MTIE | MIE_MSIE | MIE_MEIE) + csrrs zero, mie, t0 // set mie +#ifdef __riscv_flen + li t0, MSTATUS_FS + csrrs zero, mstatus, t0 // set MSTATUS_FS bit to open f/d isa in riscv + fscsr x0 +#endif + addi sp, sp, -8 + STORE ra, 0(sp) + call board_init + LOAD ra, 0(sp) + addi sp, sp, 8 + la t0, trap_entry + csrw mtvec, t0 + ret \ No newline at end of file -- 2.49.1 From 744773848c83ea4cbf109b6876754a593959adda Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Tue, 10 Mar 2026 21:02:16 +0100 Subject: [PATCH 06/79] adds initial smp tx_port --- port/threadx_smp/inc/tx_port.h | 332 +++++++++++++++++++++++++++++++++ 1 file changed, 332 insertions(+) create mode 100644 port/threadx_smp/inc/tx_port.h diff --git a/port/threadx_smp/inc/tx_port.h b/port/threadx_smp/inc/tx_port.h new file mode 100644 index 0000000..c6e5673 --- /dev/null +++ b/port/threadx_smp/inc/tx_port.h @@ -0,0 +1,332 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * 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 Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A-SMP */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + +#ifdef __ASSEMBLER__ + +#if __riscv_xlen == 64 +#define SLL32 sllw +#define STORE sd +#define LOAD ld +#define LWU lwu +#define LOG_REGBYTES 3 +#else +#define SLL32 sll +#define STORE sw +#define LOAD lw +#define LWU lw +#define LOG_REGBYTES 2 +#endif +#define REGBYTES (1 << LOG_REGBYTES) + +#include "tx_asm_offsets.inc" + +#else /*not __ASSEMBLER__ */ + +/************* Define ThreadX SMP constants. *************/ + +/* Define the ThreadX SMP maximum number of cores. */ + +#ifndef TX_THREAD_SMP_MAX_CORES +#define TX_THREAD_SMP_MAX_CORES 4 +#endif + +/* Define the ThreadX SMP core mask. */ + +#ifndef TX_THREAD_SMP_CORE_MASK +#define TX_THREAD_SMP_CORE_MASK 0xF /* Where bit 0 represents Core 0, bit 1 represents Core 1, etc. */ +#endif + +/* Define INLINE_DECLARE */ + +#define INLINE_DECLARE inline + +/* Define ThreadX SMP initialization macro. */ + +#define TX_PORT_SPECIFIC_PRE_INITIALIZATION + +/* Define ThreadX SMP pre-scheduler initialization. */ + +#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION + +/* Enable the inter-core interrupt logic. */ + +#define TX_THREAD_SMP_INTER_CORE_INTERRUPT + +/* Determine if there is customer-specific wakeup logic needed. */ + +#ifdef TX_THREAD_SMP_WAKEUP_LOGIC + +/* Include customer-specific wakeup code. */ + +#include "tx_thread_smp_core_wakeup.h" +#else + +#ifdef TX_THREAD_SMP_DEFAULT_WAKEUP_LOGIC + +/* Default wakeup code. */ +#define TX_THREAD_SMP_WAKEUP_LOGIC +#define TX_THREAD_SMP_WAKEUP(i) _tx_thread_smp_core_preempt(i) +#endif +#endif + +/* Ensure that the in-line resume/suspend define is not allowed. */ + +#ifdef TX_INLINE_THREAD_RESUME_SUSPEND +#undef TX_INLINE_THREAD_RESUME_SUSPEND +#endif + +/************* End ThreadX SMP constants. *************/ + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + +/* Define compiler library include files. */ + +#include +#include + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 1024 /* Minimum stack size for this port */ +#endif + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 1024 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + +/* Define various constants for the ThreadX RISC-V port. */ + +#define TX_INT_DISABLE 0x00000000 /* Disable interrupts value */ +#define TX_INT_ENABLE 0x00000008 /* Enable interrupt value */ + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE ++_tx_trace_simulated_time +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS 0 + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#define TX_INLINE_INITIALIZATION + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 +#define TX_THREAD_EXTENSION_3 + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + +/************* Define ThreadX SMP data types and function prototypes. *************/ + +struct TX_THREAD_STRUCT; + +/* Define the ThreadX SMP protection structure. */ + +typedef struct TX_THREAD_SMP_PROTECT_STRUCT +{ + ULONG tx_thread_smp_protect_in_force; + ULONG tx_thread_smp_protect_core; + ULONG tx_thread_smp_protect_count; + ULONG tx_thread_smp_protect_pad_0; + ULONG tx_thread_smp_protect_pad_1; + ULONG tx_thread_smp_protect_pad_2; + ULONG tx_thread_smp_protect_pad_3; +} TX_THREAD_SMP_PROTECT; + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#define TX_INTERRUPT_SAVE_AREA ULONG64 interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_smp_protect(); +#define TX_RESTORE _tx_thread_smp_unprotect(interrupt_save); + +/************* End ThreadX SMP data type and function prototype definitions. *************/ + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = "Copyright (c) 2024 Microsoft Corporation. * ThreadX RISC-V64/GNU Version 6.4.2 *"; +#else +extern CHAR _tx_version_id[]; +#endif + +#endif /*not __ASSEMBLER__ */ +#endif -- 2.49.1 From a75db78425c0e864b5ae897aefbe85024952b744 Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Tue, 10 Mar 2026 21:02:29 +0100 Subject: [PATCH 07/79] adds ipi functionality --- port/moonlight/aclint_ipi.h | 12 ++++++++++++ port/threadx_smp/src/tx_thread_smp_core_preempt.c | 6 ++++++ 2 files changed, 18 insertions(+) create mode 100644 port/moonlight/aclint_ipi.h create mode 100644 port/threadx_smp/src/tx_thread_smp_core_preempt.c diff --git a/port/moonlight/aclint_ipi.h b/port/moonlight/aclint_ipi.h new file mode 100644 index 0000000..bed9eb6 --- /dev/null +++ b/port/moonlight/aclint_ipi.h @@ -0,0 +1,12 @@ +#ifndef _DEVICES_ACLINT_IPI +#define _DEVICES_ACLINT_IPI + +#include "gen/aclint.h" +#include "platform.h" +#include + +static void send_ipi(uint32_t target_core) +{ + set_aclint_msip(aclint, target_core, 1); +} +#endif /* _DEVICES_ACLINT_IPI */ diff --git a/port/threadx_smp/src/tx_thread_smp_core_preempt.c b/port/threadx_smp/src/tx_thread_smp_core_preempt.c new file mode 100644 index 0000000..cf11396 --- /dev/null +++ b/port/threadx_smp/src/tx_thread_smp_core_preempt.c @@ -0,0 +1,6 @@ +#include +#include +void _tx_thread_smp_core_preempt(UINT target_core) +{ + send_ipi(target_core); +} \ No newline at end of file -- 2.49.1 From 1117527a02f71b5fc4f364dcf7e996f35a76b66d Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Wed, 11 Mar 2026 19:13:10 +0100 Subject: [PATCH 08/79] changes so that cmake builds for now --- port/threadx_smp/CMakeLists.txt | 32 +++++++++++---- port/threadx_smp/cmake/BuildAsmOffsets.cmake | 41 ++++++++++++++++++++ 2 files changed, 66 insertions(+), 7 deletions(-) create mode 100644 port/threadx_smp/cmake/BuildAsmOffsets.cmake diff --git a/port/threadx_smp/CMakeLists.txt b/port/threadx_smp/CMakeLists.txt index 87419a1..8331d65 100644 --- a/port/threadx_smp/CMakeLists.txt +++ b/port/threadx_smp/CMakeLists.txt @@ -1,3 +1,6 @@ +cmake_minimum_required(VERSION 3.24) +project(smp_demo LANGUAGES C ASM) + set(THREADX_COMMON_SMP_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../third-party/threadx/common_smp) if(NOT EXISTS "${THREADX_COMMON_SMP_DIR}") message(FATAL_ERROR "could not find ThreadX SMP sources, is the submodule checked out?") @@ -16,6 +19,23 @@ set(THREADX_SMP_GENERATED_INC_DIR ${CMAKE_CURRENT_BINARY_DIR}/generated) set(THREADX_SMP_OFFSET_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/src/tx_asm_offsets.c) set(THREADX_SMP_OFFSET_ASM ${THREADX_SMP_GENERATED_INC_DIR}/tx_asm_offsets.s) set(THREADX_SMP_OFFSET_INC ${THREADX_SMP_GENERATED_INC_DIR}/tx_asm_offsets.inc) +set(THREADX_SMP_OFFSET_INCLUDE_DIRS + ${THREADX_COMMON_SMP_DIR}/inc + ${THREADX_SMP_CUSTOM_INC} +) +set(THREADX_SMP_OFFSET_COMPILE_DEFINITIONS + TX_QUEUE_MESSAGE_MAX_SIZE=16 +) +set(THREADX_SMP_OFFSET_INCLUDE_ARGS ${THREADX_SMP_OFFSET_INCLUDE_DIRS}) +list(TRANSFORM THREADX_SMP_OFFSET_INCLUDE_ARGS PREPEND -I) +set(THREADX_SMP_OFFSET_DEFINE_ARGS ${THREADX_SMP_OFFSET_COMPILE_DEFINITIONS}) +list(TRANSFORM THREADX_SMP_OFFSET_DEFINE_ARGS PREPEND -D) +set(THREADX_SMP_OFFSET_CFLAGS ${CMAKE_C_FLAGS}) +if(CMAKE_BUILD_TYPE) + string(TOUPPER ${CMAKE_BUILD_TYPE} THREADX_SMP_BUILD_TYPE_UPPER) + list(APPEND THREADX_SMP_OFFSET_CFLAGS ${CMAKE_C_FLAGS_${THREADX_SMP_BUILD_TYPE_UPPER}}) +endif() +separate_arguments(THREADX_SMP_OFFSET_CFLAGS) file(GLOB THREADX_SMP_SOURCES ${THREADX_COMMON_SMP_DIR}/src/*.c) add_library(threadx_smp STATIC) @@ -30,14 +50,16 @@ target_include_directories(threadx_smp PUBLIC target_include_directories(threadx_smp PRIVATE ${THREADX_SMP_GENERATED_INC_DIR} ) -target_compile_definitions(threadx_smp PRIVATE -DTX_QUEUE_MESSAGE_MAX_SIZE=16) #This is addressed in PR #503 +target_compile_definitions(threadx_smp PRIVATE TX_QUEUE_MESSAGE_MAX_SIZE=16) #This is addressed in PR #503 add_custom_command( OUTPUT ${THREADX_SMP_OFFSET_INC} COMMAND ${CMAKE_COMMAND} -E make_directory ${THREADX_SMP_GENERATED_INC_DIR} COMMAND ${CMAKE_C_COMPILER} - $<$>:$,PREPEND,-I>> - $<$>:$,PREPEND,-D>> + ${CMAKE_C_COMPILER_ARG1} + ${THREADX_SMP_OFFSET_CFLAGS} + ${THREADX_SMP_OFFSET_INCLUDE_ARGS} + ${THREADX_SMP_OFFSET_DEFINE_ARGS} -S -o ${THREADX_SMP_OFFSET_ASM} ${THREADX_SMP_OFFSET_SOURCE} @@ -57,10 +79,6 @@ add_custom_command( add_custom_target(threadx_smp_offsets DEPENDS ${THREADX_SMP_OFFSET_INC}) add_dependencies(threadx_smp threadx_smp_offsets) - -#Definition smp_demo -project(smp_demo LANGUAGES C ASM) -cmake_minimum_required(VERSION 3.24) add_executable(smp_demo) target_include_directories(smp_demo PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../moonlight diff --git a/port/threadx_smp/cmake/BuildAsmOffsets.cmake b/port/threadx_smp/cmake/BuildAsmOffsets.cmake new file mode 100644 index 0000000..83d1ffa --- /dev/null +++ b/port/threadx_smp/cmake/BuildAsmOffsets.cmake @@ -0,0 +1,41 @@ +if(NOT DEFINED COMPILER) + message(FATAL_ERROR "COMPILER is required") +endif() + +if(NOT DEFINED SOURCE) + message(FATAL_ERROR "SOURCE is required") +endif() + +if(NOT DEFINED ASM_OUTPUT) + message(FATAL_ERROR "ASM_OUTPUT is required") +endif() + +if(NOT DEFINED HEADER_OUTPUT) + message(FATAL_ERROR "HEADER_OUTPUT is required") +endif() + +if(NOT DEFINED GENERATE_SCRIPT) + message(FATAL_ERROR "GENERATE_SCRIPT is required") +endif() + +execute_process( + COMMAND "${COMPILER}" ${COMPILER_ARG1} ${CFLAG_ARGS} ${INCLUDE_ARGS} ${DEFINE_ARGS} -S -o "${ASM_OUTPUT}" "${SOURCE}" + RESULT_VARIABLE compile_result + OUTPUT_VARIABLE compile_stdout + ERROR_VARIABLE compile_stderr +) + +if(NOT compile_result EQUAL 0) + message(FATAL_ERROR "failed to compile asm offsets source\n${compile_stdout}${compile_stderr}") +endif() + +execute_process( + COMMAND "${CMAKE_COMMAND}" -DINPUT="${ASM_OUTPUT}" -DOUTPUT="${HEADER_OUTPUT}" -P "${GENERATE_SCRIPT}" + RESULT_VARIABLE generate_result + OUTPUT_VARIABLE generate_stdout + ERROR_VARIABLE generate_stderr +) + +if(NOT generate_result EQUAL 0) + message(FATAL_ERROR "failed to generate asm offsets header\n${generate_stdout}${generate_stderr}") +endif() -- 2.49.1 From cca56f89c6d11a589cc64added44514b6421daf3 Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Thu, 12 Mar 2026 15:21:18 +0100 Subject: [PATCH 09/79] adds other files necessary to build smp library --- port/threadx_smp/CMakeLists.txt | 42 +- port/threadx_smp/inc/csr.h | 371 ++++++++++++++++ .../src/tx_thread_context_restore.S | 402 ++++++++++++++++++ port/threadx_smp/src/tx_thread_context_save.S | 289 +++++++++++++ port/threadx_smp/src/tx_thread_smp_core_get.S | 12 + .../src/tx_thread_smp_current_state_get.S | 14 + .../src/tx_thread_smp_current_thread_get.S | 19 + .../src/tx_thread_smp_initialize_wait.S | 23 + .../src/tx_thread_smp_low_level_initialize.S | 31 ++ port/threadx_smp/src/tx_thread_smp_protect.S | 50 +++ port/threadx_smp/src/tx_thread_smp_time_get.c | 8 + .../threadx_smp/src/tx_thread_smp_unprotect.S | 43 ++ port/threadx_smp/src/tx_thread_stack_build.S | 229 ++++++++++ .../threadx_smp/src/tx_thread_system_return.S | 198 +++++++++ 14 files changed, 1716 insertions(+), 15 deletions(-) create mode 100644 port/threadx_smp/inc/csr.h create mode 100644 port/threadx_smp/src/tx_thread_context_restore.S create mode 100644 port/threadx_smp/src/tx_thread_context_save.S create mode 100644 port/threadx_smp/src/tx_thread_smp_core_get.S create mode 100644 port/threadx_smp/src/tx_thread_smp_current_state_get.S create mode 100644 port/threadx_smp/src/tx_thread_smp_current_thread_get.S create mode 100644 port/threadx_smp/src/tx_thread_smp_initialize_wait.S create mode 100644 port/threadx_smp/src/tx_thread_smp_low_level_initialize.S create mode 100644 port/threadx_smp/src/tx_thread_smp_protect.S create mode 100644 port/threadx_smp/src/tx_thread_smp_time_get.c create mode 100644 port/threadx_smp/src/tx_thread_smp_unprotect.S create mode 100644 port/threadx_smp/src/tx_thread_stack_build.S create mode 100644 port/threadx_smp/src/tx_thread_system_return.S diff --git a/port/threadx_smp/CMakeLists.txt b/port/threadx_smp/CMakeLists.txt index 8331d65..9bf33f1 100644 --- a/port/threadx_smp/CMakeLists.txt +++ b/port/threadx_smp/CMakeLists.txt @@ -11,9 +11,21 @@ set(THREADX_SMP_CUSTOM_INC ${CMAKE_CURRENT_SOURCE_DIR}/../moonlight ) set(THREADX_SMP_CUSTOM_SRC - src/tx_thread_smp_core_preempt.c src/tx_initialize_low_level.S + src/tx_thread_context_restore.S + src/tx_thread_context_save.S src/tx_thread_schedule.S + src/tx_thread_smp_core_get.S + src/tx_thread_smp_core_preempt.c + src/tx_thread_smp_current_state_get.S + src/tx_thread_smp_current_thread_get.S + src/tx_thread_smp_initialize_wait.S + src/tx_thread_smp_low_level_initialize.S + src/tx_thread_smp_protect.S + src/tx_thread_smp_time_get.c + src/tx_thread_smp_unprotect.S + src/tx_thread_stack_build.S + src/tx_thread_system_return.S ) set(THREADX_SMP_GENERATED_INC_DIR ${CMAKE_CURRENT_BINARY_DIR}/generated) set(THREADX_SMP_OFFSET_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/src/tx_asm_offsets.c) @@ -56,22 +68,22 @@ add_custom_command( OUTPUT ${THREADX_SMP_OFFSET_INC} COMMAND ${CMAKE_COMMAND} -E make_directory ${THREADX_SMP_GENERATED_INC_DIR} COMMAND ${CMAKE_C_COMPILER} - ${CMAKE_C_COMPILER_ARG1} - ${THREADX_SMP_OFFSET_CFLAGS} - ${THREADX_SMP_OFFSET_INCLUDE_ARGS} - ${THREADX_SMP_OFFSET_DEFINE_ARGS} - -S - -o ${THREADX_SMP_OFFSET_ASM} - ${THREADX_SMP_OFFSET_SOURCE} + ${CMAKE_C_COMPILER_ARG1} + ${THREADX_SMP_OFFSET_CFLAGS} + ${THREADX_SMP_OFFSET_INCLUDE_ARGS} + ${THREADX_SMP_OFFSET_DEFINE_ARGS} + -S + -o ${THREADX_SMP_OFFSET_ASM} + ${THREADX_SMP_OFFSET_SOURCE} COMMAND ${CMAKE_COMMAND} - -DINPUT=${THREADX_SMP_OFFSET_ASM} - -DOUTPUT=${THREADX_SMP_OFFSET_INC} - -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/GenerateAsmOffsets.cmake + -DINPUT=${THREADX_SMP_OFFSET_ASM} + -DOUTPUT=${THREADX_SMP_OFFSET_INC} + -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/GenerateAsmOffsets.cmake DEPENDS - ${THREADX_SMP_OFFSET_SOURCE} - ${CMAKE_CURRENT_SOURCE_DIR}/cmake/GenerateAsmOffsets.cmake - ${CMAKE_CURRENT_SOURCE_DIR}/inc/tx_port.h - ${THREADX_COMMON_SMP_DIR}/inc/tx_api.h + ${THREADX_SMP_OFFSET_SOURCE} + ${CMAKE_CURRENT_SOURCE_DIR}/cmake/GenerateAsmOffsets.cmake + ${CMAKE_CURRENT_SOURCE_DIR}/inc/tx_port.h + ${THREADX_COMMON_SMP_DIR}/inc/tx_api.h COMMAND_EXPAND_LISTS VERBATIM ) diff --git a/port/threadx_smp/inc/csr.h b/port/threadx_smp/inc/csr.h new file mode 100644 index 0000000..3b64b4f --- /dev/null +++ b/port/threadx_smp/inc/csr.h @@ -0,0 +1,371 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * 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 CSR_H +#define 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) + +// Supervisor Status Register, sstatus +#define SSTATUS_SPP (1L << 8) // Previous mode, 1=Supervisor, 0=User +#define SSTATUS_SPIE (1L << 5) // Supervisor Previous Interrupt Enable +#define SSTATUS_UPIE (1L << 4) // User Previous Interrupt Enable +#define SSTATUS_SIE (1L << 1) // Supervisor Interrupt Enable +#define SSTATUS_UIE (1L << 0) // User Interrupt Enable +#define SSTATUS_SPIE (1L << 5) +#define SSTATUS_UPIE (1L << 4) + +// Supervisor Interrupt Enable +#define SIE_SEIE (1L << 9) // external +#define SIE_STIE (1L << 5) // timer +#define SIE_SSIE (1L << 1) // software + +#ifndef __ASSEMBLER__ + +#include + +static inline uint64_t riscv_get_core() +{ + uint64_t x; + asm volatile("csrr %0, mhartid" : "=r"(x)); + return x; +} + +static inline uint64_t riscv_get_mstatus() +{ + uint64_t x; + asm volatile("csrr %0, mstatus" : "=r"(x)); + return x; +} + +static inline void riscv_writ_mstatus(uint64_t x) +{ + asm volatile("csrw mstatus, %0" : : "r"(x)); +} + +// machine exception program counter, holds the +// instruction address to which a return from +// exception will go. +static inline void riscv_writ_mepc(uint64_t x) +{ + asm volatile("csrw mepc, %0" : : "r"(x)); +} + +static inline uint64_t riscv_get_sstatus() +{ + uint64_t x; + asm volatile("csrr %0, sstatus" : "=r"(x)); + return x; +} + +static inline void riscv_writ_sstatus(uint64_t x) +{ + asm volatile("csrw sstatus, %0" : : "r"(x)); +} + +// Supervisor Interrupt Pending +static inline uint64_t riscv_get_sip() +{ + uint64_t x; + asm volatile("csrr %0, sip" : "=r"(x)); + return x; +} + +static inline void riscv_writ_sip(uint64_t x) +{ + asm volatile("csrw sip, %0" : : "r"(x)); +} + +static inline uint64_t riscv_get_sie() +{ + uint64_t x; + asm volatile("csrr %0, sie" : "=r"(x)); + return x; +} + +static inline void riscv_writ_sie(uint64_t x) +{ + asm volatile("csrw sie, %0" : : "r"(x)); +} + +static inline uint64_t riscv_get_mie() +{ + uint64_t x; + asm volatile("csrr %0, mie" : "=r"(x)); + return x; +} + +static inline void riscv_writ_mie(uint64_t x) +{ + asm volatile("csrw mie, %0" : : "r"(x)); +} + +// supervisor exception program counter, holds the +// instruction address to which a return from +// exception will go. +static inline void riscv_writ_sepc(uint64_t x) +{ + asm volatile("csrw sepc, %0" : : "r"(x)); +} + +static inline uint64_t riscv_get_sepc() +{ + uint64_t x; + asm volatile("csrr %0, sepc" : "=r"(x)); + return x; +} + +// Machine Exception Delegation +static inline uint64_t riscv_get_medeleg() +{ + uint64_t x; + asm volatile("csrr %0, medeleg" : "=r"(x)); + return x; +} + +static inline void riscv_writ_medeleg(uint64_t x) +{ + asm volatile("csrw medeleg, %0" : : "r"(x)); +} + +// Machine Interrupt Delegation +static inline uint64_t riscv_get_mideleg() +{ + uint64_t x; + asm volatile("csrr %0, mideleg" : "=r"(x)); + return x; +} + +static inline void riscv_writ_mideleg(uint64_t x) +{ + asm volatile("csrw mideleg, %0" : : "r"(x)); +} + +// Supervisor Trap-Vector Base Address +// low two bits are mode. +static inline void riscv_writ_stvec(uint64_t x) +{ + asm volatile("csrw stvec, %0" : : "r"(x)); +} + +static inline uint64_t riscv_get_stvec() +{ + uint64_t x; + asm volatile("csrr %0, stvec" : "=r"(x)); + return x; +} + +// Supervisor Timer Comparison Register +static inline uint64_t riscv_get_stimecmp() +{ + uint64_t x; + // asm volatile("csrr %0, stimecmp" : "=r" (x) ); + asm volatile("csrr %0, 0x14d" : "=r"(x)); + return x; +} + +static inline void riscv_writ_stimecmp(uint64_t x) +{ + // asm volatile("csrw stimecmp, %0" : : "r" (x)); + asm volatile("csrw 0x14d, %0" : : "r"(x)); +} + +// Machine Environment Configuration Register +static inline uint64_t riscv_get_menvcfg() +{ + uint64_t x; + // asm volatile("csrr %0, menvcfg" : "=r" (x) ); + asm volatile("csrr %0, 0x30a" : "=r"(x)); + return x; +} + +static inline void riscv_writ_menvcfg(uint64_t x) +{ + // asm volatile("csrw menvcfg, %0" : : "r" (x)); + asm volatile("csrw 0x30a, %0" : : "r"(x)); +} + +// Physical Memory Protection +static inline void riscv_writ_pmpcfg0(uint64_t x) +{ + asm volatile("csrw pmpcfg0, %0" : : "r"(x)); +} + +static inline void riscv_writ_pmpaddr0(uint64_t x) +{ + asm volatile("csrw pmpaddr0, %0" : : "r"(x)); +} + +// supervisor address translation and protection; +// holds the address of the page table. +static inline void riscv_writ_satp(uint64_t x) +{ + asm volatile("csrw satp, %0" : : "r"(x)); +} + +static inline uint64_t riscv_get_satp() +{ + uint64_t x; + asm volatile("csrr %0, satp" : "=r"(x)); + return x; +} + +// Supervisor Trap Cause +static inline uint64_t riscv_get_scause() +{ + uint64_t x; + asm volatile("csrr %0, scause" : "=r"(x)); + return x; +} + +// Supervisor Trap Value +static inline uint64_t riscv_get_stval() +{ + uint64_t x; + asm volatile("csrr %0, stval" : "=r"(x)); + return x; +} + +// Machine-mode Counter-Enable +static inline void riscv_writ_mcounteren(uint64_t x) +{ + asm volatile("csrw mcounteren, %0" : : "r"(x)); +} + +static inline uint64_t riscv_get_mcounteren() +{ + uint64_t x; + asm volatile("csrr %0, mcounteren" : "=r"(x)); + return x; +} + +// machine-mode cycle counter +static inline uint64_t riscv_get_time() +{ + uint64_t x; + asm volatile("csrr %0, time" : "=r"(x)); + return x; +} + +// enable device interrupts +static inline void riscv_sintr_on() +{ + uint64_t sstatus = riscv_get_sstatus(); + sstatus |= SSTATUS_SIE; + riscv_writ_sstatus(sstatus); +} + +// disable device interrupts +static inline void riscv_sintr_off() +{ + uint64_t sstatus = riscv_get_sstatus(); + sstatus &= (~SSTATUS_SIE); + riscv_writ_sstatus(sstatus); +} + +// are device interrupts enabled? +static inline int riscv_sintr_get() +{ + uint64_t x = riscv_get_sstatus(); + return (x & SSTATUS_SIE) != 0; +} + +static inline void riscv_sintr_restore(int x) +{ + if (x) + riscv_sintr_on(); + else + riscv_sintr_off(); +} + +// enable device interrupts +static inline void riscv_mintr_on() +{ + uint64_t mstatus = riscv_get_mstatus(); + mstatus |= MSTATUS_MIE; + riscv_writ_mstatus(mstatus); +} + +// disable device interrupts +static inline void riscv_mintr_off() +{ + uint64_t mstatus = riscv_get_mstatus(); + mstatus &= (~MSTATUS_MIE); + riscv_writ_mstatus(mstatus); +} + +// are device interrupts enabled? +static inline int riscv_mintr_get() +{ + uint64_t x = riscv_get_mstatus(); + return (x & MSTATUS_MIE) != 0; +} + +static inline void riscv_mintr_restore(int x) +{ + if (x) + riscv_mintr_on(); + else + riscv_mintr_off(); +} + +static inline uint64_t riscv_get_sp() +{ + uint64_t x; + asm volatile("mv %0, sp" : "=r"(x)); + return x; +} + +// read and write tp, the thread pointer, which xv6 uses to hold +// this core's hartid (core number), the index into cpus[]. +static inline uint64_t riscv_get_tp() +{ + uint64_t x; + asm volatile("mv %0, tp" : "=r"(x)); + return x; +} + +static inline void riscv_writ_tp(uint64_t x) +{ + asm volatile("mv tp, %0" : : "r"(x)); +} + +static inline uint64_t riscv_get_ra() +{ + uint64_t x; + asm volatile("mv %0, ra" : "=r"(x)); + return x; +} + +// flush the TLB. +static inline void sfence_vma() +{ + // the zero, zero means flush all TLB entries. + asm volatile("sfence.vma zero, zero"); +} + +#endif // __ASSEMBLER__ + +#endif diff --git a/port/threadx_smp/src/tx_thread_context_restore.S b/port/threadx_smp/src/tx_thread_context_restore.S new file mode 100644 index 0000000..8171155 --- /dev/null +++ b/port/threadx_smp/src/tx_thread_context_restore.S @@ -0,0 +1,402 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * 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 Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#include "tx_port.h" + + .section .text +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore RISC-V64/GNU */ +/* 6.2.1 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 03-08-2023 Scott Larson Initial Version 6.2.1 */ +/* */ +/**************************************************************************/ +/* VOID _tx_thread_context_restore(VOID) +{ */ + .global _tx_thread_context_restore +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + csrci mstatus, 0x08 // Disable interrupts + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + call _tx_execution_isr_exit // Call the ISR execution exit function +#endif + + /* Determine if interrupts are nested. */ + /* if (--_tx_thread_system_state) + { */ + + csrr t3, mhartid // Pickup current hart ID + slli t4, t3, 2 // Build per-hart ULONG offset + slli t5, t3, LOG_REGBYTES // Build per-hart pointer offset + la t0, _tx_thread_system_state // Pickup base of system-state array + add t0, t0, t4 // Select this hart's system-state slot + lw t1, 0(t0) // Pickup nested interrupt count + addi t1, t1, -1 // Decrement the nested interrupt counter + sw t1, 0(t0) // Store new nested count + beqz t1, _tx_thread_not_nested_restore // If 0, not nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + /* Recover floating point registers. */ +#if defined(__riscv_float_abi_single) + flw f0, 31*REGBYTES(sp) // Recover ft0 + flw f1, 32*REGBYTES(sp) // Recover ft1 + flw f2, 33*REGBYTES(sp) // Recover ft2 + flw f3, 34*REGBYTES(sp) // Recover ft3 + flw f4, 35*REGBYTES(sp) // Recover ft4 + flw f5, 36*REGBYTES(sp) // Recover ft5 + flw f6, 37*REGBYTES(sp) // Recover ft6 + flw f7, 38*REGBYTES(sp) // Recover ft7 + flw f10,41*REGBYTES(sp) // Recover fa0 + flw f11,42*REGBYTES(sp) // Recover fa1 + flw f12,43*REGBYTES(sp) // Recover fa2 + flw f13,44*REGBYTES(sp) // Recover fa3 + flw f14,45*REGBYTES(sp) // Recover fa4 + flw f15,46*REGBYTES(sp) // Recover fa5 + flw f16,47*REGBYTES(sp) // Recover fa6 + flw f17,48*REGBYTES(sp) // Recover fa7 + flw f28,59*REGBYTES(sp) // Recover ft8 + flw f29,60*REGBYTES(sp) // Recover ft9 + flw f30,61*REGBYTES(sp) // Recover ft10 + flw f31,62*REGBYTES(sp) // Recover ft11 + lw t0, 63*REGBYTES(sp) // Recover fcsr + csrw fcsr, t0 // +#elif defined(__riscv_float_abi_double) + fld f0, 31*REGBYTES(sp) // Recover ft0 + fld f1, 32*REGBYTES(sp) // Recover ft1 + fld f2, 33*REGBYTES(sp) // Recover ft2 + fld f3, 34*REGBYTES(sp) // Recover ft3 + fld f4, 35*REGBYTES(sp) // Recover ft4 + fld f5, 36*REGBYTES(sp) // Recover ft5 + fld f6, 37*REGBYTES(sp) // Recover ft6 + fld f7, 38*REGBYTES(sp) // Recover ft7 + fld f10,41*REGBYTES(sp) // Recover fa0 + fld f11,42*REGBYTES(sp) // Recover fa1 + fld f12,43*REGBYTES(sp) // Recover fa2 + fld f13,44*REGBYTES(sp) // Recover fa3 + fld f14,45*REGBYTES(sp) // Recover fa4 + fld f15,46*REGBYTES(sp) // Recover fa5 + fld f16,47*REGBYTES(sp) // Recover fa6 + fld f17,48*REGBYTES(sp) // Recover fa7 + fld f28,59*REGBYTES(sp) // Recover ft8 + fld f29,60*REGBYTES(sp) // Recover ft9 + fld f30,61*REGBYTES(sp) // Recover ft10 + fld f31,62*REGBYTES(sp) // Recover ft11 + LOAD t0, 63*REGBYTES(sp) // Recover fcsr + csrw fcsr, t0 // +#endif + + /* Recover standard registers. */ + + /* Restore registers, + Skip global pointer because that does not change. + Also skip the saved registers since they have been restored by any function we called, + except s0 since we use it ourselves. */ + + LOAD t0, 30*REGBYTES(sp) // Recover mepc + csrw mepc, t0 // Setup mepc + li t0, 0x1880 // Prepare MPIP +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + li t1, 1<<13 + or t0, t1, t0 +#endif + csrw mstatus, t0 // Enable MPIP + + LOAD x1, 28*REGBYTES(sp) // Recover RA + LOAD x5, 19*REGBYTES(sp) // Recover t0 + LOAD x6, 18*REGBYTES(sp) // Recover t1 + LOAD x7, 17*REGBYTES(sp) // Recover t2 + LOAD x8, 12*REGBYTES(sp) // Recover s0 + LOAD x10, 27*REGBYTES(sp) // Recover a0 + LOAD x11, 26*REGBYTES(sp) // Recover a1 + LOAD x12, 25*REGBYTES(sp) // Recover a2 + LOAD x13, 24*REGBYTES(sp) // Recover a3 + LOAD x14, 23*REGBYTES(sp) // Recover a4 + LOAD x15, 22*REGBYTES(sp) // Recover a5 + LOAD x16, 21*REGBYTES(sp) // Recover a6 + LOAD x17, 20*REGBYTES(sp) // Recover a7 + LOAD x28, 16*REGBYTES(sp) // Recover t3 + LOAD x29, 15*REGBYTES(sp) // Recover t4 + LOAD x30, 14*REGBYTES(sp) // Recover t5 + LOAD x31, 13*REGBYTES(sp) // Recover t6 + +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + addi sp, sp, 65*REGBYTES // Recover stack frame - with floating point enabled +#else + addi sp, sp, 32*REGBYTES // Recover stack frame - without floating point enabled +#endif + mret // Return to point of interrupt + + /* } */ +_tx_thread_not_nested_restore: + /* Determine if a thread was interrupted and no preemption is required. */ + /* else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + || (_tx_thread_preempt_disable)) + { */ + + la t0, _tx_thread_current_ptr // Pickup base of current-thread array + add t0, t0, t5 // Select this hart's current-thread slot + LOAD t1, 0(t0) // Pickup current thread pointer + beqz t1, _tx_thread_idle_system_restore // If NULL, idle system restore + + la t0, _tx_thread_execute_ptr // Pickup base of execute-thread array + add t0, t0, t5 // Select this hart's execute-thread slot + LOAD t2, 0(t0) // Pickup thread execute pointer + beq t1, t2, _tx_thread_no_preempt_restore // Same thread selected, no preemption + + la t0, _tx_thread_smp_protection // Pickup protection structure + lw t2, 4(t0) // Pickup owning hart + bne t2, t3, _tx_thread_preempt_restore // If owned by another hart, preempt + + LOAD t2, _tx_thread_preempt_disable // Pickup preempt disable flag + bgtz t2, _tx_thread_no_preempt_restore // If set, restore interrupted thread + + +_tx_thread_no_preempt_restore: + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + /* SP = _tx_thread_current_ptr -> tx_thread_stack_ptr; */ + + LOAD sp, 2*REGBYTES(t1) // Switch back to thread's stack + + /* Recover floating point registers. */ +#if defined(__riscv_float_abi_single) + flw f0, 31*REGBYTES(sp) // Recover ft0 + flw f1, 32*REGBYTES(sp) // Recover ft1 + flw f2, 33*REGBYTES(sp) // Recover ft2 + flw f3, 34*REGBYTES(sp) // Recover ft3 + flw f4, 35*REGBYTES(sp) // Recover ft4 + flw f5, 36*REGBYTES(sp) // Recover ft5 + flw f6, 37*REGBYTES(sp) // Recover ft6 + flw f7, 38*REGBYTES(sp) // Recover ft7 + flw f10,41*REGBYTES(sp) // Recover fa0 + flw f11,42*REGBYTES(sp) // Recover fa1 + flw f12,43*REGBYTES(sp) // Recover fa2 + flw f13,44*REGBYTES(sp) // Recover fa3 + flw f14,45*REGBYTES(sp) // Recover fa4 + flw f15,46*REGBYTES(sp) // Recover fa5 + flw f16,47*REGBYTES(sp) // Recover fa6 + flw f17,48*REGBYTES(sp) // Recover fa7 + flw f28,59*REGBYTES(sp) // Recover ft8 + flw f29,60*REGBYTES(sp) // Recover ft9 + flw f30,61*REGBYTES(sp) // Recover ft10 + flw f31,62*REGBYTES(sp) // Recover ft11 + lw t0, 63*REGBYTES(sp) // Recover fcsr + csrw fcsr, t0 // +#elif defined(__riscv_float_abi_double) + fld f0, 31*REGBYTES(sp) // Recover ft0 + fld f1, 32*REGBYTES(sp) // Recover ft1 + fld f2, 33*REGBYTES(sp) // Recover ft2 + fld f3, 34*REGBYTES(sp) // Recover ft3 + fld f4, 35*REGBYTES(sp) // Recover ft4 + fld f5, 36*REGBYTES(sp) // Recover ft5 + fld f6, 37*REGBYTES(sp) // Recover ft6 + fld f7, 38*REGBYTES(sp) // Recover ft7 + fld f10,41*REGBYTES(sp) // Recover fa0 + fld f11,42*REGBYTES(sp) // Recover fa1 + fld f12,43*REGBYTES(sp) // Recover fa2 + fld f13,44*REGBYTES(sp) // Recover fa3 + fld f14,45*REGBYTES(sp) // Recover fa4 + fld f15,46*REGBYTES(sp) // Recover fa5 + fld f16,47*REGBYTES(sp) // Recover fa6 + fld f17,48*REGBYTES(sp) // Recover fa7 + fld f28,59*REGBYTES(sp) // Recover ft8 + fld f29,60*REGBYTES(sp) // Recover ft9 + fld f30,61*REGBYTES(sp) // Recover ft10 + fld f31,62*REGBYTES(sp) // Recover ft11 + LOAD t0, 63*REGBYTES(sp) // Recover fcsr + csrw fcsr, t0 // +#endif + + /* Recover the saved context and return to the point of interrupt. */ + + /* Recover standard registers. */ + /* Restore registers, + Skip global pointer because that does not change */ + + LOAD t0, 30*REGBYTES(sp) // Recover mepc + csrw mepc, t0 // Setup mepc + li t0, 0x1880 // Prepare MPIP +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + li t1, 1<<13 + or t0, t1, t0 +#endif + csrw mstatus, t0 // Enable MPIP + + LOAD x1, 28*REGBYTES(sp) // Recover RA + LOAD x5, 19*REGBYTES(sp) // Recover t0 + LOAD x6, 18*REGBYTES(sp) // Recover t1 + LOAD x7, 17*REGBYTES(sp) // Recover t2 + LOAD x8, 12*REGBYTES(sp) // Recover s0 + LOAD x10, 27*REGBYTES(sp) // Recover a0 + LOAD x11, 26*REGBYTES(sp) // Recover a1 + LOAD x12, 25*REGBYTES(sp) // Recover a2 + LOAD x13, 24*REGBYTES(sp) // Recover a3 + LOAD x14, 23*REGBYTES(sp) // Recover a4 + LOAD x15, 22*REGBYTES(sp) // Recover a5 + LOAD x16, 21*REGBYTES(sp) // Recover a6 + LOAD x17, 20*REGBYTES(sp) // Recover a7 + LOAD x28, 16*REGBYTES(sp) // Recover t3 + LOAD x29, 15*REGBYTES(sp) // Recover t4 + LOAD x30, 14*REGBYTES(sp) // Recover t5 + LOAD x31, 13*REGBYTES(sp) // Recover t6 + +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + addi sp, sp, 65*REGBYTES // Recover stack frame - with floating point enabled +#else + addi sp, sp, 32*REGBYTES // Recover stack frame - without floating point enabled +#endif + mret // Return to point of interrupt + + /* } + else + { */ +_tx_thread_preempt_restore: + /* Instead of directly activating the thread again, ensure we save the + entire stack frame by saving the remaining registers. */ + + LOAD t0, 2*REGBYTES(t1) // Pickup thread's stack pointer + ori t3, x0, 1 // Build interrupt stack type + STORE t3, 0(t0) // Store stack type + + /* Store floating point preserved registers. */ +#ifdef __riscv_float_abi_single + fsw f8, 39*REGBYTES(t0) // Store fs0 + fsw f9, 40*REGBYTES(t0) // Store fs1 + fsw f18, 49*REGBYTES(t0) // Store fs2 + fsw f19, 50*REGBYTES(t0) // Store fs3 + fsw f20, 51*REGBYTES(t0) // Store fs4 + fsw f21, 52*REGBYTES(t0) // Store fs5 + fsw f22, 53*REGBYTES(t0) // Store fs6 + fsw f23, 54*REGBYTES(t0) // Store fs7 + fsw f24, 55*REGBYTES(t0) // Store fs8 + fsw f25, 56*REGBYTES(t0) // Store fs9 + fsw f26, 57*REGBYTES(t0) // Store fs10 + fsw f27, 58*REGBYTES(t0) // Store fs11 +#elif defined(__riscv_float_abi_double) + fsd f8, 39*REGBYTES(t0) // Store fs0 + fsd f9, 40*REGBYTES(t0) // Store fs1 + fsd f18, 49*REGBYTES(t0) // Store fs2 + fsd f19, 50*REGBYTES(t0) // Store fs3 + fsd f20, 51*REGBYTES(t0) // Store fs4 + fsd f21, 52*REGBYTES(t0) // Store fs5 + fsd f22, 53*REGBYTES(t0) // Store fs6 + fsd f23, 54*REGBYTES(t0) // Store fs7 + fsd f24, 55*REGBYTES(t0) // Store fs8 + fsd f25, 56*REGBYTES(t0) // Store fs9 + fsd f26, 57*REGBYTES(t0) // Store fs10 + fsd f27, 58*REGBYTES(t0) // Store fs11 +#endif + + /* Store standard preserved registers. */ + + STORE x9, 11*REGBYTES(t0) // Store s1 + STORE x18, 10*REGBYTES(t0) // Store s2 + STORE x19, 9*REGBYTES(t0) // Store s3 + STORE x20, 8*REGBYTES(t0) // Store s4 + STORE x21, 7*REGBYTES(t0) // Store s5 + STORE x22, 6*REGBYTES(t0) // Store s6 + STORE x23, 5*REGBYTES(t0) // Store s7 + STORE x24, 4*REGBYTES(t0) // Store s8 + STORE x25, 3*REGBYTES(t0) // Store s9 + STORE x26, 2*REGBYTES(t0) // Store s10 + STORE x27, 1*REGBYTES(t0) // Store s11 + // Note: s0 is already stored! + + /* Save the remaining time-slice and disable it. */ + /* if (_tx_timer_time_slice) + { */ + + la t0, _tx_timer_time_slice // Pickup base of time-slice array + add t0, t0, t4 // Select this hart's time-slice slot + lw t2, 0(t0) // Pickup time slice + beqz t2, _tx_thread_dont_save_ts // If 0, skip time slice processing + + /* _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice + _tx_timer_time_slice = 0; */ + + sw t2, TX_THREAD_TIME_SLICE_OFFSET(t1) // Save current time slice + sw x0, 0(t0) // Clear global time slice + + + /* } */ +_tx_thread_dont_save_ts: + /* Clear the current task pointer. */ + /* _tx_thread_current_ptr = TX_NULL; */ + + /* Return to the scheduler. */ + /* _tx_thread_schedule(); */ + + la t0, _tx_thread_current_ptr // Pickup base of current-thread array + add t0, t0, t5 // Select this hart's current-thread slot + STORE x0, 0(t0) // Clear current thread pointer + + fence rw, rw // Publish current-thread clear before ready token + addi t0, t1, TX_THREAD_SMP_LOCK_READY_BIT_OFFSET // Pickup lock/ready-bit address + li t2, 1 // Rebuild ready token + amoswap.w.rl x0, t2, (t0) // Set thread ready token for reschedule + /* } */ + +_tx_thread_idle_system_restore: + /* Just return back to the scheduler! */ + j _tx_thread_schedule // Return to scheduler + +/* } */ diff --git a/port/threadx_smp/src/tx_thread_context_save.S b/port/threadx_smp/src/tx_thread_context_save.S new file mode 100644 index 0000000..bddb7df --- /dev/null +++ b/port/threadx_smp/src/tx_thread_context_save.S @@ -0,0 +1,289 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * 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 Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#include "tx_port.h" + + .section .text +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save RISC-V64/GNU */ +/* 6.2.1 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 03-08-2023 Scott Larson Initial Version 6.2.1 */ +/* */ +/**************************************************************************/ +/* VOID _tx_thread_context_save(VOID) +{ */ + .global _tx_thread_context_save +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that interrupts are locked + out and the interrupt stack fame has been allocated and x1 (ra) has + been saved on the stack. */ + + STORE t0, 19*REGBYTES(sp) // Save t0 + STORE t1, 18*REGBYTES(sp) // Save t1 + STORE x7, 17*REGBYTES(sp) // Save t2 before reusing it + STORE x28, 16*REGBYTES(sp) // Save t3 before reusing it + STORE x29, 15*REGBYTES(sp) // Save t4 before reusing it + + csrr t2, mhartid // Pickup current hart ID + slli t3, t2, 2 // Build per-hart ULONG offset + slli t4, t2, LOG_REGBYTES // Build per-hart pointer offset + la t1, _tx_thread_system_state // Pickup base of system state array + add t0, t1, t3 // Select this hart's system-state slot + lw t1, 0(t0) // Pickup system state + + /* Check for a nested interrupt condition. */ + /* if (_tx_thread_system_state++) + { */ + beqz t1, _tx_thread_not_nested_save // If 0, first interrupt condition + addi t1, t1, 1 // Increment the interrupt counter + sw t1, 0(t0) // Store the interrupt counter + + /* Nested interrupt condition. + Save the reset of the scratch registers on the stack and return to the + calling ISR. */ + + STORE x8, 12*REGBYTES(sp) // Store s0 + STORE x10, 27*REGBYTES(sp) // Store a0 + STORE x11, 26*REGBYTES(sp) // Store a1 + STORE x12, 25*REGBYTES(sp) // Store a2 + STORE x13, 24*REGBYTES(sp) // Store a3 + STORE x14, 23*REGBYTES(sp) // Store a4 + STORE x15, 22*REGBYTES(sp) // Store a5 + STORE x16, 21*REGBYTES(sp) // Store a6 + STORE x17, 20*REGBYTES(sp) // Store a7 + STORE x30, 14*REGBYTES(sp) // Store t5 + STORE x31, 13*REGBYTES(sp) // Store t6 + csrr t0, mepc // Load exception program counter + STORE t0, 30*REGBYTES(sp) // Save it on the stack + + /* Save floating point scratch registers. */ +#if defined(__riscv_float_abi_single) + fsw f0, 31*REGBYTES(sp) // Store ft0 + fsw f1, 32*REGBYTES(sp) // Store ft1 + fsw f2, 33*REGBYTES(sp) // Store ft2 + fsw f3, 34*REGBYTES(sp) // Store ft3 + fsw f4, 35*REGBYTES(sp) // Store ft4 + fsw f5, 36*REGBYTES(sp) // Store ft5 + fsw f6, 37*REGBYTES(sp) // Store ft6 + fsw f7, 38*REGBYTES(sp) // Store ft7 + fsw f10,41*REGBYTES(sp) // Store fa0 + fsw f11,42*REGBYTES(sp) // Store fa1 + fsw f12,43*REGBYTES(sp) // Store fa2 + fsw f13,44*REGBYTES(sp) // Store fa3 + fsw f14,45*REGBYTES(sp) // Store fa4 + fsw f15,46*REGBYTES(sp) // Store fa5 + fsw f16,47*REGBYTES(sp) // Store fa6 + fsw f17,48*REGBYTES(sp) // Store fa7 + fsw f28,59*REGBYTES(sp) // Store ft8 + fsw f29,60*REGBYTES(sp) // Store ft9 + fsw f30,61*REGBYTES(sp) // Store ft10 + fsw f31,62*REGBYTES(sp) // Store ft11 + csrr t0, fcsr + STORE t0, 63*REGBYTES(sp) // Store fcsr +#elif defined(__riscv_float_abi_double) + fsd f0, 31*REGBYTES(sp) // Store ft0 + fsd f1, 32*REGBYTES(sp) // Store ft1 + fsd f2, 33*REGBYTES(sp) // Store ft2 + fsd f3, 34*REGBYTES(sp) // Store ft3 + fsd f4, 35*REGBYTES(sp) // Store ft4 + fsd f5, 36*REGBYTES(sp) // Store ft5 + fsd f6, 37*REGBYTES(sp) // Store ft6 + fsd f7, 38*REGBYTES(sp) // Store ft7 + fsd f10,41*REGBYTES(sp) // Store fa0 + fsd f11,42*REGBYTES(sp) // Store fa1 + fsd f12,43*REGBYTES(sp) // Store fa2 + fsd f13,44*REGBYTES(sp) // Store fa3 + fsd f14,45*REGBYTES(sp) // Store fa4 + fsd f15,46*REGBYTES(sp) // Store fa5 + fsd f16,47*REGBYTES(sp) // Store fa6 + fsd f17,48*REGBYTES(sp) // Store fa7 + fsd f28,59*REGBYTES(sp) // Store ft8 + fsd f29,60*REGBYTES(sp) // Store ft9 + fsd f30,61*REGBYTES(sp) // Store ft10 + fsd f31,62*REGBYTES(sp) // Store ft11 + csrr t0, fcsr + STORE t0, 63*REGBYTES(sp) // Store fcsr +#endif + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + call _tx_execution_isr_enter // Call the ISR execution enter function +#endif + + ret // Return to calling ISR + +_tx_thread_not_nested_save: + /* } */ + + /* Otherwise, not nested, check to see if a thread was running. */ + /* else if (_tx_thread_current_ptr) + { */ + addi t1, t1, 1 // Increment the interrupt counter + sw t1, 0(t0) // Store the interrupt counter + + /* Not nested: Find the user thread that was running and load our SP */ + + la t1, _tx_thread_current_ptr // Pickup base of current-thread array + add t0, t1, t4 // Select this hart's current-thread slot + LOAD t0, 0(t0) // Pickup current thread pointer + beqz t0, _tx_thread_idle_system_save // If NULL, idle system was interrupted + + /* Save the standard scratch registers. */ + + STORE x8, 12*REGBYTES(sp) // Store s0 + STORE x10, 27*REGBYTES(sp) // Store a0 + STORE x11, 26*REGBYTES(sp) // Store a1 + STORE x12, 25*REGBYTES(sp) // Store a2 + STORE x13, 24*REGBYTES(sp) // Store a3 + STORE x14, 23*REGBYTES(sp) // Store a4 + STORE x15, 22*REGBYTES(sp) // Store a5 + STORE x16, 21*REGBYTES(sp) // Store a6 + STORE x17, 20*REGBYTES(sp) // Store a7 + STORE x30, 14*REGBYTES(sp) // Store t5 + STORE x31, 13*REGBYTES(sp) // Store t6 + + csrr t0, mepc // Load exception program counter + STORE t0, 30*REGBYTES(sp) // Save it on the stack + + /* Save floating point scratch registers. */ +#if defined(__riscv_float_abi_single) + fsw f0, 31*REGBYTES(sp) // Store ft0 + fsw f1, 32*REGBYTES(sp) // Store ft1 + fsw f2, 33*REGBYTES(sp) // Store ft2 + fsw f3, 34*REGBYTES(sp) // Store ft3 + fsw f4, 35*REGBYTES(sp) // Store ft4 + fsw f5, 36*REGBYTES(sp) // Store ft5 + fsw f6, 37*REGBYTES(sp) // Store ft6 + fsw f7, 38*REGBYTES(sp) // Store ft7 + fsw f10,41*REGBYTES(sp) // Store fa0 + fsw f11,42*REGBYTES(sp) // Store fa1 + fsw f12,43*REGBYTES(sp) // Store fa2 + fsw f13,44*REGBYTES(sp) // Store fa3 + fsw f14,45*REGBYTES(sp) // Store fa4 + fsw f15,46*REGBYTES(sp) // Store fa5 + fsw f16,47*REGBYTES(sp) // Store fa6 + fsw f17,48*REGBYTES(sp) // Store fa7 + fsw f28,59*REGBYTES(sp) // Store ft8 + fsw f29,60*REGBYTES(sp) // Store ft9 + fsw f30,61*REGBYTES(sp) // Store ft10 + fsw f31,62*REGBYTES(sp) // Store ft11 + csrr t0, fcsr + STORE t0, 63*REGBYTES(sp) // Store fcsr +#elif defined(__riscv_float_abi_double) + fsd f0, 31*REGBYTES(sp) // Store ft0 + fsd f1, 32*REGBYTES(sp) // Store ft1 + fsd f2, 33*REGBYTES(sp) // Store ft2 + fsd f3, 34*REGBYTES(sp) // Store ft3 + fsd f4, 35*REGBYTES(sp) // Store ft4 + fsd f5, 36*REGBYTES(sp) // Store ft5 + fsd f6, 37*REGBYTES(sp) // Store ft6 + fsd f7, 38*REGBYTES(sp) // Store ft7 + fsd f10,41*REGBYTES(sp) // Store fa0 + fsd f11,42*REGBYTES(sp) // Store fa1 + fsd f12,43*REGBYTES(sp) // Store fa2 + fsd f13,44*REGBYTES(sp) // Store fa3 + fsd f14,45*REGBYTES(sp) // Store fa4 + fsd f15,46*REGBYTES(sp) // Store fa5 + fsd f16,47*REGBYTES(sp) // Store fa6 + fsd f17,48*REGBYTES(sp) // Store fa7 + fsd f28,59*REGBYTES(sp) // Store ft8 + fsd f29,60*REGBYTES(sp) // Store ft9 + fsd f30,61*REGBYTES(sp) // Store ft10 + fsd f31,62*REGBYTES(sp) // Store ft11 + csrr t0, fcsr + STORE t0, 63*REGBYTES(sp) // Store fcsr +#endif + + /* Save the current stack pointer in the thread's control block. */ + /* _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; */ + + /* Switch to the system stack. */ + /* sp = _tx_thread_system_stack_ptr; */ + + la t0, _tx_thread_current_ptr // Pickup base of current-thread array + add t0, t0, t4 // Select this hart's current-thread slot + LOAD t1, 0(t0) // Pickup current thread pointer + STORE sp, 2*REGBYTES(t1) // Save stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + /* _tx_execution_isr_enter is called with thread stack pointer */ + call _tx_execution_isr_enter // Call the ISR execution enter function +#endif + + la t0, _tx_thread_system_stack_ptr // Pickup base of system-stack array + add t0, t0, t4 // Select this hart's system-stack slot + LOAD sp, 0(t0) // Switch to system stack + ret // Return to calling ISR + + /* } + else + { */ + +_tx_thread_idle_system_save: + + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + call _tx_execution_isr_enter // Call the ISR execution enter function +#endif + + /* Interrupt occurred in the scheduling loop. */ + + /* } +} */ +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + addi sp, sp, 65*REGBYTES // Recover stack frame - with floating point enabled +#else + addi sp, sp, 32*REGBYTES // Recover the reserved stack space +#endif + ret // Return to calling ISR diff --git a/port/threadx_smp/src/tx_thread_smp_core_get.S b/port/threadx_smp/src/tx_thread_smp_core_get.S new file mode 100644 index 0000000..fd89b7a --- /dev/null +++ b/port/threadx_smp/src/tx_thread_smp_core_get.S @@ -0,0 +1,12 @@ +#include "tx_port.h" + + .section .text + .align 2 + + .global _tx_thread_smp_core_get + .type _tx_thread_smp_core_get, @function +_tx_thread_smp_core_get: + csrr a0, mhartid // Pickup the current hart ID + ret + + diff --git a/port/threadx_smp/src/tx_thread_smp_current_state_get.S b/port/threadx_smp/src/tx_thread_smp_current_state_get.S new file mode 100644 index 0000000..4fea92a --- /dev/null +++ b/port/threadx_smp/src/tx_thread_smp_current_state_get.S @@ -0,0 +1,14 @@ +#include "tx_port.h" + + .section .text + .align 2 + + .global _tx_thread_smp_current_state_get + .type _tx_thread_smp_current_state_get, @function +_tx_thread_smp_current_state_get: + csrr t0, mhartid // Pickup current hart ID + la t1, _tx_thread_system_state // Base of per-hart system-state array + slli t0, t0, 2 // Build offset into array + add t1, t1, t0 // Select this hart's slot + LWU a0, 0(t1) // Return current system state + ret diff --git a/port/threadx_smp/src/tx_thread_smp_current_thread_get.S b/port/threadx_smp/src/tx_thread_smp_current_thread_get.S new file mode 100644 index 0000000..6f6ec97 --- /dev/null +++ b/port/threadx_smp/src/tx_thread_smp_current_thread_get.S @@ -0,0 +1,19 @@ +#include "tx_port.h" + + .section .text + .align 2 + + .global _tx_thread_smp_current_thread_get + .type _tx_thread_smp_current_thread_get, @function +_tx_thread_smp_current_thread_get: + csrr t0, mstatus // Pickup current interrupt posture + csrci mstatus, 0x08 // Lockout interrupts + + csrr t1, mhartid // Pickup current hart ID + la t2, _tx_thread_current_ptr // Base of current-thread pointer array + slli t1, t1, LOG_REGBYTES // Build per-hart byte offset + add t2, t2, t1 // Select this hart's slot + LOAD a0, 0(t2) // Pickup current thread pointer + + csrw mstatus, t0 // Restore interrupt posture + ret diff --git a/port/threadx_smp/src/tx_thread_smp_initialize_wait.S b/port/threadx_smp/src/tx_thread_smp_initialize_wait.S new file mode 100644 index 0000000..db78833 --- /dev/null +++ b/port/threadx_smp/src/tx_thread_smp_initialize_wait.S @@ -0,0 +1,23 @@ +#include "tx_port.h" + + .section .text + .align 2 + + .global _tx_thread_smp_initialize_wait + .type _tx_thread_smp_initialize_wait, @function +_tx_thread_smp_initialize_wait: + + /* Core 0 continues initialization. All other harts wait until the + release flag is set by the low-level SMP initialization path. */ + csrr t0, mhartid // Pickup current hart ID + beqz t0, _tx_thread_smp_initialize_done // Core 0 does not wait + + la t1, _tx_thread_smp_release_cores_flag // Release flag address + +_tx_thread_smp_initialize_wait_loop: + LWU t2, 0(t1) // Pickup release flag + bnez t2, _tx_thread_smp_initialize_done // Exit once core 0 releases secondaries + j _tx_thread_smp_initialize_wait_loop // Keep waiting + +_tx_thread_smp_initialize_done: + ret diff --git a/port/threadx_smp/src/tx_thread_smp_low_level_initialize.S b/port/threadx_smp/src/tx_thread_smp_low_level_initialize.S new file mode 100644 index 0000000..f2f7357 --- /dev/null +++ b/port/threadx_smp/src/tx_thread_smp_low_level_initialize.S @@ -0,0 +1,31 @@ +#include "tx_port.h" + + .section .text + .align 2 + + .global _tx_thread_smp_low_level_initialize + .type _tx_thread_smp_low_level_initialize, @function +_tx_thread_smp_low_level_initialize: + + /* Only hart 0 performs low-level SMP initialization. */ + csrr t0, mhartid // Pickup current hart ID + bnez t0, _tx_thread_smp_low_level_initialize_done + + /* Keep secondary harts parked until kernel-enter releases them. */ + la t1, _tx_thread_smp_release_cores_flag + sw x0, 0(t1) // Clear release flag + +#ifdef TX_THREAD_SMP_DYNAMIC_CORE_MAX + /* Record the detected core count supplied by the caller. */ + la t1, _tx_thread_smp_detected_cores + sw a0, 0(t1) +#endif + + /* Platform-specific secondary-hart startup can go here. + This is where core 0 would program boot addresses, send startup IPIs, + or otherwise bring the other harts online. */ + + fence rw, rw // Publish startup state + +_tx_thread_smp_low_level_initialize_done: + ret diff --git a/port/threadx_smp/src/tx_thread_smp_protect.S b/port/threadx_smp/src/tx_thread_smp_protect.S new file mode 100644 index 0000000..4618e3c --- /dev/null +++ b/port/threadx_smp/src/tx_thread_smp_protect.S @@ -0,0 +1,50 @@ +#include "tx_port.h" + + .section .text + .align 2 + + .global _tx_thread_smp_protect + .type _tx_thread_smp_protect, @function +_tx_thread_smp_protect: + + /* Disable interrupts so we don't get preempted. */ + csrr a0, mstatus // Pickup current interrupt posture + csrci mstatus, 0x08 // Lockout interrupts + + /* Pickup the hart ID. */ + csrr t2, mhartid // Pickup the current hart ID + + /* Build address to protection structure. */ + la t1, _tx_thread_smp_protection + + /* If this hart already owns protection, just nest the count. */ + LWU t3, 4(t1) // Pickup owning hart + beq t3, t2, _owned // Already owned by this hart + + /* Try to get the protection. */ + LWU t4, 0(t1) // Pickup protection flag + beqz t4, _get_protection // If clear, try to claim it + + /* Protection is busy. Restore interrupts and retry. */ + csrw mstatus, a0 // Restore interrupts + j _tx_thread_smp_protect // Restart the protection attempt + +_get_protection: + li t4, 1 // Build lock value + amoswap.w.aq t5, t4, (t1) // Attempt to get protection + bnez t5, _protection_busy // If old value != 0, retry + +_got_protection: + fence rw, rw // Ensure lock acquisition is visible + sw t2, 4(t1) // Save owning hart + +_owned: + LWU t5, 8(t1) // Pickup ownership count + addi t5, t5, 1 // Increment ownership count + sw t5, 8(t1) // Store ownership count + fence rw, rw // Publish owner/count before return + ret + +_protection_busy: + csrw mstatus, a0 // Restore interrupts + j _tx_thread_smp_protect // Restart the protection attempt diff --git a/port/threadx_smp/src/tx_thread_smp_time_get.c b/port/threadx_smp/src/tx_thread_smp_time_get.c new file mode 100644 index 0000000..fd1cb36 --- /dev/null +++ b/port/threadx_smp/src/tx_thread_smp_time_get.c @@ -0,0 +1,8 @@ +#include "platform.h" +#include "tx_api.h" +#include + +ULONG _tx_thread_smp_time_get(void) +{ + return (ULONG)get_aclint_mtime(aclint); +} diff --git a/port/threadx_smp/src/tx_thread_smp_unprotect.S b/port/threadx_smp/src/tx_thread_smp_unprotect.S new file mode 100644 index 0000000..431898b --- /dev/null +++ b/port/threadx_smp/src/tx_thread_smp_unprotect.S @@ -0,0 +1,43 @@ +#include "tx_port.h" + + .section .text + .align 2 + + .global _tx_thread_smp_unprotect + .type _tx_thread_smp_unprotect, @function +_tx_thread_smp_unprotect: + + /* Lockout interrupts while protection state is updated. */ + csrci mstatus, 0x08 // Lockout interrupts + + /* Pickup the current hart ID. */ + csrr t1, mhartid // Pickup hart ID + + /* Build address of protection structure. */ + la t2, _tx_thread_smp_protection + + /* Only the owning hart may release the protection. */ + LWU t3, 4(t2) // Pickup owning hart + bne t1, t3, _still_protected // Not owner, skip release + + /* Pickup and decrement the protection count. */ + LWU t3, 8(t2) // Pickup protection count + beqz t3, _still_protected // Already cleared + addi t3, t3, -1 // Decrement protection count + sw t3, 8(t2) // Store new count + bnez t3, _still_protected // Still nested, stay protected + + /* If preemption is disabled, keep protection in force. */ + la t4, _tx_thread_preempt_disable + LWU t5, 0(t4) // Pickup preempt disable + bnez t5, _still_protected // Skip protection release + + /* Release the protection. */ + li t3, -1 // Invalid owner value + sw t3, 4(t2) // Mark owning hart invalid + fence rw, rw // Ensure shared accesses complete + amoswap.w.rl x0, x0, (t2) // Release protection flag + +_still_protected: + csrw mstatus, a0 // Restore interrupt posture + ret diff --git a/port/threadx_smp/src/tx_thread_stack_build.S b/port/threadx_smp/src/tx_thread_stack_build.S new file mode 100644 index 0000000..d11d2bf --- /dev/null +++ b/port/threadx_smp/src/tx_thread_stack_build.S @@ -0,0 +1,229 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * 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 Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#include "tx_port.h" + + .section .text +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build RISC-V64/GNU */ +/* 6.2.1 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread control blk */ +/* function_ptr Pointer to return function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 03-08-2023 Scott Larson Initial Version 6.2.1 */ +/* */ +/**************************************************************************/ +/* VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +{ */ + .global _tx_thread_stack_build +_tx_thread_stack_build: + + /* Build a fake interrupt frame. The form of the fake interrupt stack + on the RISC-V should look like the following after it is built: + Reg Index + Stack Top: 1 0 Interrupt stack frame type + x27 1 Initial s11 + x26 2 Initial s10 + x25 3 Initial s9 + x24 4 Initial s8 + x23 5 Initial s7 + x22 6 Initial s6 + x21 7 Initial s5 + x20 8 Initial s4 + x19 9 Initial s3 + x18 10 Initial s2 + x9 11 Initial s1 + x8 12 Initial s0 + x31 13 Initial t6 + x30 14 Initial t5 + x29 15 Initial t4 + x28 16 Initial t3 + x7 17 Initial t2 + x6 18 Initial t1 + x5 19 Initial t0 + x17 20 Initial a7 + x16 21 Initial a6 + x15 22 Initial a5 + x14 23 Initial a4 + x13 24 Initial a3 + x12 25 Initial a2 + x11 26 Initial a1 + x10 27 Initial a0 + x1 28 Initial ra + -- 29 reserved + mepc 30 Initial mepc +If floating point support: + f0 31 Inital ft0 + f1 32 Inital ft1 + f2 33 Inital ft2 + f3 34 Inital ft3 + f4 35 Inital ft4 + f5 36 Inital ft5 + f6 37 Inital ft6 + f7 38 Inital ft7 + f8 39 Inital fs0 + f9 40 Inital fs1 + f10 41 Inital fa0 + f11 42 Inital fa1 + f12 43 Inital fa2 + f13 44 Inital fa3 + f14 45 Inital fa4 + f15 46 Inital fa5 + f16 47 Inital fa6 + f17 48 Inital fa7 + f18 49 Inital fs2 + f19 50 Inital fs3 + f20 51 Inital fs4 + f21 52 Inital fs5 + f22 53 Inital fs6 + f23 54 Inital fs7 + f24 55 Inital fs8 + f25 56 Inital fs9 + f26 57 Inital fs10 + f27 58 Inital fs11 + f28 59 Inital ft8 + f29 60 Inital ft9 + f30 61 Inital ft10 + f31 62 Inital ft11 + fscr 63 Inital fscr + + Stack Bottom: (higher memory address) */ + + LOAD t0, TX_THREAD_STACK_END_OFFSET(a0) // Pickup end of stack area + andi t0, t0, -4*REGBYTES // Ensure alignment (16-byte for RV32 & 32-byte for RV64) + + /* Actually build the stack frame. */ + +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + addi t0, t0, -65*REGBYTES +#else + addi t0, t0, -32*REGBYTES // Allocate space for the stack frame +#endif + li t1, 1 // Build stack type + STORE t1, 0*REGBYTES(t0) // Place stack type on the top + STORE x0, 1*REGBYTES(t0) // Initial s11 + STORE x0, 2*REGBYTES(t0) // Initial s10 + STORE x0, 3*REGBYTES(t0) // Initial s9 + STORE x0, 4*REGBYTES(t0) // Initial s8 + STORE x0, 5*REGBYTES(t0) // Initial s7 + STORE x0, 6*REGBYTES(t0) // Initial s6 + STORE x0, 7*REGBYTES(t0) // Initial s5 + STORE x0, 8*REGBYTES(t0) // Initial s4 + STORE x0, 9*REGBYTES(t0) // Initial s3 + STORE x0, 10*REGBYTES(t0) // Initial s2 + STORE x0, 11*REGBYTES(t0) // Initial s1 + STORE x0, 12*REGBYTES(t0) // Initial s0 + STORE x0, 13*REGBYTES(t0) // Initial t6 + STORE x0, 14*REGBYTES(t0) // Initial t5 + STORE x0, 15*REGBYTES(t0) // Initial t4 + STORE x0, 16*REGBYTES(t0) // Initial t3 + STORE x0, 17*REGBYTES(t0) // Initial t2 + STORE x0, 18*REGBYTES(t0) // Initial t1 + STORE x0, 19*REGBYTES(t0) // Initial t0 + STORE x0, 20*REGBYTES(t0) // Initial a7 + STORE x0, 21*REGBYTES(t0) // Initial a6 + STORE x0, 22*REGBYTES(t0) // Initial a5 + STORE x0, 23*REGBYTES(t0) // Initial a4 + STORE x0, 24*REGBYTES(t0) // Initial a3 + STORE x0, 25*REGBYTES(t0) // Initial a2 + STORE x0, 26*REGBYTES(t0) // Initial a1 + STORE x0, 27*REGBYTES(t0) // Initial a0 + STORE x0, 28*REGBYTES(t0) // Initial ra + STORE a1, 30*REGBYTES(t0) // Initial mepc +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + STORE x0, 31*REGBYTES(t0) // Inital ft0 + STORE x0, 32*REGBYTES(t0) // Inital ft1 + STORE x0, 33*REGBYTES(t0) // Inital ft2 + STORE x0, 34*REGBYTES(t0) // Inital ft3 + STORE x0, 35*REGBYTES(t0) // Inital ft4 + STORE x0, 36*REGBYTES(t0) // Inital ft5 + STORE x0, 37*REGBYTES(t0) // Inital ft6 + STORE x0, 38*REGBYTES(t0) // Inital ft7 + STORE x0, 39*REGBYTES(t0) // Inital fs0 + STORE x0, 40*REGBYTES(t0) // Inital fs1 + STORE x0, 41*REGBYTES(t0) // Inital fa0 + STORE x0, 42*REGBYTES(t0) // Inital fa1 + STORE x0, 43*REGBYTES(t0) // Inital fa2 + STORE x0, 44*REGBYTES(t0) // Inital fa3 + STORE x0, 45*REGBYTES(t0) // Inital fa4 + STORE x0, 46*REGBYTES(t0) // Inital fa5 + STORE x0, 47*REGBYTES(t0) // Inital fa6 + STORE x0, 48*REGBYTES(t0) // Inital fa7 + STORE x0, 49*REGBYTES(t0) // Inital fs2 + STORE x0, 50*REGBYTES(t0) // Inital fs3 + STORE x0, 51*REGBYTES(t0) // Inital fs4 + STORE x0, 52*REGBYTES(t0) // Inital fs5 + STORE x0, 53*REGBYTES(t0) // Inital fs6 + STORE x0, 54*REGBYTES(t0) // Inital fs7 + STORE x0, 55*REGBYTES(t0) // Inital fs8 + STORE x0, 56*REGBYTES(t0) // Inital fs9 + STORE x0, 57*REGBYTES(t0) // Inital fs10 + STORE x0, 58*REGBYTES(t0) // Inital fs11 + STORE x0, 59*REGBYTES(t0) // Inital ft8 + STORE x0, 60*REGBYTES(t0) // Inital ft9 + STORE x0, 61*REGBYTES(t0) // Inital ft10 + STORE x0, 62*REGBYTES(t0) // Inital ft11 + csrr a1, fcsr // Read fcsr and use it for initial value for each thread + STORE a1, 63*REGBYTES(t0) // Initial fscr + STORE x0, 64*REGBYTES(t0) // Reserved word (0) +#else + STORE x0, 31*REGBYTES(t0) // Reserved word (0) +#endif + + /* Setup stack pointer. */ + /* thread_ptr -> tx_thread_stack_ptr = t0; */ + + STORE t0, 2*REGBYTES(a0) // Save stack pointer in thread's + addi t1, x0, 1 // Build ready flag + sw t1, TX_THREAD_SMP_LOCK_READY_BIT_OFFSET(a0) // Set ready flag + ret // control block and return +/* } */ diff --git a/port/threadx_smp/src/tx_thread_system_return.S b/port/threadx_smp/src/tx_thread_system_return.S new file mode 100644 index 0000000..5fc5811 --- /dev/null +++ b/port/threadx_smp/src/tx_thread_system_return.S @@ -0,0 +1,198 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * 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 Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#include "tx_port.h" + + .section .text +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return RISC-V64/GNU */ +/* 6.2.1 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the system. Only a minimal context */ +/* is saved since the compiler assumes temp registers are going to get */ +/* slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 03-08-2023 Scott Larson Initial Version 6.2.1 */ +/* */ +/**************************************************************************/ +/* VOID _tx_thread_system_return(VOID) +{ */ + .global _tx_thread_system_return +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + addi sp, sp, -29*REGBYTES // Allocate space on the stack - with floating point enabled +#else + addi sp, sp, -16*REGBYTES // Allocate space on the stack - without floating point enabled +#endif + + /* Store floating point preserved registers. */ +#if defined(__riscv_float_abi_single) + fsw f8, 15*REGBYTES(sp) // Store fs0 + fsw f9, 16*REGBYTES(sp) // Store fs1 + fsw f18, 17*REGBYTES(sp) // Store fs2 + fsw f19, 18*REGBYTES(sp) // Store fs3 + fsw f20, 19*REGBYTES(sp) // Store fs4 + fsw f21, 20*REGBYTES(sp) // Store fs5 + fsw f22, 21*REGBYTES(sp) // Store fs6 + fsw f23, 22*REGBYTES(sp) // Store fs7 + fsw f24, 23*REGBYTES(sp) // Store fs8 + fsw f25, 24*REGBYTES(sp) // Store fs9 + fsw f26, 25*REGBYTES(sp) // Store fs10 + fsw f27, 26*REGBYTES(sp) // Store fs11 + csrr t0, fcsr + STORE t0, 27*REGBYTES(sp) // Store fcsr +#elif defined(__riscv_float_abi_double) + fsd f8, 15*REGBYTES(sp) // Store fs0 + fsd f9, 16*REGBYTES(sp) // Store fs1 + fsd f18, 17*REGBYTES(sp) // Store fs2 + fsd f19, 18*REGBYTES(sp) // Store fs3 + fsd f20, 19*REGBYTES(sp) // Store fs4 + fsd f21, 20*REGBYTES(sp) // Store fs5 + fsd f22, 21*REGBYTES(sp) // Store fs6 + fsd f23, 22*REGBYTES(sp) // Store fs7 + fsd f24, 23*REGBYTES(sp) // Store fs8 + fsd f25, 24*REGBYTES(sp) // Store fs9 + fsd f26, 25*REGBYTES(sp) // Store fs10 + fsd f27, 26*REGBYTES(sp) // Store fs11 + csrr t0, fcsr + STORE t0, 27*REGBYTES(sp) // Store fcsr +#endif + + STORE x0, 0(sp) // Solicited stack type + STORE x1, 13*REGBYTES(sp) // Save RA + STORE x8, 12*REGBYTES(sp) // Save s0 + STORE x9, 11*REGBYTES(sp) // Save s1 + STORE x18, 10*REGBYTES(sp) // Save s2 + STORE x19, 9*REGBYTES(sp) // Save s3 + STORE x20, 8*REGBYTES(sp) // Save s4 + STORE x21, 7*REGBYTES(sp) // Save s5 + STORE x22, 6*REGBYTES(sp) // Save s6 + STORE x23, 5*REGBYTES(sp) // Save s7 + STORE x24, 4*REGBYTES(sp) // Save s8 + STORE x25, 3*REGBYTES(sp) // Save s9 + STORE x26, 2*REGBYTES(sp) // Save s10 + STORE x27, 1*REGBYTES(sp) // Save s11 + csrr t0, mstatus // Pickup mstatus + STORE t0, 14*REGBYTES(sp) // Save mstatus + + + /* Lockout interrupts. - will be enabled in _tx_thread_schedule */ + + csrci mstatus, 0xF + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + call _tx_execution_thread_exit // Call the thread execution exit function +#endif + + csrr t5, mhartid // Pickup current hart ID + slli t6, t5, 2 // Build per-hart ULONG offset + slli t5, t5, LOG_REGBYTES // Build per-hart pointer offset + + la t0, _tx_thread_current_ptr // Pickup base of current-thread array + add t0, t0, t5 // Select this hart's current-thread slot + LOAD t1, 0(t0) // Pickup current thread pointer + la t2, _tx_thread_system_stack_ptr // Pickup base of system-stack array + + /* Save current stack and switch to system stack. */ + /* _tx_thread_current_ptr -> tx_thread_stack_ptr = SP; + SP = _tx_thread_system_stack_ptr; */ + + STORE sp, 2*REGBYTES(t1) // Save stack pointer + add t2, t2, t5 // Select this hart's system-stack slot + LOAD sp, 0(t2) // Switch to system stack + + /* Determine if the time-slice is active. */ + /* if (_tx_timer_time_slice) + { */ + + la t4, _tx_timer_time_slice // Pickup base of time-slice array + add t4, t4, t6 // Select this hart's time-slice slot + lw t3, 0(t4) // Pickup time slice value + la t2, _tx_thread_schedule // Pickup address of scheduling loop + beqz t3, _tx_thread_dont_save_ts // If no time-slice, don't save it + + /* Save time-slice for the thread and clear the current time-slice. */ + /* _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + _tx_timer_time_slice = 0; */ + + sw t3, TX_THREAD_TIME_SLICE_OFFSET(t1) // Save current time-slice for thread + sw x0, 0(t4) // Clear time-slice variable + + /* } */ +_tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + /* _tx_thread_current_ptr = TX_NULL; */ + + STORE x0, 0(t0) // Clear current thread pointer + + /* Make the thread runnable again before returning to the scheduler. */ + fence rw, rw // Publish current-thread clear before ready token + addi t3, t1, TX_THREAD_SMP_LOCK_READY_BIT_OFFSET // Pickup lock/ready-bit address + li t4, 1 // Build ready token + amoswap.w.rl x0, t4, (t3) // Restore ready token + + /* Clear protection state. */ + la t3, _tx_thread_preempt_disable // Pickup preempt-disable address + sw x0, 0(t3) // Clear preempt disable flag + + la t3, _tx_thread_smp_protection // Pickup protection structure + sw x0, 8(t3) // Clear protection count + li t4, -1 // Build invalid owner value + sw t4, 4(t3) // Invalidate owning hart + fence rw, rw // Ensure shared accesses complete before unlock + sw x0, 0(t3) // Clear protection in-force flag + jr t2 // Return to thread scheduler + +/* } */ -- 2.49.1 From 5ebcce634a63c6ff079b6ae97988d169a28c23aa Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Thu, 12 Mar 2026 15:29:41 +0100 Subject: [PATCH 10/79] small cleanup --- port/threadx_smp/CMakeLists.txt | 3 +-- port/threadx_smp/src/tx_thread_smp_time_get.c | 8 -------- 2 files changed, 1 insertion(+), 10 deletions(-) delete mode 100644 port/threadx_smp/src/tx_thread_smp_time_get.c diff --git a/port/threadx_smp/CMakeLists.txt b/port/threadx_smp/CMakeLists.txt index 9bf33f1..0cffc7e 100644 --- a/port/threadx_smp/CMakeLists.txt +++ b/port/threadx_smp/CMakeLists.txt @@ -8,7 +8,7 @@ endif() set(THREADX_SMP_CUSTOM_INC ${CMAKE_CURRENT_SOURCE_DIR}/inc - ${CMAKE_CURRENT_SOURCE_DIR}/../moonlight + ${CMAKE_CURRENT_SOURCE_DIR}/../moonlight #needed for aclint (inter process interrupts) ) set(THREADX_SMP_CUSTOM_SRC src/tx_initialize_low_level.S @@ -22,7 +22,6 @@ set(THREADX_SMP_CUSTOM_SRC src/tx_thread_smp_initialize_wait.S src/tx_thread_smp_low_level_initialize.S src/tx_thread_smp_protect.S - src/tx_thread_smp_time_get.c src/tx_thread_smp_unprotect.S src/tx_thread_stack_build.S src/tx_thread_system_return.S diff --git a/port/threadx_smp/src/tx_thread_smp_time_get.c b/port/threadx_smp/src/tx_thread_smp_time_get.c deleted file mode 100644 index fd1cb36..0000000 --- a/port/threadx_smp/src/tx_thread_smp_time_get.c +++ /dev/null @@ -1,8 +0,0 @@ -#include "platform.h" -#include "tx_api.h" -#include - -ULONG _tx_thread_smp_time_get(void) -{ - return (ULONG)get_aclint_mtime(aclint); -} -- 2.49.1 From 3fc721d6f23b1816d49f4ee150ab4f35d3d56177 Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Thu, 12 Mar 2026 15:38:15 +0100 Subject: [PATCH 11/79] moves spm_offset functionality into its own cmake file --- port/threadx_smp/CMakeLists.txt | 62 +++++------------ .../threadx_smp/cmake/ThreadXSmpOffsets.cmake | 67 +++++++++++++++++++ 2 files changed, 83 insertions(+), 46 deletions(-) create mode 100644 port/threadx_smp/cmake/ThreadXSmpOffsets.cmake diff --git a/port/threadx_smp/CMakeLists.txt b/port/threadx_smp/CMakeLists.txt index 0cffc7e..74dd720 100644 --- a/port/threadx_smp/CMakeLists.txt +++ b/port/threadx_smp/CMakeLists.txt @@ -1,6 +1,8 @@ cmake_minimum_required(VERSION 3.24) project(smp_demo LANGUAGES C ASM) +include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/ThreadXSmpOffsets.cmake) + set(THREADX_COMMON_SMP_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../third-party/threadx/common_smp) if(NOT EXISTS "${THREADX_COMMON_SMP_DIR}") message(FATAL_ERROR "could not find ThreadX SMP sources, is the submodule checked out?") @@ -26,27 +28,21 @@ set(THREADX_SMP_CUSTOM_SRC src/tx_thread_stack_build.S src/tx_thread_system_return.S ) -set(THREADX_SMP_GENERATED_INC_DIR ${CMAKE_CURRENT_BINARY_DIR}/generated) -set(THREADX_SMP_OFFSET_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/src/tx_asm_offsets.c) -set(THREADX_SMP_OFFSET_ASM ${THREADX_SMP_GENERATED_INC_DIR}/tx_asm_offsets.s) -set(THREADX_SMP_OFFSET_INC ${THREADX_SMP_GENERATED_INC_DIR}/tx_asm_offsets.inc) -set(THREADX_SMP_OFFSET_INCLUDE_DIRS - ${THREADX_COMMON_SMP_DIR}/inc - ${THREADX_SMP_CUSTOM_INC} + +threadx_smp_add_offsets( + TARGET threadx_smp_offsets + OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/generated + SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/src/tx_asm_offsets.c + INCLUDE_DIRS + ${THREADX_COMMON_SMP_DIR}/inc + ${THREADX_SMP_CUSTOM_INC} + COMPILE_DEFINITIONS + TX_QUEUE_MESSAGE_MAX_SIZE=16 + DEPENDS + ${CMAKE_CURRENT_SOURCE_DIR}/inc/tx_port.h + ${THREADX_COMMON_SMP_DIR}/inc/tx_api.h + OUT_INCLUDE_DIR THREADX_SMP_GENERATED_INC_DIR ) -set(THREADX_SMP_OFFSET_COMPILE_DEFINITIONS - TX_QUEUE_MESSAGE_MAX_SIZE=16 -) -set(THREADX_SMP_OFFSET_INCLUDE_ARGS ${THREADX_SMP_OFFSET_INCLUDE_DIRS}) -list(TRANSFORM THREADX_SMP_OFFSET_INCLUDE_ARGS PREPEND -I) -set(THREADX_SMP_OFFSET_DEFINE_ARGS ${THREADX_SMP_OFFSET_COMPILE_DEFINITIONS}) -list(TRANSFORM THREADX_SMP_OFFSET_DEFINE_ARGS PREPEND -D) -set(THREADX_SMP_OFFSET_CFLAGS ${CMAKE_C_FLAGS}) -if(CMAKE_BUILD_TYPE) - string(TOUPPER ${CMAKE_BUILD_TYPE} THREADX_SMP_BUILD_TYPE_UPPER) - list(APPEND THREADX_SMP_OFFSET_CFLAGS ${CMAKE_C_FLAGS_${THREADX_SMP_BUILD_TYPE_UPPER}}) -endif() -separate_arguments(THREADX_SMP_OFFSET_CFLAGS) file(GLOB THREADX_SMP_SOURCES ${THREADX_COMMON_SMP_DIR}/src/*.c) add_library(threadx_smp STATIC) @@ -62,32 +58,6 @@ target_include_directories(threadx_smp PRIVATE ${THREADX_SMP_GENERATED_INC_DIR} ) target_compile_definitions(threadx_smp PRIVATE TX_QUEUE_MESSAGE_MAX_SIZE=16) #This is addressed in PR #503 - -add_custom_command( - OUTPUT ${THREADX_SMP_OFFSET_INC} - COMMAND ${CMAKE_COMMAND} -E make_directory ${THREADX_SMP_GENERATED_INC_DIR} - COMMAND ${CMAKE_C_COMPILER} - ${CMAKE_C_COMPILER_ARG1} - ${THREADX_SMP_OFFSET_CFLAGS} - ${THREADX_SMP_OFFSET_INCLUDE_ARGS} - ${THREADX_SMP_OFFSET_DEFINE_ARGS} - -S - -o ${THREADX_SMP_OFFSET_ASM} - ${THREADX_SMP_OFFSET_SOURCE} - COMMAND ${CMAKE_COMMAND} - -DINPUT=${THREADX_SMP_OFFSET_ASM} - -DOUTPUT=${THREADX_SMP_OFFSET_INC} - -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/GenerateAsmOffsets.cmake - DEPENDS - ${THREADX_SMP_OFFSET_SOURCE} - ${CMAKE_CURRENT_SOURCE_DIR}/cmake/GenerateAsmOffsets.cmake - ${CMAKE_CURRENT_SOURCE_DIR}/inc/tx_port.h - ${THREADX_COMMON_SMP_DIR}/inc/tx_api.h - COMMAND_EXPAND_LISTS - VERBATIM -) - -add_custom_target(threadx_smp_offsets DEPENDS ${THREADX_SMP_OFFSET_INC}) add_dependencies(threadx_smp threadx_smp_offsets) add_executable(smp_demo) diff --git a/port/threadx_smp/cmake/ThreadXSmpOffsets.cmake b/port/threadx_smp/cmake/ThreadXSmpOffsets.cmake new file mode 100644 index 0000000..1de4447 --- /dev/null +++ b/port/threadx_smp/cmake/ThreadXSmpOffsets.cmake @@ -0,0 +1,67 @@ +function(threadx_smp_add_offsets) + set(options) + set(oneValueArgs TARGET OUTPUT_DIR SOURCE OUT_INCLUDE_DIR) + set(multiValueArgs INCLUDE_DIRS COMPILE_DEFINITIONS DEPENDS) + cmake_parse_arguments(THREADX_SMP_OFFSETS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(NOT THREADX_SMP_OFFSETS_TARGET) + message(FATAL_ERROR "threadx_smp_add_offsets requires TARGET") + endif() + + if(NOT THREADX_SMP_OFFSETS_OUTPUT_DIR) + message(FATAL_ERROR "threadx_smp_add_offsets requires OUTPUT_DIR") + endif() + + if(NOT THREADX_SMP_OFFSETS_SOURCE) + message(FATAL_ERROR "threadx_smp_add_offsets requires SOURCE") + endif() + + if(NOT THREADX_SMP_OFFSETS_OUT_INCLUDE_DIR) + message(FATAL_ERROR "threadx_smp_add_offsets requires OUT_INCLUDE_DIR") + endif() + + set(threadx_smp_generate_script "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/GenerateAsmOffsets.cmake") + set(threadx_smp_offset_asm "${THREADX_SMP_OFFSETS_OUTPUT_DIR}/tx_asm_offsets.s") + set(threadx_smp_offset_inc "${THREADX_SMP_OFFSETS_OUTPUT_DIR}/tx_asm_offsets.inc") + + set(threadx_smp_offset_include_args ${THREADX_SMP_OFFSETS_INCLUDE_DIRS}) + list(TRANSFORM threadx_smp_offset_include_args PREPEND -I) + + set(threadx_smp_offset_define_args ${THREADX_SMP_OFFSETS_COMPILE_DEFINITIONS}) + list(TRANSFORM threadx_smp_offset_define_args PREPEND -D) + + set(threadx_smp_offset_cflags ${CMAKE_C_FLAGS}) + if(CMAKE_BUILD_TYPE) + string(TOUPPER "${CMAKE_BUILD_TYPE}" threadx_smp_build_type_upper) + list(APPEND threadx_smp_offset_cflags ${CMAKE_C_FLAGS_${threadx_smp_build_type_upper}}) + endif() + separate_arguments(threadx_smp_offset_cflags) + + add_custom_command( + OUTPUT ${threadx_smp_offset_inc} + BYPRODUCTS ${threadx_smp_offset_asm} + COMMAND ${CMAKE_COMMAND} -E make_directory ${THREADX_SMP_OFFSETS_OUTPUT_DIR} + COMMAND ${CMAKE_C_COMPILER} + ${CMAKE_C_COMPILER_ARG1} + ${threadx_smp_offset_cflags} + ${threadx_smp_offset_include_args} + ${threadx_smp_offset_define_args} + -S + -o ${threadx_smp_offset_asm} + ${THREADX_SMP_OFFSETS_SOURCE} + COMMAND ${CMAKE_COMMAND} + -DINPUT=${threadx_smp_offset_asm} + -DOUTPUT=${threadx_smp_offset_inc} + -P ${threadx_smp_generate_script} + DEPENDS + ${THREADX_SMP_OFFSETS_SOURCE} + ${threadx_smp_generate_script} + ${THREADX_SMP_OFFSETS_DEPENDS} + COMMAND_EXPAND_LISTS + VERBATIM + ) + + add_custom_target(${THREADX_SMP_OFFSETS_TARGET} DEPENDS ${threadx_smp_offset_inc}) + + set(${THREADX_SMP_OFFSETS_OUT_INCLUDE_DIR} ${THREADX_SMP_OFFSETS_OUTPUT_DIR} PARENT_SCOPE) +endfunction() -- 2.49.1 From 6d635345dd0b5dffe025af191acd07cdaf830c58 Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Thu, 12 Mar 2026 15:56:33 +0100 Subject: [PATCH 12/79] small formatting changes --- port/threadx/src/tx_initialize_low_level.S | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/port/threadx/src/tx_initialize_low_level.S b/port/threadx/src/tx_initialize_low_level.S index 558d705..48ed119 100644 --- a/port/threadx/src/tx_initialize_low_level.S +++ b/port/threadx/src/tx_initialize_low_level.S @@ -138,19 +138,19 @@ trap_handler: .extern __heap_start .extern board_init _tx_initialize_low_level: - STORE sp, _tx_thread_system_stack_ptr, t0 // Save system stack pointer + STORE sp, _tx_thread_system_stack_ptr, t0 // Save system stack pointer - la t0, __heap_start // Pickup first free address - STORE t0, _tx_initialize_unused_memory, t1 // Save unused memory address + la t0, __heap_start // Pickup first free address + STORE t0, _tx_initialize_unused_memory, t1 // Save unused memory address li t0, MSTATUS_MIE - csrrc zero, mstatus, t0 // clear MSTATUS_MIE bit + csrrc zero, mstatus, t0 // clear MSTATUS_MIE bit li t0, (MSTATUS_MPP_M | MSTATUS_MPIE ) - csrrs zero, mstatus, t0 // set MSTATUS_MPP, MPIE bit + csrrs zero, mstatus, t0 // set MSTATUS_MPP, MPIE bit li t0, (MIE_MTIE | MIE_MSIE | MIE_MEIE) - csrrs zero, mie, t0 // set mie + csrrs zero, mie, t0 // set mie #ifdef __riscv_flen li t0, MSTATUS_FS - csrrs zero, mstatus, t0 // set MSTATUS_FS bit to open f/d isa in riscv + csrrs zero, mstatus, t0 // set MSTATUS_FS bit to open f/d isa in riscv fscsr x0 #endif addi sp, sp, -8 -- 2.49.1 From 28aaf8fd96dd3b9f9d75f4397df615c8c2519bcb Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Thu, 12 Mar 2026 15:56:48 +0100 Subject: [PATCH 13/79] makes smp a lib only --- port/threadx_smp/CMakeLists.txt | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/port/threadx_smp/CMakeLists.txt b/port/threadx_smp/CMakeLists.txt index 74dd720..a97bfc7 100644 --- a/port/threadx_smp/CMakeLists.txt +++ b/port/threadx_smp/CMakeLists.txt @@ -34,13 +34,11 @@ threadx_smp_add_offsets( OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/generated SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/src/tx_asm_offsets.c INCLUDE_DIRS - ${THREADX_COMMON_SMP_DIR}/inc - ${THREADX_SMP_CUSTOM_INC} - COMPILE_DEFINITIONS - TX_QUEUE_MESSAGE_MAX_SIZE=16 + ${THREADX_COMMON_SMP_DIR}/inc + ${THREADX_SMP_CUSTOM_INC} DEPENDS - ${CMAKE_CURRENT_SOURCE_DIR}/inc/tx_port.h - ${THREADX_COMMON_SMP_DIR}/inc/tx_api.h + ${CMAKE_CURRENT_SOURCE_DIR}/inc/tx_port.h + ${THREADX_COMMON_SMP_DIR}/inc/tx_api.h OUT_INCLUDE_DIR THREADX_SMP_GENERATED_INC_DIR ) @@ -59,13 +57,3 @@ target_include_directories(threadx_smp PRIVATE ) target_compile_definitions(threadx_smp PRIVATE TX_QUEUE_MESSAGE_MAX_SIZE=16) #This is addressed in PR #503 add_dependencies(threadx_smp threadx_smp_offsets) - -add_executable(smp_demo) -target_include_directories(smp_demo PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/../moonlight -) -target_link_libraries(smp_demo PRIVATE threadx_smp) - -target_sources(smp_demo PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../src/thread_demo/main.c) - -#cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE=$PWD/../../cmake/rv32gc_gnu.cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=YES -- 2.49.1 From 1da9671197336d3556e9d21a4f4a36caa030922d Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Thu, 12 Mar 2026 15:57:02 +0100 Subject: [PATCH 14/79] adds trap_entry to smp lib --- .../threadx_smp/src/tx_initialize_low_level.S | 81 ++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-) diff --git a/port/threadx_smp/src/tx_initialize_low_level.S b/port/threadx_smp/src/tx_initialize_low_level.S index 0602ea9..48ed119 100644 --- a/port/threadx_smp/src/tx_initialize_low_level.S +++ b/port/threadx_smp/src/tx_initialize_low_level.S @@ -10,6 +10,85 @@ #include "csr.h" #include "tx_port.h" + + .section .text + .align 4 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* trap_entry RISC-V64/GNU */ +/* 6.2.1 */ +/* AUTHOR */ +/* */ +/* Jer6y , luojun@oerv.isrc.iscas.ac.cn */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for riscv processor trap handle */ +/* It will do the contex save and call c trap_handler and do contex */ +/* load */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* trap_handler */ +/* */ +/* CALLED BY */ +/* */ +/* hardware exception */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 10-25-2024 Jerry Luo */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + .global trap_entry + .extern _tx_thread_context_restore + trap_entry: +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + addi sp, sp, -65*REGBYTES // Allocate space for all registers - with floating point enabled +#else + addi sp, sp, -32*REGBYTES // Allocate space for all registers - without floating point enabled +#endif + + STORE x1, 28*REGBYTES(sp) // Store RA, 28*REGBYTES(because call will override ra [ra is a calle register in riscv]) + + call _tx_thread_context_save + + csrr a0, mcause + csrr a1, mepc + csrr a2, mtval + addi sp, sp, -8 + STORE ra, 0(sp) + call trap_handler + LOAD ra, 0(sp) + addi sp, sp, 8 + call _tx_thread_context_restore + // it will nerver return +.weak trap_handler +trap_handler: +1: + j 1b .section .text /**************************************************************************/ /* */ @@ -81,4 +160,4 @@ _tx_initialize_low_level: addi sp, sp, 8 la t0, trap_entry csrw mtvec, t0 - ret \ No newline at end of file + ret -- 2.49.1 From c390c4b8db95ef876d30937fb4a35c8d4779cce6 Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Thu, 12 Mar 2026 15:57:28 +0100 Subject: [PATCH 15/79] adds smp demo as target --- CMakeLists.txt | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f0fcd06..eabe543 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,9 @@ target_link_libraries(c PUBLIC gcc) set(THREADX_CUSTOM_PORT ${CMAKE_CURRENT_LIST_DIR}/port/threadx) add_subdirectory(third-party/threadx) target_link_libraries(threadx PUBLIC c) +#Adds threadx_smp +add_subdirectory(port/threadx_smp) +target_link_libraries(threadx_smp PUBLIC c) # Adds netxduo set(NETXDUO_CUSTOM_PORT ${CMAKE_CURRENT_LIST_DIR}/port/threadx) set(NXD_ENABLE_FILE_SERVERS OFF) @@ -30,12 +33,12 @@ endif() ############################################################################### project(threadx_demo C ASM) option(NX_DEBUG "compile netxduo debug output in" OFF) -set(TARGET_MEM "ram" CACHE STRING "memory map to use" ) +set(TARGET_MEM "ram" CACHE STRING "memory map to use") set(CMAKE_EXECUTABLE_SUFFIX_C ".elf") function(setup_target TARGET) set(options) - set(oneValueArgs) # none for now + set(oneValueArgs) # none for now set(multiValueArgs LIBRARIES SOURCES) cmake_parse_arguments(ST "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) if(ST_UNPARSED_ARGUMENTS) @@ -43,13 +46,13 @@ function(setup_target TARGET) endif() add_executable(${TARGET}) - target_sources(${TARGET} PRIVATE - port/picolibc/port.c - port/moonlight/bootup.c - port/moonlight/board.c - port/moonlight/trap_non_vectored.c - port/moonlight/exception.c - port/moonlight/vector_table.c + target_sources(${TARGET} PRIVATE + port/picolibc/port.c + port/moonlight/bootup.c + port/moonlight/board.c + port/moonlight/trap_non_vectored.c + port/moonlight/exception.c + port/moonlight/vector_table.c ) if("netxduo" IN_LIST ST_LIBRARIES) target_sources(${TARGET} PRIVATE port/moonlight/mnrs_network_driver.c) @@ -67,7 +70,7 @@ function(setup_target TARGET) -ffunction-sections ) if(NX_DEBUG) - target_compile_definitions(${TARGET} PRIVATE NX_DEBUG NX_DEBUG_PACKET) + target_compile_definitions(${TARGET} PRIVATE NX_DEBUG NX_DEBUG_PACKET) endif() target_link_directories(${TARGET} PRIVATE src) # needed for linker script includes target_link_options(${TARGET} PRIVATE @@ -80,19 +83,16 @@ function(setup_target TARGET) if(ST_LIBRARIES) target_link_libraries(${TARGET} PRIVATE ${ST_LIBRARIES}) endif() - target_link_libraries(${TARGET} PRIVATE threadx) add_custom_command(TARGET ${TARGET} POST_BUILD - COMMAND ${OBJCOPY} -O ihex $ ${CMAKE_BINARY_DIR}/${TARGET}.hex + COMMAND ${OBJCOPY} -O ihex $ ${CMAKE_BINARY_DIR}/${TARGET}.hex COMMAND ${OBJCOPY} -O binary $ ${CMAKE_BINARY_DIR}/${TARGET}.bin COMMAND ${SIZE} $ - COMMAND ${OBJDUMP} -S $ > ${TARGET}.dis + COMMAND ${OBJDUMP} -S $ > ${TARGET}.dis COMMENT "Creating collateral for ${TARGET}" ) endfunction() -setup_target(thread_demo SOURCES src/thread_demo/main.c) -setup_target(tcp_demo - LIBRARIES netxduo - SOURCES src/tcp_demo/main.c -) +setup_target(thread_demo LIBRARIES threadx SOURCES src/thread_demo/main.c) +setup_target(tcp_demo LIBRARIES threadx netxduo SOURCES src/tcp_demo/main.c) +setup_target(smp_demo LIBRARIES threadx_smp SOURCES src/thread_demo/main.c) -- 2.49.1 From ebe891dad66ac6a6c834aed3cbe84c0d6f020b7c Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Thu, 12 Mar 2026 16:15:57 +0100 Subject: [PATCH 16/79] adds smp tx_timer_interrupt --- port/threadx_smp/CMakeLists.txt | 1 + port/threadx_smp/src/tx_timer_interrupt.c | 129 ++++++++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 port/threadx_smp/src/tx_timer_interrupt.c diff --git a/port/threadx_smp/CMakeLists.txt b/port/threadx_smp/CMakeLists.txt index a97bfc7..898381a 100644 --- a/port/threadx_smp/CMakeLists.txt +++ b/port/threadx_smp/CMakeLists.txt @@ -27,6 +27,7 @@ set(THREADX_SMP_CUSTOM_SRC src/tx_thread_smp_unprotect.S src/tx_thread_stack_build.S src/tx_thread_system_return.S + src/tx_timer_interrupt.c ) threadx_smp_add_offsets( diff --git a/port/threadx_smp/src/tx_timer_interrupt.c b/port/threadx_smp/src/tx_timer_interrupt.c new file mode 100644 index 0000000..bbb07ae --- /dev/null +++ b/port/threadx_smp/src/tx_timer_interrupt.c @@ -0,0 +1,129 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * 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 Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE +#define TX_THREAD_SMP_SOURCE_CODE + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" +#include "tx_timer.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt RISC-V64/GNU */ +/* 6.2.1 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 03-08-2023 Scott Larson Initial Version 6.2.1 */ +/* */ +/**************************************************************************/ +VOID _tx_timer_interrupt(VOID) +{ +UINT saved_posture; + + + /* Only core 0 advances the global timer wheel. */ + if (TX_SMP_CORE_ID != ((UINT) 0)) + { + return; + } + + /* Get the protection. */ + saved_posture = _tx_thread_smp_protect(); + + /* Indicate timer interrupt processing is active. */ + _tx_timer_interrupt_active++; + + /* Increment system clock. */ + _tx_timer_system_clock++; + + /* Test for timer expiration. */ + if (*_tx_timer_current_ptr) + { + + /* Set expiration flag. */ + _tx_timer_expired = TX_TRUE; + } + else + { + + /* No timer expired, increment the timer pointer. */ + _tx_timer_current_ptr++; + + /* Check for wrap-around. */ + if (_tx_timer_current_ptr == _tx_timer_list_end) + { + + /* Wrap to beginning of list. */ + _tx_timer_current_ptr = _tx_timer_list_start; + } + } + + /* Did a timer expire? */ + if (_tx_timer_expired) + { + /* Process timer expiration. */ + _tx_timer_expiration_process(); + } + + /* Process time-slice expiration for all cores. */ + _tx_thread_time_slice(); + + /* Timer interrupt processing is no longer active. */ + _tx_timer_interrupt_active--; + + /* Release the protection. */ + _tx_thread_smp_unprotect(saved_posture); +} -- 2.49.1 From 239cd26a5c3385d61a94b537dd68d3107680e4aa Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Thu, 12 Mar 2026 16:31:51 +0100 Subject: [PATCH 17/79] adds stub for dynamic core discovery in port --- port/threadx_smp/inc/tx_port.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/port/threadx_smp/inc/tx_port.h b/port/threadx_smp/inc/tx_port.h index c6e5673..5553645 100644 --- a/port/threadx_smp/inc/tx_port.h +++ b/port/threadx_smp/inc/tx_port.h @@ -69,7 +69,7 @@ #define LWU lw #define LOG_REGBYTES 2 #endif -#define REGBYTES (1 << LOG_REGBYTES) +#define REGBYTES (1 << LOG_REGBYTES) #include "tx_asm_offsets.inc" @@ -89,6 +89,13 @@ #define TX_THREAD_SMP_CORE_MASK 0xF /* Where bit 0 represents Core 0, bit 1 represents Core 1, etc. */ #endif +/* Declare ThreadX to discover the cores dynamically. The ceiling is given by TX_THREAD_SMP_MAX_CORES */ +/* +The current implementation does not support dynamic discovery, but simply set the runtime determined number to +TX_THREAD_SMP_MAX_CORES. See tx_thread_smp_low_level_initialize.S +#define TX_THREAD_SMP_DYNAMIC_CORE_MAX +*/ + /* Define INLINE_DECLARE */ #define INLINE_DECLARE inline -- 2.49.1 From 3ba7cdda9e6deee5eb8ba1278b5320814c07afbb Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Thu, 12 Mar 2026 20:17:42 +0100 Subject: [PATCH 18/79] fortifies regex for asm offset parsing --- port/threadx_smp/cmake/GenerateAsmOffsets.cmake | 2 +- port/threadx_smp/src/tx_asm_offsets.c | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/port/threadx_smp/cmake/GenerateAsmOffsets.cmake b/port/threadx_smp/cmake/GenerateAsmOffsets.cmake index 9657e20..4b598e3 100644 --- a/port/threadx_smp/cmake/GenerateAsmOffsets.cmake +++ b/port/threadx_smp/cmake/GenerateAsmOffsets.cmake @@ -11,7 +11,7 @@ file(STRINGS "${INPUT}" OFFSET_LINES REGEX "->") set(OFFSET_CONTENT "/* Generated by GenerateAsmOffsets.cmake. */\n") foreach(LINE IN LISTS OFFSET_LINES) - string(REGEX MATCH "->([A-Za-z0-9_]+)[^0-9-]*(-?[0-9]+)" _ "${LINE}") + string(REGEX MATCH "-->([A-Za-z0-9_]+)[^0-9-]*(-?[0-9]+)" _ "${LINE}") if(NOT CMAKE_MATCH_1) continue() endif() diff --git a/port/threadx_smp/src/tx_asm_offsets.c b/port/threadx_smp/src/tx_asm_offsets.c index 1ec59fc..50324d1 100644 --- a/port/threadx_smp/src/tx_asm_offsets.c +++ b/port/threadx_smp/src/tx_asm_offsets.c @@ -2,9 +2,7 @@ #include "tx_api.h" - -#define TX_ASM_OFFSET(symbol, value) __asm__ volatile("\n.ascii \"->" #symbol " %c0\\n\"" : : "i" (value)) - +#define TX_ASM_OFFSET(symbol, value) __asm__ volatile("\n.ascii \"-->" #symbol " %c0\\n\"" : : "i"(value)) void tx_asm_offsets_generate(void) { -- 2.49.1 From 58fc04bbb35e7887d18f513256b80d5dcf982c66 Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Mon, 16 Mar 2026 08:08:07 +0100 Subject: [PATCH 19/79] adds trap handler --- port/moonlight/trap_non_vectored.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/port/moonlight/trap_non_vectored.c b/port/moonlight/trap_non_vectored.c index 513778c..0f891d7 100644 --- a/port/moonlight/trap_non_vectored.c +++ b/port/moonlight/trap_non_vectored.c @@ -1,4 +1,5 @@ #include "hwtimer.h" +#include "platform.h" #include "riscv-traps.h" #include #include @@ -39,6 +40,11 @@ void trap_handler(uintptr_t mcause, uintptr_t mepc, uintptr_t mtval) { hwtimer_handler(); _tx_timer_interrupt(); break; +#ifdef TX_THREAD_SMP_INTER_CORE_INTERRUPT + case RISCV_INT_MSI: + set_aclint_msip(aclint, csr_read_mhartid(), 0); + break; +#endif case RISCV_INT_MEI: puts("[INTERRUPT]: handler ext irq error!\n"); while(1) -- 2.49.1 From 2d0ec274ac7a0b7e0556b2749f811efc53c1296b Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Mon, 16 Mar 2026 08:08:18 +0100 Subject: [PATCH 20/79] makes bootup multi core able --- port/moonlight/bootup.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/port/moonlight/bootup.c b/port/moonlight/bootup.c index d4f15f7..d212bcf 100644 --- a/port/moonlight/bootup.c +++ b/port/moonlight/bootup.c @@ -9,7 +9,7 @@ #include #include #include - +#include #ifdef __cplusplus #define EXTERN_C extern "C" #else @@ -50,7 +50,6 @@ extern int main(void); // The linker script will place this in the reset entry point. // It will be 'called' with no stack or C runtime configuration. -// NOTE - this only supports a single hart. // tp will not be initialized void _start(void) { // Setup SP and GP @@ -64,6 +63,24 @@ void _start(void) { "la gp, __global_pointer$;" ".option pop;" "la sp, _sp;" +#if defined(__riscv_zicsr) + "csrr t0, mhartid;" +#else + "li t0, 0;" +#endif + "la t1, __stack_size;" + "la t1, __stack_size;" + "la sp, _sp;" + // Loop incase M extension is not present + "1:;" + "beqz t0, 2f;" + "sub sp, sp, t1;" + "addi t0, t0, -1;" + "j 1b;" + "2:;" +#ifdef TX_THREAD_SMP_MAX_CORES + "call _tx_thread_smp_initialize_wait;" +#endif "jal zero, _initialize;" : /* output: none %0 */ : /* input: none */ -- 2.49.1 From 2433b66dc08d33c7df247a068fd1c75dc587a199 Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Mon, 16 Mar 2026 08:08:49 +0100 Subject: [PATCH 21/79] corrects port files, enables wakeups in port --- port/threadx_smp/inc/tx_port.h | 4 ++ .../src/tx_thread_smp_initialize_wait.S | 64 ++++++++++++++++--- .../src/tx_thread_smp_low_level_initialize.S | 22 ------- 3 files changed, 60 insertions(+), 30 deletions(-) diff --git a/port/threadx_smp/inc/tx_port.h b/port/threadx_smp/inc/tx_port.h index 5553645..aefe3fd 100644 --- a/port/threadx_smp/inc/tx_port.h +++ b/port/threadx_smp/inc/tx_port.h @@ -112,6 +112,10 @@ TX_THREAD_SMP_MAX_CORES. See tx_thread_smp_low_level_initialize.S #define TX_THREAD_SMP_INTER_CORE_INTERRUPT +/* Use default wakeup logic*/ + +#define TX_THREAD_SMP_DEFAULT_WAKEUP_LOGIC + /* Determine if there is customer-specific wakeup logic needed. */ #ifdef TX_THREAD_SMP_WAKEUP_LOGIC diff --git a/port/threadx_smp/src/tx_thread_smp_initialize_wait.S b/port/threadx_smp/src/tx_thread_smp_initialize_wait.S index db78833..1f4e459 100644 --- a/port/threadx_smp/src/tx_thread_smp_initialize_wait.S +++ b/port/threadx_smp/src/tx_thread_smp_initialize_wait.S @@ -1,23 +1,71 @@ #include "tx_port.h" - +#include "csr.h" .section .text .align 2 .global _tx_thread_smp_initialize_wait .type _tx_thread_smp_initialize_wait, @function + .extern _tx_thread_schedule _tx_thread_smp_initialize_wait: - /* Core 0 continues initialization. All other harts wait until the - release flag is set by the low-level SMP initialization path. */ + /* Lockout interrupts while startup synchronization is in progress. */ + csrci mstatus, 0x08 // Lockout interrupts + + /* Pickup current hart ID. */ csrr t0, mhartid // Pickup current hart ID beqz t0, _tx_thread_smp_initialize_done // Core 0 does not wait - la t1, _tx_thread_smp_release_cores_flag // Release flag address + /* Build per-hart offsets for ULONG and pointer arrays. */ + slli t1, t0, 2 // ULONG array offset + slli t2, t0, LOG_REGBYTES // Pointer array offset -_tx_thread_smp_initialize_wait_loop: - LWU t2, 0(t1) // Pickup release flag - bnez t2, _tx_thread_smp_initialize_done // Exit once core 0 releases secondaries - j _tx_thread_smp_initialize_wait_loop // Keep waiting + /* Wait until ThreadX has acknowledged this hart by setting its + system state to TX_INITIALIZE_IN_PROGRESS. */ + li t3, 0xF0F0F0F0 // TX_INITIALIZE_IN_PROGRESS + la t4, _tx_thread_system_state // Base of system state array + add t5, t4, t1 // This hart's system state slot + +_tx_thread_smp_wait_for_initialize: + LWU t6, 0(t5) // Pickup current hart's system state + bne t6, t3, _tx_thread_smp_wait_for_initialize + + /* Save the system stack pointer for this hart. */ + la t3, _tx_thread_system_stack_ptr // Base of system stack pointer array + add t3, t3, t2 // Select this hart's slot + STORE sp, 0(t3) // Save system stack pointer + + /* Wait for core 0 to release the secondary harts. */ + la t3, _tx_thread_smp_release_cores_flag // Release flag address + +_tx_thread_smp_wait_for_release: + LWU t6, 0(t3) // Pickup release flag + beqz t6, _tx_thread_smp_wait_for_release + + /* Acknowledge the release by clearing this hart's system state. */ + sw x0, 0(t5) // Set this hart's system state to zero + + /* Wait for core 0 to finish initialization. */ +_tx_thread_smp_wait_for_core0: + LWU t6, 0(t4) // Pickup core 0 system state + bnez t6, _tx_thread_smp_wait_for_core0 + + /* Prepare interrupt state */ + li t0, MSTATUS_MIE + csrrc zero, mstatus, t0 // clear MSTATUS_MIE bit + li t0, (MSTATUS_MPP_M | MSTATUS_MPIE ) + csrrs zero, mstatus, t0 // set MSTATUS_MPP, MPIE bit + li t0, (MIE_MTIE | MIE_MSIE | MIE_MEIE) + csrrs zero, mie, t0 // set mie +#ifdef __riscv_flen + li t0, MSTATUS_FS + csrrs zero, mstatus, t0 // set MSTATUS_FS bit to open f/d isa in riscv + fscsr x0 +#endif + la t0, trap_entry + csrw mtvec, t0 + + /* Initialization is complete for this hart, enter the scheduler. */ + j _tx_thread_schedule _tx_thread_smp_initialize_done: ret diff --git a/port/threadx_smp/src/tx_thread_smp_low_level_initialize.S b/port/threadx_smp/src/tx_thread_smp_low_level_initialize.S index f2f7357..fb8536e 100644 --- a/port/threadx_smp/src/tx_thread_smp_low_level_initialize.S +++ b/port/threadx_smp/src/tx_thread_smp_low_level_initialize.S @@ -6,26 +6,4 @@ .global _tx_thread_smp_low_level_initialize .type _tx_thread_smp_low_level_initialize, @function _tx_thread_smp_low_level_initialize: - - /* Only hart 0 performs low-level SMP initialization. */ - csrr t0, mhartid // Pickup current hart ID - bnez t0, _tx_thread_smp_low_level_initialize_done - - /* Keep secondary harts parked until kernel-enter releases them. */ - la t1, _tx_thread_smp_release_cores_flag - sw x0, 0(t1) // Clear release flag - -#ifdef TX_THREAD_SMP_DYNAMIC_CORE_MAX - /* Record the detected core count supplied by the caller. */ - la t1, _tx_thread_smp_detected_cores - sw a0, 0(t1) -#endif - - /* Platform-specific secondary-hart startup can go here. - This is where core 0 would program boot addresses, send startup IPIs, - or otherwise bring the other harts online. */ - - fence rw, rw // Publish startup state - -_tx_thread_smp_low_level_initialize_done: ret -- 2.49.1 From 950dd213c80f6a8624ff6910e9791c58eabba7e6 Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Sat, 21 Mar 2026 15:10:45 +0100 Subject: [PATCH 22/79] makes setup_target function more robust when being called in other CMakeLists.txt files --- CMakeLists.txt | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index eabe543..91f75a2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,22 +47,22 @@ function(setup_target TARGET) add_executable(${TARGET}) target_sources(${TARGET} PRIVATE - port/picolibc/port.c - port/moonlight/bootup.c - port/moonlight/board.c - port/moonlight/trap_non_vectored.c - port/moonlight/exception.c - port/moonlight/vector_table.c + ${CMAKE_SOURCE_DIR}/port/picolibc/port.c + ${CMAKE_SOURCE_DIR}/port/moonlight/bootup.c + ${CMAKE_SOURCE_DIR}/port/moonlight/board.c + ${CMAKE_SOURCE_DIR}/port/moonlight/trap_non_vectored.c + ${CMAKE_SOURCE_DIR}/port/moonlight/exception.c + ${CMAKE_SOURCE_DIR}/port/moonlight/vector_table.c ) if("netxduo" IN_LIST ST_LIBRARIES) - target_sources(${TARGET} PRIVATE port/moonlight/mnrs_network_driver.c) + target_sources(${TARGET} PRIVATE ${CMAKE_SOURCE_DIR}/port/moonlight/mnrs_network_driver.c) endif() if(ST_SOURCES) target_sources(${TARGET} PRIVATE ${ST_SOURCES}) endif() - target_include_directories(${TARGET} PRIVATE port/moonlight src) + target_include_directories(${TARGET} PRIVATE ${CMAKE_SOURCE_DIR}/port/moonlight ${CMAKE_SOURCE_DIR}/src) target_compile_options(${TARGET} PRIVATE -ffreestanding -fno-builtin @@ -72,7 +72,7 @@ function(setup_target TARGET) if(NX_DEBUG) target_compile_definitions(${TARGET} PRIVATE NX_DEBUG NX_DEBUG_PACKET) endif() - target_link_directories(${TARGET} PRIVATE src) # needed for linker script includes + target_link_directories(${TARGET} PRIVATE ${CMAKE_SOURCE_DIR}/src) # needed for linker script includes target_link_options(${TARGET} PRIVATE -nostartfiles -nostdlib -- 2.49.1 From 3ccc0de209c06ec78fc84af75ff60cc728c0a18e Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Sat, 21 Mar 2026 15:12:08 +0100 Subject: [PATCH 23/79] adds overlooked file This was just never used in the demos and thus not noticed --- port/threadx/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/port/threadx/CMakeLists.txt b/port/threadx/CMakeLists.txt index e9c2e6a..f05a060 100644 --- a/port/threadx/CMakeLists.txt +++ b/port/threadx/CMakeLists.txt @@ -5,6 +5,7 @@ target_sources(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR}/src/tx_initialize_low_level.S ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_context_restore.S ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_context_save.S + ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_interrupt_control.S ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_schedule.S ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_stack_build.S ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_system_return.S -- 2.49.1 From ba1b9c44eeaa1b007c4d439a3344b454cac45ae2 Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Sat, 21 Mar 2026 15:33:02 +0100 Subject: [PATCH 24/79] makes the port provide its dependencies as a library --- CMakeLists.txt | 37 ++++++----------------- port/moonlight/CMakeLists.txt | 55 +++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 28 deletions(-) create mode 100644 port/moonlight/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 91f75a2..b488de6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,54 +36,35 @@ option(NX_DEBUG "compile netxduo debug output in" OFF) set(TARGET_MEM "ram" CACHE STRING "memory map to use") set(CMAKE_EXECUTABLE_SUFFIX_C ".elf") +add_subdirectory(port/moonlight) + function(setup_target TARGET) set(options) set(oneValueArgs) # none for now set(multiValueArgs LIBRARIES SOURCES) cmake_parse_arguments(ST "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) if(ST_UNPARSED_ARGUMENTS) - message(FATAL_ERROR "setup_target(${target} ...): unknown args: ${ST_UNPARSED_ARGUMENTS}") + message(FATAL_ERROR "setup_target(${TARGET} ...): unknown args: ${ST_UNPARSED_ARGUMENTS}") endif() add_executable(${TARGET}) - target_sources(${TARGET} PRIVATE - ${CMAKE_SOURCE_DIR}/port/picolibc/port.c - ${CMAKE_SOURCE_DIR}/port/moonlight/bootup.c - ${CMAKE_SOURCE_DIR}/port/moonlight/board.c - ${CMAKE_SOURCE_DIR}/port/moonlight/trap_non_vectored.c - ${CMAKE_SOURCE_DIR}/port/moonlight/exception.c - ${CMAKE_SOURCE_DIR}/port/moonlight/vector_table.c - ) + target_add_moonlight_platform(${TARGET}) + if("netxduo" IN_LIST ST_LIBRARIES) - target_sources(${TARGET} PRIVATE ${CMAKE_SOURCE_DIR}/port/moonlight/mnrs_network_driver.c) + target_add_moonlight_network_driver(${TARGET}) endif() if(ST_SOURCES) target_sources(${TARGET} PRIVATE ${ST_SOURCES}) endif() - target_include_directories(${TARGET} PRIVATE ${CMAKE_SOURCE_DIR}/port/moonlight ${CMAKE_SOURCE_DIR}/src) - target_compile_options(${TARGET} PRIVATE - -ffreestanding - -fno-builtin - -fdata-sections - -ffunction-sections - ) - if(NX_DEBUG) - target_compile_definitions(${TARGET} PRIVATE NX_DEBUG NX_DEBUG_PACKET) - endif() - target_link_directories(${TARGET} PRIVATE ${CMAKE_SOURCE_DIR}/src) # needed for linker script includes - target_link_options(${TARGET} PRIVATE - -nostartfiles - -nostdlib - -T ${CMAKE_SOURCE_DIR}/src/${TARGET_MEM}.lds - -Wl,--gc-sections - -Wl,-Map=${CMAKE_BINARY_DIR}/${TARGET}.map - ) if(ST_LIBRARIES) target_link_libraries(${TARGET} PRIVATE ${ST_LIBRARIES}) endif() + target_link_options(${TARGET} PRIVATE + -Wl,-Map=${CMAKE_BINARY_DIR}/${TARGET}.map) + add_custom_command(TARGET ${TARGET} POST_BUILD COMMAND ${OBJCOPY} -O ihex $ ${CMAKE_BINARY_DIR}/${TARGET}.hex COMMAND ${OBJCOPY} -O binary $ ${CMAKE_BINARY_DIR}/${TARGET}.bin diff --git a/port/moonlight/CMakeLists.txt b/port/moonlight/CMakeLists.txt new file mode 100644 index 0000000..a211b39 --- /dev/null +++ b/port/moonlight/CMakeLists.txt @@ -0,0 +1,55 @@ +set(MOONLIGHT_ROOT ${CMAKE_CURRENT_LIST_DIR}) +set(THREADX4TGFS_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../..) + +set(MOONLIGHT_PLATFORM_SOURCES + ${THREADX4TGFS_ROOT}/port/picolibc/port.c + ${MOONLIGHT_ROOT}/bootup.c + ${MOONLIGHT_ROOT}/board.c + ${MOONLIGHT_ROOT}/trap_non_vectored.c + ${MOONLIGHT_ROOT}/exception.c + ${MOONLIGHT_ROOT}/vector_table.c) + +add_library(moonlight_platform_defaults INTERFACE) +target_include_directories(moonlight_platform_defaults INTERFACE + ${MOONLIGHT_ROOT} + ${THREADX4TGFS_ROOT}/src) +target_compile_options(moonlight_platform_defaults INTERFACE + -ffreestanding + -fno-builtin + -fdata-sections + -ffunction-sections) +target_link_directories(moonlight_platform_defaults INTERFACE + ${THREADX4TGFS_ROOT}/src) +target_link_options(moonlight_platform_defaults INTERFACE + -nostartfiles + -nostdlib + -T ${THREADX4TGFS_ROOT}/src/${TARGET_MEM}.lds + -Wl,--gc-sections) +if(NX_DEBUG) + target_compile_definitions(moonlight_platform_defaults INTERFACE + NX_DEBUG + NX_DEBUG_PACKET) +endif() + +add_library(moonlight_platform_common OBJECT + ${MOONLIGHT_PLATFORM_SOURCES}) +target_link_libraries(moonlight_platform_common PUBLIC + moonlight_platform_defaults + c + threadx) + +add_library(moonlight_network_driver OBJECT + ${MOONLIGHT_ROOT}/mnrs_network_driver.c) +target_link_libraries(moonlight_network_driver PUBLIC + moonlight_platform_defaults + c + threadx + netxduo) + +function(target_add_moonlight_platform TARGET) + target_link_libraries(${TARGET} PRIVATE moonlight_platform_common) +endfunction() + +function(target_add_moonlight_network_driver TARGET) + target_link_libraries(${TARGET} PRIVATE moonlight_network_driver) +endfunction() -- 2.49.1 From 083565251e09942994b8395006f2654f823e3e41 Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Sat, 21 Mar 2026 15:40:54 +0100 Subject: [PATCH 25/79] cleans up moonlight dir --- port/moonlight/CMakeLists.txt | 16 +++++++++------- port/moonlight/{ => inc}/aclint.h | 0 port/moonlight/{ => inc}/aclint_ipi.h | 0 port/moonlight/{ => inc}/gen/aclint.h | 0 port/moonlight/{ => inc}/gen/ethmac.h | 0 port/moonlight/{ => inc}/gen/uart.h | 0 port/moonlight/{ => inc}/hwtimer.h | 2 +- port/moonlight/{ => inc}/platform.h | 0 port/moonlight/{ => inc}/riscv-csr.h | 0 port/moonlight/{ => inc}/riscv-traps.h | 0 port/moonlight/{ => inc}/uart.h | 0 port/moonlight/{ => inc}/vector_table.h | 0 port/moonlight/{ => src}/board.c | 0 port/moonlight/{ => src}/bootup.c | 0 port/moonlight/{ => src}/exception.c | 0 port/moonlight/{ => src}/mnrs_network_driver.c | 0 port/moonlight/{ => src}/trap_non_vectored.c | 0 port/moonlight/{ => src}/trap_vectored.c | 0 port/moonlight/{ => src}/vector_table.c | 0 port/threadx_smp/CMakeLists.txt | 2 +- 20 files changed, 11 insertions(+), 9 deletions(-) rename port/moonlight/{ => inc}/aclint.h (100%) rename port/moonlight/{ => inc}/aclint_ipi.h (100%) rename port/moonlight/{ => inc}/gen/aclint.h (100%) rename port/moonlight/{ => inc}/gen/ethmac.h (100%) rename port/moonlight/{ => inc}/gen/uart.h (100%) rename port/moonlight/{ => inc}/hwtimer.h (92%) rename port/moonlight/{ => inc}/platform.h (100%) rename port/moonlight/{ => inc}/riscv-csr.h (100%) rename port/moonlight/{ => inc}/riscv-traps.h (100%) rename port/moonlight/{ => inc}/uart.h (100%) rename port/moonlight/{ => inc}/vector_table.h (100%) rename port/moonlight/{ => src}/board.c (100%) rename port/moonlight/{ => src}/bootup.c (100%) rename port/moonlight/{ => src}/exception.c (100%) rename port/moonlight/{ => src}/mnrs_network_driver.c (100%) rename port/moonlight/{ => src}/trap_non_vectored.c (100%) rename port/moonlight/{ => src}/trap_vectored.c (100%) rename port/moonlight/{ => src}/vector_table.c (100%) diff --git a/port/moonlight/CMakeLists.txt b/port/moonlight/CMakeLists.txt index a211b39..febfa5c 100644 --- a/port/moonlight/CMakeLists.txt +++ b/port/moonlight/CMakeLists.txt @@ -1,17 +1,19 @@ set(MOONLIGHT_ROOT ${CMAKE_CURRENT_LIST_DIR}) set(THREADX4TGFS_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../..) +set(MOONLIGHT_SRC_DIR ${MOONLIGHT_ROOT}/src) +set(MOONLIGHT_INC_DIR ${MOONLIGHT_ROOT}/inc) set(MOONLIGHT_PLATFORM_SOURCES ${THREADX4TGFS_ROOT}/port/picolibc/port.c - ${MOONLIGHT_ROOT}/bootup.c - ${MOONLIGHT_ROOT}/board.c - ${MOONLIGHT_ROOT}/trap_non_vectored.c - ${MOONLIGHT_ROOT}/exception.c - ${MOONLIGHT_ROOT}/vector_table.c) + ${MOONLIGHT_SRC_DIR}/bootup.c + ${MOONLIGHT_SRC_DIR}/board.c + ${MOONLIGHT_SRC_DIR}/trap_non_vectored.c + ${MOONLIGHT_SRC_DIR}/exception.c + ${MOONLIGHT_SRC_DIR}/vector_table.c) add_library(moonlight_platform_defaults INTERFACE) target_include_directories(moonlight_platform_defaults INTERFACE - ${MOONLIGHT_ROOT} + ${MOONLIGHT_INC_DIR} ${THREADX4TGFS_ROOT}/src) target_compile_options(moonlight_platform_defaults INTERFACE -ffreestanding @@ -39,7 +41,7 @@ target_link_libraries(moonlight_platform_common PUBLIC threadx) add_library(moonlight_network_driver OBJECT - ${MOONLIGHT_ROOT}/mnrs_network_driver.c) + ${MOONLIGHT_SRC_DIR}/mnrs_network_driver.c) target_link_libraries(moonlight_network_driver PUBLIC moonlight_platform_defaults c diff --git a/port/moonlight/aclint.h b/port/moonlight/inc/aclint.h similarity index 100% rename from port/moonlight/aclint.h rename to port/moonlight/inc/aclint.h diff --git a/port/moonlight/aclint_ipi.h b/port/moonlight/inc/aclint_ipi.h similarity index 100% rename from port/moonlight/aclint_ipi.h rename to port/moonlight/inc/aclint_ipi.h diff --git a/port/moonlight/gen/aclint.h b/port/moonlight/inc/gen/aclint.h similarity index 100% rename from port/moonlight/gen/aclint.h rename to port/moonlight/inc/gen/aclint.h diff --git a/port/moonlight/gen/ethmac.h b/port/moonlight/inc/gen/ethmac.h similarity index 100% rename from port/moonlight/gen/ethmac.h rename to port/moonlight/inc/gen/ethmac.h diff --git a/port/moonlight/gen/uart.h b/port/moonlight/inc/gen/uart.h similarity index 100% rename from port/moonlight/gen/uart.h rename to port/moonlight/inc/gen/uart.h diff --git a/port/moonlight/hwtimer.h b/port/moonlight/inc/hwtimer.h similarity index 92% rename from port/moonlight/hwtimer.h rename to port/moonlight/inc/hwtimer.h index fb7e7a9..0c339fd 100644 --- a/port/moonlight/hwtimer.h +++ b/port/moonlight/inc/hwtimer.h @@ -15,7 +15,7 @@ #include "platform.h" #define TICKNUM_PER_SECOND 32768 -#define TICKNUM_PER_TIMER (TICKNUM_PER_SECOND / 100) // ~ 1ms timer +#define TICKNUM_PER_TIMER (TICKNUM_PER_SECOND / 1000) // ~ 1ms timer static inline int hwtimer_init(void) { diff --git a/port/moonlight/platform.h b/port/moonlight/inc/platform.h similarity index 100% rename from port/moonlight/platform.h rename to port/moonlight/inc/platform.h diff --git a/port/moonlight/riscv-csr.h b/port/moonlight/inc/riscv-csr.h similarity index 100% rename from port/moonlight/riscv-csr.h rename to port/moonlight/inc/riscv-csr.h diff --git a/port/moonlight/riscv-traps.h b/port/moonlight/inc/riscv-traps.h similarity index 100% rename from port/moonlight/riscv-traps.h rename to port/moonlight/inc/riscv-traps.h diff --git a/port/moonlight/uart.h b/port/moonlight/inc/uart.h similarity index 100% rename from port/moonlight/uart.h rename to port/moonlight/inc/uart.h diff --git a/port/moonlight/vector_table.h b/port/moonlight/inc/vector_table.h similarity index 100% rename from port/moonlight/vector_table.h rename to port/moonlight/inc/vector_table.h diff --git a/port/moonlight/board.c b/port/moonlight/src/board.c similarity index 100% rename from port/moonlight/board.c rename to port/moonlight/src/board.c diff --git a/port/moonlight/bootup.c b/port/moonlight/src/bootup.c similarity index 100% rename from port/moonlight/bootup.c rename to port/moonlight/src/bootup.c diff --git a/port/moonlight/exception.c b/port/moonlight/src/exception.c similarity index 100% rename from port/moonlight/exception.c rename to port/moonlight/src/exception.c diff --git a/port/moonlight/mnrs_network_driver.c b/port/moonlight/src/mnrs_network_driver.c similarity index 100% rename from port/moonlight/mnrs_network_driver.c rename to port/moonlight/src/mnrs_network_driver.c diff --git a/port/moonlight/trap_non_vectored.c b/port/moonlight/src/trap_non_vectored.c similarity index 100% rename from port/moonlight/trap_non_vectored.c rename to port/moonlight/src/trap_non_vectored.c diff --git a/port/moonlight/trap_vectored.c b/port/moonlight/src/trap_vectored.c similarity index 100% rename from port/moonlight/trap_vectored.c rename to port/moonlight/src/trap_vectored.c diff --git a/port/moonlight/vector_table.c b/port/moonlight/src/vector_table.c similarity index 100% rename from port/moonlight/vector_table.c rename to port/moonlight/src/vector_table.c diff --git a/port/threadx_smp/CMakeLists.txt b/port/threadx_smp/CMakeLists.txt index 898381a..e4c04ee 100644 --- a/port/threadx_smp/CMakeLists.txt +++ b/port/threadx_smp/CMakeLists.txt @@ -10,7 +10,7 @@ endif() set(THREADX_SMP_CUSTOM_INC ${CMAKE_CURRENT_SOURCE_DIR}/inc - ${CMAKE_CURRENT_SOURCE_DIR}/../moonlight #needed for aclint (inter process interrupts) + ${CMAKE_CURRENT_SOURCE_DIR}/../moonlight/inc # needed for Moonlight SMP support headers ) set(THREADX_SMP_CUSTOM_SRC src/tx_initialize_low_level.S -- 2.49.1 From d85f524d07ae6fa461fb39babdb8b9e1ee6f9476 Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Sat, 21 Mar 2026 16:32:29 +0100 Subject: [PATCH 26/79] makes setup_target take output dir for binaries and target mem as arguments --- CMakeLists.txt | 24 +++++++++++++++++++----- port/moonlight/CMakeLists.txt | 5 ----- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b488de6..1ca9292 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,14 +40,24 @@ add_subdirectory(port/moonlight) function(setup_target TARGET) set(options) - set(oneValueArgs) # none for now + set(oneValueArgs OUTPUT_DIR TARGET_MEM) set(multiValueArgs LIBRARIES SOURCES) cmake_parse_arguments(ST "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) if(ST_UNPARSED_ARGUMENTS) message(FATAL_ERROR "setup_target(${TARGET} ...): unknown args: ${ST_UNPARSED_ARGUMENTS}") endif() + if(NOT ST_OUTPUT_DIR) + set(ST_OUTPUT_DIR ${CMAKE_BINARY_DIR}) + endif() + if(NOT ST_TARGET_MEM) + set(ST_TARGET_MEM ${TARGET_MEM}) + endif() + add_executable(${TARGET}) + set_target_properties(${TARGET} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${ST_OUTPUT_DIR} + ) target_add_moonlight_platform(${TARGET}) if("netxduo" IN_LIST ST_LIBRARIES) @@ -63,13 +73,17 @@ function(setup_target TARGET) endif() target_link_options(${TARGET} PRIVATE - -Wl,-Map=${CMAKE_BINARY_DIR}/${TARGET}.map) + -nostartfiles + -nostdlib + -T ${CMAKE_SOURCE_DIR}/src/${ST_TARGET_MEM}.lds + -Wl,--gc-sections + -Wl,-Map=${ST_OUTPUT_DIR}/${TARGET}.map) add_custom_command(TARGET ${TARGET} POST_BUILD - COMMAND ${OBJCOPY} -O ihex $ ${CMAKE_BINARY_DIR}/${TARGET}.hex - COMMAND ${OBJCOPY} -O binary $ ${CMAKE_BINARY_DIR}/${TARGET}.bin + COMMAND ${OBJCOPY} -O ihex $ ${ST_OUTPUT_DIR}/${TARGET}.hex + COMMAND ${OBJCOPY} -O binary $ ${ST_OUTPUT_DIR}/${TARGET}.bin COMMAND ${SIZE} $ - COMMAND ${OBJDUMP} -S $ > ${TARGET}.dis + COMMAND ${OBJDUMP} -S $ > ${ST_OUTPUT_DIR}/${TARGET}.dis COMMENT "Creating collateral for ${TARGET}" ) endfunction() diff --git a/port/moonlight/CMakeLists.txt b/port/moonlight/CMakeLists.txt index febfa5c..a25ff5c 100644 --- a/port/moonlight/CMakeLists.txt +++ b/port/moonlight/CMakeLists.txt @@ -22,11 +22,6 @@ target_compile_options(moonlight_platform_defaults INTERFACE -ffunction-sections) target_link_directories(moonlight_platform_defaults INTERFACE ${THREADX4TGFS_ROOT}/src) -target_link_options(moonlight_platform_defaults INTERFACE - -nostartfiles - -nostdlib - -T ${THREADX4TGFS_ROOT}/src/${TARGET_MEM}.lds - -Wl,--gc-sections) if(NX_DEBUG) target_compile_definitions(moonlight_platform_defaults INTERFACE NX_DEBUG -- 2.49.1 From dfa1c2182cfdf7b1d5c9d7da626b77386189df3b Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Sat, 21 Mar 2026 16:34:08 +0100 Subject: [PATCH 27/79] adds initial testing setup --- CMakeLists.txt | 5 ++ port/threadx/inc/tx_port.h | 103 ++++++++++++++++++++++++++++++++++++ test/CMakeLists.txt | 3 ++ test/threadx/CMakeLists.txt | 65 +++++++++++++++++++++++ 4 files changed, 176 insertions(+) create mode 100644 test/CMakeLists.txt create mode 100644 test/threadx/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 1ca9292..819df8c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,7 @@ endif() ############################################################################### project(threadx_demo C ASM) option(NX_DEBUG "compile netxduo debug output in" OFF) +option(ENABLE_THREADX_REGRESSION "Build ThreadX regression test executables" OFF) set(TARGET_MEM "ram" CACHE STRING "memory map to use") set(CMAKE_EXECUTABLE_SUFFIX_C ".elf") @@ -88,6 +89,10 @@ function(setup_target TARGET) ) endfunction() +if(ENABLE_THREADX_REGRESSION) + add_subdirectory(test) +endif() + setup_target(thread_demo LIBRARIES threadx SOURCES src/thread_demo/main.c) setup_target(tcp_demo LIBRARIES threadx netxduo SOURCES src/tcp_demo/main.c) setup_target(smp_demo LIBRARIES threadx_smp SOURCES src/thread_demo/main.c) diff --git a/port/threadx/inc/tx_port.h b/port/threadx/inc/tx_port.h index 1e57546..7ee7252 100644 --- a/port/threadx/inc/tx_port.h +++ b/port/threadx/inc/tx_port.h @@ -203,6 +203,109 @@ typedef unsigned short USHORT; #define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) #define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) +/* Define automated regression test extensions. These are required for the + ThreadX regression tests. */ + +typedef unsigned int TEST_FLAG; +extern TEST_FLAG threadx_byte_allocate_loop_test; +extern TEST_FLAG threadx_byte_release_loop_test; +extern TEST_FLAG threadx_mutex_suspension_put_test; +extern TEST_FLAG threadx_mutex_suspension_priority_test; +#ifndef TX_TIMER_PROCESS_IN_ISR +extern TEST_FLAG threadx_delete_timer_thread; +#endif + +extern void abort_and_resume_byte_allocating_thread(void); +extern void abort_all_threads_suspended_on_mutex(void); +extern void suspend_lowest_priority(void); +#ifndef TX_TIMER_PROCESS_IN_ISR +extern void delete_timer_thread(void); +#endif +extern TEST_FLAG test_stack_analyze_flag; +extern TEST_FLAG test_initialize_flag; +extern TEST_FLAG test_forced_mutex_timeout; + +#ifdef TX_REGRESSION_TEST + +/* Define extension macros for automated coverage tests. */ + +#define TX_BYTE_ALLOCATE_EXTENSION \ + if (threadx_byte_allocate_loop_test == ((TEST_FLAG)1)) \ + { \ + pool_ptr->tx_byte_pool_owner = TX_NULL; \ + threadx_byte_allocate_loop_test = ((TEST_FLAG)0); \ + } + +#define TX_BYTE_RELEASE_EXTENSION \ + if (threadx_byte_release_loop_test == ((TEST_FLAG)1)) \ + { \ + threadx_byte_release_loop_test = ((TEST_FLAG)0); \ + abort_and_resume_byte_allocating_thread(); \ + } + +#define TX_MUTEX_PUT_EXTENSION_1 \ + if (threadx_mutex_suspension_put_test == ((TEST_FLAG)1)) \ + { \ + threadx_mutex_suspension_put_test = ((TEST_FLAG)0); \ + abort_all_threads_suspended_on_mutex(); \ + } + +#define TX_MUTEX_PUT_EXTENSION_2 \ + if (test_forced_mutex_timeout == ((TEST_FLAG)1)) \ + { \ + test_forced_mutex_timeout = ((TEST_FLAG)0); \ + _tx_thread_wait_abort(mutex_ptr->tx_mutex_suspension_list); \ + } + +#define TX_MUTEX_PRIORITY_CHANGE_EXTENSION \ + if (threadx_mutex_suspension_priority_test == ((TEST_FLAG)1)) \ + { \ + threadx_mutex_suspension_priority_test = ((TEST_FLAG)0); \ + suspend_lowest_priority(); \ + } + +#ifndef TX_TIMER_PROCESS_IN_ISR + +#define TX_TIMER_INITIALIZE_EXTENSION(a) \ + if (threadx_delete_timer_thread == ((TEST_FLAG)1)) \ + { \ + threadx_delete_timer_thread = ((TEST_FLAG)0); \ + delete_timer_thread(); \ + (a) = ((UINT)1); \ + } + +#endif + +#define TX_THREAD_STACK_ANALYZE_EXTENSION \ + if (test_stack_analyze_flag == ((TEST_FLAG)1)) \ + { \ + thread_ptr->tx_thread_id = ((TEST_FLAG)0); \ + test_stack_analyze_flag = ((TEST_FLAG)0); \ + } \ + else if (test_stack_analyze_flag == ((TEST_FLAG)2)) \ + { \ + stack_ptr = thread_ptr->tx_thread_stack_start; \ + test_stack_analyze_flag = ((TEST_FLAG)0); \ + } \ + else if (test_stack_analyze_flag == ((TEST_FLAG)3)) \ + { \ + *stack_ptr = TX_STACK_FILL; \ + test_stack_analyze_flag = ((TEST_FLAG)0); \ + } \ + else \ + { \ + test_stack_analyze_flag = ((TEST_FLAG)0); \ + } + +#define TX_INITIALIZE_KERNEL_ENTER_EXTENSION \ + if (test_initialize_flag == ((TEST_FLAG)1)) \ + { \ + test_initialize_flag = ((TEST_FLAG)0); \ + return; \ + } + +#endif + /* Define the ThreadX object creation extensions for the remaining objects. */ #define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..f456146 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.21) + +add_subdirectory(threadx) diff --git a/test/threadx/CMakeLists.txt b/test/threadx/CMakeLists.txt new file mode 100644 index 0000000..8352b07 --- /dev/null +++ b/test/threadx/CMakeLists.txt @@ -0,0 +1,65 @@ +cmake_minimum_required(VERSION 3.21) + +enable_testing() + +set(TX_REGRESSION_DIR + ${CMAKE_SOURCE_DIR}/third-party/threadx/test/tx/regression +) +set(TX_CMAKE_DIR + ${CMAKE_SOURCE_DIR}/third-party/threadx/test/tx/cmake +) + +set(TX_REGRESSION_CASES + ${TX_REGRESSION_DIR}/threadx_block_memory_basic_test.c + ${TX_REGRESSION_DIR}/threadx_byte_memory_basic_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_basic_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_basic_test.c + ${TX_REGRESSION_DIR}/threadx_queue_basic_one_word_test.c + ${TX_REGRESSION_DIR}/threadx_semaphore_basic_test.c + ${TX_REGRESSION_DIR}/threadx_thread_basic_execution_test.c + ${TX_REGRESSION_DIR}/threadx_timer_simple_test.c +) + +set(TX_REGRESSION_TARGETS) +set(TX_REGRESSION_OUTPUT_DIR ${CMAKE_BINARY_DIR}/test) +set(TX_REGRESSION_TARGET_MEM ram_dram) + +add_library( + threadx_regression_support STATIC + ${TX_REGRESSION_DIR}/testcontrol.c + ${TX_CMAKE_DIR}/samples/fake.c +) +target_link_libraries(threadx_regression_support PUBLIC threadx c) +target_compile_definitions( + threadx_regression_support + PUBLIC + CTEST + BATCH_TEST + TEST_STACK_SIZE_PRINTF=4096 +) +function(add_threadx_regression_test TEST_SOURCE) + get_filename_component(TEST_NAME ${TEST_SOURCE} NAME_WE) + + setup_target( + ${TEST_NAME} + OUTPUT_DIR ${TX_REGRESSION_OUTPUT_DIR} + TARGET_MEM ${TX_REGRESSION_TARGET_MEM} + LIBRARIES threadx_regression_support + SOURCES ${TEST_SOURCE} + ) + + list(APPEND TX_REGRESSION_TARGETS ${TEST_NAME}) + set(TX_REGRESSION_TARGETS ${TX_REGRESSION_TARGETS} PARENT_SCOPE) + + # The executable is embedded and not directly host-runnable. + # Add a real add_test() command here once a QEMU or board runner exists. +endfunction() + +foreach(test_case ${TX_REGRESSION_CASES}) + add_threadx_regression_test(${test_case}) +endforeach() + +add_custom_target( + threadx_regression_build + DEPENDS ${TX_REGRESSION_TARGETS} +) -- 2.49.1 From be47608a03465f134357383135156accfdc33ac8 Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Sat, 21 Mar 2026 17:53:43 +0100 Subject: [PATCH 28/79] corrects hwtimer TICKNUM this change went unnoticed when moving the file into moonlight/inc --- port/moonlight/inc/hwtimer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/port/moonlight/inc/hwtimer.h b/port/moonlight/inc/hwtimer.h index 0c339fd..fb7e7a9 100644 --- a/port/moonlight/inc/hwtimer.h +++ b/port/moonlight/inc/hwtimer.h @@ -15,7 +15,7 @@ #include "platform.h" #define TICKNUM_PER_SECOND 32768 -#define TICKNUM_PER_TIMER (TICKNUM_PER_SECOND / 1000) // ~ 1ms timer +#define TICKNUM_PER_TIMER (TICKNUM_PER_SECOND / 100) // ~ 1ms timer static inline int hwtimer_init(void) { -- 2.49.1 From fdf477044f11ed7cadc3345499fc6e99a1af9b25 Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Sat, 21 Mar 2026 18:06:00 +0100 Subject: [PATCH 29/79] removes arguments for consumers (setting output dir and target_mem) --- CMakeLists.txt | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 819df8c..c2f8382 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,7 +33,6 @@ endif() ############################################################################### project(threadx_demo C ASM) option(NX_DEBUG "compile netxduo debug output in" OFF) -option(ENABLE_THREADX_REGRESSION "Build ThreadX regression test executables" OFF) set(TARGET_MEM "ram" CACHE STRING "memory map to use") set(CMAKE_EXECUTABLE_SUFFIX_C ".elf") @@ -41,23 +40,16 @@ add_subdirectory(port/moonlight) function(setup_target TARGET) set(options) - set(oneValueArgs OUTPUT_DIR TARGET_MEM) + set(oneValueArgs) #None for now set(multiValueArgs LIBRARIES SOURCES) cmake_parse_arguments(ST "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) if(ST_UNPARSED_ARGUMENTS) message(FATAL_ERROR "setup_target(${TARGET} ...): unknown args: ${ST_UNPARSED_ARGUMENTS}") endif() - if(NOT ST_OUTPUT_DIR) - set(ST_OUTPUT_DIR ${CMAKE_BINARY_DIR}) - endif() - if(NOT ST_TARGET_MEM) - set(ST_TARGET_MEM ${TARGET_MEM}) - endif() - add_executable(${TARGET}) set_target_properties(${TARGET} PROPERTIES - RUNTIME_OUTPUT_DIRECTORY ${ST_OUTPUT_DIR} + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR} ) target_add_moonlight_platform(${TARGET}) @@ -76,23 +68,19 @@ function(setup_target TARGET) target_link_options(${TARGET} PRIVATE -nostartfiles -nostdlib - -T ${CMAKE_SOURCE_DIR}/src/${ST_TARGET_MEM}.lds + -T ${CMAKE_SOURCE_DIR}/src/${TARGET_MEM}.lds -Wl,--gc-sections - -Wl,-Map=${ST_OUTPUT_DIR}/${TARGET}.map) + -Wl,-Map=${CMAKE_BINARY_DIR}/${TARGET}.map) add_custom_command(TARGET ${TARGET} POST_BUILD - COMMAND ${OBJCOPY} -O ihex $ ${ST_OUTPUT_DIR}/${TARGET}.hex - COMMAND ${OBJCOPY} -O binary $ ${ST_OUTPUT_DIR}/${TARGET}.bin + COMMAND ${OBJCOPY} -O ihex $ ${CMAKE_BINARY_DIR}/${TARGET}.hex + COMMAND ${OBJCOPY} -O binary $ ${CMAKE_BINARY_DIR}/${TARGET}.bin COMMAND ${SIZE} $ - COMMAND ${OBJDUMP} -S $ > ${ST_OUTPUT_DIR}/${TARGET}.dis + COMMAND ${OBJDUMP} -S $ > ${CMAKE_BINARY_DIR}/${TARGET}.dis COMMENT "Creating collateral for ${TARGET}" ) endfunction() -if(ENABLE_THREADX_REGRESSION) - add_subdirectory(test) -endif() - setup_target(thread_demo LIBRARIES threadx SOURCES src/thread_demo/main.c) setup_target(tcp_demo LIBRARIES threadx netxduo SOURCES src/tcp_demo/main.c) setup_target(smp_demo LIBRARIES threadx_smp SOURCES src/thread_demo/main.c) -- 2.49.1 From c98ac25ca6b88b27badf6a8f310b6ab38c255392 Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Sat, 21 Mar 2026 18:19:45 +0100 Subject: [PATCH 30/79] makes compilation of the network driver depend on netxduo being built --- port/moonlight/CMakeLists.txt | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/port/moonlight/CMakeLists.txt b/port/moonlight/CMakeLists.txt index a25ff5c..5edf9da 100644 --- a/port/moonlight/CMakeLists.txt +++ b/port/moonlight/CMakeLists.txt @@ -35,18 +35,24 @@ target_link_libraries(moonlight_platform_common PUBLIC c threadx) -add_library(moonlight_network_driver OBJECT - ${MOONLIGHT_SRC_DIR}/mnrs_network_driver.c) -target_link_libraries(moonlight_network_driver PUBLIC - moonlight_platform_defaults - c - threadx - netxduo) +if(DEFINED NETXDUO_CUSTOM_PORT) + add_library(moonlight_network_driver OBJECT + ${MOONLIGHT_SRC_DIR}/mnrs_network_driver.c) + target_link_libraries(moonlight_network_driver PUBLIC + moonlight_platform_defaults + c + threadx + netxduo) +endif() function(target_add_moonlight_platform TARGET) target_link_libraries(${TARGET} PRIVATE moonlight_platform_common) endfunction() function(target_add_moonlight_network_driver TARGET) + if(NOT TARGET moonlight_network_driver) + message(FATAL_ERROR "moonlight_network_driver requires netxduo to be available in this build") + endif() + target_link_libraries(${TARGET} PRIVATE moonlight_network_driver) endfunction() -- 2.49.1 From 55cd39271a832939849f8374bcac56aeba1fe11a Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Sat, 21 Mar 2026 18:23:25 +0100 Subject: [PATCH 31/79] adds toplevel CMakeLists.txt for tests --- test/CMakeLists.txt | 66 ++++++++++++++++++++++++++- test/CMakePresets.json | 90 +++++++++++++++++++++++++++++++++++++ test/threadx/CMakeLists.txt | 14 ++---- 3 files changed, 159 insertions(+), 11 deletions(-) create mode 100644 test/CMakePresets.json diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f456146..7c32ba0 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,3 +1,67 @@ cmake_minimum_required(VERSION 3.21) -add_subdirectory(threadx) +set(BUILD_SHARED_LIBS OFF) +get_filename_component(THREADX4TGFS_ROOT "${CMAKE_CURRENT_LIST_DIR}/.." ABSOLUTE) + +if(NOT DEFINED CMAKE_TOOLCHAIN_FILE) + set(CMAKE_TOOLCHAIN_FILE + ${THREADX4TGFS_ROOT}/cmake/rv32imac_gnu.cmake + CACHE FILEPATH "Toolchain file") +endif() +include(${CMAKE_TOOLCHAIN_FILE}) + +set(__THREAD_LOCAL_STORAGE OFF) +add_subdirectory(${THREADX4TGFS_ROOT}/third-party/picolibc ${CMAKE_BINARY_DIR}/picolibc) +target_link_libraries(c PUBLIC gcc) + +set(THREADX_CUSTOM_PORT ${THREADX4TGFS_ROOT}/port/threadx) +add_subdirectory(${THREADX4TGFS_ROOT}/third-party/threadx ${CMAKE_BINARY_DIR}/threadx) +target_link_libraries(threadx PUBLIC c) +target_compile_definitions(threadx PUBLIC TX_REGRESSION_TEST) + +project(threadx_regression C ASM) +enable_testing() +set(TARGET_MEM "ram_dram" CACHE STRING "memory map to use") +set(CMAKE_EXECUTABLE_SUFFIX_C ".elf") + +add_subdirectory(${THREADX4TGFS_ROOT}/port/moonlight ${CMAKE_BINARY_DIR}/port/moonlight) + +function(setup_target TARGET) + set(options) + set(multiValueArgs LIBRARIES SOURCES) + cmake_parse_arguments(ST "${options}" "" "${multiValueArgs}" ${ARGN}) + if(ST_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "setup_target(${TARGET} ...): unknown args: ${ST_UNPARSED_ARGUMENTS}") + endif() + + add_executable(${TARGET}) + set_target_properties(${TARGET} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR} + ) + target_add_moonlight_platform(${TARGET}) + + if(ST_SOURCES) + target_sources(${TARGET} PRIVATE ${ST_SOURCES}) + endif() + + if(ST_LIBRARIES) + target_link_libraries(${TARGET} PRIVATE ${ST_LIBRARIES}) + endif() + + target_link_options(${TARGET} PRIVATE + -nostartfiles + -nostdlib + -T ${THREADX4TGFS_ROOT}/src/${TARGET_MEM}.lds + -Wl,--gc-sections + -Wl,-Map=${CMAKE_BINARY_DIR}/${TARGET}.map) + + add_custom_command(TARGET ${TARGET} POST_BUILD + COMMAND ${OBJCOPY} -O ihex $ ${CMAKE_BINARY_DIR}/${TARGET}.hex + COMMAND ${OBJCOPY} -O binary $ ${CMAKE_BINARY_DIR}/${TARGET}.bin + COMMAND ${SIZE} $ + COMMAND ${OBJDUMP} -S $ > ${CMAKE_BINARY_DIR}/${TARGET}.dis + COMMENT "Creating collateral for ${TARGET}" + ) +endfunction() + +add_subdirectory(threadx regression_targets) diff --git a/test/CMakePresets.json b/test/CMakePresets.json new file mode 100644 index 0000000..240564f --- /dev/null +++ b/test/CMakePresets.json @@ -0,0 +1,90 @@ +{ + "version": 3, + "cmakeMinimumRequired": { + "major": 3, + "minor": 24, + "patch": 0 + }, + "configurePresets": [ + { + "name": "Debug", + "binaryDir": "${sourceDir}/../build/${presetName}/test", + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/../cmake/rv64imac_gnu.cmake", + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" + } + }, + { + "name": "Debug32", + "binaryDir": "${sourceDir}/../build/${presetName}/test", + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/../cmake/rv32imac_gnu.cmake", + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" + } + }, + { + "name": "Release", + "binaryDir": "${sourceDir}/../build/${presetName}/test", + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/../cmake/rv64imac_gnu.cmake", + "CMAKE_BUILD_TYPE": "RelWithDebInfo", + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" + } + }, + { + "name": "Release32", + "binaryDir": "${sourceDir}/../build/${presetName}/test", + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/../cmake/rv32imac_gnu.cmake", + "CMAKE_BUILD_TYPE": "RelWithDebInfo", + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" + } + }, + { + "name": "MinSizeRel", + "binaryDir": "${sourceDir}/../build/${presetName}/test", + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/../cmake/rv64imac_gnu.cmake", + "CMAKE_BUILD_TYPE": "MinSizeRel", + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" + } + }, + { + "name": "MinSizeRel32", + "binaryDir": "${sourceDir}/../build/${presetName}/test", + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/../cmake/rv32imac_gnu.cmake", + "CMAKE_BUILD_TYPE": "MinSizeRel", + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" + } + } + ], + "buildPresets": [ + { + "name": "Debug", + "configurePreset": "Debug" + }, + { + "name": "Debug32", + "configurePreset": "Debug32" + }, + { + "name": "Release", + "configurePreset": "Release" + }, + { + "name": "Release32", + "configurePreset": "Release32" + }, + { + "name": "MinSizeRel", + "configurePreset": "MinSizeRel" + }, + { + "name": "MinSizeRel32", + "configurePreset": "MinSizeRel32" + } + ] +} diff --git a/test/threadx/CMakeLists.txt b/test/threadx/CMakeLists.txt index 8352b07..efbbab6 100644 --- a/test/threadx/CMakeLists.txt +++ b/test/threadx/CMakeLists.txt @@ -1,12 +1,8 @@ -cmake_minimum_required(VERSION 3.21) - -enable_testing() - set(TX_REGRESSION_DIR - ${CMAKE_SOURCE_DIR}/third-party/threadx/test/tx/regression + ${THREADX4TGFS_ROOT}/third-party/threadx/test/tx/regression ) set(TX_CMAKE_DIR - ${CMAKE_SOURCE_DIR}/third-party/threadx/test/tx/cmake + ${THREADX4TGFS_ROOT}/third-party/threadx/test/tx/cmake ) set(TX_REGRESSION_CASES @@ -21,8 +17,6 @@ set(TX_REGRESSION_CASES ) set(TX_REGRESSION_TARGETS) -set(TX_REGRESSION_OUTPUT_DIR ${CMAKE_BINARY_DIR}/test) -set(TX_REGRESSION_TARGET_MEM ram_dram) add_library( threadx_regression_support STATIC @@ -33,17 +27,17 @@ target_link_libraries(threadx_regression_support PUBLIC threadx c) target_compile_definitions( threadx_regression_support PUBLIC + TX_REGRESSION_TEST CTEST BATCH_TEST TEST_STACK_SIZE_PRINTF=4096 ) + function(add_threadx_regression_test TEST_SOURCE) get_filename_component(TEST_NAME ${TEST_SOURCE} NAME_WE) setup_target( ${TEST_NAME} - OUTPUT_DIR ${TX_REGRESSION_OUTPUT_DIR} - TARGET_MEM ${TX_REGRESSION_TARGET_MEM} LIBRARIES threadx_regression_support SOURCES ${TEST_SOURCE} ) -- 2.49.1 From 4cfa80d7a67b561753b823a6d68dd6bbee785508 Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Sat, 21 Mar 2026 18:27:26 +0100 Subject: [PATCH 32/79] adds build presets for demos --- CMakePresets.json | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/CMakePresets.json b/CMakePresets.json index ac9f2c7..c84eddd 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -63,5 +63,31 @@ "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" } } + ], + "buildPresets": [ + { + "name": "Debug", + "configurePreset": "Debug" + }, + { + "name": "Debug32", + "configurePreset": "Debug32" + }, + { + "name": "Release", + "configurePreset": "Release" + }, + { + "name": "Release32", + "configurePreset": "Release32" + }, + { + "name": "MinSizeRel", + "configurePreset": "MinSizeRel" + }, + { + "name": "MinSizeRel32", + "configurePreset": "MinSizeRel32" + } ] } \ No newline at end of file -- 2.49.1 From 124783a57ce9c30f7663911598c1af8aadfb952e Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Sat, 21 Mar 2026 20:15:33 +0100 Subject: [PATCH 33/79] corrects exit --- port/moonlight/src/bootup.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/port/moonlight/src/bootup.c b/port/moonlight/src/bootup.c index d212bcf..69df782 100644 --- a/port/moonlight/src/bootup.c +++ b/port/moonlight/src/bootup.c @@ -124,5 +124,7 @@ void _exit(int exit_code) { // Halt while (1) { __asm__ volatile ("wfi"); + // Incase periodic interrupts are scheduled (e.g. timer), additionaly jump to self + for (;;); } } \ No newline at end of file -- 2.49.1 From 014cf9f8eede42d4ac718b37c5e478cc3616b9c7 Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Sat, 21 Mar 2026 20:15:46 +0100 Subject: [PATCH 34/79] adds testing hook for trap_non_vectored --- port/moonlight/src/trap_non_vectored.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/port/moonlight/src/trap_non_vectored.c b/port/moonlight/src/trap_non_vectored.c index 0f891d7..90c5d21 100644 --- a/port/moonlight/src/trap_non_vectored.c +++ b/port/moonlight/src/trap_non_vectored.c @@ -18,6 +18,9 @@ extern void _tx_timer_interrupt(void); extern uintptr_t exception(uintptr_t mcause, uintptr_t mepc, uintptr_t mtval); +#ifdef TX_REGRESSION_TEST +extern void test_interrupt_dispatch(void); +#endif void (*irq_handler[__riscv_xlen])(); @@ -38,6 +41,9 @@ void trap_handler(uintptr_t mcause, uintptr_t mepc, uintptr_t mtval) { printf("Timer interrupt being handled (pc=%lx)\n", mepc); #endif hwtimer_handler(); +#ifdef TX_REGRESSION_TEST + test_interrupt_dispatch(); +#endif _tx_timer_interrupt(); break; #ifdef TX_THREAD_SMP_INTER_CORE_INTERRUPT -- 2.49.1 From 8bcbc9881ee50d862b0bc055de5a684fe3666e6e Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Sat, 21 Mar 2026 20:19:42 +0100 Subject: [PATCH 35/79] Revert "adds testing hook for trap_non_vectored" This reverts commit 014cf9f8eede42d4ac718b37c5e478cc3616b9c7. --- port/moonlight/src/trap_non_vectored.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/port/moonlight/src/trap_non_vectored.c b/port/moonlight/src/trap_non_vectored.c index 90c5d21..0f891d7 100644 --- a/port/moonlight/src/trap_non_vectored.c +++ b/port/moonlight/src/trap_non_vectored.c @@ -18,9 +18,6 @@ extern void _tx_timer_interrupt(void); extern uintptr_t exception(uintptr_t mcause, uintptr_t mepc, uintptr_t mtval); -#ifdef TX_REGRESSION_TEST -extern void test_interrupt_dispatch(void); -#endif void (*irq_handler[__riscv_xlen])(); @@ -41,9 +38,6 @@ void trap_handler(uintptr_t mcause, uintptr_t mepc, uintptr_t mtval) { printf("Timer interrupt being handled (pc=%lx)\n", mepc); #endif hwtimer_handler(); -#ifdef TX_REGRESSION_TEST - test_interrupt_dispatch(); -#endif _tx_timer_interrupt(); break; #ifdef TX_THREAD_SMP_INTER_CORE_INTERRUPT -- 2.49.1 From 7307a86860e3b5b81c07c566842fcf2323efc671 Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Sat, 21 Mar 2026 20:27:16 +0100 Subject: [PATCH 36/79] adds ISR check that is closer to productive code This approach patches the existing trap handler to call the test routine --- port/moonlight/CMakeLists.txt | 6 +++++- test/CMakeLists.txt | 12 ++++++++++++ test/threadx/generate_trap_file.sh | 27 +++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) create mode 100755 test/threadx/generate_trap_file.sh diff --git a/port/moonlight/CMakeLists.txt b/port/moonlight/CMakeLists.txt index 5edf9da..ae2e4c6 100644 --- a/port/moonlight/CMakeLists.txt +++ b/port/moonlight/CMakeLists.txt @@ -3,11 +3,15 @@ set(THREADX4TGFS_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../..) set(MOONLIGHT_SRC_DIR ${MOONLIGHT_ROOT}/src) set(MOONLIGHT_INC_DIR ${MOONLIGHT_ROOT}/inc) +if(NOT DEFINED MOONLIGHT_TRAP_SOURCE) + set(MOONLIGHT_TRAP_SOURCE ${MOONLIGHT_SRC_DIR}/trap_non_vectored.c) +endif() + set(MOONLIGHT_PLATFORM_SOURCES ${THREADX4TGFS_ROOT}/port/picolibc/port.c ${MOONLIGHT_SRC_DIR}/bootup.c ${MOONLIGHT_SRC_DIR}/board.c - ${MOONLIGHT_SRC_DIR}/trap_non_vectored.c + ${MOONLIGHT_TRAP_SOURCE} ${MOONLIGHT_SRC_DIR}/exception.c ${MOONLIGHT_SRC_DIR}/vector_table.c) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 7c32ba0..53f201c 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -24,6 +24,18 @@ enable_testing() set(TARGET_MEM "ram_dram" CACHE STRING "memory map to use") set(CMAKE_EXECUTABLE_SUFFIX_C ".elf") +set(MOONLIGHT_TRAP_SOURCE_INPUT ${THREADX4TGFS_ROOT}/port/moonlight/src/trap_non_vectored.c) +set(MOONLIGHT_TRAP_SOURCE ${CMAKE_BINARY_DIR}/generated/trap_non_vectored.c) +execute_process( + COMMAND bash ${CMAKE_CURRENT_LIST_DIR}/threadx/generate_trap_file.sh + ${MOONLIGHT_TRAP_SOURCE_INPUT} + ${MOONLIGHT_TRAP_SOURCE} + RESULT_VARIABLE MOONLIGHT_TRAP_GENERATE_RESULT +) +if(NOT MOONLIGHT_TRAP_GENERATE_RESULT EQUAL 0) + message(FATAL_ERROR "Failed to generate regression trap source") +endif() + add_subdirectory(${THREADX4TGFS_ROOT}/port/moonlight ${CMAKE_BINARY_DIR}/port/moonlight) function(setup_target TARGET) diff --git a/test/threadx/generate_trap_file.sh b/test/threadx/generate_trap_file.sh new file mode 100755 index 0000000..2bce871 --- /dev/null +++ b/test/threadx/generate_trap_file.sh @@ -0,0 +1,27 @@ +#!/bin/bash +set -eu + +src="$1" +dst="$2" + +extern_anchor='extern void _tx_timer_interrupt(void);' +call_anchor='_tx_timer_interrupt();' + +line=$(grep -n -F "$extern_anchor" "$src" | head -n 1 | cut -d: -f1) +if [ -z "$line" ]; then + echo "failed to find _tx_timer_interrupt declaration anchor in trap source" >&2 + exit 1 +fi +sed "${line}a\\ +extern void test_interrupt_dispatch(void);" "$src" > "$dst.tmp1" + +line=$(grep -n -F "$call_anchor" "$dst.tmp1" | head -n 1 | cut -d: -f1) +if [ -z "$line" ]; then + echo "failed to find _tx_timer_interrupt call anchor in trap source" >&2 + rm -f "$dst.tmp1" + exit 1 +fi +mkdir -p "$(dirname "$dst")" +sed "${line}i\\ + test_interrupt_dispatch();" "$dst.tmp1" > "$dst" +rm -f "$dst.tmp1" -- 2.49.1 From 9947ce8d5213291ffce74a6df1e64ffa4c03f35a Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Sun, 22 Mar 2026 15:56:00 +0100 Subject: [PATCH 37/79] corrects naming of TX_INTERRUPT_SAVE_AREA to meet test expectations Also corrects the sizes, as ULONG is XLEN bits --- port/threadx/inc/tx_port.h | 18 +++++++++--------- port/threadx_smp/inc/tx_port.h | 6 +++--- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/port/threadx/inc/tx_port.h b/port/threadx/inc/tx_port.h index 7ee7252..e2fc50e 100644 --- a/port/threadx/inc/tx_port.h +++ b/port/threadx/inc/tx_port.h @@ -335,25 +335,25 @@ extern TEST_FLAG test_forced_mutex_timeout; #ifdef TX_DISABLE_INLINE -ULONG64 _tx_thread_interrupt_control(unsigned int new_posture); +ULONG _tx_thread_interrupt_control(unsigned int new_posture); -#define TX_INTERRUPT_SAVE_AREA register ULONG64 interrupt_save; +#define TX_INTERRUPT_SAVE_AREA register ULONG tx_saved_posture; -#define TX_DISABLE interrupt_save = _tx_thread_interrupt_control(TX_INT_DISABLE); -#define TX_RESTORE _tx_thread_interrupt_control(interrupt_save); +#define TX_DISABLE tx_saved_posture = _tx_thread_interrupt_control(TX_INT_DISABLE); +#define TX_RESTORE _tx_thread_interrupt_control(tx_saved_posture); #else -#define TX_INTERRUPT_SAVE_AREA ULONG64 interrupt_save; -/* Atomically read mstatus into interrupt_save and clear bit 3 of mstatus. */ +#define TX_INTERRUPT_SAVE_AREA ULONG tx_saved_posture; +/* Atomically read mstatus into tx_saved_posture and clear bit 3 of mstatus. */ #define TX_DISABLE \ { \ - __asm__("csrrci %0, mstatus, 0x08" : "=r"(interrupt_save) :); \ + __asm__("csrrci %0, mstatus, 0x08" : "=r"(tx_saved_posture) :); \ }; -/* We only care about mstatus.mie (bit 3), so mask interrupt_save and write to mstatus. */ +/* We only care about mstatus.mie (bit 3), so mask tx_saved_posture and write to mstatus. */ #define TX_RESTORE \ { \ - register ULONG64 __tempmask = interrupt_save & 0x08; \ + register ULONG __tempmask = tx_saved_posture & 0x08; \ __asm__("csrrs x0, mstatus, %0 \n\t" : : "r"(__tempmask) :); \ }; diff --git a/port/threadx_smp/inc/tx_port.h b/port/threadx_smp/inc/tx_port.h index aefe3fd..698f36a 100644 --- a/port/threadx_smp/inc/tx_port.h +++ b/port/threadx_smp/inc/tx_port.h @@ -315,10 +315,10 @@ typedef struct TX_THREAD_SMP_PROTECT_STRUCT is used to define a local function save area for the disable and restore macros. */ -#define TX_INTERRUPT_SAVE_AREA ULONG64 interrupt_save; +#define TX_INTERRUPT_SAVE_AREA ULONG tx_saved_posture; -#define TX_DISABLE interrupt_save = _tx_thread_smp_protect(); -#define TX_RESTORE _tx_thread_smp_unprotect(interrupt_save); +#define TX_DISABLE tx_saved_posture = _tx_thread_smp_protect(); +#define TX_RESTORE _tx_thread_smp_unprotect(tx_saved_posture); /************* End ThreadX SMP data type and function prototype definitions. *************/ -- 2.49.1 From bbad53cc1a2eee340de3f61aa28c30f6b5c4613a Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Sun, 22 Mar 2026 17:12:16 +0100 Subject: [PATCH 38/79] adds all regression tests --- test/CMakeLists.txt | 14 ++- test/threadx/CMakeLists.txt | 110 +++++++++++++++++- .../generate_kernel_setup_test_file.sh | 16 +++ test/threadx/generate_trap_file.sh | 14 ++- 4 files changed, 141 insertions(+), 13 deletions(-) create mode 100755 test/threadx/generate_kernel_setup_test_file.sh diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 53f201c..e27e77c 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -5,8 +5,8 @@ get_filename_component(THREADX4TGFS_ROOT "${CMAKE_CURRENT_LIST_DIR}/.." ABSOLUTE if(NOT DEFINED CMAKE_TOOLCHAIN_FILE) set(CMAKE_TOOLCHAIN_FILE - ${THREADX4TGFS_ROOT}/cmake/rv32imac_gnu.cmake - CACHE FILEPATH "Toolchain file") + ${THREADX4TGFS_ROOT}/cmake/rv32imac_gnu.cmake + CACHE FILEPATH "Toolchain file") endif() include(${CMAKE_TOOLCHAIN_FILE}) @@ -24,12 +24,14 @@ enable_testing() set(TARGET_MEM "ram_dram" CACHE STRING "memory map to use") set(CMAKE_EXECUTABLE_SUFFIX_C ".elf") +# The regression tests require the Timer ISR to call the function test_interrupt_dispatch(void) +# We patch the current trap handler and include it using MOONLIGHT_TRAP_SOURCE set(MOONLIGHT_TRAP_SOURCE_INPUT ${THREADX4TGFS_ROOT}/port/moonlight/src/trap_non_vectored.c) set(MOONLIGHT_TRAP_SOURCE ${CMAKE_BINARY_DIR}/generated/trap_non_vectored.c) execute_process( COMMAND bash ${CMAKE_CURRENT_LIST_DIR}/threadx/generate_trap_file.sh - ${MOONLIGHT_TRAP_SOURCE_INPUT} - ${MOONLIGHT_TRAP_SOURCE} + ${MOONLIGHT_TRAP_SOURCE_INPUT} + ${MOONLIGHT_TRAP_SOURCE} RESULT_VARIABLE MOONLIGHT_TRAP_GENERATE_RESULT ) if(NOT MOONLIGHT_TRAP_GENERATE_RESULT EQUAL 0) @@ -70,9 +72,9 @@ function(setup_target TARGET) add_custom_command(TARGET ${TARGET} POST_BUILD COMMAND ${OBJCOPY} -O ihex $ ${CMAKE_BINARY_DIR}/${TARGET}.hex COMMAND ${OBJCOPY} -O binary $ ${CMAKE_BINARY_DIR}/${TARGET}.bin - COMMAND ${SIZE} $ + #COMMAND ${SIZE} $ COMMAND ${OBJDUMP} -S $ > ${CMAKE_BINARY_DIR}/${TARGET}.dis - COMMENT "Creating collateral for ${TARGET}" + #COMMENT "Creating collateral for ${TARGET}" ) endfunction() diff --git a/test/threadx/CMakeLists.txt b/test/threadx/CMakeLists.txt index efbbab6..d7217dd 100644 --- a/test/threadx/CMakeLists.txt +++ b/test/threadx/CMakeLists.txt @@ -5,15 +5,117 @@ set(TX_CMAKE_DIR ${THREADX4TGFS_ROOT}/third-party/threadx/test/tx/cmake ) +# This time patch a test. Since we use a patched trap vector, we need the function test_interrupt_dispatch(void) to exist +# As this test is not using testcontrol.c to provide the symbol, we do so in this workaround +set(TX_KERNEL_SETUP_TEST_SOURCE_INPUT ${THREADX4TGFS_ROOT}/third-party/threadx/test/tx/regression/threadx_initialize_kernel_setup_test.c) +set(TX_KERNEL_SETUP_TEST_SOURCE ${CMAKE_BINARY_DIR}/generated/threadx_initialize_kernel_setup_test.c) +execute_process( + COMMAND bash ${CMAKE_CURRENT_LIST_DIR}/generate_kernel_setup_test_file.sh + ${TX_KERNEL_SETUP_TEST_SOURCE_INPUT} + ${TX_KERNEL_SETUP_TEST_SOURCE} + RESULT_VARIABLE TX_KERNEL_SETUP_TEST_GENERATE_RESULT +) +if(NOT TX_KERNEL_SETUP_TEST_GENERATE_RESULT EQUAL 0) + message(FATAL_ERROR "Failed to generate patched kernel setup test source") +endif() + set(TX_REGRESSION_CASES ${TX_REGRESSION_DIR}/threadx_block_memory_basic_test.c + ${TX_REGRESSION_DIR}/threadx_block_memory_error_detection_test.c + ${TX_REGRESSION_DIR}/threadx_block_memory_information_test.c + ${TX_REGRESSION_DIR}/threadx_block_memory_prioritize_test.c + ${TX_REGRESSION_DIR}/threadx_block_memory_suspension_test.c + ${TX_REGRESSION_DIR}/threadx_block_memory_suspension_timeout_test.c + ${TX_REGRESSION_DIR}/threadx_block_memory_thread_terminate_test.c ${TX_REGRESSION_DIR}/threadx_byte_memory_basic_test.c + ${TX_REGRESSION_DIR}/threadx_byte_memory_information_test.c + ${TX_REGRESSION_DIR}/threadx_byte_memory_prioritize_test.c + ${TX_REGRESSION_DIR}/threadx_byte_memory_suspension_test.c + ${TX_REGRESSION_DIR}/threadx_byte_memory_suspension_timeout_test.c + ${TX_REGRESSION_DIR}/threadx_byte_memory_thread_contention_test.c + ${TX_REGRESSION_DIR}/threadx_byte_memory_thread_terminate_test.c ${TX_REGRESSION_DIR}/threadx_event_flag_basic_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_information_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_isr_set_clear_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_isr_wait_abort_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_single_thread_terminate_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_suspension_consume_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_suspension_different_bits_consume_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_suspension_different_bits_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_suspension_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_suspension_timeout_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_thread_terminate_test.c + ${TX_REGRESSION_DIR}/threadx_interrupt_control_test.c ${TX_REGRESSION_DIR}/threadx_mutex_basic_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_delete_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_information_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_nested_priority_inheritance_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_no_preemption_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_preemption_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_priority_inheritance_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_proritize_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_suspension_timeout_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_thread_terminate_test.c + ${TX_REGRESSION_DIR}/threadx_queue_basic_eight_word_test.c + ${TX_REGRESSION_DIR}/threadx_queue_basic_four_word_test.c ${TX_REGRESSION_DIR}/threadx_queue_basic_one_word_test.c + ${TX_REGRESSION_DIR}/threadx_queue_basic_sixteen_word_test.c + ${TX_REGRESSION_DIR}/threadx_queue_basic_two_word_test.c + ${TX_REGRESSION_DIR}/threadx_queue_basic_max_message_size_test.c + ${TX_REGRESSION_DIR}/threadx_queue_empty_suspension_test.c + ${TX_REGRESSION_DIR}/threadx_queue_flush_no_suspension_test.c + ${TX_REGRESSION_DIR}/threadx_queue_flush_test.c + ${TX_REGRESSION_DIR}/threadx_queue_front_send_test.c + ${TX_REGRESSION_DIR}/threadx_queue_full_suspension_test.c + ${TX_REGRESSION_DIR}/threadx_queue_information_test.c + ${TX_REGRESSION_DIR}/threadx_queue_prioritize.c + ${TX_REGRESSION_DIR}/threadx_queue_suspension_timeout_test.c + ${TX_REGRESSION_DIR}/threadx_queue_thread_terminate_test.c ${TX_REGRESSION_DIR}/threadx_semaphore_basic_test.c + ${TX_REGRESSION_DIR}/threadx_semaphore_ceiling_put_test.c + ${TX_REGRESSION_DIR}/threadx_semaphore_delete_test.c + ${TX_REGRESSION_DIR}/threadx_semaphore_information_test.c + ${TX_REGRESSION_DIR}/threadx_semaphore_non_preemption_test.c + ${TX_REGRESSION_DIR}/threadx_semaphore_preemption_test.c + ${TX_REGRESSION_DIR}/threadx_semaphore_prioritize.c + ${TX_REGRESSION_DIR}/threadx_semaphore_thread_terminate_test.c + ${TX_REGRESSION_DIR}/threadx_semaphore_timeout_test.c ${TX_REGRESSION_DIR}/threadx_thread_basic_execution_test.c + ${TX_REGRESSION_DIR}/threadx_thread_basic_time_slice_test.c + ${TX_REGRESSION_DIR}/threadx_thread_completed_test.c + ${TX_REGRESSION_DIR}/threadx_thread_create_preemption_threshold_test.c + ${TX_REGRESSION_DIR}/threadx_thread_delayed_suspension_test.c + ${TX_REGRESSION_DIR}/threadx_thread_information_test.c + ${TX_REGRESSION_DIR}/threadx_thread_multi_level_preemption_threshold_test.c + ${TX_REGRESSION_DIR}/threadx_thread_multiple_non_current_test.c + ${TX_REGRESSION_DIR}/threadx_thread_multiple_sleep_test.c + ${TX_REGRESSION_DIR}/threadx_thread_multiple_suspension_test.c + ${TX_REGRESSION_DIR}/threadx_thread_multiple_time_slice_test.c + ${TX_REGRESSION_DIR}/threadx_thread_preemptable_suspension_test.c + ${TX_REGRESSION_DIR}/threadx_thread_preemption_change_test.c + ${TX_REGRESSION_DIR}/threadx_thread_priority_change.c + ${TX_REGRESSION_DIR}/threadx_thread_relinquish_test.c + ${TX_REGRESSION_DIR}/threadx_thread_reset_test.c + ${TX_REGRESSION_DIR}/threadx_thread_simple_sleep_non_clear_test.c + ${TX_REGRESSION_DIR}/threadx_thread_simple_sleep_test.c + ${TX_REGRESSION_DIR}/threadx_thread_simple_suspend_test.c + ${TX_REGRESSION_DIR}/threadx_thread_sleep_for_100ticks_test.c + ${TX_REGRESSION_DIR}/threadx_thread_sleep_terminate_test.c + ${TX_REGRESSION_DIR}/threadx_thread_stack_checking_test.c + ${TX_REGRESSION_DIR}/threadx_thread_terminate_delete_test.c + ${TX_REGRESSION_DIR}/threadx_thread_time_slice_change_test.c + ${TX_REGRESSION_DIR}/threadx_thread_wait_abort_and_isr_test.c + ${TX_REGRESSION_DIR}/threadx_thread_wait_abort_test.c + ${TX_REGRESSION_DIR}/threadx_time_get_set_test.c + ${TX_REGRESSION_DIR}/threadx_timer_activate_deactivate_test.c + ${TX_REGRESSION_DIR}/threadx_timer_deactivate_accuracy_test.c + ${TX_REGRESSION_DIR}/threadx_timer_information_test.c + ${TX_REGRESSION_DIR}/threadx_timer_large_timer_accuracy_test.c + ${TX_REGRESSION_DIR}/threadx_timer_multiple_accuracy_test.c + ${TX_REGRESSION_DIR}/threadx_timer_multiple_test.c ${TX_REGRESSION_DIR}/threadx_timer_simple_test.c + ${TX_REGRESSION_DIR}/threadx_trace_basic_test.c + ${TX_KERNEL_SETUP_TEST_SOURCE} ) set(TX_REGRESSION_TARGETS) @@ -36,9 +138,15 @@ target_compile_definitions( function(add_threadx_regression_test TEST_SOURCE) get_filename_component(TEST_NAME ${TEST_SOURCE} NAME_WE) + if(TEST_NAME STREQUAL "threadx_initialize_kernel_setup_test") + set(test_libraries threadx) + else() + set(test_libraries threadx_regression_support) + endif() + setup_target( ${TEST_NAME} - LIBRARIES threadx_regression_support + LIBRARIES ${test_libraries} SOURCES ${TEST_SOURCE} ) diff --git a/test/threadx/generate_kernel_setup_test_file.sh b/test/threadx/generate_kernel_setup_test_file.sh new file mode 100755 index 0000000..01dc5cc --- /dev/null +++ b/test/threadx/generate_kernel_setup_test_file.sh @@ -0,0 +1,16 @@ +#!/bin/bash +set -eu + +src="$1" +dst="$2" + +anchor='void tx_application_define(void *first_unused_memory){}' + +line=$(grep -n -F "$anchor" "$src" | head -n 1 | cut -d: -f1) +if [ -z "$line" ]; then + echo "failed to find tx_application_define anchor in kernel setup test source" >&2 + exit 1 +fi +mkdir -p "$(dirname "$dst")" +sed "${line}a\\ +void test_interrupt_dispatch(void){}" "$src" > "$dst" diff --git a/test/threadx/generate_trap_file.sh b/test/threadx/generate_trap_file.sh index 2bce871..ad7d6e7 100755 --- a/test/threadx/generate_trap_file.sh +++ b/test/threadx/generate_trap_file.sh @@ -7,21 +7,23 @@ dst="$2" extern_anchor='extern void _tx_timer_interrupt(void);' call_anchor='_tx_timer_interrupt();' +mkdir -p "$(dirname "$dst")" +tmp1="${dst}.tmp1" + line=$(grep -n -F "$extern_anchor" "$src" | head -n 1 | cut -d: -f1) if [ -z "$line" ]; then echo "failed to find _tx_timer_interrupt declaration anchor in trap source" >&2 exit 1 fi sed "${line}a\\ -extern void test_interrupt_dispatch(void);" "$src" > "$dst.tmp1" +extern void test_interrupt_dispatch(void);" "$src" > "$tmp1" -line=$(grep -n -F "$call_anchor" "$dst.tmp1" | head -n 1 | cut -d: -f1) +line=$(grep -n -F "$call_anchor" "$tmp1" | head -n 1 | cut -d: -f1) if [ -z "$line" ]; then echo "failed to find _tx_timer_interrupt call anchor in trap source" >&2 - rm -f "$dst.tmp1" + rm -f "$tmp1" exit 1 fi -mkdir -p "$(dirname "$dst")" sed "${line}i\\ - test_interrupt_dispatch();" "$dst.tmp1" > "$dst" -rm -f "$dst.tmp1" + test_interrupt_dispatch();" "$tmp1" > "$dst" +rm -f "$tmp1" -- 2.49.1 From 78163ebb99c007d1f9602f31418db1a99905f777 Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Sun, 22 Mar 2026 18:27:07 +0100 Subject: [PATCH 39/79] adds test harness --- test/CMakeLists.txt | 35 +++++++++++++----- test/threadx/CMakeLists.txt | 40 +++++++++++++++------ test/threadx/run_threadx_simple_test.sh | 40 +++++++++++++++++++++ test/threadx/run_threadx_test.sh | 48 +++++++++++++++++++++++++ 4 files changed, 144 insertions(+), 19 deletions(-) create mode 100755 test/threadx/run_threadx_simple_test.sh create mode 100755 test/threadx/run_threadx_test.sh diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e27e77c..d42c1ae 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -24,19 +24,38 @@ enable_testing() set(TARGET_MEM "ram_dram" CACHE STRING "memory map to use") set(CMAKE_EXECUTABLE_SUFFIX_C ".elf") +set(THREADX_TEST_SIMULATOR + "" + CACHE FILEPATH "Path to the RISC-V VP executable used by CTest") +if(NOT THREADX_TEST_SIMULATOR) + message(FATAL_ERROR + "THREADX_TEST_SIMULATOR is not set. Configure with -DTHREADX_TEST_SIMULATOR=/path/to/riscv-vp") +endif() + +# Evaluate the variable from the toolchain file to decide which XLEN we are targetting (IMAC only!) +if(CMAKE_SYSTEM_PROCESSOR STREQUAL "risc-v32") + set(THREADX_TEST_ISA "rv32imac_m") +elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "risc-v64") + set(THREADX_TEST_ISA "rv64imac_m") +else() + message(FATAL_ERROR + "Unsupported CMAKE_SYSTEM_PROCESSOR for VP ISA selection: ${CMAKE_SYSTEM_PROCESSOR}") +endif() + # The regression tests require the Timer ISR to call the function test_interrupt_dispatch(void) -# We patch the current trap handler and include it using MOONLIGHT_TRAP_SOURCE +# We patch the current trap handler and include it using MOONLIGHT_TRAP_SOURCE. set(MOONLIGHT_TRAP_SOURCE_INPUT ${THREADX4TGFS_ROOT}/port/moonlight/src/trap_non_vectored.c) set(MOONLIGHT_TRAP_SOURCE ${CMAKE_BINARY_DIR}/generated/trap_non_vectored.c) -execute_process( +add_custom_command( + OUTPUT ${MOONLIGHT_TRAP_SOURCE} COMMAND bash ${CMAKE_CURRENT_LIST_DIR}/threadx/generate_trap_file.sh - ${MOONLIGHT_TRAP_SOURCE_INPUT} - ${MOONLIGHT_TRAP_SOURCE} - RESULT_VARIABLE MOONLIGHT_TRAP_GENERATE_RESULT + ${MOONLIGHT_TRAP_SOURCE_INPUT} + ${MOONLIGHT_TRAP_SOURCE} + DEPENDS ${MOONLIGHT_TRAP_SOURCE_INPUT} + ${CMAKE_CURRENT_LIST_DIR}/threadx/generate_trap_file.sh + VERBATIM ) -if(NOT MOONLIGHT_TRAP_GENERATE_RESULT EQUAL 0) - message(FATAL_ERROR "Failed to generate regression trap source") -endif() +set_source_files_properties(${MOONLIGHT_TRAP_SOURCE} PROPERTIES GENERATED TRUE) add_subdirectory(${THREADX4TGFS_ROOT}/port/moonlight ${CMAKE_BINARY_DIR}/port/moonlight) diff --git a/test/threadx/CMakeLists.txt b/test/threadx/CMakeLists.txt index d7217dd..ae9a56b 100644 --- a/test/threadx/CMakeLists.txt +++ b/test/threadx/CMakeLists.txt @@ -5,19 +5,19 @@ set(TX_CMAKE_DIR ${THREADX4TGFS_ROOT}/third-party/threadx/test/tx/cmake ) -# This time patch a test. Since we use a patched trap vector, we need the function test_interrupt_dispatch(void) to exist -# As this test is not using testcontrol.c to provide the symbol, we do so in this workaround +# This test needs a local test_interrupt_dispatch() because it does not link testcontrol.c. set(TX_KERNEL_SETUP_TEST_SOURCE_INPUT ${THREADX4TGFS_ROOT}/third-party/threadx/test/tx/regression/threadx_initialize_kernel_setup_test.c) set(TX_KERNEL_SETUP_TEST_SOURCE ${CMAKE_BINARY_DIR}/generated/threadx_initialize_kernel_setup_test.c) -execute_process( +add_custom_command( + OUTPUT ${TX_KERNEL_SETUP_TEST_SOURCE} COMMAND bash ${CMAKE_CURRENT_LIST_DIR}/generate_kernel_setup_test_file.sh - ${TX_KERNEL_SETUP_TEST_SOURCE_INPUT} - ${TX_KERNEL_SETUP_TEST_SOURCE} - RESULT_VARIABLE TX_KERNEL_SETUP_TEST_GENERATE_RESULT + ${TX_KERNEL_SETUP_TEST_SOURCE_INPUT} + ${TX_KERNEL_SETUP_TEST_SOURCE} + DEPENDS ${TX_KERNEL_SETUP_TEST_SOURCE_INPUT} + ${CMAKE_CURRENT_LIST_DIR}/generate_kernel_setup_test_file.sh + VERBATIM ) -if(NOT TX_KERNEL_SETUP_TEST_GENERATE_RESULT EQUAL 0) - message(FATAL_ERROR "Failed to generate patched kernel setup test source") -endif() +set_source_files_properties(${TX_KERNEL_SETUP_TEST_SOURCE} PROPERTIES GENERATED TRUE) set(TX_REGRESSION_CASES ${TX_REGRESSION_DIR}/threadx_block_memory_basic_test.c @@ -153,8 +153,26 @@ function(add_threadx_regression_test TEST_SOURCE) list(APPEND TX_REGRESSION_TARGETS ${TEST_NAME}) set(TX_REGRESSION_TARGETS ${TX_REGRESSION_TARGETS} PARENT_SCOPE) - # The executable is embedded and not directly host-runnable. - # Add a real add_test() command here once a QEMU or board runner exists. + if(TEST_NAME STREQUAL "threadx_initialize_kernel_setup_test") + add_test( + NAME ${TEST_NAME} + COMMAND ${CMAKE_CURRENT_LIST_DIR}/run_threadx_simple_test.sh + ${THREADX_TEST_SIMULATOR} + ${THREADX_TEST_ISA} + $ + "Running Initialize Kernel Setup Test................................ SUCCESS!" + "Running Initialize Kernel Setup Test................................ ERROR!" + ) + else() + add_test( + NAME ${TEST_NAME} + COMMAND ${CMAKE_CURRENT_LIST_DIR}/run_threadx_test.sh + ${THREADX_TEST_SIMULATOR} + ${THREADX_TEST_ISA} + $ + ) + endif() + set_tests_properties(${TEST_NAME} PROPERTIES TIMEOUT 5) endfunction() foreach(test_case ${TX_REGRESSION_CASES}) diff --git a/test/threadx/run_threadx_simple_test.sh b/test/threadx/run_threadx_simple_test.sh new file mode 100755 index 0000000..1e9ef7c --- /dev/null +++ b/test/threadx/run_threadx_simple_test.sh @@ -0,0 +1,40 @@ +#!/bin/bash +set -euo pipefail + +simulator="$1" +isa="$2" +elf="$3" +pass_pattern="$4" +fail_pattern="$5" + +if [ ! -x "$simulator" ]; then + echo "Simulator not found or not executable: $simulator" >&2 + exit 2 +fi + +if [ ! -f "$elf" ]; then + echo "ELF not found: $elf" >&2 + exit 2 +fi + +log_file=$(mktemp) +trap 'rm -f "$log_file"' EXIT + +"$simulator" \ + --isa="$isa" \ + -f "$elf" \ + -p tb.top.core.finish_condition=1 \ + -m 10s \ + | tee "$log_file" + +if grep -Fq "$fail_pattern" "$log_file"; then + echo "ThreadX regression reported failure pattern: $fail_pattern" >&2 + exit 1 +fi + +if ! grep -Fq "$pass_pattern" "$log_file"; then + echo "Missing expected success pattern in simulator output: $pass_pattern" >&2 + exit 1 +fi + +exit 0 diff --git a/test/threadx/run_threadx_test.sh b/test/threadx/run_threadx_test.sh new file mode 100755 index 0000000..e728176 --- /dev/null +++ b/test/threadx/run_threadx_test.sh @@ -0,0 +1,48 @@ +#!/bin/bash +set -euo pipefail + +simulator="$1" +isa="$2" +elf="$3" + +if [ ! -x "$simulator" ]; then + echo "Simulator not found or not executable: $simulator" >&2 + exit 2 +fi + +if [ ! -f "$elf" ]; then + echo "ELF not found: $elf" >&2 + exit 2 +fi + +log_file=$(mktemp) +trap 'rm -f "$log_file"' EXIT + +"$simulator" \ + --isa="$isa" \ + -f "$elf" \ + -p tb.top.core.finish_condition=1 \ + -m 10s \ + | tee "$log_file" + +summary_line=$(grep -F "**** Test Summary:" "$log_file" | tail -n 1 || true) +if [ -z "$summary_line" ]; then + echo "Missing ThreadX test summary in simulator output" >&2 + exit 1 +fi + +read -r passed failed errors <&2 + exit 1 +fi + +if [ "$failed" -ne 0 ] || [ "$errors" -ne 0 ]; then + echo "ThreadX regression reported failure: passed=$passed failed=$failed errors=$errors" >&2 + exit 1 +fi + +exit 0 -- 2.49.1 From b8d4472657840a9deef94dba3b0387709b7c200d Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Sun, 22 Mar 2026 21:14:39 +0100 Subject: [PATCH 40/79] corrects interrupt disable and restore macros Turns out the port was really that shitty --- port/threadx/inc/tx_port.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/port/threadx/inc/tx_port.h b/port/threadx/inc/tx_port.h index e2fc50e..734eb81 100644 --- a/port/threadx/inc/tx_port.h +++ b/port/threadx/inc/tx_port.h @@ -348,13 +348,13 @@ ULONG _tx_thread_interrupt_control(unsigned int new_posture); /* Atomically read mstatus into tx_saved_posture and clear bit 3 of mstatus. */ #define TX_DISABLE \ { \ - __asm__("csrrci %0, mstatus, 0x08" : "=r"(tx_saved_posture) :); \ + __asm__ volatile("csrrci %0, mstatus, 0x08" : "=r"(tx_saved_posture) : : "memory"); \ }; /* We only care about mstatus.mie (bit 3), so mask tx_saved_posture and write to mstatus. */ #define TX_RESTORE \ { \ register ULONG __tempmask = tx_saved_posture & 0x08; \ - __asm__("csrrs x0, mstatus, %0 \n\t" : : "r"(__tempmask) :); \ + __asm__ volatile("csrrs x0, mstatus, %0" : : "r"(__tempmask) : "memory"); \ }; #endif -- 2.49.1 From bbbdae160a0e04736370c655015ad08b99bfd325 Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Sun, 22 Mar 2026 21:18:40 +0100 Subject: [PATCH 41/79] makes the timer tick actually every ms --- port/moonlight/inc/hwtimer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/port/moonlight/inc/hwtimer.h b/port/moonlight/inc/hwtimer.h index fb7e7a9..0c339fd 100644 --- a/port/moonlight/inc/hwtimer.h +++ b/port/moonlight/inc/hwtimer.h @@ -15,7 +15,7 @@ #include "platform.h" #define TICKNUM_PER_SECOND 32768 -#define TICKNUM_PER_TIMER (TICKNUM_PER_SECOND / 100) // ~ 1ms timer +#define TICKNUM_PER_TIMER (TICKNUM_PER_SECOND / 1000) // ~ 1ms timer static inline int hwtimer_init(void) { -- 2.49.1 From 59b9e9e7e32b59164c04176a41356f728f3d2aa0 Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Sun, 22 Mar 2026 21:37:30 +0100 Subject: [PATCH 42/79] adds explicit dependencies for generated files --- test/CMakeLists.txt | 2 ++ test/threadx/CMakeLists.txt | 15 +++++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d42c1ae..f8d8c89 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -55,9 +55,11 @@ add_custom_command( ${CMAKE_CURRENT_LIST_DIR}/threadx/generate_trap_file.sh VERBATIM ) +add_custom_target(threadx_regression_generated_trap DEPENDS ${MOONLIGHT_TRAP_SOURCE}) set_source_files_properties(${MOONLIGHT_TRAP_SOURCE} PROPERTIES GENERATED TRUE) add_subdirectory(${THREADX4TGFS_ROOT}/port/moonlight ${CMAKE_BINARY_DIR}/port/moonlight) +add_dependencies(moonlight_platform_common threadx_regression_generated_trap) function(setup_target TARGET) set(options) diff --git a/test/threadx/CMakeLists.txt b/test/threadx/CMakeLists.txt index ae9a56b..6264350 100644 --- a/test/threadx/CMakeLists.txt +++ b/test/threadx/CMakeLists.txt @@ -11,12 +11,15 @@ set(TX_KERNEL_SETUP_TEST_SOURCE ${CMAKE_BINARY_DIR}/generated/threadx_initialize add_custom_command( OUTPUT ${TX_KERNEL_SETUP_TEST_SOURCE} COMMAND bash ${CMAKE_CURRENT_LIST_DIR}/generate_kernel_setup_test_file.sh - ${TX_KERNEL_SETUP_TEST_SOURCE_INPUT} - ${TX_KERNEL_SETUP_TEST_SOURCE} + ${TX_KERNEL_SETUP_TEST_SOURCE_INPUT} + ${TX_KERNEL_SETUP_TEST_SOURCE} DEPENDS ${TX_KERNEL_SETUP_TEST_SOURCE_INPUT} - ${CMAKE_CURRENT_LIST_DIR}/generate_kernel_setup_test_file.sh + ${CMAKE_CURRENT_LIST_DIR}/generate_kernel_setup_test_file.sh VERBATIM ) +add_custom_target(threadx_regression_generated_kernel_setup_test + DEPENDS ${TX_KERNEL_SETUP_TEST_SOURCE} +) set_source_files_properties(${TX_KERNEL_SETUP_TEST_SOURCE} PROPERTIES GENERATED TRUE) set(TX_REGRESSION_CASES @@ -150,6 +153,10 @@ function(add_threadx_regression_test TEST_SOURCE) SOURCES ${TEST_SOURCE} ) + if(TEST_NAME STREQUAL "threadx_initialize_kernel_setup_test") + add_dependencies(${TEST_NAME} threadx_regression_generated_kernel_setup_test) + endif() + list(APPEND TX_REGRESSION_TARGETS ${TEST_NAME}) set(TX_REGRESSION_TARGETS ${TX_REGRESSION_TARGETS} PARENT_SCOPE) @@ -172,7 +179,7 @@ function(add_threadx_regression_test TEST_SOURCE) $ ) endif() - set_tests_properties(${TEST_NAME} PROPERTIES TIMEOUT 5) + set_tests_properties(${TEST_NAME} PROPERTIES TIMEOUT 10) endfunction() foreach(test_case ${TX_REGRESSION_CASES}) -- 2.49.1 From 47ba20d762fdbacbb1bf9f24dc3f19ea4649b1e6 Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Sun, 22 Mar 2026 22:47:26 +0100 Subject: [PATCH 43/79] Changes simulation ending mechanism (exit function) As higher optimization might actually schedule a jump to self while waiting for timer interrupts this is a more robust implementation in that sense --- port/moonlight/src/bootup.c | 6 +++--- test/threadx/run_threadx_test.sh | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/port/moonlight/src/bootup.c b/port/moonlight/src/bootup.c index 69df782..17ac817 100644 --- a/port/moonlight/src/bootup.c +++ b/port/moonlight/src/bootup.c @@ -10,6 +10,7 @@ #include #include #include +#include #ifdef __cplusplus #define EXTERN_C extern "C" #else @@ -121,10 +122,9 @@ void _initialize(void) { // This should never be called. Busy loop with the CPU in idle state. void _exit(int exit_code) { (void)exit_code; - // Halt + csr_clr_bits_mie(MIE_MTI_BIT_MASK); + csr_clr_bits_mstatus(MSTATUS_MIE_BIT_MASK); while (1) { __asm__ volatile ("wfi"); - // Incase periodic interrupts are scheduled (e.g. timer), additionaly jump to self - for (;;); } } \ No newline at end of file diff --git a/test/threadx/run_threadx_test.sh b/test/threadx/run_threadx_test.sh index e728176..fa40c93 100755 --- a/test/threadx/run_threadx_test.sh +++ b/test/threadx/run_threadx_test.sh @@ -21,7 +21,6 @@ trap 'rm -f "$log_file"' EXIT "$simulator" \ --isa="$isa" \ -f "$elf" \ - -p tb.top.core.finish_condition=1 \ -m 10s \ | tee "$log_file" -- 2.49.1 From 4e87ae718b71224afbd96d8a0464de2507c7e6b9 Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Mon, 23 Mar 2026 16:32:33 +0100 Subject: [PATCH 44/79] removes patching of kernel tets, as a correct testsuite requires a patched threadx anyways This will not build, as atp threadx is still checked in clean --- test/threadx/CMakeLists.txt | 22 +------------------ .../generate_kernel_setup_test_file.sh | 16 -------------- 2 files changed, 1 insertion(+), 37 deletions(-) delete mode 100755 test/threadx/generate_kernel_setup_test_file.sh diff --git a/test/threadx/CMakeLists.txt b/test/threadx/CMakeLists.txt index 6264350..724ddc2 100644 --- a/test/threadx/CMakeLists.txt +++ b/test/threadx/CMakeLists.txt @@ -5,23 +5,6 @@ set(TX_CMAKE_DIR ${THREADX4TGFS_ROOT}/third-party/threadx/test/tx/cmake ) -# This test needs a local test_interrupt_dispatch() because it does not link testcontrol.c. -set(TX_KERNEL_SETUP_TEST_SOURCE_INPUT ${THREADX4TGFS_ROOT}/third-party/threadx/test/tx/regression/threadx_initialize_kernel_setup_test.c) -set(TX_KERNEL_SETUP_TEST_SOURCE ${CMAKE_BINARY_DIR}/generated/threadx_initialize_kernel_setup_test.c) -add_custom_command( - OUTPUT ${TX_KERNEL_SETUP_TEST_SOURCE} - COMMAND bash ${CMAKE_CURRENT_LIST_DIR}/generate_kernel_setup_test_file.sh - ${TX_KERNEL_SETUP_TEST_SOURCE_INPUT} - ${TX_KERNEL_SETUP_TEST_SOURCE} - DEPENDS ${TX_KERNEL_SETUP_TEST_SOURCE_INPUT} - ${CMAKE_CURRENT_LIST_DIR}/generate_kernel_setup_test_file.sh - VERBATIM -) -add_custom_target(threadx_regression_generated_kernel_setup_test - DEPENDS ${TX_KERNEL_SETUP_TEST_SOURCE} -) -set_source_files_properties(${TX_KERNEL_SETUP_TEST_SOURCE} PROPERTIES GENERATED TRUE) - set(TX_REGRESSION_CASES ${TX_REGRESSION_DIR}/threadx_block_memory_basic_test.c ${TX_REGRESSION_DIR}/threadx_block_memory_error_detection_test.c @@ -118,7 +101,7 @@ set(TX_REGRESSION_CASES ${TX_REGRESSION_DIR}/threadx_timer_multiple_test.c ${TX_REGRESSION_DIR}/threadx_timer_simple_test.c ${TX_REGRESSION_DIR}/threadx_trace_basic_test.c - ${TX_KERNEL_SETUP_TEST_SOURCE} + ${TX_REGRESSION_DIR}/threadx_initialize_kernel_setup_test.c ) set(TX_REGRESSION_TARGETS) @@ -153,9 +136,6 @@ function(add_threadx_regression_test TEST_SOURCE) SOURCES ${TEST_SOURCE} ) - if(TEST_NAME STREQUAL "threadx_initialize_kernel_setup_test") - add_dependencies(${TEST_NAME} threadx_regression_generated_kernel_setup_test) - endif() list(APPEND TX_REGRESSION_TARGETS ${TEST_NAME}) set(TX_REGRESSION_TARGETS ${TX_REGRESSION_TARGETS} PARENT_SCOPE) diff --git a/test/threadx/generate_kernel_setup_test_file.sh b/test/threadx/generate_kernel_setup_test_file.sh deleted file mode 100755 index 01dc5cc..0000000 --- a/test/threadx/generate_kernel_setup_test_file.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash -set -eu - -src="$1" -dst="$2" - -anchor='void tx_application_define(void *first_unused_memory){}' - -line=$(grep -n -F "$anchor" "$src" | head -n 1 | cut -d: -f1) -if [ -z "$line" ]; then - echo "failed to find tx_application_define anchor in kernel setup test source" >&2 - exit 1 -fi -mkdir -p "$(dirname "$dst")" -sed "${line}a\\ -void test_interrupt_dispatch(void){}" "$src" > "$dst" -- 2.49.1 From e02a58e25f200bbf25ec72bb7cfcb19faa7c5752 Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Mon, 23 Mar 2026 16:58:56 +0100 Subject: [PATCH 45/79] switches to tohost based exit code, reducing complexity in deciding failure / pass --- port/moonlight/src/bootup.c | 7 ++-- test/threadx/CMakeLists.txt | 28 +++++---------- test/threadx/run_threadx_simple_test.sh | 40 --------------------- test/threadx/run_threadx_test.sh | 47 ------------------------- 4 files changed, 13 insertions(+), 109 deletions(-) delete mode 100755 test/threadx/run_threadx_simple_test.sh delete mode 100755 test/threadx/run_threadx_test.sh diff --git a/port/moonlight/src/bootup.c b/port/moonlight/src/bootup.c index 17ac817..5230ffb 100644 --- a/port/moonlight/src/bootup.c +++ b/port/moonlight/src/bootup.c @@ -37,6 +37,7 @@ EXTERN_C function_t __init_array_start; EXTERN_C function_t __init_array_end; EXTERN_C function_t __fini_array_start; EXTERN_C function_t __fini_array_end; +EXTERN_C volatile uintptr_t tohost; // This function will be placed by the linker script according to the section // Raw function 'called' by the CPU with no runtime. @@ -119,9 +120,11 @@ void _initialize(void) { _exit(rc); } -// This should never be called. Busy loop with the CPU in idle state. +// This should never be called. Report the exit code through HTIF and idle the CPU. void _exit(int exit_code) { - (void)exit_code; + uintptr_t htif_exit_code = (((uintptr_t)(unsigned int)exit_code) << 1) | 1u; + + tohost = htif_exit_code; csr_clr_bits_mie(MIE_MTI_BIT_MASK); csr_clr_bits_mstatus(MSTATUS_MIE_BIT_MASK); while (1) { diff --git a/test/threadx/CMakeLists.txt b/test/threadx/CMakeLists.txt index 724ddc2..2eb06c7 100644 --- a/test/threadx/CMakeLists.txt +++ b/test/threadx/CMakeLists.txt @@ -140,26 +140,14 @@ function(add_threadx_regression_test TEST_SOURCE) list(APPEND TX_REGRESSION_TARGETS ${TEST_NAME}) set(TX_REGRESSION_TARGETS ${TX_REGRESSION_TARGETS} PARENT_SCOPE) - if(TEST_NAME STREQUAL "threadx_initialize_kernel_setup_test") - add_test( - NAME ${TEST_NAME} - COMMAND ${CMAKE_CURRENT_LIST_DIR}/run_threadx_simple_test.sh - ${THREADX_TEST_SIMULATOR} - ${THREADX_TEST_ISA} - $ - "Running Initialize Kernel Setup Test................................ SUCCESS!" - "Running Initialize Kernel Setup Test................................ ERROR!" - ) - else() - add_test( - NAME ${TEST_NAME} - COMMAND ${CMAKE_CURRENT_LIST_DIR}/run_threadx_test.sh - ${THREADX_TEST_SIMULATOR} - ${THREADX_TEST_ISA} - $ - ) - endif() - set_tests_properties(${TEST_NAME} PROPERTIES TIMEOUT 10) + add_test( + NAME ${TEST_NAME} + COMMAND ${THREADX_TEST_SIMULATOR} + --isa=${THREADX_TEST_ISA} + -f $ + -m 60s + ) + set_tests_properties(${TEST_NAME} PROPERTIES TIMEOUT 60) endfunction() foreach(test_case ${TX_REGRESSION_CASES}) diff --git a/test/threadx/run_threadx_simple_test.sh b/test/threadx/run_threadx_simple_test.sh deleted file mode 100755 index 1e9ef7c..0000000 --- a/test/threadx/run_threadx_simple_test.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/bash -set -euo pipefail - -simulator="$1" -isa="$2" -elf="$3" -pass_pattern="$4" -fail_pattern="$5" - -if [ ! -x "$simulator" ]; then - echo "Simulator not found or not executable: $simulator" >&2 - exit 2 -fi - -if [ ! -f "$elf" ]; then - echo "ELF not found: $elf" >&2 - exit 2 -fi - -log_file=$(mktemp) -trap 'rm -f "$log_file"' EXIT - -"$simulator" \ - --isa="$isa" \ - -f "$elf" \ - -p tb.top.core.finish_condition=1 \ - -m 10s \ - | tee "$log_file" - -if grep -Fq "$fail_pattern" "$log_file"; then - echo "ThreadX regression reported failure pattern: $fail_pattern" >&2 - exit 1 -fi - -if ! grep -Fq "$pass_pattern" "$log_file"; then - echo "Missing expected success pattern in simulator output: $pass_pattern" >&2 - exit 1 -fi - -exit 0 diff --git a/test/threadx/run_threadx_test.sh b/test/threadx/run_threadx_test.sh deleted file mode 100755 index fa40c93..0000000 --- a/test/threadx/run_threadx_test.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/bash -set -euo pipefail - -simulator="$1" -isa="$2" -elf="$3" - -if [ ! -x "$simulator" ]; then - echo "Simulator not found or not executable: $simulator" >&2 - exit 2 -fi - -if [ ! -f "$elf" ]; then - echo "ELF not found: $elf" >&2 - exit 2 -fi - -log_file=$(mktemp) -trap 'rm -f "$log_file"' EXIT - -"$simulator" \ - --isa="$isa" \ - -f "$elf" \ - -m 10s \ - | tee "$log_file" - -summary_line=$(grep -F "**** Test Summary:" "$log_file" | tail -n 1 || true) -if [ -z "$summary_line" ]; then - echo "Missing ThreadX test summary in simulator output" >&2 - exit 1 -fi - -read -r passed failed errors <&2 - exit 1 -fi - -if [ "$failed" -ne 0 ] || [ "$errors" -ne 0 ]; then - echo "ThreadX regression reported failure: passed=$passed failed=$failed errors=$errors" >&2 - exit 1 -fi - -exit 0 -- 2.49.1 From 24d22e42f66311e28698da6c43e7b667afdc4d76 Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Tue, 24 Mar 2026 14:37:47 +0100 Subject: [PATCH 46/79] moves threadx to minres fork --- .gitmodules | 2 +- third-party/threadx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 0fd4ff1..cd8f876 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "third-party/threadx"] path = third-party/threadx - url = https://github.com/eclipse-threadx/threadx.git + url = https://github.com/Minres/threadx.git [submodule "third-party/netxduo"] path = third-party/netxduo url = https://github.com/eclipse-threadx/netxduo.git diff --git a/third-party/threadx b/third-party/threadx index 4b6e810..13ec502 160000 --- a/third-party/threadx +++ b/third-party/threadx @@ -1 +1 @@ -Subproject commit 4b6e8100d932a3a67b34c6eb17f84f3bffb9e2ae +Subproject commit 13ec5027ee00ca2338c4c988d5a13f9ef037e91a -- 2.49.1 From 7253aadd52d97bfd9591d1069ef0d53f878e497a Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Tue, 24 Mar 2026 14:38:16 +0100 Subject: [PATCH 47/79] corrects alignment for 64bit in port --- port/threadx/inc/tx_port.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/port/threadx/inc/tx_port.h b/port/threadx/inc/tx_port.h index 734eb81..716d672 100644 --- a/port/threadx/inc/tx_port.h +++ b/port/threadx/inc/tx_port.h @@ -96,11 +96,14 @@ typedef unsigned char UCHAR; typedef int INT; typedef unsigned int UINT; typedef int LONG; -typedef unsigned int ULONG; +typedef unsigned int ULONG; // ThreadX expects ULONG to be 32 bit typedef unsigned long long ULONG64; typedef short SHORT; typedef unsigned short USHORT; #define ULONG64_DEFINED +#define ALIGN_TYPE_DEFINED +// Since ULONG is not actually unsigned long, it is to small to hold pointers for 64-bit systems +#define ALIGN_TYPE unsigned long /* Define the priority levels for ThreadX. Legal values range from 32 to 1024 and MUST be evenly divisible by 32. */ -- 2.49.1 From c11f68d20c9982f45d0c45c226312b31866a0301 Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Tue, 24 Mar 2026 18:00:50 +0100 Subject: [PATCH 48/79] initial test harness for smp regression --- port/moonlight/CMakeLists.txt | 33 ++- port/threadx_smp/CMakeLists.txt | 199 +++++++++++++++++- port/threadx_smp/inc/tx_port.h | 105 +++++++++ .../src/tx_thread_interrupt_control.S | 81 +++++++ test/CMakeLists.txt | 17 +- test/smp/CMakeLists.txt | 178 ++++++++++++++++ test/threadx/generate_trap_file.sh | 2 +- third-party/threadx | 2 +- 8 files changed, 605 insertions(+), 12 deletions(-) create mode 100644 port/threadx_smp/src/tx_thread_interrupt_control.S create mode 100644 test/smp/CMakeLists.txt diff --git a/port/moonlight/CMakeLists.txt b/port/moonlight/CMakeLists.txt index ae2e4c6..e790854 100644 --- a/port/moonlight/CMakeLists.txt +++ b/port/moonlight/CMakeLists.txt @@ -32,12 +32,19 @@ if(NX_DEBUG) NX_DEBUG_PACKET) endif() -add_library(moonlight_platform_common OBJECT - ${MOONLIGHT_PLATFORM_SOURCES}) -target_link_libraries(moonlight_platform_common PUBLIC - moonlight_platform_defaults - c - threadx) +function(moonlight_define_platform TARGET THREADX_TARGET) + add_library(${TARGET} OBJECT + ${MOONLIGHT_PLATFORM_SOURCES}) + target_link_libraries(${TARGET} PUBLIC + moonlight_platform_defaults + c + ${THREADX_TARGET}) +endfunction() + +moonlight_define_platform(moonlight_platform_common threadx) +if(TARGET threadx_smp) + moonlight_define_platform(moonlight_platform_common_smp threadx_smp) +endif() if(DEFINED NETXDUO_CUSTOM_PORT) add_library(moonlight_network_driver OBJECT @@ -50,7 +57,19 @@ if(DEFINED NETXDUO_CUSTOM_PORT) endif() function(target_add_moonlight_platform TARGET) - target_link_libraries(${TARGET} PRIVATE moonlight_platform_common) + set(options) + set(oneValueArgs PLATFORM_TARGET) + cmake_parse_arguments(TAMP "${options}" "${oneValueArgs}" "" ${ARGN}) + + if(TAMP_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "target_add_moonlight_platform(${TARGET} ...): unknown args: ${TAMP_UNPARSED_ARGUMENTS}") + endif() + + if(NOT TAMP_PLATFORM_TARGET) + set(TAMP_PLATFORM_TARGET moonlight_platform_common) + endif() + + target_link_libraries(${TARGET} PRIVATE ${TAMP_PLATFORM_TARGET}) endfunction() function(target_add_moonlight_network_driver TARGET) diff --git a/port/threadx_smp/CMakeLists.txt b/port/threadx_smp/CMakeLists.txt index e4c04ee..1f2c4bd 100644 --- a/port/threadx_smp/CMakeLists.txt +++ b/port/threadx_smp/CMakeLists.txt @@ -16,6 +16,7 @@ set(THREADX_SMP_CUSTOM_SRC src/tx_initialize_low_level.S src/tx_thread_context_restore.S src/tx_thread_context_save.S + src/tx_thread_interrupt_control.S src/tx_thread_schedule.S src/tx_thread_smp_core_get.S src/tx_thread_smp_core_preempt.c @@ -43,7 +44,203 @@ threadx_smp_add_offsets( OUT_INCLUDE_DIR THREADX_SMP_GENERATED_INC_DIR ) -file(GLOB THREADX_SMP_SOURCES ${THREADX_COMMON_SMP_DIR}/src/*.c) +set(THREADX_SMP_SOURCES + ${THREADX_COMMON_SMP_DIR}/src/tx_block_allocate.c + ${THREADX_COMMON_SMP_DIR}/src/tx_block_pool_cleanup.c + ${THREADX_COMMON_SMP_DIR}/src/tx_block_pool_create.c + ${THREADX_COMMON_SMP_DIR}/src/tx_block_pool_delete.c + ${THREADX_COMMON_SMP_DIR}/src/tx_block_pool_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_block_pool_initialize.c + ${THREADX_COMMON_SMP_DIR}/src/tx_block_pool_performance_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_block_pool_performance_system_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_block_pool_prioritize.c + ${THREADX_COMMON_SMP_DIR}/src/tx_block_release.c + ${THREADX_COMMON_SMP_DIR}/src/tx_byte_allocate.c + ${THREADX_COMMON_SMP_DIR}/src/tx_byte_pool_cleanup.c + ${THREADX_COMMON_SMP_DIR}/src/tx_byte_pool_create.c + ${THREADX_COMMON_SMP_DIR}/src/tx_byte_pool_delete.c + ${THREADX_COMMON_SMP_DIR}/src/tx_byte_pool_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_byte_pool_initialize.c + ${THREADX_COMMON_SMP_DIR}/src/tx_byte_pool_performance_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_byte_pool_performance_system_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_byte_pool_prioritize.c + ${THREADX_COMMON_SMP_DIR}/src/tx_byte_pool_search.c + ${THREADX_COMMON_SMP_DIR}/src/tx_byte_release.c + ${THREADX_COMMON_SMP_DIR}/src/txe_block_allocate.c + ${THREADX_COMMON_SMP_DIR}/src/txe_block_pool_create.c + ${THREADX_COMMON_SMP_DIR}/src/txe_block_pool_delete.c + ${THREADX_COMMON_SMP_DIR}/src/txe_block_pool_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/txe_block_pool_prioritize.c + ${THREADX_COMMON_SMP_DIR}/src/txe_block_release.c + ${THREADX_COMMON_SMP_DIR}/src/txe_byte_allocate.c + ${THREADX_COMMON_SMP_DIR}/src/txe_byte_pool_create.c + ${THREADX_COMMON_SMP_DIR}/src/txe_byte_pool_delete.c + ${THREADX_COMMON_SMP_DIR}/src/txe_byte_pool_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/txe_byte_pool_prioritize.c + ${THREADX_COMMON_SMP_DIR}/src/txe_byte_release.c + ${THREADX_COMMON_SMP_DIR}/src/txe_event_flags_create.c + ${THREADX_COMMON_SMP_DIR}/src/txe_event_flags_delete.c + ${THREADX_COMMON_SMP_DIR}/src/txe_event_flags_get.c + ${THREADX_COMMON_SMP_DIR}/src/txe_event_flags_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/txe_event_flags_set.c + ${THREADX_COMMON_SMP_DIR}/src/txe_event_flags_set_notify.c + ${THREADX_COMMON_SMP_DIR}/src/txe_mutex_create.c + ${THREADX_COMMON_SMP_DIR}/src/txe_mutex_delete.c + ${THREADX_COMMON_SMP_DIR}/src/txe_mutex_get.c + ${THREADX_COMMON_SMP_DIR}/src/txe_mutex_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/txe_mutex_prioritize.c + ${THREADX_COMMON_SMP_DIR}/src/txe_mutex_put.c + ${THREADX_COMMON_SMP_DIR}/src/txe_queue_create.c + ${THREADX_COMMON_SMP_DIR}/src/txe_queue_delete.c + ${THREADX_COMMON_SMP_DIR}/src/txe_queue_flush.c + ${THREADX_COMMON_SMP_DIR}/src/txe_queue_front_send.c + ${THREADX_COMMON_SMP_DIR}/src/txe_queue_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/txe_queue_prioritize.c + ${THREADX_COMMON_SMP_DIR}/src/txe_queue_receive.c + ${THREADX_COMMON_SMP_DIR}/src/txe_queue_send.c + ${THREADX_COMMON_SMP_DIR}/src/txe_queue_send_notify.c + ${THREADX_COMMON_SMP_DIR}/src/txe_semaphore_ceiling_put.c + ${THREADX_COMMON_SMP_DIR}/src/txe_semaphore_create.c + ${THREADX_COMMON_SMP_DIR}/src/txe_semaphore_delete.c + ${THREADX_COMMON_SMP_DIR}/src/txe_semaphore_get.c + ${THREADX_COMMON_SMP_DIR}/src/txe_semaphore_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/txe_semaphore_prioritize.c + ${THREADX_COMMON_SMP_DIR}/src/txe_semaphore_put.c + ${THREADX_COMMON_SMP_DIR}/src/txe_semaphore_put_notify.c + ${THREADX_COMMON_SMP_DIR}/src/txe_thread_create.c + ${THREADX_COMMON_SMP_DIR}/src/txe_thread_delete.c + ${THREADX_COMMON_SMP_DIR}/src/txe_thread_entry_exit_notify.c + ${THREADX_COMMON_SMP_DIR}/src/txe_thread_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/txe_thread_preemption_change.c + ${THREADX_COMMON_SMP_DIR}/src/txe_thread_priority_change.c + ${THREADX_COMMON_SMP_DIR}/src/txe_thread_relinquish.c + ${THREADX_COMMON_SMP_DIR}/src/txe_thread_reset.c + ${THREADX_COMMON_SMP_DIR}/src/txe_thread_resume.c + ${THREADX_COMMON_SMP_DIR}/src/txe_thread_suspend.c + ${THREADX_COMMON_SMP_DIR}/src/txe_thread_terminate.c + ${THREADX_COMMON_SMP_DIR}/src/txe_thread_time_slice_change.c + ${THREADX_COMMON_SMP_DIR}/src/txe_thread_wait_abort.c + ${THREADX_COMMON_SMP_DIR}/src/txe_timer_activate.c + ${THREADX_COMMON_SMP_DIR}/src/txe_timer_change.c + ${THREADX_COMMON_SMP_DIR}/src/txe_timer_create.c + ${THREADX_COMMON_SMP_DIR}/src/txe_timer_deactivate.c + ${THREADX_COMMON_SMP_DIR}/src/txe_timer_delete.c + ${THREADX_COMMON_SMP_DIR}/src/txe_timer_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_event_flags_cleanup.c + ${THREADX_COMMON_SMP_DIR}/src/tx_event_flags_create.c + ${THREADX_COMMON_SMP_DIR}/src/tx_event_flags_delete.c + ${THREADX_COMMON_SMP_DIR}/src/tx_event_flags_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_event_flags_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_event_flags_initialize.c + ${THREADX_COMMON_SMP_DIR}/src/tx_event_flags_performance_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_event_flags_performance_system_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_event_flags_set.c + ${THREADX_COMMON_SMP_DIR}/src/tx_event_flags_set_notify.c + ${THREADX_COMMON_SMP_DIR}/src/tx_initialize_high_level.c + ${THREADX_COMMON_SMP_DIR}/src/tx_initialize_kernel_enter.c + ${THREADX_COMMON_SMP_DIR}/src/tx_initialize_kernel_setup.c + ${THREADX_COMMON_SMP_DIR}/src/tx_misra.c + ${THREADX_COMMON_SMP_DIR}/src/tx_mutex_cleanup.c + ${THREADX_COMMON_SMP_DIR}/src/tx_mutex_create.c + ${THREADX_COMMON_SMP_DIR}/src/tx_mutex_delete.c + ${THREADX_COMMON_SMP_DIR}/src/tx_mutex_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_mutex_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_mutex_initialize.c + ${THREADX_COMMON_SMP_DIR}/src/tx_mutex_performance_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_mutex_performance_system_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_mutex_prioritize.c + ${THREADX_COMMON_SMP_DIR}/src/tx_mutex_priority_change.c + ${THREADX_COMMON_SMP_DIR}/src/tx_mutex_put.c + ${THREADX_COMMON_SMP_DIR}/src/tx_queue_cleanup.c + ${THREADX_COMMON_SMP_DIR}/src/tx_queue_create.c + ${THREADX_COMMON_SMP_DIR}/src/tx_queue_delete.c + ${THREADX_COMMON_SMP_DIR}/src/tx_queue_flush.c + ${THREADX_COMMON_SMP_DIR}/src/tx_queue_front_send.c + ${THREADX_COMMON_SMP_DIR}/src/tx_queue_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_queue_initialize.c + ${THREADX_COMMON_SMP_DIR}/src/tx_queue_performance_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_queue_performance_system_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_queue_prioritize.c + ${THREADX_COMMON_SMP_DIR}/src/tx_queue_receive.c + ${THREADX_COMMON_SMP_DIR}/src/tx_queue_send.c + ${THREADX_COMMON_SMP_DIR}/src/tx_queue_send_notify.c + ${THREADX_COMMON_SMP_DIR}/src/tx_semaphore_ceiling_put.c + ${THREADX_COMMON_SMP_DIR}/src/tx_semaphore_cleanup.c + ${THREADX_COMMON_SMP_DIR}/src/tx_semaphore_create.c + ${THREADX_COMMON_SMP_DIR}/src/tx_semaphore_delete.c + ${THREADX_COMMON_SMP_DIR}/src/tx_semaphore_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_semaphore_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_semaphore_initialize.c + ${THREADX_COMMON_SMP_DIR}/src/tx_semaphore_performance_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_semaphore_performance_system_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_semaphore_prioritize.c + ${THREADX_COMMON_SMP_DIR}/src/tx_semaphore_put.c + ${THREADX_COMMON_SMP_DIR}/src/tx_semaphore_put_notify.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_create.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_delete.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_entry_exit_notify.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_identify.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_initialize.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_performance_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_performance_system_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_preemption_change.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_priority_change.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_relinquish.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_reset.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_resume.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_shell_entry.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_sleep.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_smp_core_exclude.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_smp_core_exclude_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_smp_current_state_set.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_smp_debug_entry_insert.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_smp_high_level_initialize.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_smp_rebalance_execute_list.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_smp_utilities.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_stack_analyze.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_stack_error_handler.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_stack_error_notify.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_suspend.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_system_preempt_check.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_system_resume.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_system_suspend.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_terminate.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_timeout.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_time_slice.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_time_slice_change.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_wait_abort.c + ${THREADX_COMMON_SMP_DIR}/src/tx_time_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_timer_activate.c + ${THREADX_COMMON_SMP_DIR}/src/tx_timer_change.c + ${THREADX_COMMON_SMP_DIR}/src/tx_timer_create.c + ${THREADX_COMMON_SMP_DIR}/src/tx_timer_deactivate.c + ${THREADX_COMMON_SMP_DIR}/src/tx_timer_delete.c + ${THREADX_COMMON_SMP_DIR}/src/tx_timer_expiration_process.c + ${THREADX_COMMON_SMP_DIR}/src/tx_timer_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_timer_initialize.c + ${THREADX_COMMON_SMP_DIR}/src/tx_timer_performance_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_timer_performance_system_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_timer_smp_core_exclude.c + ${THREADX_COMMON_SMP_DIR}/src/tx_timer_smp_core_exclude_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_timer_system_activate.c + ${THREADX_COMMON_SMP_DIR}/src/tx_timer_system_deactivate.c + ${THREADX_COMMON_SMP_DIR}/src/tx_timer_thread_entry.c + ${THREADX_COMMON_SMP_DIR}/src/tx_time_set.c + ${THREADX_COMMON_SMP_DIR}/src/tx_trace_buffer_full_notify.c + ${THREADX_COMMON_SMP_DIR}/src/tx_trace_disable.c + ${THREADX_COMMON_SMP_DIR}/src/tx_trace_enable.c + ${THREADX_COMMON_SMP_DIR}/src/tx_trace_event_filter.c + ${THREADX_COMMON_SMP_DIR}/src/tx_trace_event_unfilter.c + ${THREADX_COMMON_SMP_DIR}/src/tx_trace_initialize.c + ${THREADX_COMMON_SMP_DIR}/src/tx_trace_interrupt_control.c + ${THREADX_COMMON_SMP_DIR}/src/tx_trace_isr_enter_insert.c + ${THREADX_COMMON_SMP_DIR}/src/tx_trace_isr_exit_insert.c + ${THREADX_COMMON_SMP_DIR}/src/tx_trace_object_register.c + ${THREADX_COMMON_SMP_DIR}/src/tx_trace_object_unregister.c + ${THREADX_COMMON_SMP_DIR}/src/tx_trace_user_event_insert.c +) + add_library(threadx_smp STATIC) target_sources(threadx_smp PRIVATE ${THREADX_SMP_SOURCES} diff --git a/port/threadx_smp/inc/tx_port.h b/port/threadx_smp/inc/tx_port.h index 698f36a..e4fc881 100644 --- a/port/threadx_smp/inc/tx_port.h +++ b/port/threadx_smp/inc/tx_port.h @@ -77,6 +77,8 @@ /************* Define ThreadX SMP constants. *************/ +#define TX_DISABLE_INLINE + /* Define the ThreadX SMP maximum number of cores. */ #ifndef TX_THREAD_SMP_MAX_CORES @@ -271,6 +273,109 @@ typedef unsigned short USHORT; #define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) #define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) +/* Define automated regression test extensions. These are required for the + ThreadX regression tests. */ + +typedef unsigned int TEST_FLAG; +extern TEST_FLAG threadx_byte_allocate_loop_test; +extern TEST_FLAG threadx_byte_release_loop_test; +extern TEST_FLAG threadx_mutex_suspension_put_test; +extern TEST_FLAG threadx_mutex_suspension_priority_test; +#ifndef TX_TIMER_PROCESS_IN_ISR +extern TEST_FLAG threadx_delete_timer_thread; +#endif + +extern void abort_and_resume_byte_allocating_thread(void); +extern void abort_all_threads_suspended_on_mutex(void); +extern void suspend_lowest_priority(void); +#ifndef TX_TIMER_PROCESS_IN_ISR +extern void delete_timer_thread(void); +#endif +extern TEST_FLAG test_stack_analyze_flag; +extern TEST_FLAG test_initialize_flag; +extern TEST_FLAG test_forced_mutex_timeout; + +#ifdef TX_REGRESSION_TEST + +/* Define extension macros for automated coverage tests. */ + +#define TX_BYTE_ALLOCATE_EXTENSION \ + if (threadx_byte_allocate_loop_test == ((TEST_FLAG)1)) \ + { \ + pool_ptr->tx_byte_pool_owner = TX_NULL; \ + threadx_byte_allocate_loop_test = ((TEST_FLAG)0); \ + } + +#define TX_BYTE_RELEASE_EXTENSION \ + if (threadx_byte_release_loop_test == ((TEST_FLAG)1)) \ + { \ + threadx_byte_release_loop_test = ((TEST_FLAG)0); \ + abort_and_resume_byte_allocating_thread(); \ + } + +#define TX_MUTEX_PUT_EXTENSION_1 \ + if (threadx_mutex_suspension_put_test == ((TEST_FLAG)1)) \ + { \ + threadx_mutex_suspension_put_test = ((TEST_FLAG)0); \ + abort_all_threads_suspended_on_mutex(); \ + } + +#define TX_MUTEX_PUT_EXTENSION_2 \ + if (test_forced_mutex_timeout == ((TEST_FLAG)1)) \ + { \ + test_forced_mutex_timeout = ((TEST_FLAG)0); \ + _tx_thread_wait_abort(mutex_ptr->tx_mutex_suspension_list); \ + } + +#define TX_MUTEX_PRIORITY_CHANGE_EXTENSION \ + if (threadx_mutex_suspension_priority_test == ((TEST_FLAG)1)) \ + { \ + threadx_mutex_suspension_priority_test = ((TEST_FLAG)0); \ + suspend_lowest_priority(); \ + } + +#ifndef TX_TIMER_PROCESS_IN_ISR + +#define TX_TIMER_INITIALIZE_EXTENSION(a) \ + if (threadx_delete_timer_thread == ((TEST_FLAG)1)) \ + { \ + threadx_delete_timer_thread = ((TEST_FLAG)0); \ + delete_timer_thread(); \ + (a) = ((UINT)1); \ + } + +#endif + +#define TX_THREAD_STACK_ANALYZE_EXTENSION \ + if (test_stack_analyze_flag == ((TEST_FLAG)1)) \ + { \ + thread_ptr->tx_thread_id = ((TEST_FLAG)0); \ + test_stack_analyze_flag = ((TEST_FLAG)0); \ + } \ + else if (test_stack_analyze_flag == ((TEST_FLAG)2)) \ + { \ + stack_ptr = thread_ptr->tx_thread_stack_start; \ + test_stack_analyze_flag = ((TEST_FLAG)0); \ + } \ + else if (test_stack_analyze_flag == ((TEST_FLAG)3)) \ + { \ + *stack_ptr = TX_STACK_FILL; \ + test_stack_analyze_flag = ((TEST_FLAG)0); \ + } \ + else \ + { \ + test_stack_analyze_flag = ((TEST_FLAG)0); \ + } + +#define TX_INITIALIZE_KERNEL_ENTER_EXTENSION \ + if (test_initialize_flag == ((TEST_FLAG)1)) \ + { \ + test_initialize_flag = ((TEST_FLAG)0); \ + return; \ + } + +#endif + /* Define the ThreadX object creation extensions for the remaining objects. */ #define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) diff --git a/port/threadx_smp/src/tx_thread_interrupt_control.S b/port/threadx_smp/src/tx_thread_interrupt_control.S new file mode 100644 index 0000000..c75c7c4 --- /dev/null +++ b/port/threadx_smp/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * 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 Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + RETURN_MASK = 0x000000000000000F + SET_SR_MASK = 0xFFFFFFFFFFFFFFF0 + + .section .text +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control RISC-V64/GNU */ +/* 6.2.1 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 03-08-2023 Scott Larson Initial Version 6.2.1 */ +/* */ +/**************************************************************************/ +/* UINT _tx_thread_interrupt_control(UINT new_posture) +{ */ + .global _tx_thread_interrupt_control +_tx_thread_interrupt_control: + /* Pickup current interrupt lockout posture. */ + + csrr t0, mstatus + mv t1, t0 // Save original mstatus for return + + /* Apply the new interrupt posture. */ + + li t2, SET_SR_MASK // Build set SR mask + and t0, t0, t2 // Isolate interrupt lockout bits + or t0, t0, a0 // Put new lockout bits in + csrw mstatus, t0 + andi a0, t1, RETURN_MASK // Return original mstatus. + ret +/* } */ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f8d8c89..0c9d216 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -19,6 +19,10 @@ add_subdirectory(${THREADX4TGFS_ROOT}/third-party/threadx ${CMAKE_BINARY_DIR}/th target_link_libraries(threadx PUBLIC c) target_compile_definitions(threadx PUBLIC TX_REGRESSION_TEST) +add_subdirectory(${THREADX4TGFS_ROOT}/port/threadx_smp ${CMAKE_BINARY_DIR}/port/threadx_smp) +target_link_libraries(threadx_smp PUBLIC c) +target_compile_definitions(threadx_smp PUBLIC TX_REGRESSION_TEST TX_THREAD_SMP_ONLY_CORE_0_DEFAULT TX_SMP_NOT_POSSIBLE) + project(threadx_regression C ASM) enable_testing() set(TARGET_MEM "ram_dram" CACHE STRING "memory map to use") @@ -60,11 +64,15 @@ set_source_files_properties(${MOONLIGHT_TRAP_SOURCE} PROPERTIES GENERATED TRUE) add_subdirectory(${THREADX4TGFS_ROOT}/port/moonlight ${CMAKE_BINARY_DIR}/port/moonlight) add_dependencies(moonlight_platform_common threadx_regression_generated_trap) +if(TARGET moonlight_platform_common_smp) + add_dependencies(moonlight_platform_common_smp threadx_regression_generated_trap) +endif() function(setup_target TARGET) set(options) + set(oneValueArgs PLATFORM_TARGET) set(multiValueArgs LIBRARIES SOURCES) - cmake_parse_arguments(ST "${options}" "" "${multiValueArgs}" ${ARGN}) + cmake_parse_arguments(ST "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) if(ST_UNPARSED_ARGUMENTS) message(FATAL_ERROR "setup_target(${TARGET} ...): unknown args: ${ST_UNPARSED_ARGUMENTS}") endif() @@ -73,7 +81,11 @@ function(setup_target TARGET) set_target_properties(${TARGET} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR} ) - target_add_moonlight_platform(${TARGET}) + if(ST_PLATFORM_TARGET) + target_add_moonlight_platform(${TARGET} PLATFORM_TARGET ${ST_PLATFORM_TARGET}) + else() + target_add_moonlight_platform(${TARGET}) + endif() if(ST_SOURCES) target_sources(${TARGET} PRIVATE ${ST_SOURCES}) @@ -100,3 +112,4 @@ function(setup_target TARGET) endfunction() add_subdirectory(threadx regression_targets) +add_subdirectory(smp smp_regression_targets) diff --git a/test/smp/CMakeLists.txt b/test/smp/CMakeLists.txt new file mode 100644 index 0000000..8cafa8a --- /dev/null +++ b/test/smp/CMakeLists.txt @@ -0,0 +1,178 @@ +set(TX_REGRESSION_DIR + ${THREADX4TGFS_ROOT}/third-party/threadx/test/smp/regression +) +set(TX_CMAKE_DIR + ${THREADX4TGFS_ROOT}/third-party/threadx/test/tx/cmake +) + +set(TX_REGRESSION_CASES + ${TX_REGRESSION_DIR}/threadx_block_memory_basic_test.c + ${TX_REGRESSION_DIR}/threadx_block_memory_error_detection_test.c + ${TX_REGRESSION_DIR}/threadx_block_memory_information_test.c + ${TX_REGRESSION_DIR}/threadx_block_memory_prioritize_test.c + ${TX_REGRESSION_DIR}/threadx_block_memory_suspension_test.c + ${TX_REGRESSION_DIR}/threadx_block_memory_suspension_timeout_test.c + ${TX_REGRESSION_DIR}/threadx_block_memory_thread_terminate_test.c + ${TX_REGRESSION_DIR}/threadx_byte_memory_basic_test.c + ${TX_REGRESSION_DIR}/threadx_byte_memory_information_test.c + ${TX_REGRESSION_DIR}/threadx_byte_memory_prioritize_test.c + ${TX_REGRESSION_DIR}/threadx_byte_memory_suspension_test.c + ${TX_REGRESSION_DIR}/threadx_byte_memory_suspension_timeout_test.c + ${TX_REGRESSION_DIR}/threadx_byte_memory_thread_contention_test.c + ${TX_REGRESSION_DIR}/threadx_byte_memory_thread_terminate_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_basic_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_information_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_isr_set_clear_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_isr_wait_abort_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_single_thread_terminate_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_suspension_consume_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_suspension_different_bits_consume_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_suspension_different_bits_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_suspension_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_suspension_timeout_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_thread_terminate_test.c + ${TX_REGRESSION_DIR}/threadx_interrupt_control_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_basic_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_delete_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_information_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_nested_priority_inheritance_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_no_preemption_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_preemption_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_priority_inheritance_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_proritize_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_suspension_timeout_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_thread_terminate_test.c + ${TX_REGRESSION_DIR}/threadx_queue_basic_eight_word_test.c + ${TX_REGRESSION_DIR}/threadx_queue_basic_four_word_test.c + ${TX_REGRESSION_DIR}/threadx_queue_basic_one_word_test.c + ${TX_REGRESSION_DIR}/threadx_queue_basic_sixteen_word_test.c + ${TX_REGRESSION_DIR}/threadx_queue_basic_two_word_test.c + ${TX_REGRESSION_DIR}/threadx_queue_basic_max_message_size_test.c + ${TX_REGRESSION_DIR}/threadx_queue_empty_suspension_test.c + ${TX_REGRESSION_DIR}/threadx_queue_flush_no_suspension_test.c + ${TX_REGRESSION_DIR}/threadx_queue_flush_test.c + ${TX_REGRESSION_DIR}/threadx_queue_front_send_test.c + ${TX_REGRESSION_DIR}/threadx_queue_full_suspension_test.c + ${TX_REGRESSION_DIR}/threadx_queue_information_test.c + ${TX_REGRESSION_DIR}/threadx_queue_prioritize.c + ${TX_REGRESSION_DIR}/threadx_queue_suspension_timeout_test.c + ${TX_REGRESSION_DIR}/threadx_queue_thread_terminate_test.c + ${TX_REGRESSION_DIR}/threadx_semaphore_basic_test.c + ${TX_REGRESSION_DIR}/threadx_semaphore_ceiling_put_test.c + ${TX_REGRESSION_DIR}/threadx_semaphore_delete_test.c + ${TX_REGRESSION_DIR}/threadx_semaphore_information_test.c + ${TX_REGRESSION_DIR}/threadx_semaphore_non_preemption_test.c + ${TX_REGRESSION_DIR}/threadx_semaphore_preemption_test.c + ${TX_REGRESSION_DIR}/threadx_semaphore_prioritize.c + ${TX_REGRESSION_DIR}/threadx_semaphore_thread_terminate_test.c + ${TX_REGRESSION_DIR}/threadx_semaphore_timeout_test.c + ${TX_REGRESSION_DIR}/threadx_smp_multiple_threads_one_core_test.c + ${TX_REGRESSION_DIR}/threadx_smp_non_trivial_scheduling_test.c + ${TX_REGRESSION_DIR}/threadx_smp_one_thread_dynamic_exclusion_test.c + ${TX_REGRESSION_DIR}/threadx_smp_preemption_threshold_test.c + ${TX_REGRESSION_DIR}/threadx_smp_random_resume_suspend_exclusion_pt_test.c + ${TX_REGRESSION_DIR}/threadx_smp_random_resume_suspend_exclusion_test.c + ${TX_REGRESSION_DIR}/threadx_smp_random_resume_suspend_test.c + ${TX_REGRESSION_DIR}/threadx_smp_rebalance_exclusion_test.c + ${TX_REGRESSION_DIR}/threadx_smp_relinquish_test.c + ${TX_REGRESSION_DIR}/threadx_smp_resume_suspend_accending_order_test.c + ${TX_REGRESSION_DIR}/threadx_smp_resume_suspend_decending_order_test.c + ${TX_REGRESSION_DIR}/threadx_smp_time_slice_test.c + ${TX_REGRESSION_DIR}/threadx_smp_two_threads_one_core_test.c + ${TX_REGRESSION_DIR}/threadx_thread_basic_execution_test.c + ${TX_REGRESSION_DIR}/threadx_thread_basic_time_slice_test.c + ${TX_REGRESSION_DIR}/threadx_thread_completed_test.c + ${TX_REGRESSION_DIR}/threadx_thread_create_preemption_threshold_test.c + ${TX_REGRESSION_DIR}/threadx_thread_delayed_suspension_test.c + ${TX_REGRESSION_DIR}/threadx_thread_information_test.c + ${TX_REGRESSION_DIR}/threadx_thread_multi_level_preemption_threshold_test.c + ${TX_REGRESSION_DIR}/threadx_thread_multiple_non_current_test.c + ${TX_REGRESSION_DIR}/threadx_thread_multiple_sleep_test.c + ${TX_REGRESSION_DIR}/threadx_thread_multiple_suspension_test.c + ${TX_REGRESSION_DIR}/threadx_thread_multiple_time_slice_test.c + ${TX_REGRESSION_DIR}/threadx_thread_preemptable_suspension_test.c + ${TX_REGRESSION_DIR}/threadx_thread_preemption_change_test.c + ${TX_REGRESSION_DIR}/threadx_thread_priority_change.c + ${TX_REGRESSION_DIR}/threadx_thread_relinquish_test.c + ${TX_REGRESSION_DIR}/threadx_thread_reset_test.c + ${TX_REGRESSION_DIR}/threadx_thread_simple_sleep_non_clear_test.c + ${TX_REGRESSION_DIR}/threadx_thread_simple_sleep_test.c + ${TX_REGRESSION_DIR}/threadx_thread_simple_suspend_test.c + ${TX_REGRESSION_DIR}/threadx_thread_sleep_for_100ticks_test.c + ${TX_REGRESSION_DIR}/threadx_thread_sleep_terminate_test.c + ${TX_REGRESSION_DIR}/threadx_thread_stack_checking_test.c + ${TX_REGRESSION_DIR}/threadx_thread_terminate_delete_test.c + ${TX_REGRESSION_DIR}/threadx_thread_time_slice_change_test.c + ${TX_REGRESSION_DIR}/threadx_thread_wait_abort_and_isr_test.c + ${TX_REGRESSION_DIR}/threadx_thread_wait_abort_test.c + ${TX_REGRESSION_DIR}/threadx_time_get_set_test.c + ${TX_REGRESSION_DIR}/threadx_timer_activate_deactivate_test.c + ${TX_REGRESSION_DIR}/threadx_timer_deactivate_accuracy_test.c + ${TX_REGRESSION_DIR}/threadx_timer_information_test.c + ${TX_REGRESSION_DIR}/threadx_timer_large_timer_accuracy_test.c + ${TX_REGRESSION_DIR}/threadx_timer_multiple_accuracy_test.c + ${TX_REGRESSION_DIR}/threadx_timer_multiple_test.c + ${TX_REGRESSION_DIR}/threadx_timer_simple_test.c + #${TX_REGRESSION_DIR}/threadx_trace_basic_test.c # requires windows specific port + ${TX_REGRESSION_DIR}/threadx_initialize_kernel_setup_test.c +) + +set(TX_REGRESSION_TARGETS) + +add_library( + threadx_smp_regression_support STATIC + ${TX_REGRESSION_DIR}/testcontrol.c + ${TX_CMAKE_DIR}/samples/fake.c +) +target_link_libraries(threadx_smp_regression_support PUBLIC threadx_smp c) +target_compile_definitions( + threadx_smp_regression_support + PUBLIC + TX_REGRESSION_TEST + TX_THREAD_SMP_ONLY_CORE_0_DEFAULT + TX_SMP_NOT_POSSIBLE + CTEST + BATCH_TEST + TEST_STACK_SIZE_PRINTF=4096 +) + +function(add_threadx_regression_test TEST_SOURCE) + get_filename_component(TEST_NAME ${TEST_SOURCE} NAME_WE) + set(TARGET_NAME smp_${TEST_NAME}) + set(CTEST_NAME smp_${TEST_NAME}) + + if(TEST_NAME STREQUAL "threadx_initialize_kernel_setup_test") + set(test_libraries threadx_smp) + else() + set(test_libraries threadx_smp_regression_support) + endif() + + setup_target( + ${TARGET_NAME} + PLATFORM_TARGET moonlight_platform_common_smp + LIBRARIES ${test_libraries} + SOURCES ${TEST_SOURCE} + ) + + + list(APPEND TX_REGRESSION_TARGETS ${TARGET_NAME}) + set(TX_REGRESSION_TARGETS ${TX_REGRESSION_TARGETS} PARENT_SCOPE) + + add_test( + NAME ${CTEST_NAME} + COMMAND ${THREADX_TEST_SIMULATOR} + --isa=${THREADX_TEST_ISA} + -f $ + -m 60s + ) + set_tests_properties(${CTEST_NAME} PROPERTIES TIMEOUT 10) +endfunction() + +foreach(test_case ${TX_REGRESSION_CASES}) + add_threadx_regression_test(${test_case}) +endforeach() + +add_custom_target( + threadx_smp_regression_build + DEPENDS ${TX_REGRESSION_TARGETS} +) diff --git a/test/threadx/generate_trap_file.sh b/test/threadx/generate_trap_file.sh index ad7d6e7..2e0f430 100755 --- a/test/threadx/generate_trap_file.sh +++ b/test/threadx/generate_trap_file.sh @@ -16,7 +16,7 @@ if [ -z "$line" ]; then exit 1 fi sed "${line}a\\ -extern void test_interrupt_dispatch(void);" "$src" > "$tmp1" +void test_interrupt_dispatch(void) __attribute__((weak));\nvoid test_interrupt_dispatch(void) {}" "$src" > "$tmp1" line=$(grep -n -F "$call_anchor" "$tmp1" | head -n 1 | cut -d: -f1) if [ -z "$line" ]; then diff --git a/third-party/threadx b/third-party/threadx index 13ec502..8cd9aed 160000 --- a/third-party/threadx +++ b/third-party/threadx @@ -1 +1 @@ -Subproject commit 13ec5027ee00ca2338c4c988d5a13f9ef037e91a +Subproject commit 8cd9aedbbb014b1837d893681155487f440f9aea -- 2.49.1 From c9c2d2009f52877cb57787b36019528a8f0f4f18 Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Wed, 25 Mar 2026 08:49:20 +0100 Subject: [PATCH 49/79] adds regression for smp --- CMakeLists.txt | 10 +- port/moonlight/CMakeLists.txt | 14 +- test/CMakeLists.txt | 115 ---------- test/CMakePresets.json | 90 -------- test/smp/CMakeLists.txt | 401 ++++++++++++++++++++------------- test/smp/CMakePresets.json | 72 ++++++ test/threadx/CMakeLists.txt | 103 ++++++++- test/threadx/CMakePresets.json | 72 ++++++ third-party/threadx | 2 +- 9 files changed, 515 insertions(+), 364 deletions(-) delete mode 100644 test/CMakeLists.txt delete mode 100644 test/CMakePresets.json create mode 100644 test/smp/CMakePresets.json create mode 100644 test/threadx/CMakePresets.json diff --git a/CMakeLists.txt b/CMakeLists.txt index c2f8382..80b54cb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,7 +40,7 @@ add_subdirectory(port/moonlight) function(setup_target TARGET) set(options) - set(oneValueArgs) #None for now + set(oneValueArgs PLATFORM_TARGET) set(multiValueArgs LIBRARIES SOURCES) cmake_parse_arguments(ST "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) if(ST_UNPARSED_ARGUMENTS) @@ -51,7 +51,11 @@ function(setup_target TARGET) set_target_properties(${TARGET} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR} ) - target_add_moonlight_platform(${TARGET}) + if(ST_PLATFORM_TARGET) + target_add_moonlight_platform(${TARGET} PLATFORM_TARGET ${ST_PLATFORM_TARGET}) + else() + target_add_moonlight_platform(${TARGET}) + endif() if("netxduo" IN_LIST ST_LIBRARIES) target_add_moonlight_network_driver(${TARGET}) @@ -83,4 +87,4 @@ endfunction() setup_target(thread_demo LIBRARIES threadx SOURCES src/thread_demo/main.c) setup_target(tcp_demo LIBRARIES threadx netxduo SOURCES src/tcp_demo/main.c) -setup_target(smp_demo LIBRARIES threadx_smp SOURCES src/thread_demo/main.c) +setup_target(smp_demo PLATFORM_TARGET moonlight_platform_common_smp LIBRARIES threadx_smp SOURCES src/thread_demo/main.c) diff --git a/port/moonlight/CMakeLists.txt b/port/moonlight/CMakeLists.txt index e790854..f56fa22 100644 --- a/port/moonlight/CMakeLists.txt +++ b/port/moonlight/CMakeLists.txt @@ -3,7 +3,7 @@ set(THREADX4TGFS_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../..) set(MOONLIGHT_SRC_DIR ${MOONLIGHT_ROOT}/src) set(MOONLIGHT_INC_DIR ${MOONLIGHT_ROOT}/inc) -if(NOT DEFINED MOONLIGHT_TRAP_SOURCE) +if(NOT DEFINED MOONLIGHT_TRAP_SOURCE) # Hook to use vectored traps, also used in regression testing set(MOONLIGHT_TRAP_SOURCE ${MOONLIGHT_SRC_DIR}/trap_non_vectored.c) endif() @@ -15,6 +15,7 @@ set(MOONLIGHT_PLATFORM_SOURCES ${MOONLIGHT_SRC_DIR}/exception.c ${MOONLIGHT_SRC_DIR}/vector_table.c) +# define a basic library containing all generic board components add_library(moonlight_platform_defaults INTERFACE) target_include_directories(moonlight_platform_defaults INTERFACE ${MOONLIGHT_INC_DIR} @@ -32,6 +33,7 @@ if(NX_DEBUG) NX_DEBUG_PACKET) endif() +# helper to tie in different threadx runtimes function(moonlight_define_platform TARGET THREADX_TARGET) add_library(${TARGET} OBJECT ${MOONLIGHT_PLATFORM_SOURCES}) @@ -41,7 +43,10 @@ function(moonlight_define_platform TARGET THREADX_TARGET) ${THREADX_TARGET}) endfunction() -moonlight_define_platform(moonlight_platform_common threadx) +if(TARGET threadx) + moonlight_define_platform(moonlight_platform_common threadx) +endif() + if(TARGET threadx_smp) moonlight_define_platform(moonlight_platform_common_smp threadx_smp) endif() @@ -56,6 +61,7 @@ if(DEFINED NETXDUO_CUSTOM_PORT) netxduo) endif() +# helper for consumers to inject moonlight as a dependency into a target function(target_add_moonlight_platform TARGET) set(options) set(oneValueArgs PLATFORM_TARGET) @@ -66,12 +72,16 @@ function(target_add_moonlight_platform TARGET) endif() if(NOT TAMP_PLATFORM_TARGET) + if(NOT TARGET moonlight_platform_common) + message(FATAL_ERROR "moonlight_platform_common is not available in this build; pass PLATFORM_TARGET explicitly") + endif() set(TAMP_PLATFORM_TARGET moonlight_platform_common) endif() target_link_libraries(${TARGET} PRIVATE ${TAMP_PLATFORM_TARGET}) endfunction() +# helper to inject the network driver into a target function(target_add_moonlight_network_driver TARGET) if(NOT TARGET moonlight_network_driver) message(FATAL_ERROR "moonlight_network_driver requires netxduo to be available in this build") diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt deleted file mode 100644 index 0c9d216..0000000 --- a/test/CMakeLists.txt +++ /dev/null @@ -1,115 +0,0 @@ -cmake_minimum_required(VERSION 3.21) - -set(BUILD_SHARED_LIBS OFF) -get_filename_component(THREADX4TGFS_ROOT "${CMAKE_CURRENT_LIST_DIR}/.." ABSOLUTE) - -if(NOT DEFINED CMAKE_TOOLCHAIN_FILE) - set(CMAKE_TOOLCHAIN_FILE - ${THREADX4TGFS_ROOT}/cmake/rv32imac_gnu.cmake - CACHE FILEPATH "Toolchain file") -endif() -include(${CMAKE_TOOLCHAIN_FILE}) - -set(__THREAD_LOCAL_STORAGE OFF) -add_subdirectory(${THREADX4TGFS_ROOT}/third-party/picolibc ${CMAKE_BINARY_DIR}/picolibc) -target_link_libraries(c PUBLIC gcc) - -set(THREADX_CUSTOM_PORT ${THREADX4TGFS_ROOT}/port/threadx) -add_subdirectory(${THREADX4TGFS_ROOT}/third-party/threadx ${CMAKE_BINARY_DIR}/threadx) -target_link_libraries(threadx PUBLIC c) -target_compile_definitions(threadx PUBLIC TX_REGRESSION_TEST) - -add_subdirectory(${THREADX4TGFS_ROOT}/port/threadx_smp ${CMAKE_BINARY_DIR}/port/threadx_smp) -target_link_libraries(threadx_smp PUBLIC c) -target_compile_definitions(threadx_smp PUBLIC TX_REGRESSION_TEST TX_THREAD_SMP_ONLY_CORE_0_DEFAULT TX_SMP_NOT_POSSIBLE) - -project(threadx_regression C ASM) -enable_testing() -set(TARGET_MEM "ram_dram" CACHE STRING "memory map to use") -set(CMAKE_EXECUTABLE_SUFFIX_C ".elf") - -set(THREADX_TEST_SIMULATOR - "" - CACHE FILEPATH "Path to the RISC-V VP executable used by CTest") -if(NOT THREADX_TEST_SIMULATOR) - message(FATAL_ERROR - "THREADX_TEST_SIMULATOR is not set. Configure with -DTHREADX_TEST_SIMULATOR=/path/to/riscv-vp") -endif() - -# Evaluate the variable from the toolchain file to decide which XLEN we are targetting (IMAC only!) -if(CMAKE_SYSTEM_PROCESSOR STREQUAL "risc-v32") - set(THREADX_TEST_ISA "rv32imac_m") -elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "risc-v64") - set(THREADX_TEST_ISA "rv64imac_m") -else() - message(FATAL_ERROR - "Unsupported CMAKE_SYSTEM_PROCESSOR for VP ISA selection: ${CMAKE_SYSTEM_PROCESSOR}") -endif() - -# The regression tests require the Timer ISR to call the function test_interrupt_dispatch(void) -# We patch the current trap handler and include it using MOONLIGHT_TRAP_SOURCE. -set(MOONLIGHT_TRAP_SOURCE_INPUT ${THREADX4TGFS_ROOT}/port/moonlight/src/trap_non_vectored.c) -set(MOONLIGHT_TRAP_SOURCE ${CMAKE_BINARY_DIR}/generated/trap_non_vectored.c) -add_custom_command( - OUTPUT ${MOONLIGHT_TRAP_SOURCE} - COMMAND bash ${CMAKE_CURRENT_LIST_DIR}/threadx/generate_trap_file.sh - ${MOONLIGHT_TRAP_SOURCE_INPUT} - ${MOONLIGHT_TRAP_SOURCE} - DEPENDS ${MOONLIGHT_TRAP_SOURCE_INPUT} - ${CMAKE_CURRENT_LIST_DIR}/threadx/generate_trap_file.sh - VERBATIM -) -add_custom_target(threadx_regression_generated_trap DEPENDS ${MOONLIGHT_TRAP_SOURCE}) -set_source_files_properties(${MOONLIGHT_TRAP_SOURCE} PROPERTIES GENERATED TRUE) - -add_subdirectory(${THREADX4TGFS_ROOT}/port/moonlight ${CMAKE_BINARY_DIR}/port/moonlight) -add_dependencies(moonlight_platform_common threadx_regression_generated_trap) -if(TARGET moonlight_platform_common_smp) - add_dependencies(moonlight_platform_common_smp threadx_regression_generated_trap) -endif() - -function(setup_target TARGET) - set(options) - set(oneValueArgs PLATFORM_TARGET) - set(multiValueArgs LIBRARIES SOURCES) - cmake_parse_arguments(ST "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - if(ST_UNPARSED_ARGUMENTS) - message(FATAL_ERROR "setup_target(${TARGET} ...): unknown args: ${ST_UNPARSED_ARGUMENTS}") - endif() - - add_executable(${TARGET}) - set_target_properties(${TARGET} PROPERTIES - RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR} - ) - if(ST_PLATFORM_TARGET) - target_add_moonlight_platform(${TARGET} PLATFORM_TARGET ${ST_PLATFORM_TARGET}) - else() - target_add_moonlight_platform(${TARGET}) - endif() - - if(ST_SOURCES) - target_sources(${TARGET} PRIVATE ${ST_SOURCES}) - endif() - - if(ST_LIBRARIES) - target_link_libraries(${TARGET} PRIVATE ${ST_LIBRARIES}) - endif() - - target_link_options(${TARGET} PRIVATE - -nostartfiles - -nostdlib - -T ${THREADX4TGFS_ROOT}/src/${TARGET_MEM}.lds - -Wl,--gc-sections - -Wl,-Map=${CMAKE_BINARY_DIR}/${TARGET}.map) - - add_custom_command(TARGET ${TARGET} POST_BUILD - COMMAND ${OBJCOPY} -O ihex $ ${CMAKE_BINARY_DIR}/${TARGET}.hex - COMMAND ${OBJCOPY} -O binary $ ${CMAKE_BINARY_DIR}/${TARGET}.bin - #COMMAND ${SIZE} $ - COMMAND ${OBJDUMP} -S $ > ${CMAKE_BINARY_DIR}/${TARGET}.dis - #COMMENT "Creating collateral for ${TARGET}" - ) -endfunction() - -add_subdirectory(threadx regression_targets) -add_subdirectory(smp smp_regression_targets) diff --git a/test/CMakePresets.json b/test/CMakePresets.json deleted file mode 100644 index 240564f..0000000 --- a/test/CMakePresets.json +++ /dev/null @@ -1,90 +0,0 @@ -{ - "version": 3, - "cmakeMinimumRequired": { - "major": 3, - "minor": 24, - "patch": 0 - }, - "configurePresets": [ - { - "name": "Debug", - "binaryDir": "${sourceDir}/../build/${presetName}/test", - "cacheVariables": { - "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/../cmake/rv64imac_gnu.cmake", - "CMAKE_BUILD_TYPE": "Debug", - "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" - } - }, - { - "name": "Debug32", - "binaryDir": "${sourceDir}/../build/${presetName}/test", - "cacheVariables": { - "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/../cmake/rv32imac_gnu.cmake", - "CMAKE_BUILD_TYPE": "Debug", - "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" - } - }, - { - "name": "Release", - "binaryDir": "${sourceDir}/../build/${presetName}/test", - "cacheVariables": { - "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/../cmake/rv64imac_gnu.cmake", - "CMAKE_BUILD_TYPE": "RelWithDebInfo", - "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" - } - }, - { - "name": "Release32", - "binaryDir": "${sourceDir}/../build/${presetName}/test", - "cacheVariables": { - "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/../cmake/rv32imac_gnu.cmake", - "CMAKE_BUILD_TYPE": "RelWithDebInfo", - "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" - } - }, - { - "name": "MinSizeRel", - "binaryDir": "${sourceDir}/../build/${presetName}/test", - "cacheVariables": { - "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/../cmake/rv64imac_gnu.cmake", - "CMAKE_BUILD_TYPE": "MinSizeRel", - "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" - } - }, - { - "name": "MinSizeRel32", - "binaryDir": "${sourceDir}/../build/${presetName}/test", - "cacheVariables": { - "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/../cmake/rv32imac_gnu.cmake", - "CMAKE_BUILD_TYPE": "MinSizeRel", - "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" - } - } - ], - "buildPresets": [ - { - "name": "Debug", - "configurePreset": "Debug" - }, - { - "name": "Debug32", - "configurePreset": "Debug32" - }, - { - "name": "Release", - "configurePreset": "Release" - }, - { - "name": "Release32", - "configurePreset": "Release32" - }, - { - "name": "MinSizeRel", - "configurePreset": "MinSizeRel" - }, - { - "name": "MinSizeRel32", - "configurePreset": "MinSizeRel32" - } - ] -} diff --git a/test/smp/CMakeLists.txt b/test/smp/CMakeLists.txt index 8cafa8a..73b9ace 100644 --- a/test/smp/CMakeLists.txt +++ b/test/smp/CMakeLists.txt @@ -1,178 +1,275 @@ +cmake_minimum_required(VERSION 3.21) + +set(BUILD_SHARED_LIBS OFF) +get_filename_component(THREADX4TGFS_ROOT "${CMAKE_CURRENT_LIST_DIR}/../.." ABSOLUTE) + +if(NOT DEFINED CMAKE_TOOLCHAIN_FILE) + set(CMAKE_TOOLCHAIN_FILE + ${THREADX4TGFS_ROOT}/cmake/rv32imac_gnu.cmake + CACHE FILEPATH "Toolchain file") +endif() +include(${CMAKE_TOOLCHAIN_FILE}) + +project(threadx_smp_regression LANGUAGES C ASM) +enable_testing() + +set(TARGET_MEM "ram_dram" CACHE STRING "memory map to use") +set(CMAKE_EXECUTABLE_SUFFIX_C ".elf") + +set(THREADX_TEST_SIMULATOR + "" + CACHE FILEPATH "Path to the RISC-V VP executable used by CTest") +if(NOT THREADX_TEST_SIMULATOR) + message(FATAL_ERROR + "THREADX_TEST_SIMULATOR is not set. Configure with -DTHREADX_TEST_SIMULATOR=/path/to/riscv-vp") +endif() + +if(CMAKE_SYSTEM_PROCESSOR STREQUAL "risc-v32") + set(THREADX_TEST_ISA "rv32imac_m") +elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "risc-v64") + set(THREADX_TEST_ISA "rv64imac_m") +else() + message(FATAL_ERROR + "Unsupported CMAKE_SYSTEM_PROCESSOR for VP ISA selection: ${CMAKE_SYSTEM_PROCESSOR}") +endif() + +set(__THREAD_LOCAL_STORAGE OFF) +add_subdirectory(${THREADX4TGFS_ROOT}/third-party/picolibc ${CMAKE_BINARY_DIR}/picolibc) +target_link_libraries(c PUBLIC gcc) + +add_subdirectory(${THREADX4TGFS_ROOT}/port/threadx_smp ${CMAKE_BINARY_DIR}/port/threadx_smp) +target_link_libraries(threadx_smp PUBLIC c) +target_compile_definitions(threadx_smp PUBLIC TX_REGRESSION_TEST TX_THREAD_SMP_ONLY_CORE_0_DEFAULT TX_SMP_NOT_POSSIBLE) + +set(MOONLIGHT_TRAP_SOURCE_INPUT ${THREADX4TGFS_ROOT}/port/moonlight/src/trap_non_vectored.c) +set(MOONLIGHT_TRAP_SOURCE ${CMAKE_BINARY_DIR}/generated/trap_non_vectored.c) +add_custom_command( + OUTPUT ${MOONLIGHT_TRAP_SOURCE} + COMMAND bash ${THREADX4TGFS_ROOT}/test/threadx/generate_trap_file.sh + ${MOONLIGHT_TRAP_SOURCE_INPUT} + ${MOONLIGHT_TRAP_SOURCE} + DEPENDS ${MOONLIGHT_TRAP_SOURCE_INPUT} + ${THREADX4TGFS_ROOT}/test/threadx/generate_trap_file.sh + VERBATIM +) +add_custom_target(threadx_regression_generated_trap DEPENDS ${MOONLIGHT_TRAP_SOURCE}) +set_source_files_properties(${MOONLIGHT_TRAP_SOURCE} PROPERTIES GENERATED TRUE) + +add_subdirectory(${THREADX4TGFS_ROOT}/port/moonlight ${CMAKE_BINARY_DIR}/port/moonlight) +add_dependencies(moonlight_platform_common_smp threadx_regression_generated_trap) + +function(setup_target TARGET) + set(options) + set(oneValueArgs PLATFORM_TARGET) + set(multiValueArgs LIBRARIES SOURCES) + cmake_parse_arguments(ST "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + if(ST_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "setup_target(${TARGET} ...): unknown args: ${ST_UNPARSED_ARGUMENTS}") + endif() + + add_executable(${TARGET}) + set_target_properties(${TARGET} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR} + ) + if(ST_PLATFORM_TARGET) + target_add_moonlight_platform(${TARGET} PLATFORM_TARGET ${ST_PLATFORM_TARGET}) + else() + target_add_moonlight_platform(${TARGET}) + endif() + + if(ST_SOURCES) + target_sources(${TARGET} PRIVATE ${ST_SOURCES}) + endif() + + if(ST_LIBRARIES) + target_link_libraries(${TARGET} PRIVATE ${ST_LIBRARIES}) + endif() + + target_link_options(${TARGET} PRIVATE + -nostartfiles + -nostdlib + -T ${THREADX4TGFS_ROOT}/src/${TARGET_MEM}.lds + -Wl,--gc-sections + -Wl,-Map=${CMAKE_BINARY_DIR}/${TARGET}.map) + + add_custom_command(TARGET ${TARGET} POST_BUILD + COMMAND ${OBJCOPY} -O ihex $ ${CMAKE_BINARY_DIR}/${TARGET}.hex + COMMAND ${OBJCOPY} -O binary $ ${CMAKE_BINARY_DIR}/${TARGET}.bin + COMMAND ${OBJDUMP} -S $ > ${CMAKE_BINARY_DIR}/${TARGET}.dis + ) +endfunction() + set(TX_REGRESSION_DIR - ${THREADX4TGFS_ROOT}/third-party/threadx/test/smp/regression + ${THREADX4TGFS_ROOT}/third-party/threadx/test/smp/regression ) set(TX_CMAKE_DIR - ${THREADX4TGFS_ROOT}/third-party/threadx/test/tx/cmake + ${THREADX4TGFS_ROOT}/third-party/threadx/test/tx/cmake ) set(TX_REGRESSION_CASES - ${TX_REGRESSION_DIR}/threadx_block_memory_basic_test.c - ${TX_REGRESSION_DIR}/threadx_block_memory_error_detection_test.c - ${TX_REGRESSION_DIR}/threadx_block_memory_information_test.c - ${TX_REGRESSION_DIR}/threadx_block_memory_prioritize_test.c - ${TX_REGRESSION_DIR}/threadx_block_memory_suspension_test.c - ${TX_REGRESSION_DIR}/threadx_block_memory_suspension_timeout_test.c - ${TX_REGRESSION_DIR}/threadx_block_memory_thread_terminate_test.c - ${TX_REGRESSION_DIR}/threadx_byte_memory_basic_test.c - ${TX_REGRESSION_DIR}/threadx_byte_memory_information_test.c - ${TX_REGRESSION_DIR}/threadx_byte_memory_prioritize_test.c - ${TX_REGRESSION_DIR}/threadx_byte_memory_suspension_test.c - ${TX_REGRESSION_DIR}/threadx_byte_memory_suspension_timeout_test.c - ${TX_REGRESSION_DIR}/threadx_byte_memory_thread_contention_test.c - ${TX_REGRESSION_DIR}/threadx_byte_memory_thread_terminate_test.c - ${TX_REGRESSION_DIR}/threadx_event_flag_basic_test.c - ${TX_REGRESSION_DIR}/threadx_event_flag_information_test.c - ${TX_REGRESSION_DIR}/threadx_event_flag_isr_set_clear_test.c - ${TX_REGRESSION_DIR}/threadx_event_flag_isr_wait_abort_test.c - ${TX_REGRESSION_DIR}/threadx_event_flag_single_thread_terminate_test.c - ${TX_REGRESSION_DIR}/threadx_event_flag_suspension_consume_test.c - ${TX_REGRESSION_DIR}/threadx_event_flag_suspension_different_bits_consume_test.c - ${TX_REGRESSION_DIR}/threadx_event_flag_suspension_different_bits_test.c - ${TX_REGRESSION_DIR}/threadx_event_flag_suspension_test.c - ${TX_REGRESSION_DIR}/threadx_event_flag_suspension_timeout_test.c - ${TX_REGRESSION_DIR}/threadx_event_flag_thread_terminate_test.c - ${TX_REGRESSION_DIR}/threadx_interrupt_control_test.c - ${TX_REGRESSION_DIR}/threadx_mutex_basic_test.c - ${TX_REGRESSION_DIR}/threadx_mutex_delete_test.c - ${TX_REGRESSION_DIR}/threadx_mutex_information_test.c - ${TX_REGRESSION_DIR}/threadx_mutex_nested_priority_inheritance_test.c - ${TX_REGRESSION_DIR}/threadx_mutex_no_preemption_test.c - ${TX_REGRESSION_DIR}/threadx_mutex_preemption_test.c - ${TX_REGRESSION_DIR}/threadx_mutex_priority_inheritance_test.c - ${TX_REGRESSION_DIR}/threadx_mutex_proritize_test.c - ${TX_REGRESSION_DIR}/threadx_mutex_suspension_timeout_test.c - ${TX_REGRESSION_DIR}/threadx_mutex_thread_terminate_test.c - ${TX_REGRESSION_DIR}/threadx_queue_basic_eight_word_test.c - ${TX_REGRESSION_DIR}/threadx_queue_basic_four_word_test.c - ${TX_REGRESSION_DIR}/threadx_queue_basic_one_word_test.c - ${TX_REGRESSION_DIR}/threadx_queue_basic_sixteen_word_test.c - ${TX_REGRESSION_DIR}/threadx_queue_basic_two_word_test.c - ${TX_REGRESSION_DIR}/threadx_queue_basic_max_message_size_test.c - ${TX_REGRESSION_DIR}/threadx_queue_empty_suspension_test.c - ${TX_REGRESSION_DIR}/threadx_queue_flush_no_suspension_test.c - ${TX_REGRESSION_DIR}/threadx_queue_flush_test.c - ${TX_REGRESSION_DIR}/threadx_queue_front_send_test.c - ${TX_REGRESSION_DIR}/threadx_queue_full_suspension_test.c - ${TX_REGRESSION_DIR}/threadx_queue_information_test.c - ${TX_REGRESSION_DIR}/threadx_queue_prioritize.c - ${TX_REGRESSION_DIR}/threadx_queue_suspension_timeout_test.c - ${TX_REGRESSION_DIR}/threadx_queue_thread_terminate_test.c - ${TX_REGRESSION_DIR}/threadx_semaphore_basic_test.c - ${TX_REGRESSION_DIR}/threadx_semaphore_ceiling_put_test.c - ${TX_REGRESSION_DIR}/threadx_semaphore_delete_test.c - ${TX_REGRESSION_DIR}/threadx_semaphore_information_test.c - ${TX_REGRESSION_DIR}/threadx_semaphore_non_preemption_test.c - ${TX_REGRESSION_DIR}/threadx_semaphore_preemption_test.c - ${TX_REGRESSION_DIR}/threadx_semaphore_prioritize.c - ${TX_REGRESSION_DIR}/threadx_semaphore_thread_terminate_test.c - ${TX_REGRESSION_DIR}/threadx_semaphore_timeout_test.c - ${TX_REGRESSION_DIR}/threadx_smp_multiple_threads_one_core_test.c - ${TX_REGRESSION_DIR}/threadx_smp_non_trivial_scheduling_test.c - ${TX_REGRESSION_DIR}/threadx_smp_one_thread_dynamic_exclusion_test.c - ${TX_REGRESSION_DIR}/threadx_smp_preemption_threshold_test.c - ${TX_REGRESSION_DIR}/threadx_smp_random_resume_suspend_exclusion_pt_test.c - ${TX_REGRESSION_DIR}/threadx_smp_random_resume_suspend_exclusion_test.c - ${TX_REGRESSION_DIR}/threadx_smp_random_resume_suspend_test.c - ${TX_REGRESSION_DIR}/threadx_smp_rebalance_exclusion_test.c - ${TX_REGRESSION_DIR}/threadx_smp_relinquish_test.c - ${TX_REGRESSION_DIR}/threadx_smp_resume_suspend_accending_order_test.c - ${TX_REGRESSION_DIR}/threadx_smp_resume_suspend_decending_order_test.c - ${TX_REGRESSION_DIR}/threadx_smp_time_slice_test.c - ${TX_REGRESSION_DIR}/threadx_smp_two_threads_one_core_test.c - ${TX_REGRESSION_DIR}/threadx_thread_basic_execution_test.c - ${TX_REGRESSION_DIR}/threadx_thread_basic_time_slice_test.c - ${TX_REGRESSION_DIR}/threadx_thread_completed_test.c - ${TX_REGRESSION_DIR}/threadx_thread_create_preemption_threshold_test.c - ${TX_REGRESSION_DIR}/threadx_thread_delayed_suspension_test.c - ${TX_REGRESSION_DIR}/threadx_thread_information_test.c - ${TX_REGRESSION_DIR}/threadx_thread_multi_level_preemption_threshold_test.c - ${TX_REGRESSION_DIR}/threadx_thread_multiple_non_current_test.c - ${TX_REGRESSION_DIR}/threadx_thread_multiple_sleep_test.c - ${TX_REGRESSION_DIR}/threadx_thread_multiple_suspension_test.c - ${TX_REGRESSION_DIR}/threadx_thread_multiple_time_slice_test.c - ${TX_REGRESSION_DIR}/threadx_thread_preemptable_suspension_test.c - ${TX_REGRESSION_DIR}/threadx_thread_preemption_change_test.c - ${TX_REGRESSION_DIR}/threadx_thread_priority_change.c - ${TX_REGRESSION_DIR}/threadx_thread_relinquish_test.c - ${TX_REGRESSION_DIR}/threadx_thread_reset_test.c - ${TX_REGRESSION_DIR}/threadx_thread_simple_sleep_non_clear_test.c - ${TX_REGRESSION_DIR}/threadx_thread_simple_sleep_test.c - ${TX_REGRESSION_DIR}/threadx_thread_simple_suspend_test.c - ${TX_REGRESSION_DIR}/threadx_thread_sleep_for_100ticks_test.c - ${TX_REGRESSION_DIR}/threadx_thread_sleep_terminate_test.c - ${TX_REGRESSION_DIR}/threadx_thread_stack_checking_test.c - ${TX_REGRESSION_DIR}/threadx_thread_terminate_delete_test.c - ${TX_REGRESSION_DIR}/threadx_thread_time_slice_change_test.c - ${TX_REGRESSION_DIR}/threadx_thread_wait_abort_and_isr_test.c - ${TX_REGRESSION_DIR}/threadx_thread_wait_abort_test.c - ${TX_REGRESSION_DIR}/threadx_time_get_set_test.c - ${TX_REGRESSION_DIR}/threadx_timer_activate_deactivate_test.c - ${TX_REGRESSION_DIR}/threadx_timer_deactivate_accuracy_test.c - ${TX_REGRESSION_DIR}/threadx_timer_information_test.c - ${TX_REGRESSION_DIR}/threadx_timer_large_timer_accuracy_test.c - ${TX_REGRESSION_DIR}/threadx_timer_multiple_accuracy_test.c - ${TX_REGRESSION_DIR}/threadx_timer_multiple_test.c - ${TX_REGRESSION_DIR}/threadx_timer_simple_test.c - #${TX_REGRESSION_DIR}/threadx_trace_basic_test.c # requires windows specific port - ${TX_REGRESSION_DIR}/threadx_initialize_kernel_setup_test.c + ${TX_REGRESSION_DIR}/threadx_block_memory_basic_test.c + ${TX_REGRESSION_DIR}/threadx_block_memory_error_detection_test.c + ${TX_REGRESSION_DIR}/threadx_block_memory_information_test.c + ${TX_REGRESSION_DIR}/threadx_block_memory_prioritize_test.c + ${TX_REGRESSION_DIR}/threadx_block_memory_suspension_test.c + ${TX_REGRESSION_DIR}/threadx_block_memory_suspension_timeout_test.c + ${TX_REGRESSION_DIR}/threadx_block_memory_thread_terminate_test.c + ${TX_REGRESSION_DIR}/threadx_byte_memory_basic_test.c + ${TX_REGRESSION_DIR}/threadx_byte_memory_information_test.c + ${TX_REGRESSION_DIR}/threadx_byte_memory_prioritize_test.c + ${TX_REGRESSION_DIR}/threadx_byte_memory_suspension_test.c + ${TX_REGRESSION_DIR}/threadx_byte_memory_suspension_timeout_test.c + ${TX_REGRESSION_DIR}/threadx_byte_memory_thread_contention_test.c + ${TX_REGRESSION_DIR}/threadx_byte_memory_thread_terminate_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_basic_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_information_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_isr_set_clear_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_isr_wait_abort_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_single_thread_terminate_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_suspension_consume_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_suspension_different_bits_consume_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_suspension_different_bits_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_suspension_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_suspension_timeout_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_thread_terminate_test.c + ${TX_REGRESSION_DIR}/threadx_initialize_kernel_setup_test.c + ${TX_REGRESSION_DIR}/threadx_interrupt_control_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_basic_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_delete_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_information_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_nested_priority_inheritance_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_no_preemption_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_preemption_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_priority_inheritance_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_proritize_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_suspension_timeout_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_thread_terminate_test.c + ${TX_REGRESSION_DIR}/threadx_queue_basic_eight_word_test.c + ${TX_REGRESSION_DIR}/threadx_queue_basic_four_word_test.c + ${TX_REGRESSION_DIR}/threadx_queue_basic_max_message_size_test.c + ${TX_REGRESSION_DIR}/threadx_queue_basic_one_word_test.c + ${TX_REGRESSION_DIR}/threadx_queue_basic_sixteen_word_test.c + ${TX_REGRESSION_DIR}/threadx_queue_basic_two_word_test.c + ${TX_REGRESSION_DIR}/threadx_queue_empty_suspension_test.c + ${TX_REGRESSION_DIR}/threadx_queue_flush_no_suspension_test.c + ${TX_REGRESSION_DIR}/threadx_queue_flush_test.c + ${TX_REGRESSION_DIR}/threadx_queue_front_send_test.c + ${TX_REGRESSION_DIR}/threadx_queue_full_suspension_test.c + ${TX_REGRESSION_DIR}/threadx_queue_information_test.c + ${TX_REGRESSION_DIR}/threadx_queue_prioritize.c + ${TX_REGRESSION_DIR}/threadx_queue_suspension_timeout_test.c + ${TX_REGRESSION_DIR}/threadx_queue_thread_terminate_test.c + ${TX_REGRESSION_DIR}/threadx_semaphore_basic_test.c + ${TX_REGRESSION_DIR}/threadx_semaphore_ceiling_put_test.c + ${TX_REGRESSION_DIR}/threadx_semaphore_delete_test.c + ${TX_REGRESSION_DIR}/threadx_semaphore_information_test.c + ${TX_REGRESSION_DIR}/threadx_semaphore_non_preemption_test.c + ${TX_REGRESSION_DIR}/threadx_semaphore_preemption_test.c + ${TX_REGRESSION_DIR}/threadx_semaphore_prioritize.c + ${TX_REGRESSION_DIR}/threadx_semaphore_thread_terminate_test.c + ${TX_REGRESSION_DIR}/threadx_semaphore_timeout_test.c + ${TX_REGRESSION_DIR}/threadx_smp_multiple_threads_one_core_test.c + ${TX_REGRESSION_DIR}/threadx_smp_non_trivial_scheduling_test.c + ${TX_REGRESSION_DIR}/threadx_smp_one_thread_dynamic_exclusion_test.c + ${TX_REGRESSION_DIR}/threadx_smp_preemption_threshold_test.c + ${TX_REGRESSION_DIR}/threadx_smp_random_resume_suspend_exclusion_pt_test.c + ${TX_REGRESSION_DIR}/threadx_smp_random_resume_suspend_exclusion_test.c + ${TX_REGRESSION_DIR}/threadx_smp_random_resume_suspend_test.c + ${TX_REGRESSION_DIR}/threadx_smp_rebalance_exclusion_test.c + ${TX_REGRESSION_DIR}/threadx_smp_relinquish_test.c + ${TX_REGRESSION_DIR}/threadx_smp_resume_suspend_accending_order_test.c + ${TX_REGRESSION_DIR}/threadx_smp_resume_suspend_decending_order_test.c + ${TX_REGRESSION_DIR}/threadx_smp_time_slice_test.c + ${TX_REGRESSION_DIR}/threadx_smp_two_threads_one_core_test.c + ${TX_REGRESSION_DIR}/threadx_thread_basic_execution_test.c + ${TX_REGRESSION_DIR}/threadx_thread_basic_time_slice_test.c + ${TX_REGRESSION_DIR}/threadx_thread_completed_test.c + ${TX_REGRESSION_DIR}/threadx_thread_create_preemption_threshold_test.c + ${TX_REGRESSION_DIR}/threadx_thread_delayed_suspension_test.c + ${TX_REGRESSION_DIR}/threadx_thread_information_test.c + ${TX_REGRESSION_DIR}/threadx_thread_multi_level_preemption_threshold_test.c + ${TX_REGRESSION_DIR}/threadx_thread_multiple_non_current_test.c + ${TX_REGRESSION_DIR}/threadx_thread_multiple_sleep_test.c + ${TX_REGRESSION_DIR}/threadx_thread_multiple_suspension_test.c + ${TX_REGRESSION_DIR}/threadx_thread_multiple_time_slice_test.c + ${TX_REGRESSION_DIR}/threadx_thread_preemptable_suspension_test.c + ${TX_REGRESSION_DIR}/threadx_thread_preemption_change_test.c + ${TX_REGRESSION_DIR}/threadx_thread_priority_change.c + ${TX_REGRESSION_DIR}/threadx_thread_relinquish_test.c + ${TX_REGRESSION_DIR}/threadx_thread_reset_test.c + ${TX_REGRESSION_DIR}/threadx_thread_simple_sleep_non_clear_test.c + ${TX_REGRESSION_DIR}/threadx_thread_simple_sleep_test.c + ${TX_REGRESSION_DIR}/threadx_thread_simple_suspend_test.c + ${TX_REGRESSION_DIR}/threadx_thread_sleep_for_100ticks_test.c + ${TX_REGRESSION_DIR}/threadx_thread_sleep_terminate_test.c + ${TX_REGRESSION_DIR}/threadx_thread_stack_checking_test.c + ${TX_REGRESSION_DIR}/threadx_thread_terminate_delete_test.c + ${TX_REGRESSION_DIR}/threadx_thread_time_slice_change_test.c + ${TX_REGRESSION_DIR}/threadx_thread_wait_abort_and_isr_test.c + ${TX_REGRESSION_DIR}/threadx_thread_wait_abort_test.c + ${TX_REGRESSION_DIR}/threadx_time_get_set_test.c + ${TX_REGRESSION_DIR}/threadx_timer_activate_deactivate_test.c + ${TX_REGRESSION_DIR}/threadx_timer_deactivate_accuracy_test.c + ${TX_REGRESSION_DIR}/threadx_timer_information_test.c + ${TX_REGRESSION_DIR}/threadx_timer_large_timer_accuracy_test.c + ${TX_REGRESSION_DIR}/threadx_timer_multiple_accuracy_test.c + ${TX_REGRESSION_DIR}/threadx_timer_multiple_test.c + ${TX_REGRESSION_DIR}/threadx_timer_simple_test.c + #${TX_REGRESSION_DIR}/threadx_trace_basic_test.c #windows specifics in the test ) -set(TX_REGRESSION_TARGETS) - add_library( - threadx_smp_regression_support STATIC - ${TX_REGRESSION_DIR}/testcontrol.c - ${TX_CMAKE_DIR}/samples/fake.c + threadx_smp_regression_support STATIC + ${TX_REGRESSION_DIR}/testcontrol.c + ${TX_CMAKE_DIR}/samples/fake.c ) target_link_libraries(threadx_smp_regression_support PUBLIC threadx_smp c) target_compile_definitions( - threadx_smp_regression_support - PUBLIC - TX_REGRESSION_TEST - TX_THREAD_SMP_ONLY_CORE_0_DEFAULT - TX_SMP_NOT_POSSIBLE - CTEST - BATCH_TEST - TEST_STACK_SIZE_PRINTF=4096 + threadx_smp_regression_support + PUBLIC + TX_REGRESSION_TEST + TX_THREAD_SMP_ONLY_CORE_0_DEFAULT + TX_SMP_NOT_POSSIBLE + CTEST + BATCH_TEST + TEST_STACK_SIZE_PRINTF=4096 ) function(add_threadx_regression_test TEST_SOURCE) - get_filename_component(TEST_NAME ${TEST_SOURCE} NAME_WE) - set(TARGET_NAME smp_${TEST_NAME}) - set(CTEST_NAME smp_${TEST_NAME}) + get_filename_component(TEST_NAME ${TEST_SOURCE} NAME_WE) - if(TEST_NAME STREQUAL "threadx_initialize_kernel_setup_test") - set(test_libraries threadx_smp) - else() - set(test_libraries threadx_smp_regression_support) - endif() + if(TEST_NAME STREQUAL "threadx_initialize_kernel_setup_test") + set(test_libraries threadx_smp) + else() + set(test_libraries threadx_smp_regression_support) + endif() - setup_target( - ${TARGET_NAME} - PLATFORM_TARGET moonlight_platform_common_smp - LIBRARIES ${test_libraries} - SOURCES ${TEST_SOURCE} - ) + setup_target( + ${TEST_NAME} + PLATFORM_TARGET moonlight_platform_common_smp + LIBRARIES ${test_libraries} + SOURCES ${TEST_SOURCE} + ) + list(APPEND TX_REGRESSION_TARGETS ${TEST_NAME}) + set(TX_REGRESSION_TARGETS ${TX_REGRESSION_TARGETS} PARENT_SCOPE) - list(APPEND TX_REGRESSION_TARGETS ${TARGET_NAME}) - set(TX_REGRESSION_TARGETS ${TX_REGRESSION_TARGETS} PARENT_SCOPE) - - add_test( - NAME ${CTEST_NAME} - COMMAND ${THREADX_TEST_SIMULATOR} - --isa=${THREADX_TEST_ISA} - -f $ - -m 60s - ) - set_tests_properties(${CTEST_NAME} PROPERTIES TIMEOUT 10) + add_test( + NAME ${TEST_NAME} + COMMAND ${THREADX_TEST_SIMULATOR} + --isa=${THREADX_TEST_ISA} + -f $ + -m 60s + -p tb.top.num_cores=4 + ) + set_tests_properties(${TEST_NAME} PROPERTIES TIMEOUT 10) endfunction() foreach(test_case ${TX_REGRESSION_CASES}) - add_threadx_regression_test(${test_case}) + add_threadx_regression_test(${test_case}) endforeach() add_custom_target( - threadx_smp_regression_build - DEPENDS ${TX_REGRESSION_TARGETS} + threadx_smp_regression_build + DEPENDS ${TX_REGRESSION_TARGETS} ) diff --git a/test/smp/CMakePresets.json b/test/smp/CMakePresets.json new file mode 100644 index 0000000..4ecfd90 --- /dev/null +++ b/test/smp/CMakePresets.json @@ -0,0 +1,72 @@ +{ + "version": 3, + "cmakeMinimumRequired": { + "major": 3, + "minor": 24, + "patch": 0 + }, + "configurePresets": [ + { + "name": "Debug", + "binaryDir": "${sourceDir}/../../build/${presetName}/test/smp", + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/../../cmake/rv64imac_gnu.cmake", + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" + } + }, + { + "name": "Debug32", + "binaryDir": "${sourceDir}/../../build/${presetName}/test/smp", + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/../../cmake/rv32imac_gnu.cmake", + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" + } + }, + { + "name": "Release", + "binaryDir": "${sourceDir}/../../build/${presetName}/test/smp", + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/../../cmake/rv64imac_gnu.cmake", + "CMAKE_BUILD_TYPE": "RelWithDebInfo", + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" + } + }, + { + "name": "Release32", + "binaryDir": "${sourceDir}/../../build/${presetName}/test/smp", + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/../../cmake/rv32imac_gnu.cmake", + "CMAKE_BUILD_TYPE": "RelWithDebInfo", + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" + } + }, + { + "name": "MinSizeRel", + "binaryDir": "${sourceDir}/../../build/${presetName}/test/smp", + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/../../cmake/rv64imac_gnu.cmake", + "CMAKE_BUILD_TYPE": "MinSizeRel", + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" + } + }, + { + "name": "MinSizeRel32", + "binaryDir": "${sourceDir}/../../build/${presetName}/test/smp", + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/../../cmake/rv32imac_gnu.cmake", + "CMAKE_BUILD_TYPE": "MinSizeRel", + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" + } + } + ], + "buildPresets": [ + { "name": "Debug", "configurePreset": "Debug" }, + { "name": "Debug32", "configurePreset": "Debug32" }, + { "name": "Release", "configurePreset": "Release" }, + { "name": "Release32", "configurePreset": "Release32" }, + { "name": "MinSizeRel", "configurePreset": "MinSizeRel" }, + { "name": "MinSizeRel32", "configurePreset": "MinSizeRel32" } + ] +} diff --git a/test/threadx/CMakeLists.txt b/test/threadx/CMakeLists.txt index 2eb06c7..0404a00 100644 --- a/test/threadx/CMakeLists.txt +++ b/test/threadx/CMakeLists.txt @@ -1,3 +1,105 @@ +cmake_minimum_required(VERSION 3.21) + +set(BUILD_SHARED_LIBS OFF) +get_filename_component(THREADX4TGFS_ROOT "${CMAKE_CURRENT_LIST_DIR}/../.." ABSOLUTE) + +if(NOT DEFINED CMAKE_TOOLCHAIN_FILE) + set(CMAKE_TOOLCHAIN_FILE + ${THREADX4TGFS_ROOT}/cmake/rv32imac_gnu.cmake + CACHE FILEPATH "Toolchain file") +endif() +include(${CMAKE_TOOLCHAIN_FILE}) + +project(threadx_regression LANGUAGES C ASM) +enable_testing() + +set(TARGET_MEM "ram_dram" CACHE STRING "memory map to use") +set(CMAKE_EXECUTABLE_SUFFIX_C ".elf") + +set(THREADX_TEST_SIMULATOR + "" + CACHE FILEPATH "Path to the RISC-V VP executable used by CTest") +if(NOT THREADX_TEST_SIMULATOR) + message(FATAL_ERROR + "THREADX_TEST_SIMULATOR is not set. Configure with -DTHREADX_TEST_SIMULATOR=/path/to/riscv-vp") +endif() + +if(CMAKE_SYSTEM_PROCESSOR STREQUAL "risc-v32") + set(THREADX_TEST_ISA "rv32imac_m") +elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "risc-v64") + set(THREADX_TEST_ISA "rv64imac_m") +else() + message(FATAL_ERROR + "Unsupported CMAKE_SYSTEM_PROCESSOR for VP ISA selection: ${CMAKE_SYSTEM_PROCESSOR}") +endif() + +set(__THREAD_LOCAL_STORAGE OFF) +add_subdirectory(${THREADX4TGFS_ROOT}/third-party/picolibc ${CMAKE_BINARY_DIR}/picolibc) +target_link_libraries(c PUBLIC gcc) + +set(THREADX_CUSTOM_PORT ${THREADX4TGFS_ROOT}/port/threadx) +add_subdirectory(${THREADX4TGFS_ROOT}/third-party/threadx ${CMAKE_BINARY_DIR}/threadx) +target_link_libraries(threadx PUBLIC c) +target_compile_definitions(threadx PUBLIC TX_REGRESSION_TEST) + +set(MOONLIGHT_TRAP_SOURCE_INPUT ${THREADX4TGFS_ROOT}/port/moonlight/src/trap_non_vectored.c) +set(MOONLIGHT_TRAP_SOURCE ${CMAKE_BINARY_DIR}/generated/trap_non_vectored.c) +add_custom_command( + OUTPUT ${MOONLIGHT_TRAP_SOURCE} + COMMAND bash ${CMAKE_CURRENT_LIST_DIR}/generate_trap_file.sh + ${MOONLIGHT_TRAP_SOURCE_INPUT} + ${MOONLIGHT_TRAP_SOURCE} + DEPENDS ${MOONLIGHT_TRAP_SOURCE_INPUT} + ${CMAKE_CURRENT_LIST_DIR}/generate_trap_file.sh + VERBATIM +) +add_custom_target(threadx_regression_generated_trap DEPENDS ${MOONLIGHT_TRAP_SOURCE}) +set_source_files_properties(${MOONLIGHT_TRAP_SOURCE} PROPERTIES GENERATED TRUE) + +add_subdirectory(${THREADX4TGFS_ROOT}/port/moonlight ${CMAKE_BINARY_DIR}/port/moonlight) +add_dependencies(moonlight_platform_common threadx_regression_generated_trap) + +function(setup_target TARGET) + set(options) + set(oneValueArgs PLATFORM_TARGET) + set(multiValueArgs LIBRARIES SOURCES) + cmake_parse_arguments(ST "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + if(ST_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "setup_target(${TARGET} ...): unknown args: ${ST_UNPARSED_ARGUMENTS}") + endif() + + add_executable(${TARGET}) + set_target_properties(${TARGET} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR} + ) + if(ST_PLATFORM_TARGET) + target_add_moonlight_platform(${TARGET} PLATFORM_TARGET ${ST_PLATFORM_TARGET}) + else() + target_add_moonlight_platform(${TARGET}) + endif() + + if(ST_SOURCES) + target_sources(${TARGET} PRIVATE ${ST_SOURCES}) + endif() + + if(ST_LIBRARIES) + target_link_libraries(${TARGET} PRIVATE ${ST_LIBRARIES}) + endif() + + target_link_options(${TARGET} PRIVATE + -nostartfiles + -nostdlib + -T ${THREADX4TGFS_ROOT}/src/${TARGET_MEM}.lds + -Wl,--gc-sections + -Wl,-Map=${CMAKE_BINARY_DIR}/${TARGET}.map) + + add_custom_command(TARGET ${TARGET} POST_BUILD + COMMAND ${OBJCOPY} -O ihex $ ${CMAKE_BINARY_DIR}/${TARGET}.hex + COMMAND ${OBJCOPY} -O binary $ ${CMAKE_BINARY_DIR}/${TARGET}.bin + COMMAND ${OBJDUMP} -S $ > ${CMAKE_BINARY_DIR}/${TARGET}.dis + ) +endfunction() + set(TX_REGRESSION_DIR ${THREADX4TGFS_ROOT}/third-party/threadx/test/tx/regression ) @@ -136,7 +238,6 @@ function(add_threadx_regression_test TEST_SOURCE) SOURCES ${TEST_SOURCE} ) - list(APPEND TX_REGRESSION_TARGETS ${TEST_NAME}) set(TX_REGRESSION_TARGETS ${TX_REGRESSION_TARGETS} PARENT_SCOPE) diff --git a/test/threadx/CMakePresets.json b/test/threadx/CMakePresets.json new file mode 100644 index 0000000..afbc481 --- /dev/null +++ b/test/threadx/CMakePresets.json @@ -0,0 +1,72 @@ +{ + "version": 3, + "cmakeMinimumRequired": { + "major": 3, + "minor": 24, + "patch": 0 + }, + "configurePresets": [ + { + "name": "Debug", + "binaryDir": "${sourceDir}/../../build/${presetName}/test/threadx", + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/../../cmake/rv64imac_gnu.cmake", + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" + } + }, + { + "name": "Debug32", + "binaryDir": "${sourceDir}/../../build/${presetName}/test/threadx", + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/../../cmake/rv32imac_gnu.cmake", + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" + } + }, + { + "name": "Release", + "binaryDir": "${sourceDir}/../../build/${presetName}/test/threadx", + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/../../cmake/rv64imac_gnu.cmake", + "CMAKE_BUILD_TYPE": "RelWithDebInfo", + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" + } + }, + { + "name": "Release32", + "binaryDir": "${sourceDir}/../../build/${presetName}/test/threadx", + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/../../cmake/rv32imac_gnu.cmake", + "CMAKE_BUILD_TYPE": "RelWithDebInfo", + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" + } + }, + { + "name": "MinSizeRel", + "binaryDir": "${sourceDir}/../../build/${presetName}/test/threadx", + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/../../cmake/rv64imac_gnu.cmake", + "CMAKE_BUILD_TYPE": "MinSizeRel", + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" + } + }, + { + "name": "MinSizeRel32", + "binaryDir": "${sourceDir}/../../build/${presetName}/test/threadx", + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/../../cmake/rv32imac_gnu.cmake", + "CMAKE_BUILD_TYPE": "MinSizeRel", + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" + } + } + ], + "buildPresets": [ + { "name": "Debug", "configurePreset": "Debug" }, + { "name": "Debug32", "configurePreset": "Debug32" }, + { "name": "Release", "configurePreset": "Release" }, + { "name": "Release32", "configurePreset": "Release32" }, + { "name": "MinSizeRel", "configurePreset": "MinSizeRel" }, + { "name": "MinSizeRel32", "configurePreset": "MinSizeRel32" } + ] +} diff --git a/third-party/threadx b/third-party/threadx index 8cd9aed..cfbccc8 160000 --- a/third-party/threadx +++ b/third-party/threadx @@ -1 +1 @@ -Subproject commit 8cd9aedbbb014b1837d893681155487f440f9aea +Subproject commit cfbccc838ea15d4d359e48965a1bf8bc3f841d49 -- 2.49.1 From 1aa9944581203ae936cb2ee124fc453589cb1b26 Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Wed, 25 Mar 2026 09:27:44 +0100 Subject: [PATCH 50/79] corrects timeout for smp ctest --- test/smp/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/smp/CMakeLists.txt b/test/smp/CMakeLists.txt index 73b9ace..fe543e0 100644 --- a/test/smp/CMakeLists.txt +++ b/test/smp/CMakeLists.txt @@ -262,7 +262,7 @@ function(add_threadx_regression_test TEST_SOURCE) -m 60s -p tb.top.num_cores=4 ) - set_tests_properties(${TEST_NAME} PROPERTIES TIMEOUT 10) + set_tests_properties(${TEST_NAME} PROPERTIES TIMEOUT 60) endfunction() foreach(test_case ${TX_REGRESSION_CASES}) -- 2.49.1 From 8a41f3e0fa58596fe1592dd820b1f8825a86bd6c Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Thu, 26 Mar 2026 15:55:32 +0100 Subject: [PATCH 51/79] small cleanuo to smp asm files --- port/threadx_smp/src/tx_thread_schedule.S | 17 ++++++----------- port/threadx_smp/src/tx_thread_smp_protect.S | 9 +-------- port/threadx_smp/src/tx_thread_smp_unprotect.S | 1 - 3 files changed, 7 insertions(+), 20 deletions(-) diff --git a/port/threadx_smp/src/tx_thread_schedule.S b/port/threadx_smp/src/tx_thread_schedule.S index eb3fa53..736e261 100644 --- a/port/threadx_smp/src/tx_thread_schedule.S +++ b/port/threadx_smp/src/tx_thread_schedule.S @@ -80,20 +80,15 @@ _tx_thread_schedule: la t0, _tx_thread_execute_ptr // Pickup address of execute ptr add t0, t0, t4 // Select this hart's execute slot _tx_thread_schedule_loop: + csrci mstatus, 0x08 // Lockout interrupts + LOAD t1, 0(t0) // Pickup next thread to execute + bnez t1, _tx_thread_schedule_thread // If non-NULL, continue + csrsi mstatus, 0x08 // Enable interrupts #ifdef TX_ENABLE_WFI - csrci mstatus, 0x08 // Lockout interrupts - LOAD t1, 0(t0) // Pickup next thread to execute - bnez t1, _tx_thread_schedule_thread // If non-NULL, continue - csrsi mstatus, 0x08 // Enable interrupts wfi // Wait for interrupt - j _tx_thread_schedule_loop // Keep looking for a thread -#else - csrci mstatus, 0x08 // Lockout interrupts - LOAD t1, 0(t0) // Pickup next thread to execute - bnez t1, _tx_thread_schedule_thread // If non-NULL, continue - csrsi mstatus, 0x08 // Enable interrupts - j _tx_thread_schedule_loop // Keep looking for a thread #endif + j _tx_thread_schedule_loop // Keep looking for a thread + _tx_thread_schedule_thread: /* Atomically claim the thread's ready token so only one hart can diff --git a/port/threadx_smp/src/tx_thread_smp_protect.S b/port/threadx_smp/src/tx_thread_smp_protect.S index 4618e3c..7762057 100644 --- a/port/threadx_smp/src/tx_thread_smp_protect.S +++ b/port/threadx_smp/src/tx_thread_smp_protect.S @@ -23,18 +23,11 @@ _tx_thread_smp_protect: /* Try to get the protection. */ LWU t4, 0(t1) // Pickup protection flag - beqz t4, _get_protection // If clear, try to claim it - - /* Protection is busy. Restore interrupts and retry. */ - csrw mstatus, a0 // Restore interrupts - j _tx_thread_smp_protect // Restart the protection attempt - -_get_protection: + bnez t4, _protection_busy // If set, protection is busy li t4, 1 // Build lock value amoswap.w.aq t5, t4, (t1) // Attempt to get protection bnez t5, _protection_busy // If old value != 0, retry -_got_protection: fence rw, rw // Ensure lock acquisition is visible sw t2, 4(t1) // Save owning hart diff --git a/port/threadx_smp/src/tx_thread_smp_unprotect.S b/port/threadx_smp/src/tx_thread_smp_unprotect.S index 431898b..eaa6be8 100644 --- a/port/threadx_smp/src/tx_thread_smp_unprotect.S +++ b/port/threadx_smp/src/tx_thread_smp_unprotect.S @@ -35,7 +35,6 @@ _tx_thread_smp_unprotect: /* Release the protection. */ li t3, -1 // Invalid owner value sw t3, 4(t2) // Mark owning hart invalid - fence rw, rw // Ensure shared accesses complete amoswap.w.rl x0, x0, (t2) // Release protection flag _still_protected: -- 2.49.1 From ab6c2d0bc48870a382dfd4dcd50589eb8a27091b Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Thu, 26 Mar 2026 15:56:47 +0100 Subject: [PATCH 52/79] adds newest version of threadx fork ATP Debug32 passes all tx and smp regression tests --- third-party/threadx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third-party/threadx b/third-party/threadx index cfbccc8..fea52c2 160000 --- a/third-party/threadx +++ b/third-party/threadx @@ -1 +1 @@ -Subproject commit cfbccc838ea15d4d359e48965a1bf8bc3f841d49 +Subproject commit fea52c29b7ee6a7feb9d2ea3b47b34f26267530a -- 2.49.1 From 870a5f9c3630dc7277fa980768670143cddd731f Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Thu, 26 Mar 2026 16:18:04 +0100 Subject: [PATCH 53/79] adds newer version of threadx fork; regression tx and smp now works for Debug32 and Release32 --- third-party/threadx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third-party/threadx b/third-party/threadx index fea52c2..b148d1d 160000 --- a/third-party/threadx +++ b/third-party/threadx @@ -1 +1 @@ -Subproject commit fea52c29b7ee6a7feb9d2ea3b47b34f26267530a +Subproject commit b148d1de4136730127bf66505e1deff073f618b9 -- 2.49.1 From dd0c8379308133dcf6fd1d4fd696077eba368197 Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Mon, 30 Mar 2026 19:48:09 +0200 Subject: [PATCH 54/79] corrects stack alignment --- port/threadx/src/tx_thread_stack_build.S | 2 +- port/threadx_smp/src/tx_thread_stack_build.S | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/port/threadx/src/tx_thread_stack_build.S b/port/threadx/src/tx_thread_stack_build.S index ec6f90b..2436d30 100644 --- a/port/threadx/src/tx_thread_stack_build.S +++ b/port/threadx/src/tx_thread_stack_build.S @@ -139,7 +139,7 @@ If floating point support: Stack Bottom: (higher memory address) */ LOAD t0, TX_THREAD_STACK_END_OFFSET(a0) // Pickup end of stack area - andi t0, t0, -4*REGBYTES // Ensure alignment (16-byte for RV32 & 32-byte for RV64) + andi t0, t0, -16 // Ensure 16-byte alignment /* Actually build the stack frame. */ diff --git a/port/threadx_smp/src/tx_thread_stack_build.S b/port/threadx_smp/src/tx_thread_stack_build.S index d11d2bf..bc6fc73 100644 --- a/port/threadx_smp/src/tx_thread_stack_build.S +++ b/port/threadx_smp/src/tx_thread_stack_build.S @@ -139,7 +139,7 @@ If floating point support: Stack Bottom: (higher memory address) */ LOAD t0, TX_THREAD_STACK_END_OFFSET(a0) // Pickup end of stack area - andi t0, t0, -4*REGBYTES // Ensure alignment (16-byte for RV32 & 32-byte for RV64) + andi t0, t0, -16 // Ensure 16-byte alignment /* Actually build the stack frame. */ -- 2.49.1 From 7e2e162fe1b6a9ab41a0c93a7a9bb5e65d7e7802 Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Tue, 31 Mar 2026 16:09:44 +0200 Subject: [PATCH 55/79] cleans up nx_port dependency --- port/threadx/inc/nx_port.h | 1 - 1 file changed, 1 deletion(-) diff --git a/port/threadx/inc/nx_port.h b/port/threadx/inc/nx_port.h index 10875a0..2605d8d 100644 --- a/port/threadx/inc/nx_port.h +++ b/port/threadx/inc/nx_port.h @@ -47,7 +47,6 @@ #ifndef NX_PORT_H #define NX_PORT_H -#include "tx_port.h" /* Determine if the optional NetX user define file should be used. */ #ifdef NX_INCLUDE_USER_DEFINE_FILE -- 2.49.1 From 824b71c283bb45201123e2e2165c07f8b4c6c9fc Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Tue, 31 Mar 2026 16:13:34 +0200 Subject: [PATCH 56/79] adds csr.h to the moonlight port (adds duplicate as it is also in port/threadx/inc) --- port/moonlight/inc/csr.h | 371 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 371 insertions(+) create mode 100644 port/moonlight/inc/csr.h diff --git a/port/moonlight/inc/csr.h b/port/moonlight/inc/csr.h new file mode 100644 index 0000000..3b64b4f --- /dev/null +++ b/port/moonlight/inc/csr.h @@ -0,0 +1,371 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * 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 CSR_H +#define 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) + +// Supervisor Status Register, sstatus +#define SSTATUS_SPP (1L << 8) // Previous mode, 1=Supervisor, 0=User +#define SSTATUS_SPIE (1L << 5) // Supervisor Previous Interrupt Enable +#define SSTATUS_UPIE (1L << 4) // User Previous Interrupt Enable +#define SSTATUS_SIE (1L << 1) // Supervisor Interrupt Enable +#define SSTATUS_UIE (1L << 0) // User Interrupt Enable +#define SSTATUS_SPIE (1L << 5) +#define SSTATUS_UPIE (1L << 4) + +// Supervisor Interrupt Enable +#define SIE_SEIE (1L << 9) // external +#define SIE_STIE (1L << 5) // timer +#define SIE_SSIE (1L << 1) // software + +#ifndef __ASSEMBLER__ + +#include + +static inline uint64_t riscv_get_core() +{ + uint64_t x; + asm volatile("csrr %0, mhartid" : "=r"(x)); + return x; +} + +static inline uint64_t riscv_get_mstatus() +{ + uint64_t x; + asm volatile("csrr %0, mstatus" : "=r"(x)); + return x; +} + +static inline void riscv_writ_mstatus(uint64_t x) +{ + asm volatile("csrw mstatus, %0" : : "r"(x)); +} + +// machine exception program counter, holds the +// instruction address to which a return from +// exception will go. +static inline void riscv_writ_mepc(uint64_t x) +{ + asm volatile("csrw mepc, %0" : : "r"(x)); +} + +static inline uint64_t riscv_get_sstatus() +{ + uint64_t x; + asm volatile("csrr %0, sstatus" : "=r"(x)); + return x; +} + +static inline void riscv_writ_sstatus(uint64_t x) +{ + asm volatile("csrw sstatus, %0" : : "r"(x)); +} + +// Supervisor Interrupt Pending +static inline uint64_t riscv_get_sip() +{ + uint64_t x; + asm volatile("csrr %0, sip" : "=r"(x)); + return x; +} + +static inline void riscv_writ_sip(uint64_t x) +{ + asm volatile("csrw sip, %0" : : "r"(x)); +} + +static inline uint64_t riscv_get_sie() +{ + uint64_t x; + asm volatile("csrr %0, sie" : "=r"(x)); + return x; +} + +static inline void riscv_writ_sie(uint64_t x) +{ + asm volatile("csrw sie, %0" : : "r"(x)); +} + +static inline uint64_t riscv_get_mie() +{ + uint64_t x; + asm volatile("csrr %0, mie" : "=r"(x)); + return x; +} + +static inline void riscv_writ_mie(uint64_t x) +{ + asm volatile("csrw mie, %0" : : "r"(x)); +} + +// supervisor exception program counter, holds the +// instruction address to which a return from +// exception will go. +static inline void riscv_writ_sepc(uint64_t x) +{ + asm volatile("csrw sepc, %0" : : "r"(x)); +} + +static inline uint64_t riscv_get_sepc() +{ + uint64_t x; + asm volatile("csrr %0, sepc" : "=r"(x)); + return x; +} + +// Machine Exception Delegation +static inline uint64_t riscv_get_medeleg() +{ + uint64_t x; + asm volatile("csrr %0, medeleg" : "=r"(x)); + return x; +} + +static inline void riscv_writ_medeleg(uint64_t x) +{ + asm volatile("csrw medeleg, %0" : : "r"(x)); +} + +// Machine Interrupt Delegation +static inline uint64_t riscv_get_mideleg() +{ + uint64_t x; + asm volatile("csrr %0, mideleg" : "=r"(x)); + return x; +} + +static inline void riscv_writ_mideleg(uint64_t x) +{ + asm volatile("csrw mideleg, %0" : : "r"(x)); +} + +// Supervisor Trap-Vector Base Address +// low two bits are mode. +static inline void riscv_writ_stvec(uint64_t x) +{ + asm volatile("csrw stvec, %0" : : "r"(x)); +} + +static inline uint64_t riscv_get_stvec() +{ + uint64_t x; + asm volatile("csrr %0, stvec" : "=r"(x)); + return x; +} + +// Supervisor Timer Comparison Register +static inline uint64_t riscv_get_stimecmp() +{ + uint64_t x; + // asm volatile("csrr %0, stimecmp" : "=r" (x) ); + asm volatile("csrr %0, 0x14d" : "=r"(x)); + return x; +} + +static inline void riscv_writ_stimecmp(uint64_t x) +{ + // asm volatile("csrw stimecmp, %0" : : "r" (x)); + asm volatile("csrw 0x14d, %0" : : "r"(x)); +} + +// Machine Environment Configuration Register +static inline uint64_t riscv_get_menvcfg() +{ + uint64_t x; + // asm volatile("csrr %0, menvcfg" : "=r" (x) ); + asm volatile("csrr %0, 0x30a" : "=r"(x)); + return x; +} + +static inline void riscv_writ_menvcfg(uint64_t x) +{ + // asm volatile("csrw menvcfg, %0" : : "r" (x)); + asm volatile("csrw 0x30a, %0" : : "r"(x)); +} + +// Physical Memory Protection +static inline void riscv_writ_pmpcfg0(uint64_t x) +{ + asm volatile("csrw pmpcfg0, %0" : : "r"(x)); +} + +static inline void riscv_writ_pmpaddr0(uint64_t x) +{ + asm volatile("csrw pmpaddr0, %0" : : "r"(x)); +} + +// supervisor address translation and protection; +// holds the address of the page table. +static inline void riscv_writ_satp(uint64_t x) +{ + asm volatile("csrw satp, %0" : : "r"(x)); +} + +static inline uint64_t riscv_get_satp() +{ + uint64_t x; + asm volatile("csrr %0, satp" : "=r"(x)); + return x; +} + +// Supervisor Trap Cause +static inline uint64_t riscv_get_scause() +{ + uint64_t x; + asm volatile("csrr %0, scause" : "=r"(x)); + return x; +} + +// Supervisor Trap Value +static inline uint64_t riscv_get_stval() +{ + uint64_t x; + asm volatile("csrr %0, stval" : "=r"(x)); + return x; +} + +// Machine-mode Counter-Enable +static inline void riscv_writ_mcounteren(uint64_t x) +{ + asm volatile("csrw mcounteren, %0" : : "r"(x)); +} + +static inline uint64_t riscv_get_mcounteren() +{ + uint64_t x; + asm volatile("csrr %0, mcounteren" : "=r"(x)); + return x; +} + +// machine-mode cycle counter +static inline uint64_t riscv_get_time() +{ + uint64_t x; + asm volatile("csrr %0, time" : "=r"(x)); + return x; +} + +// enable device interrupts +static inline void riscv_sintr_on() +{ + uint64_t sstatus = riscv_get_sstatus(); + sstatus |= SSTATUS_SIE; + riscv_writ_sstatus(sstatus); +} + +// disable device interrupts +static inline void riscv_sintr_off() +{ + uint64_t sstatus = riscv_get_sstatus(); + sstatus &= (~SSTATUS_SIE); + riscv_writ_sstatus(sstatus); +} + +// are device interrupts enabled? +static inline int riscv_sintr_get() +{ + uint64_t x = riscv_get_sstatus(); + return (x & SSTATUS_SIE) != 0; +} + +static inline void riscv_sintr_restore(int x) +{ + if (x) + riscv_sintr_on(); + else + riscv_sintr_off(); +} + +// enable device interrupts +static inline void riscv_mintr_on() +{ + uint64_t mstatus = riscv_get_mstatus(); + mstatus |= MSTATUS_MIE; + riscv_writ_mstatus(mstatus); +} + +// disable device interrupts +static inline void riscv_mintr_off() +{ + uint64_t mstatus = riscv_get_mstatus(); + mstatus &= (~MSTATUS_MIE); + riscv_writ_mstatus(mstatus); +} + +// are device interrupts enabled? +static inline int riscv_mintr_get() +{ + uint64_t x = riscv_get_mstatus(); + return (x & MSTATUS_MIE) != 0; +} + +static inline void riscv_mintr_restore(int x) +{ + if (x) + riscv_mintr_on(); + else + riscv_mintr_off(); +} + +static inline uint64_t riscv_get_sp() +{ + uint64_t x; + asm volatile("mv %0, sp" : "=r"(x)); + return x; +} + +// read and write tp, the thread pointer, which xv6 uses to hold +// this core's hartid (core number), the index into cpus[]. +static inline uint64_t riscv_get_tp() +{ + uint64_t x; + asm volatile("mv %0, tp" : "=r"(x)); + return x; +} + +static inline void riscv_writ_tp(uint64_t x) +{ + asm volatile("mv tp, %0" : : "r"(x)); +} + +static inline uint64_t riscv_get_ra() +{ + uint64_t x; + asm volatile("mv %0, ra" : "=r"(x)); + return x; +} + +// flush the TLB. +static inline void sfence_vma() +{ + // the zero, zero means flush all TLB entries. + asm volatile("sfence.vma zero, zero"); +} + +#endif // __ASSEMBLER__ + +#endif -- 2.49.1 From b5016c23b0be8dc106e0ba3605099a71a52b465e Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Tue, 31 Mar 2026 16:13:45 +0200 Subject: [PATCH 57/79] updates README --- README.md | 39 +++++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index c8b70d9..985b848 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,46 @@ -# Running the RTOS +# ThreadX Port for the TGFS cores and accompanying Virtual prototypes -This repositiory contains prototypical RISC-V implementation of ThreadX designed to run on the RISC-V VP by MINRES (tested on commit [d96cd4a](https://github.com/Minres/RISCV-VP/tree/d96cd4a01de1f4bcc77db3eceb57f5c144395847)). +This repositiory contains a RISC-V implementation of ThreadX designed to run on the TGFS cores or the RISC-V VP by MINRES (available on [Github](https://github.com/Minres/RISCV-VP/tree/d96cd4a01de1f4bcc77db3eceb57f5c144395847)). + +**Note**: SMP support additionally requires a VP variant that allows the number of cores to be configured. ## Building the RTOS -Four presets are provided, targetting RV32 and RV64 combined with GC or IMAC. The presets use rv32imac and rv64imac. +A set of presets is provided, targetting 32 and 64-bit systems. + +E.g. building for the RV64IMAC configuration: + +```bash +cmake --preset Debug +cmake --build --preset Debug --parallel +``` ## Running on the VP A run command can look like this: ```bash -riscv-vp --isa=rv64gc_m -f build/Debug/main.elf +riscv-vp --isa=rv64imac_m -f build/Debug/thread_demo.elf ``` -## What is running? +## Running the Regression Suites -The current implementation is just a demo, taken from the existing threadx qemu implementation. +The regression suites live in test/. Both suites register their cases with CTest and require the path to the MINRES `riscv-vp` simulator to be provided through `THREADX_TEST_SIMULATOR`. + +The SMP regression suite additionally requires a `riscv-vp` variant that supports a configurable number of cores. + +Examples: + +```bash +cmake -S test/threadx --preset Debug32 -DTHREADX_TEST_SIMULATOR=/opt/riscv-vp/bin/riscv-vp +cmake --build --preset Debug32 --parallel +ctest --test-dir ../../build/Debug32/test/threadx --output-on-failure --parallel 4 +``` + +## Demo Applications + +The repository currently provides three demo applications: + +- `thread_demo`: a basic single-kernel ThreadX demo based on the standard ThreadX sample application +- `smp_demo`: the same basic demo application for running on a ThreadX SMP kernel +- `tcp_demo`: a NetX Duo demo that exercises the TCP/IP stack on top of ThreadX -- 2.49.1 From 6e82d494882cec2730653920945d69246477a5d2 Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Tue, 31 Mar 2026 16:13:53 +0200 Subject: [PATCH 58/79] adds Jenkinsfile --- Jenkinsfile | 153 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 Jenkinsfile diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..5b10ef9 --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,153 @@ +def suites = ['threadx', 'smp'] +def presets = ['Debug32', 'Release32'] + +def runOneRegression(String suite, String preset, String simulatorPath) { + def suiteDir = "test/${suite}" + def buildDir = "../../build/${preset}/test/${suite}" + + stage("${suite}-${preset}") { + dir(suiteDir) { + sh """ + set -eu + test -n "${simulatorPath}" + test -x "${simulatorPath}" + cmake --preset "${preset}" \\ + -DTHREADX_TEST_SIMULATOR="${simulatorPath}" + cmake --build "${buildDir}" --parallel "\$(nproc)" + ctest \\ + --test-dir "${buildDir}" \\ + --output-on-failure \\ + --output-junit "${buildDir}/ctest-results.xml" \\ + --parallel "1" + """ + } + } +} + +def resolveImageCommit(String image) { + sh( + script: """ + set -eu + docker pull "${image}" >/dev/null + docker image inspect --format='{{ index .Config.Labels "git-commit" }}' "${image}" + """, + returnStdout: true + ).trim() +} + +def runRegressionLane(String image, String simulatorPath, boolean allowFailure) { + def parallelTasks = [:] + + for (String suite : suites) { + for (String preset : presets) { + def suiteName = suite + def presetName = preset + def taskName = "${suiteName}-${presetName}" + parallelTasks[taskName] = { + if (allowFailure) { + catchError(buildResult: 'UNSTABLE', stageResult: 'UNSTABLE') { + runOneRegression(suiteName, presetName, simulatorPath) + } + } else { + runOneRegression(suiteName, presetName, simulatorPath) + } + } + } + } + + docker.image(image).inside { + sh ''' + set -eu + cmake --version + ctest --version + ''' + parallel parallelTasks + } +} + +properties([ + parameters([ + string( + name: 'SIMULATOR_IMAGE_PINNED', + defaultValue: 'git.minres.com/here/here-vp:ac4f736', + description: 'Version-pinned Docker image used for the blocking regression lane' + ), + string( + name: 'SIMULATOR_IMAGE_LATEST', + defaultValue: 'git.minres.com/here/here-vp:latest', + description: 'Moving Docker image tag used for the canary lane' + ), + string( + name: 'THREADX_TEST_SIMULATOR', + defaultValue: '/usr/local/bin/riscv-vp', + description: 'Absolute path to the simulator executable inside the Docker image' + ) + ]) +]) + +def canaryShouldRun = true + +node { + timestamps { + ansiColor('xterm') { + try { + stage('Checkout') { + checkout scm + } + + stage('Check Canary Divergence') { + def pinnedCommit = resolveImageCommit(params.SIMULATOR_IMAGE_PINNED) + def latestCommit = resolveImageCommit(params.SIMULATOR_IMAGE_LATEST) + + if (!pinnedCommit) { + error "Missing git-commit label on ${params.SIMULATOR_IMAGE_PINNED}" + } + if (!latestCommit) { + error "Missing git-commit label on ${params.SIMULATOR_IMAGE_LATEST}" + } + + canaryShouldRun = (pinnedCommit != latestCommit) + + if (canaryShouldRun) { + echo "Canary enabled: ${params.SIMULATOR_IMAGE_LATEST} (${latestCommit}) diverges from ${params.SIMULATOR_IMAGE_PINNED} (${pinnedCommit})" + } else { + echo "Canary skipped: ${params.SIMULATOR_IMAGE_LATEST} and ${params.SIMULATOR_IMAGE_PINNED} both use git-commit ${pinnedCommit}" + } + } + + stage('Stable Regression') { + runRegressionLane(params.SIMULATOR_IMAGE_PINNED, params.THREADX_TEST_SIMULATOR, false) + } + + stage('Simulator Canary') { + if (canaryShouldRun) { + runRegressionLane(params.SIMULATOR_IMAGE_LATEST, params.THREADX_TEST_SIMULATOR, true) + } else { + echo 'Skipping canary lane because latest and pinned images are identical.' + } + } + } catch (err) { + currentBuild.result = 'FAILURE' + throw err + } finally { + junit allowEmptyResults: true, testResults: 'build/*/test/*/ctest-results.xml' + archiveArtifacts artifacts: 'build/*/test/*/*.map,build/*/test/*/*.dis,build/*/test/*/Testing/**', allowEmptyArchive: true + + if (currentBuild.currentResult == 'SUCCESS') { + rocketSend ":thumbsup: ThreadX regression run passed, results at ${env.RUN_DISPLAY_URL} " + } else if (currentBuild.currentResult == 'UNSTABLE') { + rocketSend ":warning: ThreadX canary regression is unstable on ${params.SIMULATOR_IMAGE_LATEST}, please check ${env.RUN_DISPLAY_URL} " + } else if (currentBuild.currentResult == 'FAILURE') { + archiveArtifacts artifacts: 'failed_seeds_*.txt', followSymlinks: false, onlyIfSuccessful: false + rocketSend ":thumbsdown: ThreadX regression failed, please check ${env.RUN_DISPLAY_URL} " + emailext recipientProviders: [culprits(), requestor()], + subject: "ThreadX Pipeline Failed: ${currentBuild.fullDisplayName}", + body: """ +

Build Status: ${currentBuild.currentResult}

+

Check logs at Build Console Logs or at Overview

+ """ + } + } + } + } +} -- 2.49.1 From 9d698fb911deb11cd0b610592df983218e829b1e Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Tue, 31 Mar 2026 16:21:20 +0200 Subject: [PATCH 59/79] removes ansiColor step --- Jenkinsfile | 98 ++++++++++++++++++++++++++--------------------------- 1 file changed, 48 insertions(+), 50 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 5b10ef9..d86810f 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -89,64 +89,62 @@ def canaryShouldRun = true node { timestamps { - ansiColor('xterm') { - try { - stage('Checkout') { - checkout scm + try { + stage('Checkout') { + checkout scm + } + + stage('Check Canary Divergence') { + def pinnedCommit = resolveImageCommit(params.SIMULATOR_IMAGE_PINNED) + def latestCommit = resolveImageCommit(params.SIMULATOR_IMAGE_LATEST) + + if (!pinnedCommit) { + error "Missing git-commit label on ${params.SIMULATOR_IMAGE_PINNED}" + } + if (!latestCommit) { + error "Missing git-commit label on ${params.SIMULATOR_IMAGE_LATEST}" } - stage('Check Canary Divergence') { - def pinnedCommit = resolveImageCommit(params.SIMULATOR_IMAGE_PINNED) - def latestCommit = resolveImageCommit(params.SIMULATOR_IMAGE_LATEST) + canaryShouldRun = (pinnedCommit != latestCommit) - if (!pinnedCommit) { - error "Missing git-commit label on ${params.SIMULATOR_IMAGE_PINNED}" - } - if (!latestCommit) { - error "Missing git-commit label on ${params.SIMULATOR_IMAGE_LATEST}" - } - - canaryShouldRun = (pinnedCommit != latestCommit) - - if (canaryShouldRun) { - echo "Canary enabled: ${params.SIMULATOR_IMAGE_LATEST} (${latestCommit}) diverges from ${params.SIMULATOR_IMAGE_PINNED} (${pinnedCommit})" - } else { - echo "Canary skipped: ${params.SIMULATOR_IMAGE_LATEST} and ${params.SIMULATOR_IMAGE_PINNED} both use git-commit ${pinnedCommit}" - } + if (canaryShouldRun) { + echo "Canary enabled: ${params.SIMULATOR_IMAGE_LATEST} (${latestCommit}) diverges from ${params.SIMULATOR_IMAGE_PINNED} (${pinnedCommit})" + } else { + echo "Canary skipped: ${params.SIMULATOR_IMAGE_LATEST} and ${params.SIMULATOR_IMAGE_PINNED} both use git-commit ${pinnedCommit}" } + } - stage('Stable Regression') { - runRegressionLane(params.SIMULATOR_IMAGE_PINNED, params.THREADX_TEST_SIMULATOR, false) - } + stage('Stable Regression') { + runRegressionLane(params.SIMULATOR_IMAGE_PINNED, params.THREADX_TEST_SIMULATOR, false) + } - stage('Simulator Canary') { - if (canaryShouldRun) { - runRegressionLane(params.SIMULATOR_IMAGE_LATEST, params.THREADX_TEST_SIMULATOR, true) - } else { - echo 'Skipping canary lane because latest and pinned images are identical.' - } + stage('Simulator Canary') { + if (canaryShouldRun) { + runRegressionLane(params.SIMULATOR_IMAGE_LATEST, params.THREADX_TEST_SIMULATOR, true) + } else { + echo 'Skipping canary lane because latest and pinned images are identical.' } - } catch (err) { - currentBuild.result = 'FAILURE' - throw err - } finally { - junit allowEmptyResults: true, testResults: 'build/*/test/*/ctest-results.xml' - archiveArtifacts artifacts: 'build/*/test/*/*.map,build/*/test/*/*.dis,build/*/test/*/Testing/**', allowEmptyArchive: true + } + } catch (err) { + currentBuild.result = 'FAILURE' + throw err + } finally { + junit allowEmptyResults: true, testResults: 'build/*/test/*/ctest-results.xml' + archiveArtifacts artifacts: 'build/*/test/*/*.map,build/*/test/*/*.dis,build/*/test/*/Testing/**', allowEmptyArchive: true - if (currentBuild.currentResult == 'SUCCESS') { - rocketSend ":thumbsup: ThreadX regression run passed, results at ${env.RUN_DISPLAY_URL} " - } else if (currentBuild.currentResult == 'UNSTABLE') { - rocketSend ":warning: ThreadX canary regression is unstable on ${params.SIMULATOR_IMAGE_LATEST}, please check ${env.RUN_DISPLAY_URL} " - } else if (currentBuild.currentResult == 'FAILURE') { - archiveArtifacts artifacts: 'failed_seeds_*.txt', followSymlinks: false, onlyIfSuccessful: false - rocketSend ":thumbsdown: ThreadX regression failed, please check ${env.RUN_DISPLAY_URL} " - emailext recipientProviders: [culprits(), requestor()], - subject: "ThreadX Pipeline Failed: ${currentBuild.fullDisplayName}", - body: """ -

Build Status: ${currentBuild.currentResult}

-

Check logs at Build Console Logs or at Overview

- """ - } + if (currentBuild.currentResult == 'SUCCESS') { + rocketSend ":thumbsup: ThreadX regression run passed, results at ${env.RUN_DISPLAY_URL} " + } else if (currentBuild.currentResult == 'UNSTABLE') { + rocketSend ":warning: ThreadX canary regression is unstable on ${params.SIMULATOR_IMAGE_LATEST}, please check ${env.RUN_DISPLAY_URL} " + } else if (currentBuild.currentResult == 'FAILURE') { + archiveArtifacts artifacts: 'failed_seeds_*.txt', followSymlinks: false, onlyIfSuccessful: false + rocketSend ":thumbsdown: ThreadX regression failed, please check ${env.RUN_DISPLAY_URL} " + emailext recipientProviders: [culprits(), requestor()], + subject: "ThreadX Pipeline Failed: ${currentBuild.fullDisplayName}", + body: """ +

Build Status: ${currentBuild.currentResult}

+

Check logs at Build Console Logs or at Overview

+ """ } } } -- 2.49.1 From f366a9211e7f6cd391f054f6344ad0b10b4d812a Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Tue, 31 Mar 2026 16:51:52 +0200 Subject: [PATCH 60/79] adds Info for debugging --- Jenkinsfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Jenkinsfile b/Jenkinsfile index d86810f..2787b83 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -28,6 +28,8 @@ def resolveImageCommit(String image) { sh( script: """ set -eu + id + whoami docker pull "${image}" >/dev/null docker image inspect --format='{{ index .Config.Labels "git-commit" }}' "${image}" """, -- 2.49.1 From 6b16e22ba672e8b49d066eba7bfcd4a53352ae6b Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Tue, 31 Mar 2026 16:59:58 +0200 Subject: [PATCH 61/79] next try at debugging the Jenkinsfile --- Jenkinsfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 2787b83..75c734a 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -28,8 +28,7 @@ def resolveImageCommit(String image) { sh( script: """ set -eu - id - whoami + docker login docker pull "${image}" >/dev/null docker image inspect --format='{{ index .Config.Labels "git-commit" }}' "${image}" """, -- 2.49.1 From 0e0ad03e6384f8d4d23a9c8c0010362347901a11 Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Tue, 31 Mar 2026 17:20:09 +0200 Subject: [PATCH 62/79] adds auth to docker login --- Jenkinsfile | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 75c734a..6b0714a 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -25,15 +25,22 @@ def runOneRegression(String suite, String preset, String simulatorPath) { } def resolveImageCommit(String image) { - sh( - script: """ - set -eu - docker login - docker pull "${image}" >/dev/null - docker image inspect --format='{{ index .Config.Labels "git-commit" }}' "${image}" - """, - returnStdout: true - ).trim() + + withCredentials([usernamePassword( + credentialsId: gitea-jenkins, + usernameVariable: 'REGISTRY_USER', + passwordVariable: 'REGISTRY_PASS' + )]) { + return sh( + script: """ + set -eu + echo "\$REGISTRY_PASS" | docker login "git.minres.com" -u "\$REGISTRY_USER" --password-stdin >/dev/null + docker pull "${image}" >/dev/null + docker image inspect --format='{{ index .Config.Labels "git-commit" }}' "${image}" + """, + returnStdout: true + ).trim() + } } def runRegressionLane(String image, String simulatorPath, boolean allowFailure) { -- 2.49.1 From dc30c46763f655c2717ae65fc0aaf56a2379b0ef Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Tue, 31 Mar 2026 17:24:38 +0200 Subject: [PATCH 63/79] adds quote do cred ID --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 6b0714a..2928e93 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -27,7 +27,7 @@ def runOneRegression(String suite, String preset, String simulatorPath) { def resolveImageCommit(String image) { withCredentials([usernamePassword( - credentialsId: gitea-jenkins, + credentialsId: 'gitea-jenkins', usernameVariable: 'REGISTRY_USER', passwordVariable: 'REGISTRY_PASS' )]) { -- 2.49.1 From ea1945983f3284c53ccfff90b1c4467609c61c9b Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Tue, 31 Mar 2026 17:28:03 +0200 Subject: [PATCH 64/79] Removes parameters for latest image and simulator path also addresses latest error --- Jenkinsfile | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 2928e93..3471adf 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,5 +1,7 @@ def suites = ['threadx', 'smp'] def presets = ['Debug32', 'Release32'] +def simulatorImageLatest = 'git.minres.com/here/here-vp:latest' +def threadxTestSimulator = '/usr/local/bin/riscv-vp' def runOneRegression(String suite, String preset, String simulatorPath) { def suiteDir = "test/${suite}" @@ -43,11 +45,11 @@ def resolveImageCommit(String image) { } } -def runRegressionLane(String image, String simulatorPath, boolean allowFailure) { +def runRegressionLane(String image, String simulatorPath, boolean allowFailure, List suiteNames, List presetNames) { def parallelTasks = [:] - for (String suite : suites) { - for (String preset : presets) { + for (String suite : suiteNames) { + for (String preset : presetNames) { def suiteName = suite def presetName = preset def taskName = "${suiteName}-${presetName}" @@ -79,16 +81,6 @@ properties([ name: 'SIMULATOR_IMAGE_PINNED', defaultValue: 'git.minres.com/here/here-vp:ac4f736', description: 'Version-pinned Docker image used for the blocking regression lane' - ), - string( - name: 'SIMULATOR_IMAGE_LATEST', - defaultValue: 'git.minres.com/here/here-vp:latest', - description: 'Moving Docker image tag used for the canary lane' - ), - string( - name: 'THREADX_TEST_SIMULATOR', - defaultValue: '/usr/local/bin/riscv-vp', - description: 'Absolute path to the simulator executable inside the Docker image' ) ]) ]) @@ -104,31 +96,31 @@ node { stage('Check Canary Divergence') { def pinnedCommit = resolveImageCommit(params.SIMULATOR_IMAGE_PINNED) - def latestCommit = resolveImageCommit(params.SIMULATOR_IMAGE_LATEST) + def latestCommit = resolveImageCommit(simulatorImageLatest) if (!pinnedCommit) { error "Missing git-commit label on ${params.SIMULATOR_IMAGE_PINNED}" } if (!latestCommit) { - error "Missing git-commit label on ${params.SIMULATOR_IMAGE_LATEST}" + error "Missing git-commit label on ${simulatorImageLatest}" } canaryShouldRun = (pinnedCommit != latestCommit) if (canaryShouldRun) { - echo "Canary enabled: ${params.SIMULATOR_IMAGE_LATEST} (${latestCommit}) diverges from ${params.SIMULATOR_IMAGE_PINNED} (${pinnedCommit})" + echo "Canary enabled: ${simulatorImageLatest} (${latestCommit}) diverges from ${params.SIMULATOR_IMAGE_PINNED} (${pinnedCommit})" } else { - echo "Canary skipped: ${params.SIMULATOR_IMAGE_LATEST} and ${params.SIMULATOR_IMAGE_PINNED} both use git-commit ${pinnedCommit}" + echo "Canary skipped: ${simulatorImageLatest} and ${params.SIMULATOR_IMAGE_PINNED} both use git-commit ${pinnedCommit}" } } stage('Stable Regression') { - runRegressionLane(params.SIMULATOR_IMAGE_PINNED, params.THREADX_TEST_SIMULATOR, false) + runRegressionLane(params.SIMULATOR_IMAGE_PINNED, threadxTestSimulator, false, suites, presets) } stage('Simulator Canary') { if (canaryShouldRun) { - runRegressionLane(params.SIMULATOR_IMAGE_LATEST, params.THREADX_TEST_SIMULATOR, true) + runRegressionLane(simulatorImageLatest, threadxTestSimulator, true, suites, presets) } else { echo 'Skipping canary lane because latest and pinned images are identical.' } @@ -143,7 +135,7 @@ node { if (currentBuild.currentResult == 'SUCCESS') { rocketSend ":thumbsup: ThreadX regression run passed, results at ${env.RUN_DISPLAY_URL} " } else if (currentBuild.currentResult == 'UNSTABLE') { - rocketSend ":warning: ThreadX canary regression is unstable on ${params.SIMULATOR_IMAGE_LATEST}, please check ${env.RUN_DISPLAY_URL} " + rocketSend ":warning: ThreadX canary regression is unstable on ${simulatorImageLatest}, please check ${env.RUN_DISPLAY_URL} " } else if (currentBuild.currentResult == 'FAILURE') { archiveArtifacts artifacts: 'failed_seeds_*.txt', followSymlinks: false, onlyIfSuccessful: false rocketSend ":thumbsdown: ThreadX regression failed, please check ${env.RUN_DISPLAY_URL} " -- 2.49.1 From 3cd42fcb10f1471732a0a577be2220805e615e69 Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Tue, 31 Mar 2026 17:31:31 +0200 Subject: [PATCH 65/79] adds submodules to checkout --- Jenkinsfile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Jenkinsfile b/Jenkinsfile index 3471adf..3e775ef 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -92,6 +92,10 @@ node { try { stage('Checkout') { checkout scm + sh ''' + git submodule sync --recursive + git submodule update --init --recursive + ''' } stage('Check Canary Divergence') { -- 2.49.1 From bb79d43ff712a5c215dafa281f2cd815eaffb957 Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Tue, 31 Mar 2026 17:34:12 +0200 Subject: [PATCH 66/79] removes parallel build to enable searching for build error --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 3e775ef..dc575f4 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -15,7 +15,7 @@ def runOneRegression(String suite, String preset, String simulatorPath) { test -x "${simulatorPath}" cmake --preset "${preset}" \\ -DTHREADX_TEST_SIMULATOR="${simulatorPath}" - cmake --build "${buildDir}" --parallel "\$(nproc)" + cmake --build "${buildDir}" ctest \\ --test-dir "${buildDir}" \\ --output-on-failure \\ -- 2.49.1 From 27fc04011cc9ba2643d1cdd9234806706285c51d Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Tue, 31 Mar 2026 17:39:44 +0200 Subject: [PATCH 67/79] forces rebuild for cmake --- Jenkinsfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index dc575f4..141c347 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -13,9 +13,9 @@ def runOneRegression(String suite, String preset, String simulatorPath) { set -eu test -n "${simulatorPath}" test -x "${simulatorPath}" - cmake --preset "${preset}" \\ + cmake --fresh --preset "${preset}" \\ -DTHREADX_TEST_SIMULATOR="${simulatorPath}" - cmake --build "${buildDir}" + cmake --build --preset "${preset}" ctest \\ --test-dir "${buildDir}" \\ --output-on-failure \\ -- 2.49.1 From 856b5f9d32ef5253b58715d00ea4bdd9414eede5 Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Tue, 31 Mar 2026 21:59:05 +0200 Subject: [PATCH 68/79] updates threadx --- third-party/threadx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third-party/threadx b/third-party/threadx index b148d1d..ad06189 160000 --- a/third-party/threadx +++ b/third-party/threadx @@ -1 +1 @@ -Subproject commit b148d1de4136730127bf66505e1deff073f618b9 +Subproject commit ad06189db3489b4a641106093ca2f365a346ae91 -- 2.49.1 From 8dfc5ab688d10e8429cae49a843500b22ee9d4b8 Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Tue, 31 Mar 2026 22:52:50 +0200 Subject: [PATCH 69/79] parallelizes ctest and build in regression --- Jenkinsfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 141c347..8d7530c 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -15,12 +15,12 @@ def runOneRegression(String suite, String preset, String simulatorPath) { test -x "${simulatorPath}" cmake --fresh --preset "${preset}" \\ -DTHREADX_TEST_SIMULATOR="${simulatorPath}" - cmake --build --preset "${preset}" + cmake --build --preset "${preset}" --parallel "\$(nproc)" ctest \\ --test-dir "${buildDir}" \\ --output-on-failure \\ --output-junit "${buildDir}/ctest-results.xml" \\ - --parallel "1" + --parallel "4" """ } } @@ -134,7 +134,7 @@ node { throw err } finally { junit allowEmptyResults: true, testResults: 'build/*/test/*/ctest-results.xml' - archiveArtifacts artifacts: 'build/*/test/*/*.map,build/*/test/*/*.dis,build/*/test/*/Testing/**', allowEmptyArchive: true + archiveArtifacts artifacts: 'build/*/test/*/Testing/**', allowEmptyArchive: true if (currentBuild.currentResult == 'SUCCESS') { rocketSend ":thumbsup: ThreadX regression run passed, results at ${env.RUN_DISPLAY_URL} " -- 2.49.1 From bdf3893bf23aa99aa8a6fbd42995c27dc92060b2 Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Wed, 1 Apr 2026 18:21:20 +0200 Subject: [PATCH 70/79] updates latest image for regression --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 8d7530c..45cefb0 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -79,7 +79,7 @@ properties([ parameters([ string( name: 'SIMULATOR_IMAGE_PINNED', - defaultValue: 'git.minres.com/here/here-vp:ac4f736', + defaultValue: 'git.minres.com/here/here-vp:4fcc192', description: 'Version-pinned Docker image used for the blocking regression lane' ) ]) -- 2.49.1 From eda2a85dc12603e3968078b2d13c880d60a5cb1e Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Thu, 2 Apr 2026 10:31:12 +0200 Subject: [PATCH 71/79] makes ULONG 64 bit on RV64 --- port/threadx/inc/tx_port.h | 9 ++------- port/threadx/src/tx_thread_context_restore.S | 12 ++++++------ port/threadx/src/tx_thread_context_save.S | 4 ++-- port/threadx/src/tx_thread_schedule.S | 2 +- port/threadx/src/tx_thread_stack_build.S | 2 +- port/threadx/src/tx_thread_system_return.S | 6 +++--- 6 files changed, 15 insertions(+), 20 deletions(-) diff --git a/port/threadx/inc/tx_port.h b/port/threadx/inc/tx_port.h index 716d672..2da25c8 100644 --- a/port/threadx/inc/tx_port.h +++ b/port/threadx/inc/tx_port.h @@ -66,9 +66,7 @@ #define LWU lw #define LOG_REGBYTES 2 #endif -#define REGBYTES (1 << LOG_REGBYTES) -#define TX_THREAD_STACK_END_OFFSET 2 * 4 + 2 * REGBYTES -#define TX_THREAD_TIME_SLICE_OFFSET 3 * 4 + 3 * REGBYTES +#define REGBYTES (1 << LOG_REGBYTES) #else /*not __ASSEMBLER__ */ @@ -96,14 +94,11 @@ typedef unsigned char UCHAR; typedef int INT; typedef unsigned int UINT; typedef int LONG; -typedef unsigned int ULONG; // ThreadX expects ULONG to be 32 bit +typedef unsigned long ULONG; typedef unsigned long long ULONG64; typedef short SHORT; typedef unsigned short USHORT; #define ULONG64_DEFINED -#define ALIGN_TYPE_DEFINED -// Since ULONG is not actually unsigned long, it is to small to hold pointers for 64-bit systems -#define ALIGN_TYPE unsigned long /* Define the priority levels for ThreadX. Legal values range from 32 to 1024 and MUST be evenly divisible by 32. */ diff --git a/port/threadx/src/tx_thread_context_restore.S b/port/threadx/src/tx_thread_context_restore.S index 200f230..29f9452 100644 --- a/port/threadx/src/tx_thread_context_restore.S +++ b/port/threadx/src/tx_thread_context_restore.S @@ -80,9 +80,9 @@ _tx_thread_context_restore: { */ la t0, _tx_thread_system_state // Pickup addr of nested interrupt count - lw t1, 0(t0) // Pickup nested interrupt count + LOAD t1, 0(t0) // Pickup nested interrupt count addi t1, t1, -1 // Decrement the nested interrupt counter - sw t1, 0(t0) // Store new nested count + STORE t1, 0(t0) // Store new nested count beqz t1, _tx_thread_not_nested_restore // If 0, not nested restore /* Interrupts are nested. */ @@ -190,7 +190,7 @@ _tx_thread_not_nested_restore: LOAD t1, _tx_thread_current_ptr // Pickup current thread pointer beqz t1, _tx_thread_idle_system_restore // If NULL, idle system restore - LOAD t2, _tx_thread_preempt_disable // Pickup preempt disable flag + LWU t2, _tx_thread_preempt_disable // Pickup preempt disable flag bgtz t2, _tx_thread_no_preempt_restore // If set, restore interrupted thread LOAD t2, _tx_thread_execute_ptr // Pickup thread execute pointer @@ -354,14 +354,14 @@ _tx_thread_preempt_restore: { */ la t0, _tx_timer_time_slice // Pickup time slice variable address - lw t2, 0(t0) // Pickup time slice + LOAD t2, 0(t0) // Pickup time slice beqz t2, _tx_thread_dont_save_ts // If 0, skip time slice processing /* _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice _tx_timer_time_slice = 0; */ - sw t2, TX_THREAD_TIME_SLICE_OFFSET(t1) // Save current time slice - sw x0, 0(t0) // Clear global time slice + STORE t2, 6*REGBYTES(t1) // Save current time slice + STORE x0, 0(t0) // Clear global time slice /* } */ diff --git a/port/threadx/src/tx_thread_context_save.S b/port/threadx/src/tx_thread_context_save.S index 0de69be..c07ecf0 100644 --- a/port/threadx/src/tx_thread_context_save.S +++ b/port/threadx/src/tx_thread_context_save.S @@ -74,14 +74,14 @@ _tx_thread_context_save: STORE t1, 18*REGBYTES(sp) la t0, _tx_thread_system_state // Pickup address of system state - lw t1, 0(t0) // Pickup system state + LOAD t1, 0(t0) // Pickup system state /* Check for a nested interrupt condition. */ /* if (_tx_thread_system_state++) { */ beqz t1, _tx_thread_not_nested_save // If 0, first interrupt condition addi t1, t1, 1 // Increment the interrupt counter - sw t1, 0(t0) // Store the interrupt counter + STORE t1, 0(t0) // Store the interrupt counter /* Nested interrupt condition. Save the reset of the scratch registers on the stack and return to the diff --git a/port/threadx/src/tx_thread_schedule.S b/port/threadx/src/tx_thread_schedule.S index c9be4c6..1169443 100644 --- a/port/threadx/src/tx_thread_schedule.S +++ b/port/threadx/src/tx_thread_schedule.S @@ -105,12 +105,12 @@ _tx_thread_schedule_loop: /* _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; */ la t2, _tx_timer_time_slice // Pickup time-slice variable address + STORE t3, 0(t2) // Store new time-slice*/ /* Switch to the thread's stack. */ /* SP = _tx_thread_execute_ptr -> tx_thread_stack_ptr; */ LOAD sp, 2*REGBYTES(t1) // Switch to thread's stack - STORE t3, 0(t2) // Store new time-slice*/ #ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY diff --git a/port/threadx/src/tx_thread_stack_build.S b/port/threadx/src/tx_thread_stack_build.S index 2436d30..d7d54f3 100644 --- a/port/threadx/src/tx_thread_stack_build.S +++ b/port/threadx/src/tx_thread_stack_build.S @@ -138,7 +138,7 @@ If floating point support: Stack Bottom: (higher memory address) */ - LOAD t0, TX_THREAD_STACK_END_OFFSET(a0) // Pickup end of stack area + LOAD t0, 4*REGBYTES(a0) // Pickup end of stack area andi t0, t0, -16 // Ensure 16-byte alignment /* Actually build the stack frame. */ diff --git a/port/threadx/src/tx_thread_system_return.S b/port/threadx/src/tx_thread_system_return.S index ec6ff78..2b05c2a 100644 --- a/port/threadx/src/tx_thread_system_return.S +++ b/port/threadx/src/tx_thread_system_return.S @@ -151,7 +151,7 @@ _tx_thread_system_return: { */ la t4, _tx_timer_time_slice // Pickup time slice variable addr - lw t3, 0(t4) // Pickup time slice value + LOAD t3, 0(t4) // Pickup time slice value la t2, _tx_thread_schedule // Pickup address of scheduling loop beqz t3, _tx_thread_dont_save_ts // If no time-slice, don't save it @@ -159,8 +159,8 @@ _tx_thread_system_return: /* _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; _tx_timer_time_slice = 0; */ - sw t3, TX_THREAD_TIME_SLICE_OFFSET(t1) // Save current time-slice for thread - sw x0, 0(t4) // Clear time-slice variable + STORE t3, 6*REGBYTES(t1) // Save current time-slice for thread + STORE x0, 0(t4) // Clear time-slice variable /* } */ _tx_thread_dont_save_ts: -- 2.49.1 From c16e6b0446aeb647ab932c7dbb3fd6526274098f Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Thu, 2 Apr 2026 10:48:43 +0200 Subject: [PATCH 72/79] adjust SMP port to also use ULONG = 8 bytes --- port/threadx_smp/inc/tx_port.h | 6 +++++- .../src/tx_thread_context_restore.S | 18 +++++++++--------- port/threadx_smp/src/tx_thread_context_save.S | 8 ++++---- port/threadx_smp/src/tx_thread_schedule.S | 4 ++-- .../src/tx_thread_smp_current_state_get.S | 4 ++-- .../src/tx_thread_smp_initialize_wait.S | 14 +++++++++----- port/threadx_smp/src/tx_thread_smp_protect.S | 12 ++++++------ port/threadx_smp/src/tx_thread_smp_unprotect.S | 10 +++++----- port/threadx_smp/src/tx_thread_stack_build.S | 4 ++-- port/threadx_smp/src/tx_thread_system_return.S | 16 ++++++++-------- 10 files changed, 52 insertions(+), 44 deletions(-) diff --git a/port/threadx_smp/inc/tx_port.h b/port/threadx_smp/inc/tx_port.h index e4fc881..00587fb 100644 --- a/port/threadx_smp/inc/tx_port.h +++ b/port/threadx_smp/inc/tx_port.h @@ -61,12 +61,16 @@ #define STORE sd #define LOAD ld #define LWU lwu +#define AMOSWAP_AQ amoswap.d.aq +#define AMOSWAP_RL amoswap.d.rl #define LOG_REGBYTES 3 #else #define SLL32 sll #define STORE sw #define LOAD lw #define LWU lw +#define AMOSWAP_AQ amoswap.w.aq +#define AMOSWAP_RL amoswap.w.rl #define LOG_REGBYTES 2 #endif #define REGBYTES (1 << LOG_REGBYTES) @@ -166,7 +170,7 @@ typedef unsigned char UCHAR; typedef int INT; typedef unsigned int UINT; typedef int LONG; -typedef unsigned int ULONG; +typedef unsigned long ULONG; typedef unsigned long long ULONG64; typedef short SHORT; typedef unsigned short USHORT; diff --git a/port/threadx_smp/src/tx_thread_context_restore.S b/port/threadx_smp/src/tx_thread_context_restore.S index 8171155..0335047 100644 --- a/port/threadx_smp/src/tx_thread_context_restore.S +++ b/port/threadx_smp/src/tx_thread_context_restore.S @@ -80,13 +80,13 @@ _tx_thread_context_restore: { */ csrr t3, mhartid // Pickup current hart ID - slli t4, t3, 2 // Build per-hart ULONG offset + slli t4, t3, LOG_REGBYTES // Build per-hart ULONG offset slli t5, t3, LOG_REGBYTES // Build per-hart pointer offset la t0, _tx_thread_system_state // Pickup base of system-state array add t0, t0, t4 // Select this hart's system-state slot - lw t1, 0(t0) // Pickup nested interrupt count + LOAD t1, 0(t0) // Pickup nested interrupt count addi t1, t1, -1 // Decrement the nested interrupt counter - sw t1, 0(t0) // Store new nested count + STORE t1, 0(t0) // Store new nested count beqz t1, _tx_thread_not_nested_restore // If 0, not nested restore /* Interrupts are nested. */ @@ -202,10 +202,10 @@ _tx_thread_not_nested_restore: beq t1, t2, _tx_thread_no_preempt_restore // Same thread selected, no preemption la t0, _tx_thread_smp_protection // Pickup protection structure - lw t2, 4(t0) // Pickup owning hart + LOAD t2, 1*REGBYTES(t0) // Pickup owning hart bne t2, t3, _tx_thread_preempt_restore // If owned by another hart, preempt - LOAD t2, _tx_thread_preempt_disable // Pickup preempt disable flag + LWU t2, _tx_thread_preempt_disable // Pickup preempt disable flag bgtz t2, _tx_thread_no_preempt_restore // If set, restore interrupted thread @@ -367,14 +367,14 @@ _tx_thread_preempt_restore: la t0, _tx_timer_time_slice // Pickup base of time-slice array add t0, t0, t4 // Select this hart's time-slice slot - lw t2, 0(t0) // Pickup time slice + LOAD t2, 0(t0) // Pickup time slice beqz t2, _tx_thread_dont_save_ts // If 0, skip time slice processing /* _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice _tx_timer_time_slice = 0; */ - sw t2, TX_THREAD_TIME_SLICE_OFFSET(t1) // Save current time slice - sw x0, 0(t0) // Clear global time slice + STORE t2, 6*REGBYTES(t1) // Save current time slice + STORE x0, 0(t0) // Clear global time slice /* } */ @@ -392,7 +392,7 @@ _tx_thread_dont_save_ts: fence rw, rw // Publish current-thread clear before ready token addi t0, t1, TX_THREAD_SMP_LOCK_READY_BIT_OFFSET // Pickup lock/ready-bit address li t2, 1 // Rebuild ready token - amoswap.w.rl x0, t2, (t0) // Set thread ready token for reschedule + AMOSWAP_RL x0, t2, (t0) // Set thread ready token for reschedule /* } */ _tx_thread_idle_system_restore: diff --git a/port/threadx_smp/src/tx_thread_context_save.S b/port/threadx_smp/src/tx_thread_context_save.S index bddb7df..13b8d28 100644 --- a/port/threadx_smp/src/tx_thread_context_save.S +++ b/port/threadx_smp/src/tx_thread_context_save.S @@ -77,18 +77,18 @@ _tx_thread_context_save: STORE x29, 15*REGBYTES(sp) // Save t4 before reusing it csrr t2, mhartid // Pickup current hart ID - slli t3, t2, 2 // Build per-hart ULONG offset + slli t3, t2, LOG_REGBYTES // Build per-hart ULONG offset slli t4, t2, LOG_REGBYTES // Build per-hart pointer offset la t1, _tx_thread_system_state // Pickup base of system state array add t0, t1, t3 // Select this hart's system-state slot - lw t1, 0(t0) // Pickup system state + LOAD t1, 0(t0) // Pickup system state /* Check for a nested interrupt condition. */ /* if (_tx_thread_system_state++) { */ beqz t1, _tx_thread_not_nested_save // If 0, first interrupt condition addi t1, t1, 1 // Increment the interrupt counter - sw t1, 0(t0) // Store the interrupt counter + STORE t1, 0(t0) // Store the interrupt counter /* Nested interrupt condition. Save the reset of the scratch registers on the stack and return to the @@ -170,7 +170,7 @@ _tx_thread_not_nested_save: /* else if (_tx_thread_current_ptr) { */ addi t1, t1, 1 // Increment the interrupt counter - sw t1, 0(t0) // Store the interrupt counter + STORE t1, 0(t0) // Store the interrupt counter /* Not nested: Find the user thread that was running and load our SP */ diff --git a/port/threadx_smp/src/tx_thread_schedule.S b/port/threadx_smp/src/tx_thread_schedule.S index 736e261..38f891d 100644 --- a/port/threadx_smp/src/tx_thread_schedule.S +++ b/port/threadx_smp/src/tx_thread_schedule.S @@ -94,7 +94,7 @@ _tx_thread_schedule_thread: /* Atomically claim the thread's ready token so only one hart can dispatch this TCB at a time. */ addi t2, t1, TX_THREAD_SMP_LOCK_READY_BIT_OFFSET // Pickup lock/ready-bit address - amoswap.w.aq t3, x0, (t2) // Clear it and fetch prior state + AMOSWAP_AQ t3, x0, (t2) // Clear it and fetch prior state beqz t3, _tx_thread_schedule // If not ready, retry scheduling /* } @@ -115,7 +115,7 @@ _tx_thread_schedule_thread: and restart so the new selection is not missed. */ STORE x0, 0(t5) // Clear current thread pointer li t3, 1 // Rebuild ready token - amoswap.w.rl x0, t3, (t2) // Restore ready token with release ordering + AMOSWAP_RL x0, t3, (t2) // Restore ready token with release ordering j _tx_thread_schedule_loop // Restart scheduling _execute_pointer_did_not_change: diff --git a/port/threadx_smp/src/tx_thread_smp_current_state_get.S b/port/threadx_smp/src/tx_thread_smp_current_state_get.S index 4fea92a..4fa5ae1 100644 --- a/port/threadx_smp/src/tx_thread_smp_current_state_get.S +++ b/port/threadx_smp/src/tx_thread_smp_current_state_get.S @@ -8,7 +8,7 @@ _tx_thread_smp_current_state_get: csrr t0, mhartid // Pickup current hart ID la t1, _tx_thread_system_state // Base of per-hart system-state array - slli t0, t0, 2 // Build offset into array + slli t0, t0, LOG_REGBYTES // Build offset into array add t1, t1, t0 // Select this hart's slot - LWU a0, 0(t1) // Return current system state + LOAD a0, 0(t1) // Return current system state ret diff --git a/port/threadx_smp/src/tx_thread_smp_initialize_wait.S b/port/threadx_smp/src/tx_thread_smp_initialize_wait.S index 1f4e459..64b55c5 100644 --- a/port/threadx_smp/src/tx_thread_smp_initialize_wait.S +++ b/port/threadx_smp/src/tx_thread_smp_initialize_wait.S @@ -16,17 +16,21 @@ _tx_thread_smp_initialize_wait: beqz t0, _tx_thread_smp_initialize_done // Core 0 does not wait /* Build per-hart offsets for ULONG and pointer arrays. */ - slli t1, t0, 2 // ULONG array offset + slli t1, t0, LOG_REGBYTES // ULONG array offset slli t2, t0, LOG_REGBYTES // Pointer array offset /* Wait until ThreadX has acknowledged this hart by setting its system state to TX_INITIALIZE_IN_PROGRESS. */ li t3, 0xF0F0F0F0 // TX_INITIALIZE_IN_PROGRESS +#if __riscv_xlen == 64 + slli t3, t3, 32 + srli t3, t3, 32 +#endif la t4, _tx_thread_system_state // Base of system state array add t5, t4, t1 // This hart's system state slot _tx_thread_smp_wait_for_initialize: - LWU t6, 0(t5) // Pickup current hart's system state + LOAD t6, 0(t5) // Pickup current hart's system state bne t6, t3, _tx_thread_smp_wait_for_initialize /* Save the system stack pointer for this hart. */ @@ -38,15 +42,15 @@ _tx_thread_smp_wait_for_initialize: la t3, _tx_thread_smp_release_cores_flag // Release flag address _tx_thread_smp_wait_for_release: - LWU t6, 0(t3) // Pickup release flag + LOAD t6, 0(t3) // Pickup release flag beqz t6, _tx_thread_smp_wait_for_release /* Acknowledge the release by clearing this hart's system state. */ - sw x0, 0(t5) // Set this hart's system state to zero + STORE x0, 0(t5) // Set this hart's system state to zero /* Wait for core 0 to finish initialization. */ _tx_thread_smp_wait_for_core0: - LWU t6, 0(t4) // Pickup core 0 system state + LOAD t6, 0(t4) // Pickup core 0 system state bnez t6, _tx_thread_smp_wait_for_core0 /* Prepare interrupt state */ diff --git a/port/threadx_smp/src/tx_thread_smp_protect.S b/port/threadx_smp/src/tx_thread_smp_protect.S index 7762057..8057bda 100644 --- a/port/threadx_smp/src/tx_thread_smp_protect.S +++ b/port/threadx_smp/src/tx_thread_smp_protect.S @@ -18,23 +18,23 @@ _tx_thread_smp_protect: la t1, _tx_thread_smp_protection /* If this hart already owns protection, just nest the count. */ - LWU t3, 4(t1) // Pickup owning hart + LOAD t3, 1*REGBYTES(t1) // Pickup owning hart beq t3, t2, _owned // Already owned by this hart /* Try to get the protection. */ - LWU t4, 0(t1) // Pickup protection flag + LOAD t4, 0(t1) // Pickup protection flag bnez t4, _protection_busy // If set, protection is busy li t4, 1 // Build lock value - amoswap.w.aq t5, t4, (t1) // Attempt to get protection + AMOSWAP_AQ t5, t4, (t1) // Attempt to get protection bnez t5, _protection_busy // If old value != 0, retry fence rw, rw // Ensure lock acquisition is visible - sw t2, 4(t1) // Save owning hart + STORE t2, 1*REGBYTES(t1) // Save owning hart _owned: - LWU t5, 8(t1) // Pickup ownership count + LOAD t5, 2*REGBYTES(t1) // Pickup ownership count addi t5, t5, 1 // Increment ownership count - sw t5, 8(t1) // Store ownership count + STORE t5, 2*REGBYTES(t1) // Store ownership count fence rw, rw // Publish owner/count before return ret diff --git a/port/threadx_smp/src/tx_thread_smp_unprotect.S b/port/threadx_smp/src/tx_thread_smp_unprotect.S index eaa6be8..2a21a5a 100644 --- a/port/threadx_smp/src/tx_thread_smp_unprotect.S +++ b/port/threadx_smp/src/tx_thread_smp_unprotect.S @@ -17,14 +17,14 @@ _tx_thread_smp_unprotect: la t2, _tx_thread_smp_protection /* Only the owning hart may release the protection. */ - LWU t3, 4(t2) // Pickup owning hart + LOAD t3, 1*REGBYTES(t2) // Pickup owning hart bne t1, t3, _still_protected // Not owner, skip release /* Pickup and decrement the protection count. */ - LWU t3, 8(t2) // Pickup protection count + LOAD t3, 2*REGBYTES(t2) // Pickup protection count beqz t3, _still_protected // Already cleared addi t3, t3, -1 // Decrement protection count - sw t3, 8(t2) // Store new count + STORE t3, 2*REGBYTES(t2) // Store new count bnez t3, _still_protected // Still nested, stay protected /* If preemption is disabled, keep protection in force. */ @@ -34,8 +34,8 @@ _tx_thread_smp_unprotect: /* Release the protection. */ li t3, -1 // Invalid owner value - sw t3, 4(t2) // Mark owning hart invalid - amoswap.w.rl x0, x0, (t2) // Release protection flag + STORE t3, 1*REGBYTES(t2) // Mark owning hart invalid + AMOSWAP_RL x0, x0, (t2) // Release protection flag _still_protected: csrw mstatus, a0 // Restore interrupt posture diff --git a/port/threadx_smp/src/tx_thread_stack_build.S b/port/threadx_smp/src/tx_thread_stack_build.S index bc6fc73..8eef6dd 100644 --- a/port/threadx_smp/src/tx_thread_stack_build.S +++ b/port/threadx_smp/src/tx_thread_stack_build.S @@ -138,7 +138,7 @@ If floating point support: Stack Bottom: (higher memory address) */ - LOAD t0, TX_THREAD_STACK_END_OFFSET(a0) // Pickup end of stack area + LOAD t0, 4*REGBYTES(a0) // Pickup end of stack area andi t0, t0, -16 // Ensure 16-byte alignment /* Actually build the stack frame. */ @@ -224,6 +224,6 @@ If floating point support: STORE t0, 2*REGBYTES(a0) // Save stack pointer in thread's addi t1, x0, 1 // Build ready flag - sw t1, TX_THREAD_SMP_LOCK_READY_BIT_OFFSET(a0) // Set ready flag + STORE t1, TX_THREAD_SMP_LOCK_READY_BIT_OFFSET(a0) // Set ready flag ret // control block and return /* } */ diff --git a/port/threadx_smp/src/tx_thread_system_return.S b/port/threadx_smp/src/tx_thread_system_return.S index 5fc5811..f752462 100644 --- a/port/threadx_smp/src/tx_thread_system_return.S +++ b/port/threadx_smp/src/tx_thread_system_return.S @@ -136,7 +136,7 @@ _tx_thread_system_return: #endif csrr t5, mhartid // Pickup current hart ID - slli t6, t5, 2 // Build per-hart ULONG offset + slli t6, t5, LOG_REGBYTES // Build per-hart ULONG offset slli t5, t5, LOG_REGBYTES // Build per-hart pointer offset la t0, _tx_thread_current_ptr // Pickup base of current-thread array @@ -158,7 +158,7 @@ _tx_thread_system_return: la t4, _tx_timer_time_slice // Pickup base of time-slice array add t4, t4, t6 // Select this hart's time-slice slot - lw t3, 0(t4) // Pickup time slice value + LOAD t3, 0(t4) // Pickup time slice value la t2, _tx_thread_schedule // Pickup address of scheduling loop beqz t3, _tx_thread_dont_save_ts // If no time-slice, don't save it @@ -166,8 +166,8 @@ _tx_thread_system_return: /* _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; _tx_timer_time_slice = 0; */ - sw t3, TX_THREAD_TIME_SLICE_OFFSET(t1) // Save current time-slice for thread - sw x0, 0(t4) // Clear time-slice variable + STORE t3, 6*REGBYTES(t1) // Save current time-slice for thread + STORE x0, 0(t4) // Clear time-slice variable /* } */ _tx_thread_dont_save_ts: @@ -181,18 +181,18 @@ _tx_thread_dont_save_ts: fence rw, rw // Publish current-thread clear before ready token addi t3, t1, TX_THREAD_SMP_LOCK_READY_BIT_OFFSET // Pickup lock/ready-bit address li t4, 1 // Build ready token - amoswap.w.rl x0, t4, (t3) // Restore ready token + AMOSWAP_RL x0, t4, (t3) // Restore ready token /* Clear protection state. */ la t3, _tx_thread_preempt_disable // Pickup preempt-disable address sw x0, 0(t3) // Clear preempt disable flag la t3, _tx_thread_smp_protection // Pickup protection structure - sw x0, 8(t3) // Clear protection count + STORE x0, 2*REGBYTES(t3) // Clear protection count li t4, -1 // Build invalid owner value - sw t4, 4(t3) // Invalidate owning hart + STORE t4, 1*REGBYTES(t3) // Invalidate owning hart fence rw, rw // Ensure shared accesses complete before unlock - sw x0, 0(t3) // Clear protection in-force flag + STORE x0, 0(t3) // Clear protection in-force flag jr t2 // Return to thread scheduler /* } */ -- 2.49.1 From 9212d40d863b4eb182759ad1def03c7c1181c8df Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Thu, 2 Apr 2026 11:00:50 +0200 Subject: [PATCH 73/79] adds newest version of patched threadx tests --- third-party/threadx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third-party/threadx b/third-party/threadx index ad06189..c9ff0ce 160000 --- a/third-party/threadx +++ b/third-party/threadx @@ -1 +1 @@ -Subproject commit ad06189db3489b4a641106093ca2f365a346ae91 +Subproject commit c9ff0ce2c837a57f2bc5bf6d5d7b3876b01a5aa8 -- 2.49.1 From a6a6a034d6ac226c8f25bd349c1818dbe956d625 Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Thu, 2 Apr 2026 11:16:02 +0200 Subject: [PATCH 74/79] removes convoluted generation of offsets Ass ULONG is now 8 bytes on RV64, the SMP offset is the only one left, harcoding it as a magic number is simpler --- port/threadx_smp/CMakeLists.txt | 19 ------ .../threadx_smp/cmake/ThreadXSmpOffsets.cmake | 67 ------------------- port/threadx_smp/inc/tx_port.h | 32 ++++----- port/threadx_smp/src/tx_asm_offsets.c | 12 ---- 4 files changed, 16 insertions(+), 114 deletions(-) delete mode 100644 port/threadx_smp/cmake/ThreadXSmpOffsets.cmake delete mode 100644 port/threadx_smp/src/tx_asm_offsets.c diff --git a/port/threadx_smp/CMakeLists.txt b/port/threadx_smp/CMakeLists.txt index 1f2c4bd..6095b7e 100644 --- a/port/threadx_smp/CMakeLists.txt +++ b/port/threadx_smp/CMakeLists.txt @@ -1,8 +1,6 @@ cmake_minimum_required(VERSION 3.24) project(smp_demo LANGUAGES C ASM) -include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/ThreadXSmpOffsets.cmake) - set(THREADX_COMMON_SMP_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../third-party/threadx/common_smp) if(NOT EXISTS "${THREADX_COMMON_SMP_DIR}") message(FATAL_ERROR "could not find ThreadX SMP sources, is the submodule checked out?") @@ -31,19 +29,6 @@ set(THREADX_SMP_CUSTOM_SRC src/tx_timer_interrupt.c ) -threadx_smp_add_offsets( - TARGET threadx_smp_offsets - OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/generated - SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/src/tx_asm_offsets.c - INCLUDE_DIRS - ${THREADX_COMMON_SMP_DIR}/inc - ${THREADX_SMP_CUSTOM_INC} - DEPENDS - ${CMAKE_CURRENT_SOURCE_DIR}/inc/tx_port.h - ${THREADX_COMMON_SMP_DIR}/inc/tx_api.h - OUT_INCLUDE_DIR THREADX_SMP_GENERATED_INC_DIR -) - set(THREADX_SMP_SOURCES ${THREADX_COMMON_SMP_DIR}/src/tx_block_allocate.c ${THREADX_COMMON_SMP_DIR}/src/tx_block_pool_cleanup.c @@ -250,8 +235,4 @@ target_include_directories(threadx_smp PUBLIC ${THREADX_COMMON_SMP_DIR}/inc ${THREADX_SMP_CUSTOM_INC} ) -target_include_directories(threadx_smp PRIVATE - ${THREADX_SMP_GENERATED_INC_DIR} -) target_compile_definitions(threadx_smp PRIVATE TX_QUEUE_MESSAGE_MAX_SIZE=16) #This is addressed in PR #503 -add_dependencies(threadx_smp threadx_smp_offsets) diff --git a/port/threadx_smp/cmake/ThreadXSmpOffsets.cmake b/port/threadx_smp/cmake/ThreadXSmpOffsets.cmake deleted file mode 100644 index 1de4447..0000000 --- a/port/threadx_smp/cmake/ThreadXSmpOffsets.cmake +++ /dev/null @@ -1,67 +0,0 @@ -function(threadx_smp_add_offsets) - set(options) - set(oneValueArgs TARGET OUTPUT_DIR SOURCE OUT_INCLUDE_DIR) - set(multiValueArgs INCLUDE_DIRS COMPILE_DEFINITIONS DEPENDS) - cmake_parse_arguments(THREADX_SMP_OFFSETS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - if(NOT THREADX_SMP_OFFSETS_TARGET) - message(FATAL_ERROR "threadx_smp_add_offsets requires TARGET") - endif() - - if(NOT THREADX_SMP_OFFSETS_OUTPUT_DIR) - message(FATAL_ERROR "threadx_smp_add_offsets requires OUTPUT_DIR") - endif() - - if(NOT THREADX_SMP_OFFSETS_SOURCE) - message(FATAL_ERROR "threadx_smp_add_offsets requires SOURCE") - endif() - - if(NOT THREADX_SMP_OFFSETS_OUT_INCLUDE_DIR) - message(FATAL_ERROR "threadx_smp_add_offsets requires OUT_INCLUDE_DIR") - endif() - - set(threadx_smp_generate_script "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/GenerateAsmOffsets.cmake") - set(threadx_smp_offset_asm "${THREADX_SMP_OFFSETS_OUTPUT_DIR}/tx_asm_offsets.s") - set(threadx_smp_offset_inc "${THREADX_SMP_OFFSETS_OUTPUT_DIR}/tx_asm_offsets.inc") - - set(threadx_smp_offset_include_args ${THREADX_SMP_OFFSETS_INCLUDE_DIRS}) - list(TRANSFORM threadx_smp_offset_include_args PREPEND -I) - - set(threadx_smp_offset_define_args ${THREADX_SMP_OFFSETS_COMPILE_DEFINITIONS}) - list(TRANSFORM threadx_smp_offset_define_args PREPEND -D) - - set(threadx_smp_offset_cflags ${CMAKE_C_FLAGS}) - if(CMAKE_BUILD_TYPE) - string(TOUPPER "${CMAKE_BUILD_TYPE}" threadx_smp_build_type_upper) - list(APPEND threadx_smp_offset_cflags ${CMAKE_C_FLAGS_${threadx_smp_build_type_upper}}) - endif() - separate_arguments(threadx_smp_offset_cflags) - - add_custom_command( - OUTPUT ${threadx_smp_offset_inc} - BYPRODUCTS ${threadx_smp_offset_asm} - COMMAND ${CMAKE_COMMAND} -E make_directory ${THREADX_SMP_OFFSETS_OUTPUT_DIR} - COMMAND ${CMAKE_C_COMPILER} - ${CMAKE_C_COMPILER_ARG1} - ${threadx_smp_offset_cflags} - ${threadx_smp_offset_include_args} - ${threadx_smp_offset_define_args} - -S - -o ${threadx_smp_offset_asm} - ${THREADX_SMP_OFFSETS_SOURCE} - COMMAND ${CMAKE_COMMAND} - -DINPUT=${threadx_smp_offset_asm} - -DOUTPUT=${threadx_smp_offset_inc} - -P ${threadx_smp_generate_script} - DEPENDS - ${THREADX_SMP_OFFSETS_SOURCE} - ${threadx_smp_generate_script} - ${THREADX_SMP_OFFSETS_DEPENDS} - COMMAND_EXPAND_LISTS - VERBATIM - ) - - add_custom_target(${THREADX_SMP_OFFSETS_TARGET} DEPENDS ${threadx_smp_offset_inc}) - - set(${THREADX_SMP_OFFSETS_OUT_INCLUDE_DIR} ${THREADX_SMP_OFFSETS_OUTPUT_DIR} PARENT_SCOPE) -endfunction() diff --git a/port/threadx_smp/inc/tx_port.h b/port/threadx_smp/inc/tx_port.h index 00587fb..1b0f0c2 100644 --- a/port/threadx_smp/inc/tx_port.h +++ b/port/threadx_smp/inc/tx_port.h @@ -57,26 +57,26 @@ #ifdef __ASSEMBLER__ #if __riscv_xlen == 64 -#define SLL32 sllw -#define STORE sd -#define LOAD ld -#define LWU lwu -#define AMOSWAP_AQ amoswap.d.aq -#define AMOSWAP_RL amoswap.d.rl -#define LOG_REGBYTES 3 +#define SLL32 sllw +#define STORE sd +#define LOAD ld +#define LWU lwu +#define AMOSWAP_AQ amoswap.d.aq +#define AMOSWAP_RL amoswap.d.rl +#define LOG_REGBYTES 3 +#define TX_THREAD_SMP_LOCK_READY_BIT_OFFSET 312 // This changes if thread or timer internal extensions are used #else -#define SLL32 sll -#define STORE sw -#define LOAD lw -#define LWU lw -#define AMOSWAP_AQ amoswap.w.aq -#define AMOSWAP_RL amoswap.w.rl -#define LOG_REGBYTES 2 +#define SLL32 sll +#define STORE sw +#define LOAD lw +#define LWU lw +#define AMOSWAP_AQ amoswap.w.aq +#define AMOSWAP_RL amoswap.w.rl +#define LOG_REGBYTES 2 +#define TX_THREAD_SMP_LOCK_READY_BIT_OFFSET 168 // This changes if thread or timer internal extensions are used #endif #define REGBYTES (1 << LOG_REGBYTES) -#include "tx_asm_offsets.inc" - #else /*not __ASSEMBLER__ */ /************* Define ThreadX SMP constants. *************/ diff --git a/port/threadx_smp/src/tx_asm_offsets.c b/port/threadx_smp/src/tx_asm_offsets.c deleted file mode 100644 index 50324d1..0000000 --- a/port/threadx_smp/src/tx_asm_offsets.c +++ /dev/null @@ -1,12 +0,0 @@ -#include - -#include "tx_api.h" - -#define TX_ASM_OFFSET(symbol, value) __asm__ volatile("\n.ascii \"-->" #symbol " %c0\\n\"" : : "i"(value)) - -void tx_asm_offsets_generate(void) -{ - TX_ASM_OFFSET(TX_THREAD_STACK_END_OFFSET, offsetof(TX_THREAD, tx_thread_stack_end)); - TX_ASM_OFFSET(TX_THREAD_TIME_SLICE_OFFSET, offsetof(TX_THREAD, tx_thread_time_slice)); - TX_ASM_OFFSET(TX_THREAD_SMP_LOCK_READY_BIT_OFFSET, offsetof(TX_THREAD, tx_thread_smp_lock_ready_bit)); -} -- 2.49.1 From 8772df619f3414032a633a3b67092c80d3be32b1 Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Thu, 2 Apr 2026 11:38:16 +0200 Subject: [PATCH 75/79] adds 64 bit versions to regression --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 45cefb0..1491ade 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,5 +1,5 @@ def suites = ['threadx', 'smp'] -def presets = ['Debug32', 'Release32'] +def presets = ['Debug32', 'Release32', 'Debug', 'Release'] def simulatorImageLatest = 'git.minres.com/here/here-vp:latest' def threadxTestSimulator = '/usr/local/bin/riscv-vp' -- 2.49.1 From ffc49bc6a9726cbcadb7ea5e3ab17f85b075e014 Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Thu, 2 Apr 2026 14:25:58 +0200 Subject: [PATCH 76/79] small formatting --- .../threadx_smp/src/tx_initialize_low_level.S | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/port/threadx_smp/src/tx_initialize_low_level.S b/port/threadx_smp/src/tx_initialize_low_level.S index 48ed119..5a4951f 100644 --- a/port/threadx_smp/src/tx_initialize_low_level.S +++ b/port/threadx_smp/src/tx_initialize_low_level.S @@ -73,23 +73,24 @@ STORE x1, 28*REGBYTES(sp) // Store RA, 28*REGBYTES(because call will override ra [ra is a calle register in riscv]) - call _tx_thread_context_save + call _tx_thread_context_save csrr a0, mcause csrr a1, mepc csrr a2, mtval addi sp, sp, -8 - STORE ra, 0(sp) - call trap_handler - LOAD ra, 0(sp) - addi sp, sp, 8 + STORE ra, 0(sp) + call trap_handler + LOAD ra, 0(sp) + addi sp, sp, 8 call _tx_thread_context_restore // it will nerver return .weak trap_handler -trap_handler: -1: - j 1b - .section .text + trap_handler: + 1: + j 1b + +.section .text /**************************************************************************/ /* */ /* FUNCTION RELEASE */ -- 2.49.1 From c0386ea4a1c847af77b13a93acb004405d985f1e Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Thu, 2 Apr 2026 14:26:24 +0200 Subject: [PATCH 77/79] makes bootup code work for any number of cores --- port/moonlight/src/bootup.c | 77 ++++++++++++++++++++++++++++++++++--- 1 file changed, 72 insertions(+), 5 deletions(-) diff --git a/port/moonlight/src/bootup.c b/port/moonlight/src/bootup.c index 5230ffb..aeaf38e 100644 --- a/port/moonlight/src/bootup.c +++ b/port/moonlight/src/bootup.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #ifdef __cplusplus @@ -16,6 +17,9 @@ #else #define EXTERN_C extern #endif +#if defined(TX_THREAD_SMP_MAX_CORES) && (TX_THREAD_SMP_MAX_CORES > 1) +#define BOOTUP_SMP_ENABLED +#endif // Generic C function pointer. typedef void(*function_t)(void) ; @@ -45,11 +49,58 @@ EXTERN_C void _start(void) __attribute__ ((naked,section(".text.boot"))); // Entry and exit points as C functions. EXTERN_C void _initialize(void) __attribute__ ((noreturn,section(".text.boot"))); +EXTERN_C void _secondary_sleep_forever(void) __attribute__ ((noreturn,section(".text.boot"))); EXTERN_C void _exit(int exit_code) __attribute__ ((noreturn,noinline,weak)); // Standard entry point, no arguments. extern int main(void); +#if defined BOOTUP_SMP_ENABLED +EXTERN_C void _secondary_initialize(void) __attribute__ ((noreturn,section(".text.boot"))); +EXTERN_C void _secondary_ipi_trap(void) __attribute__ ((naked,noreturn,section(".text.boot"))); +EXTERN_C void _tx_thread_smp_initialize_wait(void) __attribute__ ((noreturn)); + +void _secondary_initialize(void) { + csr_write_mtvec((uint_xlen_t)_secondary_ipi_trap); + csr_set_bits_mie(MIE_MSI_BIT_MASK); + csr_set_bits_mstatus(MSTATUS_MIE_BIT_MASK); + + __asm__ volatile ("wfi"); + + csr_clr_bits_mstatus(MSTATUS_MIE_BIT_MASK); + + _tx_thread_smp_initialize_wait(); +} + +void _secondary_ipi_trap(void) { +#if __riscv_xlen == 64 + __asm__ volatile ("addi sp, sp, -16;" + "sd ra, 8(sp);" + "call bootup_ipi_clear_handler;" + "ld ra, 8(sp);" + "addi sp, sp, 16;" + "mret"); +#else + __asm__ volatile ("addi sp, sp, -8;" + "sw ra, 4(sp);" + "call bootup_ipi_clear_handler;" + "lw ra, 4(sp);" + "addi sp, sp, 8;" + "mret"); +#endif +} + +static void bootup_wake_secondary_cores(void) { + for (UINT core = 1; core < TX_THREAD_SMP_MAX_CORES; ++core) { + send_ipi(core); + } +} + +__attribute__((used)) void bootup_ipi_clear_handler(void) { + set_aclint_msip(aclint, csr_read_mhartid(), 0); +} +#endif + // The linker script will place this in the reset entry point. // It will be 'called' with no stack or C runtime configuration. // tp will not be initialized @@ -61,7 +112,7 @@ void _start(void) { // The 'norelax' option is critical here. // Without 'norelax' the global pointer will // be loaded relative to the global pointer! - ".option norelax;" + ".option norelax;" "la gp, __global_pointer$;" ".option pop;" "la sp, _sp;" @@ -74,15 +125,20 @@ void _start(void) { "la t1, __stack_size;" "la sp, _sp;" // Loop incase M extension is not present + "mv t2, t0;" "1:;" - "beqz t0, 2f;" + "beqz t2, 2f;" "sub sp, sp, t1;" - "addi t0, t0, -1;" + "addi t2, t2, -1;" "j 1b;" "2:;" -#ifdef TX_THREAD_SMP_MAX_CORES - "call _tx_thread_smp_initialize_wait;" + "beqz t0, 3f;" +#ifdef BOOTUP_SMP_ENABLED + "jal zero, _secondary_initialize;" +#else + "jal zero, _secondary_sleep_forever;" #endif + "3:;" "jal zero, _initialize;" : /* output: none %0 */ : /* input: none */ @@ -107,6 +163,9 @@ void _initialize(void) { ++entry) { (*entry)(); } +#ifdef BOOTUP_SMP_ENABLED + bootup_wake_secondary_cores(); +#endif #ifdef __THREAD_LOCAL_STORAGE _set_tls(__tls_base) #endif @@ -120,6 +179,14 @@ void _initialize(void) { _exit(rc); } +void _secondary_sleep_forever(void) { + csr_clr_bits_mie(MIE_MTI_BIT_MASK); + csr_clr_bits_mstatus(MSTATUS_MIE_BIT_MASK); + while (1) { + __asm__ volatile ("wfi"); + } +} + // This should never be called. Report the exit code through HTIF and idle the CPU. void _exit(int exit_code) { uintptr_t htif_exit_code = (((uintptr_t)(unsigned int)exit_code) << 1) | 1u; -- 2.49.1 From ac1fb36e2c01cd7f9ced44fe6ea4843e8edd99aa Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Thu, 2 Apr 2026 14:49:43 +0200 Subject: [PATCH 78/79] adds MinSizeRel to the regression --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 1491ade..5946185 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,5 +1,5 @@ def suites = ['threadx', 'smp'] -def presets = ['Debug32', 'Release32', 'Debug', 'Release'] +def presets = ['Debug32', 'Release32', 'MinSizeRel32','Debug', 'Release', 'MinSizeRel'] def simulatorImageLatest = 'git.minres.com/here/here-vp:latest' def threadxTestSimulator = '/usr/local/bin/riscv-vp' -- 2.49.1 From f58aa42abd1cac6d23cfa935bbf14ed4f13bbd07 Mon Sep 17 00:00:00 2001 From: Eyck-Alexander Jentzsch Date: Thu, 2 Apr 2026 14:52:02 +0200 Subject: [PATCH 79/79] changes regression such that non-smp test run before smp tests --- Jenkinsfile | 43 +++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 5946185..d83ddde 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -46,32 +46,35 @@ def resolveImageCommit(String image) { } def runRegressionLane(String image, String simulatorPath, boolean allowFailure, List suiteNames, List presetNames) { - def parallelTasks = [:] - - for (String suite : suiteNames) { - for (String preset : presetNames) { - def suiteName = suite - def presetName = preset - def taskName = "${suiteName}-${presetName}" - parallelTasks[taskName] = { - if (allowFailure) { - catchError(buildResult: 'UNSTABLE', stageResult: 'UNSTABLE') { - runOneRegression(suiteName, presetName, simulatorPath) - } - } else { - runOneRegression(suiteName, presetName, simulatorPath) - } - } - } - } - docker.image(image).inside { sh ''' set -eu cmake --version ctest --version ''' - parallel parallelTasks + + for (String suite : suiteNames) { + def suiteName = suite + def parallelTasks = [:] + + for (String preset : presetNames) { + def presetName = preset + def taskName = "${suiteName}-${presetName}" + parallelTasks[taskName] = { + if (allowFailure) { + catchError(buildResult: 'UNSTABLE', stageResult: 'UNSTABLE') { + runOneRegression(suiteName, presetName, simulatorPath) + } + } else { + runOneRegression(suiteName, presetName, simulatorPath) + } + } + } + + stage("${suiteName} Suite") { + parallel parallelTasks + } + } } } -- 2.49.1