diff --git a/.github/workflows/regression_test.yml b/.github/workflows/regression_test.yml index c55a2c56e..72e4198ba 100644 --- a/.github/workflows/regression_test.yml +++ b/.github/workflows/regression_test.yml @@ -41,6 +41,23 @@ jobs: cmake_path: ./test/smp/cmake result_affix: SMP skip_deploy: true + riscv: + permissions: + contents: read + issues: read + checks: write + pull-requests: write + pages: write + id-token: write + uses: ./.github/workflows/regression_template.yml + with: + install_script: ./scripts/install_riscv.sh + build_script: ./scripts/build_tx_riscv.sh + test_script: ./scripts/test_tx_riscv.sh + cmake_path: ./test/tx/cmake/riscv + result_affix: RISC-V + skip_deploy: true + skip_coverage: true deploy: permissions: contents: read @@ -49,7 +66,7 @@ jobs: pull-requests: write pages: write id-token: write - needs: [tx, smp] + needs: [tx, smp, riscv] uses: ./.github/workflows/regression_template.yml with: skip_test: true diff --git a/common/src/tx_trace_object_register.c b/common/src/tx_trace_object_register.c index f7cd4a4a1..741896a5a 100644 --- a/common/src/tx_trace_object_register.c +++ b/common/src/tx_trace_object_register.c @@ -85,7 +85,7 @@ TX_TRACE_OBJECT_ENTRY *entry_ptr; /* Trace buffer is enabled, proceed. */ /* Pickup the total entries. */ - entries = _tx_trace_total_registry_entries; + entries = (UINT) _tx_trace_total_registry_entries; /* Determine if there are available entries in the registry. */ if (_tx_trace_available_registry_entries != ((ULONG) 0)) @@ -98,7 +98,7 @@ TX_TRACE_OBJECT_ENTRY *entry_ptr; loop_break = TX_FALSE; /* Loop to find available entry. */ - i = _tx_trace_registry_search_start; + i = (UINT) _tx_trace_registry_search_start; do { diff --git a/common/src/tx_trace_object_unregister.c b/common/src/tx_trace_object_unregister.c index 2166c9734..5d0330a5e 100644 --- a/common/src/tx_trace_object_unregister.c +++ b/common/src/tx_trace_object_unregister.c @@ -78,7 +78,7 @@ TX_TRACE_OBJECT_ENTRY *entry_ptr; /* Registry is setup, proceed. */ /* Pickup the total entries. */ - entries = _tx_trace_total_registry_entries; + entries = (UINT) _tx_trace_total_registry_entries; /* Loop to find available entry. */ for (i = ((ULONG) 0); i < entries; i++) diff --git a/ports/risc-v32/gnu/example_build/qemu_virt/build_libthreadx.sh b/ports/risc-v32/gnu/example_build/qemu_virt/build_libthreadx.sh index ba6b40222..f3d806441 100755 --- a/ports/risc-v32/gnu/example_build/qemu_virt/build_libthreadx.sh +++ b/ports/risc-v32/gnu/example_build/qemu_virt/build_libthreadx.sh @@ -1,5 +1,24 @@ #!/bin/bash +printf "y\n" | rm -rf ../../../../../build/ +rm -f kernel.elf + pushd ../../../../../ cmake -Bbuild -GNinja -DCMAKE_TOOLCHAIN_FILE=cmake/riscv32_gnu.cmake . cmake --build ./build/ -popd \ No newline at end of file +popd + +riscv32-unknown-elf-gcc \ + -march=rv32gc -mabi=ilp32d \ + -mcmodel=medany -O0 -g3 -Wall \ + -ffunction-sections -fdata-sections \ + -I../../../../../common/inc \ + -I../../inc \ + entry.s \ + tx_initialize_low_level.S \ + board.c uart.c hwtimer.c plic.c trap.c demo_threadx.c \ + -L../../../../../build -lthreadx \ + -T link.lds -nostartfiles \ + -o kernel.elf + + +qemu-system-riscv32 -nographic -smp 1 -bios none -m 128M -machine virt -kernel kernel.elf diff --git a/ports/risc-v32/gnu/inc/tx_port.h b/ports/risc-v32/gnu/inc/tx_port.h index ed0146751..5348a02e3 100644 --- a/ports/risc-v32/gnu/inc/tx_port.h +++ b/ports/risc-v32/gnu/inc/tx_port.h @@ -271,6 +271,104 @@ UINT _tx_thread_interrupt_control(UIN #define TX_SEMAPHORE_DISABLE TX_DISABLE +/* Define automated coverage test extensions for the ThreadX regression test. */ + +#ifndef __ASSEMBLER__ + +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 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 /* TX_REGRESSION_TEST */ + +#endif /* __ASSEMBLER__ */ + + /* Define the version ID of ThreadX. This may be utilized by the application. */ #ifndef __ASSEMBLER__ diff --git a/ports/risc-v32/gnu/src/tx_thread_context_restore.S b/ports/risc-v32/gnu/src/tx_thread_context_restore.S index 73a07f61d..5fa80437d 100644 --- a/ports/risc-v32/gnu/src/tx_thread_context_restore.S +++ b/ports/risc-v32/gnu/src/tx_thread_context_restore.S @@ -284,7 +284,7 @@ _tx_thread_no_preempt_restore: csrr t1, mstatus li t2, 0x1888 // MPP(0x1800) | MPIE(0x80) | MIE(0x08) - li t3, 0x1800 // Set MPP to Machine mode + li t3, 0x1880 // Set MPP=Machine(0x1800) + MPIE(0x80) so mret re-enables MIE li t4, ~0x1888 // Clear mask for MPP/MPIE/MIE and t1, t1, t4 or t1, t1, t3 diff --git a/ports/risc-v64/gnu/example_build/qemu_virt/build_libthreadx.sh b/ports/risc-v64/gnu/example_build/qemu_virt/build_libthreadx.sh index 24a4c3ac4..35f7ac5aa 100755 --- a/ports/risc-v64/gnu/example_build/qemu_virt/build_libthreadx.sh +++ b/ports/risc-v64/gnu/example_build/qemu_virt/build_libthreadx.sh @@ -1,6 +1,24 @@ #!/bin/bash +printf "y\n" | rm -rf ../../../../../build/ +rm -f kernel.elf pushd ../../../../../ cmake -Bbuild -GNinja -DCMAKE_TOOLCHAIN_FILE=cmake/riscv64_gnu.cmake . cmake --build ./build/ popd + +riscv64-unknown-elf-gcc \ + -march=rv64gc -mabi=lp64d \ + -mcmodel=medany -O0 -g3 -Wall \ + -ffunction-sections -fdata-sections \ + -I../../../../../common/inc \ + -I../../inc \ + entry.s \ + tx_initialize_low_level.S \ + board.c uart.c hwtimer.c plic.c trap.c demo_threadx.c \ + -L../../../../../build -lthreadx \ + -T link.lds -nostartfiles \ + -o kernel.elf + + +qemu-system-riscv64 -nographic -smp 1 -bios none -m 128M -machine virt -kernel kernel.elf diff --git a/ports/risc-v64/gnu/inc/tx_port.h b/ports/risc-v64/gnu/inc/tx_port.h index ae617b397..ca37e47ab 100644 --- a/ports/risc-v64/gnu/inc/tx_port.h +++ b/ports/risc-v64/gnu/inc/tx_port.h @@ -272,6 +272,104 @@ UINT _tx_thread_interrupt_control(UIN #define TX_SEMAPHORE_DISABLE TX_DISABLE +/* Define automated coverage test extensions for the ThreadX regression test. */ + +#ifndef __ASSEMBLER__ + +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 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 /* TX_REGRESSION_TEST */ + +#endif /* __ASSEMBLER__ */ + + /* Define the version ID of ThreadX. This may be utilized by the application. */ #ifndef __ASSEMBLER__ diff --git a/ports/risc-v64/gnu/src/tx_thread_context_restore.S b/ports/risc-v64/gnu/src/tx_thread_context_restore.S index 6aae3981b..5a50f0656 100644 --- a/ports/risc-v64/gnu/src/tx_thread_context_restore.S +++ b/ports/risc-v64/gnu/src/tx_thread_context_restore.S @@ -281,7 +281,7 @@ _tx_thread_no_preempt_restore: csrr t1, mstatus li t2, 0x1888 // MPP(0x1800) | MPIE(0x80) | MIE(0x08) - li t3, 0x1800 // Set MPP to Machine mode + li t3, 0x1880 // Set MPP=Machine(0x1800) + MPIE(0x80) so mret re-enables MIE li t4, ~0x1888 // Clear mask for MPP/MPIE/MIE and t1, t1, t4 or t1, t1, t3 diff --git a/scripts/build_tx_riscv.sh b/scripts/build_tx_riscv.sh new file mode 100755 index 000000000..bad952dff --- /dev/null +++ b/scripts/build_tx_riscv.sh @@ -0,0 +1,15 @@ +#!/bin/bash +# Build RISC-V regression tests for both RV32 and RV64. +# Usage: build_tx_riscv.sh [all|] + +SCRIPT_DIR="$(dirname "$(realpath "$0")")" +RUN_SH="${SCRIPT_DIR}/../test/tx/cmake/riscv/run.sh" + +ARGS="${@:-all}" + +echo "=== Building RISC-V32 ===" +"$RUN_SH" riscv32 build $ARGS + +echo "" +echo "=== Building RISC-V64 ===" +"$RUN_SH" riscv64 build $ARGS diff --git a/scripts/install_riscv.sh b/scripts/install_riscv.sh new file mode 100755 index 000000000..9c2f2cdb8 --- /dev/null +++ b/scripts/install_riscv.sh @@ -0,0 +1,32 @@ +#!/bin/bash +# Install RISC-V bare-metal cross-compiler toolchain and QEMU for CI. +set -e + +RELEASE_TAG="2026.04.26" +BASE_URL="https://github.com/riscv-collab/riscv-gnu-toolchain/releases/download/${RELEASE_TAG}" +# Use ubuntu-24.04 binaries to match ubuntu-latest runners. +RV32_TARBALL="riscv32-elf-ubuntu-24.04-gcc.tar.xz" +RV64_TARBALL="riscv64-elf-ubuntu-24.04-gcc.tar.xz" + +echo "=== Installing QEMU and build tools ===" +sudo apt-get update -qq +sudo apt-get install -y -qq qemu-system-misc ninja-build cmake + +echo "=== Downloading RISC-V GCC toolchain (${RELEASE_TAG}) ===" + +# Both tarballs extract into riscv/ with non-overlapping prefixes +# (riscv32-unknown-elf-* and riscv64-unknown-elf-*). +for tarball in "$RV32_TARBALL" "$RV64_TARBALL"; do + echo "Downloading ${tarball} ..." + wget --no-verbose "${BASE_URL}/${tarball}" -O "/tmp/${tarball}" + sudo tar xJf "/tmp/${tarball}" -C /opt + rm "/tmp/${tarball}" +done + +TOOLCHAIN_BIN=/opt/riscv/bin +echo "$TOOLCHAIN_BIN" >> "$GITHUB_PATH" + +echo "=== Verifying installation ===" +"$TOOLCHAIN_BIN/riscv32-unknown-elf-gcc" --version | head -1 +"$TOOLCHAIN_BIN/riscv64-unknown-elf-gcc" --version | head -1 +qemu-system-riscv64 --version | head -1 diff --git a/scripts/test_tx_riscv.sh b/scripts/test_tx_riscv.sh new file mode 100755 index 000000000..7d35c53fc --- /dev/null +++ b/scripts/test_tx_riscv.sh @@ -0,0 +1,19 @@ +#!/bin/bash +# Run RISC-V regression tests for both RV32 and RV64 on QEMU. +# Usage: test_tx_riscv.sh [all|] + +SCRIPT_DIR="$(dirname "$(realpath "$0")")" +RUN_SH="${SCRIPT_DIR}/../test/tx/cmake/riscv/run.sh" + +ARGS="${@:-all}" + +exit_code=0 + +echo "=== Testing RISC-V32 ===" +CTEST_PARALLEL_LEVEL=4 "$RUN_SH" riscv32 test $ARGS || exit_code=$? + +echo "" +echo "=== Testing RISC-V64 ===" +CTEST_PARALLEL_LEVEL=4 "$RUN_SH" riscv64 test $ARGS || exit_code=$? + +exit $exit_code diff --git a/test/tx/cmake/riscv/CMakeLists.txt b/test/tx/cmake/riscv/CMakeLists.txt new file mode 100644 index 000000000..0352622d0 --- /dev/null +++ b/test/tx/cmake/riscv/CMakeLists.txt @@ -0,0 +1,79 @@ +cmake_minimum_required(VERSION 3.13 FATAL_ERROR) +cmake_policy(SET CMP0054 NEW) +cmake_policy(SET CMP0057 NEW) + +project(threadx_riscv_test LANGUAGES C ASM) + +# Build configurations (same defines as Linux tests, minus coverage instrumentation) +set(BUILD_CONFIGURATIONS default_build disable_notify_callbacks_build + stack_checking_build stack_checking_rand_fill_build trace_build) +set(CMAKE_CONFIGURATION_TYPES + ${BUILD_CONFIGURATIONS} + CACHE STRING "list of supported configuration types" FORCE) +set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + ${CMAKE_CONFIGURATION_TYPES}) +list(GET CMAKE_CONFIGURATION_TYPES 0 BUILD_TYPE) +if((NOT CMAKE_BUILD_TYPE) OR (NOT ("${CMAKE_BUILD_TYPE}" IN_LIST + CMAKE_CONFIGURATION_TYPES))) + set(CMAKE_BUILD_TYPE + "${BUILD_TYPE}" + CACHE STRING "Build Type of the project" FORCE) +endif() + +message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") +message(STATUS "Using toolchain file: ${CMAKE_TOOLCHAIN_FILE}.") +message(STATUS "THREADX_ARCH: ${THREADX_ARCH}") + +# Per-configuration compile definitions +set(default_build -DTX_QUEUE_MESSAGE_MAX_SIZE=32) +set(disable_notify_callbacks_build -DTX_QUEUE_MESSAGE_MAX_SIZE=32 -DTX_DISABLE_NOTIFY_CALLBACKS) +set(stack_checking_build -DTX_QUEUE_MESSAGE_MAX_SIZE=32 -DTX_ENABLE_STACK_CHECKING) +set(stack_checking_rand_fill_build -DTX_QUEUE_MESSAGE_MAX_SIZE=32 -DTX_ENABLE_STACK_CHECKING -DTX_ENABLE_RANDOM_NUMBER_STACK_FILLING) +set(trace_build -DTX_QUEUE_MESSAGE_MAX_SIZE=32 -DTX_ENABLE_EVENT_TRACE) + +add_compile_options( + -std=c99 + -O0 + -g3 + -fdiagnostics-color + -Werror + -ffunction-sections + -fdata-sections + -DTX_REGRESSION_TEST + -DTEST_STACK_SIZE_PRINTF=4096 + -DEXTERNAL_EXIT + ${${CMAKE_BUILD_TYPE}} +) + +enable_testing() + +# Add ThreadX library (uses the RISC-V toolchain set by CMAKE_TOOLCHAIN_FILE) +add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/../../../.. threadx) + +# Add BSP +add_subdirectory(bsp) + +# Add regression tests +add_subdirectory(regression) + +# Strict warning flags for the ThreadX library build +target_compile_options( + threadx + PRIVATE -Werror + -Wall + -Wextra + -pedantic + -fmessage-length=0 + -fsigned-char + -ffunction-sections + -fdata-sections + -Wunused + -Wuninitialized + -Wmissing-declarations + -Wconversion + -Wpointer-arith + -Wlogical-op + -Waggregate-return + -Wfloat-equal + $<$:-include$stdlib.h> +) diff --git a/test/tx/cmake/riscv/bsp/CMakeLists.txt b/test/tx/cmake/riscv/bsp/CMakeLists.txt new file mode 100644 index 000000000..a6b8171a1 --- /dev/null +++ b/test/tx/cmake/riscv/bsp/CMakeLists.txt @@ -0,0 +1,25 @@ +cmake_minimum_required(VERSION 3.13 FATAL_ERROR) + +# BSP library for RISC-V QEMU virt regression tests. +# Provides board init, UART, CLINT timer, PLIC, trap handler, +# newlib syscall stubs, and low-level ThreadX initialization. + +project(riscv_bsp LANGUAGES C ASM) + +set(BSP_DIR ${CMAKE_CURRENT_LIST_DIR}) + +add_library(riscv_bsp + ${BSP_DIR}/entry.S + ${BSP_DIR}/tx_initialize_low_level.S + ${BSP_DIR}/board.c + ${BSP_DIR}/trap.c + ${BSP_DIR}/hwtimer.c + ${BSP_DIR}/plic.c + ${BSP_DIR}/uart.c + ${BSP_DIR}/syscalls.c + ${BSP_DIR}/printf.c +) + +target_include_directories(riscv_bsp PUBLIC ${BSP_DIR}) +target_link_libraries(riscv_bsp PUBLIC azrtos::threadx) +target_compile_definitions(riscv_bsp PUBLIC EXTERNAL_EXIT) diff --git a/test/tx/cmake/riscv/bsp/board.c b/test/tx/cmake/riscv/bsp/board.c new file mode 100644 index 000000000..0891200e0 --- /dev/null +++ b/test/tx/cmake/riscv/bsp/board.c @@ -0,0 +1,30 @@ +/*************************************************************************** + * Copyright (c) 2026 10xEngineers + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + +#include "plic.h" +#include "hwtimer.h" +#include "uart.h" +#include +#include + +int board_init(void) +{ + int ret; + ret = plic_init(); + if (ret) + return ret; + ret = uart_init(); + if (ret) + return ret; + ret = hwtimer_init(); + if (ret) + return ret; + return 0; +} diff --git a/test/tx/cmake/riscv/bsp/csr.h b/test/tx/cmake/riscv/bsp/csr.h new file mode 100644 index 000000000..f3d15f824 --- /dev/null +++ b/test/tx/cmake/riscv/bsp/csr.h @@ -0,0 +1,84 @@ +/*************************************************************************** + * Copyright (c) 2026 10xEngineers + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + +#ifndef RISCV_CSR_H +#define RISCV_CSR_H + +/* Machine Status Register, mstatus */ +#define MSTATUS_MPP_MASK (3L << 11) +#define MSTATUS_MPP_M (3L << 11) +#define MSTATUS_MPP_S (1L << 11) +#define MSTATUS_MPP_U (0L << 11) +#define MSTATUS_MIE (1L << 3) +#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) +#define MIE_SSIE (1L << 1) +#define MIE_SEIE (1L << 9) + +#ifndef __ASSEMBLER__ + +#include + +static inline uintptr_t riscv_get_core(void) +{ + uintptr_t x; + __asm__ volatile("csrr %0, mhartid" : "=r" (x)); + return x; +} + +static inline uintptr_t riscv_get_mstatus(void) +{ + uintptr_t x; + __asm__ volatile("csrr %0, mstatus" : "=r" (x)); + return x; +} + +static inline void riscv_writ_mstatus(uintptr_t x) +{ + __asm__ volatile("csrw mstatus, %0" : : "r" (x)); +} + +static inline void riscv_mintr_on(void) +{ + uintptr_t mstatus = riscv_get_mstatus(); + mstatus |= MSTATUS_MIE; + riscv_writ_mstatus(mstatus); +} + +static inline void riscv_mintr_off(void) +{ + uintptr_t mstatus = riscv_get_mstatus(); + mstatus &= (~MSTATUS_MIE); + riscv_writ_mstatus(mstatus); +} + +static inline int riscv_mintr_get(void) +{ + uintptr_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(); +} + +#endif /* __ASSEMBLER__ */ + +#endif /* RISCV_CSR_H */ diff --git a/test/tx/cmake/riscv/bsp/entry.S b/test/tx/cmake/riscv/bsp/entry.S new file mode 100644 index 000000000..2f16c0525 --- /dev/null +++ b/test/tx/cmake/riscv/bsp/entry.S @@ -0,0 +1,75 @@ +/*************************************************************************** + * Copyright (c) 2026 10xEngineers + * + * 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 + **************************************************************************/ + +/* Boot code for RISC-V QEMU virt regression tests. + Works for both RV32 and RV64 (instructions are width-agnostic). */ + +.section .text.init +.align 4 +.global _start +.extern main +.extern _sysstack_end +.extern _bss_start +.extern _bss_end + +_start: + csrr t0, mhartid + bne t0, zero, _secondary_halt + + /* Zero all general-purpose registers. */ + li x1, 0 + li x2, 0 + li x3, 0 + li x4, 0 + li x5, 0 + li x6, 0 + li x7, 0 + li x8, 0 + li x9, 0 + li x10, 0 + li x11, 0 + li x12, 0 + li x13, 0 + li x14, 0 + li x15, 0 + li x16, 0 + li x17, 0 + li x18, 0 + li x19, 0 + li x20, 0 + li x21, 0 + li x22, 0 + li x23, 0 + li x24, 0 + li x25, 0 + li x26, 0 + li x27, 0 + li x28, 0 + li x29, 0 + li x30, 0 + li x31, 0 + + /* Set up stack pointer from linker symbol. */ + la sp, _sysstack_end + + /* Clear BSS. */ + la t0, _bss_start + la t1, _bss_end +_bss_clean: + bgeu t0, t1, _bss_done + sb zero, 0(t0) + addi t0, t0, 1 + j _bss_clean +_bss_done: + call main + +_secondary_halt: + wfi + j _secondary_halt diff --git a/test/tx/cmake/riscv/bsp/hwtimer.c b/test/tx/cmake/riscv/bsp/hwtimer.c new file mode 100644 index 000000000..99821f1ee --- /dev/null +++ b/test/tx/cmake/riscv/bsp/hwtimer.c @@ -0,0 +1,33 @@ +/*************************************************************************** + * Copyright (c) 2026 10xEngineers + * + * 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 "hwtimer.h" +#include + +#define CLINT (0x02000000L) +#define CLINT_TIME (CLINT + 0xBFF8) +#define CLINT_TIMECMP(id) (CLINT + 0x4000 + 8 * (id)) + +int hwtimer_init(void) +{ + uintptr_t hart = riscv_get_core(); + uint64_t time = *((volatile uint64_t *)CLINT_TIME); + *((volatile uint64_t *)CLINT_TIMECMP(hart)) = time + TICKNUM_PER_TIMER; + return 0; +} + +int hwtimer_handler(void) +{ + uintptr_t hart = riscv_get_core(); + uint64_t time = *((volatile uint64_t *)CLINT_TIME); + *((volatile uint64_t *)CLINT_TIMECMP(hart)) = time + TICKNUM_PER_TIMER; + return 0; +} diff --git a/test/tx/cmake/riscv/bsp/hwtimer.h b/test/tx/cmake/riscv/bsp/hwtimer.h new file mode 100644 index 000000000..e6b9efa4e --- /dev/null +++ b/test/tx/cmake/riscv/bsp/hwtimer.h @@ -0,0 +1,22 @@ +/*************************************************************************** + * Copyright (c) 2026 10xEngineers + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + +#ifndef RISCV_HWTIMER_H +#define RISCV_HWTIMER_H + +#include + +#define TICKNUM_PER_SECOND 10000000 +#define TICKNUM_PER_TIMER (TICKNUM_PER_SECOND / 10) + +int hwtimer_init(void); +int hwtimer_handler(void); + +#endif /* RISCV_HWTIMER_H */ diff --git a/test/tx/cmake/riscv/bsp/link.lds b/test/tx/cmake/riscv/bsp/link.lds new file mode 100644 index 000000000..42f08fb6e --- /dev/null +++ b/test/tx/cmake/riscv/bsp/link.lds @@ -0,0 +1,60 @@ +/*************************************************************************** + * Linker script for RISC-V QEMU virt regression tests. + * Provides 128 MB RAM starting at 0x80000000 (QEMU virt default). + * System stack is 16 KB to accommodate test framework overhead. + **************************************************************************/ + +OUTPUT_ARCH( "riscv" ) +ENTRY( _start ) + +MEMORY +{ + RAM (rwx) : ORIGIN = 0x80000000, LENGTH = 128M +} + +SECTIONS +{ + . = 0x80000000; + + .text : { + KEEP(*(.text.init)) + *(.text .text.*) + . = ALIGN(0x1000); + PROVIDE(etext = .); + } > RAM + + .rodata : { + . = ALIGN(16); + *(.srodata .srodata.*) + . = ALIGN(16); + *(.rodata .rodata.*) + } > RAM + + .data : { + . = ALIGN(16); + *(.sdata .sdata.*) + . = ALIGN(16); + *(.data .data.*) + } > RAM + + .bss : { + . = ALIGN(16); + _bss_start = .; + *(.sbss .sbss.*) + . = ALIGN(16); + *(.bss .bss.*) + *(COMMON) + _bss_end = .; + } > RAM + + .stack : { + . = ALIGN(4096); + _sysstack_start = .; + . += 0x4000; + _sysstack_end = .; + } > RAM + + PROVIDE(_end = .); + + ASSERT(. <= ORIGIN(RAM) + LENGTH(RAM), "Image exceeds available RAM") +} diff --git a/test/tx/cmake/riscv/bsp/plic.c b/test/tx/cmake/riscv/bsp/plic.c new file mode 100644 index 000000000..f19e5b3aa --- /dev/null +++ b/test/tx/cmake/riscv/bsp/plic.c @@ -0,0 +1,80 @@ +/*************************************************************************** + * Copyright (c) 2026 10xEngineers + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + +#include "plic.h" +#include + +irq_callback callbacks[MAX_CALLBACK_NUM]; + +void plic_irq_enable(int irqno) +{ + uintptr_t hart = riscv_get_core(); + *(uint32_t *)PLIC_MENABLE(hart) = (*(uint32_t *)PLIC_MENABLE(hart) | (1 << irqno)); +} + +void plic_irq_disable(int irqno) +{ + uintptr_t hart = riscv_get_core(); + *(uint32_t *)PLIC_MENABLE(hart) = (*(uint32_t *)PLIC_MENABLE(hart) & (~(1 << irqno))); +} + +void plic_prio_set(int irqno, int prio) +{ + PLIC_SET_PRIO(irqno, prio); +} + +int plic_prio_get(int irqno) +{ + return (int)PLIC_GET_PRIO(irqno); +} + +int plic_register_callback(int irqno, irq_callback callback) +{ + if (!(irqno >= 0 && irqno < MAX_CALLBACK_NUM)) + return -1; + callbacks[irqno] = callback; + return 0; +} + +int plic_unregister_callback(int irqno) +{ + return plic_register_callback(irqno, NULL); +} + +int plic_init(void) +{ + for (int i = 0; i < MAX_CALLBACK_NUM; i++) + { + callbacks[i] = NULL; + } + return 0; +} + +int plic_claim(void) +{ + uintptr_t hart = riscv_get_core(); + return (int)(*(uint32_t *)PLIC_MCLAIM(hart)); +} + +void plic_complete(int irqno) +{ + uintptr_t hart = riscv_get_core(); + *(uint32_t *)(PLIC_MCOMPLETE(hart)) = (uint32_t)irqno; +} + +int plic_irq_intr(void) +{ + int ret = -1; + int irqno = plic_claim(); + if (callbacks[irqno] != NULL) + ret = (callbacks[irqno])(irqno); + plic_complete(irqno); + return ret; +} diff --git a/test/tx/cmake/riscv/bsp/plic.h b/test/tx/cmake/riscv/bsp/plic.h new file mode 100644 index 000000000..e2734ea63 --- /dev/null +++ b/test/tx/cmake/riscv/bsp/plic.h @@ -0,0 +1,46 @@ +/*************************************************************************** + * Copyright (c) 2026 10xEngineers + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + +#ifndef RISCV_PLIC_H +#define RISCV_PLIC_H + +#include "csr.h" +#include + +#define PLIC 0x0c000000L +#define PLIC_PRIORITY (PLIC + 0x0) +#define PLIC_PENDING (PLIC + 0x1000) +#define PLIC_MENABLE(hart) (PLIC + 0x2000 + (hart) * 0x100) +#define PLIC_SENABLE(hart) (PLIC + 0x2080 + (hart) * 0x100) +#define PLIC_MPRIORITY(hart) (PLIC + 0x200000 + (hart) * 0x2000) +#define PLIC_SPRIORITY(hart) (PLIC + 0x201000 + (hart) * 0x2000) +#define PLIC_MCLAIM(hart) (PLIC + 0x200004 + (hart) * 0x2000) +#define PLIC_SCLAIM(hart) (PLIC + 0x201004 + (hart) * 0x2000) +#define PLIC_MCOMPLETE(hart) (PLIC + 0x200004 + (hart) * 0x2000) +#define PLIC_SCOMPLETE(hart) (PLIC + 0x201004 + (hart) * 0x2000) + +#define PLIC_GET_PRIO(irqno) (*(uint32_t *)(PLIC_PRIORITY + (irqno) * 4)) +#define PLIC_SET_PRIO(irqno, prio) (*(uint32_t *)(PLIC_PRIORITY + (irqno) * 4) = (prio)) + +#define MAX_CALLBACK_NUM 128 +typedef int (*irq_callback)(int irqno); + +void plic_irq_enable(int irqno); +void plic_irq_disable(int irqno); +int plic_prio_get(int irqno); +void plic_prio_set(int irqno, int prio); +int plic_register_callback(int irqno, irq_callback callback); +int plic_unregister_callback(int irqno); +int plic_init(void); +int plic_claim(void); +void plic_complete(int irqno); +int plic_irq_intr(void); + +#endif /* RISCV_PLIC_H */ diff --git a/test/tx/cmake/riscv/bsp/printf.c b/test/tx/cmake/riscv/bsp/printf.c new file mode 100644 index 000000000..4aa185cc3 --- /dev/null +++ b/test/tx/cmake/riscv/bsp/printf.c @@ -0,0 +1,168 @@ +/*************************************************************************** + * Copyright (c) 2026 10xEngineers + * + * 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 + **************************************************************************/ + +/* Minimal printf/puts for RISC-V QEMU virt regression tests. + Avoids linking newlib's stdio which is compiled with mcmodel=medlow + and causes relocation overflow on RV64 (DRAM at 0x80000000). + Only the format specifiers used by the test suite are supported: + %s, %d, %u, %lu, %ld, %%, and plain strings. */ + +#include +#include + +extern int _write(int fd, const char *buf, int count); + +static void print_str(const char *s) +{ + const char *p = s; + while (*p) + p++; + _write(1, s, (int)(p - s)); +} + +static void print_unsigned(unsigned long val) +{ + char buf[21]; + int i = 0; + + if (val == 0) + { + _write(1, "0", 1); + return; + } + + while (val > 0) + { + buf[i++] = (char)('0' + (int)(val % 10u)); + val /= 10u; + } + + /* Reverse. */ + for (int j = i - 1; j >= 0; j--) + _write(1, &buf[j], 1); +} + +static void print_hex(unsigned long val) +{ + static const char hex[] = "0123456789abcdef"; + char buf[17]; + int i = 0; + + if (val == 0) + { + _write(1, "0", 1); + return; + } + + while (val > 0) + { + buf[i++] = hex[val & 0xfu]; + val >>= 4; + } + + for (int j = i - 1; j >= 0; j--) + _write(1, &buf[j], 1); +} + +static void print_signed(long val) +{ + if (val < 0) + { + _write(1, "-", 1); + print_unsigned((unsigned long)(-val)); + } + else + { + print_unsigned((unsigned long)val); + } +} + +int printf(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + + const char *p = fmt; + const char *seg = p; + + while (*p) + { + if (*p == '%') + { + /* Flush preceding literal segment. */ + if (p > seg) + _write(1, seg, (int)(p - seg)); + + p++; + /* Check for 'l' length modifier. */ + int is_long = 0; + if (*p == 'l') + { + is_long = 1; + p++; + } + + switch (*p) + { + case 's': + print_str(va_arg(ap, const char *)); + break; + case 'd': + if (is_long) + print_signed(va_arg(ap, long)); + else + print_signed((long)va_arg(ap, int)); + break; + case 'u': + if (is_long) + print_unsigned(va_arg(ap, unsigned long)); + else + print_unsigned((unsigned long)va_arg(ap, unsigned int)); + break; + case 'x': + if (is_long) + print_hex(va_arg(ap, unsigned long)); + else + print_hex((unsigned long)va_arg(ap, unsigned int)); + break; + case '%': + _write(1, "%", 1); + break; + default: + /* Unknown specifier — print as-is. */ + _write(1, "%", 1); + if (is_long) + _write(1, "l", 1); + _write(1, p, 1); + break; + } + p++; + seg = p; + } + else + { + p++; + } + } + + /* Flush remaining literal segment. */ + if (p > seg) + _write(1, seg, (int)(p - seg)); + + va_end(ap); + return 0; +} + +int puts(const char *s) +{ + print_str(s); + _write(1, "\n", 1); + return 0; +} diff --git a/test/tx/cmake/riscv/bsp/syscalls.c b/test/tx/cmake/riscv/bsp/syscalls.c new file mode 100644 index 000000000..802fe8afa --- /dev/null +++ b/test/tx/cmake/riscv/bsp/syscalls.c @@ -0,0 +1,128 @@ +/*************************************************************************** + * Copyright (c) 2026 10xEngineers + * + * 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 + **************************************************************************/ + +/* Newlib system call stubs for RISC-V QEMU virt regression tests. + Provides _write (UART output for printf), _exit (QEMU test finisher), + and other minimal stubs needed by newlib. */ + +#include +#include +#include + +#define UART0_THR (*(volatile unsigned char *)0x10000000L) +#define UART0_LSR (*(volatile unsigned char *)0x10000005L) +#define LSR_TX_IDLE (1 << 5) + +/* QEMU virt sifive_test device for clean exit. */ +#define VIRT_TEST (*(volatile uint32_t *)0x100000L) +#define VIRT_TEST_PASS 0x5555u +#define VIRT_TEST_FAIL 0x3333u + +/* Called by testcontrol.c when EXTERNAL_EXIT is defined. */ +__attribute__((noreturn)) void external_exit(unsigned int code) +{ + if (code == 0) + VIRT_TEST = VIRT_TEST_PASS; + else + VIRT_TEST = (code << 16) | VIRT_TEST_FAIL; + + /* Should not reach here, but halt if it does. */ + while (1) + ; +} + +int _write(int fd, const char *buf, int count) +{ + (void)fd; + for (int i = 0; i < count; i++) + { + while ((UART0_LSR & LSR_TX_IDLE) == 0) + ; + UART0_THR = (unsigned char)buf[i]; + } + return count; +} + +extern char _end[]; +static char *heap_ptr = 0; + +void *_sbrk(int incr) +{ + if (heap_ptr == 0) + heap_ptr = _end; + + char *prev = heap_ptr; + heap_ptr += incr; + return prev; +} + +int _close(int fd) +{ + (void)fd; + errno = EBADF; + return -1; +} + +int _fstat(int fd, struct stat *st) +{ + (void)fd; + st->st_mode = S_IFCHR; + return 0; +} + +int _isatty(int fd) +{ + (void)fd; + return 1; +} + +int _lseek(int fd, int offset, int whence) +{ + (void)fd; + (void)offset; + (void)whence; + errno = ESPIPE; + return -1; +} + +int _read(int fd, char *buf, int count) +{ + (void)fd; + (void)buf; + (void)count; + return 0; +} + +__attribute__((noreturn)) void _exit(int code) +{ + external_exit((unsigned int)code); +} + +/* Override newlib's exit() to avoid pulling in __call_exitprocs and + __stdio_exit_handler (compiled with mcmodel=medlow). */ +__attribute__((noreturn)) void exit(int code) +{ + external_exit((unsigned int)code); +} + +/* Minimal rand/srand to avoid pulling newlib's version which uses + _impure_ptr (compiled with mcmodel=medlow, unreachable on RV64). */ +static unsigned long rand_seed = 1; + +int rand(void) +{ + rand_seed = rand_seed * 1103515245UL + 12345UL; + return (int)((rand_seed >> 16) & 0x7fff); +} + +void srand(unsigned int seed) +{ + rand_seed = seed; +} diff --git a/test/tx/cmake/riscv/bsp/trap.c b/test/tx/cmake/riscv/bsp/trap.c new file mode 100644 index 000000000..0ac7990ac --- /dev/null +++ b/test/tx/cmake/riscv/bsp/trap.c @@ -0,0 +1,84 @@ +/*************************************************************************** + * Copyright (c) 2026 10xEngineers + * + * 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 + **************************************************************************/ + +/* Trap handler for RISC-V QEMU virt regression tests. + Calls test_interrupt_dispatch() BEFORE _tx_timer_interrupt() to match + the Linux port's behavior (generate_test_file.sh injection order). */ + +#include "csr.h" +#include +#include +#include "uart.h" +#include "hwtimer.h" +#include "plic.h" +#include +#include +#include + +/* Interrupt bit is the MSB of mcause */ +#define MCAUSE_INT_BIT ((uintptr_t)1 << (sizeof(uintptr_t) * 8 - 1)) + +#define OS_IS_INTERUPT(mcause) ((mcause) & MCAUSE_INT_BIT) +#define OS_IS_TICK_INT(mcause) ((mcause) == (MCAUSE_INT_BIT | 7u)) +#define OS_IS_SOFT_INT(mcause) ((mcause) == (MCAUSE_INT_BIT | 3u)) +#define OS_IS_EXT_INT(mcause) ((mcause) == (MCAUSE_INT_BIT | 11u)) + +extern void _tx_timer_interrupt(void); +extern void test_interrupt_dispatch(void) __attribute__((weak)); +extern void _exit(int code) __attribute__((noreturn)); + +void trap_handler(uintptr_t mcause, uintptr_t mepc, uintptr_t mtval) +{ + (void)mepc; + (void)mtval; + + if (OS_IS_INTERUPT(mcause)) + { + if (OS_IS_TICK_INT(mcause)) + { + hwtimer_handler(); + + /* Trace ISR enter event (matches Linux port behavior). */ + _tx_trace_isr_enter_insert(0); + + /* Call test ISR dispatch BEFORE timer interrupt (matches Linux). */ + if (test_interrupt_dispatch) + test_interrupt_dispatch(); + + _tx_timer_interrupt(); + + /* Trace ISR exit event. */ + _tx_trace_isr_exit_insert(0); + } + else if (OS_IS_EXT_INT(mcause)) + { + int ret = plic_irq_intr(); + if (ret) + { + uart_puts("[INTERRUPT]: handler irq error!"); + while (1) + ; + } + } + else + { + uart_puts("[INTERRUPT]: unhandled interrupt!"); + while (1) + ; + } + } + else + { + uart_puts("[EXCEPTION]: unhandled exception!"); + printf(" mcause=0x%lx mepc=0x%lx mtval=0x%lx\n", + (unsigned long)mcause, (unsigned long)mepc, (unsigned long)mtval); + _exit(1); + } +} diff --git a/test/tx/cmake/riscv/bsp/tx_initialize_low_level.S b/test/tx/cmake/riscv/bsp/tx_initialize_low_level.S new file mode 100644 index 000000000..8d474d66b --- /dev/null +++ b/test/tx/cmake/riscv/bsp/tx_initialize_low_level.S @@ -0,0 +1,100 @@ +/*************************************************************************** + * Copyright (c) 2026 10xEngineers + * + * 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 + **************************************************************************/ + +/* Trap entry and low-level init for RISC-V QEMU virt regression tests. + Supports both RV32 and RV64 via __riscv_xlen conditionals. */ + +#include "csr.h" + +#if __riscv_xlen == 64 +#define STORE sd +#define LOAD ld +#define REGBYTES 8 +#else +#define STORE sw +#define LOAD lw +#define REGBYTES 4 +#endif + + .section .text + .align 4 + +/**************************************************************************/ +/* trap_entry — saves context, calls C trap_handler, restores context. */ +/**************************************************************************/ + .global trap_entry + .extern trap_handler + .extern _tx_thread_context_restore + +trap_entry: +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + addi sp, sp, -(65 * REGBYTES) +#else + addi sp, sp, -(32 * REGBYTES) +#endif + + STORE x1, (28 * REGBYTES)(sp) + + call _tx_thread_context_save + + csrr a0, mcause + csrr a1, mepc + csrr a2, mtval + addi sp, sp, -REGBYTES + STORE ra, 0(sp) + call trap_handler + LOAD ra, 0(sp) + addi sp, sp, REGBYTES + + call _tx_thread_context_restore + +_trap_err: + wfi + j _trap_err + +/**************************************************************************/ +/* _tx_initialize_low_level — hardware init for QEMU virt. */ +/**************************************************************************/ + .section .text + .global _tx_initialize_low_level + .extern _end + .extern board_init + +_tx_initialize_low_level: + + la t0, _tx_thread_system_stack_ptr + STORE sp, 0(t0) + + la t0, _end + la t1, _tx_initialize_unused_memory + STORE t0, 0(t1) + + li t0, MSTATUS_MIE + csrrc zero, mstatus, t0 + li t0, (MSTATUS_MPP_M | MSTATUS_MPIE) + csrrs zero, mstatus, t0 + li t0, (MIE_MTIE | MIE_MSIE | MIE_MEIE) + csrrs zero, mie, t0 + +#ifdef __riscv_flen + li t0, MSTATUS_FS + csrrs zero, mstatus, t0 + fscsr x0 +#endif + + addi sp, sp, -REGBYTES + STORE ra, 0(sp) + call board_init + LOAD ra, 0(sp) + addi sp, sp, REGBYTES + + la t0, trap_entry + csrw mtvec, t0 + ret diff --git a/test/tx/cmake/riscv/bsp/uart.c b/test/tx/cmake/riscv/bsp/uart.c new file mode 100644 index 000000000..c1a6b64ba --- /dev/null +++ b/test/tx/cmake/riscv/bsp/uart.c @@ -0,0 +1,79 @@ +/*************************************************************************** + * Copyright (c) 2026 10xEngineers + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + +#include "uart.h" +#include "csr.h" +#include "plic.h" +#include + +#define Reg(reg) ((volatile unsigned char *)(UART0 + (reg))) + +#define RHR 0 +#define THR 0 +#define IER 1 +#define IER_RX_ENABLE (1<<0) +#define IER_TX_ENABLE (1<<1) +#define FCR 2 +#define FCR_FIFO_ENABLE (1<<0) +#define FCR_FIFO_CLEAR (3<<1) +#define LCR 3 +#define LCR_EIGHT_BITS (3<<0) +#define LCR_BAUD_LATCH (1<<7) +#define LSR 5 +#define LSR_RX_READY (1<<0) +#define LSR_TX_IDLE (1<<5) + +#define ReadReg(reg) (*(Reg(reg))) +#define WriteReg(reg, v) (*(Reg(reg)) = (v)) + +int uart_init(void) +{ + WriteReg(IER, 0x00); + WriteReg(LCR, LCR_BAUD_LATCH); + WriteReg(0, 0x03); + WriteReg(1, 0x00); + WriteReg(LCR, LCR_EIGHT_BITS); + WriteReg(FCR, FCR_FIFO_ENABLE | FCR_FIFO_CLEAR); + + plic_irq_enable(UART0_IRQ); + plic_prio_set(UART0_IRQ, 1); + + return 0; +} + +void uart_putc_nolock(int ch) +{ + while ((ReadReg(LSR) & LSR_TX_IDLE) == 0) + ; + WriteReg(THR, ch); +} + +int uart_putc(int ch) +{ + int intr_enable = riscv_mintr_get(); + riscv_mintr_off(); + uart_putc_nolock(ch); + riscv_mintr_restore(intr_enable); + return 1; +} + +int uart_puts(const char *str) +{ + int i; + int intr_enable = riscv_mintr_get(); + riscv_mintr_off(); + for (i = 0; str[i] != 0; i++) + { + uart_putc_nolock(str[i]); + } + uart_putc_nolock('\n'); + riscv_mintr_restore(intr_enable); + return i; +} diff --git a/test/tx/cmake/riscv/bsp/uart.h b/test/tx/cmake/riscv/bsp/uart.h new file mode 100644 index 000000000..765ec38f9 --- /dev/null +++ b/test/tx/cmake/riscv/bsp/uart.h @@ -0,0 +1,22 @@ +/*************************************************************************** + * Copyright (c) 2026 10xEngineers + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + +#ifndef RISCV_UART_H +#define RISCV_UART_H + +#define UART0 0x10000000L +#define UART0_IRQ 10 + +int uart_init(void); +int uart_putc(int ch); +void uart_putc_nolock(int ch); +int uart_puts(const char *str); + +#endif /* RISCV_UART_H */ diff --git a/test/tx/cmake/riscv/regression/CMakeLists.txt b/test/tx/cmake/riscv/regression/CMakeLists.txt new file mode 100644 index 000000000..5e381b894 --- /dev/null +++ b/test/tx/cmake/riscv/regression/CMakeLists.txt @@ -0,0 +1,165 @@ +cmake_minimum_required(VERSION 3.13 FATAL_ERROR) +cmake_policy(SET CMP0057 NEW) + +project(riscv_regression_test LANGUAGES C ASM) + +set(SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/../../../regression) +set(BSP_DIR ${CMAKE_CURRENT_LIST_DIR}/../bsp) + +# Determine QEMU arch from THREADX_ARCH +if(THREADX_ARCH STREQUAL "risc-v64") + set(QEMU_SYSTEM "qemu-system-riscv64") + set(XLEN_TAG "rv64") +else() + set(QEMU_SYSTEM "qemu-system-riscv32") + set(XLEN_TAG "rv32") +endif() + +set(regression_test_cases + ${SOURCE_DIR}/threadx_block_memory_basic_test.c + ${SOURCE_DIR}/threadx_block_memory_error_detection_test.c + ${SOURCE_DIR}/threadx_block_memory_information_test.c + ${SOURCE_DIR}/threadx_block_memory_prioritize_test.c + ${SOURCE_DIR}/threadx_block_memory_suspension_test.c + ${SOURCE_DIR}/threadx_block_memory_suspension_timeout_test.c + ${SOURCE_DIR}/threadx_block_memory_thread_terminate_test.c + ${SOURCE_DIR}/threadx_byte_memory_basic_test.c + ${SOURCE_DIR}/threadx_byte_memory_information_test.c + ${SOURCE_DIR}/threadx_byte_memory_prioritize_test.c + ${SOURCE_DIR}/threadx_byte_memory_suspension_test.c + ${SOURCE_DIR}/threadx_byte_memory_suspension_timeout_test.c + ${SOURCE_DIR}/threadx_byte_memory_thread_contention_test.c + ${SOURCE_DIR}/threadx_byte_memory_thread_terminate_test.c + ${SOURCE_DIR}/threadx_event_flag_basic_test.c + ${SOURCE_DIR}/threadx_event_flag_information_test.c + ${SOURCE_DIR}/threadx_event_flag_isr_set_clear_test.c + ${SOURCE_DIR}/threadx_event_flag_isr_wait_abort_test.c + ${SOURCE_DIR}/threadx_event_flag_single_thread_terminate_test.c + ${SOURCE_DIR}/threadx_event_flag_suspension_consume_test.c + ${SOURCE_DIR}/threadx_event_flag_suspension_different_bits_consume_test.c + ${SOURCE_DIR}/threadx_event_flag_suspension_different_bits_test.c + ${SOURCE_DIR}/threadx_event_flag_suspension_test.c + ${SOURCE_DIR}/threadx_event_flag_suspension_timeout_test.c + ${SOURCE_DIR}/threadx_event_flag_thread_terminate_test.c + ${SOURCE_DIR}/threadx_interrupt_control_test.c + ${SOURCE_DIR}/threadx_mutex_basic_test.c + ${SOURCE_DIR}/threadx_mutex_delete_test.c + ${SOURCE_DIR}/threadx_mutex_information_test.c + ${SOURCE_DIR}/threadx_mutex_nested_priority_inheritance_test.c + ${SOURCE_DIR}/threadx_mutex_no_preemption_test.c + ${SOURCE_DIR}/threadx_mutex_preemption_test.c + ${SOURCE_DIR}/threadx_mutex_priority_inheritance_test.c + ${SOURCE_DIR}/threadx_mutex_proritize_test.c + ${SOURCE_DIR}/threadx_mutex_suspension_timeout_test.c + ${SOURCE_DIR}/threadx_mutex_thread_terminate_test.c + ${SOURCE_DIR}/threadx_queue_basic_eight_word_test.c + ${SOURCE_DIR}/threadx_queue_basic_four_word_test.c + ${SOURCE_DIR}/threadx_queue_basic_one_word_test.c + ${SOURCE_DIR}/threadx_queue_basic_sixteen_word_test.c + ${SOURCE_DIR}/threadx_queue_basic_two_word_test.c + ${SOURCE_DIR}/threadx_queue_basic_max_message_size_test.c + ${SOURCE_DIR}/threadx_queue_empty_suspension_test.c + ${SOURCE_DIR}/threadx_queue_flush_no_suspension_test.c + ${SOURCE_DIR}/threadx_queue_flush_test.c + ${SOURCE_DIR}/threadx_queue_front_send_test.c + ${SOURCE_DIR}/threadx_queue_full_suspension_test.c + ${SOURCE_DIR}/threadx_queue_information_test.c + ${SOURCE_DIR}/threadx_queue_prioritize.c + ${SOURCE_DIR}/threadx_queue_suspension_timeout_test.c + ${SOURCE_DIR}/threadx_queue_thread_terminate_test.c + ${SOURCE_DIR}/threadx_semaphore_basic_test.c + ${SOURCE_DIR}/threadx_semaphore_ceiling_put_test.c + ${SOURCE_DIR}/threadx_semaphore_delete_test.c + ${SOURCE_DIR}/threadx_semaphore_information_test.c + ${SOURCE_DIR}/threadx_semaphore_non_preemption_test.c + ${SOURCE_DIR}/threadx_semaphore_preemption_test.c + ${SOURCE_DIR}/threadx_semaphore_prioritize.c + ${SOURCE_DIR}/threadx_semaphore_thread_terminate_test.c + ${SOURCE_DIR}/threadx_semaphore_timeout_test.c + ${SOURCE_DIR}/threadx_thread_basic_execution_test.c + ${SOURCE_DIR}/threadx_thread_basic_time_slice_test.c + ${SOURCE_DIR}/threadx_thread_completed_test.c + ${SOURCE_DIR}/threadx_thread_create_preemption_threshold_test.c + ${SOURCE_DIR}/threadx_thread_delayed_suspension_test.c + ${SOURCE_DIR}/threadx_thread_information_test.c + ${SOURCE_DIR}/threadx_thread_multi_level_preemption_threshold_test.c + ${SOURCE_DIR}/threadx_thread_multiple_non_current_test.c + ${SOURCE_DIR}/threadx_thread_multiple_sleep_test.c + ${SOURCE_DIR}/threadx_thread_multiple_suspension_test.c + ${SOURCE_DIR}/threadx_thread_multiple_time_slice_test.c + ${SOURCE_DIR}/threadx_thread_preemptable_suspension_test.c + ${SOURCE_DIR}/threadx_thread_preemption_change_test.c + ${SOURCE_DIR}/threadx_thread_priority_change.c + ${SOURCE_DIR}/threadx_thread_relinquish_test.c + ${SOURCE_DIR}/threadx_thread_reset_test.c + ${SOURCE_DIR}/threadx_thread_simple_sleep_non_clear_test.c + ${SOURCE_DIR}/threadx_thread_simple_sleep_test.c + ${SOURCE_DIR}/threadx_thread_simple_suspend_test.c + ${SOURCE_DIR}/threadx_thread_sleep_for_100ticks_test.c + ${SOURCE_DIR}/threadx_thread_sleep_terminate_test.c + ${SOURCE_DIR}/threadx_thread_stack_checking_test.c + ${SOURCE_DIR}/threadx_thread_terminate_delete_test.c + ${SOURCE_DIR}/threadx_thread_time_slice_change_test.c + ${SOURCE_DIR}/threadx_thread_wait_abort_and_isr_test.c + ${SOURCE_DIR}/threadx_thread_wait_abort_test.c + ${SOURCE_DIR}/threadx_time_get_set_test.c + ${SOURCE_DIR}/threadx_timer_activate_deactivate_test.c + ${SOURCE_DIR}/threadx_timer_deactivate_accuracy_test.c + ${SOURCE_DIR}/threadx_timer_information_test.c + ${SOURCE_DIR}/threadx_timer_large_timer_accuracy_test.c + ${SOURCE_DIR}/threadx_timer_multiple_accuracy_test.c + ${SOURCE_DIR}/threadx_timer_multiple_test.c + ${SOURCE_DIR}/threadx_timer_simple_test.c + # threadx_trace_basic_test.c excluded: references tx_saved_posture + # (the Linux port's interrupt save variable) which is named + # interrupt_save on RISC-V. +) + +# Tests that provide their own main() and must NOT link with testcontrol. +set(standalone_test_cases + ${SOURCE_DIR}/threadx_initialize_kernel_setup_test.c +) + +# Build testcontrol.c as a static library (no patched tx_initialize_low_level +# needed — the BSP provides it directly with test_interrupt_dispatch hook). +add_library(riscv_test_utility ${SOURCE_DIR}/testcontrol.c) +target_link_libraries(riscv_test_utility PUBLIC riscv_bsp azrtos::threadx) +target_compile_definitions(riscv_test_utility PUBLIC + CTEST BATCH_TEST EXTERNAL_EXIT TEST_STACK_SIZE_PRINTF=4096) +# Bare-metal newlib needs explicit stdlib.h for rand() used in some tests. +target_compile_options(riscv_test_utility PUBLIC -include stdlib.h) + +# Helper function to register a test executable with QEMU + CTest. +function(register_riscv_test test_name) + target_link_options(${test_name} PRIVATE + -T ${BSP_DIR}/link.lds -nostartfiles + -Wl,--gc-sections) + add_test( + NAME ${CMAKE_BUILD_TYPE}::${XLEN_TAG}::${test_name} + COMMAND ${QEMU_SYSTEM} -nographic -smp 1 -bios none -m 128M + -machine virt -kernel $ + ) + set_tests_properties(${CMAKE_BUILD_TYPE}::${XLEN_TAG}::${test_name} + PROPERTIES TIMEOUT 120) +endfunction() + +# Regular tests (linked with testcontrol). +foreach(test_case ${regression_test_cases}) + get_filename_component(test_name ${test_case} NAME_WE) + add_executable(${test_name} ${test_case}) + target_link_libraries(${test_name} PRIVATE + -Wl,--whole-archive riscv_bsp -Wl,--no-whole-archive + riscv_test_utility) + register_riscv_test(${test_name}) +endforeach() + +# Standalone tests (provide their own main, no testcontrol). +foreach(test_case ${standalone_test_cases}) + get_filename_component(test_name ${test_case} NAME_WE) + add_executable(${test_name} ${test_case}) + target_link_libraries(${test_name} PRIVATE + -Wl,--whole-archive riscv_bsp -Wl,--no-whole-archive + azrtos::threadx) + target_compile_options(${test_name} PRIVATE -include stdlib.h) + register_riscv_test(${test_name}) +endforeach() diff --git a/test/tx/cmake/riscv/run.sh b/test/tx/cmake/riscv/run.sh new file mode 100755 index 000000000..890efa452 --- /dev/null +++ b/test/tx/cmake/riscv/run.sh @@ -0,0 +1,151 @@ +#!/bin/bash + +set -e + +# RISC-V regression test build/test runner. +# Usage: run.sh [all| ...] +# arch: riscv32 or riscv64 + +function help() { + echo "Usage: $0 [all| ...]" + echo "" + echo " arch: riscv32 | riscv64" + echo " command: build | test" + echo "" + echo "Available build configurations:" + for build in ${build_configurations[*]}; do + echo " $build" + done + exit 1 +} + +function validate() { + for build in ${build_configurations[*]}; do + if [ "$1" == "$build" ]; then + return + fi + done + help +} + +function generate() { + local arch=$1 + local build=$2 + local toolchain_file + local build_dir + + if [ "$arch" == "riscv32" ]; then + toolchain_file="${REPO_ROOT}/cmake/riscv32_gnu.cmake" + else + toolchain_file="${REPO_ROOT}/cmake/riscv64_gnu.cmake" + fi + + build_dir="build/${arch}_${build}" + cmake -B"$build_dir" -GNinja \ + -DCMAKE_TOOLCHAIN_FILE="$toolchain_file" \ + -DCMAKE_BUILD_TYPE="$build" \ + . +} + +function build() { + local arch=$1 + local build=$2 + cmake --build "build/${arch}_${build}" +} + +function run_test() { + local arch=$1 + local build=$2 + local parallel_jobs=$3 + + pushd "build/${arch}_${build}" + [ -z "${CTEST_PARALLEL_LEVEL}" ] && parallel="-j${parallel_jobs}" + if [ -z "${CTEST_REPEAT_FAIL}" ]; then + repeat_fail=2 + else + repeat_fail=${CTEST_REPEAT_FAIL} + fi + ctest $parallel --timeout 1000 \ + -O "${arch}_${build}.txt" \ + -T test --no-compress-output \ + --test-output-size-passed 4194304 \ + --test-output-size-failed 4194304 \ + --output-on-failure \ + --repeat until-pass:${repeat_fail} \ + --output-junit "${arch}_${build}.xml" + popd +} + +# Determine repo root and script directory +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +REPO_ROOT="$(cd "${SCRIPT_DIR}/../../../.." && pwd)" +cd "$SCRIPT_DIR" + +# Parse build configurations from CMakeLists.txt +result=$(sed -n "/(BUILD_CONFIGURATIONS/,/)/p" CMakeLists.txt | sed ':label;N;s/\n/ /;b label' | grep -Pzo "[a-zA-Z0-9_]*build[a-zA-Z0-9_]*\s*" | tr -d '\0') +IFS=' ' +read -ra build_configurations <<< "$result" + +if [ $# -lt 2 ]; then + help +fi + +arch=$1 +shift + +if [ "$arch" != "riscv32" ] && [ "$arch" != "riscv64" ]; then + echo "Error: arch must be 'riscv32' or 'riscv64'" + help +fi + +command=$1 +shift + +if [ "$#" == "0" ]; then + builds=${build_configurations[0]} +elif [ "$*" == "all" ]; then + builds=${build_configurations[@]} +else + for item in $*; do + validate "$item" + done + builds=$* +fi + +if [ "$command" == "build" ]; then + for item in $builds; do + generate "$arch" "$item" + echo "" + done + + for item in $builds; do + echo "Building ${arch} ${item}" + build "$arch" "$item" + echo "" + done +elif [ "$command" == "test" ]; then + cores=$(nproc) + if [ -z "${CTEST_PARALLEL_LEVEL}" ]; then + build_counts=$(echo $builds | wc -w) + parallel_jobs=$(($cores / $build_counts)) + parallel_jobs=$(($parallel_jobs + 2)) + pids="" + for item in $builds; do + echo "Testing ${arch} ${item}" + run_test "$arch" "$item" "$parallel_jobs" & + pids+=" $!" + done + exit_code=0 + for p in $pids; do + wait $p || exit_code=$? + done + exit $exit_code + else + for item in $builds; do + echo "Testing ${arch} ${item}" + run_test "$arch" "$item" "$parallel_jobs" + done + fi +else + help +fi diff --git a/test/tx/regression/threadx_block_memory_basic_test.c b/test/tx/regression/threadx_block_memory_basic_test.c index 12e301d1a..b2dccbff6 100644 --- a/test/tx/regression/threadx_block_memory_basic_test.c +++ b/test/tx/regression/threadx_block_memory_basic_test.c @@ -217,8 +217,8 @@ CHAR *pointer; } /* Create block pools 0 and 1. */ - status = tx_block_pool_create(&pool_0, "pool 0", 100, pointer, 320); - pointer = pointer + 320; + status = tx_block_pool_create(&pool_0, "pool 0", 100, pointer, 340); + pointer = pointer + 340; /* Check status. */ if (status != TX_SUCCESS) @@ -228,8 +228,8 @@ CHAR *pointer; test_control_return(1); } - status = tx_block_pool_create(&pool_1, "pool 1", 100, pointer, 320); - pointer = pointer + 320; + status = tx_block_pool_create(&pool_1, "pool 1", 100, pointer, 340); + pointer = pointer + 340; /* Check status. */ if (status != TX_SUCCESS) diff --git a/test/tx/regression/threadx_block_memory_error_detection_test.c b/test/tx/regression/threadx_block_memory_error_detection_test.c index 7b7f2f610..e06c4d174 100644 --- a/test/tx/regression/threadx_block_memory_error_detection_test.c +++ b/test/tx/regression/threadx_block_memory_error_detection_test.c @@ -49,8 +49,8 @@ INT status; pointer = pointer + TEST_STACK_SIZE_PRINTF; /* Create block pool 0. */ - status = tx_block_pool_create(&pool_0, "pool 0", 100, pointer, 320); - pointer = pointer + 320; + status = tx_block_pool_create(&pool_0, "pool 0", 100, pointer, 340); + pointer = pointer + 340; #ifndef TX_DISABLE_ERROR_CHECKING /* skip this test and pretend it passed */ @@ -128,8 +128,8 @@ INT i; #ifndef TX_DISABLE_ERROR_CHECKING /* skip this test and pretend it passed */ - status = tx_block_pool_create(&pool_1, "pool 1", 100, pointer, 320); - pointer = pointer + 320; + status = tx_block_pool_create(&pool_1, "pool 1", 100, pointer, 340); + pointer = pointer + 340; /* Attempt to create a pool with an invalid size. */ status = _txe_block_pool_create(&pool_2, "pool 2", 100, pointer, 320, 777777); diff --git a/test/tx/regression/threadx_block_memory_suspension_test.c b/test/tx/regression/threadx_block_memory_suspension_test.c index da14f454e..3b49f5d9b 100644 --- a/test/tx/regression/threadx_block_memory_suspension_test.c +++ b/test/tx/regression/threadx_block_memory_suspension_test.c @@ -83,8 +83,8 @@ CHAR *pointer; } /* Create block pool. */ - status = tx_block_pool_create(&pool_0, "pool 0", 100, pointer, 320); - pointer = pointer + 320; + status = tx_block_pool_create(&pool_0, "pool 0", 100, pointer, 340); + pointer = pointer + 340; /* Check status. */ if (status != TX_SUCCESS) diff --git a/test/tx/regression/threadx_block_memory_suspension_timeout_test.c b/test/tx/regression/threadx_block_memory_suspension_timeout_test.c index dc460662d..a2be3c6c9 100644 --- a/test/tx/regression/threadx_block_memory_suspension_timeout_test.c +++ b/test/tx/regression/threadx_block_memory_suspension_timeout_test.c @@ -84,8 +84,8 @@ CHAR *pointer; } /* Create block pool. */ - status = tx_block_pool_create(&pool_0, "pool 0", 100, pointer, 320); - pointer = pointer + 320; + status = tx_block_pool_create(&pool_0, "pool 0", 100, pointer, 340); + pointer = pointer + 340; /* Check status. */ if (status != TX_SUCCESS) diff --git a/test/tx/regression/threadx_block_memory_thread_terminate_test.c b/test/tx/regression/threadx_block_memory_thread_terminate_test.c index 6064891df..c568d9a20 100644 --- a/test/tx/regression/threadx_block_memory_thread_terminate_test.c +++ b/test/tx/regression/threadx_block_memory_thread_terminate_test.c @@ -69,8 +69,8 @@ CHAR *pointer; } /* Create block pool. */ - status = tx_block_pool_create(&pool_0, "pool 0", 100, pointer, 320); - pointer = pointer + 320; + status = tx_block_pool_create(&pool_0, "pool 0", 100, pointer, 340); + pointer = pointer + 340; /* Check status. */ if (status != TX_SUCCESS) diff --git a/test/tx/regression/threadx_byte_memory_basic_test.c b/test/tx/regression/threadx_byte_memory_basic_test.c index 36f413317..d153ccafa 100644 --- a/test/tx/regression/threadx_byte_memory_basic_test.c +++ b/test/tx/regression/threadx_byte_memory_basic_test.c @@ -4,6 +4,15 @@ #include #include "tx_api.h" +/* Per-allocation overhead in byte pools: next pointer + ALIGN_TYPE alignment marker. */ +#define BYTE_POOL_OVERHEAD (sizeof(UCHAR *) + sizeof(ALIGN_TYPE)) + +/* Pool size for pool_0: exactly fits 3 allocations of 24 bytes with no room for a 4th. */ +#define POOL_0_SIZE (ULONG)(3u * (24u + BYTE_POOL_OVERHEAD) + BYTE_POOL_OVERHEAD) + +/* Pool size for pool_4: must accommodate 3 allocations of 84 bytes (tightest pattern). */ +#define POOL_4_SIZE (ULONG)(3u * (84u + BYTE_POOL_OVERHEAD) + BYTE_POOL_OVERHEAD + 16u) + typedef struct BYTE_MEMORY_TEST_STRUCT { ULONG first; @@ -256,8 +265,8 @@ CHAR *pointer; } /* Create byte pools 0 and 1. */ - status = tx_byte_pool_create(&pool_0, "pool 0", pointer, 108); - pointer = pointer + 108; + status = tx_byte_pool_create(&pool_0, "pool 0", pointer, POOL_0_SIZE); + pointer = pointer + POOL_0_SIZE; /* Check status. */ if (status != TX_SUCCESS) @@ -279,9 +288,9 @@ CHAR *pointer; } /* Test for search pointer issue on wrapped seach with prior block to search pointer merged. */ - status = tx_byte_pool_create(&pool_4, "pool 4", pointer, 300); + status = tx_byte_pool_create(&pool_4, "pool 4", pointer, POOL_4_SIZE); pool_4_memory = pointer; - pointer = pointer + 300; + pointer = pointer + POOL_4_SIZE; /* Check status. */ if (status != TX_SUCCESS) @@ -865,7 +874,7 @@ UCHAR *save_search; } /* Move the search pointer to the third block to exercise that code in the byte search algorithm. */ - pool_0.tx_byte_pool_search = (UCHAR *) pointer_3-8; + pool_0.tx_byte_pool_search = (UCHAR *) pointer_3 - BYTE_POOL_OVERHEAD; /* Now allocate the block again. */ status = tx_byte_allocate(&pool_0, (VOID **) &pointer_2, 24, TX_NO_WAIT); @@ -885,7 +894,7 @@ UCHAR *save_search; status += tx_byte_release(pointer_1); /* Move the search pointer to the third block to exercise that code in the byte search algorithm. */ - pool_0.tx_byte_pool_search = (UCHAR *) pointer_3-8; + pool_0.tx_byte_pool_search = (UCHAR *) pointer_3 - BYTE_POOL_OVERHEAD; /* Allocate a large block to force the search pointer update. */ status = tx_byte_allocate(&pool_0, (VOID **) &pointer_3, 88, TX_NO_WAIT); @@ -961,7 +970,7 @@ UCHAR *save_search; } /* Create pool 4. */ - status = tx_byte_pool_create(&pool_4, "pool 4", pool_4_memory, 300); + status = tx_byte_pool_create(&pool_4, "pool 4", pool_4_memory, POOL_4_SIZE); /* Check status. */ if (status != TX_SUCCESS) diff --git a/test/tx/regression/threadx_event_flag_suspension_timeout_test.c b/test/tx/regression/threadx_event_flag_suspension_timeout_test.c index 149cb58fa..f4ee39955 100644 --- a/test/tx/regression/threadx_event_flag_suspension_timeout_test.c +++ b/test/tx/regression/threadx_event_flag_suspension_timeout_test.c @@ -178,17 +178,12 @@ UINT status; /* Sleep for 63 ticks. */ tx_thread_sleep(63); - /* Check the run counters. */ - if (((thread_1_counter != 32) -#ifdef __linux__ - && (thread_1_counter != 33) /* Depending on the starting time, thread 1 can run either 32 or 33 rounds. */ -#endif - ) || - ((thread_2_counter != 13) -#ifdef __linux__ - && (thread_2_counter != 14) /* When CPU starves, the thread 2 can run 14 ronuds. */ -#endif - )) + /* Check the run counters. Depending on the starting time relative to + the tick boundary, thread 1 can run either 32 or 33 rounds and + thread 2 can run 13 or 14 rounds. This variance applies to any + platform with non-deterministic tick alignment (Linux, QEMU, etc.). */ + if (((thread_1_counter != 32) && (thread_1_counter != 33)) || + ((thread_2_counter != 13) && (thread_2_counter != 14))) { /* Event flag error. */