diff --git a/.github/workflows/regression_test.yml b/.github/workflows/regression_test.yml index 72e4198ba..ee5519fdb 100644 --- a/.github/workflows/regression_test.yml +++ b/.github/workflows/regression_test.yml @@ -41,23 +41,24 @@ 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 + # riscv: disabled — re-enable when RISC-V CI is ready + # 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 @@ -66,7 +67,7 @@ jobs: pull-requests: write pages: write id-token: write - needs: [tx, smp, riscv] + needs: [tx, smp] uses: ./.github/workflows/regression_template.yml with: skip_test: true diff --git a/.gitignore b/.gitignore index 349eaddcf..47438c97a 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,7 @@ CTestTestfile.cmake *.a *.htm + +# Local build artifacts +build_m7/ +.codex diff --git a/cmake/riscv64-gcc-rv32imc.cmake b/cmake/riscv64-gcc-rv32imc.cmake index 836ed1ce3..aa19b6245 100644 --- a/cmake/riscv64-gcc-rv32imc.cmake +++ b/cmake/riscv64-gcc-rv32imc.cmake @@ -15,8 +15,15 @@ # CMake toolchain file for CORE-V MCU (CV32E40P, RV32IMC) # -# Uses the riscv64-unknown-elf-gcc multi-lib toolchain (Ubuntu package -# gcc-riscv64-unknown-elf) to cross-compile for a 32-bit RISC-V target. +# Uses the riscv64-unknown-elf-gcc cross-compiler to produce rv32imc/ilp32 +# bare-metal firmware. The riscv-collab toolchain (installed to /opt/riscv by +# scripts/install_riscv.sh) is used by default. The Ubuntu package +# gcc-riscv64-unknown-elf also works and can be installed via install_deps.sh. +# +# Note: the riscv-collab toolchain is built without multilib, so it does not +# ship an rv32/ilp32 libgcc. The CORE-V MCU BSP provides a weak __clzsi2 +# fallback in bsp/clz.c to satisfy any __builtin_clz() calls without relying +# on libgcc. # # Target ISA : rv32imc_zicsr (integer, multiply, compressed, Zicsr) # ABI : ilp32 (32-bit int/long/ptr, no hardware FP) @@ -33,7 +40,12 @@ set(CFLAGS "${ARCH_FLAGS}") set(ASFLAGS "${ARCH_FLAGS}") set(LDFLAGS "${ARCH_FLAGS}") -# Toolchain binaries (riscv64-unknown-elf can target rv32 via multilib) +# Toolchain binaries: riscv64-unknown-elf-gcc cross-compiler. +# The riscv-collab toolchain (scripts/install_riscv.sh → /opt/riscv/bin) is the +# preferred choice. The Ubuntu package (gcc-riscv64-unknown-elf, install via +# install_deps.sh) is also supported. Both are searched via PATH so whichever +# comes first is used; ensure /opt/riscv/bin precedes /usr/bin if you want the +# riscv-collab toolchain. set(CMAKE_C_COMPILER riscv64-unknown-elf-gcc) set(CMAKE_CXX_COMPILER riscv64-unknown-elf-g++) set(AS riscv64-unknown-elf-as) diff --git a/cmake/threadx_riscv_port.cmake b/cmake/threadx_riscv_port.cmake new file mode 100644 index 000000000..99a995ace --- /dev/null +++ b/cmake/threadx_riscv_port.cmake @@ -0,0 +1,50 @@ +# threadx_riscv_port.cmake +# +# Helper function shared by the three RISC-V port CMakeLists files +# (risc-v32/gnu, risc-v32/clang, risc-v64/gnu). +# +# Usage: +# include(cmake/threadx_riscv_port.cmake) +# threadx_add_riscv_port(SRC_DIR +# INC_DIR +# [EXAMPLE_DIR ]) +# +# SRC_DIR — directory containing the 8 ThreadX .S port files. +# INC_DIR — directory containing tx_port.h (added as PUBLIC include). +# EXAMPLE_DIR — optional: if provided and contains a CMakeLists.txt, +# add_subdirectory() is called on it. + +function(threadx_add_riscv_port) + cmake_parse_arguments(RISCV "" "SRC_DIR;INC_DIR;EXAMPLE_DIR" "" ${ARGN}) + + if(NOT RISCV_SRC_DIR) + message(FATAL_ERROR "threadx_add_riscv_port: SRC_DIR is required") + endif() + if(NOT RISCV_INC_DIR) + message(FATAL_ERROR "threadx_add_riscv_port: INC_DIR is required") + endif() + + target_sources(${PROJECT_NAME} + PRIVATE + # {{BEGIN_TARGET_SOURCES}} + ${RISCV_SRC_DIR}/tx_initialize_low_level.S + ${RISCV_SRC_DIR}/tx_thread_context_restore.S + ${RISCV_SRC_DIR}/tx_thread_context_save.S + ${RISCV_SRC_DIR}/tx_thread_interrupt_control.S + ${RISCV_SRC_DIR}/tx_thread_schedule.S + ${RISCV_SRC_DIR}/tx_thread_stack_build.S + ${RISCV_SRC_DIR}/tx_thread_system_return.S + ${RISCV_SRC_DIR}/tx_timer_interrupt.S + # {{END_TARGET_SOURCES}} + ) + + target_include_directories(${PROJECT_NAME} + PUBLIC + ${RISCV_INC_DIR} + ) + + if(RISCV_EXAMPLE_DIR AND + EXISTS ${RISCV_EXAMPLE_DIR}/CMakeLists.txt) + add_subdirectory(${RISCV_EXAMPLE_DIR}) + endif() +endfunction() diff --git a/ports/cortex_m33/gnu/CMakeLists.txt b/ports/cortex_m33/gnu/CMakeLists.txt index 5ad3b8e75..763a2696c 100644 --- a/ports/cortex_m33/gnu/CMakeLists.txt +++ b/ports/cortex_m33/gnu/CMakeLists.txt @@ -1,7 +1,8 @@ -target_sources(${PROJECT_NAME} PRIVATE +target_sources(${PROJECT_NAME} + PRIVATE + # {{BEGIN_TARGET_SOURCES}} ${CMAKE_CURRENT_LIST_DIR}/src/txe_thread_secure_stack_allocate.c ${CMAKE_CURRENT_LIST_DIR}/src/txe_thread_secure_stack_free.c - ${CMAKE_CURRENT_LIST_DIR}/src/tx_initialize_low_level.S ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_context_restore.S ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_context_save.S ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_interrupt_control.S @@ -14,8 +15,10 @@ target_sources(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_stack_build.S ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_system_return.S ${CMAKE_CURRENT_LIST_DIR}/src/tx_timer_interrupt.S + # {{END_TARGET_SOURCES}} ) -target_include_directories(${PROJECT_NAME} PUBLIC - inc +target_include_directories(${PROJECT_NAME} + PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/inc ) diff --git a/ports/cortex_m33/gnu/example_build/sample_threadx.c b/ports/cortex_m33/gnu/example_build/sample_threadx.c new file mode 100644 index 000000000..13ffadbaa --- /dev/null +++ b/ports/cortex_m33/gnu/example_build/sample_threadx.c @@ -0,0 +1,370 @@ +/* This is a small demo of the high-performance ThreadX kernel. It includes examples of eight + threads of different priorities, using a message queue, semaphore, mutex, event flags group, + byte pool, and block pool. */ + +#include "tx_api.h" + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define the ThreadX object control blocks... */ + +TX_THREAD thread_0; +TX_THREAD thread_1; +TX_THREAD thread_2; +TX_THREAD thread_3; +TX_THREAD thread_4; +TX_THREAD thread_5; +TX_THREAD thread_6; +TX_THREAD thread_7; +TX_QUEUE queue_0; +TX_SEMAPHORE semaphore_0; +TX_MUTEX mutex_0; +TX_EVENT_FLAGS_GROUP event_flags_0; +TX_BYTE_POOL byte_pool_0; +TX_BLOCK_POOL block_pool_0; +UCHAR memory_area[DEMO_BYTE_POOL_SIZE]; + + +/* Define the counters used in the demo application... */ + +ULONG thread_0_counter; +ULONG thread_1_counter; +ULONG thread_1_messages_sent; +ULONG thread_2_counter; +ULONG thread_2_messages_received; +ULONG thread_3_counter; +ULONG thread_4_counter; +ULONG thread_5_counter; +ULONG thread_6_counter; +ULONG thread_7_counter; + + +/* Define thread prototypes. */ + +void thread_0_entry(ULONG thread_input); +void thread_1_entry(ULONG thread_input); +void thread_2_entry(ULONG thread_input); +void thread_3_and_4_entry(ULONG thread_input); +void thread_5_entry(ULONG thread_input); +void thread_6_and_7_entry(ULONG thread_input); + + +/* Define main entry point. */ + +int main() +{ + + /* Enter the ThreadX kernel. */ + tx_kernel_enter(); +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", memory_area, DEMO_BYTE_POOL_SIZE); + + /* Put system definition stuff in here, e.g. thread creates and other assorted + create information. */ + + /* Allocate the stack for thread 0. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create the main thread. */ + tx_thread_create(&thread_0, "thread 0", thread_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 1, 1, TX_NO_TIME_SLICE, TX_AUTO_START); + + + /* Allocate the stack for thread 1. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create threads 1 and 2. These threads pass information through a ThreadX + message queue. It is also interesting to note that these threads have a time + slice. */ + tx_thread_create(&thread_1, "thread 1", thread_1_entry, 1, + pointer, DEMO_STACK_SIZE, + 16, 16, 4, TX_AUTO_START); + + /* Allocate the stack for thread 2. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + tx_thread_create(&thread_2, "thread 2", thread_2_entry, 2, + pointer, DEMO_STACK_SIZE, + 16, 16, 4, TX_AUTO_START); + + /* Allocate the stack for thread 3. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create threads 3 and 4. These threads compete for a ThreadX counting semaphore. + An interesting thing here is that both threads share the same instruction area. */ + tx_thread_create(&thread_3, "thread 3", thread_3_and_4_entry, 3, + pointer, DEMO_STACK_SIZE, + 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the stack for thread 4. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + tx_thread_create(&thread_4, "thread 4", thread_3_and_4_entry, 4, + pointer, DEMO_STACK_SIZE, + 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the stack for thread 5. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create thread 5. This thread simply pends on an event flag which will be set + by thread_0. */ + tx_thread_create(&thread_5, "thread 5", thread_5_entry, 5, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the stack for thread 6. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create threads 6 and 7. These threads compete for a ThreadX mutex. */ + tx_thread_create(&thread_6, "thread 6", thread_6_and_7_entry, 6, + pointer, DEMO_STACK_SIZE, + 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the stack for thread 7. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + tx_thread_create(&thread_7, "thread 7", thread_6_and_7_entry, 7, + pointer, DEMO_STACK_SIZE, + 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the message queue. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_QUEUE_SIZE*sizeof(ULONG), TX_NO_WAIT); + + /* Create the message queue shared by threads 1 and 2. */ + tx_queue_create(&queue_0, "queue 0", TX_1_ULONG, pointer, DEMO_QUEUE_SIZE*sizeof(ULONG)); + + /* Create the semaphore used by threads 3 and 4. */ + tx_semaphore_create(&semaphore_0, "semaphore 0", 1); + + /* Create the event flags group used by threads 1 and 5. */ + tx_event_flags_create(&event_flags_0, "event flags 0"); + + /* Create the mutex used by thread 6 and 7 without priority inheritance. */ + tx_mutex_create(&mutex_0, "mutex 0", TX_NO_INHERIT); + + /* Allocate the memory for a small block pool. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_BLOCK_POOL_SIZE, TX_NO_WAIT); + + /* Create a block memory pool to allocate a message buffer from. */ + tx_block_pool_create(&block_pool_0, "block pool 0", sizeof(ULONG), pointer, DEMO_BLOCK_POOL_SIZE); + + /* Allocate a block and release the block memory. */ + tx_block_allocate(&block_pool_0, (VOID **) &pointer, TX_NO_WAIT); + + /* Release the block back to the pool. */ + tx_block_release(pointer); +} + + + +/* Define the test threads. */ + +void thread_0_entry(ULONG thread_input) +{ + +UINT status; + + + /* This thread simply sits in while-forever-sleep loop. */ + while(1) + { + + /* Increment the thread counter. */ + thread_0_counter++; + + /* Sleep for 10 ticks. */ + tx_thread_sleep(10); + + /* Set event flag 0 to wakeup thread 5. */ + status = tx_event_flags_set(&event_flags_0, 0x1, TX_OR); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + } +} + + +void thread_1_entry(ULONG thread_input) +{ + +UINT status; + + + /* This thread simply sends messages to a queue shared by thread 2. */ + while(1) + { + + /* Increment the thread counter. */ + thread_1_counter++; + + /* Send message to queue 0. */ + status = tx_queue_send(&queue_0, &thread_1_messages_sent, TX_WAIT_FOREVER); + + /* Check completion status. */ + if (status != TX_SUCCESS) + break; + + /* Increment the message sent. */ + thread_1_messages_sent++; + } +} + + +void thread_2_entry(ULONG thread_input) +{ + +ULONG received_message; +UINT status; + + /* This thread retrieves messages placed on the queue by thread 1. */ + while(1) + { + + /* Increment the thread counter. */ + thread_2_counter++; + + /* Retrieve a message from the queue. */ + status = tx_queue_receive(&queue_0, &received_message, TX_WAIT_FOREVER); + + /* Check completion status and make sure the message is what we + expected. */ + if ((status != TX_SUCCESS) || (received_message != thread_2_messages_received)) + break; + + /* Otherwise, all is okay. Increment the received message count. */ + thread_2_messages_received++; + } +} + + +void thread_3_and_4_entry(ULONG thread_input) +{ + +UINT status; + + + /* This function is executed from thread 3 and thread 4. As the loop + below shows, these function compete for ownership of semaphore_0. */ + while(1) + { + + /* Increment the thread counter. */ + if (thread_input == 3) + thread_3_counter++; + else + thread_4_counter++; + + /* Get the semaphore with suspension. */ + status = tx_semaphore_get(&semaphore_0, TX_WAIT_FOREVER); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + + /* Sleep for 2 ticks to hold the semaphore. */ + tx_thread_sleep(2); + + /* Release the semaphore. */ + status = tx_semaphore_put(&semaphore_0); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + } +} + + +void thread_5_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual_flags; + + + /* This thread simply waits for an event in a forever loop. */ + while(1) + { + + /* Increment the thread counter. */ + thread_5_counter++; + + /* Wait for event flag 0. */ + status = tx_event_flags_get(&event_flags_0, 0x1, TX_OR_CLEAR, + &actual_flags, TX_WAIT_FOREVER); + + /* Check status. */ + if ((status != TX_SUCCESS) || (actual_flags != 0x1)) + break; + } +} + + +void thread_6_and_7_entry(ULONG thread_input) +{ + +UINT status; + + + /* This function is executed from thread 6 and thread 7. As the loop + below shows, these function compete for ownership of mutex_0. */ + while(1) + { + + /* Increment the thread counter. */ + if (thread_input == 6) + thread_6_counter++; + else + thread_7_counter++; + + /* Get the mutex with suspension. */ + status = tx_mutex_get(&mutex_0, TX_WAIT_FOREVER); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + + /* Get the mutex again with suspension. This shows + that an owning thread may retrieve the mutex it + owns multiple times. */ + status = tx_mutex_get(&mutex_0, TX_WAIT_FOREVER); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + + /* Sleep for 2 ticks to hold the mutex. */ + tx_thread_sleep(2); + + /* Release the mutex. */ + status = tx_mutex_put(&mutex_0); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + + /* Release the mutex again. This will actually + release ownership since it was obtained twice. */ + status = tx_mutex_put(&mutex_0); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + } +} diff --git a/ports/cortex_m33/gnu/src/tx_initialize_low_level.S b/ports/cortex_m33/gnu/example_build/tx_initialize_low_level.S similarity index 100% rename from ports/cortex_m33/gnu/src/tx_initialize_low_level.S rename to ports/cortex_m33/gnu/example_build/tx_initialize_low_level.S diff --git a/ports/cortex_m55/gnu/CMakeLists.txt b/ports/cortex_m55/gnu/CMakeLists.txt index 5ad3b8e75..763a2696c 100644 --- a/ports/cortex_m55/gnu/CMakeLists.txt +++ b/ports/cortex_m55/gnu/CMakeLists.txt @@ -1,7 +1,8 @@ -target_sources(${PROJECT_NAME} PRIVATE +target_sources(${PROJECT_NAME} + PRIVATE + # {{BEGIN_TARGET_SOURCES}} ${CMAKE_CURRENT_LIST_DIR}/src/txe_thread_secure_stack_allocate.c ${CMAKE_CURRENT_LIST_DIR}/src/txe_thread_secure_stack_free.c - ${CMAKE_CURRENT_LIST_DIR}/src/tx_initialize_low_level.S ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_context_restore.S ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_context_save.S ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_interrupt_control.S @@ -14,8 +15,10 @@ target_sources(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_stack_build.S ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_system_return.S ${CMAKE_CURRENT_LIST_DIR}/src/tx_timer_interrupt.S + # {{END_TARGET_SOURCES}} ) -target_include_directories(${PROJECT_NAME} PUBLIC - inc +target_include_directories(${PROJECT_NAME} + PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/inc ) diff --git a/ports/cortex_m55/gnu/example_build/sample_threadx.c b/ports/cortex_m55/gnu/example_build/sample_threadx.c new file mode 100644 index 000000000..13ffadbaa --- /dev/null +++ b/ports/cortex_m55/gnu/example_build/sample_threadx.c @@ -0,0 +1,370 @@ +/* This is a small demo of the high-performance ThreadX kernel. It includes examples of eight + threads of different priorities, using a message queue, semaphore, mutex, event flags group, + byte pool, and block pool. */ + +#include "tx_api.h" + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define the ThreadX object control blocks... */ + +TX_THREAD thread_0; +TX_THREAD thread_1; +TX_THREAD thread_2; +TX_THREAD thread_3; +TX_THREAD thread_4; +TX_THREAD thread_5; +TX_THREAD thread_6; +TX_THREAD thread_7; +TX_QUEUE queue_0; +TX_SEMAPHORE semaphore_0; +TX_MUTEX mutex_0; +TX_EVENT_FLAGS_GROUP event_flags_0; +TX_BYTE_POOL byte_pool_0; +TX_BLOCK_POOL block_pool_0; +UCHAR memory_area[DEMO_BYTE_POOL_SIZE]; + + +/* Define the counters used in the demo application... */ + +ULONG thread_0_counter; +ULONG thread_1_counter; +ULONG thread_1_messages_sent; +ULONG thread_2_counter; +ULONG thread_2_messages_received; +ULONG thread_3_counter; +ULONG thread_4_counter; +ULONG thread_5_counter; +ULONG thread_6_counter; +ULONG thread_7_counter; + + +/* Define thread prototypes. */ + +void thread_0_entry(ULONG thread_input); +void thread_1_entry(ULONG thread_input); +void thread_2_entry(ULONG thread_input); +void thread_3_and_4_entry(ULONG thread_input); +void thread_5_entry(ULONG thread_input); +void thread_6_and_7_entry(ULONG thread_input); + + +/* Define main entry point. */ + +int main() +{ + + /* Enter the ThreadX kernel. */ + tx_kernel_enter(); +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", memory_area, DEMO_BYTE_POOL_SIZE); + + /* Put system definition stuff in here, e.g. thread creates and other assorted + create information. */ + + /* Allocate the stack for thread 0. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create the main thread. */ + tx_thread_create(&thread_0, "thread 0", thread_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 1, 1, TX_NO_TIME_SLICE, TX_AUTO_START); + + + /* Allocate the stack for thread 1. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create threads 1 and 2. These threads pass information through a ThreadX + message queue. It is also interesting to note that these threads have a time + slice. */ + tx_thread_create(&thread_1, "thread 1", thread_1_entry, 1, + pointer, DEMO_STACK_SIZE, + 16, 16, 4, TX_AUTO_START); + + /* Allocate the stack for thread 2. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + tx_thread_create(&thread_2, "thread 2", thread_2_entry, 2, + pointer, DEMO_STACK_SIZE, + 16, 16, 4, TX_AUTO_START); + + /* Allocate the stack for thread 3. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create threads 3 and 4. These threads compete for a ThreadX counting semaphore. + An interesting thing here is that both threads share the same instruction area. */ + tx_thread_create(&thread_3, "thread 3", thread_3_and_4_entry, 3, + pointer, DEMO_STACK_SIZE, + 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the stack for thread 4. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + tx_thread_create(&thread_4, "thread 4", thread_3_and_4_entry, 4, + pointer, DEMO_STACK_SIZE, + 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the stack for thread 5. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create thread 5. This thread simply pends on an event flag which will be set + by thread_0. */ + tx_thread_create(&thread_5, "thread 5", thread_5_entry, 5, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the stack for thread 6. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create threads 6 and 7. These threads compete for a ThreadX mutex. */ + tx_thread_create(&thread_6, "thread 6", thread_6_and_7_entry, 6, + pointer, DEMO_STACK_SIZE, + 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the stack for thread 7. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + tx_thread_create(&thread_7, "thread 7", thread_6_and_7_entry, 7, + pointer, DEMO_STACK_SIZE, + 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the message queue. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_QUEUE_SIZE*sizeof(ULONG), TX_NO_WAIT); + + /* Create the message queue shared by threads 1 and 2. */ + tx_queue_create(&queue_0, "queue 0", TX_1_ULONG, pointer, DEMO_QUEUE_SIZE*sizeof(ULONG)); + + /* Create the semaphore used by threads 3 and 4. */ + tx_semaphore_create(&semaphore_0, "semaphore 0", 1); + + /* Create the event flags group used by threads 1 and 5. */ + tx_event_flags_create(&event_flags_0, "event flags 0"); + + /* Create the mutex used by thread 6 and 7 without priority inheritance. */ + tx_mutex_create(&mutex_0, "mutex 0", TX_NO_INHERIT); + + /* Allocate the memory for a small block pool. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_BLOCK_POOL_SIZE, TX_NO_WAIT); + + /* Create a block memory pool to allocate a message buffer from. */ + tx_block_pool_create(&block_pool_0, "block pool 0", sizeof(ULONG), pointer, DEMO_BLOCK_POOL_SIZE); + + /* Allocate a block and release the block memory. */ + tx_block_allocate(&block_pool_0, (VOID **) &pointer, TX_NO_WAIT); + + /* Release the block back to the pool. */ + tx_block_release(pointer); +} + + + +/* Define the test threads. */ + +void thread_0_entry(ULONG thread_input) +{ + +UINT status; + + + /* This thread simply sits in while-forever-sleep loop. */ + while(1) + { + + /* Increment the thread counter. */ + thread_0_counter++; + + /* Sleep for 10 ticks. */ + tx_thread_sleep(10); + + /* Set event flag 0 to wakeup thread 5. */ + status = tx_event_flags_set(&event_flags_0, 0x1, TX_OR); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + } +} + + +void thread_1_entry(ULONG thread_input) +{ + +UINT status; + + + /* This thread simply sends messages to a queue shared by thread 2. */ + while(1) + { + + /* Increment the thread counter. */ + thread_1_counter++; + + /* Send message to queue 0. */ + status = tx_queue_send(&queue_0, &thread_1_messages_sent, TX_WAIT_FOREVER); + + /* Check completion status. */ + if (status != TX_SUCCESS) + break; + + /* Increment the message sent. */ + thread_1_messages_sent++; + } +} + + +void thread_2_entry(ULONG thread_input) +{ + +ULONG received_message; +UINT status; + + /* This thread retrieves messages placed on the queue by thread 1. */ + while(1) + { + + /* Increment the thread counter. */ + thread_2_counter++; + + /* Retrieve a message from the queue. */ + status = tx_queue_receive(&queue_0, &received_message, TX_WAIT_FOREVER); + + /* Check completion status and make sure the message is what we + expected. */ + if ((status != TX_SUCCESS) || (received_message != thread_2_messages_received)) + break; + + /* Otherwise, all is okay. Increment the received message count. */ + thread_2_messages_received++; + } +} + + +void thread_3_and_4_entry(ULONG thread_input) +{ + +UINT status; + + + /* This function is executed from thread 3 and thread 4. As the loop + below shows, these function compete for ownership of semaphore_0. */ + while(1) + { + + /* Increment the thread counter. */ + if (thread_input == 3) + thread_3_counter++; + else + thread_4_counter++; + + /* Get the semaphore with suspension. */ + status = tx_semaphore_get(&semaphore_0, TX_WAIT_FOREVER); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + + /* Sleep for 2 ticks to hold the semaphore. */ + tx_thread_sleep(2); + + /* Release the semaphore. */ + status = tx_semaphore_put(&semaphore_0); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + } +} + + +void thread_5_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual_flags; + + + /* This thread simply waits for an event in a forever loop. */ + while(1) + { + + /* Increment the thread counter. */ + thread_5_counter++; + + /* Wait for event flag 0. */ + status = tx_event_flags_get(&event_flags_0, 0x1, TX_OR_CLEAR, + &actual_flags, TX_WAIT_FOREVER); + + /* Check status. */ + if ((status != TX_SUCCESS) || (actual_flags != 0x1)) + break; + } +} + + +void thread_6_and_7_entry(ULONG thread_input) +{ + +UINT status; + + + /* This function is executed from thread 6 and thread 7. As the loop + below shows, these function compete for ownership of mutex_0. */ + while(1) + { + + /* Increment the thread counter. */ + if (thread_input == 6) + thread_6_counter++; + else + thread_7_counter++; + + /* Get the mutex with suspension. */ + status = tx_mutex_get(&mutex_0, TX_WAIT_FOREVER); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + + /* Get the mutex again with suspension. This shows + that an owning thread may retrieve the mutex it + owns multiple times. */ + status = tx_mutex_get(&mutex_0, TX_WAIT_FOREVER); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + + /* Sleep for 2 ticks to hold the mutex. */ + tx_thread_sleep(2); + + /* Release the mutex. */ + status = tx_mutex_put(&mutex_0); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + + /* Release the mutex again. This will actually + release ownership since it was obtained twice. */ + status = tx_mutex_put(&mutex_0); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + } +} diff --git a/ports/cortex_m55/gnu/src/tx_initialize_low_level.S b/ports/cortex_m55/gnu/example_build/tx_initialize_low_level.S similarity index 100% rename from ports/cortex_m55/gnu/src/tx_initialize_low_level.S rename to ports/cortex_m55/gnu/example_build/tx_initialize_low_level.S diff --git a/ports/cortex_m85/gnu/CMakeLists.txt b/ports/cortex_m85/gnu/CMakeLists.txt index 5ad3b8e75..763a2696c 100644 --- a/ports/cortex_m85/gnu/CMakeLists.txt +++ b/ports/cortex_m85/gnu/CMakeLists.txt @@ -1,7 +1,8 @@ -target_sources(${PROJECT_NAME} PRIVATE +target_sources(${PROJECT_NAME} + PRIVATE + # {{BEGIN_TARGET_SOURCES}} ${CMAKE_CURRENT_LIST_DIR}/src/txe_thread_secure_stack_allocate.c ${CMAKE_CURRENT_LIST_DIR}/src/txe_thread_secure_stack_free.c - ${CMAKE_CURRENT_LIST_DIR}/src/tx_initialize_low_level.S ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_context_restore.S ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_context_save.S ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_interrupt_control.S @@ -14,8 +15,10 @@ target_sources(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_stack_build.S ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_system_return.S ${CMAKE_CURRENT_LIST_DIR}/src/tx_timer_interrupt.S + # {{END_TARGET_SOURCES}} ) -target_include_directories(${PROJECT_NAME} PUBLIC - inc +target_include_directories(${PROJECT_NAME} + PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/inc ) diff --git a/ports/cortex_m85/gnu/example_build/sample_threadx.c b/ports/cortex_m85/gnu/example_build/sample_threadx.c new file mode 100644 index 000000000..13ffadbaa --- /dev/null +++ b/ports/cortex_m85/gnu/example_build/sample_threadx.c @@ -0,0 +1,370 @@ +/* This is a small demo of the high-performance ThreadX kernel. It includes examples of eight + threads of different priorities, using a message queue, semaphore, mutex, event flags group, + byte pool, and block pool. */ + +#include "tx_api.h" + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define the ThreadX object control blocks... */ + +TX_THREAD thread_0; +TX_THREAD thread_1; +TX_THREAD thread_2; +TX_THREAD thread_3; +TX_THREAD thread_4; +TX_THREAD thread_5; +TX_THREAD thread_6; +TX_THREAD thread_7; +TX_QUEUE queue_0; +TX_SEMAPHORE semaphore_0; +TX_MUTEX mutex_0; +TX_EVENT_FLAGS_GROUP event_flags_0; +TX_BYTE_POOL byte_pool_0; +TX_BLOCK_POOL block_pool_0; +UCHAR memory_area[DEMO_BYTE_POOL_SIZE]; + + +/* Define the counters used in the demo application... */ + +ULONG thread_0_counter; +ULONG thread_1_counter; +ULONG thread_1_messages_sent; +ULONG thread_2_counter; +ULONG thread_2_messages_received; +ULONG thread_3_counter; +ULONG thread_4_counter; +ULONG thread_5_counter; +ULONG thread_6_counter; +ULONG thread_7_counter; + + +/* Define thread prototypes. */ + +void thread_0_entry(ULONG thread_input); +void thread_1_entry(ULONG thread_input); +void thread_2_entry(ULONG thread_input); +void thread_3_and_4_entry(ULONG thread_input); +void thread_5_entry(ULONG thread_input); +void thread_6_and_7_entry(ULONG thread_input); + + +/* Define main entry point. */ + +int main() +{ + + /* Enter the ThreadX kernel. */ + tx_kernel_enter(); +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", memory_area, DEMO_BYTE_POOL_SIZE); + + /* Put system definition stuff in here, e.g. thread creates and other assorted + create information. */ + + /* Allocate the stack for thread 0. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create the main thread. */ + tx_thread_create(&thread_0, "thread 0", thread_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 1, 1, TX_NO_TIME_SLICE, TX_AUTO_START); + + + /* Allocate the stack for thread 1. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create threads 1 and 2. These threads pass information through a ThreadX + message queue. It is also interesting to note that these threads have a time + slice. */ + tx_thread_create(&thread_1, "thread 1", thread_1_entry, 1, + pointer, DEMO_STACK_SIZE, + 16, 16, 4, TX_AUTO_START); + + /* Allocate the stack for thread 2. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + tx_thread_create(&thread_2, "thread 2", thread_2_entry, 2, + pointer, DEMO_STACK_SIZE, + 16, 16, 4, TX_AUTO_START); + + /* Allocate the stack for thread 3. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create threads 3 and 4. These threads compete for a ThreadX counting semaphore. + An interesting thing here is that both threads share the same instruction area. */ + tx_thread_create(&thread_3, "thread 3", thread_3_and_4_entry, 3, + pointer, DEMO_STACK_SIZE, + 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the stack for thread 4. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + tx_thread_create(&thread_4, "thread 4", thread_3_and_4_entry, 4, + pointer, DEMO_STACK_SIZE, + 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the stack for thread 5. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create thread 5. This thread simply pends on an event flag which will be set + by thread_0. */ + tx_thread_create(&thread_5, "thread 5", thread_5_entry, 5, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the stack for thread 6. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create threads 6 and 7. These threads compete for a ThreadX mutex. */ + tx_thread_create(&thread_6, "thread 6", thread_6_and_7_entry, 6, + pointer, DEMO_STACK_SIZE, + 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the stack for thread 7. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + tx_thread_create(&thread_7, "thread 7", thread_6_and_7_entry, 7, + pointer, DEMO_STACK_SIZE, + 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the message queue. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_QUEUE_SIZE*sizeof(ULONG), TX_NO_WAIT); + + /* Create the message queue shared by threads 1 and 2. */ + tx_queue_create(&queue_0, "queue 0", TX_1_ULONG, pointer, DEMO_QUEUE_SIZE*sizeof(ULONG)); + + /* Create the semaphore used by threads 3 and 4. */ + tx_semaphore_create(&semaphore_0, "semaphore 0", 1); + + /* Create the event flags group used by threads 1 and 5. */ + tx_event_flags_create(&event_flags_0, "event flags 0"); + + /* Create the mutex used by thread 6 and 7 without priority inheritance. */ + tx_mutex_create(&mutex_0, "mutex 0", TX_NO_INHERIT); + + /* Allocate the memory for a small block pool. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_BLOCK_POOL_SIZE, TX_NO_WAIT); + + /* Create a block memory pool to allocate a message buffer from. */ + tx_block_pool_create(&block_pool_0, "block pool 0", sizeof(ULONG), pointer, DEMO_BLOCK_POOL_SIZE); + + /* Allocate a block and release the block memory. */ + tx_block_allocate(&block_pool_0, (VOID **) &pointer, TX_NO_WAIT); + + /* Release the block back to the pool. */ + tx_block_release(pointer); +} + + + +/* Define the test threads. */ + +void thread_0_entry(ULONG thread_input) +{ + +UINT status; + + + /* This thread simply sits in while-forever-sleep loop. */ + while(1) + { + + /* Increment the thread counter. */ + thread_0_counter++; + + /* Sleep for 10 ticks. */ + tx_thread_sleep(10); + + /* Set event flag 0 to wakeup thread 5. */ + status = tx_event_flags_set(&event_flags_0, 0x1, TX_OR); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + } +} + + +void thread_1_entry(ULONG thread_input) +{ + +UINT status; + + + /* This thread simply sends messages to a queue shared by thread 2. */ + while(1) + { + + /* Increment the thread counter. */ + thread_1_counter++; + + /* Send message to queue 0. */ + status = tx_queue_send(&queue_0, &thread_1_messages_sent, TX_WAIT_FOREVER); + + /* Check completion status. */ + if (status != TX_SUCCESS) + break; + + /* Increment the message sent. */ + thread_1_messages_sent++; + } +} + + +void thread_2_entry(ULONG thread_input) +{ + +ULONG received_message; +UINT status; + + /* This thread retrieves messages placed on the queue by thread 1. */ + while(1) + { + + /* Increment the thread counter. */ + thread_2_counter++; + + /* Retrieve a message from the queue. */ + status = tx_queue_receive(&queue_0, &received_message, TX_WAIT_FOREVER); + + /* Check completion status and make sure the message is what we + expected. */ + if ((status != TX_SUCCESS) || (received_message != thread_2_messages_received)) + break; + + /* Otherwise, all is okay. Increment the received message count. */ + thread_2_messages_received++; + } +} + + +void thread_3_and_4_entry(ULONG thread_input) +{ + +UINT status; + + + /* This function is executed from thread 3 and thread 4. As the loop + below shows, these function compete for ownership of semaphore_0. */ + while(1) + { + + /* Increment the thread counter. */ + if (thread_input == 3) + thread_3_counter++; + else + thread_4_counter++; + + /* Get the semaphore with suspension. */ + status = tx_semaphore_get(&semaphore_0, TX_WAIT_FOREVER); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + + /* Sleep for 2 ticks to hold the semaphore. */ + tx_thread_sleep(2); + + /* Release the semaphore. */ + status = tx_semaphore_put(&semaphore_0); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + } +} + + +void thread_5_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual_flags; + + + /* This thread simply waits for an event in a forever loop. */ + while(1) + { + + /* Increment the thread counter. */ + thread_5_counter++; + + /* Wait for event flag 0. */ + status = tx_event_flags_get(&event_flags_0, 0x1, TX_OR_CLEAR, + &actual_flags, TX_WAIT_FOREVER); + + /* Check status. */ + if ((status != TX_SUCCESS) || (actual_flags != 0x1)) + break; + } +} + + +void thread_6_and_7_entry(ULONG thread_input) +{ + +UINT status; + + + /* This function is executed from thread 6 and thread 7. As the loop + below shows, these function compete for ownership of mutex_0. */ + while(1) + { + + /* Increment the thread counter. */ + if (thread_input == 6) + thread_6_counter++; + else + thread_7_counter++; + + /* Get the mutex with suspension. */ + status = tx_mutex_get(&mutex_0, TX_WAIT_FOREVER); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + + /* Get the mutex again with suspension. This shows + that an owning thread may retrieve the mutex it + owns multiple times. */ + status = tx_mutex_get(&mutex_0, TX_WAIT_FOREVER); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + + /* Sleep for 2 ticks to hold the mutex. */ + tx_thread_sleep(2); + + /* Release the mutex. */ + status = tx_mutex_put(&mutex_0); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + + /* Release the mutex again. This will actually + release ownership since it was obtained twice. */ + status = tx_mutex_put(&mutex_0); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + } +} diff --git a/ports/cortex_m85/gnu/src/tx_initialize_low_level.S b/ports/cortex_m85/gnu/example_build/tx_initialize_low_level.S similarity index 100% rename from ports/cortex_m85/gnu/src/tx_initialize_low_level.S rename to ports/cortex_m85/gnu/example_build/tx_initialize_low_level.S diff --git a/ports/risc-v32/clang/CMakeLists.txt b/ports/risc-v32/clang/CMakeLists.txt index 9b7251031..beaa31aab 100644 --- a/ports/risc-v32/clang/CMakeLists.txt +++ b/ports/risc-v32/clang/CMakeLists.txt @@ -1,19 +1,11 @@ +# RISC-V32 Clang port. +# +# Clang and GNU use identical RISC-V assembly sources; this port +# compiles from gnu/src/ so fixes and features are shared automatically. +include(${CMAKE_CURRENT_LIST_DIR}/../../../cmake/threadx_riscv_port.cmake) -target_sources(${PROJECT_NAME} - PRIVATE - # {{BEGIN_TARGET_SOURCES}} - ${CMAKE_CURRENT_LIST_DIR}/src/tx_initialize_low_level.S - ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_context_restore.S - ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_context_save.S - ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_interrupt_control.S - ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_schedule.S - ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_stack_build.S - ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_system_return.S - ${CMAKE_CURRENT_LIST_DIR}/src/tx_timer_interrupt.S - # {{END_TARGET_SOURCES}} -) - -target_include_directories(${PROJECT_NAME} - PUBLIC - ${CMAKE_CURRENT_LIST_DIR}/inc +threadx_add_riscv_port( + SRC_DIR ${CMAKE_CURRENT_LIST_DIR}/../gnu/src + INC_DIR ${CMAKE_CURRENT_LIST_DIR}/inc + EXAMPLE_DIR ${CMAKE_CURRENT_LIST_DIR}/example_build/qemu_virt ) diff --git a/ports/risc-v32/clang/example_build/qemu_virt/build_threadx_sample.sh b/ports/risc-v32/clang/example_build/qemu_virt/build_threadx_sample.sh index a1b3cb887..cda0edbb9 100755 --- a/ports/risc-v32/clang/example_build/qemu_virt/build_threadx_sample.sh +++ b/ports/risc-v32/clang/example_build/qemu_virt/build_threadx_sample.sh @@ -8,7 +8,7 @@ mkdir -p build CC=clang-18 LD=ld.lld-18 -$CC -I $BASEDIR/ports/risc-v32/clang/inc -I $BASEDIR/build/custom_inc -g --sysroot=/opt/riscv_rv32ima/riscv32-unknown-elf --target=riscv32 -march=rv32ima_zicsr -mabi=ilp32 -o build/entry.obj -c entry.s +$CC -I $BASEDIR/ports/risc-v32/clang/inc -I $BASEDIR/build/custom_inc -g --sysroot=/opt/riscv_rv32ima/riscv32-unknown-elf --target=riscv32 -march=rv32ima_zicsr -mabi=ilp32 -o build/entry.obj -c entry.S $CC -DTX_INCLUDE_USER_DEFINE_FILE -I $BASEDIR/ports/risc-v32/clang/inc -I $BASEDIR/build/custom_inc -isystem $BASEDIR/common/inc -g --sysroot=/opt/riscv_rv32ima/riscv32-unknown-elf --target=riscv32 -march=rv32ima_zicsr -mabi=ilp32 -D__ASSEMBLER__ -o build/tx_initialize_low_level.obj -c tx_initialize_low_level.S $CC -DTX_INCLUDE_USER_DEFINE_FILE -I $BASEDIR/ports/risc-v32/clang/inc -I $BASEDIR/build/custom_inc -isystem $BASEDIR/common/inc -g --sysroot=/opt/riscv_rv32ima/riscv32-unknown-elf --target=riscv32 -march=rv32ima_zicsr -mabi=ilp32 -o build/board.obj -c board.c $CC -DTX_INCLUDE_USER_DEFINE_FILE -I $BASEDIR/ports/risc-v32/clang/inc -I $BASEDIR/build/custom_inc -isystem $BASEDIR/common/inc -g --sysroot=/opt/riscv_rv32ima/riscv32-unknown-elf --target=riscv32 -march=rv32ima_zicsr -mabi=ilp32 -o build/hwtimer.obj -c hwtimer.c diff --git a/ports/risc-v32/clang/example_build/qemu_virt/csr.h b/ports/risc-v32/clang/example_build/qemu_virt/csr.h deleted file mode 100644 index ab335dccc..000000000 --- a/ports/risc-v32/clang/example_build/qemu_virt/csr.h +++ /dev/null @@ -1,343 +0,0 @@ -/*************************************************************************** - * Copyright (c) 2026 Quintauris - * - * This program and the accompanying materials are made available under the - * terms of the MIT License which is available at - * https://opensource.org/licenses/MIT. - * - * SPDX-License-Identifier: MIT - **************************************************************************/ - - -#ifndef RISCV_CSR_H -#define RISCV_CSR_H - - -// Machine Status Register, mstatus -#define MSTATUS_MPP_MASK (3L << 11) // previous mode. -#define MSTATUS_MPP_M (3L << 11) -#define MSTATUS_MPP_S (1L << 11) -#define MSTATUS_MPP_U (0L << 11) -#define MSTATUS_MIE (1L << 3) // machine-mode interrupt enable. -#define MSTATUS_MPIE (1L << 7) -#define MSTATUS_FS (1L << 13) - -// Machine-mode Interrupt Enable -#define MIE_MTIE (1L << 7) -#define MIE_MSIE (1L << 3) -#define MIE_MEIE (1L << 11) -#define MIE_STIE (1L << 5) // supervisor timer -#define MIE_SSIE (1L << 1) -#define MIE_SEIE (1L << 9) - -// Supervisor Status Register, sstatus -#define SSTATUS_SPP (1L << 8) // Previous mode, 1=Supervisor, 0=User -#define SSTATUS_SPIE (1L << 5) // Supervisor Previous Interrupt Enable -#define SSTATUS_UPIE (1L << 4) // User Previous Interrupt Enable -#define SSTATUS_SIE (1L << 1) // Supervisor Interrupt Enable -#define SSTATUS_UIE (1L << 0) // User Interrupt Enable -#define SSTATUS_SPIE (1L << 5) -#define SSTATUS_UPIE (1L << 4) - -// Supervisor Interrupt Enable -#define SIE_SEIE (1L << 9) // external -#define SIE_STIE (1L << 5) // timer -#define SIE_SSIE (1L << 1) // software - -#ifndef __ASSEMBLER__ - -#include - -static inline uint32_t riscv_get_core() -{ - uint32_t x; - asm volatile("csrr %0, mhartid" : "=r" (x) ); - return x; -} - -static inline uint32_t riscv_get_mstatus() -{ - uint32_t x; - asm volatile("csrr %0, mstatus" : "=r" (x) ); - return x; -} - -static inline void riscv_writ_mstatus(uint32_t x) -{ - asm volatile("csrw mstatus, %0" : : "r" (x)); -} - -static inline void riscv_writ_mepc(uint32_t x) -{ - asm volatile("csrw mepc, %0" : : "r" (x)); -} - -static inline uint32_t riscv_get_sstatus() -{ - uint32_t x; - asm volatile("csrr %0, sstatus" : "=r" (x) ); - return x; -} - -static inline void riscv_writ_sstatus(uint32_t x) -{ - asm volatile("csrw sstatus, %0" : : "r" (x)); -} - -static inline uint32_t riscv_get_sip() -{ - uint32_t x; - asm volatile("csrr %0, sip" : "=r" (x) ); - return x; -} - -static inline void riscv_writ_sip(uint32_t x) -{ - asm volatile("csrw sip, %0" : : "r" (x)); -} - -static inline uint32_t riscv_get_sie() -{ - uint32_t x; - asm volatile("csrr %0, sie" : "=r" (x) ); - return x; -} - -static inline void riscv_writ_sie(uint32_t x) -{ - asm volatile("csrw sie, %0" : : "r" (x)); -} - -static inline uint32_t riscv_get_mie() -{ - uint32_t x; - asm volatile("csrr %0, mie" : "=r" (x) ); - return x; -} - -static inline void riscv_writ_mie(uint32_t x) -{ - asm volatile("csrw mie, %0" : : "r" (x)); -} - -static inline void riscv_writ_sepc(uint32_t x) -{ - asm volatile("csrw sepc, %0" : : "r" (x)); -} - -static inline uint32_t riscv_get_sepc() -{ - uint32_t x; - asm volatile("csrr %0, sepc" : "=r" (x) ); - return x; -} - -static inline uint32_t riscv_get_medeleg() -{ - uint32_t x; - asm volatile("csrr %0, medeleg" : "=r" (x) ); - return x; -} - -static inline void riscv_writ_medeleg(uint32_t x) -{ - asm volatile("csrw medeleg, %0" : : "r" (x)); -} - -static inline uint32_t riscv_get_mideleg() -{ - uint32_t x; - asm volatile("csrr %0, mideleg" : "=r" (x) ); - return x; -} - -static inline void riscv_writ_mideleg(uint32_t x) -{ - asm volatile("csrw mideleg, %0" : : "r" (x)); -} - -static inline void riscv_writ_stvec(uint32_t x) -{ - asm volatile("csrw stvec, %0" : : "r" (x)); -} - -static inline uint32_t riscv_get_stvec() -{ - uint32_t x; - asm volatile("csrr %0, stvec" : "=r" (x) ); - return x; -} - -static inline uint32_t riscv_get_stimecmp() -{ - uint32_t x; - asm volatile("csrr %0, 0x14d" : "=r" (x) ); - return x; -} - -static inline void riscv_writ_stimecmp(uint32_t x) -{ - asm volatile("csrw 0x14d, %0" : : "r" (x)); -} - -static inline uint32_t riscv_get_menvcfg() -{ - uint32_t x; - asm volatile("csrr %0, 0x30a" : "=r" (x) ); - return x; -} - -static inline void riscv_writ_menvcfg(uint32_t x) -{ - asm volatile("csrw 0x30a, %0" : : "r" (x)); -} - -static inline void riscv_writ_pmpcfg0(uint32_t x) -{ - asm volatile("csrw pmpcfg0, %0" : : "r" (x)); -} - -static inline void riscv_writ_pmpaddr0(uint32_t x) -{ - asm volatile("csrw pmpaddr0, %0" : : "r" (x)); -} - -static inline void riscv_writ_satp(uint32_t x) -{ - asm volatile("csrw satp, %0" : : "r" (x)); -} - -static inline uint32_t riscv_get_satp() -{ - uint32_t x; - asm volatile("csrr %0, satp" : "=r" (x) ); - return x; -} - -static inline uint32_t riscv_get_scause() -{ - uint32_t x; - asm volatile("csrr %0, scause" : "=r" (x) ); - return x; -} - -static inline uint32_t riscv_get_stval() -{ - uint32_t x; - asm volatile("csrr %0, stval" : "=r" (x) ); - return x; -} - -static inline void riscv_writ_mcounteren(uint32_t x) -{ - asm volatile("csrw mcounteren, %0" : : "r" (x)); -} - -static inline uint32_t riscv_get_mcounteren() -{ - uint32_t x; - asm volatile("csrr %0, mcounteren" : "=r" (x) ); - return x; -} - -static inline uint32_t riscv_get_time() -{ - uint32_t x; - asm volatile("csrr %0, time" : "=r" (x) ); - return x; -} - -static inline void riscv_sintr_on() -{ - uint32_t sstatus = riscv_get_sstatus(); - sstatus |= SSTATUS_SIE; - riscv_writ_sstatus(sstatus); -} - -static inline void riscv_sintr_off() -{ - uint32_t sstatus = riscv_get_sstatus(); - sstatus &= (~SSTATUS_SIE); - riscv_writ_sstatus(sstatus); -} - -static inline int riscv_sintr_get() -{ - uint32_t x = riscv_get_sstatus(); - return (x & SSTATUS_SIE) != 0; -} - -static inline void riscv_sintr_restore(int x) -{ - if(x) - riscv_sintr_on(); - else - riscv_sintr_off(); -} - -static inline void riscv_mintr_on() -{ - uint32_t mstatus = riscv_get_mstatus(); - mstatus |= MSTATUS_MIE; - riscv_writ_mstatus(mstatus); -} - -static inline void riscv_mintr_off() -{ - uint32_t mstatus = riscv_get_mstatus(); - mstatus &= (~MSTATUS_MIE); - riscv_writ_mstatus(mstatus); -} - -static inline int riscv_mintr_get() -{ - uint32_t x = riscv_get_mstatus(); - return (x & MSTATUS_MIE) != 0; -} - -static inline void riscv_mintr_restore(int x) -{ - if(x) - riscv_mintr_on(); - else - riscv_mintr_off(); -} - -static inline uint32_t riscv_get_sp() -{ - uint32_t x; - asm volatile("mv %0, sp" : "=r" (x) ); - return x; -} - -// read and write tp, the thread pointer, which xv6 uses to hold -// this core's hartid (core number), the index into cpus[]. -static inline uint32_t riscv_get_tp() -{ - uint32_t x; - asm volatile("mv %0, tp" : "=r" (x) ); - return x; -} - -static inline void riscv_writ_tp(uint32_t x) -{ - asm volatile("mv tp, %0" : : "r" (x)); -} - -static inline uint32_t riscv_get_ra() -{ - uint32_t x; - asm volatile("mv %0, ra" : "=r" (x) ); - return x; -} - -// flush the TLB. -static inline void sfence_vma() -{ - // the zero, zero means flush all TLB entries. - asm volatile("sfence.vma zero, zero"); -} - -#endif // __ASSEMBLER__ - -#endif diff --git a/ports/risc-v32/clang/example_build/qemu_virt/csr.h b/ports/risc-v32/clang/example_build/qemu_virt/csr.h new file mode 120000 index 000000000..b5df6f125 --- /dev/null +++ b/ports/risc-v32/clang/example_build/qemu_virt/csr.h @@ -0,0 +1 @@ +../../../../risc-v_common/inc/csr.h \ No newline at end of file diff --git a/ports/risc-v32/clang/example_build/qemu_virt/entry.s b/ports/risc-v32/clang/example_build/qemu_virt/entry.S similarity index 100% rename from ports/risc-v32/clang/example_build/qemu_virt/entry.s rename to ports/risc-v32/clang/example_build/qemu_virt/entry.S diff --git a/ports/risc-v32/clang/example_build/qemu_virt/plic.c b/ports/risc-v32/clang/example_build/qemu_virt/plic.c deleted file mode 100644 index 01e5c71a4..000000000 --- a/ports/risc-v32/clang/example_build/qemu_virt/plic.c +++ /dev/null @@ -1,72 +0,0 @@ -#include "plic.h" -#include -irq_callback callbacks[MAX_CALLBACK_NUM]; - -void plic_irq_enable(int irqno) -{ - int hart = riscv_get_core(); - *(uint32_t*)PLIC_MENABLE(hart) = (*(uint32_t*)PLIC_MENABLE(hart) | (1 << irqno)); - return; -} - -void plic_irq_disable(int irqno) -{ - int hart = riscv_get_core(); - *(uint32_t*)PLIC_MENABLE(hart) = (*(uint32_t*)PLIC_MENABLE(hart) & (~(1 << irqno))); - return; -} - -void plic_prio_set(int irqno, int prio) -{ - PLIC_SET_PRIO(irqno, prio); -} - -int plic_prio_get(int irqno) -{ - return 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 - -#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 - diff --git a/ports/risc-v32/clang/example_build/qemu_virt/plic.h b/ports/risc-v32/clang/example_build/qemu_virt/plic.h new file mode 120000 index 000000000..f776a3d2a --- /dev/null +++ b/ports/risc-v32/clang/example_build/qemu_virt/plic.h @@ -0,0 +1 @@ +../../../../risc-v_common/example_build/plic/plic.h \ No newline at end of file diff --git a/ports/risc-v32/clang/example_build/qemu_virt/trap.c b/ports/risc-v32/clang/example_build/qemu_virt/trap.c deleted file mode 100644 index a2733e02a..000000000 --- a/ports/risc-v32/clang/example_build/qemu_virt/trap.c +++ /dev/null @@ -1,67 +0,0 @@ -#include "csr.h" -#include -#include "uart.h" -#include "hwtimer.h" -#include "plic.h" -#include -#include - -#define OS_IS_INTERUPT(mcause) (mcause & 0x80000000u) -#define OS_IS_EXCEPTION(mcause) (~(OS_IS_INTERUPT)) -#define OS_IS_TICK_INT(mcause) (mcause == 0x80000007u) -#define OS_IS_SOFT_INT(mcause) (mcause == 0x80000003u) -#define OS_IS_EXT_INT(mcause) (mcause == 0x8000000bu) -#define OS_IS_TRAP_USER(mcause) (mcause == 0x0000000bu) -extern void _tx_timer_interrupt(void); - -extern int uart_putc(int ch); - -static void print_hex(uintptr_t val) -{ - char digits[] = "0123456789ABCDEF"; - uart_putc('0'); - uart_putc('x'); - for(int i = (sizeof(uintptr_t)*2) - 1; i >= 0; i--) { - int d = (val >> (i*4)) & 0xF; - uart_putc(digits[d]); - } - uart_putc('\n'); -} - -void trap_handler(uintptr_t mcause, uintptr_t mepc, uintptr_t mtval) -{ - // uart_puts("DEBUG : threadx/ports/risc-v32/gnu/example_build/qemu_virt/trap.c, trap_handler\n"); - if(OS_IS_INTERUPT(mcause)) - { - if(OS_IS_TICK_INT(mcause)) - { - hwtimer_handler(); - _tx_timer_interrupt(); - } - else if(OS_IS_EXT_INT(mcause)) - { - int ret = plic_irq_intr(); - if(ret) - { - puts("[INTERRUPT]: handler irq error!"); - while(1) ; - } - } - else - { - puts("[INTERRUPT]: now can't deal with the interrupt!"); - while(1) ; - } - } - else - { - puts("[EXCEPTION] : Unkown Error!!"); - puts("mcause:"); - print_hex(mcause); - puts("mepc:"); - print_hex(mepc); - puts("mtval:"); - print_hex(mtval); - while(1) ; - } -} diff --git a/ports/risc-v32/clang/example_build/qemu_virt/trap.c b/ports/risc-v32/clang/example_build/qemu_virt/trap.c new file mode 120000 index 000000000..bdce33054 --- /dev/null +++ b/ports/risc-v32/clang/example_build/qemu_virt/trap.c @@ -0,0 +1 @@ +../../../../risc-v_common/example_build/trap/trap_qemu.c \ No newline at end of file diff --git a/ports/risc-v32/clang/example_build/qemu_virt/uart.c b/ports/risc-v32/clang/example_build/qemu_virt/uart.c deleted file mode 100644 index a175b7d25..000000000 --- a/ports/risc-v32/clang/example_build/qemu_virt/uart.c +++ /dev/null @@ -1,102 +0,0 @@ -#include "uart.h" -#include "csr.h" -#include "plic.h" -#include - -// the UART control registers are memory-mapped -// at address UART0. this macro returns the -// address of one of the registers. -#define Reg(reg) ((volatile unsigned char *)(UART0 + (reg))) - -// the UART control registers. -// some have different meanings for -// read vs write. -// see http://byterunner.com/16550.html -#define RHR 0 // receive holding register (for input bytes) -#define THR 0 // transmit holding register (for output bytes) -#define IER 1 // interrupt enable register -#define IER_RX_ENABLE (1<<0) -#define IER_TX_ENABLE (1<<1) -#define FCR 2 // FIFO control register -#define FCR_FIFO_ENABLE (1<<0) -#define FCR_FIFO_CLEAR (3<<1) // clear the content of the two FIFOs -#define ISR 2 // interrupt status register -#define LCR 3 // line control register -#define LCR_EIGHT_BITS (3<<0) -#define LCR_BAUD_LATCH (1<<7) // special mode to set baud rate -#define LSR 5 // line status register -#define LSR_RX_READY (1<<0) // input is waiting to be read from RHR -#define LSR_TX_IDLE (1<<5) // THR can accept another character to send - -#define ReadReg(reg) (*(Reg(reg))) -#define WriteReg(reg, v) (*(Reg(reg)) = (v)) - -int uart_init(void) -{ - // disable interrupts. - WriteReg(IER, 0x00); - - // special mode to set baud rate. - WriteReg(LCR, LCR_BAUD_LATCH); - - // LSB for baud rate of 38.4K. - WriteReg(0, 0x03); - - // MSB for baud rate of 38.4K. - WriteReg(1, 0x00); - - // leave set-baud mode, - // and set word length to 8 bits, no parity. - WriteReg(LCR, LCR_EIGHT_BITS); - - // reset and enable FIFOs. - WriteReg(FCR, FCR_FIFO_ENABLE | FCR_FIFO_CLEAR); - - // enable transmit and receive interrupts. - // WriteReg(IER, IER_TX_ENABLE | IER_RX_ENABLE); - - //enable UART0 in PLIC - plic_irq_enable(UART0_IRQ); - - //set UART0 priority in PLIC - plic_prio_set(UART0_IRQ, 1); - - //register callback for UART0 - //plic_register_callback(UART0_IRQ, uart_intr); - puts("[UART0] : Uart Init Done, this is Test output!"); - return 0; -} - -void uart_putc_nolock(int ch) -{ - // wait for Transmit Holding Empty to be set in LSR. - while((ReadReg(LSR) & LSR_TX_IDLE) == 0) - ; - WriteReg(THR, ch); - return; -} - -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/ports/risc-v32/clang/example_build/qemu_virt/uart.c b/ports/risc-v32/clang/example_build/qemu_virt/uart.c new file mode 120000 index 000000000..1666c8c8d --- /dev/null +++ b/ports/risc-v32/clang/example_build/qemu_virt/uart.c @@ -0,0 +1 @@ +../../../../risc-v_common/example_build/uart/uart_qemu_ns16550.c \ No newline at end of file diff --git a/ports/risc-v32/clang/example_build/qemu_virt/uart.h b/ports/risc-v32/clang/example_build/qemu_virt/uart.h deleted file mode 100644 index debfd9dfa..000000000 --- a/ports/risc-v32/clang/example_build/qemu_virt/uart.h +++ /dev/null @@ -1,23 +0,0 @@ -/*************************************************************************** - * Copyright (c) 2024 Microsoft Corporation - * Copyright (c) 2026-present Eclipse ThreadX contributors - * - * This program and the accompanying materials are made available under the - * terms of the MIT License which is available at - * https://opensource.org/licenses/MIT. - * - * SPDX-License-Identifier: MIT - **************************************************************************/ - -#ifndef RISCV_UART_H -#define RISCV_UART_H - -#define UART0 0x10000000L -#define UART0_IRQ 10 - -#define puts uart_puts -int uart_init(void); -int uart_putc(int ch); -void uart_putc_nolock(int ch); -int uart_puts(const char* str); -#endif diff --git a/ports/risc-v32/clang/example_build/qemu_virt/uart.h b/ports/risc-v32/clang/example_build/qemu_virt/uart.h new file mode 120000 index 000000000..aba5ae7b9 --- /dev/null +++ b/ports/risc-v32/clang/example_build/qemu_virt/uart.h @@ -0,0 +1 @@ +../../../../risc-v_common/example_build/uart/uart_qemu_ns16550.h \ No newline at end of file diff --git a/ports/risc-v32/clang/inc/tx_port.h b/ports/risc-v32/clang/inc/tx_port.h index f89c148c0..95995032c 100644 --- a/ports/risc-v32/clang/inc/tx_port.h +++ b/ports/risc-v32/clang/inc/tx_port.h @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (c) 2025 Quintauris + * Copyright (c) 2026 Quintauris * * This program and the accompanying materials are made available under the * terms of the MIT License which is available at @@ -24,7 +24,7 @@ /* */ /* PORT SPECIFIC C INFORMATION RELEASE */ /* */ -/* tx_port.h RISC-V32/GNU */ +/* tx_port.h RISC-V32/Clang */ /* 6.4.x */ /* */ /* AUTHOR */ @@ -47,228 +47,8 @@ #ifndef TX_PORT_H #define TX_PORT_H -#ifndef __ASSEMBLER__ - -/* Include for memset. */ -#include - - -/* Determine if the optional ThreadX user define file should be used. */ - -#ifdef TX_INCLUDE_USER_DEFINE_FILE - - -/* Yes, include the user defines in tx_user.h. The defines in this file may - alternately be defined on the command line. */ - -#include "tx_user.h" -#endif /* TX_INCLUDE_USER_DEFINE_FILE */ - -#endif /* __ASSEMBLER__ */ - - -/* Define ThreadX basic types for this port. */ - -#define VOID void - -#ifndef __ASSEMBLER__ -typedef char CHAR; -typedef unsigned char UCHAR; -typedef int INT; -typedef unsigned int UINT; -typedef long LONG; -typedef unsigned long ULONG; -typedef unsigned long long ULONG64; -typedef short SHORT; -typedef unsigned short USHORT; -#define ULONG64_DEFINED -#endif /* __ASSEMBLER__ */ - - - - -/* Define the priority levels for ThreadX. Legal values range - from 32 to 1024 and MUST be evenly divisible by 32. */ - -#ifndef TX_MAX_PRIORITIES -#define TX_MAX_PRIORITIES 32 -#endif - - -/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during - thread creation is less than this value, the thread create call will return an error. */ - -#ifndef TX_MINIMUM_STACK -#define TX_MINIMUM_STACK 1024 /* Minimum stack size for this port */ -#endif - - -/* Define the system timer thread's default stack size and priority. These are only applicable - if TX_TIMER_PROCESS_IN_ISR is not defined. */ - -#ifndef TX_TIMER_THREAD_STACK_SIZE -#define TX_TIMER_THREAD_STACK_SIZE 1024 /* Default timer thread stack size */ -#endif - -#ifndef TX_TIMER_THREAD_PRIORITY -#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ -#endif - - -/* Define various constants for the ThreadX RISC-V port. */ - -#define TX_INT_DISABLE 0x00000000 /* Disable interrupts value */ -#define TX_INT_ENABLE 0x00000008 /* Enable interrupt value */ - - -/* Define the clock source for trace event entry time stamp. The following two item are port specific. - For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock - source constants would be: - -#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) -#define TX_TRACE_TIME_MASK 0x0000FFFFUL - -*/ - -#ifndef TX_TRACE_TIME_SOURCE -#define TX_TRACE_TIME_SOURCE ++_tx_trace_simulated_time -#endif -#ifndef TX_TRACE_TIME_MASK -#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL -#endif - - -/* Define the port specific options for the _tx_build_options variable. This variable indicates - how the ThreadX library was built. */ - -#define TX_PORT_SPECIFIC_BUILD_OPTIONS 0 - - -/* Define the in-line initialization constant so that modules with in-line - initialization capabilities can prevent their initialization from being - a function call. */ - -#define TX_INLINE_INITIALIZATION - - -/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is - disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack - checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING - define is negated, thereby forcing the stack fill which is necessary for the stack checking - logic. */ - -#ifdef TX_ENABLE_STACK_CHECKING -#undef TX_DISABLE_STACK_FILLING -#endif - - -/* Define the TX_THREAD control block extensions for this port. The main reason - for the multiple macros is so that backward compatibility can be maintained with - existing ThreadX kernel awareness modules. */ - -#define TX_THREAD_EXTENSION_0 -#define TX_THREAD_EXTENSION_1 -#define TX_THREAD_EXTENSION_2 -#define TX_THREAD_EXTENSION_3 - - -/* Define the port extensions of the remaining ThreadX objects. */ - -#define TX_BLOCK_POOL_EXTENSION -#define TX_BYTE_POOL_EXTENSION -#define TX_EVENT_FLAGS_GROUP_EXTENSION -#define TX_MUTEX_EXTENSION -#define TX_QUEUE_EXTENSION -#define TX_SEMAPHORE_EXTENSION -#define TX_TIMER_EXTENSION - - -/* Define the user extension field of the thread control block. Nothing - additional is needed for this port so it is defined as white space. */ - -#ifndef TX_THREAD_USER_EXTENSION -#define TX_THREAD_USER_EXTENSION -#endif - - -/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, - tx_thread_shell_entry, and tx_thread_terminate. */ - -#define TX_THREAD_CREATE_EXTENSION(thread_ptr) -#define TX_THREAD_DELETE_EXTENSION(thread_ptr) -#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) -#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) - - -/* Define the ThreadX object creation extensions for the remaining objects. */ - -#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) -#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) -#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) -#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) -#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) -#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) -#define TX_TIMER_CREATE_EXTENSION(timer_ptr) - - -/* Define the ThreadX object deletion extensions for the remaining objects. */ - -#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) -#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) -#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) -#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) -#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) -#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) -#define TX_TIMER_DELETE_EXTENSION(timer_ptr) - - -/* Define ThreadX interrupt lockout and restore macros for protection on - access of critical kernel information. The restore interrupt macro must - restore the interrupt posture of the running thread prior to the value - present prior to the disable macro. In most cases, the save area macro - is used to define a local function save area for the disable and restore - macros. */ - -/* Expose helper used to perform an atomic read/modify/write of mstatus. - The helper composes and returns the posture per ThreadX contract. */ -#ifndef __ASSEMBLER__ -UINT _tx_thread_interrupt_control(UINT new_posture); -#endif - -#ifdef TX_DISABLE_INLINE - -#define TX_INTERRUPT_SAVE_AREA register UINT interrupt_save; - -#define TX_DISABLE __asm__ volatile("csrrci %0, mstatus, 8" : "=r" (interrupt_save) :: "memory"); -#define TX_RESTORE { \ - unsigned long _temp_mstatus; \ - __asm__ volatile( \ - "csrc mstatus, 8\n" \ - "andi %0, %1, 8\n" \ - "csrs mstatus, %0" \ - : "=&r" (_temp_mstatus) \ - : "r" (interrupt_save) \ - : "memory"); \ - } - -#else - -#define TX_INTERRUPT_SAVE_AREA register UINT interrupt_save; - -#define TX_DISABLE interrupt_save = _tx_thread_interrupt_control(TX_INT_DISABLE); -#define TX_RESTORE _tx_thread_interrupt_control(interrupt_save); - -#endif /* TX_DISABLE_INLINE */ - - -/* Define the interrupt lockout macros for each ThreadX object. */ - -#define TX_BLOCK_POOL_DISABLE TX_DISABLE -#define TX_BYTE_POOL_DISABLE TX_DISABLE -#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE -#define TX_MUTEX_DISABLE TX_DISABLE -#define TX_QUEUE_DISABLE TX_DISABLE -#define TX_SEMAPHORE_DISABLE TX_DISABLE +/* Include shared RISC-V32 port definitions common to all toolchain ports. */ +#include "../../common/tx_port_riscv32_common.h" /* Define the version ID of ThreadX. This may be utilized by the application. */ @@ -282,4 +62,4 @@ extern CHAR _tx_version_id[]; #endif /* TX_THREAD_INIT */ #endif /* __ASSEMBLER__ */ -#endif /* TX_PORT_H */ \ No newline at end of file +#endif /* TX_PORT_H */ diff --git a/ports/risc-v32/clang/src/tx_initialize_low_level.S b/ports/risc-v32/clang/src/tx_initialize_low_level.S deleted file mode 100644 index 467c37355..000000000 --- a/ports/risc-v32/clang/src/tx_initialize_low_level.S +++ /dev/null @@ -1,112 +0,0 @@ -/*************************************************************************** - * Copyright (c) 2026 Quintauris - * - * This program and the accompanying materials are made available under the - * terms of the MIT License which is available at - * https://opensource.org/licenses/MIT. - * - * SPDX-License-Identifier: MIT - **************************************************************************/ - - -/**************************************************************************/ -/**************************************************************************/ -/** */ -/** ThreadX Component */ -/** */ -/** Initialize */ -/** */ -/**************************************************************************/ -/**************************************************************************/ - - .section .data - .global __tx_free_memory_start -__tx_free_memory_start: - - - .section .text -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_initialize_low_level RISC-V32/GNU */ -/* 6.4.x */ -/* AUTHOR */ -/* */ -/* Francisco Merino, Quintauris */ -/* */ -/* DESCRIPTION */ -/* */ -/* This function is responsible for any low-level processor */ -/* initialization, including setting up interrupt vectors, setting */ -/* up a periodic timer interrupt source, saving the system stack */ -/* pointer for use in ISR processing later, and finding the first */ -/* available RAM memory address for tx_application_define. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* _tx_initialize_kernel_enter ThreadX entry function */ -/* */ -/**************************************************************************/ -/* VOID _tx_initialize_low_level(VOID) -{ */ -// .global _tx_initialize_low_level - .weak _tx_initialize_low_level -_tx_initialize_low_level: - - /* Save the system stack pointer. */ - /* _tx_thread_system_stack_ptr = sp; */ - - la t0, _tx_thread_system_stack_ptr // Pickup address of system stack ptr - sw sp, 0(t0) // Save system stack pointer - - /* Pickup first free address. */ - /* _tx_initialize_unused_memory(__tx_free_memory_start); */ - - la t0, __tx_free_memory_start // Pickup first free address - la t1, _tx_initialize_unused_memory // Pickup address of unused memory - sw t0, 0(t1) // Save unused memory address - - /* Initialize floating point control/status register if floating point is enabled. */ -#ifdef __riscv_flen - li t0, 0 - csrw fcsr, t0 // Clear FP control/status register -#endif - - ret - -/* Timer Interrupt Handler Note: - Platform-specific implementations must provide their own timer ISR. - The timer interrupt handler should follow this execution flow: - - 1. Disable interrupts (if not done by hardware exception entry) - 2. Allocate interrupt stack frame (65*4 bytes with FP, 32*4 bytes without) - 3. Save RA (x1) on the stack at offset 28*4 - 4. Call _tx_thread_context_save to save thread context - 5. Call _tx_timer_interrupt to process the timer tick - 6. Call _tx_thread_context_restore to resume execution (does not return) - - Example (for CLINT timer): - - _tx_timer_interrupt_handler: - addi sp, sp, -32*4 - sw ra, 28*4(sp) - call _tx_thread_context_save - call _tx_timer_interrupt - j _tx_thread_context_restore - - The port assumes Machine mode (M-mode) execution. - For Supervisor mode (S-mode), use sstatus and SIE/SPIE instead of mstatus. - See the RISC-V Privileged Specification for more details. */ \ No newline at end of file diff --git a/ports/risc-v32/clang/src/tx_thread_context_restore.S b/ports/risc-v32/clang/src/tx_thread_context_restore.S deleted file mode 100644 index 88ac0ce34..000000000 --- a/ports/risc-v32/clang/src/tx_thread_context_restore.S +++ /dev/null @@ -1,410 +0,0 @@ -/*************************************************************************** - * Copyright (c) 2025 Quintauris - * - * This program and the accompanying materials are made available under the - * terms of the MIT License which is available at - * https://opensource.org/licenses/MIT. - * - * SPDX-License-Identifier: MIT - **************************************************************************/ - - -/**************************************************************************/ -/**************************************************************************/ -/** */ -/** ThreadX Component */ -/** */ -/** Thread */ -/** */ -/**************************************************************************/ -/**************************************************************************/ - - .section .text -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_context_restore RISC-V32/GNU */ -/* 6.4.x */ -/* AUTHOR */ -/* */ -/* Francisco Merino, Quintauris */ -/* */ -/* DESCRIPTION */ -/* */ -/* This function restores the interrupt context if it is processing a */ -/* nested interrupt. If not, it returns to the interrupt thread if no */ -/* preemption is necessary. Otherwise, if preemption is necessary or */ -/* if no thread was running, the function returns to the scheduler. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* _tx_thread_schedule Thread scheduling routine */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs Interrupt Service Routines */ -/* */ -/**************************************************************************/ -/* VOID _tx_thread_context_restore(VOID) -{ */ - .global _tx_thread_context_restore -_tx_thread_context_restore: - - /* Lockout interrupts. */ - - csrci mstatus, 0x08 // Disable interrupts (MIE bit 3) - -#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY - call _tx_execution_isr_exit // Call the ISR execution exit function -#endif - - /* Determine if interrupts are nested. */ - /* if (--_tx_thread_system_state) - { */ - - la t0, _tx_thread_system_state // Pickup addr of nested interrupt count - lw t1, 0(t0) // Pickup nested interrupt count - addi t1, t1, -1 // Decrement the nested interrupt counter - sw t1, 0(t0) // Store new nested count - beqz t1, _tx_thread_not_nested_restore // If 0, not nested restore - - /* Interrupts are nested. */ - - /* Just recover the saved registers and return to the point of - interrupt. */ - - /* Recover floating point registers. */ -#if defined(__riscv_float_abi_single) - flw f0, 31*4(sp) // Recover ft0 - flw f1, 32*4(sp) // Recover ft1 - flw f2, 33*4(sp) // Recover ft2 - flw f3, 34*4(sp) // Recover ft3 - flw f4, 35*4(sp) // Recover ft4 - flw f5, 36*4(sp) // Recover ft5 - flw f6, 37*4(sp) // Recover ft6 - flw f7, 38*4(sp) // Recover ft7 - flw f10, 41*4(sp) // Recover fa0 - flw f11, 42*4(sp) // Recover fa1 - flw f12, 43*4(sp) // Recover fa2 - flw f13, 44*4(sp) // Recover fa3 - flw f14, 45*4(sp) // Recover fa4 - flw f15, 46*4(sp) // Recover fa5 - flw f16, 47*4(sp) // Recover fa6 - flw f17, 48*4(sp) // Recover fa7 - flw f28, 59*4(sp) // Recover ft8 - flw f29, 60*4(sp) // Recover ft9 - flw f30, 61*4(sp) // Recover ft10 - flw f31, 62*4(sp) // Recover ft11 - lw t0, 63*4(sp) // Recover fcsr - csrw fcsr, t0 // Restore fcsr -#elif defined(__riscv_float_abi_double) - fld f0, 31*4(sp) // Recover ft0 - fld f1, 32*4(sp) // Recover ft1 - fld f2, 33*4(sp) // Recover ft2 - fld f3, 34*4(sp) // Recover ft3 - fld f4, 35*4(sp) // Recover ft4 - fld f5, 36*4(sp) // Recover ft5 - fld f6, 37*4(sp) // Recover ft6 - fld f7, 38*4(sp) // Recover ft7 - fld f10, 41*4(sp) // Recover fa0 - fld f11, 42*4(sp) // Recover fa1 - fld f12, 43*4(sp) // Recover fa2 - fld f13, 44*4(sp) // Recover fa3 - fld f14, 45*4(sp) // Recover fa4 - fld f15, 46*4(sp) // Recover fa5 - fld f16, 47*4(sp) // Recover fa6 - fld f17, 48*4(sp) // Recover fa7 - fld f28, 59*4(sp) // Recover ft8 - fld f29, 60*4(sp) // Recover ft9 - fld f30, 61*4(sp) // Recover ft10 - fld f31, 62*4(sp) // Recover ft11 - lw t0, 63*4(sp) // Recover fcsr - csrw fcsr, t0 // Restore fcsr -#endif - - /* Recover standard registers. */ - - /* Restore registers, - Skip global pointer because that does not change. - Also skip the saved registers since they have been restored by any function we called, - except s0 since we use it ourselves. */ - - lw t0, 30*4(sp) // Recover mepc - csrw mepc, t0 // Setup mepc - - /* Compose mstatus via read/modify/write to avoid clobbering unrelated bits. - Set MPIE and restore MPP to Machine, preserve other fields. */ - - csrr t1, mstatus - - /* Clear MPP/MPIE/MIE bits in t1 then set desired values. */ - - li t2, 0x1888 // MPP(0x1800) | MPIE(0x80) | MIE(0x08) - li t3, 0x1800 // Set MPP to Machine mode (bits 12:11) - - /* Construct new mstatus in t1: clear mask bits, set MPP/MPIE and optionally FP bit, - preserve everything except the bits we will modify. */ - - li t4, ~0x1888 // Clear mask for MPP/MPIE/MIE - and t1, t1, t4 - or t1, t1, t3 - -#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) - li t0, 0x2000 // Set FS bits (bits 14:13 to 01) for FP state - or t1, t1, t0 -#endif - csrw mstatus, t1 // Update mstatus safely - - lw ra, 28*4(sp) // Recover return address - lw t0, 19*4(sp) // Recover t0 - lw t1, 18*4(sp) // Recover t1 - lw t2, 17*4(sp) // Recover t2 - lw s0, 12*4(sp) // Recover s0 - lw a0, 27*4(sp) // Recover a0 - lw a1, 26*4(sp) // Recover a1 - lw a2, 25*4(sp) // Recover a2 - lw a3, 24*4(sp) // Recover a3 - lw a4, 23*4(sp) // Recover a4 - lw a5, 22*4(sp) // Recover a5 - lw a6, 21*4(sp) // Recover a6 - lw a7, 20*4(sp) // Recover a7 - lw t3, 16*4(sp) // Recover t3 - lw t4, 15*4(sp) // Recover t4 - lw t5, 14*4(sp) // Recover t5 - lw t6, 13*4(sp) // Recover t6 - -#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) - addi sp, sp, 65*4 // Recover stack frame - with floating point enabled -#else - addi sp, sp, 32*4 // Recover stack frame - without floating point enabled -#endif - mret // Return to point of interrupt - - /* } */ -_tx_thread_not_nested_restore: - /* Determine if a thread was interrupted and no preemption is required. */ - /* else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) - || (_tx_thread_preempt_disable)) - { */ - - la t0, _tx_thread_current_ptr // Pickup current thread pointer address - lw t1, 0(t0) // Pickup current thread pointer - - beqz t1, _tx_thread_idle_system_restore // If NULL, idle system restore - - - la t0, _tx_thread_preempt_disable // Pickup preempt disable flag address - lw t2, 0(t0) // Pickup preempt disable flag (UINT) - - bgtz t2, _tx_thread_no_preempt_restore // If set, restore interrupted thread - - - la t0, _tx_thread_execute_ptr // Pickup thread execute pointer address - lw t2, 0(t0) // Pickup thread execute pointer - - bne t1, t2, _tx_thread_preempt_restore // If higher-priority thread is ready, preempt - - -_tx_thread_no_preempt_restore: - /* Restore interrupted thread or ISR. */ - - /* Pickup the saved stack pointer. */ - /* sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; */ - - lw sp, 8(t1) // Switch back to thread's stack - - /* Recover floating point registers. */ -#if defined(__riscv_float_abi_single) - flw f0, 31*4(sp) // Recover ft0 - flw f1, 32*4(sp) // Recover ft1 - flw f2, 33*4(sp) // Recover ft2 - flw f3, 34*4(sp) // Recover ft3 - flw f4, 35*4(sp) // Recover ft4 - flw f5, 36*4(sp) // Recover ft5 - flw f6, 37*4(sp) // Recover ft6 - flw f7, 38*4(sp) // Recover ft7 - flw f10, 41*4(sp) // Recover fa0 - flw f11, 42*4(sp) // Recover fa1 - flw f12, 43*4(sp) // Recover fa2 - flw f13, 44*4(sp) // Recover fa3 - flw f14, 45*4(sp) // Recover fa4 - flw f15, 46*4(sp) // Recover fa5 - flw f16, 47*4(sp) // Recover fa6 - flw f17, 48*4(sp) // Recover fa7 - flw f28, 59*4(sp) // Recover ft8 - flw f29, 60*4(sp) // Recover ft9 - flw f30, 61*4(sp) // Recover ft10 - flw f31, 62*4(sp) // Recover ft11 - lw t0, 63*4(sp) // Recover fcsr - csrw fcsr, t0 // Restore fcsr -#elif defined(__riscv_float_abi_double) - fld f0, 31*4(sp) // Recover ft0 - fld f1, 32*4(sp) // Recover ft1 - fld f2, 33*4(sp) // Recover ft2 - fld f3, 34*4(sp) // Recover ft3 - fld f4, 35*4(sp) // Recover ft4 - fld f5, 36*4(sp) // Recover ft5 - fld f6, 37*4(sp) // Recover ft6 - fld f7, 38*4(sp) // Recover ft7 - fld f10, 41*4(sp) // Recover fa0 - fld f11, 42*4(sp) // Recover fa1 - fld f12, 43*4(sp) // Recover fa2 - fld f13, 44*4(sp) // Recover fa3 - fld f14, 45*4(sp) // Recover fa4 - fld f15, 46*4(sp) // Recover fa5 - fld f16, 47*4(sp) // Recover fa6 - fld f17, 48*4(sp) // Recover fa7 - fld f28, 59*4(sp) // Recover ft8 - fld f29, 60*4(sp) // Recover ft9 - fld f30, 61*4(sp) // Recover ft10 - fld f31, 62*4(sp) // Recover ft11 - lw t0, 63*4(sp) // Recover fcsr - csrw fcsr, t0 // Restore fcsr -#endif - - /* Recover the saved context and return to the point of interrupt. */ - - /* Recover standard registers. */ - /* Restore registers, - Skip global pointer because that does not change */ - - lw t0, 30*4(sp) // Recover mepc - csrw mepc, t0 // Setup mepc - - /* Compose mstatus via read/modify/write to avoid clobbering unrelated bits. */ - - csrr t1, mstatus - li t2, 0x1888 // MPP(0x1800) | MPIE(0x80) | MIE(0x08) - li t3, 0x1800 // Set MPP to Machine mode - li t4, ~0x1888 // Clear mask for MPP/MPIE/MIE - and t1, t1, t4 - or t1, t1, t3 - -#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) - li t0, 0x2000 // Set FS bits for FP state - or t1, t1, t0 -#endif - csrw mstatus, t1 // Update mstatus safely - - lw ra, 28*4(sp) // Recover return address - lw t0, 19*4(sp) // Recover t0 - lw t1, 18*4(sp) // Recover t1 - lw t2, 17*4(sp) // Recover t2 - lw s0, 12*4(sp) // Recover s0 - lw a0, 27*4(sp) // Recover a0 - lw a1, 26*4(sp) // Recover a1 - lw a2, 25*4(sp) // Recover a2 - lw a3, 24*4(sp) // Recover a3 - lw a4, 23*4(sp) // Recover a4 - lw a5, 22*4(sp) // Recover a5 - lw a6, 21*4(sp) // Recover a6 - lw a7, 20*4(sp) // Recover a7 - lw t3, 16*4(sp) // Recover t3 - lw t4, 15*4(sp) // Recover t4 - lw t5, 14*4(sp) // Recover t5 - lw t6, 13*4(sp) // Recover t6 - -#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) - addi sp, sp, 65*4 // Recover stack frame - with floating point enabled -#else - addi sp, sp, 32*4 // Recover stack frame - without floating point enabled -#endif - mret // Return to point of interrupt - - /* } - else - { */ -_tx_thread_preempt_restore: - /* Instead of directly activating the thread again, ensure we save the - entire stack frame by saving the remaining registers. */ - - lw t0, 8(t1) // Pickup thread's stack pointer - ori t3, zero, 1 // Build interrupt stack type - sw t3, 0(t0) // Store stack type - - /* Store floating point preserved registers. */ -#ifdef __riscv_float_abi_single - fsw f8, 39*4(t0) // Store fs0 - fsw f9, 40*4(t0) // Store fs1 - fsw f18, 49*4(t0) // Store fs2 - fsw f19, 50*4(t0) // Store fs3 - fsw f20, 51*4(t0) // Store fs4 - fsw f21, 52*4(t0) // Store fs5 - fsw f22, 53*4(t0) // Store fs6 - fsw f23, 54*4(t0) // Store fs7 - fsw f24, 55*4(t0) // Store fs8 - fsw f25, 56*4(t0) // Store fs9 - fsw f26, 57*4(t0) // Store fs10 - fsw f27, 58*4(t0) // Store fs11 -#elif defined(__riscv_float_abi_double) - fsd f8, 39*4(t0) // Store fs0 - fsd f9, 40*4(t0) // Store fs1 - fsd f18, 49*4(t0) // Store fs2 - fsd f19, 50*4(t0) // Store fs3 - fsd f20, 51*4(t0) // Store fs4 - fsd f21, 52*4(t0) // Store fs5 - fsd f22, 53*4(t0) // Store fs6 - fsd f23, 54*4(t0) // Store fs7 - fsd f24, 55*4(t0) // Store fs8 - fsd f25, 56*4(t0) // Store fs9 - fsd f26, 57*4(t0) // Store fs10 - fsd f27, 58*4(t0) // Store fs11 -#endif - - /* Store standard preserved registers. */ - - sw x9, 11*4(t0) // Store s1 - sw x18, 10*4(t0) // Store s2 - sw x19, 9*4(t0) // Store s3 - sw x20, 8*4(t0) // Store s4 - sw x21, 7*4(t0) // Store s5 - sw x22, 6*4(t0) // Store s6 - sw x23, 5*4(t0) // Store s7 - sw x24, 4*4(t0) // Store s8 - sw x25, 3*4(t0) // Store s9 - sw x26, 2*4(t0) // Store s10 - sw x27, 1*4(t0) // Store s11 - // Note: s0 is already stored! - - /* Save the remaining time-slice and disable it. */ - /* if (_tx_timer_time_slice) - { */ - - la t0, _tx_timer_time_slice // Pickup time slice variable address - lw t2, 0(t0) // Pickup time slice - beqz t2, _tx_thread_dont_save_ts // If 0, skip time slice processing - - /* _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice - _tx_timer_time_slice = 0; */ - - sw t2, 24(t1) // Save current time slice - sw x0, 0(t0) // Clear global time slice - - - /* } */ -_tx_thread_dont_save_ts: - /* Clear the current task pointer. */ - /* _tx_thread_current_ptr = TX_NULL; */ - - /* Return to the scheduler. */ - /* _tx_thread_schedule(); */ - - la t0, _tx_thread_current_ptr // Pickup current thread pointer address - sw x0, 0(t0) // Clear current thread pointer - - /* } */ - -_tx_thread_idle_system_restore: - /* Just return back to the scheduler! */ - j _tx_thread_schedule // Return to scheduler - -/* } */ diff --git a/ports/risc-v32/clang/src/tx_thread_context_save.S b/ports/risc-v32/clang/src/tx_thread_context_save.S deleted file mode 100644 index ffb302e36..000000000 --- a/ports/risc-v32/clang/src/tx_thread_context_save.S +++ /dev/null @@ -1,271 +0,0 @@ -/*************************************************************************** - * Copyright (c) 2026 Quintauris - * - * This program and the accompanying materials are made available under the - * terms of the MIT License which is available at - * https://opensource.org/licenses/MIT. - * - * SPDX-License-Identifier: MIT - **************************************************************************/ - - -/**************************************************************************/ -/**************************************************************************/ -/** */ -/** ThreadX Component */ -/** */ -/** Thread */ -/** */ -/**************************************************************************/ -/**************************************************************************/ - - .section .text -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_context_save RISC-V32/GNU */ -/* 6.4.x */ -/* AUTHOR */ -/* */ -/* Francisco Merino, Quintauris */ -/* */ -/* DESCRIPTION */ -/* */ -/* This function saves the context of an executing thread in the */ -/* beginning of interrupt processing. The function also ensures that */ -/* the system stack is used upon return to the calling ISR. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs */ -/* */ -/**************************************************************************/ -/* VOID _tx_thread_context_save(VOID) -{ */ - .global _tx_thread_context_save -_tx_thread_context_save: - - /* Upon entry to this routine, RA/x1 has been saved on the stack - and the stack has been already allocated for the entire context: - addi sp, sp, -32*4 (or -65*4) - sw ra, 28*4(sp) - */ - - sw t0, 19*4(sp) // Store t0 - sw t1, 18*4(sp) // Store t1 - - /* Check for a nested interrupt. */ - /* if (_tx_thread_system_state++) - { */ - - la t0, _tx_thread_system_state // Pickup addr of system state var - lw t1, 0(t0) // Pickup system state - addi t1, t1, 1 // Increment system state - sw t1, 0(t0) // Store system state - li t0, 1 - bgt t1, t0, _tx_thread_nested_save // If it's more than 1, nested interrupt - - /* First level interrupt, save the rest of the scratch registers and - check for a thread to preempt. */ - - sw t2, 17*4(sp) // Store t2 - sw s0, 12*4(sp) // Store s0 - sw a0, 27*4(sp) // Store a0 - sw a1, 26*4(sp) // Store a1 - sw a2, 25*4(sp) // Store a2 - sw a3, 24*4(sp) // Store a3 - sw a4, 23*4(sp) // Store a4 - sw a5, 22*4(sp) // Store a5 - sw a6, 21*4(sp) // Store a6 - sw a7, 20*4(sp) // Store a7 - sw t3, 16*4(sp) // Store t3 - sw t4, 15*4(sp) // Store t4 - sw t5, 14*4(sp) // Store t5 - sw t6, 13*4(sp) // Store t6 - - /* Save floating point registers. */ -#if defined(__riscv_float_abi_single) - fsw f0, 31*4(sp) // Store ft0 - fsw f1, 32*4(sp) // Store ft1 - fsw f2, 33*4(sp) // Store ft2 - fsw f3, 34*4(sp) // Store ft3 - fsw f4, 35*4(sp) // Store ft4 - fsw f5, 36*4(sp) // Store ft5 - fsw f6, 37*4(sp) // Store ft6 - fsw f7, 38*4(sp) // Store ft7 - fsw f10, 41*4(sp) // Store fa0 - fsw f11, 42*4(sp) // Store fa1 - fsw f12, 43*4(sp) // Store fa2 - fsw f13, 44*4(sp) // Store fa3 - fsw f14, 45*4(sp) // Store fa4 - fsw f15, 46*4(sp) // Store fa5 - fsw f16, 47*4(sp) // Store fa6 - fsw f17, 48*4(sp) // Store fa7 - fsw f28, 59*4(sp) // Store ft8 - fsw f29, 60*4(sp) // Store ft9 - fsw f30, 61*4(sp) // Store ft10 - fsw f31, 62*4(sp) // Store ft11 - csrr t0, fcsr - sw t0, 63*4(sp) // Store fcsr -#elif defined(__riscv_float_abi_double) - fsd f0, 31*4(sp) // Store ft0 - fsd f1, 32*4(sp) // Store ft1 - fsd f2, 33*4(sp) // Store ft2 - fsd f3, 34*4(sp) // Store ft3 - fsd f4, 35*4(sp) // Store ft4 - fsd f5, 36*4(sp) // Store ft5 - fsd f6, 37*4(sp) // Store ft6 - fsd f7, 38*4(sp) // Store ft7 - fsd f10, 41*4(sp) // Store fa0 - fsd f11, 42*4(sp) // Store fa1 - fsd f12, 43*4(sp) // Store fa2 - fsd f13, 44*4(sp) // Store fa3 - fsd f14, 45*4(sp) // Store fa4 - fsd f15, 46*4(sp) // Store fa5 - fsd f16, 47*4(sp) // Store fa6 - fsd f17, 48*4(sp) // Store fa7 - fsd f28, 59*4(sp) // Store ft8 - fsd f29, 60*4(sp) // Store ft9 - fsd f30, 61*4(sp) // Store ft10 - fsd f31, 62*4(sp) // Store ft11 - csrr t0, fcsr - sw t0, 63*4(sp) // Store fcsr -#endif - - csrr t0, mepc - sw t0, 30*4(sp) // Save it on the stack - - /* Save mstatus. */ - csrr t0, mstatus - sw t0, 29*4(sp) - - la t1, _tx_thread_current_ptr // Pickup address of current thread ptr - lw t2, 0(t1) // Pickup current thread pointer - beqz t2, _tx_thread_idle_system_save // If NULL, idle system was interrupted - - /* Save the current thread's stack pointer and switch to the system stack. */ - /* _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; - sp = _tx_thread_system_stack_ptr; */ - - sw sp, 8(t2) // Save stack pointer - la t0, _tx_thread_system_stack_ptr - lw sp, 0(t0) // Switch to system stack - - /* Call the ISR execution exit function if enabled. */ -#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY - call _tx_execution_isr_enter // Call the ISR execution enter function -#endif - - ret // Return to ISR - -_tx_thread_nested_save: - - /* Nested interrupt! Just save the scratch registers and return to the ISR. */ - - sw t2, 17*4(sp) // Store t2 - sw s0, 12*4(sp) // Store s0 - sw a0, 27*4(sp) // Store a0 - sw a1, 26*4(sp) // Store a1 - sw a2, 25*4(sp) // Store a2 - sw a3, 24*4(sp) // Store a3 - sw a4, 23*4(sp) // Store a4 - sw a5, 22*4(sp) // Store a5 - sw a6, 21*4(sp) // Store a6 - sw a7, 20*4(sp) // Store a7 - sw t3, 16*4(sp) // Store t3 - sw t4, 15*4(sp) // Store t4 - sw t5, 14*4(sp) // Store t5 - sw t6, 13*4(sp) // Store t6 - - /* Save floating point registers. */ -#if defined(__riscv_float_abi_single) - fsw f0, 31*4(sp) // Store ft0 - fsw f1, 32*4(sp) // Store ft1 - fsw f2, 33*4(sp) // Store ft2 - fsw f3, 34*4(sp) // Store ft3 - fsw f4, 35*4(sp) // Store ft4 - fsw f5, 36*4(sp) // Store ft5 - fsw f6, 37*4(sp) // Store ft6 - fsw f7, 38*4(sp) // Store ft7 - fsw f10, 41*4(sp) // Store fa0 - fsw f11, 42*4(sp) // Store fa1 - fsw f12, 43*4(sp) // Store fa2 - fsw f13, 44*4(sp) // Store fa3 - fsw f14, 45*4(sp) // Store fa4 - fsw f15, 46*4(sp) // Store fa5 - fsw f16, 47*4(sp) // Store fa6 - fsw f17, 48*4(sp) // Store fa7 - fsw f28, 59*4(sp) // Store ft8 - fsw f29, 60*4(sp) // Store ft9 - fsw f30, 61*4(sp) // Store ft10 - fsw f31, 62*4(sp) // Store ft11 - csrr t0, fcsr - sw t0, 63*4(sp) // Store fcsr -#elif defined(__riscv_float_abi_double) - fsd f0, 31*4(sp) // Store ft0 - fsd f1, 32*4(sp) // Store ft1 - fsd f2, 33*4(sp) // Store ft2 - fsd f3, 34*4(sp) // Store ft3 - fsd f4, 35*4(sp) // Store ft4 - fsd f5, 36*4(sp) // Store ft5 - fsd f6, 37*4(sp) // Store ft6 - fsd f7, 38*4(sp) // Store ft7 - fsd f10, 41*4(sp) // Store fa0 - fsd f11, 42*4(sp) // Store fa1 - fsd f12, 43*4(sp) // Store fa2 - fsd f13, 44*4(sp) // Store fa3 - fsd f14, 45*4(sp) // Store fa4 - fsd f15, 46*4(sp) // Store fa5 - fsd f16, 47*4(sp) // Store fa6 - fsd f17, 48*4(sp) // Store fa7 - fsd f28, 59*4(sp) // Store ft8 - fsd f29, 60*4(sp) // Store ft9 - fsd f30, 61*4(sp) // Store ft10 - fsd f31, 62*4(sp) // Store ft11 - csrr t0, fcsr - sw t0, 63*4(sp) // Store fcsr -#endif - - csrr t0, mepc - sw t0, 30*4(sp) // Save it on stack - - csrr t0, mstatus - sw t0, 29*4(sp) - - /* Call the ISR execution exit function if enabled. */ -#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY - call _tx_execution_isr_enter // Call the ISR execution enter function -#endif - - ret // Return to ISR - -_tx_thread_idle_system_save: - - -#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY - call _tx_execution_isr_enter // Call the ISR execution enter function -#endif - - /* Interrupt occurred in the scheduling loop. */ - - /* } -} */ -#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) - addi sp, sp, 65*4 // Recover stack frame - with floating point enabled -#else - addi sp, sp, 32*4 // Recover the reserved stack space -#endif - ret // Return to calling ISR diff --git a/ports/risc-v32/clang/src/tx_thread_interrupt_control.S b/ports/risc-v32/clang/src/tx_thread_interrupt_control.S deleted file mode 100644 index 86b6745e2..000000000 --- a/ports/risc-v32/clang/src/tx_thread_interrupt_control.S +++ /dev/null @@ -1,88 +0,0 @@ -/*************************************************************************** - * Copyright (c) 2026 Quintauris - * - * This program and the accompanying materials are made available under the - * terms of the MIT License which is available at - * https://opensource.org/licenses/MIT. - * - * SPDX-License-Identifier: MIT - **************************************************************************/ - - -/**************************************************************************/ -/**************************************************************************/ -/** */ -/** ThreadX Component */ -/** */ -/** Thread */ -/** */ -/**************************************************************************/ -/**************************************************************************/ - - - .section .text -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_interrupt_control RISC-V32/GNU */ -/* 6.4.x */ -/* AUTHOR */ -/* */ -/* Francisco Merino, Quintauris */ -/* */ -/* DESCRIPTION */ -/* */ -/* This function is responsible for changing the interrupt lockout */ -/* posture of the system. */ -/* */ -/* INPUT */ -/* */ -/* new_posture New interrupt lockout posture */ -/* */ -/* OUTPUT */ -/* */ -/* old_posture Old interrupt lockout posture */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* Application Code */ -/* */ -/**************************************************************************/ -/* UINT _tx_thread_interrupt_control(UINT new_posture) -{ */ - .global _tx_thread_interrupt_control -_tx_thread_interrupt_control: - - /* Pickup current interrupt posture. */ - - csrr a1, mstatus // Pickup mstatus - andi a1, a1, 0x08 // Mask out all but MIE - - /* Check for the new posture. */ - - beqz a0, _tx_thread_interrupt_disable // If 0, disable interrupts - - /* Enable interrupts. */ - - csrsi mstatus, 0x08 // Enable interrupts (MIE bit 3) - j _tx_thread_interrupt_control_exit // Return to caller - -_tx_thread_interrupt_disable: - - /* Disable interrupts. */ - - csrci mstatus, 0x08 // Disable interrupts (MIE bit 3) - -_tx_thread_interrupt_control_exit: - - /* Return the old interrupt posture. */ - - mv a0, a1 // Setup return value - ret // Return to caller - -/* } */ diff --git a/ports/risc-v32/clang/src/tx_thread_schedule.S b/ports/risc-v32/clang/src/tx_thread_schedule.S deleted file mode 100644 index d5a54fa26..000000000 --- a/ports/risc-v32/clang/src/tx_thread_schedule.S +++ /dev/null @@ -1,318 +0,0 @@ -/*************************************************************************** - * Copyright (c) 2026 Quintauris - * - * This program and the accompanying materials are made available under the - * terms of the MIT License which is available at - * https://opensource.org/licenses/MIT. - * - * SPDX-License-Identifier: MIT - **************************************************************************/ - - -/**************************************************************************/ -/**************************************************************************/ -/** */ -/** ThreadX Component */ -/** */ -/** Thread */ -/** */ -/**************************************************************************/ -/**************************************************************************/ - - - .section .text -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_schedule RISC-V32/GNU */ -/* 6.4.x */ -/* AUTHOR */ -/* */ -/* Francisco Merino, Quintauris */ -/* */ -/* DESCRIPTION */ -/* */ -/* This function waits for a thread control block pointer to appear in */ -/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ -/* in the variable, the corresponding thread is resumed. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* _tx_initialize_kernel_enter ThreadX entry function */ -/* _tx_thread_system_return Return to system from thread */ -/* _tx_thread_context_restore Restore thread's context */ -/* */ -/**************************************************************************/ -/* VOID _tx_thread_schedule(VOID) -{ */ - .global _tx_thread_schedule -_tx_thread_schedule: - - /* Enable interrupts. */ - - csrsi mstatus, 0x08 // Enable interrupts (MIE bit 3) - - /* Wait for a thread to execute. */ - /* do - { */ -_tx_thread_schedule_loop: - - la t0, _tx_thread_execute_ptr // Pickup address of execute ptr - lw t1, 0(t0) // Pickup execute pointer - bnez t1, _tx_thread_ready_to_run // If non-NULL, a thread is ready to run - -#ifndef TX_NO_WFI - wfi // Wait for interrupt -#endif - j _tx_thread_schedule_loop // Check again - - /* } - while (_tx_thread_execute_ptr == TX_NULL); */ - -_tx_thread_ready_to_run: - - /* At this point, t1 contains the pointer to the thread to execute. - Lockout interrupts. */ - - csrci mstatus, 0x08 // Disable interrupts (MIE bit 3) - - /* Check _tx_thread_execute_ptr again, in case an interrupt occurred - between the check and the disable. */ - - lw t1, 0(t0) // Pickup execute pointer - beqz t1, _tx_thread_schedule_loop // If NULL, go back to wait loop - - /* Yes! We have a thread to execute. */ - /* _tx_thread_current_ptr = _tx_thread_execute_ptr; */ - - la t0, _tx_thread_current_ptr // Pickup address of current thread - sw t1, 0(t0) // Setup current thread pointer - - /* Increment the run count for this thread. */ - /* _tx_thread_current_ptr -> tx_thread_run_count++; */ - - lw t2, 4(t1) // Pickup run count - addi t2, t2, 1 // Increment run count - sw t2, 4(t1) // Store run count - - /* Setup time-slice values. */ - /* _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; */ - - lw t2, 24(t1) // Pickup thread time-slice - la t3, _tx_timer_time_slice // Pickup address of time-slice - sw t2, 0(t3) // Setup time-slice - - /* Call the thread execution enter function if enabled. */ -#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY - - call _tx_execution_thread_enter // Call the thread execution enter function -#endif - - /* Switch to the thread's stack. */ - /* sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; */ - - lw sp, 8(t1) // Switch to thread stack - - /* Determine the type of stack frame. */ - /* if (*sp) - { */ - - lw t0, 0(sp) // Pickup stack type - beqz t0, _tx_thread_solicited_return // If 0, solicited return - - /* Recover floating point registers. */ -#if defined(__riscv_float_abi_single) - flw f0, 31*4(sp) // Recover ft0 - flw f1, 32*4(sp) // Recover ft1 - flw f2, 33*4(sp) // Recover ft2 - flw f3, 34*4(sp) // Recover ft3 - flw f4, 35*4(sp) // Recover ft4 - flw f5, 36*4(sp) // Recover ft5 - flw f6, 37*4(sp) // Recover ft6 - flw f7, 38*4(sp) // Recover ft7 - flw f8, 39*4(sp) // Recover fs0 - flw f9, 40*4(sp) // Recover fs1 - flw f10, 41*4(sp) // Recover fa0 - flw f11, 42*4(sp) // Recover fa1 - flw f12, 43*4(sp) // Recover fa2 - flw f13, 44*4(sp) // Recover fa3 - flw f14, 45*4(sp) // Recover fa4 - flw f15, 46*4(sp) // Recover fa5 - flw f16, 47*4(sp) // Recover fa6 - flw f17, 48*4(sp) // Recover fa7 - flw f18, 49*4(sp) // Recover fs2 - flw f19, 50*4(sp) // Recover fs3 - flw f20, 51*4(sp) // Recover fs4 - flw f21, 52*4(sp) // Recover fs5 - flw f22, 53*4(sp) // Recover fs6 - flw f23, 54*4(sp) // Recover fs7 - flw f24, 55*4(sp) // Recover fs8 - flw f25, 56*4(sp) // Recover fs9 - flw f26, 57*4(sp) // Recover fs10 - flw f27, 58*4(sp) // Recover fs11 - flw f28, 59*4(sp) // Recover ft8 - flw f29, 60*4(sp) // Recover ft9 - flw f30, 61*4(sp) // Recover ft10 - flw f31, 62*4(sp) // Recover ft11 - lw t0, 63*4(sp) // Recover fcsr - csrw fcsr, t0 // Restore fcsr -#elif defined(__riscv_float_abi_double) - fld f0, 31*4(sp) // Recover ft0 - fld f1, 32*4(sp) // Recover ft1 - fld f2, 33*4(sp) // Recover ft2 - fld f3, 34*4(sp) // Recover ft3 - fld f4, 35*4(sp) // Recover ft4 - fld f5, 36*4(sp) // Recover ft5 - fld f6, 37*4(sp) // Recover ft6 - fld f7, 38*4(sp) // Recover ft7 - fld f8, 39*4(sp) // Recover fs0 - fld f9, 40*4(sp) // Recover fs1 - fld f10, 41*4(sp) // Recover fa0 - fld f11, 42*4(sp) // Recover fa1 - fld f12, 43*4(sp) // Recover fa2 - fld f13, 44*4(sp) // Recover fa3 - fld f14, 45*4(sp) // Recover fa4 - fld f15, 46*4(sp) // Recover fa5 - fld f16, 47*4(sp) // Recover fa6 - fld f17, 48*4(sp) // Recover fa7 - fld f18, 49*4(sp) // Recover fs2 - fld f19, 50*4(sp) // Recover fs3 - fld f20, 51*4(sp) // Recover fs4 - fld f21, 52*4(sp) // Recover fs5 - fld f22, 53*4(sp) // Recover fs6 - fld f23, 54*4(sp) // Recover fs7 - fld f24, 55*4(sp) // Recover fs8 - fld f25, 56*4(sp) // Recover fs9 - fld f26, 57*4(sp) // Recover fs10 - fld f27, 58*4(sp) // Recover fs11 - fld f28, 59*4(sp) // Recover ft8 - fld f29, 60*4(sp) // Recover ft9 - fld f30, 61*4(sp) // Recover ft10 - fld f31, 62*4(sp) // Recover ft11 - lw t0, 63*4(sp) // Recover fcsr - csrw fcsr, t0 // Restore fcsr -#endif - - /* Recover standard registers. */ - - lw t0, 30*4(sp) // Recover mepc - csrw mepc, t0 // Setup mepc - - li t0, 0x1880 // Prepare mstatus: MPP=Machine(0x1800) | MPIE(0x80) -#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) - li t1, 0x2000 // Set FS bits for FP state - or t0, t0, t1 -#endif - csrw mstatus, t0 // Set mstatus - - lw ra, 28*4(sp) // Recover return address - lw t0, 19*4(sp) // Recover t0 - lw t1, 18*4(sp) // Recover t1 - lw t2, 17*4(sp) // Recover t2 - lw s0, 12*4(sp) // Recover s0 - lw x9, 11*4(sp) // Recover s1 - lw a0, 27*4(sp) // Recover a0 - lw a1, 26*4(sp) // Recover a1 - lw a2, 25*4(sp) // Recover a2 - lw a3, 24*4(sp) // Recover a3 - lw a4, 23*4(sp) // Recover a4 - lw a5, 22*4(sp) // Recover a5 - lw a6, 21*4(sp) // Recover a6 - lw a7, 20*4(sp) // Recover a7 - lw t3, 16*4(sp) // Recover t3 - lw t4, 15*4(sp) // Recover t4 - lw t5, 14*4(sp) // Recover t5 - lw t6, 13*4(sp) // Recover t6 - lw x18, 10*4(sp) // Recover s2 - lw x19, 9*4(sp) // Recover s3 - lw x20, 8*4(sp) // Recover s4 - lw x21, 7*4(sp) // Recover s5 - lw x22, 6*4(sp) // Recover s6 - lw x23, 5*4(sp) // Recover s7 - lw x24, 4*4(sp) // Recover s8 - lw x25, 3*4(sp) // Recover s9 - lw x26, 2*4(sp) // Recover s10 - lw x27, 1*4(sp) // Recover s11 - -#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) - addi sp, sp, 65*4 // Recover stack frame - with floating point enabled -#else - addi sp, sp, 32*4 // Recover stack frame - without floating point enabled -#endif - mret // Return to thread - -_tx_thread_solicited_return: - - /* Recover floating point registers. */ -#if defined(__riscv_float_abi_single) - flw f8, 15*4(sp) // Recover fs0 - flw f9, 16*4(sp) // Recover fs1 - flw f18, 17*4(sp) // Recover fs2 - flw f19, 18*4(sp) // Recover fs3 - flw f20, 19*4(sp) // Recover fs4 - flw f21, 20*4(sp) // Recover fs5 - flw f22, 21*4(sp) // Recover fs6 - flw f23, 22*4(sp) // Recover fs7 - flw f24, 23*4(sp) // Recover fs8 - flw f25, 24*4(sp) // Recover fs9 - flw f26, 25*4(sp) // Recover fs10 - flw f27, 26*4(sp) // Recover fs11 - lw t0, 27*4(sp) // Recover fcsr - csrw fcsr, t0 // Restore fcsr -#elif defined(__riscv_float_abi_double) - fld f8, 15*4(sp) // Recover fs0 - fld f9, 16*4(sp) // Recover fs1 - fld f18, 17*4(sp) // Recover fs2 - fld f19, 18*4(sp) // Recover fs3 - fld f20, 19*4(sp) // Recover fs4 - fld f21, 20*4(sp) // Recover fs5 - fld f22, 21*4(sp) // Recover fs6 - fld f23, 22*4(sp) // Recover fs7 - fld f24, 23*4(sp) // Recover fs8 - fld f25, 24*4(sp) // Recover fs9 - fld f26, 25*4(sp) // Recover fs10 - fld f27, 26*4(sp) // Recover fs11 - lw t0, 27*4(sp) // Recover fcsr - csrw fcsr, t0 // Restore fcsr -#endif - - /* Recover standard registers. */ - - lw t0, 14*4(sp) // Recover mstatus - csrw mstatus, t0 // Restore mstatus - - lw ra, 13*4(sp) // Recover return address - lw s0, 12*4(sp) // Recover s0 - lw s1, 11*4(sp) // Recover s1 - lw x18, 10*4(sp) // Recover s2 - lw x19, 9*4(sp) // Recover s3 - lw x20, 8*4(sp) // Recover s4 - lw x21, 7*4(sp) // Recover s5 - lw x22, 6*4(sp) // Recover s6 - lw x23, 5*4(sp) // Recover s7 - lw x24, 4*4(sp) // Recover s8 - lw x25, 3*4(sp) // Recover s9 - lw x26, 2*4(sp) // Recover s10 - lw x27, 1*4(sp) // Recover s11 - -#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) - addi sp, sp, 29*4 // Recover stack frame - with floating point enabled -#else - addi sp, sp, 16*4 // Recover stack frame - without floating point enabled -#endif - ret // Return to thread - -/* } */ diff --git a/ports/risc-v32/clang/src/tx_thread_stack_build.S b/ports/risc-v32/clang/src/tx_thread_stack_build.S deleted file mode 100644 index 2b8ebae11..000000000 --- a/ports/risc-v32/clang/src/tx_thread_stack_build.S +++ /dev/null @@ -1,221 +0,0 @@ -/*************************************************************************** - * Copyright (c) 2026 Quintauris - * - * This program and the accompanying materials are made available under the - * terms of the MIT License which is available at - * https://opensource.org/licenses/MIT. - * - * SPDX-License-Identifier: MIT - **************************************************************************/ - - -/**************************************************************************/ -/**************************************************************************/ -/** */ -/** ThreadX Component */ -/** */ -/** Thread */ -/** */ -/**************************************************************************/ -/**************************************************************************/ - - - .section .text -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_stack_build RISC-V32/GNU */ -/* 6.4.x */ -/* AUTHOR */ -/* */ -/* Francisco Merino, Quintauris */ -/* */ -/* DESCRIPTION */ -/* */ -/* This function builds a stack frame on the supplied thread's stack. */ -/* The stack frame results in a fake interrupt return to the supplied */ -/* function pointer. */ -/* */ -/* INPUT */ -/* */ -/* thread_ptr Pointer to thread control blk */ -/* function_ptr Pointer to return function */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* _tx_thread_create Create thread service */ -/* */ -/**************************************************************************/ -/* VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) -{ */ - .global _tx_thread_stack_build -_tx_thread_stack_build: - - /* Build a fake interrupt frame. The form of the fake interrupt stack - on the RISC-V should look like the following after it is built: - Reg Index - Stack Top: 1 0 Interrupt stack frame type - x27 1 Initial s11 - x26 2 Initial s10 - x25 3 Initial s9 - x24 4 Initial s8 - x23 5 Initial s7 - x22 6 Initial s6 - x21 7 Initial s5 - x20 8 Initial s4 - x19 9 Initial s3 - x18 10 Initial s2 - x9 11 Initial s1 - x8 12 Initial s0 - x31 13 Initial t6 - x30 14 Initial t5 - x29 15 Initial t4 - x28 16 Initial t3 - x7 17 Initial t2 - x6 18 Initial t1 - x5 19 Initial t0 - x17 20 Initial a7 - x16 21 Initial a6 - x15 22 Initial a5 - x14 23 Initial a4 - x13 24 Initial a3 - x12 25 Initial a2 - x11 26 Initial a1 - x10 27 Initial a0 - x1 28 Initial ra - -- 29 reserved - mepc 30 Initial mepc -If floating point support: - f0 31 Initial ft0 - f1 32 Initial ft1 - f2 33 Initial ft2 - f3 34 Initial ft3 - f4 35 Initial ft4 - f5 36 Initial ft5 - f6 37 Initial ft6 - f7 38 Initial ft7 - f8 39 Initial fs0 - f9 40 Initial fs1 - f10 41 Initial fa0 - f11 42 Initial fa1 - f12 43 Initial fa2 - f13 44 Initial fa3 - f14 45 Initial fa4 - f15 46 Initial fa5 - f16 47 Initial fa6 - f17 48 Initial fa7 - f18 49 Initial fs2 - f19 50 Initial fs3 - f20 51 Initial fs4 - f21 52 Initial fs5 - f22 53 Initial fs6 - f23 54 Initial fs7 - f24 55 Initial fs8 - f25 56 Initial fs9 - f26 57 Initial fs10 - f27 58 Initial fs11 - f28 59 Initial ft8 - f29 60 Initial ft9 - f30 61 Initial ft10 - f31 62 Initial ft11 - fscr 63 Initial fscr - - Stack Bottom: (higher memory address) */ - - lw t0, 16(a0) // Pickup end of stack area - li t1, ~15 // Build 16-byte alignment mask - and t0, t0, t1 // Make sure 16-byte alignment - - /* Actually build the stack frame. */ - -#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) - addi t0, t0, -65*4 -#else - addi t0, t0, -32*4 // Allocate space for the stack frame -#endif - li t1, 1 // Build stack type - sw t1, 0*4(t0) // Place stack type on the top - sw zero, 1*4(t0) // Initial s11 - sw zero, 2*4(t0) // Initial s10 - sw zero, 3*4(t0) // Initial s9 - sw zero, 4*4(t0) // Initial s8 - sw zero, 5*4(t0) // Initial s7 - sw zero, 6*4(t0) // Initial s6 - sw zero, 7*4(t0) // Initial s5 - sw zero, 8*4(t0) // Initial s4 - sw zero, 9*4(t0) // Initial s3 - sw zero, 10*4(t0) // Initial s2 - sw zero, 11*4(t0) // Initial s1 - sw zero, 12*4(t0) // Initial s0 - sw zero, 13*4(t0) // Initial t6 - sw zero, 14*4(t0) // Initial t5 - sw zero, 15*4(t0) // Initial t4 - sw zero, 16*4(t0) // Initial t3 - sw zero, 17*4(t0) // Initial t2 - sw zero, 18*4(t0) // Initial t1 - sw zero, 19*4(t0) // Initial t0 - sw zero, 20*4(t0) // Initial a7 - sw zero, 21*4(t0) // Initial a6 - sw zero, 22*4(t0) // Initial a5 - sw zero, 23*4(t0) // Initial a4 - sw zero, 24*4(t0) // Initial a3 - sw zero, 25*4(t0) // Initial a2 - sw zero, 26*4(t0) // Initial a1 - sw zero, 27*4(t0) // Initial a0 - sw zero, 28*4(t0) // Initial ra - sw a1, 30*4(t0) // Initial mepc (thread entry point) -#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) - sw zero, 31*4(t0) // Initial ft0 - sw zero, 32*4(t0) // Initial ft1 - sw zero, 33*4(t0) // Initial ft2 - sw zero, 34*4(t0) // Initial ft3 - sw zero, 35*4(t0) // Initial ft4 - sw zero, 36*4(t0) // Initial ft5 - sw zero, 37*4(t0) // Initial ft6 - sw zero, 38*4(t0) // Initial ft7 - sw zero, 39*4(t0) // Initial fs0 - sw zero, 40*4(t0) // Initial fs1 - sw zero, 41*4(t0) // Initial fa0 - sw zero, 42*4(t0) // Initial fa1 - sw zero, 43*4(t0) // Initial fa2 - sw zero, 44*4(t0) // Initial fa3 - sw zero, 45*4(t0) // Initial fa4 - sw zero, 46*4(t0) // Initial fa5 - sw zero, 47*4(t0) // Initial fa6 - sw zero, 48*4(t0) // Initial fa7 - sw zero, 49*4(t0) // Initial fs2 - sw zero, 50*4(t0) // Initial fs3 - sw zero, 51*4(t0) // Initial fs4 - sw zero, 52*4(t0) // Initial fs5 - sw zero, 53*4(t0) // Initial fs6 - sw zero, 54*4(t0) // Initial fs7 - sw zero, 55*4(t0) // Initial fs8 - sw zero, 56*4(t0) // Initial fs9 - sw zero, 57*4(t0) // Initial fs10 - sw zero, 58*4(t0) // Initial fs11 - sw zero, 59*4(t0) // Initial ft8 - sw zero, 60*4(t0) // Initial ft9 - sw zero, 61*4(t0) // Initial ft10 - sw zero, 62*4(t0) // Initial ft11 - csrr a1, fcsr // Read fcsr for initial value - sw a1, 63*4(t0) // Initial fcsr - sw zero, 64*4(t0) // Reserved word (0) -#else - sw zero, 31*4(t0) // Reserved word (0) -#endif - - /* Setup stack pointer. */ - /* thread_ptr -> tx_thread_stack_ptr = t0; */ - - sw t0, 8(a0) // Save stack pointer in thread's - ret // control block and return -/* } */ diff --git a/ports/risc-v32/clang/src/tx_thread_system_return.S b/ports/risc-v32/clang/src/tx_thread_system_return.S deleted file mode 100644 index b54f54fa8..000000000 --- a/ports/risc-v32/clang/src/tx_thread_system_return.S +++ /dev/null @@ -1,168 +0,0 @@ -/*************************************************************************** - * Copyright (c) 2026 Quintauris - * - * This program and the accompanying materials are made available under the - * terms of the MIT License which is available at - * https://opensource.org/licenses/MIT. - * - * SPDX-License-Identifier: MIT - **************************************************************************/ - - -/**************************************************************************/ -/**************************************************************************/ -/** */ -/** ThreadX Component */ -/** */ -/** Thread */ -/** */ -/**************************************************************************/ -/**************************************************************************/ - - - .section .text -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_system_return RISC-V32/GNU */ -/* 6.4.x */ -/* AUTHOR */ -/* */ -/* Francisco Merino, Quintauris */ -/* */ -/* DESCRIPTION */ -/* */ -/* This function is target processor specific. It is used to transfer */ -/* control from a thread back to the system. Only a minimal context */ -/* is saved since the compiler assumes temp registers are going to get */ -/* slicked by a function call anyway. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* _tx_thread_schedule Thread scheduling loop */ -/* */ -/* CALLED BY */ -/* */ -/* ThreadX components */ -/* */ -/**************************************************************************/ -/* VOID _tx_thread_system_return(VOID) -{ */ - .global _tx_thread_system_return -_tx_thread_system_return: - - /* Save minimal context on the stack. */ - /* sp -= sizeof(stack_frame); */ - -#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) - addi sp, sp, -29*4 // Allocate space on the stack - with floating point enabled -#else - addi sp, sp, -16*4 // Allocate space on the stack - without floating point enabled -#endif - - /* Store floating point preserved registers. */ -#if defined(__riscv_float_abi_single) - fsw f8, 15*4(sp) // Store fs0 - fsw f9, 16*4(sp) // Store fs1 - fsw f18, 17*4(sp) // Store fs2 - fsw f19, 18*4(sp) // Store fs3 - fsw f20, 19*4(sp) // Store fs4 - fsw f21, 20*4(sp) // Store fs5 - fsw f22, 21*4(sp) // Store fs6 - fsw f23, 22*4(sp) // Store fs7 - fsw f24, 23*4(sp) // Store fs8 - fsw f25, 24*4(sp) // Store fs9 - fsw f26, 25*4(sp) // Store fs10 - fsw f27, 26*4(sp) // Store fs11 - csrr t0, fcsr - sw t0, 27*4(sp) // Store fcsr -#elif defined(__riscv_float_abi_double) - fsd f8, 15*4(sp) // Store fs0 - fsd f9, 16*4(sp) // Store fs1 - fsd f18, 17*4(sp) // Store fs2 - fsd f19, 18*4(sp) // Store fs3 - fsd f20, 19*4(sp) // Store fs4 - fsd f21, 20*4(sp) // Store fs5 - fsd f22, 21*4(sp) // Store fs6 - fsd f23, 22*4(sp) // Store fs7 - fsd f24, 23*4(sp) // Store fs8 - fsd f25, 24*4(sp) // Store fs9 - fsd f26, 25*4(sp) // Store fs10 - fsd f27, 26*4(sp) // Store fs11 - csrr t0, fcsr - sw t0, 27*4(sp) // Store fcsr -#endif - - sw zero, 0(sp) // Solicited stack type - sw ra, 13*4(sp) // Save return address - sw s0, 12*4(sp) // Save s0 - sw s1, 11*4(sp) // Save s1 - sw s2, 10*4(sp) // Save s2 - sw s3, 9*4(sp) // Save s3 - sw s4, 8*4(sp) // Save s4 - sw s5, 7*4(sp) // Save s5 - sw s6, 6*4(sp) // Save s6 - sw s7, 5*4(sp) // Save s7 - sw s8, 4*4(sp) // Save s8 - sw s9, 3*4(sp) // Save s9 - sw s10, 2*4(sp) // Save s10 - sw s11, 1*4(sp) // Save s11 - csrr t0, mstatus // Pickup mstatus - sw t0, 14*4(sp) // Save mstatus - - - /* Lockout interrupts. will be enabled in _tx_thread_schedule */ - - csrci mstatus, 0x08 // Disable interrupts (MIE bit 3) - -#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY - - call _tx_execution_thread_exit // Call the thread execution exit function -#endif - - la t0, _tx_thread_current_ptr // Pickup address of pointer - lw t1, 0(t0) // Pickup current thread pointer - la t2, _tx_thread_system_stack_ptr // Pickup stack pointer address - - /* Save current stack and switch to system stack. */ - /* _tx_thread_current_ptr -> tx_thread_stack_ptr = SP; - SP = _tx_thread_system_stack_ptr; */ - - sw sp, 8(t1) // Save stack pointer - lw sp, 0(t2) // Switch to system stack - - /* Determine if the time-slice is active. */ - /* if (_tx_timer_time_slice) - { */ - - la t4, _tx_timer_time_slice // Pickup time slice variable addr - lw t3, 0(t4) // Pickup time slice value - la t2, _tx_thread_schedule // Pickup address of scheduling loop - beqz t3, _tx_thread_dont_save_ts // If no time-slice, don't save it - - /* Save time-slice for the thread and clear the current time-slice. */ - /* _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; - _tx_timer_time_slice = 0; */ - - sw t3, 24(t1) // Save current time-slice for thread - sw zero, 0(t4) // Clear time-slice variable - - /* } */ -_tx_thread_dont_save_ts: - - /* Clear the current thread pointer. */ - /* _tx_thread_current_ptr = TX_NULL; */ - - sw x0, 0(t0) // Clear current thread pointer - jr t2 // Return to thread scheduler - -/* } */ diff --git a/ports/risc-v32/clang/src/tx_timer_interrupt.S b/ports/risc-v32/clang/src/tx_timer_interrupt.S deleted file mode 100644 index b64bf06b4..000000000 --- a/ports/risc-v32/clang/src/tx_timer_interrupt.S +++ /dev/null @@ -1,204 +0,0 @@ -/*************************************************************************** - * Copyright (c) 2026 Quintauris - * - * This program and the accompanying materials are made available under the - * terms of the MIT License which is available at - * https://opensource.org/licenses/MIT. - * - * SPDX-License-Identifier: MIT - **************************************************************************/ - - -/**************************************************************************/ -/**************************************************************************/ -/** */ -/** ThreadX Component */ -/** */ -/** Timer */ -/** */ -/**************************************************************************/ -/**************************************************************************/ - - .section .text - .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_timer_interrupt RISC-V32/GNU */ -/* 6.2.1 */ -/* AUTHOR */ -/* */ -/* Francisco Merino, Quintauris */ -/* */ -/* DESCRIPTION */ -/* */ -/* This function processes the hardware timer interrupt. This */ -/* processing includes incrementing the system clock and checking for */ -/* time slice and/or timer expiration. If either is found, the */ -/* interrupt context save/restore functions are called along with the */ -/* expiration functions. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* _tx_timer_expiration_process Timer expiration processing */ -/* _tx_thread_time_slice Time slice interrupted thread */ -/* */ -/* CALLED BY */ -/* */ -/* interrupt vector */ -/* */ -/**************************************************************************/ -/* VOID _tx_timer_interrupt(VOID) -{ */ - .global _tx_timer_interrupt -_tx_timer_interrupt: - - /* Increment the system clock. */ - /* _tx_timer_system_clock++; */ - - la t0, _tx_timer_system_clock // Pickup address of system clock - lw t1, 0(t0) // Pickup system clock - la t2, _tx_timer_time_slice // Pickup address of time slice - lw t3, 0(t2) // Pickup time slice - addi t1, t1, 1 // Increment system clock - sw t1, 0(t0) // Store new system clock - li t6, 0 // Clear local expired flag - - /* Test for time-slice expiration. */ - /* if (_tx_timer_time_slice) - { */ - - beqz t3, _tx_timer_no_time_slice // If 0, skip time slice processing - addi t3, t3, -1 // Decrement the time slice - - /* Decrement the time_slice. */ - /* _tx_timer_time_slice--; */ - - sw t3, 0(t2) // Store new time slice - - /* Check for expiration. */ - /* if (_tx_timer_time_slice == 0) */ - - bgtz t3, _tx_timer_no_time_slice // If not 0, has not expired yet - li t1, 1 // Build expired flag - - /* Set the time-slice expired flag. */ - /* _tx_timer_expired_time_slice = TX_TRUE; */ - - la t4, _tx_timer_expired_time_slice // Get address of expired flag - sw t1, 0(t4) // Set expired flag (UINT) - ori t6, t6, 1 // Set local expired flag - - /* } */ - -_tx_timer_no_time_slice: - - /* Test for timer expiration. */ - /* if (*_tx_timer_current_ptr) - { */ - - la t0, _tx_timer_current_ptr // Pickup address of current ptr - lw t1, 0(t0) // Pickup current pointer (word) - lw t3, 0(t1) // Pickup the current timer entry (word) - la t2, _tx_timer_expired // Pickup address of timer expired flag - li t4, 1 // Build TX_TRUE flag - beqz t3, _tx_timer_no_timer // If NULL, no timer has expired - - /* Set expiration flag. */ - /* _tx_timer_expired = TX_TRUE; */ - - ori t6, t6, 2 // Set local expired flag - sw t4, 0(t2) // Set expired flag in memory (UINT) - j _tx_timer_done // Finished timer processing - - - /* } - else - { */ -_tx_timer_no_timer: - - /* No timer expired, increment the timer pointer. */ - /* _tx_timer_current_ptr++; */ - - /* Check for wrap-around. */ - /* if (_tx_timer_current_ptr == _tx_timer_list_end) */ - - la t2, _tx_timer_list_end // Pickup address of list end pointer - lw t3, 0(t2) // Pickup actual list end - addi t1, t1, 4 // Point to next timer entry - sw t1, 0(t0) // Store new timer pointer - bne t1, t3, _tx_timer_skip_wrap // If not same, good pointer - - /* Wrap to beginning of list. */ - /* _tx_timer_current_ptr = _tx_timer_list_start; */ - - la t2, _tx_timer_list_start // Pickup address of list start pointer - lw t4, 0(t2) // Pickup start of the list - sw t4, 0(t0) // Store new timer pointer - - -_tx_timer_skip_wrap: - /* } */ - -_tx_timer_done: - - - /* See if anything has expired. */ - /* if ((_tx_timer_expired_time_slice) || (_tx_timer_expired)) - { */ - - beqz t6, _tx_timer_nothing_expired // If nothing expired skip the rest - addi sp, sp, -16 // Allocate some storage on the stack - sw t6, 0(sp) // Save local expired flag - sw ra, 4(sp) // Save ra - - /* Did a timer expire? */ - /* if (_tx_timer_expired) - { */ - - andi t2, t6, 2 // Isolate the timer expired bit - beqz t2, _tx_timer_dont_activate // No, timer not expired - - /* Call the timer expiration processing. */ - /* _tx_timer_expiration_process(void); */ - - call _tx_timer_expiration_process // Call _tx_timer_expiration_process - lw t6, 0(sp) // Recover local expired flag - - /* } */ -_tx_timer_dont_activate: - - /* Did time slice expire? */ - /* if (_tx_timer_expired_time_slice) - { */ - - andi t2, t6, 1 // Is the timer expired bit set? - beqz t2, _tx_timer_not_ts_expiration // If not, skip time slice processing - - /* Time slice interrupted thread. */ - /* _tx_thread_time_slice(); */ - - call _tx_thread_time_slice // Call time slice - - /* } */ - -_tx_timer_not_ts_expiration: - - lw ra, 4(sp) // Recover ra - addi sp, sp, 16 // Recover stack space - /* } */ - -_tx_timer_nothing_expired: - - ret - -/* } */ \ No newline at end of file diff --git a/ports/risc-v32/common/tx_port_riscv32_common.h b/ports/risc-v32/common/tx_port_riscv32_common.h new file mode 100644 index 000000000..886565a81 --- /dev/null +++ b/ports/risc-v32/common/tx_port_riscv32_common.h @@ -0,0 +1,268 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * Copyright (c) 2026-present Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port_riscv32_common.h RISC-V32 */ +/* 6.4.x */ +/* */ +/* DESCRIPTION */ +/* */ +/* Shared port definitions for all RISC-V32 toolchain ports (GNU, */ +/* Clang, etc.). Each toolchain's tx_port.h includes this file and */ +/* may add toolchain-specific definitions afterwards. */ +/* */ +/**************************************************************************/ + +/* This file is included from a toolchain-specific tx_port.h; do not */ +/* include it directly. */ + +#ifndef TX_PORT_H +#error "Include tx_port.h, not tx_port_riscv32_common.h directly." +#endif + + +#ifndef __ASSEMBLER__ + +/* Include for memset. */ +#include + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif /* TX_INCLUDE_USER_DEFINE_FILE */ + +#endif /* __ASSEMBLER__ */ + + +/* Define ThreadX basic types for this port. */ + +#define VOID void + +#ifndef __ASSEMBLER__ +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef long LONG; +typedef unsigned long ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED +#endif /* __ASSEMBLER__ */ + + + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 1024 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 1024 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX RISC-V port. */ + +#define TX_INT_DISABLE 0x00000000 /* Disable interrupts value */ +#define TX_INT_ENABLE 0x00000008 /* Enable interrupt value */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE ++_tx_trace_simulated_time +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS 0 + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#define TX_INLINE_INITIALIZATION + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 +#define TX_THREAD_EXTENSION_3 + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +/* Expose helper used to perform an atomic read/modify/write of mstatus. + The helper composes and returns the posture per ThreadX contract. */ +#ifndef __ASSEMBLER__ +UINT _tx_thread_interrupt_control(UINT new_posture); +#endif + +#ifdef TX_DISABLE_INLINE + +#define TX_INTERRUPT_SAVE_AREA register UINT interrupt_save; + +#define TX_DISABLE __asm__ volatile("csrrci %0, mstatus, 8" : "=r" (interrupt_save) :: "memory"); +#define TX_RESTORE { \ + unsigned long _temp_mstatus; \ + __asm__ volatile( \ + "csrc mstatus, 8\n" \ + "andi %0, %1, 8\n" \ + "csrs mstatus, %0" \ + : "=&r" (_temp_mstatus) \ + : "r" (interrupt_save) \ + : "memory"); \ + } + +#else + +#define TX_INTERRUPT_SAVE_AREA register UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_interrupt_control(TX_INT_DISABLE); +#define TX_RESTORE _tx_thread_interrupt_control(interrupt_save); + +#endif /* TX_DISABLE_INLINE */ + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE diff --git a/ports/risc-v32/gnu/CMakeLists.txt b/ports/risc-v32/gnu/CMakeLists.txt index 7c6785bba..050007573 100644 --- a/ports/risc-v32/gnu/CMakeLists.txt +++ b/ports/risc-v32/gnu/CMakeLists.txt @@ -1,23 +1,7 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../../../cmake/threadx_riscv_port.cmake) -target_sources(${PROJECT_NAME} - PRIVATE - # {{BEGIN_TARGET_SOURCES}} - ${CMAKE_CURRENT_LIST_DIR}/src/tx_initialize_low_level.S - ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_context_restore.S - ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_context_save.S - ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_interrupt_control.S - ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_schedule.S - ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_stack_build.S - ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_system_return.S - ${CMAKE_CURRENT_LIST_DIR}/src/tx_timer_interrupt.S - # {{END_TARGET_SOURCES}} +threadx_add_riscv_port( + SRC_DIR ${CMAKE_CURRENT_LIST_DIR}/src + INC_DIR ${CMAKE_CURRENT_LIST_DIR}/inc + EXAMPLE_DIR ${CMAKE_CURRENT_LIST_DIR}/example_build/qemu_virt ) - -target_include_directories(${PROJECT_NAME} - PUBLIC - ${CMAKE_CURRENT_LIST_DIR}/inc -) - -if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/example_build/qemu_virt/CMakeLists.txt) - add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/example_build/qemu_virt) -endif() diff --git a/ports/risc-v32/gnu/example_build/core_v_mcu/CMakeLists.txt b/ports/risc-v32/gnu/example_build/core_v_mcu/CMakeLists.txt index eb6b4c89f..6946b3474 100644 --- a/ports/risc-v32/gnu/example_build/core_v_mcu/CMakeLists.txt +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/CMakeLists.txt @@ -10,6 +10,10 @@ set(TX_USER_FILE ${CORE_V_MCU_DIR}/include/tx_user.h) set(THREADX_ARCH "risc-v32") set(THREADX_TOOLCHAIN "gnu") + +message(STATUS "Toolchain file: ${CMAKE_TOOLCHAIN_FILE}") +message(STATUS "C compiler: ${CMAKE_C_COMPILER}") + add_subdirectory(${THREADX_ROOT} threadx) target_include_directories(threadx PRIVATE ${CORE_V_MCU_DIR}/include) @@ -17,6 +21,7 @@ set(SRCS ${CORE_V_MCU_DIR}/crt0.S ${CORE_V_MCU_DIR}/vectors.S ${CORE_V_MCU_DIR}/tx_initialize_low_level.S + ${CORE_V_MCU_DIR}/bsp/clz.c ${CORE_V_MCU_DIR}/bsp/irq.c ${CORE_V_MCU_DIR}/bsp/timer_irq.c ${CORE_V_MCU_DIR}/bsp/fll.c diff --git a/ports/risc-v32/gnu/example_build/core_v_mcu/bsp/clz.c b/ports/risc-v32/gnu/example_build/core_v_mcu/bsp/clz.c new file mode 100644 index 000000000..ef3cb23ba --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/bsp/clz.c @@ -0,0 +1,29 @@ +/* clz.c — portable fallback for __clzsi2 (count leading zeros, 32-bit) + * + * The riscv-collab bare-metal toolchain (riscv64-unknown-elf from /opt/riscv) + * is built without multilib and does not ship an rv32/ilp32 libgcc. As a + * result __clzsi2, which GCC emits for __builtin_clz() on targets without a + * hardware CLZ instruction, is missing at link time. + * + * This file provides a weak definition so that the build is self-contained + * with any RISC-V bare-metal toolchain. When a toolchain does ship the symbol + * in libgcc (e.g. the Ubuntu gcc-riscv64-unknown-elf package), the libgcc copy + * will take precedence over this weak one. + */ + +#include + +__attribute__((weak)) +int __clzsi2(uint32_t x) +{ + if (x == 0U) + return 32; + + int n = 0; + if ((x & 0xFFFF0000U) == 0U) { n += 16; x <<= 16; } + if ((x & 0xFF000000U) == 0U) { n += 8; x <<= 8; } + if ((x & 0xF0000000U) == 0U) { n += 4; x <<= 4; } + if ((x & 0xC0000000U) == 0U) { n += 2; x <<= 2; } + if ((x & 0x80000000U) == 0U) { n += 1; } + return n; +} diff --git a/ports/risc-v32/gnu/example_build/cva6_ariane/entry.s b/ports/risc-v32/gnu/example_build/cva6_ariane/entry.S similarity index 100% rename from ports/risc-v32/gnu/example_build/cva6_ariane/entry.s rename to ports/risc-v32/gnu/example_build/cva6_ariane/entry.S diff --git a/ports/risc-v32/gnu/example_build/cva6_ariane/plic.c b/ports/risc-v32/gnu/example_build/cva6_ariane/plic.c deleted file mode 100644 index 01e5c71a4..000000000 --- a/ports/risc-v32/gnu/example_build/cva6_ariane/plic.c +++ /dev/null @@ -1,72 +0,0 @@ -#include "plic.h" -#include -irq_callback callbacks[MAX_CALLBACK_NUM]; - -void plic_irq_enable(int irqno) -{ - int hart = riscv_get_core(); - *(uint32_t*)PLIC_MENABLE(hart) = (*(uint32_t*)PLIC_MENABLE(hart) | (1 << irqno)); - return; -} - -void plic_irq_disable(int irqno) -{ - int hart = riscv_get_core(); - *(uint32_t*)PLIC_MENABLE(hart) = (*(uint32_t*)PLIC_MENABLE(hart) & (~(1 << irqno))); - return; -} - -void plic_prio_set(int irqno, int prio) -{ - PLIC_SET_PRIO(irqno, prio); -} - -int plic_prio_get(int irqno) -{ - return 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 - -#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 - diff --git a/ports/risc-v32/gnu/example_build/cva6_ariane/plic.h b/ports/risc-v32/gnu/example_build/cva6_ariane/plic.h new file mode 120000 index 000000000..f776a3d2a --- /dev/null +++ b/ports/risc-v32/gnu/example_build/cva6_ariane/plic.h @@ -0,0 +1 @@ +../../../../risc-v_common/example_build/plic/plic.h \ No newline at end of file diff --git a/ports/risc-v32/gnu/example_build/qemu_virt/CMakeLists.txt b/ports/risc-v32/gnu/example_build/qemu_virt/CMakeLists.txt index 9e9ff7223..43e757ace 100644 --- a/ports/risc-v32/gnu/example_build/qemu_virt/CMakeLists.txt +++ b/ports/risc-v32/gnu/example_build/qemu_virt/CMakeLists.txt @@ -2,7 +2,7 @@ set(QEMU_DEMO_DIR ${CMAKE_CURRENT_LIST_DIR}) add_executable(kernel.elf EXCLUDE_FROM_ALL ${QEMU_DEMO_DIR}/demo_threadx.c - ${QEMU_DEMO_DIR}/entry.s + ${QEMU_DEMO_DIR}/entry.S ${QEMU_DEMO_DIR}/uart.c ${QEMU_DEMO_DIR}/plic.c ${QEMU_DEMO_DIR}/hwtimer.c @@ -31,7 +31,7 @@ find_package(Python3 COMPONENTS Interpreter) if(Python3_FOUND) add_custom_target(check-functional-riscv32 COMMAND ${Python3_EXECUTABLE} - ${QEMU_DEMO_DIR}/test/azrtos_test_tx_gnu_riscv32_qemu.py + ${QEMU_DEMO_DIR}/test/threadx_test_tx_gnu_riscv32_qemu.py --elf $ --qemu qemu-system-riscv32 --gdb gdb 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 f3d806441..f6ec0bcd1 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 @@ -13,7 +13,7 @@ riscv32-unknown-elf-gcc \ -ffunction-sections -fdata-sections \ -I../../../../../common/inc \ -I../../inc \ - entry.s \ + entry.S \ tx_initialize_low_level.S \ board.c uart.c hwtimer.c plic.c trap.c demo_threadx.c \ -L../../../../../build -lthreadx \ diff --git a/ports/risc-v32/gnu/example_build/qemu_virt/csr.h b/ports/risc-v32/gnu/example_build/qemu_virt/csr.h deleted file mode 100644 index 9f986bacf..000000000 --- a/ports/risc-v32/gnu/example_build/qemu_virt/csr.h +++ /dev/null @@ -1,343 +0,0 @@ -/*************************************************************************** - * Copyright (c) 2025 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) // previous mode. -#define MSTATUS_MPP_M (3L << 11) -#define MSTATUS_MPP_S (1L << 11) -#define MSTATUS_MPP_U (0L << 11) -#define MSTATUS_MIE (1L << 3) // machine-mode interrupt enable. -#define MSTATUS_MPIE (1L << 7) -#define MSTATUS_FS (1L << 13) - -// Machine-mode Interrupt Enable -#define MIE_MTIE (1L << 7) -#define MIE_MSIE (1L << 3) -#define MIE_MEIE (1L << 11) -#define MIE_STIE (1L << 5) // supervisor timer -#define MIE_SSIE (1L << 1) -#define MIE_SEIE (1L << 9) - -// Supervisor Status Register, sstatus -#define SSTATUS_SPP (1L << 8) // Previous mode, 1=Supervisor, 0=User -#define SSTATUS_SPIE (1L << 5) // Supervisor Previous Interrupt Enable -#define SSTATUS_UPIE (1L << 4) // User Previous Interrupt Enable -#define SSTATUS_SIE (1L << 1) // Supervisor Interrupt Enable -#define SSTATUS_UIE (1L << 0) // User Interrupt Enable -#define SSTATUS_SPIE (1L << 5) -#define SSTATUS_UPIE (1L << 4) - -// Supervisor Interrupt Enable -#define SIE_SEIE (1L << 9) // external -#define SIE_STIE (1L << 5) // timer -#define SIE_SSIE (1L << 1) // software - -#ifndef __ASSEMBLER__ - -#include - -static inline uint32_t riscv_get_core() -{ - uint32_t x; - asm volatile("csrr %0, mhartid" : "=r" (x) ); - return x; -} - -static inline uint32_t riscv_get_mstatus() -{ - uint32_t x; - asm volatile("csrr %0, mstatus" : "=r" (x) ); - return x; -} - -static inline void riscv_writ_mstatus(uint32_t x) -{ - asm volatile("csrw mstatus, %0" : : "r" (x)); -} - -static inline void riscv_writ_mepc(uint32_t x) -{ - asm volatile("csrw mepc, %0" : : "r" (x)); -} - -static inline uint32_t riscv_get_sstatus() -{ - uint32_t x; - asm volatile("csrr %0, sstatus" : "=r" (x) ); - return x; -} - -static inline void riscv_writ_sstatus(uint32_t x) -{ - asm volatile("csrw sstatus, %0" : : "r" (x)); -} - -static inline uint32_t riscv_get_sip() -{ - uint32_t x; - asm volatile("csrr %0, sip" : "=r" (x) ); - return x; -} - -static inline void riscv_writ_sip(uint32_t x) -{ - asm volatile("csrw sip, %0" : : "r" (x)); -} - -static inline uint32_t riscv_get_sie() -{ - uint32_t x; - asm volatile("csrr %0, sie" : "=r" (x) ); - return x; -} - -static inline void riscv_writ_sie(uint32_t x) -{ - asm volatile("csrw sie, %0" : : "r" (x)); -} - -static inline uint32_t riscv_get_mie() -{ - uint32_t x; - asm volatile("csrr %0, mie" : "=r" (x) ); - return x; -} - -static inline void riscv_writ_mie(uint32_t x) -{ - asm volatile("csrw mie, %0" : : "r" (x)); -} - -static inline void riscv_writ_sepc(uint32_t x) -{ - asm volatile("csrw sepc, %0" : : "r" (x)); -} - -static inline uint32_t riscv_get_sepc() -{ - uint32_t x; - asm volatile("csrr %0, sepc" : "=r" (x) ); - return x; -} - -static inline uint32_t riscv_get_medeleg() -{ - uint32_t x; - asm volatile("csrr %0, medeleg" : "=r" (x) ); - return x; -} - -static inline void riscv_writ_medeleg(uint32_t x) -{ - asm volatile("csrw medeleg, %0" : : "r" (x)); -} - -static inline uint32_t riscv_get_mideleg() -{ - uint32_t x; - asm volatile("csrr %0, mideleg" : "=r" (x) ); - return x; -} - -static inline void riscv_writ_mideleg(uint32_t x) -{ - asm volatile("csrw mideleg, %0" : : "r" (x)); -} - -static inline void riscv_writ_stvec(uint32_t x) -{ - asm volatile("csrw stvec, %0" : : "r" (x)); -} - -static inline uint32_t riscv_get_stvec() -{ - uint32_t x; - asm volatile("csrr %0, stvec" : "=r" (x) ); - return x; -} - -static inline uint32_t riscv_get_stimecmp() -{ - uint32_t x; - asm volatile("csrr %0, 0x14d" : "=r" (x) ); - return x; -} - -static inline void riscv_writ_stimecmp(uint32_t x) -{ - asm volatile("csrw 0x14d, %0" : : "r" (x)); -} - -static inline uint32_t riscv_get_menvcfg() -{ - uint32_t x; - asm volatile("csrr %0, 0x30a" : "=r" (x) ); - return x; -} - -static inline void riscv_writ_menvcfg(uint32_t x) -{ - asm volatile("csrw 0x30a, %0" : : "r" (x)); -} - -static inline void riscv_writ_pmpcfg0(uint32_t x) -{ - asm volatile("csrw pmpcfg0, %0" : : "r" (x)); -} - -static inline void riscv_writ_pmpaddr0(uint32_t x) -{ - asm volatile("csrw pmpaddr0, %0" : : "r" (x)); -} - -static inline void riscv_writ_satp(uint32_t x) -{ - asm volatile("csrw satp, %0" : : "r" (x)); -} - -static inline uint32_t riscv_get_satp() -{ - uint32_t x; - asm volatile("csrr %0, satp" : "=r" (x) ); - return x; -} - -static inline uint32_t riscv_get_scause() -{ - uint32_t x; - asm volatile("csrr %0, scause" : "=r" (x) ); - return x; -} - -static inline uint32_t riscv_get_stval() -{ - uint32_t x; - asm volatile("csrr %0, stval" : "=r" (x) ); - return x; -} - -static inline void riscv_writ_mcounteren(uint32_t x) -{ - asm volatile("csrw mcounteren, %0" : : "r" (x)); -} - -static inline uint32_t riscv_get_mcounteren() -{ - uint32_t x; - asm volatile("csrr %0, mcounteren" : "=r" (x) ); - return x; -} - -static inline uint32_t riscv_get_time() -{ - uint32_t x; - asm volatile("csrr %0, time" : "=r" (x) ); - return x; -} - -static inline void riscv_sintr_on() -{ - uint32_t sstatus = riscv_get_sstatus(); - sstatus |= SSTATUS_SIE; - riscv_writ_sstatus(sstatus); -} - -static inline void riscv_sintr_off() -{ - uint32_t sstatus = riscv_get_sstatus(); - sstatus &= (~SSTATUS_SIE); - riscv_writ_sstatus(sstatus); -} - -static inline int riscv_sintr_get() -{ - uint32_t x = riscv_get_sstatus(); - return (x & SSTATUS_SIE) != 0; -} - -static inline void riscv_sintr_restore(int x) -{ - if(x) - riscv_sintr_on(); - else - riscv_sintr_off(); -} - -static inline void riscv_mintr_on() -{ - uint32_t mstatus = riscv_get_mstatus(); - mstatus |= MSTATUS_MIE; - riscv_writ_mstatus(mstatus); -} - -static inline void riscv_mintr_off() -{ - uint32_t mstatus = riscv_get_mstatus(); - mstatus &= (~MSTATUS_MIE); - riscv_writ_mstatus(mstatus); -} - -static inline int riscv_mintr_get() -{ - uint32_t x = riscv_get_mstatus(); - return (x & MSTATUS_MIE) != 0; -} - -static inline void riscv_mintr_restore(int x) -{ - if(x) - riscv_mintr_on(); - else - riscv_mintr_off(); -} - -static inline uint32_t riscv_get_sp() -{ - uint32_t x; - asm volatile("mv %0, sp" : "=r" (x) ); - return x; -} - -// read and write tp, the thread pointer, which xv6 uses to hold -// this core's hartid (core number), the index into cpus[]. -static inline uint32_t riscv_get_tp() -{ - uint32_t x; - asm volatile("mv %0, tp" : "=r" (x) ); - return x; -} - -static inline void riscv_writ_tp(uint32_t x) -{ - asm volatile("mv tp, %0" : : "r" (x)); -} - -static inline uint32_t riscv_get_ra() -{ - uint32_t x; - asm volatile("mv %0, ra" : "=r" (x) ); - return x; -} - -// flush the TLB. -static inline void sfence_vma() -{ - // the zero, zero means flush all TLB entries. - asm volatile("sfence.vma zero, zero"); -} - -#endif // __ASSEMBLER__ - -#endif diff --git a/ports/risc-v32/gnu/example_build/qemu_virt/csr.h b/ports/risc-v32/gnu/example_build/qemu_virt/csr.h new file mode 120000 index 000000000..b5df6f125 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/qemu_virt/csr.h @@ -0,0 +1 @@ +../../../../risc-v_common/inc/csr.h \ No newline at end of file diff --git a/ports/risc-v32/gnu/example_build/qemu_virt/entry.s b/ports/risc-v32/gnu/example_build/qemu_virt/entry.S similarity index 100% rename from ports/risc-v32/gnu/example_build/qemu_virt/entry.s rename to ports/risc-v32/gnu/example_build/qemu_virt/entry.S diff --git a/ports/risc-v32/gnu/example_build/qemu_virt/plic.c b/ports/risc-v32/gnu/example_build/qemu_virt/plic.c deleted file mode 100644 index 01e5c71a4..000000000 --- a/ports/risc-v32/gnu/example_build/qemu_virt/plic.c +++ /dev/null @@ -1,72 +0,0 @@ -#include "plic.h" -#include -irq_callback callbacks[MAX_CALLBACK_NUM]; - -void plic_irq_enable(int irqno) -{ - int hart = riscv_get_core(); - *(uint32_t*)PLIC_MENABLE(hart) = (*(uint32_t*)PLIC_MENABLE(hart) | (1 << irqno)); - return; -} - -void plic_irq_disable(int irqno) -{ - int hart = riscv_get_core(); - *(uint32_t*)PLIC_MENABLE(hart) = (*(uint32_t*)PLIC_MENABLE(hart) & (~(1 << irqno))); - return; -} - -void plic_prio_set(int irqno, int prio) -{ - PLIC_SET_PRIO(irqno, prio); -} - -int plic_prio_get(int irqno) -{ - return 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 - -#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 - diff --git a/ports/risc-v32/gnu/example_build/qemu_virt/plic.h b/ports/risc-v32/gnu/example_build/qemu_virt/plic.h new file mode 120000 index 000000000..f776a3d2a --- /dev/null +++ b/ports/risc-v32/gnu/example_build/qemu_virt/plic.h @@ -0,0 +1 @@ +../../../../risc-v_common/example_build/plic/plic.h \ No newline at end of file diff --git a/ports/risc-v32/gnu/example_build/qemu_virt/test/azrtos_test_tx_gnu_riscv32_qemu.py b/ports/risc-v32/gnu/example_build/qemu_virt/test/threadx_test_tx_gnu_riscv32_qemu.py similarity index 100% rename from ports/risc-v32/gnu/example_build/qemu_virt/test/azrtos_test_tx_gnu_riscv32_qemu.py rename to ports/risc-v32/gnu/example_build/qemu_virt/test/threadx_test_tx_gnu_riscv32_qemu.py diff --git a/ports/risc-v32/gnu/example_build/qemu_virt/trap.c b/ports/risc-v32/gnu/example_build/qemu_virt/trap.c deleted file mode 100644 index a2733e02a..000000000 --- a/ports/risc-v32/gnu/example_build/qemu_virt/trap.c +++ /dev/null @@ -1,67 +0,0 @@ -#include "csr.h" -#include -#include "uart.h" -#include "hwtimer.h" -#include "plic.h" -#include -#include - -#define OS_IS_INTERUPT(mcause) (mcause & 0x80000000u) -#define OS_IS_EXCEPTION(mcause) (~(OS_IS_INTERUPT)) -#define OS_IS_TICK_INT(mcause) (mcause == 0x80000007u) -#define OS_IS_SOFT_INT(mcause) (mcause == 0x80000003u) -#define OS_IS_EXT_INT(mcause) (mcause == 0x8000000bu) -#define OS_IS_TRAP_USER(mcause) (mcause == 0x0000000bu) -extern void _tx_timer_interrupt(void); - -extern int uart_putc(int ch); - -static void print_hex(uintptr_t val) -{ - char digits[] = "0123456789ABCDEF"; - uart_putc('0'); - uart_putc('x'); - for(int i = (sizeof(uintptr_t)*2) - 1; i >= 0; i--) { - int d = (val >> (i*4)) & 0xF; - uart_putc(digits[d]); - } - uart_putc('\n'); -} - -void trap_handler(uintptr_t mcause, uintptr_t mepc, uintptr_t mtval) -{ - // uart_puts("DEBUG : threadx/ports/risc-v32/gnu/example_build/qemu_virt/trap.c, trap_handler\n"); - if(OS_IS_INTERUPT(mcause)) - { - if(OS_IS_TICK_INT(mcause)) - { - hwtimer_handler(); - _tx_timer_interrupt(); - } - else if(OS_IS_EXT_INT(mcause)) - { - int ret = plic_irq_intr(); - if(ret) - { - puts("[INTERRUPT]: handler irq error!"); - while(1) ; - } - } - else - { - puts("[INTERRUPT]: now can't deal with the interrupt!"); - while(1) ; - } - } - else - { - puts("[EXCEPTION] : Unkown Error!!"); - puts("mcause:"); - print_hex(mcause); - puts("mepc:"); - print_hex(mepc); - puts("mtval:"); - print_hex(mtval); - while(1) ; - } -} diff --git a/ports/risc-v32/gnu/example_build/qemu_virt/trap.c b/ports/risc-v32/gnu/example_build/qemu_virt/trap.c new file mode 120000 index 000000000..bdce33054 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/qemu_virt/trap.c @@ -0,0 +1 @@ +../../../../risc-v_common/example_build/trap/trap_qemu.c \ No newline at end of file diff --git a/ports/risc-v32/gnu/example_build/qemu_virt/uart.c b/ports/risc-v32/gnu/example_build/qemu_virt/uart.c deleted file mode 100644 index a175b7d25..000000000 --- a/ports/risc-v32/gnu/example_build/qemu_virt/uart.c +++ /dev/null @@ -1,102 +0,0 @@ -#include "uart.h" -#include "csr.h" -#include "plic.h" -#include - -// the UART control registers are memory-mapped -// at address UART0. this macro returns the -// address of one of the registers. -#define Reg(reg) ((volatile unsigned char *)(UART0 + (reg))) - -// the UART control registers. -// some have different meanings for -// read vs write. -// see http://byterunner.com/16550.html -#define RHR 0 // receive holding register (for input bytes) -#define THR 0 // transmit holding register (for output bytes) -#define IER 1 // interrupt enable register -#define IER_RX_ENABLE (1<<0) -#define IER_TX_ENABLE (1<<1) -#define FCR 2 // FIFO control register -#define FCR_FIFO_ENABLE (1<<0) -#define FCR_FIFO_CLEAR (3<<1) // clear the content of the two FIFOs -#define ISR 2 // interrupt status register -#define LCR 3 // line control register -#define LCR_EIGHT_BITS (3<<0) -#define LCR_BAUD_LATCH (1<<7) // special mode to set baud rate -#define LSR 5 // line status register -#define LSR_RX_READY (1<<0) // input is waiting to be read from RHR -#define LSR_TX_IDLE (1<<5) // THR can accept another character to send - -#define ReadReg(reg) (*(Reg(reg))) -#define WriteReg(reg, v) (*(Reg(reg)) = (v)) - -int uart_init(void) -{ - // disable interrupts. - WriteReg(IER, 0x00); - - // special mode to set baud rate. - WriteReg(LCR, LCR_BAUD_LATCH); - - // LSB for baud rate of 38.4K. - WriteReg(0, 0x03); - - // MSB for baud rate of 38.4K. - WriteReg(1, 0x00); - - // leave set-baud mode, - // and set word length to 8 bits, no parity. - WriteReg(LCR, LCR_EIGHT_BITS); - - // reset and enable FIFOs. - WriteReg(FCR, FCR_FIFO_ENABLE | FCR_FIFO_CLEAR); - - // enable transmit and receive interrupts. - // WriteReg(IER, IER_TX_ENABLE | IER_RX_ENABLE); - - //enable UART0 in PLIC - plic_irq_enable(UART0_IRQ); - - //set UART0 priority in PLIC - plic_prio_set(UART0_IRQ, 1); - - //register callback for UART0 - //plic_register_callback(UART0_IRQ, uart_intr); - puts("[UART0] : Uart Init Done, this is Test output!"); - return 0; -} - -void uart_putc_nolock(int ch) -{ - // wait for Transmit Holding Empty to be set in LSR. - while((ReadReg(LSR) & LSR_TX_IDLE) == 0) - ; - WriteReg(THR, ch); - return; -} - -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/ports/risc-v32/gnu/example_build/qemu_virt/uart.c b/ports/risc-v32/gnu/example_build/qemu_virt/uart.c new file mode 120000 index 000000000..1666c8c8d --- /dev/null +++ b/ports/risc-v32/gnu/example_build/qemu_virt/uart.c @@ -0,0 +1 @@ +../../../../risc-v_common/example_build/uart/uart_qemu_ns16550.c \ No newline at end of file diff --git a/ports/risc-v32/gnu/example_build/qemu_virt/uart.h b/ports/risc-v32/gnu/example_build/qemu_virt/uart.h deleted file mode 100644 index debfd9dfa..000000000 --- a/ports/risc-v32/gnu/example_build/qemu_virt/uart.h +++ /dev/null @@ -1,23 +0,0 @@ -/*************************************************************************** - * Copyright (c) 2024 Microsoft Corporation - * Copyright (c) 2026-present Eclipse ThreadX contributors - * - * This program and the accompanying materials are made available under the - * terms of the MIT License which is available at - * https://opensource.org/licenses/MIT. - * - * SPDX-License-Identifier: MIT - **************************************************************************/ - -#ifndef RISCV_UART_H -#define RISCV_UART_H - -#define UART0 0x10000000L -#define UART0_IRQ 10 - -#define puts uart_puts -int uart_init(void); -int uart_putc(int ch); -void uart_putc_nolock(int ch); -int uart_puts(const char* str); -#endif diff --git a/ports/risc-v32/gnu/example_build/qemu_virt/uart.h b/ports/risc-v32/gnu/example_build/qemu_virt/uart.h new file mode 120000 index 000000000..aba5ae7b9 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/qemu_virt/uart.h @@ -0,0 +1 @@ +../../../../risc-v_common/example_build/uart/uart_qemu_ns16550.h \ No newline at end of file diff --git a/ports/risc-v32/gnu/inc/tx_port.h b/ports/risc-v32/gnu/inc/tx_port.h index 5348a02e3..11e9f87be 100644 --- a/ports/risc-v32/gnu/inc/tx_port.h +++ b/ports/risc-v32/gnu/inc/tx_port.h @@ -47,228 +47,11 @@ #ifndef TX_PORT_H #define TX_PORT_H -#ifndef __ASSEMBLER__ - -/* Include for memset. */ -#include - - -/* Determine if the optional ThreadX user define file should be used. */ - -#ifdef TX_INCLUDE_USER_DEFINE_FILE - - -/* Yes, include the user defines in tx_user.h. The defines in this file may - alternately be defined on the command line. */ - -#include "tx_user.h" -#endif /* TX_INCLUDE_USER_DEFINE_FILE */ - -#endif /* __ASSEMBLER__ */ - - -/* Define ThreadX basic types for this port. */ - -#define VOID void - -#ifndef __ASSEMBLER__ -typedef char CHAR; -typedef unsigned char UCHAR; -typedef int INT; -typedef unsigned int UINT; -typedef long LONG; -typedef unsigned long ULONG; -typedef unsigned long long ULONG64; -typedef short SHORT; -typedef unsigned short USHORT; -#define ULONG64_DEFINED -#endif /* __ASSEMBLER__ */ - - - - -/* Define the priority levels for ThreadX. Legal values range - from 32 to 1024 and MUST be evenly divisible by 32. */ - -#ifndef TX_MAX_PRIORITIES -#define TX_MAX_PRIORITIES 32 -#endif - - -/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during - thread creation is less than this value, the thread create call will return an error. */ - -#ifndef TX_MINIMUM_STACK -#define TX_MINIMUM_STACK 1024 /* Minimum stack size for this port */ -#endif - - -/* Define the system timer thread's default stack size and priority. These are only applicable - if TX_TIMER_PROCESS_IN_ISR is not defined. */ - -#ifndef TX_TIMER_THREAD_STACK_SIZE -#define TX_TIMER_THREAD_STACK_SIZE 1024 /* Default timer thread stack size */ -#endif - -#ifndef TX_TIMER_THREAD_PRIORITY -#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ -#endif - - -/* Define various constants for the ThreadX RISC-V port. */ - -#define TX_INT_DISABLE 0x00000000 /* Disable interrupts value */ -#define TX_INT_ENABLE 0x00000008 /* Enable interrupt value */ - - -/* Define the clock source for trace event entry time stamp. The following two item are port specific. - For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock - source constants would be: - -#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) -#define TX_TRACE_TIME_MASK 0x0000FFFFUL - -*/ - -#ifndef TX_TRACE_TIME_SOURCE -#define TX_TRACE_TIME_SOURCE ++_tx_trace_simulated_time -#endif -#ifndef TX_TRACE_TIME_MASK -#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL -#endif - - -/* Define the port specific options for the _tx_build_options variable. This variable indicates - how the ThreadX library was built. */ - -#define TX_PORT_SPECIFIC_BUILD_OPTIONS 0 - - -/* Define the in-line initialization constant so that modules with in-line - initialization capabilities can prevent their initialization from being - a function call. */ - -#define TX_INLINE_INITIALIZATION - - -/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is - disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack - checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING - define is negated, thereby forcing the stack fill which is necessary for the stack checking - logic. */ - -#ifdef TX_ENABLE_STACK_CHECKING -#undef TX_DISABLE_STACK_FILLING -#endif - - -/* Define the TX_THREAD control block extensions for this port. The main reason - for the multiple macros is so that backward compatibility can be maintained with - existing ThreadX kernel awareness modules. */ - -#define TX_THREAD_EXTENSION_0 -#define TX_THREAD_EXTENSION_1 -#define TX_THREAD_EXTENSION_2 -#define TX_THREAD_EXTENSION_3 - - -/* Define the port extensions of the remaining ThreadX objects. */ - -#define TX_BLOCK_POOL_EXTENSION -#define TX_BYTE_POOL_EXTENSION -#define TX_EVENT_FLAGS_GROUP_EXTENSION -#define TX_MUTEX_EXTENSION -#define TX_QUEUE_EXTENSION -#define TX_SEMAPHORE_EXTENSION -#define TX_TIMER_EXTENSION - - -/* Define the user extension field of the thread control block. Nothing - additional is needed for this port so it is defined as white space. */ - -#ifndef TX_THREAD_USER_EXTENSION -#define TX_THREAD_USER_EXTENSION -#endif - - -/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, - tx_thread_shell_entry, and tx_thread_terminate. */ - -#define TX_THREAD_CREATE_EXTENSION(thread_ptr) -#define TX_THREAD_DELETE_EXTENSION(thread_ptr) -#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) -#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) - - -/* Define the ThreadX object creation extensions for the remaining objects. */ - -#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) -#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) -#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) -#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) -#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) -#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) -#define TX_TIMER_CREATE_EXTENSION(timer_ptr) - - -/* Define the ThreadX object deletion extensions for the remaining objects. */ - -#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) -#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) -#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) -#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) -#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) -#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) -#define TX_TIMER_DELETE_EXTENSION(timer_ptr) - - -/* Define ThreadX interrupt lockout and restore macros for protection on - access of critical kernel information. The restore interrupt macro must - restore the interrupt posture of the running thread prior to the value - present prior to the disable macro. In most cases, the save area macro - is used to define a local function save area for the disable and restore - macros. */ - -/* Expose helper used to perform an atomic read/modify/write of mstatus. - The helper composes and returns the posture per ThreadX contract. */ -#ifndef __ASSEMBLER__ -UINT _tx_thread_interrupt_control(UINT new_posture); -#endif - -#ifdef TX_DISABLE_INLINE - -#define TX_INTERRUPT_SAVE_AREA register UINT interrupt_save; - -#define TX_DISABLE __asm__ volatile("csrrci %0, mstatus, 8" : "=r" (interrupt_save) :: "memory"); -#define TX_RESTORE { \ - unsigned long _temp_mstatus; \ - __asm__ volatile( \ - "csrc mstatus, 8\n" \ - "andi %0, %1, 8\n" \ - "csrs mstatus, %0" \ - : "=&r" (_temp_mstatus) \ - : "r" (interrupt_save) \ - : "memory"); \ - } - -#else - -#define TX_INTERRUPT_SAVE_AREA register UINT interrupt_save; - -#define TX_DISABLE interrupt_save = _tx_thread_interrupt_control(TX_INT_DISABLE); -#define TX_RESTORE _tx_thread_interrupt_control(interrupt_save); - -#endif /* TX_DISABLE_INLINE */ +/* Include shared RISC-V32 port definitions common to all toolchain ports. */ +#include "../../common/tx_port_riscv32_common.h" -/* Define the interrupt lockout macros for each ThreadX object. */ -#define TX_BLOCK_POOL_DISABLE TX_DISABLE -#define TX_BYTE_POOL_DISABLE TX_DISABLE -#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE -#define TX_MUTEX_DISABLE TX_DISABLE -#define TX_QUEUE_DISABLE TX_DISABLE -#define TX_SEMAPHORE_DISABLE TX_DISABLE /* Define automated coverage test extensions for the ThreadX regression test. */ diff --git a/ports/risc-v32/gnu/src/tx_initialize_low_level.S b/ports/risc-v32/gnu/src/tx_initialize_low_level.S index 703466bda..2940f2159 100644 --- a/ports/risc-v32/gnu/src/tx_initialize_low_level.S +++ b/ports/risc-v32/gnu/src/tx_initialize_low_level.S @@ -62,7 +62,7 @@ __tx_free_memory_start: /**************************************************************************/ /* VOID _tx_initialize_low_level(VOID) { */ - .global _tx_initialize_low_level + .weak _tx_initialize_low_level .weak _tx_initialize_low_level _tx_initialize_low_level: diff --git a/ports/risc-v32/gnu/src/tx_thread_schedule.S b/ports/risc-v32/gnu/src/tx_thread_schedule.S index 213aa9769..79bfd51a7 100644 --- a/ports/risc-v32/gnu/src/tx_thread_schedule.S +++ b/ports/risc-v32/gnu/src/tx_thread_schedule.S @@ -214,6 +214,7 @@ _tx_thread_schedule_skip_fp_restore: #endif /* Recover standard registers. */ + lw t0, 30*4(sp) // Recover mepc csrw mepc, t0 // Setup mepc li t0, 0x1880 // Prepare mstatus: MPP=Machine(0x1800) | MPIE(0x80) @@ -302,6 +303,7 @@ _tx_thread_schedule_solicited_skip_fp_restore: #endif /* Recover standard registers. */ + lw t0, 14*4(sp) // Reload saved mstatus (t0 may hold fcsr if FP was restored) csrw mstatus, t0 // Restore mstatus lw ra, 13*4(sp) // Recover return address diff --git a/ports/risc-v32/gnu/src/tx_thread_system_return.S b/ports/risc-v32/gnu/src/tx_thread_system_return.S index 4090e7b26..20ff25f21 100644 --- a/ports/risc-v32/gnu/src/tx_thread_system_return.S +++ b/ports/risc-v32/gnu/src/tx_thread_system_return.S @@ -69,7 +69,17 @@ _tx_thread_system_return: addi sp, sp, -16*4 // Allocate space on the stack - without floating point enabled #endif - /* Store floating point preserved registers. */ + sw zero, 0(sp) // Solicited stack type (store early so slot is always written) + + /* Read and save mstatus first; use it to guard FP register save. */ + csrr t0, mstatus // Pickup current mstatus + sw t0, 14*4(sp) // Save mstatus + + /* Store floating point preserved registers — only if FS != Off. */ +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + srli t1, t0, 13 // Shift FS field to bits [1:0] + andi t1, t1, 0x3 // Isolate FS bits + beqz t1, _tx_thread_system_return_skip_fp // Skip FP save if FS == Off #if defined(__riscv_float_abi_single) fsw f8, 15*4(sp) // Store fs0 fsw f9, 16*4(sp) // Store fs1 @@ -100,9 +110,10 @@ _tx_thread_system_return: fsd f27, 26*4(sp) // Store fs11 csrr t0, fcsr sw t0, 27*4(sp) // Store fcsr +#endif +_tx_thread_system_return_skip_fp: #endif - sw zero, 0(sp) // Solicited stack type sw ra, 13*4(sp) // Save return address sw s0, 12*4(sp) // Save s0 sw s1, 11*4(sp) // Save s1 @@ -116,9 +127,6 @@ _tx_thread_system_return: sw s9, 3*4(sp) // Save s9 sw s10, 2*4(sp) // Save s10 sw s11, 1*4(sp) // Save s11 - csrr t0, mstatus // Pickup mstatus - sw t0, 14*4(sp) // Save mstatus - /* Lockout interrupts. will be enabled in _tx_thread_schedule */ diff --git a/ports/risc-v64/gnu/CMakeLists.txt b/ports/risc-v64/gnu/CMakeLists.txt index 9357c6970..e51de7355 100644 --- a/ports/risc-v64/gnu/CMakeLists.txt +++ b/ports/risc-v64/gnu/CMakeLists.txt @@ -1,19 +1,6 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../../../cmake/threadx_riscv_port.cmake) -target_sources(${PROJECT_NAME} - PRIVATE - # {{BEGIN_TARGET_SOURCES}} - ${CMAKE_CURRENT_LIST_DIR}/src/tx_initialize_low_level.S - ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_context_restore.S - ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_context_save.S - ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_interrupt_control.S - ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_schedule.S - ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_stack_build.S - ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_system_return.S - ${CMAKE_CURRENT_LIST_DIR}/src/tx_timer_interrupt.S - # {{END_TARGET_SOURCES}} -) - -target_include_directories(${PROJECT_NAME} - PUBLIC - ${CMAKE_CURRENT_LIST_DIR}/inc +threadx_add_riscv_port( + SRC_DIR ${CMAKE_CURRENT_LIST_DIR}/src + INC_DIR ${CMAKE_CURRENT_LIST_DIR}/inc ) 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 35f7ac5aa..b655820ea 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 @@ -13,7 +13,7 @@ riscv64-unknown-elf-gcc \ -ffunction-sections -fdata-sections \ -I../../../../../common/inc \ -I../../inc \ - entry.s \ + entry.S \ tx_initialize_low_level.S \ board.c uart.c hwtimer.c plic.c trap.c demo_threadx.c \ -L../../../../../build -lthreadx \ diff --git a/ports/risc-v64/gnu/example_build/qemu_virt/csr.h b/ports/risc-v64/gnu/example_build/qemu_virt/csr.h deleted file mode 100644 index 83d05afd4..000000000 --- a/ports/risc-v64/gnu/example_build/qemu_virt/csr.h +++ /dev/null @@ -1,375 +0,0 @@ -/*************************************************************************** - * Copyright (c) 2024 Microsoft Corporation - * Copyright (c) 2026-present Eclipse ThreadX contributors - * - * This program and the accompanying materials are made available under the - * terms of the MIT License which is available at - * https://opensource.org/licenses/MIT. - * - * SPDX-License-Identifier: MIT - **************************************************************************/ - - -#ifndef RISCV_CSR_H -#define RISCV_CSR_H - - -// Machine Status Register, mstatus -#define MSTATUS_MPP_MASK (3L << 11) // previous mode. -#define MSTATUS_MPP_M (3L << 11) -#define MSTATUS_MPP_S (1L << 11) -#define MSTATUS_MPP_U (0L << 11) -#define MSTATUS_MIE (1L << 3) // machine-mode interrupt enable. -#define MSTATUS_MPIE (1L << 7) -#define MSTATUS_FS (1L << 13) -#define MSTATUS_VS (1L << 9) - -// Machine-mode Interrupt Enable -#define MIE_MTIE (1L << 7) -#define MIE_MSIE (1L << 3) -#define MIE_MEIE (1L << 11) -#define MIE_STIE (1L << 5) // supervisor timer -#define MIE_SSIE (1L << 1) -#define MIE_SEIE (1L << 9) - -// Supervisor Status Register, sstatus -#define SSTATUS_SPP (1L << 8) // Previous mode, 1=Supervisor, 0=User -#define SSTATUS_SPIE (1L << 5) // Supervisor Previous Interrupt Enable -#define SSTATUS_UPIE (1L << 4) // User Previous Interrupt Enable -#define SSTATUS_SIE (1L << 1) // Supervisor Interrupt Enable -#define SSTATUS_UIE (1L << 0) // User Interrupt Enable -#define SSTATUS_SPIE (1L << 5) -#define SSTATUS_UPIE (1L << 4) - -// Supervisor Interrupt Enable -#define SIE_SEIE (1L << 9) // external -#define SIE_STIE (1L << 5) // timer -#define SIE_SSIE (1L << 1) // software - -#ifndef __ASSEMBLER__ - -#include - -static inline uint64_t riscv_get_core() -{ - uint64_t x; - asm volatile("csrr %0, mhartid" : "=r" (x) ); - return x; -} - -static inline uint64_t riscv_get_mstatus() -{ - uint64_t x; - asm volatile("csrr %0, mstatus" : "=r" (x) ); - return x; -} - -static inline void riscv_writ_mstatus(uint64_t x) -{ - asm volatile("csrw mstatus, %0" : : "r" (x)); -} - -// machine exception program counter, holds the -// instruction address to which a return from -// exception will go. -static inline void riscv_writ_mepc(uint64_t x) -{ - asm volatile("csrw mepc, %0" : : "r" (x)); -} - -static inline uint64_t riscv_get_sstatus() -{ - uint64_t x; - asm volatile("csrr %0, sstatus" : "=r" (x) ); - return x; -} - -static inline void riscv_writ_sstatus(uint64_t x) -{ - asm volatile("csrw sstatus, %0" : : "r" (x)); -} - -// Supervisor Interrupt Pending -static inline uint64_t riscv_get_sip() -{ - uint64_t x; - asm volatile("csrr %0, sip" : "=r" (x) ); - return x; -} - -static inline void riscv_writ_sip(uint64_t x) -{ - asm volatile("csrw sip, %0" : : "r" (x)); -} - -static inline uint64_t riscv_get_sie() -{ - uint64_t x; - asm volatile("csrr %0, sie" : "=r" (x) ); - return x; -} - -static inline void riscv_writ_sie(uint64_t x) -{ - asm volatile("csrw sie, %0" : : "r" (x)); -} - -static inline uint64_t riscv_get_mie() -{ - uint64_t x; - asm volatile("csrr %0, mie" : "=r" (x) ); - return x; -} - -static inline void riscv_writ_mie(uint64_t x) -{ - asm volatile("csrw mie, %0" : : "r" (x)); -} - -// supervisor exception program counter, holds the -// instruction address to which a return from -// exception will go. -static inline void riscv_writ_sepc(uint64_t x) -{ - asm volatile("csrw sepc, %0" : : "r" (x)); -} - -static inline uint64_t riscv_get_sepc() -{ - uint64_t x; - asm volatile("csrr %0, sepc" : "=r" (x) ); - return x; -} - -// Machine Exception Delegation -static inline uint64_t riscv_get_medeleg() -{ - uint64_t x; - asm volatile("csrr %0, medeleg" : "=r" (x) ); - return x; -} - -static inline void riscv_writ_medeleg(uint64_t x) -{ - asm volatile("csrw medeleg, %0" : : "r" (x)); -} - -// Machine Interrupt Delegation -static inline uint64_t riscv_get_mideleg() -{ - uint64_t x; - asm volatile("csrr %0, mideleg" : "=r" (x) ); - return x; -} - -static inline void riscv_writ_mideleg(uint64_t x) -{ - asm volatile("csrw mideleg, %0" : : "r" (x)); -} - -// Supervisor Trap-Vector Base Address -// low two bits are mode. -static inline void riscv_writ_stvec(uint64_t x) -{ - asm volatile("csrw stvec, %0" : : "r" (x)); -} - -static inline uint64_t riscv_get_stvec() -{ - uint64_t x; - asm volatile("csrr %0, stvec" : "=r" (x) ); - return x; -} - -// Supervisor Timer Comparison Register -static inline uint64_t riscv_get_stimecmp() -{ - uint64_t x; - // asm volatile("csrr %0, stimecmp" : "=r" (x) ); - asm volatile("csrr %0, 0x14d" : "=r" (x) ); - return x; -} - -static inline void riscv_writ_stimecmp(uint64_t x) -{ - // asm volatile("csrw stimecmp, %0" : : "r" (x)); - asm volatile("csrw 0x14d, %0" : : "r" (x)); -} - -// Machine Environment Configuration Register -static inline uint64_t riscv_get_menvcfg() -{ - uint64_t x; - // asm volatile("csrr %0, menvcfg" : "=r" (x) ); - asm volatile("csrr %0, 0x30a" : "=r" (x) ); - return x; -} - -static inline void riscv_writ_menvcfg(uint64_t x) -{ - // asm volatile("csrw menvcfg, %0" : : "r" (x)); - asm volatile("csrw 0x30a, %0" : : "r" (x)); -} - -// Physical Memory Protection -static inline void riscv_writ_pmpcfg0(uint64_t x) -{ - asm volatile("csrw pmpcfg0, %0" : : "r" (x)); -} - -static inline void riscv_writ_pmpaddr0(uint64_t x) -{ - asm volatile("csrw pmpaddr0, %0" : : "r" (x)); -} - -// supervisor address translation and protection; -// holds the address of the page table. -static inline void riscv_writ_satp(uint64_t x) -{ - asm volatile("csrw satp, %0" : : "r" (x)); -} - -static inline uint64_t riscv_get_satp() -{ - uint64_t x; - asm volatile("csrr %0, satp" : "=r" (x) ); - return x; -} - -// Supervisor Trap Cause -static inline uint64_t riscv_get_scause() -{ - uint64_t x; - asm volatile("csrr %0, scause" : "=r" (x) ); - return x; -} - -// Supervisor Trap Value -static inline uint64_t riscv_get_stval() -{ - uint64_t x; - asm volatile("csrr %0, stval" : "=r" (x) ); - return x; -} - -// Machine-mode Counter-Enable -static inline void riscv_writ_mcounteren(uint64_t x) -{ - asm volatile("csrw mcounteren, %0" : : "r" (x)); -} - -static inline uint64_t riscv_get_mcounteren() -{ - uint64_t x; - asm volatile("csrr %0, mcounteren" : "=r" (x) ); - return x; -} - -// machine-mode cycle counter -static inline uint64_t riscv_get_time() -{ - uint64_t x; - asm volatile("csrr %0, time" : "=r" (x) ); - return x; -} - -// enable device interrupts -static inline void riscv_sintr_on() -{ - uint64_t sstatus = riscv_get_sstatus(); - sstatus |= SSTATUS_SIE; - riscv_writ_sstatus(sstatus); -} - -// disable device interrupts -static inline void riscv_sintr_off() -{ - uint64_t sstatus = riscv_get_sstatus(); - sstatus &= (~SSTATUS_SIE); - riscv_writ_sstatus(sstatus); -} - -// are device interrupts enabled? -static inline int riscv_sintr_get() -{ - uint64_t x = riscv_get_sstatus(); - return (x & SSTATUS_SIE) != 0; -} - -static inline void riscv_sintr_restore(int x) -{ - if(x) - riscv_sintr_on(); - else - riscv_sintr_off(); -} - -// enable device interrupts -static inline void riscv_mintr_on() -{ - uint64_t mstatus = riscv_get_mstatus(); - mstatus |= MSTATUS_MIE; - riscv_writ_mstatus(mstatus); -} - -// disable device interrupts -static inline void riscv_mintr_off() -{ - uint64_t mstatus = riscv_get_mstatus(); - mstatus &= (~MSTATUS_MIE); - riscv_writ_mstatus(mstatus); -} - -// are device interrupts enabled? -static inline int riscv_mintr_get() -{ - uint64_t x = riscv_get_mstatus(); - return (x & MSTATUS_MIE) != 0; -} - -static inline void riscv_mintr_restore(int x) -{ - if(x) - riscv_mintr_on(); - else - riscv_mintr_off(); -} - -static inline uint64_t riscv_get_sp() -{ - uint64_t x; - asm volatile("mv %0, sp" : "=r" (x) ); - return x; -} - -// read and write tp, the thread pointer, which xv6 uses to hold -// this core's hartid (core number), the index into cpus[]. -static inline uint64_t riscv_get_tp() -{ - uint64_t x; - asm volatile("mv %0, tp" : "=r" (x) ); - return x; -} - -static inline void riscv_writ_tp(uint64_t x) -{ - asm volatile("mv tp, %0" : : "r" (x)); -} - -static inline uint64_t riscv_get_ra() -{ - uint64_t x; - asm volatile("mv %0, ra" : "=r" (x) ); - return x; -} - -// flush the TLB. -static inline void sfence_vma() -{ - // the zero, zero means flush all TLB entries. - asm volatile("sfence.vma zero, zero"); -} - -#endif // __ASSEMBLER__ - -#endif diff --git a/ports/risc-v64/gnu/example_build/qemu_virt/csr.h b/ports/risc-v64/gnu/example_build/qemu_virt/csr.h new file mode 120000 index 000000000..b5df6f125 --- /dev/null +++ b/ports/risc-v64/gnu/example_build/qemu_virt/csr.h @@ -0,0 +1 @@ +../../../../risc-v_common/inc/csr.h \ No newline at end of file diff --git a/ports/risc-v64/gnu/example_build/qemu_virt/entry.s b/ports/risc-v64/gnu/example_build/qemu_virt/entry.S similarity index 100% rename from ports/risc-v64/gnu/example_build/qemu_virt/entry.s rename to ports/risc-v64/gnu/example_build/qemu_virt/entry.S diff --git a/ports/risc-v64/gnu/example_build/qemu_virt/plic.c b/ports/risc-v64/gnu/example_build/qemu_virt/plic.c deleted file mode 100644 index 01e5c71a4..000000000 --- a/ports/risc-v64/gnu/example_build/qemu_virt/plic.c +++ /dev/null @@ -1,72 +0,0 @@ -#include "plic.h" -#include -irq_callback callbacks[MAX_CALLBACK_NUM]; - -void plic_irq_enable(int irqno) -{ - int hart = riscv_get_core(); - *(uint32_t*)PLIC_MENABLE(hart) = (*(uint32_t*)PLIC_MENABLE(hart) | (1 << irqno)); - return; -} - -void plic_irq_disable(int irqno) -{ - int hart = riscv_get_core(); - *(uint32_t*)PLIC_MENABLE(hart) = (*(uint32_t*)PLIC_MENABLE(hart) & (~(1 << irqno))); - return; -} - -void plic_prio_set(int irqno, int prio) -{ - PLIC_SET_PRIO(irqno, prio); -} - -int plic_prio_get(int irqno) -{ - return 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 - -#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 - diff --git a/ports/risc-v64/gnu/example_build/qemu_virt/plic.h b/ports/risc-v64/gnu/example_build/qemu_virt/plic.h new file mode 120000 index 000000000..f776a3d2a --- /dev/null +++ b/ports/risc-v64/gnu/example_build/qemu_virt/plic.h @@ -0,0 +1 @@ +../../../../risc-v_common/example_build/plic/plic.h \ No newline at end of file diff --git a/ports/risc-v64/gnu/example_build/qemu_virt/test/threadx_test_tx_gnu_riscv64_qemu.py b/ports/risc-v64/gnu/example_build/qemu_virt/test/threadx_test_tx_gnu_riscv64_qemu.py new file mode 100644 index 000000000..459fdf217 --- /dev/null +++ b/ports/risc-v64/gnu/example_build/qemu_virt/test/threadx_test_tx_gnu_riscv64_qemu.py @@ -0,0 +1,275 @@ +import subprocess +import sys +import os +import argparse +import socket +import select + +def print_content(content): + """Prints content using os.write to handle non-blocking stdout robustly.""" + try: + msg = f"{content}\n".encode('utf-8') + total_len = len(msg) + written = 0 + fd = sys.stdout.fileno() + while written < total_len: + try: + n = os.write(fd, msg[written:]) + written += n + except BlockingIOError: + select.select([], [fd], []) + except Exception: + pass + +def get_free_port(): + """Finds a free TCP port.""" + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + s.bind(('', 0)) + return s.getsockname()[1] + +def run_qemu_test(elf_path, qemu_bin, gdb_bin): + """ + Runs a test cycle using QEMU and GDB. + """ + print(f"Testing ELF: {elf_path}") + print(f"QEMU: {qemu_bin}") + print(f"GDB: {gdb_bin}") + + # Find a free port for GDB connection + gdb_port = get_free_port() + print(f"Using GDB port: {gdb_port}") + + # 1. Start QEMU in the background + qemu_cmd = [ + qemu_bin, + "-M", "virt", + "-nographic", + "-bios", "none", # Disable default OpenSBI + "-kernel", elf_path, + "-gdb", f"tcp::{gdb_port}", "-S", + "-monitor", "none", # Disable monitor + "-serial", "stdio" # Redirect serial output to stdio + ] + + print(f"Starting QEMU: {' '.join(qemu_cmd)}") + qemu_process = subprocess.Popen( + qemu_cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True + ) + + if qemu_process.poll() is not None: + print("QEMU failed to start.") + print(qemu_process.stderr.read()) + return False + + # 2. Create a GDB command file + gdb_cmds = """ +file {elf} +target remote :{port} +set pagination off +set confirm off + +# Setup Breakpoints +break tx_application_define +break thread_0_entry +break thread_6_and_7_entry +break _tx_timer_interrupt + +# Execute to Application Definition +continue + +# Inspect mstatus once thread_0 has started +continue +print/x $mstatus + +# Verify FPU Logic and Register State exercised by thread_6/7 +continue +finish +step +step +step +print/x $mstatus +info registers float +print fpu_test_val + +# Await Timer Interrupt +continue +print "Hit Timer Interrupt" + +# Verify MEPC Integrity - Save State +print/x $mepc +set $saved_pc = $mepc + +# Verify System Timer Before ISR +set $clock_before = _tx_timer_system_clock +print $clock_before + +# Configure Time-Slice Test Conditions +set _tx_timer_time_slice = 1 +set _tx_timer_expired_time_slice = 0 +set $ts_handler_called = 0 + +# Set Breakpoint at Time-Slice Handler with Auto-Continue +tbreak _tx_thread_time_slice +commands + set $ts_handler_called = 1 + continue +end + +# Set Breakpoint at ISR Return Address +set $ret_addr = $ra +tbreak *$ret_addr +continue + +# Verify Time-Slice Handler Was Called +if $ts_handler_called == 1 + print "SUCCESS: Time-slice handler called." +else + print "FAILURE: Time-slice handler NOT called." +end + +# Verify System Timer Increment (Monotonicity) +set $clock_after = _tx_timer_system_clock +print $clock_after + +if $clock_after > $clock_before + print "SUCCESS: System timer incremented." +else + print "FAILURE: System timer did not increment." +end + +# Verify Preemption Logic (Thread Priority) +set $curr_ptr = _tx_thread_current_ptr +set $exec_ptr = _tx_thread_execute_ptr +if $curr_ptr != 0 && $exec_ptr != 0 + set $curr_prio = $curr_ptr->tx_thread_priority + set $exec_prio = $exec_ptr->tx_thread_priority + printf "PREEMPT_CHECK current_prio=%d execute_prio=%d\\n", $curr_prio, $exec_prio + if $exec_prio < $curr_prio + printf "PREEMPT_VERIFIED_OK\\n" + else + printf "PREEMPT_VERIFIED_FAIL_NOT_OBSERVED\\n" + end +else + printf "PREEMPT_VERIFIED_FAIL_NULL\\n" +end + +quit +""".format(port=gdb_port, elf=elf_path) + + gdb_cmd_file = "test_cmds.gdb" + with open(gdb_cmd_file, "w") as f: + f.write(gdb_cmds) + + # 3. Run GDB + gdb_cmd = [ + gdb_bin, + "--batch", + "-x", gdb_cmd_file + ] + + print_content(f"Starting GDB: {' '.join(gdb_cmd)}") + + GDB_TIMEOUT_S = 30 + + try: + gdb_process = subprocess.run( + gdb_cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + timeout=GDB_TIMEOUT_S, + ) + + print_content("GDB Output:") + print_content(gdb_process.stdout) + if gdb_process.stderr: + print_content("GDB Error Output:") + print_content(gdb_process.stderr) + + except subprocess.TimeoutExpired as e: + print_content( + f"FAILURE: GDB session exceeded {GDB_TIMEOUT_S}s timeout; " + "likely stuck on a `continue` that never matched a breakpoint." + ) + if e.stdout: + print_content("GDB Output (partial):") + print_content(e.stdout if isinstance(e.stdout, str) + else e.stdout.decode(errors='replace')) + if e.stderr: + print_content("GDB Error Output (partial):") + print_content(e.stderr if isinstance(e.stderr, str) + else e.stderr.decode(errors='replace')) + return False + + except Exception as e: + print_content(f"An error occurred during test execution: {e}") + return False + + finally: + # 4. Clean up + print_content("Stopping QEMU...") + qemu_process.terminate() + try: + qemu_process.wait(timeout=2) + except subprocess.TimeoutExpired: + print_content("QEMU did not terminate gracefully, killing it forcefully.") + qemu_process.kill() + + # Verify results + stdout = gdb_process.stdout + timer_hit = "Breakpoint 4, _tx_timer_interrupt" in stdout + fpu_verified = False + preemption_verified = "PREEMPT_VERIFIED_OK" in stdout + + if "Breakpoint 3, thread_6_and_7_entry" in stdout: + if "1.10" in stdout or "fpu_test_val" in stdout: + print_content("SUCCESS: FPU instructions executed and registers inspected.") + fpu_verified = True + else: + print_content("FAILURE: Hit thread, but failed to inspect FPU. " + "Output does not contain expected value.") + + if timer_hit: + print_content("SUCCESS: Timer Interrupt verified! Hit _tx_timer_interrupt.") + else: + print_content("FAILURE: Did not hit timer interrupt.") + + if preemption_verified: + print_content("SUCCESS: Preemption verified (higher-priority thread " + "preempted a lower-priority one).") + else: + if "PREEMPT_VERIFIED_FAIL_INVERTED" in stdout: + print_content("FAILURE: Preemption inverted -- lower priority " + "thread scheduled over higher priority one.") + elif "PREEMPT_VERIFIED_FAIL_NULL" in stdout: + print_content("FAILURE: Preemption check saw NULL thread pointers.") + elif "PREEMPT_VERIFIED_FAIL_NOT_OBSERVED" in stdout: + print_content("FAILURE: Preemption was not observed within the " + "loop budget.") + else: + print_content("FAILURE: Preemption check did not run to completion.") + + if timer_hit and fpu_verified and preemption_verified: + return True + else: + return False + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Run ThreadX RV64 QEMU/GDB Test") + parser.add_argument("--elf", required=True, help="Path to the kernel ELF file") + parser.add_argument("--qemu", default="qemu-system-riscv64", + help="Path to QEMU binary") + parser.add_argument("--gdb", default="riscv64-unknown-elf-gdb", + help="Path to GDB binary") + + args = parser.parse_args() + + success = run_qemu_test(args.elf, args.qemu, args.gdb) + + if success: + sys.exit(0) + else: + sys.exit(1) diff --git a/ports/risc-v64/gnu/example_build/qemu_virt/trap.c b/ports/risc-v64/gnu/example_build/qemu_virt/trap.c deleted file mode 100644 index 5de9f37a4..000000000 --- a/ports/risc-v64/gnu/example_build/qemu_virt/trap.c +++ /dev/null @@ -1,46 +0,0 @@ -#include "csr.h" -#include -#include "uart.h" -#include "hwtimer.h" -#include "plic.h" -#include -#include - -#define OS_IS_INTERUPT(mcause) (mcause & 0x8000000000000000ull) -#define OS_IS_EXCEPTION(mcause) (~(OS_IS_INTERUPT)) -#define OS_IS_TICK_INT(mcause) (mcause == 0x8000000000000007ull) -#define OS_IS_SOFT_INT(mcause) (mcause == 0x8000000000000003ull) -#define OS_IS_EXT_INT(mcause) (mcause == 0x800000000000000bull) -#define OS_IS_TRAP_USER(mcause) (mcause == 0x000000000000000bull) -extern void _tx_timer_interrupt(void); - -void trap_handler(uintptr_t mcause, uintptr_t mepc, uintptr_t mtval) -{ - if(OS_IS_INTERUPT(mcause)) - { - if(OS_IS_TICK_INT(mcause)) - { - hwtimer_handler(); - _tx_timer_interrupt(); - } - else if(OS_IS_EXT_INT(mcause)) - { - int ret = plic_irq_intr(); - if(ret) - { - puts("[INTERRUPT]: handler irq error!"); - while(1) ; - } - } - else - { - puts("[INTERRUPT]: now can't deal with the interrupt!"); - while(1) ; - } - } - else - { - puts("[EXCEPTION] : Unkown Error!!"); - while(1) ; - } -} diff --git a/ports/risc-v64/gnu/example_build/qemu_virt/trap.c b/ports/risc-v64/gnu/example_build/qemu_virt/trap.c new file mode 120000 index 000000000..bdce33054 --- /dev/null +++ b/ports/risc-v64/gnu/example_build/qemu_virt/trap.c @@ -0,0 +1 @@ +../../../../risc-v_common/example_build/trap/trap_qemu.c \ No newline at end of file diff --git a/ports/risc-v64/gnu/example_build/qemu_virt/uart.c b/ports/risc-v64/gnu/example_build/qemu_virt/uart.c deleted file mode 100644 index eea302032..000000000 --- a/ports/risc-v64/gnu/example_build/qemu_virt/uart.c +++ /dev/null @@ -1,100 +0,0 @@ -#include "uart.h" -#include "csr.h" -#include "plic.h" -#include - -// the UART control registers are memory-mapped -// at address UART0. this macro returns the -// address of one of the registers. -#define Reg(reg) ((volatile unsigned char *)(UART0 + (reg))) - -// the UART control registers. -// some have different meanings for -// read vs write. -// see http://byterunner.com/16550.html -#define RHR 0 // receive holding register (for input bytes) -#define THR 0 // transmit holding register (for output bytes) -#define IER 1 // interrupt enable register -#define IER_RX_ENABLE (1<<0) -#define IER_TX_ENABLE (1<<1) -#define FCR 2 // FIFO control register -#define FCR_FIFO_ENABLE (1<<0) -#define FCR_FIFO_CLEAR (3<<1) // clear the content of the two FIFOs -#define ISR 2 // interrupt status register -#define LCR 3 // line control register -#define LCR_EIGHT_BITS (3<<0) -#define LCR_BAUD_LATCH (1<<7) // special mode to set baud rate -#define LSR 5 // line status register -#define LSR_RX_READY (1<<0) // input is waiting to be read from RHR -#define LSR_TX_IDLE (1<<5) // THR can accept another character to send - -#define ReadReg(reg) (*(Reg(reg))) -#define WriteReg(reg, v) (*(Reg(reg)) = (v)) - -int uart_init(void) -{ - // disable interrupts. - WriteReg(IER, 0x00); - - // special mode to set baud rate. - WriteReg(LCR, LCR_BAUD_LATCH); - - // LSB for baud rate of 38.4K. - WriteReg(0, 0x03); - - // MSB for baud rate of 38.4K. - WriteReg(1, 0x00); - - // leave set-baud mode, - // and set word length to 8 bits, no parity. - WriteReg(LCR, LCR_EIGHT_BITS); - - // reset and enable FIFOs. - WriteReg(FCR, FCR_FIFO_ENABLE | FCR_FIFO_CLEAR); - - // enable transmit and receive interrupts. - // WriteReg(IER, IER_TX_ENABLE | IER_RX_ENABLE); - - //enable UART0 in PLIC - plic_irq_enable(UART0_IRQ); - - //set UART0 priority in PLIC - plic_prio_set(UART0_IRQ, 1); - - //register callback for UART0 - //plic_register_callback(UART0_IRQ, uart_intr); - puts("[UART0] : Uart Init Done, this is Test output!"); - return 0; -} - -static inline void uart_putc_nolock(int ch) -{ - // wait for Transmit Holding Empty to be set in LSR. - while((ReadReg(LSR) & LSR_TX_IDLE) == 0) - ; - WriteReg(THR, ch); - return; -} - -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/ports/risc-v64/gnu/example_build/qemu_virt/uart.c b/ports/risc-v64/gnu/example_build/qemu_virt/uart.c new file mode 120000 index 000000000..1666c8c8d --- /dev/null +++ b/ports/risc-v64/gnu/example_build/qemu_virt/uart.c @@ -0,0 +1 @@ +../../../../risc-v_common/example_build/uart/uart_qemu_ns16550.c \ No newline at end of file diff --git a/ports/risc-v64/gnu/example_build/qemu_virt/uart.h b/ports/risc-v64/gnu/example_build/qemu_virt/uart.h deleted file mode 100644 index 824f0b568..000000000 --- a/ports/risc-v64/gnu/example_build/qemu_virt/uart.h +++ /dev/null @@ -1,22 +0,0 @@ -/*************************************************************************** - * Copyright (c) 2024 Microsoft Corporation - * Copyright (c) 2026-present Eclipse ThreadX contributors - * - * This program and the accompanying materials are made available under the - * terms of the MIT License which is available at - * https://opensource.org/licenses/MIT. - * - * SPDX-License-Identifier: MIT - **************************************************************************/ - -#ifndef RISCV_UART_H -#define RISCV_UART_H - -#define UART0 0x10000000L -#define UART0_IRQ 10 - -#define puts uart_puts -int uart_init(void); -int uart_putc(int ch); -int uart_puts(const char* str); -#endif diff --git a/ports/risc-v64/gnu/example_build/qemu_virt/uart.h b/ports/risc-v64/gnu/example_build/qemu_virt/uart.h new file mode 120000 index 000000000..aba5ae7b9 --- /dev/null +++ b/ports/risc-v64/gnu/example_build/qemu_virt/uart.h @@ -0,0 +1 @@ +../../../../risc-v_common/example_build/uart/uart_qemu_ns16550.h \ No newline at end of file diff --git a/ports/risc-v64/gnu/inc/tx_port.h b/ports/risc-v64/gnu/inc/tx_port.h index 9bf167043..4df55a6a1 100644 --- a/ports/risc-v64/gnu/inc/tx_port.h +++ b/ports/risc-v64/gnu/inc/tx_port.h @@ -72,6 +72,13 @@ #define VOID void +/* IMPORTANT: On this RV64 port LONG/ULONG are intentionally 32-bit (int / + * unsigned int), NOT 64-bit. ThreadX's internal data model requires LONG + * and ULONG to be exactly 4 bytes so that control-block layouts, queue + * message sizes, and the binary API remain identical to all other ThreadX + * ports. Do NOT change these to long/unsigned long — that mistake was + * already corrected once (see PR #534). Use ULONG64 for 64-bit values. */ + #ifndef __ASSEMBLER__ typedef char CHAR; typedef unsigned char UCHAR; @@ -88,6 +95,22 @@ typedef unsigned short USHORT; #define ALIGN_TYPE_DEFINED typedef unsigned long long ALIGN_TYPE; +/* On RV64, ULONG is 32-bit but pointers are 64-bit. Store the thread + pointer in the timer's VOID * extension field so _tx_thread_timeout + can recover it without truncation. This mirrors the win64 port. */ +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_extension_ptr; + +/* TX_TIMER_EXTENSION_PTR_DEFINED signals to portable code (e.g. tests) + that the timer extension pointer mechanism is in use on this port. */ +#define TX_TIMER_EXTENSION_PTR_DEFINED + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_extension_ptr = (VOID *) (t); + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) TX_PARAMETER_NOT_USED(timeout_input); \ + (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_extension_ptr; + /* Define the priority levels for ThreadX. Legal values range from 32 to 1024 and MUST be evenly divisible by 32. */ diff --git a/ports/risc-v_common/example_build/plic/plic.c b/ports/risc-v_common/example_build/plic/plic.c new file mode 100644 index 000000000..21421a725 --- /dev/null +++ b/ports/risc-v_common/example_build/plic/plic.c @@ -0,0 +1,79 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * Copyright (c) 2026-present Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + +#include "plic.h" +#include + +irq_callback callbacks[MAX_CALLBACK_NUM]; + +void plic_irq_enable(int irqno) +{ + int hart = riscv_get_core(); + *(uint32_t*)PLIC_MENABLE(hart) = (*(uint32_t*)PLIC_MENABLE(hart) | (1 << irqno)); +} + +void plic_irq_disable(int irqno) +{ + int 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 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) +{ + int hart = riscv_get_core(); + return (*(uint32_t*)PLIC_MCLAIM(hart)); +} + +void plic_complete(int irqno) +{ + int 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/ports/risc-v_common/example_build/plic/plic.h b/ports/risc-v_common/example_build/plic/plic.h new file mode 100644 index 000000000..29d29c57c --- /dev/null +++ b/ports/risc-v_common/example_build/plic/plic.h @@ -0,0 +1,48 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * Copyright (c) 2026-present Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + +#ifndef RISCV_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/ports/risc-v_common/example_build/trap/trap_qemu.c b/ports/risc-v_common/example_build/trap/trap_qemu.c new file mode 100644 index 000000000..c9d8a0033 --- /dev/null +++ b/ports/risc-v_common/example_build/trap/trap_qemu.c @@ -0,0 +1,92 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * Copyright (c) 2026-present 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 + **************************************************************************/ + +/* Trap handler for QEMU virt machine (RV32 and RV64). + * + * mcause constants use __riscv_xlen to resolve correctly for both ISAs: + * RV32: interrupt bit = bit 31 (0x80000000) + * RV64: interrupt bit = bit 63 (0x8000000000000000) + */ + +#include "csr.h" +#include +#include "uart.h" +#include "hwtimer.h" +#include "plic.h" +#include +#include + +#define MCAUSE_INT_BIT ((uintptr_t)1 << (__riscv_xlen - 1)) + +#define OS_IS_INTERRUPT(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)) +#define OS_IS_TRAP_USER(mcause) ((mcause) == 11u) + +extern void _tx_timer_interrupt(void); + +#ifdef TX_RISCV_TRAP_DEBUG +static void print_hex(uintptr_t val) +{ + const char digits[] = "0123456789ABCDEF"; + uart_putc('0'); + uart_putc('x'); + for (int i = (int)(sizeof(uintptr_t) * 2) - 1; i >= 0; i--) + { + int d = (val >> (i * 4)) & 0xF; + uart_putc(digits[d]); + } + uart_putc('\n'); +} +#endif /* TX_RISCV_TRAP_DEBUG */ + +void trap_handler(uintptr_t mcause, uintptr_t mepc, uintptr_t mtval) +{ + if (OS_IS_INTERRUPT(mcause)) + { + if (OS_IS_TICK_INT(mcause)) + { + hwtimer_handler(); + _tx_timer_interrupt(); + } + else if (OS_IS_EXT_INT(mcause)) + { + int ret = plic_irq_intr(); + if (ret) + { + puts("[INTERRUPT]: handler irq error!"); + while (1) ; + } + } + else + { + puts("[INTERRUPT]: now can't deal with the interrupt!"); + while (1) ; + } + } + else + { + puts("[EXCEPTION] : Unknown Error!!"); +#ifdef TX_RISCV_TRAP_DEBUG + puts("mcause:"); + print_hex(mcause); + puts("mepc:"); + print_hex(mepc); + puts("mtval:"); + print_hex(mtval); +#else + (void)mepc; + (void)mtval; +#endif /* TX_RISCV_TRAP_DEBUG */ + while (1) ; + } +} diff --git a/ports/risc-v_common/example_build/uart/uart_qemu_ns16550.c b/ports/risc-v_common/example_build/uart/uart_qemu_ns16550.c new file mode 100644 index 000000000..ce9a8adbc --- /dev/null +++ b/ports/risc-v_common/example_build/uart/uart_qemu_ns16550.c @@ -0,0 +1,99 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * Copyright (c) 2026-present 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 + **************************************************************************/ + +/* NS16550-compatible UART driver for QEMU virt machine (RV32 and RV64). */ + +#include "uart.h" +#include "csr.h" +#include "plic.h" +#include + +/* Memory-mapped UART control registers. */ +#define Reg(reg) ((volatile unsigned char *)(UART0 + (reg))) + +#define RHR 0 /* receive holding register (for input bytes) */ +#define THR 0 /* transmit holding register (for output bytes) */ +#define IER 1 /* interrupt enable register */ +#define IER_RX_ENABLE (1<<0) +#define IER_TX_ENABLE (1<<1) +#define FCR 2 /* FIFO control register */ +#define FCR_FIFO_ENABLE (1<<0) +#define FCR_FIFO_CLEAR (3<<1) /* clear the content of the two FIFOs */ +#define ISR 2 /* interrupt status register */ +#define LCR 3 /* line control register */ +#define LCR_EIGHT_BITS (3<<0) +#define LCR_BAUD_LATCH (1<<7) /* special mode to set baud rate */ +#define LSR 5 /* line status register */ +#define LSR_RX_READY (1<<0) /* input is waiting to be read from RHR */ +#define LSR_TX_IDLE (1<<5) /* THR can accept another character to send */ + +#define ReadReg(reg) (*(Reg(reg))) +#define WriteReg(reg, v) (*(Reg(reg)) = (v)) + +int uart_init(void) +{ + /* Disable interrupts. */ + WriteReg(IER, 0x00); + + /* Special mode to set baud rate. */ + WriteReg(LCR, LCR_BAUD_LATCH); + + /* LSB for baud rate of 38.4K. */ + WriteReg(0, 0x03); + + /* MSB for baud rate of 38.4K. */ + WriteReg(1, 0x00); + + /* Leave set-baud mode, set word length to 8 bits, no parity. */ + WriteReg(LCR, LCR_EIGHT_BITS); + + /* Reset and enable FIFOs. */ + WriteReg(FCR, FCR_FIFO_ENABLE | FCR_FIFO_CLEAR); + + /* Enable UART0 in PLIC. */ + plic_irq_enable(UART0_IRQ); + + /* Set UART0 priority in PLIC. */ + plic_prio_set(UART0_IRQ, 1); + + puts("[UART0] : Uart Init Done, this is Test output!"); + return 0; +} + +/* uart_putc_nolock is static inline so it can be inlined into uart_putc + and uart_puts without introducing a public symbol. */ +static inline 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/ports/risc-v_common/example_build/uart/uart_qemu_ns16550.h b/ports/risc-v_common/example_build/uart/uart_qemu_ns16550.h new file mode 100644 index 000000000..67fc9ed9a --- /dev/null +++ b/ports/risc-v_common/example_build/uart/uart_qemu_ns16550.h @@ -0,0 +1,22 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * Copyright (c) 2026-present Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + +#ifndef RISCV_UART_H +#define RISCV_UART_H + +#define UART0 0x10000000L +#define UART0_IRQ 10 + +#define puts uart_puts +int uart_init(void); +int uart_putc(int ch); +int uart_puts(const char *str); +#endif /* RISCV_UART_H */ diff --git a/ports/risc-v_common/inc/csr.h b/ports/risc-v_common/inc/csr.h new file mode 100644 index 000000000..a4495a46f --- /dev/null +++ b/ports/risc-v_common/inc/csr.h @@ -0,0 +1,356 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * Copyright (c) 2026-present Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + + +#ifndef RISCV_CSR_H +#define RISCV_CSR_H + + +/* Machine Status Register, mstatus */ +#define MSTATUS_MPP_MASK (3L << 11) /* previous mode. */ +#define MSTATUS_MPP_M (3L << 11) +#define MSTATUS_MPP_S (1L << 11) +#define MSTATUS_MPP_U (0L << 11) +#define MSTATUS_MIE (1L << 3) /* machine-mode interrupt enable. */ +#define MSTATUS_MPIE (1L << 7) +#define MSTATUS_FS (1L << 13) /* floating-point status. */ +#define MSTATUS_VS (1L << 9) /* vector status (RVV). */ + +/* Machine-mode Interrupt Enable */ +#define MIE_MTIE (1L << 7) +#define MIE_MSIE (1L << 3) +#define MIE_MEIE (1L << 11) +#define MIE_STIE (1L << 5) /* supervisor timer */ +#define MIE_SSIE (1L << 1) +#define MIE_SEIE (1L << 9) + +/* Supervisor Status Register, sstatus */ +#define SSTATUS_SPP (1L << 8) /* Previous mode, 1=Supervisor, 0=User */ +#define SSTATUS_SPIE (1L << 5) /* Supervisor Previous Interrupt Enable */ +#define SSTATUS_UPIE (1L << 4) /* User Previous Interrupt Enable */ +#define SSTATUS_SIE (1L << 1) /* Supervisor Interrupt Enable */ +#define SSTATUS_UIE (1L << 0) /* User Interrupt Enable */ + +/* Supervisor Interrupt Enable */ +#define SIE_SEIE (1L << 9) /* external */ +#define SIE_STIE (1L << 5) /* timer */ +#define SIE_SSIE (1L << 1) /* software */ + +#ifndef __ASSEMBLER__ + +#include + +/* All CSR accessor functions use uintptr_t so this header works for both + RV32 (uintptr_t = uint32_t) and RV64 (uintptr_t = uint64_t). */ + +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_writ_mepc(uintptr_t x) +{ + asm volatile("csrw mepc, %0" : : "r" (x)); +} + +static inline uintptr_t riscv_get_sstatus(void) +{ + uintptr_t x; + asm volatile("csrr %0, sstatus" : "=r" (x)); + return x; +} + +static inline void riscv_writ_sstatus(uintptr_t x) +{ + asm volatile("csrw sstatus, %0" : : "r" (x)); +} + +/* Supervisor Interrupt Pending */ +static inline uintptr_t riscv_get_sip(void) +{ + uintptr_t x; + asm volatile("csrr %0, sip" : "=r" (x)); + return x; +} + +static inline void riscv_writ_sip(uintptr_t x) +{ + asm volatile("csrw sip, %0" : : "r" (x)); +} + +static inline uintptr_t riscv_get_sie(void) +{ + uintptr_t x; + asm volatile("csrr %0, sie" : "=r" (x)); + return x; +} + +static inline void riscv_writ_sie(uintptr_t x) +{ + asm volatile("csrw sie, %0" : : "r" (x)); +} + +static inline uintptr_t riscv_get_mie(void) +{ + uintptr_t x; + asm volatile("csrr %0, mie" : "=r" (x)); + return x; +} + +static inline void riscv_writ_mie(uintptr_t x) +{ + asm volatile("csrw mie, %0" : : "r" (x)); +} + +/* Supervisor exception program counter */ +static inline void riscv_writ_sepc(uintptr_t x) +{ + asm volatile("csrw sepc, %0" : : "r" (x)); +} + +static inline uintptr_t riscv_get_sepc(void) +{ + uintptr_t x; + asm volatile("csrr %0, sepc" : "=r" (x)); + return x; +} + +/* Machine Exception Delegation */ +static inline uintptr_t riscv_get_medeleg(void) +{ + uintptr_t x; + asm volatile("csrr %0, medeleg" : "=r" (x)); + return x; +} + +static inline void riscv_writ_medeleg(uintptr_t x) +{ + asm volatile("csrw medeleg, %0" : : "r" (x)); +} + +/* Machine Interrupt Delegation */ +static inline uintptr_t riscv_get_mideleg(void) +{ + uintptr_t x; + asm volatile("csrr %0, mideleg" : "=r" (x)); + return x; +} + +static inline void riscv_writ_mideleg(uintptr_t x) +{ + asm volatile("csrw mideleg, %0" : : "r" (x)); +} + +/* Supervisor Trap-Vector Base Address */ +static inline void riscv_writ_stvec(uintptr_t x) +{ + asm volatile("csrw stvec, %0" : : "r" (x)); +} + +static inline uintptr_t riscv_get_stvec(void) +{ + uintptr_t x; + asm volatile("csrr %0, stvec" : "=r" (x)); + return x; +} + +/* Supervisor Timer Comparison Register */ +static inline uintptr_t riscv_get_stimecmp(void) +{ + uintptr_t x; + asm volatile("csrr %0, 0x14d" : "=r" (x)); + return x; +} + +static inline void riscv_writ_stimecmp(uintptr_t x) +{ + asm volatile("csrw 0x14d, %0" : : "r" (x)); +} + +/* Machine Environment Configuration Register */ +static inline uintptr_t riscv_get_menvcfg(void) +{ + uintptr_t x; + asm volatile("csrr %0, 0x30a" : "=r" (x)); + return x; +} + +static inline void riscv_writ_menvcfg(uintptr_t x) +{ + asm volatile("csrw 0x30a, %0" : : "r" (x)); +} + +/* Physical Memory Protection */ +static inline void riscv_writ_pmpcfg0(uintptr_t x) +{ + asm volatile("csrw pmpcfg0, %0" : : "r" (x)); +} + +static inline void riscv_writ_pmpaddr0(uintptr_t x) +{ + asm volatile("csrw pmpaddr0, %0" : : "r" (x)); +} + +/* Supervisor address translation and protection */ +static inline void riscv_writ_satp(uintptr_t x) +{ + asm volatile("csrw satp, %0" : : "r" (x)); +} + +static inline uintptr_t riscv_get_satp(void) +{ + uintptr_t x; + asm volatile("csrr %0, satp" : "=r" (x)); + return x; +} + +/* Supervisor Trap Cause */ +static inline uintptr_t riscv_get_scause(void) +{ + uintptr_t x; + asm volatile("csrr %0, scause" : "=r" (x)); + return x; +} + +/* Supervisor Trap Value */ +static inline uintptr_t riscv_get_stval(void) +{ + uintptr_t x; + asm volatile("csrr %0, stval" : "=r" (x)); + return x; +} + +/* Machine-mode Counter-Enable */ +static inline void riscv_writ_mcounteren(uintptr_t x) +{ + asm volatile("csrw mcounteren, %0" : : "r" (x)); +} + +static inline uintptr_t riscv_get_mcounteren(void) +{ + uintptr_t x; + asm volatile("csrr %0, mcounteren" : "=r" (x)); + return x; +} + +static inline uintptr_t riscv_get_time(void) +{ + uintptr_t x; + asm volatile("csrr %0, time" : "=r" (x)); + return x; +} + +static inline void riscv_sintr_on(void) +{ + uintptr_t sstatus = riscv_get_sstatus(); + sstatus |= SSTATUS_SIE; + riscv_writ_sstatus(sstatus); +} + +static inline void riscv_sintr_off(void) +{ + uintptr_t sstatus = riscv_get_sstatus(); + sstatus &= (~(uintptr_t)SSTATUS_SIE); + riscv_writ_sstatus(sstatus); +} + +static inline int riscv_sintr_get(void) +{ + uintptr_t x = riscv_get_sstatus(); + return (x & SSTATUS_SIE) != 0; +} + +static inline void riscv_sintr_restore(int x) +{ + if (x) + riscv_sintr_on(); + else + riscv_sintr_off(); +} + +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 &= (~(uintptr_t)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(); +} + +static inline uintptr_t riscv_get_sp(void) +{ + uintptr_t x; + asm volatile("mv %0, sp" : "=r" (x)); + return x; +} + +/* Thread pointer (tp), used by some ports for hart-local storage. */ +static inline uintptr_t riscv_get_tp(void) +{ + uintptr_t x; + asm volatile("mv %0, tp" : "=r" (x)); + return x; +} + +static inline void riscv_writ_tp(uintptr_t x) +{ + asm volatile("mv tp, %0" : : "r" (x)); +} + +static inline uintptr_t riscv_get_ra(void) +{ + uintptr_t x; + asm volatile("mv %0, ra" : "=r" (x)); + return x; +} + +/* Flush all TLB entries. */ +static inline void sfence_vma(void) +{ + asm volatile("sfence.vma zero, zero"); +} + +#endif /* __ASSEMBLER__ */ + +#endif /* RISCV_CSR_H */ diff --git a/test/smp/cmake/regression/CMakeLists.txt b/test/smp/cmake/regression/CMakeLists.txt index 7d258e50e..b448ae3e3 100644 --- a/test/smp/cmake/regression/CMakeLists.txt +++ b/test/smp/cmake/regression/CMakeLists.txt @@ -135,9 +135,13 @@ add_custom_command( 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_weak_defaults OBJECT ${TESTCONTROL_WEAK_DEFAULTS_SOURCE}) +target_link_libraries(test_weak_defaults PRIVATE azrtos::threadx_smp) +target_compile_definitions(test_weak_defaults PRIVATE CTEST BATCH_TEST + TEST_STACK_SIZE_PRINTF=4096) + add_library(test_utility OBJECT ${GENERATED_LOW_LEVEL_SOURCE} - ${SOURCE_DIR}/testcontrol.c - ${TESTCONTROL_WEAK_DEFAULTS_SOURCE}) + ${SOURCE_DIR}/testcontrol.c) target_link_libraries(test_utility PRIVATE azrtos::threadx_smp) target_compile_definitions(test_utility PRIVATE CTEST BATCH_TEST TEST_STACK_SIZE_PRINTF=4096) @@ -146,9 +150,11 @@ foreach(test_case ${regression_test_cases}) get_filename_component(test_name ${test_case} NAME_WE) if(test_name STREQUAL "threadx_initialize_kernel_setup_test") - add_executable(${test_name} ${test_case}) + add_executable(${test_name} ${test_case} $) else() - add_executable(${test_name} ${test_case} $) + add_executable(${test_name} ${test_case} + $ + $) target_compile_definitions(${test_name} PRIVATE CTEST BATCH_TEST TEST_STACK_SIZE_PRINTF=4096) endif() diff --git a/test/tx/cmake/regression/CMakeLists.txt b/test/tx/cmake/regression/CMakeLists.txt index 25029ab91..34fba1b2c 100644 --- a/test/tx/cmake/regression/CMakeLists.txt +++ b/test/tx/cmake/regression/CMakeLists.txt @@ -120,9 +120,13 @@ add_custom_command( 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_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 - ${TESTCONTROL_WEAK_DEFAULTS_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) @@ -131,9 +135,11 @@ foreach(test_case ${regression_test_cases}) get_filename_component(test_name ${test_case} NAME_WE) if(test_name STREQUAL "threadx_initialize_kernel_setup_test") - add_executable(${test_name} ${test_case}) + add_executable(${test_name} ${test_case} $) else() - add_executable(${test_name} ${test_case} $) + add_executable(${test_name} ${test_case} + $ + $) target_compile_definitions(${test_name} PRIVATE CTEST BATCH_TEST TEST_STACK_SIZE_PRINTF=4096) endif() diff --git a/test/tx/cmake/riscv/CMakeLists.txt b/test/tx/cmake/riscv/CMakeLists.txt index 0352622d0..a4f7e7e84 100644 --- a/test/tx/cmake/riscv/CMakeLists.txt +++ b/test/tx/cmake/riscv/CMakeLists.txt @@ -70,7 +70,15 @@ target_compile_options( -Wunused -Wuninitialized -Wmissing-declarations - -Wconversion + # -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. + $<$:-Wconversion> -Wpointer-arith -Wlogical-op -Waggregate-return diff --git a/test/tx/cmake/riscv/regression/CMakeLists.txt b/test/tx/cmake/riscv/regression/CMakeLists.txt index 5e381b894..80b6e3bdb 100644 --- a/test/tx/cmake/riscv/regression/CMakeLists.txt +++ b/test/tx/cmake/riscv/regression/CMakeLists.txt @@ -120,6 +120,18 @@ 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) @@ -146,7 +158,7 @@ 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}) + add_executable(${test_name} ${test_case} $) target_link_libraries(${test_name} PRIVATE -Wl,--whole-archive riscv_bsp -Wl,--no-whole-archive riscv_test_utility) @@ -156,7 +168,7 @@ 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}) + add_executable(${test_name} ${test_case} $) target_link_libraries(${test_name} PRIVATE -Wl,--whole-archive riscv_bsp -Wl,--no-whole-archive azrtos::threadx) diff --git a/test/tx/regression/threadx_thread_basic_execution_test.c b/test/tx/regression/threadx_thread_basic_execution_test.c index a0f65a8cc..5bcf1a2ae 100644 --- a/test/tx/regression/threadx_thread_basic_execution_test.c +++ b/test/tx/regression/threadx_thread_basic_execution_test.c @@ -390,12 +390,11 @@ VOID (*temp_mutex_release)(TX_THREAD *thread_ptr); test_thread.tx_thread_timer.tx_timer_internal_list_head = TX_NULL; test_thread.tx_thread_suspending = TX_TRUE; test_thread.tx_thread_delayed_suspend = TX_TRUE; -#if defined(_WIN64) +#if defined(_WIN64) || defined(TX_TIMER_EXTENSION_PTR_DEFINED) { TX_TIMER_INTERNAL timeout_timer; TX_TIMER_INTERNAL *saved_expired_timer_ptr; - TX_MEMSET(&timeout_timer, 0, sizeof(TX_TIMER_INTERNAL)); saved_expired_timer_ptr = _tx_timer_expired_timer_ptr; _tx_timer_expired_timer_ptr = &timeout_timer;