From 09f0707c93718de20d89d0655923183200c166bd Mon Sep 17 00:00:00 2001 From: Akif Ejaz Date: Tue, 23 Dec 2025 14:22:36 +0500 Subject: [PATCH] Add RISC-V32 arch. port layer This update adapts the ThreadX low-level kernel routines for RV32, including: - startup and initialization logic - context save/restore implementations - interrupt control and scheduler entry - thread stack build and system return paths - timer interrupt handling These changes provide full low-level support needed to run ThreadX on RISC-V32 targets. Signed-off-by: Akif Ejaz --- ports/risc-v32/gnu/CMakeLists.txt | 19 + ports/risc-v32/gnu/inc/tx_port.h | 297 ++++++++++++++ .../gnu/src/tx_initialize_low_level.S | 109 +++++ .../gnu/src/tx_thread_context_restore.S | 382 ++++++++++++++++++ .../risc-v32/gnu/src/tx_thread_context_save.S | 283 +++++++++++++ .../gnu/src/tx_thread_interrupt_control.S | 81 ++++ ports/risc-v32/gnu/src/tx_thread_schedule.S | 305 ++++++++++++++ .../risc-v32/gnu/src/tx_thread_stack_build.S | 228 +++++++++++ .../gnu/src/tx_thread_system_return.S | 174 ++++++++ ports/risc-v32/gnu/src/tx_timer_interrupt.c | 136 +++++++ 10 files changed, 2014 insertions(+) create mode 100644 ports/risc-v32/gnu/CMakeLists.txt create mode 100644 ports/risc-v32/gnu/inc/tx_port.h create mode 100644 ports/risc-v32/gnu/src/tx_initialize_low_level.S create mode 100644 ports/risc-v32/gnu/src/tx_thread_context_restore.S create mode 100644 ports/risc-v32/gnu/src/tx_thread_context_save.S create mode 100644 ports/risc-v32/gnu/src/tx_thread_interrupt_control.S create mode 100644 ports/risc-v32/gnu/src/tx_thread_schedule.S create mode 100644 ports/risc-v32/gnu/src/tx_thread_stack_build.S create mode 100644 ports/risc-v32/gnu/src/tx_thread_system_return.S create mode 100644 ports/risc-v32/gnu/src/tx_timer_interrupt.c diff --git a/ports/risc-v32/gnu/CMakeLists.txt b/ports/risc-v32/gnu/CMakeLists.txt new file mode 100644 index 000000000..b217065d2 --- /dev/null +++ b/ports/risc-v32/gnu/CMakeLists.txt @@ -0,0 +1,19 @@ + +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.c + # {{END_TARGET_SOURCES}} +) + +target_include_directories(${PROJECT_NAME} + PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/inc +) diff --git a/ports/risc-v32/gnu/inc/tx_port.h b/ports/risc-v32/gnu/inc/tx_port.h new file mode 100644 index 000000000..d3c68646f --- /dev/null +++ b/ports/risc-v32/gnu/inc/tx_port.h @@ -0,0 +1,297 @@ +/*************************************************************************** + * 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 + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h RISC-V32/GNU */ +/* 6.4.x */ +/* */ +/* AUTHOR */ +/* */ +/* Akif Ejaz, 10xEngineers */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 23-12-2025 Akif Ejaz Initial Version 6.4.x */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + +#ifdef __ASSEMBLER__ + + +#if __riscv_xlen == 64 +# define SLL32 sllw +# define STORE sd +# define LOAD ld +# define LWU lwu +# define LOG_REGBYTES 3 +#else +# define SLL32 sll +# define STORE sw +# define LOAD lw +# define LWU lw +# define LOG_REGBYTES 2 +#endif +#define REGBYTES (1 << LOG_REGBYTES) + +#else /*not __ASSEMBLER__ */ + +/* Include for memset. */ +#include + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef long LONG; +typedef unsigned long ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED +#define ALIGN_TYPE_DEFINED +#define ALIGN_TYPE ULONG64 + + + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 1024 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 1024 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX RISC-V port. */ + +#define TX_INT_DISABLE 0x00000000 /* Disable interrupts value */ +#define TX_INT_ENABLE 0x00000008 /* Enable interrupt value */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE ++_tx_trace_simulated_time +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS 0 + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#define TX_INLINE_INITIALIZATION + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 +#define TX_THREAD_EXTENSION_3 + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#ifdef TX_DISABLE_INLINE + +ULONG64 _tx_thread_interrupt_control(unsigned int new_posture); + +#define TX_INTERRUPT_SAVE_AREA register ULONG64 interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_interrupt_control(TX_INT_DISABLE); +#define TX_RESTORE _tx_thread_interrupt_control(interrupt_save); + +#else + +#define TX_INTERRUPT_SAVE_AREA ULONG64 interrupt_save; +/* Atomically read mstatus into interrupt_save and clear bit 3 of mstatus. */ +#define TX_DISABLE {__asm__ ("csrrci %0, mstatus, 0x08" : "=r" (interrupt_save) : );}; +/* We only care about mstatus.mie (bit 3), so mask interrupt_save and write to mstatus. */ +#define TX_RESTORE {register ULONG64 __tempmask = interrupt_save & 0x08; \ + __asm__ ("csrrs x0, mstatus, %0 \n\t" : : "r" (__tempmask) : );}; + +#endif + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) 2024 Microsoft Corporation. * ThreadX RISC-V32/GNU Version 6.4.2 *"; +#else +extern CHAR _tx_version_id[]; +#endif + +#endif /*not __ASSEMBLER__ */ +#endif diff --git a/ports/risc-v32/gnu/src/tx_initialize_low_level.S b/ports/risc-v32/gnu/src/tx_initialize_low_level.S new file mode 100644 index 000000000..2c2de153e --- /dev/null +++ b/ports/risc-v32/gnu/src/tx_initialize_low_level.S @@ -0,0 +1,109 @@ +/*************************************************************************** + * 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 + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .section .data + .global __tx_free_memory_start +__tx_free_memory_start: + + + .section .text +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level RISC-V32/GNU */ +/* 6.4.x */ +/* AUTHOR */ +/* */ +/* Akif Ejaz, 10xEngineers */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for any low-level processor */ +/* initialization, including setting up interrupt vectors, setting */ +/* up a periodic timer interrupt source, saving the system stack */ +/* pointer for use in ISR processing later, and finding the first */ +/* available RAM memory address for tx_application_define. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 23-12-2025 Akif Ejaz Initial Version 6.4.x */ +/* */ +/**************************************************************************/ +/* VOID _tx_initialize_low_level(VOID) +{ */ + .global _tx_initialize_low_level + .weak _tx_initialize_low_level +_tx_initialize_low_level: + la t0, _tx_thread_system_stack_ptr // Pickup address of system stack ptr + sw sp, 0(t0) // Save system stack pointer + + la t0, __tx_free_memory_start // Pickup first free address + la t1, _tx_initialize_unused_memory // Pickup address of unused memory + sw t0, 0(t1) // Save unused memory address + +#ifdef __riscv_flen + fscsr x0 +#endif + + ret + + /* Define the actual timer interrupt/exception handler. */ + + .global timer1_plic_IRQHandler +timer1_plic_IRQHandler: + + /* Before calling _tx_thread_context_save, we have to allocate an interrupt + stack frame and save the current value of x1 (ra). */ + +//#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) +// addi sp, sp, -520 // Allocate space for all registers - with floating point enabled +//#else +// addi sp, sp, -256 // Allocate space for all registers - without floating point enabled +//#endif +// sd x1, 224(sp) // Store RA +// call _tx_thread_context_save // Call ThreadX context save + + /* Call the ThreadX timer routine. */ + call _tx_timer_interrupt // Call timer interrupt handler + call timer1_interrupt + ret + + /* Timer interrupt processing is done, jump to ThreadX context restore. */ +// j _tx_thread_context_restore // Jump to ThreadX context restore function. Note: this does not return! diff --git a/ports/risc-v32/gnu/src/tx_thread_context_restore.S b/ports/risc-v32/gnu/src/tx_thread_context_restore.S new file mode 100644 index 000000000..d7219210a --- /dev/null +++ b/ports/risc-v32/gnu/src/tx_thread_context_restore.S @@ -0,0 +1,382 @@ +/*************************************************************************** + * 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 + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#include "tx_port.h" + + .section .text +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore RISC-V32/GNU */ +/* 6.4.x */ +/* AUTHOR */ +/* */ +/* Akif Ejaz, 10xEngineers */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 23-12-2025 Akif Ejaz Initial Version 6.4.x */ +/* */ +/**************************************************************************/ +/* VOID _tx_thread_context_restore(VOID) +{ */ + .global _tx_thread_context_restore +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + csrci mstatus, 0x08 // Disable interrupts + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + call _tx_execution_isr_exit // Call the ISR execution exit function +#endif + + /* Determine if interrupts are nested. */ + /* if (--_tx_thread_system_state) + { */ + + la t0, _tx_thread_system_state // Pickup addr of nested interrupt count + LOAD t1, 0(t0) // Pickup nested interrupt count + addi t1, t1, -1 // Decrement the nested interrupt counter + STORE t1, 0(t0) // Store new nested count + beqz t1, _tx_thread_not_nested_restore // If 0, not nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + /* Recover floating point registers. */ +#if defined(__riscv_float_abi_single) + flw f0, 31*REGBYTES(sp) // Recover ft0 + flw f1, 32*REGBYTES(sp) // Recover ft1 + flw f2, 33*REGBYTES(sp) // Recover ft2 + flw f3, 34*REGBYTES(sp) // Recover ft3 + flw f4, 35*REGBYTES(sp) // Recover ft4 + flw f5, 36*REGBYTES(sp) // Recover ft5 + flw f6, 37*REGBYTES(sp) // Recover ft6 + flw f7, 38*REGBYTES(sp) // Recover ft7 + flw f10,41*REGBYTES(sp) // Recover fa0 + flw f11,42*REGBYTES(sp) // Recover fa1 + flw f12,43*REGBYTES(sp) // Recover fa2 + flw f13,44*REGBYTES(sp) // Recover fa3 + flw f14,45*REGBYTES(sp) // Recover fa4 + flw f15,46*REGBYTES(sp) // Recover fa5 + flw f16,47*REGBYTES(sp) // Recover fa6 + flw f17,48*REGBYTES(sp) // Recover fa7 + flw f28,59*REGBYTES(sp) // Recover ft8 + flw f29,60*REGBYTES(sp) // Recover ft9 + flw f30,61*REGBYTES(sp) // Recover ft10 + flw f31,62*REGBYTES(sp) // Recover ft11 + lw t0, 63*REGBYTES(sp) // Recover fcsr + csrw fcsr, t0 // +#elif defined(__riscv_float_abi_double) + fld f0, 31*REGBYTES(sp) // Recover ft0 + fld f1, 32*REGBYTES(sp) // Recover ft1 + fld f2, 33*REGBYTES(sp) // Recover ft2 + fld f3, 34*REGBYTES(sp) // Recover ft3 + fld f4, 35*REGBYTES(sp) // Recover ft4 + fld f5, 36*REGBYTES(sp) // Recover ft5 + fld f6, 37*REGBYTES(sp) // Recover ft6 + fld f7, 38*REGBYTES(sp) // Recover ft7 + fld f10,41*REGBYTES(sp) // Recover fa0 + fld f11,42*REGBYTES(sp) // Recover fa1 + fld f12,43*REGBYTES(sp) // Recover fa2 + fld f13,44*REGBYTES(sp) // Recover fa3 + fld f14,45*REGBYTES(sp) // Recover fa4 + fld f15,46*REGBYTES(sp) // Recover fa5 + fld f16,47*REGBYTES(sp) // Recover fa6 + fld f17,48*REGBYTES(sp) // Recover fa7 + fld f28,59*REGBYTES(sp) // Recover ft8 + fld f29,60*REGBYTES(sp) // Recover ft9 + fld f30,61*REGBYTES(sp) // Recover ft10 + fld f31,62*REGBYTES(sp) // Recover ft11 + LOAD t0, 63*REGBYTES(sp) // Recover fcsr + csrw fcsr, t0 // +#endif + + /* Recover standard registers. */ + + /* Restore registers, + Skip global pointer because that does not change. + Also skip the saved registers since they have been restored by any function we called, + except s0 since we use it ourselves. */ + + LOAD t0, 30*REGBYTES(sp) // Recover mepc + csrw mepc, t0 // Setup mepc + li t0, 0x1880 // Prepare MPIP +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + li t1, 1<<13 + or t0, t1, t0 +#endif + csrw mstatus, t0 // Enable MPIP + + LOAD x1, 28*REGBYTES(sp) // Recover RA + LOAD x5, 19*REGBYTES(sp) // Recover t0 + LOAD x6, 18*REGBYTES(sp) // Recover t1 + LOAD x7, 17*REGBYTES(sp) // Recover t2 + LOAD x8, 12*REGBYTES(sp) // Recover s0 + LOAD x10, 27*REGBYTES(sp) // Recover a0 + LOAD x11, 26*REGBYTES(sp) // Recover a1 + LOAD x12, 25*REGBYTES(sp) // Recover a2 + LOAD x13, 24*REGBYTES(sp) // Recover a3 + LOAD x14, 23*REGBYTES(sp) // Recover a4 + LOAD x15, 22*REGBYTES(sp) // Recover a5 + LOAD x16, 21*REGBYTES(sp) // Recover a6 + LOAD x17, 20*REGBYTES(sp) // Recover a7 + LOAD x28, 16*REGBYTES(sp) // Recover t3 + LOAD x29, 15*REGBYTES(sp) // Recover t4 + LOAD x30, 14*REGBYTES(sp) // Recover t5 + LOAD x31, 13*REGBYTES(sp) // Recover t6 + +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + addi sp, sp, 65*REGBYTES // Recover stack frame - with floating point enabled +#else + addi sp, sp, 32*REGBYTES // Recover stack frame - without floating point enabled +#endif + mret // Return to point of interrupt + + /* } */ +_tx_thread_not_nested_restore: + /* Determine if a thread was interrupted and no preemption is required. */ + /* else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + || (_tx_thread_preempt_disable)) + { */ + + LOAD t1, _tx_thread_current_ptr // Pickup current thread pointer + beqz t1, _tx_thread_idle_system_restore // If NULL, idle system restore + + LOAD t2, _tx_thread_preempt_disable // Pickup preempt disable flag + bgtz t2, _tx_thread_no_preempt_restore // If set, restore interrupted thread + + LOAD t2, _tx_thread_execute_ptr // Pickup thread execute pointer + bne t1, t2, _tx_thread_preempt_restore // If higher-priority thread is ready, preempt + + +_tx_thread_no_preempt_restore: + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + /* SP = _tx_thread_current_ptr -> tx_thread_stack_ptr; */ + + LOAD sp, 2*REGBYTES(t1) // Switch back to thread's stack + + /* Recover floating point registers. */ +#if defined(__riscv_float_abi_single) + flw f0, 31*REGBYTES(sp) // Recover ft0 + flw f1, 32*REGBYTES(sp) // Recover ft1 + flw f2, 33*REGBYTES(sp) // Recover ft2 + flw f3, 34*REGBYTES(sp) // Recover ft3 + flw f4, 35*REGBYTES(sp) // Recover ft4 + flw f5, 36*REGBYTES(sp) // Recover ft5 + flw f6, 37*REGBYTES(sp) // Recover ft6 + flw f7, 38*REGBYTES(sp) // Recover ft7 + flw f10,41*REGBYTES(sp) // Recover fa0 + flw f11,42*REGBYTES(sp) // Recover fa1 + flw f12,43*REGBYTES(sp) // Recover fa2 + flw f13,44*REGBYTES(sp) // Recover fa3 + flw f14,45*REGBYTES(sp) // Recover fa4 + flw f15,46*REGBYTES(sp) // Recover fa5 + flw f16,47*REGBYTES(sp) // Recover fa6 + flw f17,48*REGBYTES(sp) // Recover fa7 + flw f28,59*REGBYTES(sp) // Recover ft8 + flw f29,60*REGBYTES(sp) // Recover ft9 + flw f30,61*REGBYTES(sp) // Recover ft10 + flw f31,62*REGBYTES(sp) // Recover ft11 + lw t0, 63*REGBYTES(sp) // Recover fcsr + csrw fcsr, t0 // +#elif defined(__riscv_float_abi_double) + fld f0, 31*REGBYTES(sp) // Recover ft0 + fld f1, 32*REGBYTES(sp) // Recover ft1 + fld f2, 33*REGBYTES(sp) // Recover ft2 + fld f3, 34*REGBYTES(sp) // Recover ft3 + fld f4, 35*REGBYTES(sp) // Recover ft4 + fld f5, 36*REGBYTES(sp) // Recover ft5 + fld f6, 37*REGBYTES(sp) // Recover ft6 + fld f7, 38*REGBYTES(sp) // Recover ft7 + fld f10,41*REGBYTES(sp) // Recover fa0 + fld f11,42*REGBYTES(sp) // Recover fa1 + fld f12,43*REGBYTES(sp) // Recover fa2 + fld f13,44*REGBYTES(sp) // Recover fa3 + fld f14,45*REGBYTES(sp) // Recover fa4 + fld f15,46*REGBYTES(sp) // Recover fa5 + fld f16,47*REGBYTES(sp) // Recover fa6 + fld f17,48*REGBYTES(sp) // Recover fa7 + fld f28,59*REGBYTES(sp) // Recover ft8 + fld f29,60*REGBYTES(sp) // Recover ft9 + fld f30,61*REGBYTES(sp) // Recover ft10 + fld f31,62*REGBYTES(sp) // Recover ft11 + LOAD t0, 63*REGBYTES(sp) // Recover fcsr + csrw fcsr, t0 // +#endif + + /* Recover the saved context and return to the point of interrupt. */ + + /* Recover standard registers. */ + /* Restore registers, + Skip global pointer because that does not change */ + + LOAD t0, 30*REGBYTES(sp) // Recover mepc + csrw mepc, t0 // Setup mepc + li t0, 0x1880 // Prepare MPIP +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + li t1, 1<<13 + or t0, t1, t0 +#endif + csrw mstatus, t0 // Enable MPIP + + LOAD x1, 28*REGBYTES(sp) // Recover RA + LOAD x5, 19*REGBYTES(sp) // Recover t0 + LOAD x6, 18*REGBYTES(sp) // Recover t1 + LOAD x7, 17*REGBYTES(sp) // Recover t2 + LOAD x8, 12*REGBYTES(sp) // Recover s0 + LOAD x10, 27*REGBYTES(sp) // Recover a0 + LOAD x11, 26*REGBYTES(sp) // Recover a1 + LOAD x12, 25*REGBYTES(sp) // Recover a2 + LOAD x13, 24*REGBYTES(sp) // Recover a3 + LOAD x14, 23*REGBYTES(sp) // Recover a4 + LOAD x15, 22*REGBYTES(sp) // Recover a5 + LOAD x16, 21*REGBYTES(sp) // Recover a6 + LOAD x17, 20*REGBYTES(sp) // Recover a7 + LOAD x28, 16*REGBYTES(sp) // Recover t3 + LOAD x29, 15*REGBYTES(sp) // Recover t4 + LOAD x30, 14*REGBYTES(sp) // Recover t5 + LOAD x31, 13*REGBYTES(sp) // Recover t6 + +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + addi sp, sp, 65*REGBYTES // Recover stack frame - with floating point enabled +#else + addi sp, sp, 32*REGBYTES // Recover stack frame - without floating point enabled +#endif + mret // Return to point of interrupt + + /* } + else + { */ +_tx_thread_preempt_restore: + /* Instead of directly activating the thread again, ensure we save the + entire stack frame by saving the remaining registers. */ + + LOAD t0, 2*REGBYTES(t1) // Pickup thread's stack pointer + ori t3, x0, 1 // Build interrupt stack type + STORE t3, 0(t0) // Store stack type + + /* Store floating point preserved registers. */ +#ifdef __riscv_float_abi_single + fsw f8, 39*REGBYTES(t0) // Store fs0 + fsw f9, 40*REGBYTES(t0) // Store fs1 + fsw f18, 49*REGBYTES(t0) // Store fs2 + fsw f19, 50*REGBYTES(t0) // Store fs3 + fsw f20, 51*REGBYTES(t0) // Store fs4 + fsw f21, 52*REGBYTES(t0) // Store fs5 + fsw f22, 53*REGBYTES(t0) // Store fs6 + fsw f23, 54*REGBYTES(t0) // Store fs7 + fsw f24, 55*REGBYTES(t0) // Store fs8 + fsw f25, 56*REGBYTES(t0) // Store fs9 + fsw f26, 57*REGBYTES(t0) // Store fs10 + fsw f27, 58*REGBYTES(t0) // Store fs11 +#elif defined(__riscv_float_abi_double) + fsd f8, 39*REGBYTES(t0) // Store fs0 + fsd f9, 40*REGBYTES(t0) // Store fs1 + fsd f18, 49*REGBYTES(t0) // Store fs2 + fsd f19, 50*REGBYTES(t0) // Store fs3 + fsd f20, 51*REGBYTES(t0) // Store fs4 + fsd f21, 52*REGBYTES(t0) // Store fs5 + fsd f22, 53*REGBYTES(t0) // Store fs6 + fsd f23, 54*REGBYTES(t0) // Store fs7 + fsd f24, 55*REGBYTES(t0) // Store fs8 + fsd f25, 56*REGBYTES(t0) // Store fs9 + fsd f26, 57*REGBYTES(t0) // Store fs10 + fsd f27, 58*REGBYTES(t0) // Store fs11 +#endif + + /* Store standard preserved registers. */ + + STORE x9, 11*REGBYTES(t0) // Store s1 + STORE x18, 10*REGBYTES(t0) // Store s2 + STORE x19, 9*REGBYTES(t0) // Store s3 + STORE x20, 8*REGBYTES(t0) // Store s4 + STORE x21, 7*REGBYTES(t0) // Store s5 + STORE x22, 6*REGBYTES(t0) // Store s6 + STORE x23, 5*REGBYTES(t0) // Store s7 + STORE x24, 4*REGBYTES(t0) // Store s8 + STORE x25, 3*REGBYTES(t0) // Store s9 + STORE x26, 2*REGBYTES(t0) // Store s10 + STORE x27, 1*REGBYTES(t0) // Store s11 + // Note: s0 is already stored! + + /* Save the remaining time-slice and disable it. */ + /* if (_tx_timer_time_slice) + { */ + + la t0, _tx_timer_time_slice // Pickup time slice variable address + LOAD t2, 0(t0) // Pickup time slice + beqz t2, _tx_thread_dont_save_ts // If 0, skip time slice processing + + /* _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice + _tx_timer_time_slice = 0; */ + + STORE t2, 6*REGBYTES(t1) // Save current time slice + STORE x0, 0(t0) // Clear global time slice + + + /* } */ +_tx_thread_dont_save_ts: + /* Clear the current task pointer. */ + /* _tx_thread_current_ptr = TX_NULL; */ + + /* Return to the scheduler. */ + /* _tx_thread_schedule(); */ + + STORE x0, _tx_thread_current_ptr, t0 // Clear current thread pointer*/ + /* } */ + +_tx_thread_idle_system_restore: + /* Just return back to the scheduler! */ + j _tx_thread_schedule // Return to scheduler + +/* } */ diff --git a/ports/risc-v32/gnu/src/tx_thread_context_save.S b/ports/risc-v32/gnu/src/tx_thread_context_save.S new file mode 100644 index 000000000..4216007dd --- /dev/null +++ b/ports/risc-v32/gnu/src/tx_thread_context_save.S @@ -0,0 +1,283 @@ +/*************************************************************************** + * 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 + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#include "tx_port.h" + + .section .text +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save RISC-V32/GNU */ +/* 6.4.x */ +/* AUTHOR */ +/* */ +/* Akif Ejaz, 10xEngineers */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 23-12-2025 Akif Ejaz Initial Version 6.4.x */ +/* */ +/**************************************************************************/ +/* VOID _tx_thread_context_save(VOID) +{ */ + .global _tx_thread_context_save +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that interrupts are locked + out and the interrupt stack fame has been allocated and x1 (ra) has + been saved on the stack. */ + + STORE x5, 19*REGBYTES(sp) // First store t0 and t1 + STORE x6, 18*REGBYTES(sp) + + la x5, _tx_thread_system_state // Pickup address of system state + LOAD x6, 0(x5) // Pickup system state + + /* Check for a nested interrupt condition. */ + /* if (_tx_thread_system_state++) + { */ + beqz x6, _tx_thread_not_nested_save // If 0, first interrupt condition + addi x6, x6, 1 // Increment the interrupt counter + STORE x6, 0(x5) // Store the interrupt counter + + /* Nested interrupt condition. + Save the reset of the scratch registers on the stack and return to the + calling ISR. */ + + STORE x7, 17*REGBYTES(sp) // Store t2 + STORE x8, 12*REGBYTES(sp) // Store s0 + STORE x10, 27*REGBYTES(sp) // Store a0 + STORE x11, 26*REGBYTES(sp) // Store a1 + STORE x12, 25*REGBYTES(sp) // Store a2 + STORE x13, 24*REGBYTES(sp) // Store a3 + STORE x14, 23*REGBYTES(sp) // Store a4 + STORE x15, 22*REGBYTES(sp) // Store a5 + STORE x16, 21*REGBYTES(sp) // Store a6 + STORE x17, 20*REGBYTES(sp) // Store a7 + STORE x28, 16*REGBYTES(sp) // Store t3 + STORE x29, 15*REGBYTES(sp) // Store t4 + STORE x30, 14*REGBYTES(sp) // Store t5 + STORE x31, 13*REGBYTES(sp) // Store t6 + csrr t0, mepc // Load exception program counter + STORE t0, 30*REGBYTES(sp) // Save it on the stack + + /* Save floating point scratch registers. */ +#if defined(__riscv_float_abi_single) + fsw f0, 31*REGBYTES(sp) // Store ft0 + fsw f1, 32*REGBYTES(sp) // Store ft1 + fsw f2, 33*REGBYTES(sp) // Store ft2 + fsw f3, 34*REGBYTES(sp) // Store ft3 + fsw f4, 35*REGBYTES(sp) // Store ft4 + fsw f5, 36*REGBYTES(sp) // Store ft5 + fsw f6, 37*REGBYTES(sp) // Store ft6 + fsw f7, 38*REGBYTES(sp) // Store ft7 + fsw f10,41*REGBYTES(sp) // Store fa0 + fsw f11,42*REGBYTES(sp) // Store fa1 + fsw f12,43*REGBYTES(sp) // Store fa2 + fsw f13,44*REGBYTES(sp) // Store fa3 + fsw f14,45*REGBYTES(sp) // Store fa4 + fsw f15,46*REGBYTES(sp) // Store fa5 + fsw f16,47*REGBYTES(sp) // Store fa6 + fsw f17,48*REGBYTES(sp) // Store fa7 + fsw f28,59*REGBYTES(sp) // Store ft8 + fsw f29,60*REGBYTES(sp) // Store ft9 + fsw f30,61*REGBYTES(sp) // Store ft10 + fsw f31,62*REGBYTES(sp) // Store ft11 + csrr t0, fcsr + STORE t0, 63*REGBYTES(sp) // Store fcsr +#elif defined(__riscv_float_abi_double) + fsd f0, 31*REGBYTES(sp) // Store ft0 + fsd f1, 32*REGBYTES(sp) // Store ft1 + fsd f2, 33*REGBYTES(sp) // Store ft2 + fsd f3, 34*REGBYTES(sp) // Store ft3 + fsd f4, 35*REGBYTES(sp) // Store ft4 + fsd f5, 36*REGBYTES(sp) // Store ft5 + fsd f6, 37*REGBYTES(sp) // Store ft6 + fsd f7, 38*REGBYTES(sp) // Store ft7 + fsd f10,41*REGBYTES(sp) // Store fa0 + fsd f11,42*REGBYTES(sp) // Store fa1 + fsd f12,43*REGBYTES(sp) // Store fa2 + fsd f13,44*REGBYTES(sp) // Store fa3 + fsd f14,45*REGBYTES(sp) // Store fa4 + fsd f15,46*REGBYTES(sp) // Store fa5 + fsd f16,47*REGBYTES(sp) // Store fa6 + fsd f17,48*REGBYTES(sp) // Store fa7 + fsd f28,59*REGBYTES(sp) // Store ft8 + fsd f29,60*REGBYTES(sp) // Store ft9 + fsd f30,61*REGBYTES(sp) // Store ft10 + fsd f31,62*REGBYTES(sp) // Store ft11 + csrr t0, fcsr + STORE t0, 63*REGBYTES(sp) // Store fcsr +#endif + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + call _tx_execution_isr_enter // Call the ISR execution enter function +#endif + + ret // Return to calling ISR + +_tx_thread_not_nested_save: + /* } */ + + /* Otherwise, not nested, check to see if a thread was running. */ + /* else if (_tx_thread_current_ptr) + { */ + addi x6, x6, 1 // Increment the interrupt counter + STORE x6, 0(x5) // Store the interrupt counter + + /* Not nested: Find the user thread that was running and load our SP */ + + LOAD x5, _tx_thread_current_ptr // Pickup current thread pointer + beqz x5, _tx_thread_idle_system_save // If NULL, idle system was interrupted + + /* Save the standard scratch registers. */ + + STORE x7, 17*REGBYTES(sp) // Store t2 + STORE x8, 12*REGBYTES(sp) // Store s0 + STORE x10, 27*REGBYTES(sp) // Store a0 + STORE x11, 26*REGBYTES(sp) // Store a1 + STORE x12, 25*REGBYTES(sp) // Store a2 + STORE x13, 24*REGBYTES(sp) // Store a3 + STORE x14, 23*REGBYTES(sp) // Store a4 + STORE x15, 22*REGBYTES(sp) // Store a5 + STORE x16, 21*REGBYTES(sp) // Store a6 + STORE x17, 20*REGBYTES(sp) // Store a7 + STORE x28, 16*REGBYTES(sp) // Store t3 + STORE x29, 15*REGBYTES(sp) // Store t4 + STORE x30, 14*REGBYTES(sp) // Store t5 + STORE x31, 13*REGBYTES(sp) // Store t6 + + csrr t0, mepc // Load exception program counter + STORE t0, 30*REGBYTES(sp) // Save it on the stack + + /* Save floating point scratch registers. */ +#if defined(__riscv_float_abi_single) + fsw f0, 31*REGBYTES(sp) // Store ft0 + fsw f1, 32*REGBYTES(sp) // Store ft1 + fsw f2, 33*REGBYTES(sp) // Store ft2 + fsw f3, 34*REGBYTES(sp) // Store ft3 + fsw f4, 35*REGBYTES(sp) // Store ft4 + fsw f5, 36*REGBYTES(sp) // Store ft5 + fsw f6, 37*REGBYTES(sp) // Store ft6 + fsw f7, 38*REGBYTES(sp) // Store ft7 + fsw f10,41*REGBYTES(sp) // Store fa0 + fsw f11,42*REGBYTES(sp) // Store fa1 + fsw f12,43*REGBYTES(sp) // Store fa2 + fsw f13,44*REGBYTES(sp) // Store fa3 + fsw f14,45*REGBYTES(sp) // Store fa4 + fsw f15,46*REGBYTES(sp) // Store fa5 + fsw f16,47*REGBYTES(sp) // Store fa6 + fsw f17,48*REGBYTES(sp) // Store fa7 + fsw f28,59*REGBYTES(sp) // Store ft8 + fsw f29,60*REGBYTES(sp) // Store ft9 + fsw f30,61*REGBYTES(sp) // Store ft10 + fsw f31,62*REGBYTES(sp) // Store ft11 + csrr t0, fcsr + STORE t0, 63*REGBYTES(sp) // Store fcsr +#elif defined(__riscv_float_abi_double) + fsd f0, 31*REGBYTES(sp) // Store ft0 + fsd f1, 32*REGBYTES(sp) // Store ft1 + fsd f2, 33*REGBYTES(sp) // Store ft2 + fsd f3, 34*REGBYTES(sp) // Store ft3 + fsd f4, 35*REGBYTES(sp) // Store ft4 + fsd f5, 36*REGBYTES(sp) // Store ft5 + fsd f6, 37*REGBYTES(sp) // Store ft6 + fsd f7, 38*REGBYTES(sp) // Store ft7 + fsd f10,41*REGBYTES(sp) // Store fa0 + fsd f11,42*REGBYTES(sp) // Store fa1 + fsd f12,43*REGBYTES(sp) // Store fa2 + fsd f13,44*REGBYTES(sp) // Store fa3 + fsd f14,45*REGBYTES(sp) // Store fa4 + fsd f15,46*REGBYTES(sp) // Store fa5 + fsd f16,47*REGBYTES(sp) // Store fa6 + fsd f17,48*REGBYTES(sp) // Store fa7 + fsd f28,59*REGBYTES(sp) // Store ft8 + fsd f29,60*REGBYTES(sp) // Store ft9 + fsd f30,61*REGBYTES(sp) // Store ft10 + fsd f31,62*REGBYTES(sp) // Store ft11 + csrr t0, fcsr + STORE t0, 63*REGBYTES(sp) // Store fcsr +#endif + + /* Save the current stack pointer in the thread's control block. */ + /* _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; */ + + /* Switch to the system stack. */ + /* sp = _tx_thread_system_stack_ptr; */ + + LOAD t1, _tx_thread_current_ptr // Pickup current thread pointer + STORE sp, 2*REGBYTES(t1) // Save stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + /* _tx_execution_isr_enter is called with thread stack pointer */ + call _tx_execution_isr_enter // Call the ISR execution enter function +#endif + + + LOAD sp, _tx_thread_system_stack_ptr // Switch to system stack + ret // Return to calling ISR + + /* } + else + { */ + +_tx_thread_idle_system_save: + + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + call _tx_execution_isr_enter // Call the ISR execution enter function +#endif + + /* Interrupt occurred in the scheduling loop. */ + + /* } +} */ +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + addi sp, sp, 65*REGBYTES // Recover stack frame - with floating point enabled +#else + addi sp, sp, 32*REGBYTES // Recover the reserved stack space +#endif + ret // Return to calling ISR diff --git a/ports/risc-v32/gnu/src/tx_thread_interrupt_control.S b/ports/risc-v32/gnu/src/tx_thread_interrupt_control.S new file mode 100644 index 000000000..ae6f87c5d --- /dev/null +++ b/ports/risc-v32/gnu/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/*************************************************************************** + * 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 + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + RETURN_MASK = 0x0000000F + SET_SR_MASK = 0xFFFFFFF0 + + .section .text +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control RISC-V32/GNU */ +/* 6.4.x */ +/* AUTHOR */ +/* */ +/* Akif Ejaz, 10xEngineers */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 23-12-2025 Akif Ejaz Initial Version 6.4.x */ +/* */ +/**************************************************************************/ +/* UINT _tx_thread_interrupt_control(UINT new_posture) +{ */ + .global _tx_thread_interrupt_control +_tx_thread_interrupt_control: + /* Pickup current interrupt lockout posture. */ + + csrr t0, mstatus + mv t1, t0 // Save original mstatus for return + + /* Apply the new interrupt posture. */ + + li t2, SET_SR_MASK // Build set SR mask + and t0, t0, t2 // Isolate interrupt lockout bits + or t0, t0, a0 // Put new lockout bits in + csrw mstatus, t0 + andi a0, t1, RETURN_MASK // Return original mstatus. + ret +/* } */ diff --git a/ports/risc-v32/gnu/src/tx_thread_schedule.S b/ports/risc-v32/gnu/src/tx_thread_schedule.S new file mode 100644 index 000000000..32bd941f1 --- /dev/null +++ b/ports/risc-v32/gnu/src/tx_thread_schedule.S @@ -0,0 +1,305 @@ +/*************************************************************************** + * 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 + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#include "tx_port.h" + + .section .text +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule RISC-V32/GNU */ +/* 6.4.x */ +/* AUTHOR */ +/* */ +/* Akif Ejaz, 10xEngineers */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 23-12-2025 Akif Ejaz Initial Version 6.4.x */ +/* */ +/**************************************************************************/ +/* VOID _tx_thread_schedule(VOID) +{ */ + .global _tx_thread_schedule +_tx_thread_schedule: + + /* Enable interrupts. */ + csrsi mstatus, 0x08 // Enable interrupts + + /* Wait for a thread to execute. */ + /* do + { */ + + la t0, _tx_thread_execute_ptr // Pickup address of execute ptr +_tx_thread_schedule_loop: + LOAD t1, 0(t0) // Pickup next thread to execute + beqz t1, _tx_thread_schedule_loop // If NULL, wait for thread to execute + + /* } + while(_tx_thread_execute_ptr == TX_NULL); */ + + /* Yes! We have a thread to execute. Lockout interrupts and + transfer control to it. */ + csrci mstatus, 0x08 // Lockout interrupts + + /* Setup the current thread pointer. */ + /* _tx_thread_current_ptr = _tx_thread_execute_ptr; */ + + la t0, _tx_thread_current_ptr // Pickup current thread pointer address + STORE t1, 0(t0) // Set current thread pointer + + /* Increment the run count for this thread. */ + /* _tx_thread_current_ptr -> tx_thread_run_count++; */ + + LOAD t2, 1*REGBYTES(t1) // Pickup run count + LOAD t3, 6*REGBYTES(t1) // Pickup time slice value + addi t2, t2, 1 // Increment run count + STORE t2, 1*REGBYTES(t1) // Store new run count + + /* Setup time-slice, if present. */ + /* _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; */ + + la t2, _tx_timer_time_slice // Pickup time-slice variable address + + /* Switch to the thread's stack. */ + /* SP = _tx_thread_execute_ptr -> tx_thread_stack_ptr; */ + + LOAD sp, 2*REGBYTES(t1) // Switch to thread's stack + STORE t3, 0(t2) // Store new time-slice*/ + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + call _tx_execution_thread_enter // Call the thread execution enter function +#endif + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LOAD t2, 0(sp) // Pickup stack type + beqz t2, _tx_thread_synch_return // If 0, solicited thread return + + /* Determine if floating point registers need to be recovered. */ + +#if defined(__riscv_float_abi_single) + flw f0, 31*REGBYTES(sp) // Recover ft0 + flw f1, 32*REGBYTES(sp) // Recover ft1 + flw f2, 33*REGBYTES(sp) // Recover ft2 + flw f3, 34*REGBYTES(sp) // Recover ft3 + flw f4, 35*REGBYTES(sp) // Recover ft4 + flw f5, 36*REGBYTES(sp) // Recover ft5 + flw f6, 37*REGBYTES(sp) // Recover ft6 + flw f7, 38*REGBYTES(sp) // Recover ft7 + flw f8, 39*REGBYTES(sp) // Recover fs0 + flw f9, 40*REGBYTES(sp) // Recover fs1 + flw f10,41*REGBYTES(sp) // Recover fa0 + flw f11,42*REGBYTES(sp) // Recover fa1 + flw f12,43*REGBYTES(sp) // Recover fa2 + flw f13,44*REGBYTES(sp) // Recover fa3 + flw f14,45*REGBYTES(sp) // Recover fa4 + flw f15,46*REGBYTES(sp) // Recover fa5 + flw f16,47*REGBYTES(sp) // Recover fa6 + flw f17,48*REGBYTES(sp) // Recover fa7 + flw f18,49*REGBYTES(sp) // Recover fs2 + flw f19,50*REGBYTES(sp) // Recover fs3 + flw f20,51*REGBYTES(sp) // Recover fs4 + flw f21,52*REGBYTES(sp) // Recover fs5 + flw f22,53*REGBYTES(sp) // Recover fs6 + flw f23,54*REGBYTES(sp) // Recover fs7 + flw f24,55*REGBYTES(sp) // Recover fs8 + flw f25,56*REGBYTES(sp) // Recover fs9 + flw f26,57*REGBYTES(sp) // Recover fs10 + flw f27,58*REGBYTES(sp) // Recover fs11 + flw f28,59*REGBYTES(sp) // Recover ft8 + flw f29,60*REGBYTES(sp) // Recover ft9 + flw f30,61*REGBYTES(sp) // Recover ft10 + flw f31,62*REGBYTES(sp) // Recover ft11 + LOAD t0, 63*REGBYTES(sp) // Recover fcsr + csrw fcsr, t0 // +#elif defined(__riscv_float_abi_double) + fld f0, 31*REGBYTES(sp) // Recover ft0 + fld f1, 32*REGBYTES(sp) // Recover ft1 + fld f2, 33*REGBYTES(sp) // Recover ft2 + fld f3, 34*REGBYTES(sp) // Recover ft3 + fld f4, 35*REGBYTES(sp) // Recover ft4 + fld f5, 36*REGBYTES(sp) // Recover ft5 + fld f6, 37*REGBYTES(sp) // Recover ft6 + fld f7, 38*REGBYTES(sp) // Recover ft7 + fld f8, 39*REGBYTES(sp) // Recover fs0 + fld f9, 40*REGBYTES(sp) // Recover fs1 + fld f10,41*REGBYTES(sp) // Recover fa0 + fld f11,42*REGBYTES(sp) // Recover fa1 + fld f12,43*REGBYTES(sp) // Recover fa2 + fld f13,44*REGBYTES(sp) // Recover fa3 + fld f14,45*REGBYTES(sp) // Recover fa4 + fld f15,46*REGBYTES(sp) // Recover fa5 + fld f16,47*REGBYTES(sp) // Recover fa6 + fld f17,48*REGBYTES(sp) // Recover fa7 + fld f18,49*REGBYTES(sp) // Recover fs2 + fld f19,50*REGBYTES(sp) // Recover fs3 + fld f20,51*REGBYTES(sp) // Recover fs4 + fld f21,52*REGBYTES(sp) // Recover fs5 + fld f22,53*REGBYTES(sp) // Recover fs6 + fld f23,54*REGBYTES(sp) // Recover fs7 + fld f24,55*REGBYTES(sp) // Recover fs8 + fld f25,56*REGBYTES(sp) // Recover fs9 + fld f26,57*REGBYTES(sp) // Recover fs10 + fld f27,58*REGBYTES(sp) // Recover fs11 + fld f28,59*REGBYTES(sp) // Recover ft8 + fld f29,60*REGBYTES(sp) // Recover ft9 + fld f30,61*REGBYTES(sp) // Recover ft10 + fld f31,62*REGBYTES(sp) // Recover ft11 + LOAD t0, 63*REGBYTES(sp) // Recover fcsr +#endif + + /* Recover standard registers. */ + + LOAD t0, 30*REGBYTES(sp) // Recover mepc + csrw mepc, t0 // Store mepc + li t0, 0x1880 // Prepare MPIP +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + li t1, 1<<13 + or t0, t1, t0 +#endif + csrw mstatus, t0 // Enable MPIP + + LOAD x1, 28*REGBYTES(sp) // Recover RA + LOAD x5, 19*REGBYTES(sp) // Recover t0 + LOAD x6, 18*REGBYTES(sp) // Recover t1 + LOAD x7, 17*REGBYTES(sp) // Recover t2 + LOAD x8, 12*REGBYTES(sp) // Recover s0 + LOAD x9, 11*REGBYTES(sp) // Recover s1 + LOAD x10, 27*REGBYTES(sp) // Recover a0 + LOAD x11, 26*REGBYTES(sp) // Recover a1 + LOAD x12, 25*REGBYTES(sp) // Recover a2 + LOAD x13, 24*REGBYTES(sp) // Recover a3 + LOAD x14, 23*REGBYTES(sp) // Recover a4 + LOAD x15, 22*REGBYTES(sp) // Recover a5 + LOAD x16, 21*REGBYTES(sp) // Recover a6 + LOAD x17, 20*REGBYTES(sp) // Recover a7 + LOAD x18, 10*REGBYTES(sp) // Recover s2 + LOAD x19, 9*REGBYTES(sp) // Recover s3 + LOAD x20, 8*REGBYTES(sp) // Recover s4 + LOAD x21, 7*REGBYTES(sp) // Recover s5 + LOAD x22, 6*REGBYTES(sp) // Recover s6 + LOAD x23, 5*REGBYTES(sp) // Recover s7 + LOAD x24, 4*REGBYTES(sp) // Recover s8 + LOAD x25, 3*REGBYTES(sp) // Recover s9 + LOAD x26, 2*REGBYTES(sp) // Recover s10 + LOAD x27, 1*REGBYTES(sp) // Recover s11 + LOAD x28, 16*REGBYTES(sp) // Recover t3 + LOAD x29, 15*REGBYTES(sp) // Recover t4 + LOAD x30, 14*REGBYTES(sp) // Recover t5 + LOAD x31, 13*REGBYTES(sp) // Recover t6 + +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + addi sp, sp, 65*REGBYTES // Recover stack frame - with floating point registers +#else + addi sp, sp, 32*REGBYTES // Recover stack frame - without floating point registers +#endif + mret // Return to point of interrupt + +_tx_thread_synch_return: + +#if defined(__riscv_float_abi_single) + flw f8, 15*REGBYTES(sp) // Recover fs0 + flw f9, 16*REGBYTES(sp) // Recover fs1 + flw f18,17*REGBYTES(sp) // Recover fs2 + flw f19,18*REGBYTES(sp) // Recover fs3 + flw f20,19*REGBYTES(sp) // Recover fs4 + flw f21,20*REGBYTES(sp) // Recover fs5 + flw f22,21*REGBYTES(sp) // Recover fs6 + flw f23,22*REGBYTES(sp) // Recover fs7 + flw f24,23*REGBYTES(sp) // Recover fs8 + flw f25,24*REGBYTES(sp) // Recover fs9 + flw f26,25*REGBYTES(sp) // Recover fs10 + flw f27,26*REGBYTES(sp) // Recover fs11 + LOAD t0, 27*REGBYTES(sp) // Recover fcsr + csrw fcsr, t0 // +#elif defined(__riscv_float_abi_double) + fld f8, 15*REGBYTES(sp) // Recover fs0 + fld f9, 16*REGBYTES(sp) // Recover fs1 + fld f18,17*REGBYTES(sp) // Recover fs2 + fld f19,18*REGBYTES(sp) // Recover fs3 + fld f20,19*REGBYTES(sp) // Recover fs4 + fld f21,20*REGBYTES(sp) // Recover fs5 + fld f22,21*REGBYTES(sp) // Recover fs6 + fld f23,22*REGBYTES(sp) // Recover fs7 + fld f24,23*REGBYTES(sp) // Recover fs8 + fld f25,24*REGBYTES(sp) // Recover fs9 + fld f26,25*REGBYTES(sp) // Recover fs10 + fld f27,26*REGBYTES(sp) // Recover fs11 + LOAD t0, 27*REGBYTES(sp) // Recover fcsr + csrw fcsr, t0 // +#endif + + /* Recover standard preserved registers. */ + /* Recover standard registers. */ + + LOAD x1, 13*REGBYTES(sp) // Recover RA + LOAD x8, 12*REGBYTES(sp) // Recover s0 + LOAD x9, 11*REGBYTES(sp) // Recover s1 + LOAD x18, 10*REGBYTES(sp) // Recover s2 + LOAD x19, 9*REGBYTES(sp) // Recover s3 + LOAD x20, 8*REGBYTES(sp) // Recover s4 + LOAD x21, 7*REGBYTES(sp) // Recover s5 + LOAD x22, 6*REGBYTES(sp) // Recover s6 + LOAD x23, 5*REGBYTES(sp) // Recover s7 + LOAD x24, 4*REGBYTES(sp) // Recover s8 + LOAD x25, 3*REGBYTES(sp) // Recover s9 + LOAD x26, 2*REGBYTES(sp) // Recover s10 + LOAD x27, 1*REGBYTES(sp) // Recover s11 + LOAD t0, 14*REGBYTES(sp) // Recover mstatus + csrw mstatus, t0 // Store mstatus, enables interrupt +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + addi sp, sp, 29*REGBYTES // Recover stack frame +#else + addi sp, sp, 16*REGBYTES // Recover stack frame +#endif + ret // Return to thread + +/* } */ diff --git a/ports/risc-v32/gnu/src/tx_thread_stack_build.S b/ports/risc-v32/gnu/src/tx_thread_stack_build.S new file mode 100644 index 000000000..b559969df --- /dev/null +++ b/ports/risc-v32/gnu/src/tx_thread_stack_build.S @@ -0,0 +1,228 @@ +/*************************************************************************** + * 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 + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#include "tx_port.h" + + .section .text +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build RISC-V32/GNU */ +/* 6.4.x */ +/* AUTHOR */ +/* */ +/* Akif Ejaz, 10xEngineers */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread control blk */ +/* function_ptr Pointer to return function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 23-12-2025 Akif Ejaz Initial Version 6.4.x */ +/* */ +/**************************************************************************/ +/* VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +{ */ + .global _tx_thread_stack_build +_tx_thread_stack_build: + + /* Build a fake interrupt frame. The form of the fake interrupt stack + on the RISC-V should look like the following after it is built: + Reg Index + Stack Top: 1 0 Interrupt stack frame type + x27 1 Initial s11 + x26 2 Initial s10 + x25 3 Initial s9 + x24 4 Initial s8 + x23 5 Initial s7 + x22 6 Initial s6 + x21 7 Initial s5 + x20 8 Initial s4 + x19 9 Initial s3 + x18 10 Initial s2 + x9 11 Initial s1 + x8 12 Initial s0 + x31 13 Initial t6 + x30 14 Initial t5 + x29 15 Initial t4 + x28 16 Initial t3 + x7 17 Initial t2 + x6 18 Initial t1 + x5 19 Initial t0 + x17 20 Initial a7 + x16 21 Initial a6 + x15 22 Initial a5 + x14 23 Initial a4 + x13 24 Initial a3 + x12 25 Initial a2 + x11 26 Initial a1 + x10 27 Initial a0 + x1 28 Initial ra + -- 29 reserved + mepc 30 Initial mepc +If floating point support: + f0 31 Inital ft0 + f1 32 Inital ft1 + f2 33 Inital ft2 + f3 34 Inital ft3 + f4 35 Inital ft4 + f5 36 Inital ft5 + f6 37 Inital ft6 + f7 38 Inital ft7 + f8 39 Inital fs0 + f9 40 Inital fs1 + f10 41 Inital fa0 + f11 42 Inital fa1 + f12 43 Inital fa2 + f13 44 Inital fa3 + f14 45 Inital fa4 + f15 46 Inital fa5 + f16 47 Inital fa6 + f17 48 Inital fa7 + f18 49 Inital fs2 + f19 50 Inital fs3 + f20 51 Inital fs4 + f21 52 Inital fs5 + f22 53 Inital fs6 + f23 54 Inital fs7 + f24 55 Inital fs8 + f25 56 Inital fs9 + f26 57 Inital fs10 + f27 58 Inital fs11 + f28 59 Inital ft8 + f29 60 Inital ft9 + f30 61 Inital ft10 + f31 62 Inital ft11 + fscr 63 Inital fscr + + Stack Bottom: (higher memory address) */ + + LOAD t0, 4*REGBYTES(a0) // Pickup end of stack area + li t1, ~15 // Build 16-byte alignment mask + and t0, t0, t1 // Make sure 16-byte alignment + + /* Actually build the stack frame. */ + +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + addi t0, t0, -65*REGBYTES +#else + addi t0, t0, -32*REGBYTES // Allocate space for the stack frame +#endif + li t1, 1 // Build stack type + STORE t1, 0*REGBYTES(t0) // Place stack type on the top + STORE x0, 1*REGBYTES(t0) // Initial s11 + STORE x0, 2*REGBYTES(t0) // Initial s10 + STORE x0, 3*REGBYTES(t0) // Initial s9 + STORE x0, 4*REGBYTES(t0) // Initial s8 + STORE x0, 5*REGBYTES(t0) // Initial s7 + STORE x0, 6*REGBYTES(t0) // Initial s6 + STORE x0, 7*REGBYTES(t0) // Initial s5 + STORE x0, 8*REGBYTES(t0) // Initial s4 + STORE x0, 9*REGBYTES(t0) // Initial s3 + STORE x0, 10*REGBYTES(t0) // Initial s2 + STORE x0, 11*REGBYTES(t0) // Initial s1 + STORE x0, 12*REGBYTES(t0) // Initial s0 + STORE x0, 13*REGBYTES(t0) // Initial t6 + STORE x0, 14*REGBYTES(t0) // Initial t5 + STORE x0, 15*REGBYTES(t0) // Initial t4 + STORE x0, 16*REGBYTES(t0) // Initial t3 + STORE x0, 17*REGBYTES(t0) // Initial t2 + STORE x0, 18*REGBYTES(t0) // Initial t1 + STORE x0, 19*REGBYTES(t0) // Initial t0 + STORE x0, 20*REGBYTES(t0) // Initial a7 + STORE x0, 21*REGBYTES(t0) // Initial a6 + STORE x0, 22*REGBYTES(t0) // Initial a5 + STORE x0, 23*REGBYTES(t0) // Initial a4 + STORE x0, 24*REGBYTES(t0) // Initial a3 + STORE x0, 25*REGBYTES(t0) // Initial a2 + STORE x0, 26*REGBYTES(t0) // Initial a1 + STORE x0, 27*REGBYTES(t0) // Initial a0 + STORE x0, 28*REGBYTES(t0) // Initial ra + STORE a1, 30*REGBYTES(t0) // Initial mepc +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + STORE x0, 31*REGBYTES(t0) // Inital ft0 + STORE x0, 32*REGBYTES(t0) // Inital ft1 + STORE x0, 33*REGBYTES(t0) // Inital ft2 + STORE x0, 34*REGBYTES(t0) // Inital ft3 + STORE x0, 35*REGBYTES(t0) // Inital ft4 + STORE x0, 36*REGBYTES(t0) // Inital ft5 + STORE x0, 37*REGBYTES(t0) // Inital ft6 + STORE x0, 38*REGBYTES(t0) // Inital ft7 + STORE x0, 39*REGBYTES(t0) // Inital fs0 + STORE x0, 40*REGBYTES(t0) // Inital fs1 + STORE x0, 41*REGBYTES(t0) // Inital fa0 + STORE x0, 42*REGBYTES(t0) // Inital fa1 + STORE x0, 43*REGBYTES(t0) // Inital fa2 + STORE x0, 44*REGBYTES(t0) // Inital fa3 + STORE x0, 45*REGBYTES(t0) // Inital fa4 + STORE x0, 46*REGBYTES(t0) // Inital fa5 + STORE x0, 47*REGBYTES(t0) // Inital fa6 + STORE x0, 48*REGBYTES(t0) // Inital fa7 + STORE x0, 49*REGBYTES(t0) // Inital fs2 + STORE x0, 50*REGBYTES(t0) // Inital fs3 + STORE x0, 51*REGBYTES(t0) // Inital fs4 + STORE x0, 52*REGBYTES(t0) // Inital fs5 + STORE x0, 53*REGBYTES(t0) // Inital fs6 + STORE x0, 54*REGBYTES(t0) // Inital fs7 + STORE x0, 55*REGBYTES(t0) // Inital fs8 + STORE x0, 56*REGBYTES(t0) // Inital fs9 + STORE x0, 57*REGBYTES(t0) // Inital fs10 + STORE x0, 58*REGBYTES(t0) // Inital fs11 + STORE x0, 59*REGBYTES(t0) // Inital ft8 + STORE x0, 60*REGBYTES(t0) // Inital ft9 + STORE x0, 61*REGBYTES(t0) // Inital ft10 + STORE x0, 62*REGBYTES(t0) // Inital ft11 + csrr a1, fcsr // Read fcsr and use it for initial value for each thread + STORE a1, 63*REGBYTES(t0) // Initial fscr + STORE x0, 64*REGBYTES(t0) // Reserved word (0) +#else + STORE x0, 31*REGBYTES(t0) // Reserved word (0) +#endif + + /* Setup stack pointer. */ + /* thread_ptr -> tx_thread_stack_ptr = t0; */ + + STORE t0, 2*REGBYTES(a0) // Save stack pointer in thread's + ret // control block and return +/* } */ diff --git a/ports/risc-v32/gnu/src/tx_thread_system_return.S b/ports/risc-v32/gnu/src/tx_thread_system_return.S new file mode 100644 index 000000000..984c76913 --- /dev/null +++ b/ports/risc-v32/gnu/src/tx_thread_system_return.S @@ -0,0 +1,174 @@ +/*************************************************************************** + * 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 + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#include "tx_port.h" + + .section .text +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return RISC-V32/GNU */ +/* 6.4.x */ +/* AUTHOR */ +/* */ +/* Akif Ejaz, 10xEngineers */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the system. Only a minimal context */ +/* is saved since the compiler assumes temp registers are going to get */ +/* slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 23-12-2025 Akif Ejaz Initial Version 6.4.x */ +/* */ +/**************************************************************************/ +/* VOID _tx_thread_system_return(VOID) +{ */ + .global _tx_thread_system_return +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + addi sp, sp, -29*REGBYTES // Allocate space on the stack - with floating point enabled +#else + addi sp, sp, -16*REGBYTES // Allocate space on the stack - without floating point enabled +#endif + + /* Store floating point preserved registers. */ +#if defined(__riscv_float_abi_single) + fsw f8, 15*REGBYTES(sp) // Store fs0 + fsw f9, 16*REGBYTES(sp) // Store fs1 + fsw f18, 17*REGBYTES(sp) // Store fs2 + fsw f19, 18*REGBYTES(sp) // Store fs3 + fsw f20, 19*REGBYTES(sp) // Store fs4 + fsw f21, 20*REGBYTES(sp) // Store fs5 + fsw f22, 21*REGBYTES(sp) // Store fs6 + fsw f23, 22*REGBYTES(sp) // Store fs7 + fsw f24, 23*REGBYTES(sp) // Store fs8 + fsw f25, 24*REGBYTES(sp) // Store fs9 + fsw f26, 25*REGBYTES(sp) // Store fs10 + fsw f27, 26*REGBYTES(sp) // Store fs11 + csrr t0, fcsr + STORE t0, 27*REGBYTES(sp) // Store fcsr +#elif defined(__riscv_float_abi_double) + fsd f8, 15*REGBYTES(sp) // Store fs0 + fsd f9, 16*REGBYTES(sp) // Store fs1 + fsd f18, 17*REGBYTES(sp) // Store fs2 + fsd f19, 18*REGBYTES(sp) // Store fs3 + fsd f20, 19*REGBYTES(sp) // Store fs4 + fsd f21, 20*REGBYTES(sp) // Store fs5 + fsd f22, 21*REGBYTES(sp) // Store fs6 + fsd f23, 22*REGBYTES(sp) // Store fs7 + fsd f24, 23*REGBYTES(sp) // Store fs8 + fsd f25, 24*REGBYTES(sp) // Store fs9 + fsd f26, 25*REGBYTES(sp) // Store fs10 + fsd f27, 26*REGBYTES(sp) // Store fs11 + csrr t0, fcsr + STORE t0, 27*REGBYTES(sp) // Store fcsr +#endif + + STORE x0, 0(sp) // Solicited stack type + STORE x1, 13*REGBYTES(sp) // Save RA + STORE x8, 12*REGBYTES(sp) // Save s0 + STORE x9, 11*REGBYTES(sp) // Save s1 + STORE x18, 10*REGBYTES(sp) // Save s2 + STORE x19, 9*REGBYTES(sp) // Save s3 + STORE x20, 8*REGBYTES(sp) // Save s4 + STORE x21, 7*REGBYTES(sp) // Save s5 + STORE x22, 6*REGBYTES(sp) // Save s6 + STORE x23, 5*REGBYTES(sp) // Save s7 + STORE x24, 4*REGBYTES(sp) // Save s8 + STORE x25, 3*REGBYTES(sp) // Save s9 + STORE x26, 2*REGBYTES(sp) // Save s10 + STORE x27, 1*REGBYTES(sp) // Save s11 + csrr t0, mstatus // Pickup mstatus + STORE t0, 14*REGBYTES(sp) // Save mstatus + + + /* Lockout interrupts. - will be enabled in _tx_thread_schedule */ + + csrci mstatus, 0xF + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + call _tx_execution_thread_exit // Call the thread execution exit function +#endif + + la t0, _tx_thread_current_ptr // Pickup address of pointer + LOAD t1, 0(t0) // Pickup current thread pointer + la t2,_tx_thread_system_stack_ptr // Pickup stack pointer address + + /* Save current stack and switch to system stack. */ + /* _tx_thread_current_ptr -> tx_thread_stack_ptr = SP; + SP = _tx_thread_system_stack_ptr; */ + + STORE sp, 2*REGBYTES(t1) // Save stack pointer + LOAD sp, 0(t2) // Switch to system stack + + /* Determine if the time-slice is active. */ + /* if (_tx_timer_time_slice) + { */ + + la t4, _tx_timer_time_slice // Pickup time slice variable addr + LOAD t3, 0(t4) // Pickup time slice value + la t2, _tx_thread_schedule // Pickup address of scheduling loop + beqz t3, _tx_thread_dont_save_ts // If no time-slice, don't save it + + /* Save time-slice for the thread and clear the current time-slice. */ + /* _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + _tx_timer_time_slice = 0; */ + + STORE t3, 6*REGBYTES(t1) // Save current time-slice for thread + STORE x0, 0(t4) // Clear time-slice variable + + /* } */ +_tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + /* _tx_thread_current_ptr = TX_NULL; */ + + STORE x0, 0(t0) // Clear current thread pointer + jr t2 // Return to thread scheduler + +/* } */ diff --git a/ports/risc-v32/gnu/src/tx_timer_interrupt.c b/ports/risc-v32/gnu/src/tx_timer_interrupt.c new file mode 100644 index 000000000..c623daf84 --- /dev/null +++ b/ports/risc-v32/gnu/src/tx_timer_interrupt.c @@ -0,0 +1,136 @@ +/*************************************************************************** + * 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 + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_timer.h" +#include "tx_thread.h" + +extern int uart_puts(const char* str); + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt RISC-V32/GNU */ +/* 6.4.x */ +/* AUTHOR */ +/* */ +/* Akif Ejaz, 10xEngineers */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 23-12-2025 Akif Ejaz Initial Version 6.4.x */ +/* */ +/**************************************************************************/ +VOID _tx_timer_interrupt(VOID) +{ + /* Increment system clock. */ + _tx_timer_system_clock++; + + /* Test for time-slice expiration. */ + if (_tx_timer_time_slice) + { + /* Decrement the time_slice. */ + _tx_timer_time_slice--; + + /* Check for expiration. */ + if (_tx_timer_time_slice == 0) + { + + /* Set the time-slice expired flag. */ + _tx_timer_expired_time_slice = TX_TRUE; + } + } + + /* Test for timer expiration. */ + if (*_tx_timer_current_ptr) + { + + /* Set expiration flag. */ + _tx_timer_expired = TX_TRUE; + } + else + { + + /* No timer expired, increment the timer pointer. */ + _tx_timer_current_ptr++; + + /* Check for wrap-around. */ + if (_tx_timer_current_ptr == _tx_timer_list_end) + { + + /* Wrap to beginning of list. */ + _tx_timer_current_ptr = _tx_timer_list_start; + } + } + + /* See if anything has expired. */ + if ((_tx_timer_expired_time_slice) || (_tx_timer_expired)) + { + + /* Did a timer expire? */ + if (_tx_timer_expired) + { + + /* Process timer expiration. */ + _tx_timer_expiration_process(); + } + + /* Did time slice expire? */ + if (_tx_timer_expired_time_slice) + { + + /* Time slice interrupted thread. */ + _tx_thread_time_slice(); + } + } +}