diff options
Diffstat (limited to 'test/tx/cmake')
24 files changed, 1637 insertions, 46 deletions
diff --git a/test/tx/cmake/CMakeLists.txt b/test/tx/cmake/CMakeLists.txt index 2dd16ade..97b3615b 100644 --- a/test/tx/cmake/CMakeLists.txt +++ b/test/tx/cmake/CMakeLists.txt @@ -2,8 +2,16 @@ cmake_minimum_required(VERSION 3.13 FATAL_ERROR) cmake_policy(SET CMP0054 NEW) cmake_policy(SET CMP0057 NEW) +if((DEFINED THREADX_ARCH) AND ((THREADX_ARCH STREQUAL "win32") OR (THREADX_ARCH STREQUAL "win64"))) + set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) +endif() + project(threadx_test LANGUAGES C) +set(CMAKE_C_STANDARD 99) +set(CMAKE_C_STANDARD_REQUIRED ON) +set(CMAKE_C_EXTENSIONS OFF) + # Set build configurations set(BUILD_CONFIGURATIONS default_build_coverage disable_notify_callbacks_build stack_checking_build stack_checking_rand_fill_build trace_build) @@ -22,24 +30,30 @@ endif() message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") message(STATUS "Using toolchain file: ${CMAKE_TOOLCHAIN_FILE}.") -set(default_build_coverage -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) +set(default_build_coverage TX_QUEUE_MESSAGE_MAX_SIZE=32) +set(disable_notify_callbacks_build TX_QUEUE_MESSAGE_MAX_SIZE=32 TX_DISABLE_NOTIFY_CALLBACKS) +set(stack_checking_build TX_QUEUE_MESSAGE_MAX_SIZE=32 TX_ENABLE_STACK_CHECKING) +set(stack_checking_rand_fill_build TX_QUEUE_MESSAGE_MAX_SIZE=32 TX_ENABLE_STACK_CHECKING TX_ENABLE_RANDOM_NUMBER_STACK_FILLING) +set(trace_build TX_QUEUE_MESSAGE_MAX_SIZE=32 TX_ENABLE_EVENT_TRACE) -add_compile_options( - -m32 - -std=c99 - -ggdb - -g3 - -gdwarf-2 - -fdiagnostics-color - -Werror - -DTX_REGRESSION_TEST - -DTEST_STACK_SIZE_PRINTF=4096 +add_compile_definitions( + TX_REGRESSION_TEST + TEST_STACK_SIZE_PRINTF=4096 ${${CMAKE_BUILD_TYPE}}) -add_link_options(-m32) + +if(MSVC) + add_compile_options(/W3 /Zi) + add_link_options(/DEBUG /INCREMENTAL:NO) +else() + add_compile_options( + -m32 + -ggdb + -g3 + -gdwarf-2 + -fdiagnostics-color + -Werror) + add_link_options(-m32) +endif() enable_testing() @@ -49,26 +63,32 @@ add_subdirectory(samples) # Coverage if(CMAKE_BUILD_TYPE MATCHES ".*_coverage") - target_compile_options(threadx PRIVATE -fprofile-arcs -ftest-coverage) - target_link_options(threadx PRIVATE -fprofile-arcs -ftest-coverage) + if(NOT MSVC) + target_compile_options(threadx PRIVATE -fprofile-arcs -ftest-coverage) + target_link_options(threadx PRIVATE -fprofile-arcs -ftest-coverage) + endif() endif() -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 - # -Wshadow - -Wlogical-op - -Waggregate-return - -Wfloat-equal) +if(MSVC) + target_compile_options(threadx PRIVATE /W3) +else() + 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 + # -Wshadow + -Wlogical-op + -Waggregate-return + -Wfloat-equal) +endif() diff --git a/test/tx/cmake/coverage.sh b/test/tx/cmake/coverage.sh index ae2c7a4b..6dda78b3 100755 --- a/test/tx/cmake/coverage.sh +++ b/test/tx/cmake/coverage.sh @@ -1,4 +1,15 @@ #!/bin/bash +############################################################################## +# Copyright (c) 2024 Microsoft Corporation +# Copyright (c) 2026 Eclipse ThreadX contributors +# +# This program and the accompanying materials are made available under the +# terms of the MIT License which is available at +# https://opensource.org/licenses/MIT. +# +# SPDX-License-Identifier: MIT +############################################################################## + set -e diff --git a/test/tx/cmake/regression/CMakeLists.txt b/test/tx/cmake/regression/CMakeLists.txt index d60743fa..34fba1b2 100644 --- a/test/tx/cmake/regression/CMakeLists.txt +++ b/test/tx/cmake/regression/CMakeLists.txt @@ -4,6 +4,14 @@ cmake_policy(SET CMP0057 NEW) project(regression_test LANGUAGES C) set(SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/../../regression) +set(REPO_ROOT ${CMAKE_CURRENT_LIST_DIR}/../../../..) +set(PORT_LOW_LEVEL_SOURCE ${REPO_ROOT}/ports/${THREADX_ARCH}/${THREADX_TOOLCHAIN}/src/tx_initialize_low_level.c) +set(GENERATED_LOW_LEVEL_SOURCE ${CMAKE_CURRENT_BINARY_DIR}/tx_initialize_low_level.c) +set(TESTCONTROL_WEAK_DEFAULTS_SOURCE ${REPO_ROOT}/test/shared/regression/testcontrol_weak_defaults.c) + +if(NOT EXISTS ${PORT_LOW_LEVEL_SOURCE}) + message(FATAL_ERROR "Unable to locate tx_initialize_low_level.c for ${THREADX_ARCH}/${THREADX_TOOLCHAIN}") +endif() set(regression_test_cases ${SOURCE_DIR}/threadx_block_memory_basic_test.c @@ -104,19 +112,38 @@ set(regression_test_cases ${SOURCE_DIR}/threadx_initialize_kernel_setup_test.c) add_custom_command( - OUTPUT ${SOURCE_DIR}/tx_initialize_low_level.c - COMMAND bash ${CMAKE_CURRENT_LIST_DIR}/generate_test_file.sh + OUTPUT ${GENERATED_LOW_LEVEL_SOURCE} + COMMAND ${CMAKE_COMMAND} + -DSOURCE_FILE=${PORT_LOW_LEVEL_SOURCE} + -DOUTPUT_FILE=${GENERATED_LOW_LEVEL_SOURCE} + -P ${CMAKE_CURRENT_LIST_DIR}/generate_test_file.cmake + DEPENDS ${PORT_LOW_LEVEL_SOURCE} ${CMAKE_CURRENT_LIST_DIR}/generate_test_file.cmake COMMENT "Generating tx_initialize_low_level.c for test") -add_library(test_utility ${SOURCE_DIR}/tx_initialize_low_level.c - ${SOURCE_DIR}/testcontrol.c) -target_link_libraries(test_utility PUBLIC azrtos::threadx) -target_compile_definitions(test_utility PUBLIC CTEST BATCH_TEST - TEST_STACK_SIZE_PRINTF=4096) +add_library(test_weak_defaults OBJECT ${TESTCONTROL_WEAK_DEFAULTS_SOURCE}) +target_compile_definitions(test_weak_defaults PRIVATE CTEST BATCH_TEST + TEST_STACK_SIZE_PRINTF=4096) +target_link_libraries(test_weak_defaults PRIVATE azrtos::threadx) + +add_library(test_utility OBJECT ${GENERATED_LOW_LEVEL_SOURCE} + ${SOURCE_DIR}/testcontrol.c) +target_compile_definitions(test_utility PRIVATE CTEST BATCH_TEST + TEST_STACK_SIZE_PRINTF=4096) +target_link_libraries(test_utility PRIVATE azrtos::threadx) 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 test_utility) + + if(test_name STREQUAL "threadx_initialize_kernel_setup_test") + add_executable(${test_name} ${test_case} $<TARGET_OBJECTS:test_weak_defaults>) + else() + add_executable(${test_name} ${test_case} + $<TARGET_OBJECTS:test_utility> + $<TARGET_OBJECTS:test_weak_defaults>) + target_compile_definitions(${test_name} PRIVATE CTEST BATCH_TEST + TEST_STACK_SIZE_PRINTF=4096) + endif() + + target_link_libraries(${test_name} PRIVATE azrtos::threadx) add_test(${CMAKE_BUILD_TYPE}::${test_name} ${test_name}) endforeach() diff --git a/test/tx/cmake/regression/generate_test_file.cmake b/test/tx/cmake/regression/generate_test_file.cmake new file mode 100644 index 00000000..d37e744b --- /dev/null +++ b/test/tx/cmake/regression/generate_test_file.cmake @@ -0,0 +1,42 @@ +if(NOT DEFINED SOURCE_FILE) + message(FATAL_ERROR "SOURCE_FILE is required") +endif() + +if(NOT DEFINED OUTPUT_FILE) + message(FATAL_ERROR "OUTPUT_FILE is required") +endif() + +file(STRINGS "${SOURCE_FILE}" FILE_LINES) + +set(UPDATED_FILE_CONTENTS "") +set(DISPATCH_DECLARATION "VOID test_interrupt_dispatch(VOID);") +set(DISPATCH_CALL "test_interrupt_dispatch();") +set(DECLARATION_INSERTED FALSE) +set(CALL_INSERTED FALSE) + +foreach(FILE_LINE IN LISTS FILE_LINES) + if((NOT DECLARATION_INSERTED) AND + ((FILE_LINE MATCHES "^void[ \t]+\\*_tx_linux_timer_interrupt\\(void \\*p\\);[ \t]*$") + OR + (FILE_LINE MATCHES "^VOID CALLBACK[ \t]+_tx_win32_timer_interrupt\\(UINT wTimerID, UINT msg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2\\);[ \t]*$"))) + string(APPEND UPDATED_FILE_CONTENTS "${FILE_LINE}\n${DISPATCH_DECLARATION}\n") + set(DECLARATION_INSERTED TRUE) + elseif((NOT CALL_INSERTED) AND (FILE_LINE MATCHES "^([ \t]*)_tx_timer_interrupt\\(\\);[ \t]*$")) + string(APPEND UPDATED_FILE_CONTENTS "${CMAKE_MATCH_1}${DISPATCH_CALL}\n${FILE_LINE}\n") + set(CALL_INSERTED TRUE) + else() + string(APPEND UPDATED_FILE_CONTENTS "${FILE_LINE}\n") + endif() +endforeach() + +if(NOT CALL_INSERTED) + message(FATAL_ERROR "Unable to insert test interrupt dispatcher call into ${SOURCE_FILE}") +endif() + +if(NOT DECLARATION_INSERTED) + message(FATAL_ERROR "Unable to insert test interrupt dispatcher declaration into ${SOURCE_FILE}") +endif() + +get_filename_component(OUTPUT_DIRECTORY "${OUTPUT_FILE}" DIRECTORY) +file(MAKE_DIRECTORY "${OUTPUT_DIRECTORY}") +file(WRITE "${OUTPUT_FILE}" "${UPDATED_FILE_CONTENTS}") diff --git a/test/tx/cmake/regression/generate_test_file.sh b/test/tx/cmake/regression/generate_test_file.sh index ac0a70fa..67482c42 100755 --- a/test/tx/cmake/regression/generate_test_file.sh +++ b/test/tx/cmake/regression/generate_test_file.sh @@ -1,4 +1,15 @@ #!/bin/bash +############################################################################## +# Copyright (c) 2024 Microsoft Corporation +# Copyright (c) 2026 Eclipse ThreadX contributors +# +# This program and the accompanying materials are made available under the +# terms of the MIT License which is available at +# https://opensource.org/licenses/MIT. +# +# SPDX-License-Identifier: MIT +############################################################################## + dst=$(dirname $0)/../../regression/tx_initialize_low_level.c src=$(dirname $0)/../../../../ports/linux/gnu/src/tx_initialize_low_level.c diff --git a/test/tx/cmake/riscv/CMakeLists.txt b/test/tx/cmake/riscv/CMakeLists.txt new file mode 100644 index 00000000..a4f7e7e8 --- /dev/null +++ b/test/tx/cmake/riscv/CMakeLists.txt @@ -0,0 +1,87 @@ +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 is disabled for RV64 because ULONG is intentionally 32-bit + # (for ThreadX ABI compatibility across all ports) while sizeof(VOID*) and + # sizeof(ALIGN_TYPE) return size_t (64-bit on RV64). This design matches + # ARM Cortex-A72 and other 64-bit ports. The implicit conversions in + # common/src/ byte/block pool code trigger -Wconversion warnings. Since + # pool sizes are inherently limited to 32-bit (ULONG), the conversions + # are safe. No other 64-bit port with 32-bit ULONG has been tested with + # -Wconversion enabled, so we disable it here for RV64. + $<$<STREQUAL:${THREADX_ARCH},risc-v32>:-Wconversion> + -Wpointer-arith + -Wlogical-op + -Waggregate-return + -Wfloat-equal + $<$<COMPILE_LANGUAGE:C>:-include$<SEMICOLON>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 00000000..a6b8171a --- /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 00000000..0891200e --- /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 <stdint.h> +#include <stddef.h> + +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 00000000..f3d15f82 --- /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 <stdint.h> + +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 00000000..2f16c052 --- /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 00000000..99821f1e --- /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 <tx_port.h> + +#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 00000000..e6b9efa4 --- /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 <stdint.h> + +#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 00000000..42f08fb6 --- /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 00000000..a2b44fec --- /dev/null +++ b/test/tx/cmake/riscv/bsp/plic.c @@ -0,0 +1,87 @@ +/*************************************************************************** + * 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 <stddef.h> + +irq_callback callbacks[MAX_CALLBACK_NUM]; + +void plic_irq_enable(int irqno) +{ + uintptr_t hart = riscv_get_core(); + volatile uint32_t *reg = (volatile uint32_t *)(PLIC_MENABLE(hart) + (irqno / 32) * 4); + *reg = *reg | (1U << (irqno % 32)); +} + +void plic_irq_disable(int irqno) +{ + uintptr_t hart = riscv_get_core(); + volatile uint32_t *reg = (volatile uint32_t *)(PLIC_MENABLE(hart) + (irqno / 32) * 4); + *reg = *reg & ~(1U << (irqno % 32)); +} + +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 (irqno <= 0 || irqno >= MAX_CALLBACK_NUM) { + plic_complete(irqno); + return 0; // spurious or out-of-range, not an error + } + 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 00000000..e2734ea6 --- /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 <stdint.h> + +#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 00000000..4aa185cc --- /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 <stdarg.h> +#include <stddef.h> + +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 00000000..802fe8af --- /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 <sys/stat.h> +#include <errno.h> +#include <stdint.h> + +#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 00000000..0ac7990a --- /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 <stdint.h> +#include <stdio.h> +#include "uart.h" +#include "hwtimer.h" +#include "plic.h" +#include <tx_port.h> +#include <tx_api.h> +#include <tx_trace.h> + +/* 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 00000000..8d474d66 --- /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 00000000..c1a6b64b --- /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 <stdint.h> + +#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 00000000..765ec38f --- /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 00000000..80b6e3bd --- /dev/null +++ b/test/tx/cmake/riscv/regression/CMakeLists.txt @@ -0,0 +1,177 @@ +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 +) + +set(WEAK_DEFAULTS_SOURCE ${CMAKE_CURRENT_LIST_DIR}/../../../../shared/regression/testcontrol_weak_defaults.c) + +# Weak defaults must be an OBJECT library so all objects are always linked +# into each test executable, even though they only provide weak symbols. +# A STATIC library would have the linker skip them (weak defs don't satisfy +# undefined strong references during archive scanning). +add_library(riscv_test_weak_defaults OBJECT ${WEAK_DEFAULTS_SOURCE}) +target_compile_definitions(riscv_test_weak_defaults PRIVATE + CTEST BATCH_TEST EXTERNAL_EXIT TEST_STACK_SIZE_PRINTF=4096) +target_compile_options(riscv_test_weak_defaults PRIVATE -include stdlib.h) +target_link_libraries(riscv_test_weak_defaults PRIVATE azrtos::threadx) + +# 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 $<TARGET_FILE:${test_name}> + ) + 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_OBJECTS:riscv_test_weak_defaults>) + 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_OBJECTS:riscv_test_weak_defaults>) + 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 00000000..3b0905e7 --- /dev/null +++ b/test/tx/cmake/riscv/run.sh @@ -0,0 +1,162 @@ +#!/bin/bash +############################################################################## +# Copyright (c) 2024 Microsoft Corporation +# Copyright (c) 2026 Eclipse ThreadX contributors +# +# This program and the accompanying materials are made available under the +# terms of the MIT License which is available at +# https://opensource.org/licenses/MIT. +# +# SPDX-License-Identifier: MIT +############################################################################## + + +set -e + +# RISC-V regression test build/test runner. +# Usage: run.sh <arch> <build|test> [all|<config> ...] +# arch: riscv32 or riscv64 + +function help() { + echo "Usage: $0 <arch> <command> [all|<build_configuration> ...]" + 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/cmake/samples/fake.c b/test/tx/cmake/samples/fake.c index 199f257e..138117b5 100644 --- a/test/tx/cmake/samples/fake.c +++ b/test/tx/cmake/samples/fake.c @@ -1,3 +1,14 @@ +/***************************************************************************/ +/* Copyright (c) 2024 Microsoft Corporation */ +/* Copyright (c) 2026 Eclipse ThreadX contributors */ +/* */ +/* This program and the accompanying materials are made available under */ +/* the terms of the MIT License which is available at */ +/* https://opensource.org/licenses/MIT. */ +/* */ +/* SPDX-License-Identifier: MIT */ +/***************************************************************************/ + #include "tx_api.h" typedef unsigned int TEST_FLAG; |
