diff options
| author | Frédéric Desbiens <[email protected]> | 2026-06-08 10:01:32 +0200 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-06-08 10:01:32 +0200 |
| commit | 87ab09cce305eb9cd4aacac4e8c62af72f665bff (patch) | |
| tree | dd062ebbea5235d1921c64b396bf7e4ce79e9e15 /ports/risc-v64/gnu | |
| parent | 9ab0cf9683f832cdb48e2099533a5b06a3afcd64 (diff) | |
| parent | 730b61874bc5cf40768987605ae5187fde0fa1e2 (diff) | |
Merge pull request #544 from eclipse-threadx/devv6.5.1.202602_rel
Merging changes for the v.6.5.1.202602 release
Diffstat (limited to 'ports/risc-v64/gnu')
39 files changed, 2625 insertions, 804 deletions
diff --git a/ports/risc-v64/gnu/CMakeLists.txt b/ports/risc-v64/gnu/CMakeLists.txt index 9357c697..e51de735 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/bananapi-f3/.gitignore b/ports/risc-v64/gnu/example_build/bananapi-f3/.gitignore new file mode 100644 index 00000000..9cae8f84 --- /dev/null +++ b/ports/risc-v64/gnu/example_build/bananapi-f3/.gitignore @@ -0,0 +1,4 @@ +kernel.bin +kernel.elf +kernel.uImage + diff --git a/ports/risc-v64/gnu/example_build/bananapi-f3/board.c b/ports/risc-v64/gnu/example_build/bananapi-f3/board.c new file mode 100644 index 00000000..efdbfe6d --- /dev/null +++ b/ports/risc-v64/gnu/example_build/bananapi-f3/board.c @@ -0,0 +1,45 @@ +/*************************************************************************** + * Copyright (c) 2026 10xEngineers + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + +#include "plic.h" +#include "hwtimer.h" +#include "uart.h" +#include <stdint.h> +#include <stddef.h> + +void *memset(void *des, int c, size_t n) +{ + if ((des == NULL) || n == 0) + return des; + + char *t = (char *)des; + for (size_t i = 0; i < n; i++) + t[i] = c; + return t; +} + +int board_init(void) +{ + int ret; + + ret = plic_init(); + if (ret) + return ret; + + ret = uart_init(); + if (ret) + return ret; + + ret = hwtimer_init(); + if (ret) + return ret; + + return 0; +} diff --git a/ports/risc-v64/gnu/example_build/bananapi-f3/build_libthreadx.sh b/ports/risc-v64/gnu/example_build/bananapi-f3/build_libthreadx.sh new file mode 100755 index 00000000..2c94b8a1 --- /dev/null +++ b/ports/risc-v64/gnu/example_build/bananapi-f3/build_libthreadx.sh @@ -0,0 +1,70 @@ +#!/bin/bash +############################################################################## +# Copyright (c) 2024 Microsoft Corporation +# Copyright (c) 2026 Eclipse ThreadX contributors +# +# This program and the accompanying materials are made available under the +# terms of the MIT License which is available at +# https://opensource.org/licenses/MIT. +# +# SPDX-License-Identifier: MIT +############################################################################## + +# Build the bananapi-f3 (SpacemiT K1) ThreadX kernel. +# +# Boot flow: FSBL -> OpenSBI (M-mode) -> U-Boot (S-mode) -> ThreadX (S-mode). +# +# OpenSBI runs in M-mode and delegates S-mode to the next stage. +# U-Boot runs in S-mode; any code launched from U-Boot also runs in S-mode. +# +# Use the TX_RISCV_SMODE CMake option to build libthreadx.a for S-mode. +# +# This libthreadx.a (S-mode), then links the BSP objects +# to produce kernel.elf / kernel.bin. + +set -e + +# Where ThreadX is loaded by U-Boot (${loadaddr}). Must match the link.lds origin. +LOAD_ADDR=0x00200000 + +# printf "y\n" | rm -rf ../../../../../build/ +rm -f kernel.elf kernel.bin kernel.uImage + +pushd ../../../../../ +cmake -Bbuild -GNinja \ + -DCMAKE_TOOLCHAIN_FILE=cmake/riscv64_gnu.cmake \ + -DTX_USER_FILE="" \ + -DTX_RISCV_SMODE=ON \ + . +cmake --build ./build/ +popd + +riscv64-unknown-elf-gcc \ + -march=rv64gc -mabi=lp64d \ + -mcmodel=medany -O0 -g3 -Wall \ + -DTX_RISCV_SMODE \ + -ffunction-sections -fdata-sections \ + -I../../../../../common/inc \ + -I../../inc \ + entry.S \ + tx_initialize_low_level.S \ + board.c uart.c hwtimer.c plic.c trap.c demo_threadx.c \ + -L../../../../../build -lthreadx \ + -T link.lds -nostartfiles \ + -o kernel.elf + +# Strip ELF metadata down to the loadable bytes. +riscv64-unknown-elf-objcopy -O binary kernel.elf kernel.bin + +echo "== Build artifacts ==" +ls -la kernel.elf kernel.bin 2>/dev/null || true +echo +riscv64-unknown-elf-size kernel.elf || true +echo + +# Run on Bananapi BPI-F3 +# Stop the boot at U-Boot (press reset button and press "s" key continuously to stop autoboot). +# at "=>" prompt, load the kernel using the following commands: +# +# " ELF: tftpboot ${loadaddr} kernel.elf && bootelf ${loadaddr}" +# " BIN: tftpboot 0x200000 kernel.bin && go 0x200000" diff --git a/ports/risc-v64/gnu/example_build/bananapi-f3/csr.h b/ports/risc-v64/gnu/example_build/bananapi-f3/csr.h new file mode 100644 index 00000000..c5dfc630 --- /dev/null +++ b/ports/risc-v64/gnu/example_build/bananapi-f3/csr.h @@ -0,0 +1,98 @@ +/*************************************************************************** + * Copyright (c) 2026 10xEngineers + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + +/* RISC-V S-mode CSR helpers + * + * Privilege level: Supervisor (S-mode). + * Reference: RISC-V Privileged Specification + * + * Bit positions per the RISC-V Privileged Specification: + * + * sstatus: SIE(1) SPIE(5) SPP(8) FS(13:14) SUM(18) MXR(19) + * sie: SSIE(1) STIE(5) SEIE(9) + * scause: Interrupt bit = 63; Code 1=SSI, 5=STI, 9=SEI + */ + +#ifndef RISCV_CSR_H +#define RISCV_CSR_H + +#define SSTATUS_SIE (1L << 1) /* Supervisor Interrupt Enable */ +#define SSTATUS_SPIE (1L << 5) /* Previous SIE (saved on trap) */ +#define SSTATUS_SPP_MASK (1L << 8) +#define SSTATUS_SPP_S (1L << 8) /* SPP = Supervisor */ +#define SSTATUS_SPP_U (0L << 8) /* SPP = User */ +#define SSTATUS_FS (3L << 13) /* FP unit state (Off/Init/Clean/Dirty) */ + +#define SIE_SSIE (1L << 1) /* S-mode software interrupt */ +#define SIE_STIE (1L << 5) /* S-mode timer interrupt */ +#define SIE_SEIE (1L << 9) /* S-mode external interrupt */ + +#ifndef __ASSEMBLER__ + +#include <stdint.h> + +/* + * Return the hart ID of the running core. + * + * mhartid is an M-mode CSR and cannot be read from S-mode. When booted + * from U-Boot only hart 0 is active (secondary harts remain parked in + * OpenSBI HSM), so we return 0. An SMP extension would need to pass + * the hart ID through a0 or shared memory at boot. + */ +static inline uint64_t riscv_get_core(void) +{ + return 0; +} + + +static inline uint64_t riscv_read_sstatus(void) +{ + uint64_t x; + asm volatile("csrr %0, sstatus" : "=r" (x)); + return x; +} + +static inline void riscv_write_sstatus(uint64_t x) +{ + asm volatile("csrw sstatus, %0" : : "r" (x)); +} + + +static inline void riscv_sintr_on(void) +{ + riscv_write_sstatus(riscv_read_sstatus() | SSTATUS_SIE); +} + +static inline void riscv_sintr_off(void) +{ + riscv_write_sstatus(riscv_read_sstatus() & ~SSTATUS_SIE); +} + +static inline int riscv_sintr_get(void) +{ + return (riscv_read_sstatus() & SSTATUS_SIE) != 0; +} + +static inline void riscv_sintr_restore(int enabled) +{ + if (enabled) + riscv_sintr_on(); + else + riscv_sintr_off(); +} + +/* Unified names used by BSP drivers (uart.c, etc.). */ +#define riscv_intr_on riscv_sintr_on +#define riscv_intr_off riscv_sintr_off +#define riscv_intr_get riscv_sintr_get +#define riscv_intr_restore riscv_sintr_restore + +#endif /* __ASSEMBLER__ */ +#endif /* RISCV_CSR_H */ diff --git a/ports/risc-v64/gnu/example_build/bananapi-f3/demo_threadx.c b/ports/risc-v64/gnu/example_build/bananapi-f3/demo_threadx.c new file mode 100644 index 00000000..2023df12 --- /dev/null +++ b/ports/risc-v64/gnu/example_build/bananapi-f3/demo_threadx.c @@ -0,0 +1,382 @@ +/***************************************************************************/ +/* Copyright (c) 2024 Microsoft Corporation */ +/* Copyright (c) 2026 Eclipse ThreadX contributors */ +/* */ +/* This program and the accompanying materials are made available under */ +/* the terms of the MIT License which is available at */ +/* https://opensource.org/licenses/MIT. */ +/* */ +/* SPDX-License-Identifier: MIT */ +/***************************************************************************/ + +/* 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" +#include "uart.h" +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9180 +#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) + { + puts("[Thread] : thread_0_entry is here!"); + /* 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) + { + puts("[Thread] : thread_1_entry is here!"); + /* 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) + { + puts("[Thread] : thread_2_entry is here!"); + /* 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) + { + puts("[Thread] : thread_3_and_4_entry is here!"); + + /* 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) + { + puts("[Thread] : thread_5_entry is here!"); + /* 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) + { + puts("[Thread] : thread_6_and_7_entry is here!"); + /* 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/risc-v64/gnu/example_build/bananapi-f3/entry.S b/ports/risc-v64/gnu/example_build/bananapi-f3/entry.S new file mode 100644 index 00000000..f1d0f03b --- /dev/null +++ b/ports/risc-v64/gnu/example_build/bananapi-f3/entry.S @@ -0,0 +1,73 @@ +/*************************************************************************** + * Copyright (c) 2026 10xEngineers + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + + .global _start + .extern main + .extern _sysstack_start + .extern _bss_start + .extern _bss_end + + .section .text.entry + .align 4 +_start: + /* Zero all general-purpose registers (x1–x31). */ + li x1, 0 + li x2, 0 + li x3, 0 + li x4, 0 + li x5, 0 + li x6, 0 + li x7, 0 + li x8, 0 + li x9, 0 + li x10, 0 + li x11, 0 + li x12, 0 + li x13, 0 + li x14, 0 + li x15, 0 + li x16, 0 + li x17, 0 + li x18, 0 + li x19, 0 + li x20, 0 + li x21, 0 + li x22, 0 + li x23, 0 + li x24, 0 + li x25, 0 + li x26, 0 + li x27, 0 + li x28, 0 + li x29, 0 + li x30, 0 + li x31, 0 + + /* Set up the initial supervisor stack (16 KiB). */ + la t0, _sysstack_start + li t1, 0x4000 + add sp, t0, t1 + + /* Zero the .bss section. */ + la t0, _bss_start + la t1, _bss_end +_bss_clean_start: + bgeu t0, t1, _bss_clean_end + sb zero, 0(t0) + addi t0, t0, 1 + j _bss_clean_start +_bss_clean_end: + + call main + + /* Halt if main() ever returns. */ +_park: + wfi + j _park diff --git a/ports/risc-v64/gnu/example_build/bananapi-f3/hwtimer.c b/ports/risc-v64/gnu/example_build/bananapi-f3/hwtimer.c new file mode 100644 index 00000000..4881ba3c --- /dev/null +++ b/ports/risc-v64/gnu/example_build/bananapi-f3/hwtimer.c @@ -0,0 +1,56 @@ +/*************************************************************************** + * Copyright (c) 2026 10xEngineers + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + +#include "tx_port.h" +#include "csr.h" +#include "hwtimer.h" + +/* + * SBI legacy set_timer ecall + * + * EID (a7) = 0 (SBI_SET_TIMER) + * a0 = absolute mtime compare value + * + * Programs mtimecmp for the current hart and clears sip.STIP. + */ +static inline void sbi_set_timer(uint64_t stime_value) +{ + register uint64_t a0 asm("a0") = stime_value; + register uint64_t a7 asm("a7") = 0; /* SBI_SET_TIMER */ + asm volatile("ecall" + : "+r"(a0) + : "r"(a7) + : "memory"); +} + +/* + * Read the free-running mtime counter via the rdtime pseudo-instruction. + * Accessible from S-mode per RISC-V Priv Spec §10.1 (Zicntr extension). + */ +static inline uint64_t read_time(void) +{ + uint64_t t; + asm volatile("rdtime %0" : "=r"(t)); + return t; +} + +int hwtimer_init(void) +{ + uint64_t now = read_time(); + sbi_set_timer(now + TICKNUM_PER_TIMER); + return 0; +} + +int hwtimer_handler(void) +{ + uint64_t now = read_time(); + sbi_set_timer(now + TICKNUM_PER_TIMER); + return 0; +} diff --git a/ports/risc-v64/gnu/example_build/bananapi-f3/hwtimer.h b/ports/risc-v64/gnu/example_build/bananapi-f3/hwtimer.h new file mode 100644 index 00000000..9265cee9 --- /dev/null +++ b/ports/risc-v64/gnu/example_build/bananapi-f3/hwtimer.h @@ -0,0 +1,43 @@ +/*************************************************************************** + * Copyright (c) 2026 10xEngineers + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + +#ifndef RISCV_HWTIMER_H +#define RISCV_HWTIMER_H + +#include <stdint.h> + +/* SpacemiT K1 TIMER (S-mode via SBI ecall) + * + * In S-mode the CLINT MMIO registers (mtime / mtimecmp) are protected + * by PMP and inaccessible. Timer operations are performed through the + * SBI legacy interface. + * + * rdtime - pseudo-instruction reading the time CSR (aliased to mtime + * by the implementation; accessible from S-mode per Priv + * Spec). + * + * SBI legacy set_timer (EID = 0, FID = 0) - programs mtimecmp on + * the current hart. Argument a0 = absolute compare value. + * Clears the pending supervisor timer interrupt (sip.STIP) + * as a side effect. + * + * + * Timebase frequency (DTS cpus { timebase-frequency = <0x16e3600>; }): + * 24,000,000 Hz (24 MHz). + * + * ThreadX tick rate: 10 Hz (100 ms period). + */ +#define TICKNUM_PER_SECOND 24000000UL +#define TICKNUM_PER_TIMER (TICKNUM_PER_SECOND / 10) + +int hwtimer_init(void); +int hwtimer_handler(void); + +#endif /* RISCV_HWTIMER_H */ diff --git a/ports/risc-v64/gnu/example_build/bananapi-f3/link.lds b/ports/risc-v64/gnu/example_build/bananapi-f3/link.lds new file mode 100644 index 00000000..99d9ae49 --- /dev/null +++ b/ports/risc-v64/gnu/example_build/bananapi-f3/link.lds @@ -0,0 +1,87 @@ +/*************************************************************************** + * Copyright (c) 2026 10xEngineers + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + +/* Memory Layout of the SpacemiT K1 SoC. + * + * Memory map: + * + * Physical Address Size Usage + * ────────────────── ────────── ────────────────────────────── + * 0x0000_0000 512 KiB Reserved - M-mode / OpenSBI + * 0x0008_0000 ~2 GiB Usable DRAM Bank 0 + * 0x0020_0000 - ← ThreadX kernel load address + * 0x7F00_0000 16 MiB Reserved - framebuffer / runtime + * 0x8000_0000–0xFFFF_FFFF PCI / MMIO hole (not DRAM) + * 0x1_0000_0000 ~14 GiB Usable DRAM Bank 1 + * + * Peripheral MMIO: + * + * 0xD401_7000 256 B UART0 (serial console) + * 0xE000_0000 64 MiB PLIC (interrupt controller) + * 0xE400_0000 64 KiB CLINT (timer / IPI) + * + * see more details on K1 Spec: + * Web : https://www.spacemit.com/community/document/?k1 + * PDF : https://cdn-resource.spacemit.com/file/chip/K1/K1_User_Manual_en.pdf + */ + + +OUTPUT_ARCH( "riscv" ) +ENTRY( _start ) + +PHDRS +{ + text PT_LOAD FLAGS(5); /* PF_R | PF_X */ + data PT_LOAD FLAGS(6); /* PF_R | PF_W */ +} + +SECTIONS +{ + . = 0x00200000; + + .text : { + KEEP(*(.text.entry)) + *(.text .text.*) + . = ALIGN(0x1000); + PROVIDE(etext = .); + } :text + + .rodata : { + . = ALIGN(16); + *(.srodata .srodata.*) + . = ALIGN(16); + *(.rodata .rodata.*) + } :text + + .data : { + . = ALIGN(16); + *(.sdata .sdata.*) + . = ALIGN(16); + *(.data .data.*) + } :data + + .bss : { + . = ALIGN(16); + _bss_start = .; + *(.sbss .sbss.*) + . = ALIGN(16); + *(.bss .bss.*) + _bss_end = .; + } :data + + .stack : { + . = ALIGN(4096); + _sysstack_start = .; + . += 0x4000; + _sysstack_end = .; + } :data + + PROVIDE(_end = .); +} diff --git a/ports/risc-v64/gnu/example_build/bananapi-f3/plic.c b/ports/risc-v64/gnu/example_build/bananapi-f3/plic.c new file mode 100644 index 00000000..6b04b65b --- /dev/null +++ b/ports/risc-v64/gnu/example_build/bananapi-f3/plic.c @@ -0,0 +1,132 @@ +/*************************************************************************** + * 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 + **************************************************************************/ + +#include "plic.h" +#include <stddef.h> + +static irq_callback callbacks[MAX_CALLBACK_NUM]; + +#define PLIC_ENABLE(hart) PLIC_SENABLE(hart) +#define PLIC_PRIORITY_REG(hart) PLIC_SPRIORITY(hart) +#define PLIC_CLAIM_REG(hart) PLIC_SCLAIM(hart) +#define PLIC_COMPLETE_REG(hart) PLIC_SCOMPLETE(hart) + +void plic_irq_enable(int irqno) +{ + int hart = (int)riscv_get_core(); + uint32_t word = irqno / 32; + uint32_t bit = irqno % 32; + volatile uint32_t *en = (volatile uint32_t *)(PLIC_ENABLE(hart) + word * 4); + *en |= (1u << bit); +} + +void plic_irq_disable(int irqno) +{ + int hart = (int)riscv_get_core(); + uint32_t word = irqno / 32; + uint32_t bit = irqno % 32; + volatile uint32_t *en = (volatile uint32_t *)(PLIC_ENABLE(hart) + word * 4); + *en &= ~(1u << bit); +} + +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) +{ + int hart = (int)riscv_get_core(); + + for (int i = 0; i < MAX_CALLBACK_NUM; i++) + callbacks[i] = NULL; + + /* Mask everything for this hart. */ + for (int word = 0; word < (MAX_CALLBACK_NUM + 31) / 32; word++) + *(volatile uint32_t *)(PLIC_ENABLE(hart) + word * 4) = 0; + + /* Set hart threshold to 0 so any non-zero priority IRQ can fire. */ + *(volatile uint32_t *)PLIC_PRIORITY_REG(hart) = 0; + + /* + * Drain stale pending interrupts left over from a prior boot stage + * (BootROM / OpenSBI / U-Boot). We temporarily enable every source + * so claim returns the actual highest-priority pending ID, then + * complete whatever was claimed. Loop until claim returns 0 + * (no more pending). This follows the PLIC spec: claim returns 0 + * when nothing is pending for this context. + */ + for (int word = 0; word < (PLIC_NUM_SOURCES + 31) / 32; word++) + *(volatile uint32_t *)(PLIC_ENABLE(hart) + word * 4) = 0xFFFFFFFFu; + + for (;;) { + uint32_t id = *(volatile uint32_t *)PLIC_CLAIM_REG(hart); + if (id == 0) + break; + *(volatile uint32_t *)PLIC_COMPLETE_REG(hart) = id; + } + + /* Re-mask everything; individual drivers will enable their sources. */ + for (int word = 0; word < (PLIC_NUM_SOURCES + 31) / 32; word++) + *(volatile uint32_t *)(PLIC_ENABLE(hart) + word * 4) = 0; + + /* + * Set default priority for every source to PLIC_DEFAULT_PRIORITY (2), + * Priority 0 means "never pending" per the SiFive PLIC spec, so any + * source that should be active must have priority >= 1. Individual + * drivers may override this with plic_prio_set() later. + */ + for (int i = 1; i <= PLIC_NUM_SOURCES; i++) + PLIC_SET_PRIO(i, PLIC_DEFAULT_PRIORITY); + + return 0; +} + +int plic_claim(void) +{ + int hart = (int)riscv_get_core(); + return (int)*(volatile uint32_t *)PLIC_CLAIM_REG(hart); +} + +void plic_complete(int irqno) +{ + int hart = (int)riscv_get_core(); + *(volatile uint32_t *)PLIC_COMPLETE_REG(hart) = (uint32_t)irqno; +} + +int plic_irq_intr(void) +{ + int ret = -1; + int irqno = plic_claim(); + if (irqno == 0) + return ret; + if (irqno < MAX_CALLBACK_NUM && callbacks[irqno] != NULL) + ret = (callbacks[irqno])(irqno); + plic_complete(irqno); + return ret; +} diff --git a/ports/risc-v64/gnu/example_build/bananapi-f3/plic.h b/ports/risc-v64/gnu/example_build/bananapi-f3/plic.h new file mode 100644 index 00000000..fa755f89 --- /dev/null +++ b/ports/risc-v64/gnu/example_build/bananapi-f3/plic.h @@ -0,0 +1,108 @@ +/*************************************************************************** + * 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_PLIC_H +#define RISCV_PLIC_H + +#include "csr.h" +#include <stdint.h> + +/* + * SpacemiT K1 PLIC - SiFive-compatible Platform-Level Interrupt Controller. + * + * From bananapi-f3.dts (linux main tree): + * interrupt-controller@e0000000 { + * reg-names = "control"; + * interrupts-extended = <0x10 0x0b 0x10 0x09 0x11 0x0b 0x11 0x09 0x12 0x0b 0x12 0x09 0x13 0x0b 0x13 0x09 0x14 0x0b 0x14 0x09 0x15 0x0b 0x15 0x09 0x16 0x0b 0x16 0x09 0x17 0x0b 0x17 0x09>; + * compatible = "riscv,plic0"; + * #interrupt-cells = <0x01>; + * reg = <0x00 0xe0000000 0x00 0x4000000>; + * phandle = <0x1e>; + * riscv,ndev = <0x9f>; // (159 external interrupt sources) + * riscv,max-priority = <0x07>; // (priority levels 1..7; 0 = disabled) + * interrupt-controller; + * }; + + * The RISCV_APB block is at 0xE000_0000 with size 0x1000_0000. + * Within that block the PLIC occupies 0xE000_0000..0xE3FF_FFFF + * (64 MiB) and the CLINT lives at 0xE400_0000. + * + * The K1 main CPU has up to 8 X60 cores (harts 0..7). Each hart + * exposes both an M-mode and S-mode interrupt context to the PLIC. + * + * PLIC register layout: + * + * Offset Size Description + * ────────────── ────── ──────────────────────────────────────────── + * 0x000000 4/src Source priority (src 0 reserved, 1..159) + * 0x001000 20B Pending bits (5 × 32-bit words) + * 0x002000 0x100/h Enable bits per hart (M-mode at ctx 0) + * 0x002080 0x100/h Enable bits per hart (S-mode at ctx 1) + * 0x200000 0x2000/h Threshold + Claim/Complete (M-mode ctx 0) + * 0x201000 0x2000/h Threshold + Claim/Complete (S-mode ctx 1) + * + * Context mapping: + * EN_PER_HART = 0x100 (two contexts × 0x80 each) + * EN_PER_CONTEXT = 0x80 + * THRES_PER_HART = 0x2000 (two contexts × 0x1000 each) + * THRES_PER_CTX = 0x1000 + * THRES_CLAIM_OFF = 0x4 (claim/complete at threshold + 4) + * + */ + +#define PLIC 0xE0000000UL +#define PLIC_SIZE 0x04000000UL /* 64 MiB, from DTS reg */ + +#define PLIC_PRIORITY (PLIC + 0x0) +#define PLIC_PRIO_PER_ID 4 /* stride: 4 bytes/source */ + +#define PLIC_EN (PLIC + 0x2000) +#define PLIC_EN_PER_HART 0x100 +#define PLIC_EN_PER_CONTEXT 0x80 + + +#define PLIC_SENABLE(hart) (PLIC_EN + (hart) * PLIC_EN_PER_HART + PLIC_EN_PER_CONTEXT) + +#define PLIC_THRES (PLIC + 0x200000) +#define PLIC_THRES_PER_HART 0x2000 +#define PLIC_THRES_PER_CONTEXT 0x1000 +#define PLIC_THRES_CLAIM_OFF 0x4 + +/* + * S-mode threshold/claim = M-mode base + one context offset (0x1000). + */ +#define PLIC_SPRIORITY(hart) (PLIC_THRES + (hart) * PLIC_THRES_PER_HART + PLIC_THRES_PER_CONTEXT) +#define PLIC_SCLAIM(hart) (PLIC_SPRIORITY(hart) + PLIC_THRES_CLAIM_OFF) +#define PLIC_SCOMPLETE(hart) PLIC_SCLAIM(hart) + +#define PLIC_GET_PRIO(irqno) (*(volatile uint32_t *)(PLIC_PRIORITY + (irqno) * PLIC_PRIO_PER_ID)) +#define PLIC_SET_PRIO(irqno, prio) (*(volatile uint32_t *)(PLIC_PRIORITY + (irqno) * PLIC_PRIO_PER_ID) = (prio)) + + +#define PLIC_NUM_SOURCES 159 +#define PLIC_MAX_PRIORITY 7 +#define PLIC_DEFAULT_PRIORITY 2 +#define MAX_CALLBACK_NUM (PLIC_NUM_SOURCES + 1) + +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-v64/gnu/example_build/bananapi-f3/trap.c b/ports/risc-v64/gnu/example_build/bananapi-f3/trap.c new file mode 100644 index 00000000..6418b02c --- /dev/null +++ b/ports/risc-v64/gnu/example_build/bananapi-f3/trap.c @@ -0,0 +1,65 @@ +/*************************************************************************** + * 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 + **************************************************************************/ + +/* + * S-mode trap handler + */ + +#include "csr.h" +#include "uart.h" +#include "hwtimer.h" +#include "plic.h" +#include <stdint.h> +#include <tx_port.h> +#include <tx_api.h> + +/* scause bit 63: interrupt flag. */ +#define SCAUSE_INTERRUPT 0x8000000000000000ull + +#define SCAUSE_S_TIMER_INT (SCAUSE_INTERRUPT | 5u) +#define SCAUSE_S_EXTERNAL_INT (SCAUSE_INTERRUPT | 9u) + +extern void _tx_timer_interrupt(void); + +void trap_handler(uintptr_t cause, uintptr_t epc, uintptr_t tval) +{ + (void)epc; + (void)tval; + + if (cause & SCAUSE_INTERRUPT) + { + if (cause == SCAUSE_S_TIMER_INT) + { + hwtimer_handler(); + _tx_timer_interrupt(); + } + else if (cause == SCAUSE_S_EXTERNAL_INT) + { + if (plic_irq_intr() != 0) + { + puts("[trap] PLIC dispatch failed"); + while (1) + ; + } + } + else + { + puts("[trap] unhandled S-mode interrupt"); + while (1) + ; + } + } + else + { + puts("[trap] unhandled synchronous exception"); + while (1) + ; + } +} diff --git a/ports/risc-v64/gnu/example_build/bananapi-f3/tx_initialize_low_level.S b/ports/risc-v64/gnu/example_build/bananapi-f3/tx_initialize_low_level.S new file mode 100644 index 00000000..3cfa83c3 --- /dev/null +++ b/ports/risc-v64/gnu/example_build/bananapi-f3/tx_initialize_low_level.S @@ -0,0 +1,139 @@ +/*************************************************************************** + * 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 + **************************************************************************/ + +#include "csr.h" + + .section .text + .align 4 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* trap_entry RISC-V64/GNU */ +/* 6.2.1 */ +/* AUTHOR */ +/* */ +/* Akif Ejaz, 10xEngineers */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for riscv processor trap handle */ +/* It will do the contex save and call c trap_handler and do contex */ +/* load */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* trap_handler */ +/* */ +/* CALLED BY */ +/* */ +/* hardware exception */ +/* */ +/**************************************************************************/ + + .global trap_entry + .extern trap_handler + .extern _tx_thread_context_save + .extern _tx_thread_context_restore + +trap_entry: +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + addi sp, sp, -520 /* 65 * 8: integer + FP */ +#else + addi sp, sp, -256 /* 32 * 8: integer only */ +#endif + + sd x1, 224(sp) /* Save RA at offset 28*8 */ + + call _tx_thread_context_save + + csrr a0, scause + csrr a1, sepc + csrr a2, stval + addi sp, sp, -8 + sd ra, 0(sp) + call trap_handler + ld ra, 0(sp) + addi sp, sp, 8 + + call _tx_thread_context_restore + /* never returns */ +_trap_err: + wfi + j _trap_err + + .section .text +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level RISC-V64/GNU */ +/* */ +/* 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. */ +/* */ +/**************************************************************************/ + + .global _tx_initialize_low_level + .weak _tx_initialize_low_level + .extern _end + .extern board_init + .extern trap_entry + .extern _tx_thread_system_stack_ptr + .extern _tx_initialize_unused_memory + +_tx_initialize_low_level: + la t0, _tx_thread_system_stack_ptr + sd sp, 0(t0) /* Save system stack */ + + la t0, _end + la t1, _tx_initialize_unused_memory + sd t0, 0(t1) /* First free address */ + + /* Disable global S-mode interrupts during early init. */ + li t0, SSTATUS_SIE + csrrc zero, sstatus, t0 + + /* Set SPIE = 1 so SRET re-enables interrupts. */ + li t0, SSTATUS_SPIE + csrrs zero, sstatus, t0 + + /* Enable S-mode timer and external interrupts. */ + li t0, (SIE_STIE | SIE_SEIE) + csrrs zero, sie, t0 + +#ifdef __riscv_flen + li t0, SSTATUS_FS + csrrs zero, sstatus, t0 + fscsr x0 +#endif + + addi sp, sp, -8 + sd ra, 0(sp) + call board_init + ld ra, 0(sp) + addi sp, sp, 8 + + la t0, trap_entry + csrw stvec, t0 + ret diff --git a/ports/risc-v64/gnu/example_build/bananapi-f3/uart.c b/ports/risc-v64/gnu/example_build/bananapi-f3/uart.c new file mode 100644 index 00000000..3ef89d09 --- /dev/null +++ b/ports/risc-v64/gnu/example_build/bananapi-f3/uart.c @@ -0,0 +1,127 @@ +/*************************************************************************** + * 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 + **************************************************************************/ + +#include "uart.h" +#include "csr.h" +#include "plic.h" +#include <stdint.h> + +/* + * The K1 UART Register layout + * + * Offset Name Access Description + * ────── ────── ────── ────────────────────────────── + * 0x00 RBR/THR R/W Receive Buffer / Transmit Hold + * 0x00 DLL R/W (DLAB=1) Divisor Latch Low + * 0x04 IER R/W Interrupt Enable + * 0x04 DLH R/W (DLAB=1) Divisor Latch High + * 0x08 IIR R Interrupt Identification + * 0x08 FCR W FIFO Control + * 0x0C LCR R/W Line Control + * 0x10 MCR R/W Modem Control + * 0x14 LSR R Line Status + * 0x18 MSR R Modem Status + * 0x1C SPR R/W Scratchpad + */ + +#define REG(off) (*(volatile uint32_t *)(UART0 + (off))) + +#define RHR 0x00 +#define THR 0x00 +#define DLL 0x00 +#define IER 0x04 +#define DLH 0x04 +#define IIR 0x08 +#define FCR 0x08 +#define LCR 0x0C +#define MCR 0x10 +#define LSR 0x14 + + +#define IER_RAVIE (1u << 0) /* Receiver data available IRQ enable */ +#define IER_TIE (1u << 1) /* Transmit data request IRQ enable */ +#define IER_RLSE (1u << 2) /* Receiver line status IRQ enable */ +#define IER_UUE (1u << 6) /* UART unit enable */ + +#define FCR_TRFIFOE (1u << 0) /* Transmit/Receive FIFO enable */ +#define FCR_RESETRF (1u << 1) /* Reset receive FIFO */ +#define FCR_RESETTF (1u << 2) /* Reset transmit FIFO */ +#define FCR_FIFO_CLEAR (FCR_RESETRF | FCR_RESETTF) + +#define LCR_WLS_8 (3u << 0) /* 8-bit word */ +#define LCR_DLAB (1u << 7) /* Divisor latch access bit */ + +#define LSR_DR (1u << 0) /* Data ready in receive FIFO */ +#define LSR_TDRQ (1u << 5) /* Transmit holding/FIFO ready */ +#define LSR_TEMT (1u << 6) /* Transmitter empty */ + +#define ReadReg(off) (REG(off) & 0xFF) +#define WriteReg(off, v) (REG(off) = (uint32_t)((v) & 0xFF)) + +int uart_init(void) +{ + /* + * If a prior boot stage (BootROM / OpenSBI / U-Boot) already + * configured the UART, keep its baud-rate divisor so we keep + * a working console. Just make sure interrupts are masked, + * the FIFOs are clean and the unit-enable bit is set. + */ + WriteReg(IER, 0x00); + + /* + * Program the baud-rate divisor for 115200 baud. + * The PXA UART functional clock on K1 is 14.7456 MHz: + * 14,745,600 / (16 × 8) = 115,200 baud → DLL = 8, DLH = 0. + */ + WriteReg(LCR, LCR_DLAB); + WriteReg(DLL, 0x08); + WriteReg(DLH, 0x00); + + WriteReg(LCR, LCR_WLS_8); + WriteReg(FCR, FCR_TRFIFOE | FCR_FIFO_CLEAR); + + /* Enable the UART transmit/receive engines (K1-specific). */ + WriteReg(IER, IER_UUE); + + plic_irq_enable(UART0_IRQ); + plic_prio_set(UART0_IRQ, 1); + + puts("[uart] UART0 initialized"); + return 0; +} + +static inline void uart_putc_nolock(int ch) +{ + while ((ReadReg(LSR) & LSR_TDRQ) == 0) + ; + WriteReg(THR, ch); +} + +int uart_putc(int ch) +{ + int intr_enable = riscv_intr_get(); + riscv_intr_off(); + uart_putc_nolock(ch); + riscv_intr_restore(intr_enable); + return 1; +} + +int uart_puts(const char *str) +{ + int i; + int intr_enable = riscv_intr_get(); + riscv_intr_off(); + for (i = 0; str[i] != 0; i++) + uart_putc_nolock(str[i]); + uart_putc_nolock('\r'); + uart_putc_nolock('\n'); + riscv_intr_restore(intr_enable); + return i; +} diff --git a/ports/risc-v64/gnu/example_build/bananapi-f3/uart.h b/ports/risc-v64/gnu/example_build/bananapi-f3/uart.h new file mode 100644 index 00000000..b03d0dd4 --- /dev/null +++ b/ports/risc-v64/gnu/example_build/bananapi-f3/uart.h @@ -0,0 +1,72 @@ +/*************************************************************************** + * 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_UART_H +#define RISCV_UART_H + +/* SpacemiT K1 UART0 + * + * DTS node: /soc/serial@d4017000 + * serial@d4017000 { + * power-domains = <0x20 0x00>; + * reg-io-width = <0x04>; + * clk-fpga = <0xe11130>; + * pinctrl-names = "default"; + * interconnect-names = "dma-mem"; + * pinctrl-0 = <0x23>; + * interconnects = <0x22>; + * resets = <0x1d 0x01>; + * interrupts = <0x2a>; + * clocks = <0x03 0x3a 0x03 0xb4>; + * interrupt-parent = <0x1e>; + * dma-names = "rx", "tx"; + * cpuidle,pm-runtime,sleep; + * compatible = "spacemit,pxa-uart"; + * status = "okay"; + * reg = <0x00 0xd4017000 0x00 0x100>; + * dmas = <0x21 0x04 0x01 0x21 0x03 0x01>; + * reg-shift = <0x02>; + * }; + + + * K1 User Manual Section 16.3.4 defines the register layout. Because + * reg-shift = 2, the logical 16550A register indices (0..7) map to + * byte offsets 0x00, 0x04, 0x08, ..., 0x1C (stride of 4). + * + * Register map (at UART0 + offset): + * + * Offset Name R/W Description + * ────── ────────── ─── ──────────────────────────────────── + * 0x00 RBR/THR/DLL R/W Receive Buffer / Transmit Holding / + * Divisor Latch Low (when LCR.DLAB=1) + * 0x04 IER/DLH R/W Interrupt Enable / + * Divisor Latch High (when LCR.DLAB=1) + * 0x08 IIR/FCR R/W Interrupt Identification (R) / + * FIFO Control (W) + * 0x0C LCR R/W Line Control + * 0x10 MCR R/W Modem Control + * 0x14 LSR R Line Status + * 0x18 MSR R Modem Status + * 0x1C SPR R/W Scratchpad + * + */ + +#define UART0 0xD4017000UL +#define UART0_SIZE 0x100UL /* 256 bytes, from DTS reg */ +#define UART0_IRQ 42 /* DTS: interrupts = <0x2a> */ +#define UART0_REG_SHIFT 2 /* DTS: reg-shift = <0x02> */ + +#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-v64/gnu/example_build/qemu_virt/board.c b/ports/risc-v64/gnu/example_build/qemu_virt/board.c index 60a61163..805a1ac8 100644 --- a/ports/risc-v64/gnu/example_build/qemu_virt/board.c +++ b/ports/risc-v64/gnu/example_build/qemu_virt/board.c @@ -1,3 +1,14 @@ +/***************************************************************************/ +/* Copyright (c) 2024 Microsoft Corporation */ +/* Copyright (c) 2026 Eclipse ThreadX contributors */ +/* */ +/* This program and the accompanying materials are made available under */ +/* the terms of the MIT License which is available at */ +/* https://opensource.org/licenses/MIT. */ +/* */ +/* SPDX-License-Identifier: MIT */ +/***************************************************************************/ + #include "plic.h" #include "hwtimer.h" #include "uart.h" 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 24a4c3ac..0568e7a8 100755 --- a/ports/risc-v64/gnu/example_build/qemu_virt/build_libthreadx.sh +++ b/ports/risc-v64/gnu/example_build/qemu_virt/build_libthreadx.sh @@ -1,6 +1,35 @@ #!/bin/bash +############################################################################## +# Copyright (c) 2024 Microsoft Corporation +# Copyright (c) 2026 Eclipse ThreadX contributors +# +# This program and the accompanying materials are made available under the +# terms of the MIT License which is available at +# https://opensource.org/licenses/MIT. +# +# SPDX-License-Identifier: MIT +############################################################################## + +printf "y\n" | rm -rf ../../../../../build/ +rm -f kernel.elf pushd ../../../../../ cmake -Bbuild -GNinja -DCMAKE_TOOLCHAIN_FILE=cmake/riscv64_gnu.cmake . cmake --build ./build/ popd + +riscv64-unknown-elf-gcc \ + -march=rv64gc -mabi=lp64d \ + -mcmodel=medany -O0 -g3 -Wall \ + -ffunction-sections -fdata-sections \ + -I../../../../../common/inc \ + -I../../inc \ + entry.S \ + tx_initialize_low_level.S \ + board.c uart.c hwtimer.c plic.c trap.c demo_threadx.c \ + -L../../../../../build -lthreadx \ + -T link.lds -nostartfiles \ + -o kernel.elf + + +qemu-system-riscv64 -nographic -smp 1 -bios none -m 128M -machine virt -kernel kernel.elf diff --git a/ports/risc-v64/gnu/example_build/qemu_virt/csr.h b/ports/risc-v64/gnu/example_build/qemu_virt/csr.h index 92ca6644..b5df6f12 100644..120000 --- a/ports/risc-v64/gnu/example_build/qemu_virt/csr.h +++ b/ports/risc-v64/gnu/example_build/qemu_virt/csr.h @@ -1,374 +1 @@ -/*************************************************************************** - * 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) - -// 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 <stdint.h> - -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 +../../../../risc-v_common/inc/csr.h
\ No newline at end of file diff --git a/ports/risc-v64/gnu/example_build/qemu_virt/demo_threadx.c b/ports/risc-v64/gnu/example_build/qemu_virt/demo_threadx.c index 229dcc68..d3195019 100644 --- a/ports/risc-v64/gnu/example_build/qemu_virt/demo_threadx.c +++ b/ports/risc-v64/gnu/example_build/qemu_virt/demo_threadx.c @@ -1,11 +1,27 @@ +/***************************************************************************/ +/* Copyright (c) 2024 Microsoft Corporation */ +/* Copyright (c) 2026 Eclipse ThreadX contributors */ +/* */ +/* This program and the accompanying materials are made available under */ +/* the terms of the MIT License which is available at */ +/* https://opensource.org/licenses/MIT. */ +/* */ +/* SPDX-License-Identifier: MIT */ +/***************************************************************************/ + /* 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" #include "uart.h" +#if defined(__riscv_vector) +#define DEMO_STACK_SIZE (1024 + 16448) /* 16448 for RVV Extension */ +#define DEMO_BYTE_POOL_SIZE (9180 + 148032) /* 148032 for RVV Extension */ +#else #define DEMO_STACK_SIZE 1024 #define DEMO_BYTE_POOL_SIZE 9180 +#endif #define DEMO_BLOCK_POOL_SIZE 100 #define DEMO_QUEUE_SIZE 100 diff --git a/ports/risc-v64/gnu/example_build/qemu_virt/entry.S b/ports/risc-v64/gnu/example_build/qemu_virt/entry.S new file mode 100644 index 00000000..2b68310d --- /dev/null +++ b/ports/risc-v64/gnu/example_build/qemu_virt/entry.S @@ -0,0 +1,73 @@ +/***************************************************************************/ +/* Copyright (c) 2024 Microsoft Corporation */ +/* Copyright (c) 2026 Eclipse ThreadX contributors */ +/* */ +/* This program and the accompanying materials are made available under */ +/* the terms of the MIT License which is available at */ +/* https://opensource.org/licenses/MIT. */ +/* */ +/* SPDX-License-Identifier: MIT */ +/***************************************************************************/ + + +.section .text +.align 4 +.global _start +.extern main +.extern _sysstack_start +.extern _bss_start +.extern _bss_end +_start: + csrr t0, mhartid + bne t0, zero, 1f + li x1, 0 + li x2, 0 + li x3, 0 + li x4, 0 + li x5, 0 + li x6, 0 + li x7, 0 + li x8, 0 + li x9, 0 + li x10, 0 + li x11, 0 + li x12, 0 + li x13, 0 + li x14, 0 + li x15, 0 + li x16, 0 + li x17, 0 + li x18, 0 + li x19, 0 + li x20, 0 + li x21, 0 + li x22, 0 + li x23, 0 + li x24, 0 + li x25, 0 + li x26, 0 + li x27, 0 + li x28, 0 + li x29, 0 + li x30, 0 + li x31, 0 + la t0, _sysstack_start +#ifdef __riscv_vector + li t1, 0x5000 +#else + li t1, 0x1000 +#endif + add sp, t0, t1 + la t0, _bss_start + la t1, _bss_end +_bss_clean_start: + bgeu t0, t1, _bss_clean_end + sb zero, 0(t0) + addi t0, t0, 1 + j _bss_clean_start +_bss_clean_end: + call main +1: + /* todo smp */ + wfi + j 1b diff --git a/ports/risc-v64/gnu/example_build/qemu_virt/entry.s b/ports/risc-v64/gnu/example_build/qemu_virt/entry.s deleted file mode 100644 index 9b202ca1..00000000 --- a/ports/risc-v64/gnu/example_build/qemu_virt/entry.s +++ /dev/null @@ -1,58 +0,0 @@ - -.section .text -.align 4 -.global _start -.extern main -.extern _sysstack_start -.extern _bss_start -.extern _bss_end -_start: - csrr t0, mhartid - bne t0, zero, 1f - li x1, 0 - li x2, 0 - li x3, 0 - li x4, 0 - li x5, 0 - li x6, 0 - li x7, 0 - li x8, 0 - li x9, 0 - li x10, 0 - li x11, 0 - li x12, 0 - li x13, 0 - li x14, 0 - li x15, 0 - li x16, 0 - li x17, 0 - li x18, 0 - li x19, 0 - li x20, 0 - li x21, 0 - li x22, 0 - li x23, 0 - li x24, 0 - li x25, 0 - li x26, 0 - li x27, 0 - li x28, 0 - li x29, 0 - li x30, 0 - li x31, 0 - la t0, _sysstack_start - li t1, 0x1000 - add sp, t0, t1 - la t0, _bss_start - la t1, _bss_end -_bss_clean_start: - bgeu t0, t1, _bss_clean_end - sb zero, 0(t0) - addi t0, t0, 1 - j _bss_clean_start -_bss_clean_end: - call main -1: - /* todo smp */ - wfi - j 1b diff --git a/ports/risc-v64/gnu/example_build/qemu_virt/hwtimer.c b/ports/risc-v64/gnu/example_build/qemu_virt/hwtimer.c index 29cf1117..122e71a1 100644 --- a/ports/risc-v64/gnu/example_build/qemu_virt/hwtimer.c +++ b/ports/risc-v64/gnu/example_build/qemu_virt/hwtimer.c @@ -1,3 +1,14 @@ +/***************************************************************************/ +/* Copyright (c) 2024 Microsoft Corporation */ +/* Copyright (c) 2026 Eclipse ThreadX contributors */ +/* */ +/* This program and the accompanying materials are made available under */ +/* the terms of the MIT License which is available at */ +/* https://opensource.org/licenses/MIT. */ +/* */ +/* SPDX-License-Identifier: MIT */ +/***************************************************************************/ + #include "tx_port.h" #include "csr.h" #include "hwtimer.h" diff --git a/ports/risc-v64/gnu/example_build/qemu_virt/link.lds b/ports/risc-v64/gnu/example_build/qemu_virt/link.lds index 522f90d9..d0d8a6bc 100644 --- a/ports/risc-v64/gnu/example_build/qemu_virt/link.lds +++ b/ports/risc-v64/gnu/example_build/qemu_virt/link.lds @@ -42,6 +42,9 @@ SECTIONS . = ALIGN(4096); _sysstack_start = .; . += 0x1000; +#ifdef __riscv_vector + . += 0x4000; +#endif _sysstack_end = .; } diff --git a/ports/risc-v64/gnu/example_build/qemu_virt/plic.c b/ports/risc-v64/gnu/example_build/qemu_virt/plic.c index 01e5c71a..b247f4fe 100644..120000 --- a/ports/risc-v64/gnu/example_build/qemu_virt/plic.c +++ b/ports/risc-v64/gnu/example_build/qemu_virt/plic.c @@ -1,72 +1 @@ -#include "plic.h" -#include <stddef.h> -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<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; - return; -} - -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; -} +../../../../risc-v_common/example_build/plic/plic.c
\ No newline at end of file diff --git a/ports/risc-v64/gnu/example_build/qemu_virt/plic.h b/ports/risc-v64/gnu/example_build/qemu_virt/plic.h index 42e4c925..f776a3d2 100644..120000 --- a/ports/risc-v64/gnu/example_build/qemu_virt/plic.h +++ b/ports/risc-v64/gnu/example_build/qemu_virt/plic.h @@ -1,51 +1 @@ -/*************************************************************************** - * 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 <stdint.h> - -#define PLIC 0x0c000000L -#define PLIC_PRIORITY (PLIC + 0x0) -#define PLIC_PENDING (PLIC + 0x1000) -#define PLIC_MENABLE(hart) (PLIC + 0x2000 + (hart)*0x100) -#define PLIC_SENABLE(hart) (PLIC + 0x2080 + (hart)*0x100) -#define PLIC_MPRIORITY(hart) (PLIC + 0x200000 + (hart)*0x2000) -#define PLIC_SPRIORITY(hart) (PLIC + 0x201000 + (hart)*0x2000) -#define PLIC_MCLAIM(hart) (PLIC + 0x200004 + (hart)*0x2000) -#define PLIC_SCLAIM(hart) (PLIC + 0x201004 + (hart)*0x2000) -#define PLIC_MCOMPLETE(hart) (PLIC + 0x200004 + (hart)*0x2000) -#define PLIC_SCOMPLETE(hart) (PLIC + 0x201004 + (hart)*0x2000) - - -#define PLIC_GET_PRIO(irqno) (*(uint32_t *)(PLIC_PRIORITY + (irqno)*4)) -#define PLIC_SET_PRIO(irqno, prio) (*(uint32_t *)(PLIC_PRIORITY + (irqno)*4) = (prio)) - -#define MAX_CALLBACK_NUM 128 -typedef int (*irq_callback)(int irqno); - -void plic_irq_enable(int irqno); -void plic_irq_disable(int irqno); -int plic_prio_get(int irqno); -void plic_prio_set(int irqno, int prio); -int plic_register_callback(int irqno, irq_callback callback); -int plic_unregister_callback(int irqno); -int plic_init(void); -int plic_claim(void); -void plic_complete(int irqno); - -int plic_irq_intr(void); - -#endif - +../../../../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 00000000..861f50e6 --- /dev/null +++ b/ports/risc-v64/gnu/example_build/qemu_virt/test/threadx_test_tx_gnu_riscv64_qemu.py @@ -0,0 +1,286 @@ +############################################################################## +# Copyright (c) 2024 Microsoft Corporation +# Copyright (c) 2026 Eclipse ThreadX contributors +# +# This program and the accompanying materials are made available under the +# terms of the MIT License which is available at +# https://opensource.org/licenses/MIT. +# +# SPDX-License-Identifier: MIT +############################################################################## + +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 index 5de9f37a..bdce3305 100644..120000 --- a/ports/risc-v64/gnu/example_build/qemu_virt/trap.c +++ b/ports/risc-v64/gnu/example_build/qemu_virt/trap.c @@ -1,46 +1 @@ -#include "csr.h" -#include <stdint.h> -#include "uart.h" -#include "hwtimer.h" -#include "plic.h" -#include <tx_port.h> -#include <tx_api.h> - -#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) ; - } -} +../../../../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/tx_initialize_low_level.S b/ports/risc-v64/gnu/example_build/qemu_virt/tx_initialize_low_level.S index eba6c458..a9b2fda7 100644 --- a/ports/risc-v64/gnu/example_build/qemu_virt/tx_initialize_low_level.S +++ b/ports/risc-v64/gnu/example_build/qemu_virt/tx_initialize_low_level.S @@ -66,6 +66,13 @@ #else addi sp, sp, -256 // Allocate space for all registers - without floating point enabled (32*8) #endif +#if defined(__riscv_vector) + /* Allocate space for vector registers */ + csrr t4, vlenb + slli t4, t4, 5 + addi t4, t4, 4*8 + sub sp, sp, t4 +#endif sd x1, 224(sp) // Store RA (28*8 = 224, because call will override ra [ra is a callee register in riscv]) @@ -150,6 +157,10 @@ _tx_initialize_low_level: csrrs zero, mstatus, t0 // set MSTATUS_FS bit to open f/d isa in riscv fscsr x0 #endif +#ifdef __riscv_vector + li t0, MSTATUS_VS + csrrs zero, mstatus, t0 // set MSTATUS_VS bit to open vector isa in riscv +#endif addi sp, sp, -8 sd ra, 0(sp) call board_init diff --git a/ports/risc-v64/gnu/example_build/qemu_virt/uart.c b/ports/risc-v64/gnu/example_build/qemu_virt/uart.c index eea30203..1666c8c8 100644..120000 --- a/ports/risc-v64/gnu/example_build/qemu_virt/uart.c +++ b/ports/risc-v64/gnu/example_build/qemu_virt/uart.c @@ -1,100 +1 @@ -#include "uart.h" -#include "csr.h" -#include "plic.h" -#include <stdint.h> - -// 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; -} +../../../../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 index 824f0b56..aba5ae7b 100644..120000 --- a/ports/risc-v64/gnu/example_build/qemu_virt/uart.h +++ b/ports/risc-v64/gnu/example_build/qemu_virt/uart.h @@ -1,22 +1 @@ -/*************************************************************************** - * 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 +../../../../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 ae617b39..ac751208 100644 --- a/ports/risc-v64/gnu/inc/tx_port.h +++ b/ports/risc-v64/gnu/inc/tx_port.h @@ -72,20 +72,44 @@ #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; typedef int INT; typedef unsigned int UINT; -typedef long LONG; -typedef unsigned long ULONG; +typedef int LONG; +typedef unsigned int ULONG; typedef unsigned long long ULONG64; typedef short SHORT; typedef unsigned short USHORT; #define ULONG64_DEFINED #endif /* __ASSEMBLER__ */ +#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 @@ -100,7 +124,11 @@ typedef unsigned short USHORT; 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 */ +#if defined(__riscv_vector) +#define TX_MINIMUM_STACK (1024 + 16448) /* Minimum stack size for this port */ +#else +#define TX_MINIMUM_STACK 1024 /* Minimum stack size for this port */ +#endif #endif @@ -108,7 +136,11 @@ typedef unsigned short USHORT; 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 */ +#if defined(__riscv_vector) +#define TX_TIMER_THREAD_STACK_SIZE (1024 + 16448) /* Default timer thread stack size */ +#else +#define TX_TIMER_THREAD_STACK_SIZE 1024 /* Default timer thread stack size */ +#endif #endif #ifndef TX_TIMER_THREAD_PRIORITY @@ -118,8 +150,13 @@ typedef unsigned short USHORT; /* 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 */ +#ifdef TX_RISCV_SMODE +#define TX_INT_DISABLE 0x00000000 /* Disable interrupts value */ +#define TX_INT_ENABLE 0x00000002 /* Enable interrupt value (SIE bit 1 of sstatus) */ +#else +#define TX_INT_DISABLE 0x00000000 /* Disable interrupts value */ +#define TX_INT_ENABLE 0x00000008 /* Enable interrupt value (MIE bit 3 of mstatus) */ +#endif /* Define the clock source for trace event entry time stamp. The following two item are port specific. @@ -240,6 +277,19 @@ UINT _tx_thread_interrupt_control(UIN #define TX_INTERRUPT_SAVE_AREA register UINT interrupt_save; +#ifdef TX_RISCV_SMODE +#define TX_DISABLE __asm__ volatile("csrrci %0, sstatus, 2" : "=r" (interrupt_save) :: "memory"); +#define TX_RESTORE { \ + unsigned long _temp_sstatus; \ + __asm__ volatile( \ + "csrc sstatus, 2\n" \ + "andi %0, %1, 2\n" \ + "csrs sstatus, %0" \ + : "=&r" (_temp_sstatus) \ + : "r" (interrupt_save) \ + : "memory"); \ + } +#else #define TX_DISABLE __asm__ volatile("csrrci %0, mstatus, 8" : "=r" (interrupt_save) :: "memory"); #define TX_RESTORE { \ unsigned long _temp_mstatus; \ @@ -251,6 +301,7 @@ UINT _tx_thread_interrupt_control(UIN : "r" (interrupt_save) \ : "memory"); \ } +#endif /* TX_RISCV_SMODE */ #else @@ -272,12 +323,110 @@ UINT _tx_thread_interrupt_control(UIN #define TX_SEMAPHORE_DISABLE TX_DISABLE +/* Define automated coverage test extensions for the ThreadX regression test. */ + +#ifndef __ASSEMBLER__ + +typedef unsigned int TEST_FLAG; +extern TEST_FLAG threadx_byte_allocate_loop_test; +extern TEST_FLAG threadx_byte_release_loop_test; +extern TEST_FLAG threadx_mutex_suspension_put_test; +extern TEST_FLAG threadx_mutex_suspension_priority_test; +#ifndef TX_TIMER_PROCESS_IN_ISR +extern TEST_FLAG threadx_delete_timer_thread; +#endif + +extern void abort_and_resume_byte_allocating_thread(void); +extern void abort_all_threads_suspended_on_mutex(void); +extern void suspend_lowest_priority(void); +#ifndef TX_TIMER_PROCESS_IN_ISR +extern void delete_timer_thread(void); +#endif +extern TEST_FLAG test_stack_analyze_flag; +extern TEST_FLAG test_initialize_flag; +extern TEST_FLAG test_forced_mutex_timeout; + + +#ifdef TX_REGRESSION_TEST + +#define TX_BYTE_ALLOCATE_EXTENSION if (threadx_byte_allocate_loop_test == ((TEST_FLAG) 1)) \ + { \ + pool_ptr -> tx_byte_pool_owner = TX_NULL; \ + threadx_byte_allocate_loop_test = ((TEST_FLAG) 0); \ + } + +#define TX_BYTE_RELEASE_EXTENSION if (threadx_byte_release_loop_test == ((TEST_FLAG) 1)) \ + { \ + threadx_byte_release_loop_test = ((TEST_FLAG) 0); \ + abort_and_resume_byte_allocating_thread(); \ + } + +#define TX_MUTEX_PUT_EXTENSION_1 if (threadx_mutex_suspension_put_test == ((TEST_FLAG) 1)) \ + { \ + threadx_mutex_suspension_put_test = ((TEST_FLAG) 0); \ + abort_all_threads_suspended_on_mutex(); \ + } + +#define TX_MUTEX_PUT_EXTENSION_2 if (test_forced_mutex_timeout == ((TEST_FLAG) 1)) \ + { \ + test_forced_mutex_timeout = ((TEST_FLAG) 0); \ + _tx_thread_wait_abort(mutex_ptr -> tx_mutex_suspension_list); \ + } + +#define TX_MUTEX_PRIORITY_CHANGE_EXTENSION if (threadx_mutex_suspension_priority_test == ((TEST_FLAG) 1)) \ + { \ + threadx_mutex_suspension_priority_test = ((TEST_FLAG) 0); \ + suspend_lowest_priority(); \ + } + +#ifndef TX_TIMER_PROCESS_IN_ISR + +#define TX_TIMER_INITIALIZE_EXTENSION(a) if (threadx_delete_timer_thread == ((TEST_FLAG) 1)) \ + { \ + threadx_delete_timer_thread = ((TEST_FLAG) 0); \ + delete_timer_thread(); \ + (a) = ((UINT) 1); \ + } + +#endif + +#define TX_THREAD_STACK_ANALYZE_EXTENSION if (test_stack_analyze_flag == ((TEST_FLAG) 1)) \ + { \ + thread_ptr -> tx_thread_id = ((TEST_FLAG) 0); \ + test_stack_analyze_flag = ((TEST_FLAG) 0); \ + } \ + else if (test_stack_analyze_flag == ((TEST_FLAG) 2)) \ + { \ + stack_ptr = thread_ptr -> tx_thread_stack_start; \ + test_stack_analyze_flag = ((TEST_FLAG) 0); \ + } \ + else if (test_stack_analyze_flag == ((TEST_FLAG) 3)) \ + { \ + *stack_ptr = TX_STACK_FILL; \ + test_stack_analyze_flag = ((TEST_FLAG) 0); \ + } \ + else \ + { \ + test_stack_analyze_flag = ((TEST_FLAG) 0); \ + } + +#define TX_INITIALIZE_KERNEL_ENTER_EXTENSION if (test_initialize_flag == ((TEST_FLAG) 1)) \ + { \ + test_initialize_flag = ((TEST_FLAG) 0); \ + return; \ + } + +#endif /* TX_REGRESSION_TEST */ + +#endif /* __ASSEMBLER__ */ + + /* Define the version ID of ThreadX. This may be utilized by the application. */ #ifndef __ASSEMBLER__ #ifdef TX_THREAD_INIT CHAR _tx_version_id[] = - "(c) 2024 Microsoft Corp. (c) 2026-present Eclipse ThreadX contributors. * ThreadX RISC-V64/GNU Version 6.5.0.202601 *"; + "(c) 2024 Microsoft Corp. (c) 2026-present Eclipse ThreadX contributors. * ThreadX RISC-V64/GNU Version 6.5.1.202602 *"; #else extern CHAR _tx_version_id[]; #endif /* TX_THREAD_INIT */ diff --git a/ports/risc-v64/gnu/src/tx_thread_context_restore.S b/ports/risc-v64/gnu/src/tx_thread_context_restore.S index 6aae3981..cebc8a06 100644 --- a/ports/risc-v64/gnu/src/tx_thread_context_restore.S +++ b/ports/risc-v64/gnu/src/tx_thread_context_restore.S @@ -63,7 +63,11 @@ _tx_thread_context_restore: /* Lockout interrupts. */ +#ifdef TX_RISCV_SMODE + csrci sstatus, 0x02 // Disable interrupts (SIE bit 1) +#else csrci mstatus, 0x08 // Disable interrupts (MIE bit 3) +#endif #ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY call _tx_execution_isr_exit // Call the ISR execution exit function @@ -74,9 +78,9 @@ _tx_thread_context_restore: { */ la t0, _tx_thread_system_state // Pickup addr of nested interrupt count - ld t1, 0(t0) // Pickup nested interrupt count + lw t1, 0(t0) // Pickup nested interrupt count addi t1, t1, -1 // Decrement the nested interrupt counter - sd t1, 0(t0) // Store new nested count + sw t1, 0(t0) // Store new nested count beqz t1, _tx_thread_not_nested_restore // If 0, not nested restore /* Interrupts are nested. */ @@ -133,6 +137,34 @@ _tx_thread_context_restore: csrw fcsr, t0 #endif +#if defined(__riscv_vector) + /* Recover vector registers v0-v31 */ +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + addi t1, sp, 64*8 +#else + addi t1, sp, 31*8 +#endif + addi t2, t1, 4*8 + vsetvli t3, zero, e8, m8, ta, ma + vle8.v v0, (t2) // Recover v0 ~ v7 + add t2, t2, t3 + vle8.v v8, (t2) // Recover v8 ~ v15 + add t2, t2, t3 + vle8.v v16, (t2) // Recover v16 ~ v23 + add t2, t2, t3 + vle8.v v24, (t2) // Recover v24 ~ v31 + add t2, t2, t3 + + /* Recover vector CSRs */ + ld t2, 0*8(t1) + ld t3, 1*8(t1) + ld t4, 2*8(t1) + vsetvl zero, t4, t3 + csrw vstart, t2 + ld t4, 3*8(t1) + csrw vcsr, t4 +#endif + /* Recover standard registers. */ /* Restore registers, @@ -140,31 +172,46 @@ _tx_thread_context_restore: Also skip the saved registers since they have been restored by any function we called, except s0 since we use it ourselves. */ - ld t0, 30*8(sp) // Recover mepc + ld t0, 30*8(sp) // Recover exception PC +#ifdef TX_RISCV_SMODE + csrw sepc, t0 // Setup sepc + + /* Compose sstatus via read/modify/write to avoid clobbering unrelated bits. + Set SPIE and restore SPP to Supervisor, preserve other fields. */ + + csrr t1, sstatus + li t4, ~0x122 // Clear mask for SPP/SPIE/SIE + and t1, t1, t4 + li t3, 0x120 // Set SPP=Supervisor(0x100) + SPIE(0x20) so sret re-enables SIE + or t1, t1, t3 + +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + li t0, 0x6000 // Set FS=Dirty (bits 14:13) for FP state + or t1, t1, t0 +#endif + csrw sstatus, t1 // Update sstatus safely +#else 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 + li t2, 0x1880 // Set MPP(0x1800) | MPIE(0x80) - /* 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 + or t1, t1, t2 // MPP and MPIE are now set. All other bits are preserved + andi t1, t1, ~0x8 // Clear MIE, Hardware will restore it from MPIE #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 +#if defined(__riscv_vector) + li t0, 0x0200 // Set VS bits (bits 10:9 to 01) for vector state + or t1, t1, t0 +#endif csrw mstatus, t1 // Update mstatus safely +#endif ld ra, 28*8(sp) // Recover return address ld t0, 19*8(sp) // Recover t0 @@ -189,7 +236,26 @@ _tx_thread_context_restore: #else addi sp, sp, 32*8 // Recover stack frame - without floating point enabled #endif + +#if defined(__riscv_vector) +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + addi t0, sp, -65*8 +#else + addi t0, sp, -32*8 +#endif + csrr t1, vlenb // Get vector register byte length + slli t1, t1, 5 // Multiply by 32 (number of vector registers) + addi t1, t1, 4*8 // Add vector CSR space: vstart, vtype, vl, vcsr + add sp, sp, t1 // Recover vector stack frame + + ld t1, 18*8(t0) // Recover t1 + ld t0, 19*8(t0) // Recover t0 +#endif +#ifdef TX_RISCV_SMODE + sret // Return to point of interrupt +#else mret // Return to point of interrupt +#endif /* } */ _tx_thread_not_nested_restore: @@ -217,7 +283,7 @@ _tx_thread_no_preempt_restore: /* Pickup the saved stack pointer. */ /* sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; */ - ld sp, 16(t1) // Switch back to thread's stack + ld sp, 8(t1) // Switch back to thread's stack /* Recover floating point registers. */ #if defined(__riscv_float_abi_single) @@ -268,29 +334,80 @@ _tx_thread_no_preempt_restore: csrw fcsr, t0 // Restore fcsr #endif +#if defined(__riscv_vector) + /* Recover vector registers v0-v31 */ +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + addi t1, sp, 64*8 +#else + addi t1, sp, 31*8 +#endif + addi t2, t1, 4*8 + vsetvli t3, zero, e8, m8, ta, ma + vle8.v v0, (t2) // Recover v0 ~ v7 + add t2, t2, t3 + vle8.v v8, (t2) // Recover v8 ~ v15 + add t2, t2, t3 + vle8.v v16, (t2) // Recover v16 ~ v23 + add t2, t2, t3 + vle8.v v24, (t2) // Recover v24 ~ v31 + add t2, t2, t3 + + /* Recover vector CSRs */ + ld t2, 0*8(t1) + ld t3, 1*8(t1) + ld t4, 2*8(t1) + vsetvl zero, t4, t3 + csrw vstart, t2 + ld t4, 3*8(t1) + csrw vcsr, t4 +#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 */ - ld t0, 30*8(sp) // Recover mepc - csrw mepc, t0 // Setup mepc + ld t0, 30*8(sp) // Recover exception PC +#ifdef TX_RISCV_SMODE + csrw sepc, t0 // Setup sepc - /* Compose mstatus via read/modify/write to avoid clobbering unrelated bits. */ + /* Compose sstatus 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 + csrr t1, sstatus + li t4, ~0x122 // Clear mask for SPP/SPIE/SIE and t1, t1, t4 + li t3, 0x120 // Set SPP=Supervisor(0x100) + SPIE(0x20) so sret re-enables SIE or t1, t1, t3 #if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + li t0, 0x6000 // Set FS=Dirty (bits 14:13) for FP state + or t1, t1, t0 +#endif + csrw sstatus, t1 // Update sstatus safely +#else + 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 + li t2, 0x1880 // Set MPP(0x1800) | MPIE(0x80) + + or t1, t1, t2 // MPP and MPIE are now set. All other bits are preserved + andi t1, t1, ~0x8 // Clear MIE, Hardware will restore it from MPIE + +#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 +#if defined(__riscv_vector) + li t0, 0x0200 // Set VS bits (bits 10:9 to 01) for vector state + or t1, t1, t0 +#endif csrw mstatus, t1 // Update mstatus safely +#endif ld ra, 28*8(sp) // Recover return address ld t0, 19*8(sp) // Recover t0 @@ -315,7 +432,26 @@ _tx_thread_no_preempt_restore: #else addi sp, sp, 32*8 // Recover stack frame - without floating point enabled #endif + +#if defined(__riscv_vector) +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + addi t0, sp, -65*8 +#else + addi t0, sp, -32*8 +#endif + csrr t1, vlenb // Get vector register byte length + slli t1, t1, 5 // Multiply by 32 (number of vector registers) + addi t1, t1, 4*8 // Add vector CSR space: vstart, vtype, vl, vcsr + add sp, sp, t1 // Recover vector stack frame + + ld t1, 18*8(t0) // Recover t1 + ld t0, 19*8(t0) // Recover t0 +#endif +#ifdef TX_RISCV_SMODE + sret // Return to point of interrupt +#else mret // Return to point of interrupt +#endif /* } else @@ -324,7 +460,7 @@ _tx_thread_preempt_restore: /* Instead of directly activating the thread again, ensure we save the entire stack frame by saving the remaining registers. */ - ld t0, 16(t1) // Pickup thread's stack pointer + ld t0, 8(t1) // Pickup thread's stack pointer ori t3, zero, 1 // Build interrupt stack type sd t3, 0(t0) // Store stack type @@ -357,6 +493,36 @@ _tx_thread_preempt_restore: fsd f27, 58*8(t0) // Store fs11 #endif +#if defined(__riscv_vector) + /* Store vector registers and CSRs */ +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + addi t1, t0, 64*8 +#else + addi t1, t0, 31*8 +#endif + /* Store vector CSRs */ + csrr t2, vstart // Store vstart + sd t2, 0*8(t1) + csrr t2, vtype // Store vtype + sd t2, 1*8(t1) + csrr t2, vl // Store vl + sd t2, 2*8(t1) + csrr t2, vcsr // Store vcsr + sd t2, 3*8(t1) + + /* Store vector registers v0-v31 */ + addi t2, t1, 4*8 + vsetvli t3, zero, e8, m8, ta, ma + vse8.v v0, 0(t2) // Store v0 ~ v7 + add t2, t2, t3 + vse8.v v8, 0(t2) // Store v8 ~ v15 + add t2, t2, t3 + vse8.v v16, 0(t2) // Store v16 ~ v23 + add t2, t2, t3 + vse8.v v24, 0(t2) // Store v24 ~ v31 + add t2, t2, t3 +#endif + /* Store standard preserved registers. */ sd x9, 11*8(t0) // Store s1 @@ -377,14 +543,14 @@ _tx_thread_preempt_restore: { */ la t0, _tx_timer_time_slice // Pickup time slice variable address - ld t2, 0(t0) // Pickup time slice + 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; */ - sd t2, 48(t1) // Save current time slice - sd x0, 0(t0) // Clear global time slice + sw t2, 36(t1) // Save current time slice + sw x0, 0(t0) // Clear global time slice /* } */ diff --git a/ports/risc-v64/gnu/src/tx_thread_context_save.S b/ports/risc-v64/gnu/src/tx_thread_context_save.S index df6f2c9a..7935bfee 100644 --- a/ports/risc-v64/gnu/src/tx_thread_context_save.S +++ b/ports/risc-v64/gnu/src/tx_thread_context_save.S @@ -94,7 +94,11 @@ _tx_thread_context_save: sd t4, 15*8(sp) // Store t4 sd t5, 14*8(sp) // Store t5 sd t6, 13*8(sp) // Store t6 +#ifdef TX_RISCV_SMODE + csrr t0, sepc // Load exception program counter +#else csrr t0, mepc // Load exception program counter +#endif sd t0, 30*8(sp) // Save it on the stack /* Save floating point scratch registers if floating point is enabled. */ @@ -146,6 +150,36 @@ _tx_thread_context_save: sd t0, 63*8(sp) // Store fcsr #endif +#if defined(__riscv_vector) + /* Store vector registers and CSRs */ +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + addi t1, sp, 64*8 +#else + addi t1, sp, 31*8 +#endif + /* Store vector CSRs */ + csrr t2, vstart // Store vstart + sd t2, 0*8(t1) + csrr t2, vtype // Store vtype + sd t2, 1*8(t1) + csrr t2, vl // Store vl + sd t2, 2*8(t1) + csrr t2, vcsr // Store vcsr + sd t2, 3*8(t1) + + /* Store vector registers v0-v31 */ + addi t2, t1, 4*8 + vsetvli t3, zero, e8, m8, ta, ma + vse8.v v0, 0(t2) // Store v0 ~ v7 + add t2, t2, t3 + vse8.v v8, 0(t2) // Store v8 ~ v15 + add t2, t2, t3 + vse8.v v16, 0(t2) // Store v16 ~ v23 + add t2, t2, t3 + vse8.v v24, 0(t2) // Store v24 ~ v31 + add t2, t2, t3 +#endif + #ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY call _tx_execution_isr_enter // Call the ISR execution enter function #endif @@ -184,7 +218,11 @@ _tx_thread_not_nested_save: sd t5, 14*8(sp) // Store t5 sd t6, 13*8(sp) // Store t6 +#ifdef TX_RISCV_SMODE + csrr t1, sepc // Load exception program counter +#else csrr t1, mepc // Load exception program counter +#endif sd t1, 30*8(sp) // Save it on the stack /* Save floating point scratch registers if floating point is enabled. */ @@ -236,6 +274,36 @@ _tx_thread_not_nested_save: sd t0, 63*8(sp) // Store fcsr #endif +#if defined(__riscv_vector) + /* Store vector registers and CSRs */ +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + addi t1, sp, 64*8 +#else + addi t1, sp, 31*8 +#endif + /* Store vector CSRs */ + csrr t2, vstart // Store vstart + sd t2, 0*8(t1) + csrr t2, vtype // Store vtype + sd t2, 1*8(t1) + csrr t2, vl // Store vl + sd t2, 2*8(t1) + csrr t2, vcsr // Store vcsr + sd t2, 3*8(t1) + + /* Store vector registers v0-v31 */ + addi t2, t1, 4*8 + vsetvli t3, zero, e8, m8, ta, ma + vse8.v v0, 0(t2) // Store v0 ~ v7 + add t2, t2, t3 + vse8.v v8, 0(t2) // Store v8 ~ v15 + add t2, t2, t3 + vse8.v v16, 0(t2) // Store v16 ~ v23 + add t2, t2, t3 + vse8.v v24, 0(t2) // Store v24 ~ v31 + add t2, t2, t3 +#endif + /* Save the current stack pointer in the thread's control block. */ /* _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; */ @@ -244,7 +312,7 @@ _tx_thread_not_nested_save: la t1, _tx_thread_current_ptr // Pickup current thread pointer address ld t1, 0(t1) // Pickup current thread pointer - sd sp, 16(t1) // Save stack pointer + sd sp, 8(t1) // Save stack pointer #ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY /* _tx_execution_isr_enter is called with thread stack pointer */ @@ -275,4 +343,19 @@ _tx_thread_idle_system_save: #else addi sp, sp, 32*8 // Recover the reserved stack space #endif + +#if defined(__riscv_vector) +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + addi t0, sp, -65*8 +#else + addi t0, sp, -32*8 +#endif + csrr t1, vlenb // Get vector register byte length + slli t1, t1, 5 // Multiply by 32 (number of vector registers) + addi t1, t1, 4*8 // Add vector CSR space: vstart, vtype, vl, vcsr + add sp, sp, t1 // Recover vector stack frame + + ld t1, 18*8(t0) // Recover t1 + ld t0, 19*8(t0) // Recover t0 +#endif ret // Return to calling ISR diff --git a/ports/risc-v64/gnu/src/tx_thread_interrupt_control.S b/ports/risc-v64/gnu/src/tx_thread_interrupt_control.S index dfcb6b47..b43d9558 100644 --- a/ports/risc-v64/gnu/src/tx_thread_interrupt_control.S +++ b/ports/risc-v64/gnu/src/tx_thread_interrupt_control.S @@ -59,20 +59,25 @@ .global _tx_thread_interrupt_control _tx_thread_interrupt_control: /* Pickup current interrupt lockout posture. */ - /* old_mstatus = mstatus; */ +#ifdef TX_RISCV_SMODE + csrr t0, sstatus + mv t1, t0 // Save original sstatus for return + li t2, ~0x02 // Build mask to clear SIE (bit 1) + and t0, t0, t2 // Clear SIE bit + andi a0, a0, 0x02 // Mask incoming to only SIE bit + or t0, t0, a0 // Set requested SIE state + csrw sstatus, t0 + andi a0, t1, 0x02 // Return original SIE bit +#else csrr t0, mstatus mv t1, t0 // Save original mstatus for return - - /* Apply the new interrupt posture while preserving unrelated mstatus bits. */ - /* Only modify the MIE bit (bit 3) */ - /* mstatus = (mstatus & ~MIE) | (new_posture & MIE); */ - li t2, ~0x08 // Build mask to clear MIE and t0, t0, t2 // Clear MIE bit - and a0, a0, 0x08 // Mask incoming to only MIE bit + andi a0, a0, 0x08 // Mask incoming to only MIE bit or t0, t0, a0 // Set requested MIE state csrw mstatus, t0 andi a0, t1, 0x08 // Return original MIE bit +#endif ret /* } */ diff --git a/ports/risc-v64/gnu/src/tx_thread_schedule.S b/ports/risc-v64/gnu/src/tx_thread_schedule.S index d3332896..860adf0b 100644 --- a/ports/risc-v64/gnu/src/tx_thread_schedule.S +++ b/ports/risc-v64/gnu/src/tx_thread_schedule.S @@ -62,7 +62,11 @@ _tx_thread_schedule: /* Enable interrupts. */ +#ifdef TX_RISCV_SMODE + csrsi sstatus, 0x02 // Enable interrupts (SIE bit 1) +#else csrsi mstatus, 0x08 // Enable interrupts (MIE bit 3) +#endif /* Wait for a thread to execute. */ /* do @@ -94,7 +98,11 @@ _tx_thread_schedule_loop: /* Yes! We have a thread to execute. Lockout interrupts and transfer control to it. */ +#ifdef TX_RISCV_SMODE + csrci sstatus, 0x02 // Lockout interrupts +#else csrci mstatus, 0x08 // Lockout interrupts +#endif /* Setup the current thread pointer. */ /* _tx_thread_current_ptr = _tx_thread_execute_ptr; */ @@ -105,10 +113,10 @@ _tx_thread_schedule_loop: /* Increment the run count for this thread. */ /* _tx_thread_current_ptr -> tx_thread_run_count++; */ - ld t2, 8(t1) // Pickup run count - ld t3, 48(t1) // Pickup time slice value + lw t2, 4(t1) // Pickup run count + lw t3, 36(t1) // Pickup time slice value addi t2, t2, 1 // Increment run count - sd t2, 8(t1) // Store new run count + sw t2, 4(t1) // Store new run count /* Setup time-slice, if present. */ /* _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; */ @@ -118,8 +126,8 @@ _tx_thread_schedule_loop: /* Switch to the thread's stack. */ /* SP = _tx_thread_execute_ptr -> tx_thread_stack_ptr; */ - ld sp, 16(t1) // Switch to thread's stack - sd t3, 0(t2) // Store new time-slice + ld sp, 8(t1) // Switch to thread's stack + sw t3, 0(t2) // Store new time-slice #ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY @@ -206,16 +214,71 @@ _tx_thread_schedule_loop: csrw fcsr, t0 // Restore fcsr #endif +#if defined(__riscv_vector) + /* Recover vector registers v0-v31 */ +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + addi t1, sp, 64*8 +#else + addi t1, sp, 31*8 +#endif + addi t2, t1, 4*8 + vsetvli t3, zero, e8, m8, ta, ma + vle8.v v0, (t2) // Recover v0 ~ v7 + add t2, t2, t3 + vle8.v v8, (t2) // Recover v8 ~ v15 + add t2, t2, t3 + vle8.v v16, (t2) // Recover v16 ~ v23 + add t2, t2, t3 + vle8.v v24, (t2) // Recover v24 ~ v31 + add t2, t2, t3 + + /* Recover vector CSRs */ + ld t2, 0*8(t1) + ld t3, 1*8(t1) + ld t4, 2*8(t1) + vsetvl zero, t4, t3 + csrw vstart, t2 + ld t4, 3*8(t1) + csrw vcsr, t4 +#endif + /* Recover standard registers. */ - ld t0, 30*8(sp) // Recover mepc + ld t0, 30*8(sp) // Recover mepc/sepc +#ifdef TX_RISCV_SMODE + csrw sepc, t0 // Store sepc + + /* Read/modify/write sstatus to preserve SUM, MXR, UXL, FS, etc. + Only touch SPP, SPIE, SIE — the bits SRET consumes. */ + csrr t0, sstatus + li t1, ~0x122 // Clear mask: SIE(1) | SPIE(5) | SPP(8) + and t0, t0, t1 + li t1, 0x120 // Set SPP=Supervisor(0x100) | SPIE(0x20) + or t0, t0, t1 +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + li t1, 0x6000 // Set FS=Dirty (bits 14:13) + or t0, t0, t1 +#endif + csrw sstatus, t0 // Update sstatus safely +#else csrw mepc, t0 // Store mepc - li t0, 0x1880 // Prepare mstatus: MPP=Machine(0x1800) | MPIE(0x80) + + /* Read/modify/write mstatus — same principle as S-mode path. */ + csrr t0, mstatus + li t1, ~0x1888 // Clear mask: MIE(3) | MPIE(7) | MPP(11:12) + and t0, t0, t1 + li t1, 0x1880 // Set MPP=Machine(0x1800) | MPIE(0x80) + or t0, t0, t1 #if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) - li t1, 0x2000 // Set FS bits for FP state + li t1, 0x6000 // Set FS=Dirty (bits 14:13) + or t0, t0, t1 +#endif +#if defined(__riscv_vector) + li t1, 0x0200 // Set VS bits (bits 10:9 to 01) for vector state or t0, t0, t1 #endif csrw mstatus, t0 // Set mstatus +#endif ld ra, 28*8(sp) // Recover return address ld t0, 19*8(sp) // Recover t0 @@ -251,7 +314,25 @@ _tx_thread_schedule_loop: #else addi sp, sp, 32*8 // Recover stack frame - without floating point registers #endif +#if defined(__riscv_vector) +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + addi t0, sp, -65*8 +#else + addi t0, sp, -32*8 +#endif + csrr t1, vlenb // Get vector register byte length + slli t1, t1, 5 // Multiply by 32 (number of vector registers) + addi t1, t1, 4*8 // Add vector CSR space: vstart, vtype, vl, vcsr + add sp, sp, t1 // Recover vector stack frame + + ld t1, 18*8(t0) // Recover t1 + ld t0, 19*8(t0) // Recover t0 +#endif +#ifdef TX_RISCV_SMODE + sret // Return to point of interrupt +#else mret // Return to point of interrupt +#endif _tx_thread_synch_return: @@ -287,6 +368,34 @@ _tx_thread_synch_return: csrw fcsr, t0 // #endif +#if defined(__riscv_vector) + /* Recover vector registers v0-v31 */ +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + addi t1, sp, 28*8 +#else + addi t1, sp, 15*8 +#endif + addi t2, t1, 4*8 + vsetvli t3, zero, e8, m8, ta, ma + vle8.v v0, (t2) // Recover v0 ~ v7 + add t2, t2, t3 + vle8.v v8, (t2) // Recover v8 ~ v15 + add t2, t2, t3 + vle8.v v16, (t2) // Recover v16 ~ v23 + add t2, t2, t3 + vle8.v v24, (t2) // Recover v24 ~ v31 + add t2, t2, t3 + + /* Recover vector CSRs */ + ld t2, 0*8(t1) + ld t3, 1*8(t1) + ld t4, 2*8(t1) + vsetvl zero, t4, t3 + csrw vstart, t2 + ld t4, 3*8(t1) + csrw vcsr, t4 +#endif + /* Recover standard preserved registers. */ /* Recover standard registers. */ @@ -303,13 +412,23 @@ _tx_thread_synch_return: ld s9, 3*8(sp) // Recover s9 ld s10, 2*8(sp) // Recover s10 ld s11, 1*8(sp) // Recover s11 - ld t0, 14*8(sp) // Recover mstatus + ld t0, 14*8(sp) // Recover status register +#ifdef TX_RISCV_SMODE + csrw sstatus, t0 // Store sstatus, enables interrupt +#else csrw mstatus, t0 // Store mstatus, enables interrupt +#endif #if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) addi sp, sp, 29*8 // Recover stack frame #else addi sp, sp, 16*8 // Recover stack frame #endif +#if defined(__riscv_vector) + csrr t1, vlenb // Get vector register byte length + slli t1, t1, 5 // Multiply by 32 (number of vector registers) + addi t1, t1, 4*8 // Add vector CSR space: vstart, vtype, vl, vcsr + add sp, sp, t1 // Recover vector stack frame +#endif ret // Return to thread /* } */ diff --git a/ports/risc-v64/gnu/src/tx_thread_stack_build.S b/ports/risc-v64/gnu/src/tx_thread_stack_build.S index 8663a6f1..86cab4f0 100644 --- a/ports/risc-v64/gnu/src/tx_thread_stack_build.S +++ b/ports/risc-v64/gnu/src/tx_thread_stack_build.S @@ -128,10 +128,19 @@ If floating point support: f30 61 Inital ft10 f31 62 Inital ft11 fscr 63 Inital fscr +If vector extension support: + vstart 64 Initial vstart + vtype 65 Initial vtype + vl 66 Initial vl + vcsr 67 Initial vcsr + v0 68 Initial v0 + v1 69 Initial v1 + ... + v31 99 Initial v31 Stack Bottom: (higher memory address) */ - ld t0, 32(a0) // Pickup end of stack area + ld t0, 24(a0) // Pickup end of stack area li t1, ~15 // Build 16-byte alignment mask and t0, t0, t1 // Make sure 16-byte alignment @@ -142,6 +151,17 @@ If floating point support: #else addi t0, t0, -32*8 // Allocate space for the stack frame #endif + +#if defined(__riscv_vector) + /* Vector extension support: calculate space based on vlenb */ + csrr t4, vlenb // Get vector register byte length + slli t4, t4, 5 // Multiply by 32 (number of vector registers) + addi t4, t4, 4*8 // Add vector CSR space: vstart, vtype, vl, vcsr + sub t0, t0, t4 // Allocate vector space for the stack frame +#else + li t4, 0 +#endif + li t1, 1 // Build stack type sd t1, 0*8(t0) // Place stack type on the top sd zero, 1*8(t0) // Initial s11 @@ -172,7 +192,7 @@ If floating point support: sd zero, 26*8(t0) // Initial a1 sd zero, 27*8(t0) // Initial a0 sd zero, 28*8(t0) // Initial ra - sd a1, 30*8(t0) // Initial mepc (thread entry point) + sd a1, 30*8(t0) // Initial mepc/sepc (thread entry point) #if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) sd zero, 31*8(t0) // Initial ft0 sd zero, 32*8(t0) // Initial ft1 @@ -208,14 +228,36 @@ If floating point support: sd zero, 62*8(t0) // Initial ft11 csrr a1, fcsr // Read fcsr for initial value sd a1, 63*8(t0) // Initial fcsr - sd zero, 64*8(t0) // Reserved word (0) +#endif + +#if defined(__riscv_vector) + /* Clear vector register space */ +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + addi t2, t0, 64*8 // t2 = start of vector registers +#else + addi t2, t0, 31*8 // t2 = start of vector registers +#endif + add t3, t2, t4 // t3 = end of vector registers + +vector_clear_loop: + beq t2, t3, vector_clear_done // Done if reached end + sd zero, 0(t2) // Clear 8 bytes + addi t2, t2, 8 // Move to next 8 bytes + j vector_clear_loop + +vector_clear_done: +#endif + + add t2, t0, t4 +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + sd zero, 64*8(t2) // Reserved word (0) #else - sd zero, 31*8(t0) // Reserved word (0) + sd zero, 31*8(t2) // Reserved word (0) #endif /* Setup stack pointer. */ /* thread_ptr -> tx_thread_stack_ptr = t0; */ - sd t0, 16(a0) // Save stack pointer in thread's + sd t0, 8(a0) // Save stack pointer in thread's ret // control block and return /* } */ diff --git a/ports/risc-v64/gnu/src/tx_thread_system_return.S b/ports/risc-v64/gnu/src/tx_thread_system_return.S index 4e954ebe..583b3f3e 100644 --- a/ports/risc-v64/gnu/src/tx_thread_system_return.S +++ b/ports/risc-v64/gnu/src/tx_thread_system_return.S @@ -68,6 +68,12 @@ _tx_thread_system_return: #else addi sp, sp, -16*8 // Allocate space on the stack - without floating point enabled #endif +#if defined(__riscv_vector) + csrr t1, vlenb // Get vector register byte length + slli t1, t1, 5 // Multiply by 32 (number of vector registers) + addi t1, t1, 4*8 // Add vector CSR space: vstart, vtype, vl, vcsr + sub sp, sp, t1 // Allocate vector space on the stack +#endif /* Store floating point preserved registers. */ #if defined(__riscv_float_abi_single) @@ -102,6 +108,36 @@ _tx_thread_system_return: sd t0, 27*8(sp) // Store fcsr #endif +#if defined(__riscv_vector) + /* Store vector registers and CSRs */ +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + addi t1, sp, 28*8 +#else + addi t1, sp, 15*8 +#endif + /* Store vector CSRs */ + csrr t2, vstart // Store vstart + sd t2, 0*8(t1) + csrr t2, vtype // Store vtype + sd t2, 1*8(t1) + csrr t2, vl // Store vl + sd t2, 2*8(t1) + csrr t2, vcsr // Store vcsr + sd t2, 3*8(t1) + + /* Store vector registers v0-v31 */ + addi t2, t1, 4*8 + vsetvli t3, zero, e8, m8, ta, ma + vse8.v v0, 0(t2) // Store v0 ~ v7 + add t2, t2, t3 + vse8.v v8, 0(t2) // Store v8 ~ v15 + add t2, t2, t3 + vse8.v v16, 0(t2) // Store v16 ~ v23 + add t2, t2, t3 + vse8.v v24, 0(t2) // Store v24 ~ v31 + add t2, t2, t3 +#endif + sd zero, 0(sp) // Solicited stack type sd ra, 13*8(sp) // Save return address sd s0, 12*8(sp) // Save s0 @@ -116,6 +152,15 @@ _tx_thread_system_return: sd s9, 3*8(sp) // Save s9 sd s10, 2*8(sp) // Save s10 sd s11, 1*8(sp) // Save s11 +#ifdef TX_RISCV_SMODE + csrr t0, sstatus // Pickup sstatus + sd t0, 14*8(sp) // Save sstatus + + + /* Lockout interrupts. will be enabled in _tx_thread_schedule */ + + csrci sstatus, 0x02 // Disable interrupts (SIE bit 1) +#else csrr t0, mstatus // Pickup mstatus sd t0, 14*8(sp) // Save mstatus @@ -123,6 +168,7 @@ _tx_thread_system_return: /* Lockout interrupts. will be enabled in _tx_thread_schedule */ csrci mstatus, 0x08 // Disable interrupts (MIE bit 3) +#endif #ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY @@ -137,15 +183,15 @@ _tx_thread_system_return: /* _tx_thread_current_ptr -> tx_thread_stack_ptr = SP; SP = _tx_thread_system_stack_ptr; */ - sd sp, 16(t1) // Save stack pointer - ld sp, 0(t2) // Switch to system stack + sd sp, 8(t1) // Save stack pointer + ld 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 - ld t3, 0(t4) // Pickup time slice value + 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 @@ -153,8 +199,8 @@ _tx_thread_system_return: /* _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; _tx_timer_time_slice = 0; */ - sd t3, 48(t1) // Save current time-slice for thread - sd zero, 0(t4) // Clear time-slice variable + sw t3, 36(t1) // Save current time-slice for thread + sw zero, 0(t4) // Clear time-slice variable /* } */ _tx_thread_dont_save_ts: diff --git a/ports/risc-v64/gnu/src/tx_timer_interrupt.S b/ports/risc-v64/gnu/src/tx_timer_interrupt.S index 02b70461..53e572f3 100644 --- a/ports/risc-v64/gnu/src/tx_timer_interrupt.S +++ b/ports/risc-v64/gnu/src/tx_timer_interrupt.S @@ -66,11 +66,11 @@ _tx_timer_interrupt: /* _tx_timer_system_clock++; */ la t0, _tx_timer_system_clock // Pickup address of system clock - ld t1, 0(t0) // Pickup system clock + lw t1, 0(t0) // Pickup system clock la t2, _tx_timer_time_slice // Pickup address of time slice - ld t3, 0(t2) // Pickup time slice + lw t3, 0(t2) // Pickup time slice addi t1, t1, 1 // Increment system clock - sd t1, 0(t0) // Store new system clock + sw t1, 0(t0) // Store new system clock li t6, 0 // Clear local expired flag /* Test for time-slice expiration. */ @@ -83,7 +83,7 @@ _tx_timer_interrupt: /* Decrement the time_slice. */ /* _tx_timer_time_slice--; */ - sd t3, 0(t2) // Store new time slice + sw t3, 0(t2) // Store new time slice /* Check for expiration. */ /* if (_tx_timer_time_slice == 0) */ |
