Compare commits

21 Commits

Author SHA1 Message Date
2433b66dc0 corrects port files, enables wakeups in port 2026-03-16 08:08:49 +01:00
2d0ec274ac makes bootup multi core able 2026-03-16 08:08:18 +01:00
58fc04bbb3 adds trap handler 2026-03-16 08:08:07 +01:00
3ba7cdda9e fortifies regex for asm offset parsing 2026-03-12 20:17:42 +01:00
239cd26a5c adds stub for dynamic core discovery in port 2026-03-12 16:31:51 +01:00
ebe891dad6 adds smp tx_timer_interrupt 2026-03-12 16:15:57 +01:00
c390c4b8db adds smp demo as target 2026-03-12 15:57:28 +01:00
1da9671197 adds trap_entry to smp lib 2026-03-12 15:57:02 +01:00
28aaf8fd96 makes smp a lib only 2026-03-12 15:56:48 +01:00
6d635345dd small formatting changes 2026-03-12 15:56:33 +01:00
3fc721d6f2 moves spm_offset functionality into its own cmake file 2026-03-12 15:38:15 +01:00
5ebcce634a small cleanup 2026-03-12 15:29:41 +01:00
cca56f89c6 adds other files necessary to build smp library 2026-03-12 15:21:18 +01:00
1117527a02 changes so that cmake builds for now 2026-03-11 19:13:10 +01:00
a75db78425 adds ipi functionality 2026-03-10 21:02:29 +01:00
744773848c adds initial smp tx_port 2026-03-10 21:02:16 +01:00
f83e96fbcd adds initial low_level_init 2026-03-10 21:01:01 +01:00
2e955b9342 adds the generation of the offsetof defines to CMakeLists.txt
As the offsets into the TX_THREAD are needed to access certain fields and the members for smp are quite difficult to obtain this was the safest option
2026-03-10 20:59:58 +01:00
70bbde8502 adds initial smp scheduler 2026-03-10 20:57:47 +01:00
e5020b78de WIP: build system 2026-03-09 19:37:34 +01:00
f10fbbffab adds small changes to make tx_port for non-smp consistent with the one that implements smp 2026-03-09 15:49:15 +01:00
28 changed files with 2952 additions and 32 deletions

View File

@@ -15,6 +15,9 @@ target_link_libraries(c PUBLIC gcc)
set(THREADX_CUSTOM_PORT ${CMAKE_CURRENT_LIST_DIR}/port/threadx)
add_subdirectory(third-party/threadx)
target_link_libraries(threadx PUBLIC c)
#Adds threadx_smp
add_subdirectory(port/threadx_smp)
target_link_libraries(threadx_smp PUBLIC c)
# Adds netxduo
set(NETXDUO_CUSTOM_PORT ${CMAKE_CURRENT_LIST_DIR}/port/threadx)
set(NXD_ENABLE_FILE_SERVERS OFF)
@@ -30,12 +33,12 @@ endif()
###############################################################################
project(threadx_demo C ASM)
option(NX_DEBUG "compile netxduo debug output in" OFF)
set(TARGET_MEM "ram" CACHE STRING "memory map to use" )
set(TARGET_MEM "ram" CACHE STRING "memory map to use")
set(CMAKE_EXECUTABLE_SUFFIX_C ".elf")
function(setup_target TARGET)
set(options)
set(oneValueArgs) # none for now
set(oneValueArgs) # none for now
set(multiValueArgs LIBRARIES SOURCES)
cmake_parse_arguments(ST "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if(ST_UNPARSED_ARGUMENTS)
@@ -43,13 +46,13 @@ function(setup_target TARGET)
endif()
add_executable(${TARGET})
target_sources(${TARGET} PRIVATE
port/picolibc/port.c
port/moonlight/bootup.c
port/moonlight/board.c
port/moonlight/trap_non_vectored.c
port/moonlight/exception.c
port/moonlight/vector_table.c
target_sources(${TARGET} PRIVATE
port/picolibc/port.c
port/moonlight/bootup.c
port/moonlight/board.c
port/moonlight/trap_non_vectored.c
port/moonlight/exception.c
port/moonlight/vector_table.c
)
if("netxduo" IN_LIST ST_LIBRARIES)
target_sources(${TARGET} PRIVATE port/moonlight/mnrs_network_driver.c)
@@ -67,7 +70,7 @@ function(setup_target TARGET)
-ffunction-sections
)
if(NX_DEBUG)
target_compile_definitions(${TARGET} PRIVATE NX_DEBUG NX_DEBUG_PACKET)
target_compile_definitions(${TARGET} PRIVATE NX_DEBUG NX_DEBUG_PACKET)
endif()
target_link_directories(${TARGET} PRIVATE src) # needed for linker script includes
target_link_options(${TARGET} PRIVATE
@@ -80,19 +83,16 @@ function(setup_target TARGET)
if(ST_LIBRARIES)
target_link_libraries(${TARGET} PRIVATE ${ST_LIBRARIES})
endif()
target_link_libraries(${TARGET} PRIVATE threadx)
add_custom_command(TARGET ${TARGET} POST_BUILD
COMMAND ${OBJCOPY} -O ihex $<TARGET_FILE:${TARGET}> ${CMAKE_BINARY_DIR}/${TARGET}.hex
COMMAND ${OBJCOPY} -O ihex $<TARGET_FILE:${TARGET}> ${CMAKE_BINARY_DIR}/${TARGET}.hex
COMMAND ${OBJCOPY} -O binary $<TARGET_FILE:${TARGET}> ${CMAKE_BINARY_DIR}/${TARGET}.bin
COMMAND ${SIZE} $<TARGET_FILE:${TARGET}>
COMMAND ${OBJDUMP} -S $<TARGET_FILE:${TARGET}> > ${TARGET}.dis
COMMAND ${OBJDUMP} -S $<TARGET_FILE:${TARGET}> > ${TARGET}.dis
COMMENT "Creating collateral for ${TARGET}"
)
endfunction()
setup_target(thread_demo SOURCES src/thread_demo/main.c)
setup_target(tcp_demo
LIBRARIES netxduo
SOURCES src/tcp_demo/main.c
)
setup_target(thread_demo LIBRARIES threadx SOURCES src/thread_demo/main.c)
setup_target(tcp_demo LIBRARIES threadx netxduo SOURCES src/tcp_demo/main.c)
setup_target(smp_demo LIBRARIES threadx_smp SOURCES src/thread_demo/main.c)

View File

@@ -0,0 +1,12 @@
#ifndef _DEVICES_ACLINT_IPI
#define _DEVICES_ACLINT_IPI
#include "gen/aclint.h"
#include "platform.h"
#include <stdint.h>
static void send_ipi(uint32_t target_core)
{
set_aclint_msip(aclint, target_core, 1);
}
#endif /* _DEVICES_ACLINT_IPI */

View File

@@ -9,7 +9,7 @@
#include <stdint.h>
#include <string.h>
#include <picotls.h>
#include <tx_port.h>
#ifdef __cplusplus
#define EXTERN_C extern "C"
#else
@@ -50,7 +50,6 @@ extern int main(void);
// The linker script will place this in the reset entry point.
// It will be 'called' with no stack or C runtime configuration.
// NOTE - this only supports a single hart.
// tp will not be initialized
void _start(void) {
// Setup SP and GP
@@ -64,6 +63,24 @@ void _start(void) {
"la gp, __global_pointer$;"
".option pop;"
"la sp, _sp;"
#if defined(__riscv_zicsr)
"csrr t0, mhartid;"
#else
"li t0, 0;"
#endif
"la t1, __stack_size;"
"la t1, __stack_size;"
"la sp, _sp;"
// Loop incase M extension is not present
"1:;"
"beqz t0, 2f;"
"sub sp, sp, t1;"
"addi t0, t0, -1;"
"j 1b;"
"2:;"
#ifdef TX_THREAD_SMP_MAX_CORES
"call _tx_thread_smp_initialize_wait;"
#endif
"jal zero, _initialize;"
: /* output: none %0 */
: /* input: none */

View File

@@ -1,4 +1,5 @@
#include "hwtimer.h"
#include "platform.h"
#include "riscv-traps.h"
#include <stdint.h>
#include <stdio.h>
@@ -39,6 +40,11 @@ void trap_handler(uintptr_t mcause, uintptr_t mepc, uintptr_t mtval) {
hwtimer_handler();
_tx_timer_interrupt();
break;
#ifdef TX_THREAD_SMP_INTER_CORE_INTERRUPT
case RISCV_INT_MSI:
set_aclint_msip(aclint, csr_read_mhartid(), 0);
break;
#endif
case RISCV_INT_MEI:
puts("[INTERRUPT]: handler ext irq error!\n");
while(1)

View File

@@ -72,11 +72,6 @@
#else /*not __ASSEMBLER__ */
/* Include for memset. */
#include <string.h>
/* include for strtoul*/
#include <stdlib.h>
/* Determine if the optional ThreadX user define file should be used. */
#ifdef TX_INCLUDE_USER_DEFINE_FILE
@@ -90,6 +85,9 @@
/* Define compiler library include files. */
#include <stdlib.h>
#include <string.h>
/* Define ThreadX basic types for this port. */
#define VOID void

View File

@@ -138,19 +138,19 @@ trap_handler:
.extern __heap_start
.extern board_init
_tx_initialize_low_level:
STORE sp, _tx_thread_system_stack_ptr, t0 // Save system stack pointer
STORE sp, _tx_thread_system_stack_ptr, t0 // Save system stack pointer
la t0, __heap_start // Pickup first free address
STORE t0, _tx_initialize_unused_memory, t1 // Save unused memory address
la t0, __heap_start // Pickup first free address
STORE t0, _tx_initialize_unused_memory, t1 // Save unused memory address
li t0, MSTATUS_MIE
csrrc zero, mstatus, t0 // clear MSTATUS_MIE bit
csrrc zero, mstatus, t0 // clear MSTATUS_MIE bit
li t0, (MSTATUS_MPP_M | MSTATUS_MPIE )
csrrs zero, mstatus, t0 // set MSTATUS_MPP, MPIE bit
csrrs zero, mstatus, t0 // set MSTATUS_MPP, MPIE bit
li t0, (MIE_MTIE | MIE_MSIE | MIE_MEIE)
csrrs zero, mie, t0 // set mie
csrrs zero, mie, t0 // set mie
#ifdef __riscv_flen
li t0, MSTATUS_FS
csrrs zero, mstatus, t0 // set MSTATUS_FS bit to open f/d isa in riscv
csrrs zero, mstatus, t0 // set MSTATUS_FS bit to open f/d isa in riscv
fscsr x0
#endif
addi sp, sp, -8

View File

@@ -0,0 +1,60 @@
cmake_minimum_required(VERSION 3.24)
project(smp_demo LANGUAGES C ASM)
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/ThreadXSmpOffsets.cmake)
set(THREADX_COMMON_SMP_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../third-party/threadx/common_smp)
if(NOT EXISTS "${THREADX_COMMON_SMP_DIR}")
message(FATAL_ERROR "could not find ThreadX SMP sources, is the submodule checked out?")
endif()
set(THREADX_SMP_CUSTOM_INC
${CMAKE_CURRENT_SOURCE_DIR}/inc
${CMAKE_CURRENT_SOURCE_DIR}/../moonlight #needed for aclint (inter process interrupts)
)
set(THREADX_SMP_CUSTOM_SRC
src/tx_initialize_low_level.S
src/tx_thread_context_restore.S
src/tx_thread_context_save.S
src/tx_thread_schedule.S
src/tx_thread_smp_core_get.S
src/tx_thread_smp_core_preempt.c
src/tx_thread_smp_current_state_get.S
src/tx_thread_smp_current_thread_get.S
src/tx_thread_smp_initialize_wait.S
src/tx_thread_smp_low_level_initialize.S
src/tx_thread_smp_protect.S
src/tx_thread_smp_unprotect.S
src/tx_thread_stack_build.S
src/tx_thread_system_return.S
src/tx_timer_interrupt.c
)
threadx_smp_add_offsets(
TARGET threadx_smp_offsets
OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/generated
SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/src/tx_asm_offsets.c
INCLUDE_DIRS
${THREADX_COMMON_SMP_DIR}/inc
${THREADX_SMP_CUSTOM_INC}
DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/inc/tx_port.h
${THREADX_COMMON_SMP_DIR}/inc/tx_api.h
OUT_INCLUDE_DIR THREADX_SMP_GENERATED_INC_DIR
)
file(GLOB THREADX_SMP_SOURCES ${THREADX_COMMON_SMP_DIR}/src/*.c)
add_library(threadx_smp STATIC)
target_sources(threadx_smp PRIVATE
${THREADX_SMP_SOURCES}
${THREADX_SMP_CUSTOM_SRC}
)
target_include_directories(threadx_smp PUBLIC
${THREADX_COMMON_SMP_DIR}/inc
${THREADX_SMP_CUSTOM_INC}
)
target_include_directories(threadx_smp PRIVATE
${THREADX_SMP_GENERATED_INC_DIR}
)
target_compile_definitions(threadx_smp PRIVATE TX_QUEUE_MESSAGE_MAX_SIZE=16) #This is addressed in PR #503
add_dependencies(threadx_smp threadx_smp_offsets)

View File

@@ -0,0 +1,41 @@
if(NOT DEFINED COMPILER)
message(FATAL_ERROR "COMPILER is required")
endif()
if(NOT DEFINED SOURCE)
message(FATAL_ERROR "SOURCE is required")
endif()
if(NOT DEFINED ASM_OUTPUT)
message(FATAL_ERROR "ASM_OUTPUT is required")
endif()
if(NOT DEFINED HEADER_OUTPUT)
message(FATAL_ERROR "HEADER_OUTPUT is required")
endif()
if(NOT DEFINED GENERATE_SCRIPT)
message(FATAL_ERROR "GENERATE_SCRIPT is required")
endif()
execute_process(
COMMAND "${COMPILER}" ${COMPILER_ARG1} ${CFLAG_ARGS} ${INCLUDE_ARGS} ${DEFINE_ARGS} -S -o "${ASM_OUTPUT}" "${SOURCE}"
RESULT_VARIABLE compile_result
OUTPUT_VARIABLE compile_stdout
ERROR_VARIABLE compile_stderr
)
if(NOT compile_result EQUAL 0)
message(FATAL_ERROR "failed to compile asm offsets source\n${compile_stdout}${compile_stderr}")
endif()
execute_process(
COMMAND "${CMAKE_COMMAND}" -DINPUT="${ASM_OUTPUT}" -DOUTPUT="${HEADER_OUTPUT}" -P "${GENERATE_SCRIPT}"
RESULT_VARIABLE generate_result
OUTPUT_VARIABLE generate_stdout
ERROR_VARIABLE generate_stderr
)
if(NOT generate_result EQUAL 0)
message(FATAL_ERROR "failed to generate asm offsets header\n${generate_stdout}${generate_stderr}")
endif()

View File

@@ -0,0 +1,22 @@
if(NOT DEFINED INPUT)
message(FATAL_ERROR "INPUT is required")
endif()
if(NOT DEFINED OUTPUT)
message(FATAL_ERROR "OUTPUT is required")
endif()
file(STRINGS "${INPUT}" OFFSET_LINES REGEX "->")
set(OFFSET_CONTENT "/* Generated by GenerateAsmOffsets.cmake. */\n")
foreach(LINE IN LISTS OFFSET_LINES)
string(REGEX MATCH "-->([A-Za-z0-9_]+)[^0-9-]*(-?[0-9]+)" _ "${LINE}")
if(NOT CMAKE_MATCH_1)
continue()
endif()
string(APPEND OFFSET_CONTENT "#define ${CMAKE_MATCH_1} ${CMAKE_MATCH_2}\n")
endforeach()
file(WRITE "${OUTPUT}" "${OFFSET_CONTENT}")

View File

@@ -0,0 +1,67 @@
function(threadx_smp_add_offsets)
set(options)
set(oneValueArgs TARGET OUTPUT_DIR SOURCE OUT_INCLUDE_DIR)
set(multiValueArgs INCLUDE_DIRS COMPILE_DEFINITIONS DEPENDS)
cmake_parse_arguments(THREADX_SMP_OFFSETS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if(NOT THREADX_SMP_OFFSETS_TARGET)
message(FATAL_ERROR "threadx_smp_add_offsets requires TARGET")
endif()
if(NOT THREADX_SMP_OFFSETS_OUTPUT_DIR)
message(FATAL_ERROR "threadx_smp_add_offsets requires OUTPUT_DIR")
endif()
if(NOT THREADX_SMP_OFFSETS_SOURCE)
message(FATAL_ERROR "threadx_smp_add_offsets requires SOURCE")
endif()
if(NOT THREADX_SMP_OFFSETS_OUT_INCLUDE_DIR)
message(FATAL_ERROR "threadx_smp_add_offsets requires OUT_INCLUDE_DIR")
endif()
set(threadx_smp_generate_script "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/GenerateAsmOffsets.cmake")
set(threadx_smp_offset_asm "${THREADX_SMP_OFFSETS_OUTPUT_DIR}/tx_asm_offsets.s")
set(threadx_smp_offset_inc "${THREADX_SMP_OFFSETS_OUTPUT_DIR}/tx_asm_offsets.inc")
set(threadx_smp_offset_include_args ${THREADX_SMP_OFFSETS_INCLUDE_DIRS})
list(TRANSFORM threadx_smp_offset_include_args PREPEND -I)
set(threadx_smp_offset_define_args ${THREADX_SMP_OFFSETS_COMPILE_DEFINITIONS})
list(TRANSFORM threadx_smp_offset_define_args PREPEND -D)
set(threadx_smp_offset_cflags ${CMAKE_C_FLAGS})
if(CMAKE_BUILD_TYPE)
string(TOUPPER "${CMAKE_BUILD_TYPE}" threadx_smp_build_type_upper)
list(APPEND threadx_smp_offset_cflags ${CMAKE_C_FLAGS_${threadx_smp_build_type_upper}})
endif()
separate_arguments(threadx_smp_offset_cflags)
add_custom_command(
OUTPUT ${threadx_smp_offset_inc}
BYPRODUCTS ${threadx_smp_offset_asm}
COMMAND ${CMAKE_COMMAND} -E make_directory ${THREADX_SMP_OFFSETS_OUTPUT_DIR}
COMMAND ${CMAKE_C_COMPILER}
${CMAKE_C_COMPILER_ARG1}
${threadx_smp_offset_cflags}
${threadx_smp_offset_include_args}
${threadx_smp_offset_define_args}
-S
-o ${threadx_smp_offset_asm}
${THREADX_SMP_OFFSETS_SOURCE}
COMMAND ${CMAKE_COMMAND}
-DINPUT=${threadx_smp_offset_asm}
-DOUTPUT=${threadx_smp_offset_inc}
-P ${threadx_smp_generate_script}
DEPENDS
${THREADX_SMP_OFFSETS_SOURCE}
${threadx_smp_generate_script}
${THREADX_SMP_OFFSETS_DEPENDS}
COMMAND_EXPAND_LISTS
VERBATIM
)
add_custom_target(${THREADX_SMP_OFFSETS_TARGET} DEPENDS ${threadx_smp_offset_inc})
set(${THREADX_SMP_OFFSETS_OUT_INCLUDE_DIR} ${THREADX_SMP_OFFSETS_OUTPUT_DIR} PARENT_SCOPE)
endfunction()

371
port/threadx_smp/inc/csr.h Normal file
View File

@@ -0,0 +1,371 @@
/***************************************************************************
* Copyright (c) 2024 Microsoft Corporation
*
* 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 CSR_H
#define 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

View File

@@ -0,0 +1,343 @@
/***************************************************************************
* Copyright (c) 2024 Microsoft Corporation
*
* 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 ARMv8-A-SMP */
/* 6.1.10 */
/* */
/* AUTHOR */
/* */
/* William E. Lamie, Microsoft Corporation */
/* */
/* 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 */
/* */
/* 09-30-2020 William E. Lamie Initial Version 6.1 */
/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */
/* macro definition, */
/* resulting in version 6.1.10 */
/* */
/**************************************************************************/
#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)
#include "tx_asm_offsets.inc"
#else /*not __ASSEMBLER__ */
/************* Define ThreadX SMP constants. *************/
/* Define the ThreadX SMP maximum number of cores. */
#ifndef TX_THREAD_SMP_MAX_CORES
#define TX_THREAD_SMP_MAX_CORES 4
#endif
/* Define the ThreadX SMP core mask. */
#ifndef TX_THREAD_SMP_CORE_MASK
#define TX_THREAD_SMP_CORE_MASK 0xF /* Where bit 0 represents Core 0, bit 1 represents Core 1, etc. */
#endif
/* Declare ThreadX to discover the cores dynamically. The ceiling is given by TX_THREAD_SMP_MAX_CORES */
/*
The current implementation does not support dynamic discovery, but simply set the runtime determined number to
TX_THREAD_SMP_MAX_CORES. See tx_thread_smp_low_level_initialize.S
#define TX_THREAD_SMP_DYNAMIC_CORE_MAX
*/
/* Define INLINE_DECLARE */
#define INLINE_DECLARE inline
/* Define ThreadX SMP initialization macro. */
#define TX_PORT_SPECIFIC_PRE_INITIALIZATION
/* Define ThreadX SMP pre-scheduler initialization. */
#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION
/* Enable the inter-core interrupt logic. */
#define TX_THREAD_SMP_INTER_CORE_INTERRUPT
/* Use default wakeup logic*/
#define TX_THREAD_SMP_DEFAULT_WAKEUP_LOGIC
/* Determine if there is customer-specific wakeup logic needed. */
#ifdef TX_THREAD_SMP_WAKEUP_LOGIC
/* Include customer-specific wakeup code. */
#include "tx_thread_smp_core_wakeup.h"
#else
#ifdef TX_THREAD_SMP_DEFAULT_WAKEUP_LOGIC
/* Default wakeup code. */
#define TX_THREAD_SMP_WAKEUP_LOGIC
#define TX_THREAD_SMP_WAKEUP(i) _tx_thread_smp_core_preempt(i)
#endif
#endif
/* Ensure that the in-line resume/suspend define is not allowed. */
#ifdef TX_INLINE_THREAD_RESUME_SUSPEND
#undef TX_INLINE_THREAD_RESUME_SUSPEND
#endif
/************* End ThreadX SMP constants. *************/
/* 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. */
#include <stdlib.h>
#include <string.h>
/* 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 int LONG;
typedef unsigned int ULONG;
typedef unsigned long long ULONG64;
typedef short SHORT;
typedef unsigned short USHORT;
#define ULONG64_DEFINED
/* 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 SMP data types and function prototypes. *************/
struct TX_THREAD_STRUCT;
/* Define the ThreadX SMP protection structure. */
typedef struct TX_THREAD_SMP_PROTECT_STRUCT
{
ULONG tx_thread_smp_protect_in_force;
ULONG tx_thread_smp_protect_core;
ULONG tx_thread_smp_protect_count;
ULONG tx_thread_smp_protect_pad_0;
ULONG tx_thread_smp_protect_pad_1;
ULONG tx_thread_smp_protect_pad_2;
ULONG tx_thread_smp_protect_pad_3;
} TX_THREAD_SMP_PROTECT;
/* 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. */
#define TX_INTERRUPT_SAVE_AREA ULONG64 interrupt_save;
#define TX_DISABLE interrupt_save = _tx_thread_smp_protect();
#define TX_RESTORE _tx_thread_smp_unprotect(interrupt_save);
/************* End ThreadX SMP data type and function prototype definitions. *************/
/* 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-V64/GNU Version 6.4.2 *";
#else
extern CHAR _tx_version_id[];
#endif
#endif /*not __ASSEMBLER__ */
#endif

View File

@@ -0,0 +1,12 @@
#include <stddef.h>
#include "tx_api.h"
#define TX_ASM_OFFSET(symbol, value) __asm__ volatile("\n.ascii \"-->" #symbol " %c0\\n\"" : : "i"(value))
void tx_asm_offsets_generate(void)
{
TX_ASM_OFFSET(TX_THREAD_STACK_END_OFFSET, offsetof(TX_THREAD, tx_thread_stack_end));
TX_ASM_OFFSET(TX_THREAD_TIME_SLICE_OFFSET, offsetof(TX_THREAD, tx_thread_time_slice));
TX_ASM_OFFSET(TX_THREAD_SMP_LOCK_READY_BIT_OFFSET, offsetof(TX_THREAD, tx_thread_smp_lock_ready_bit));
}

View File

@@ -0,0 +1,163 @@
/***************************************************************************
* Copyright (c) 2024 Microsoft Corporation
*
* This program and the accompanying materials are made available under the
* terms of the MIT License which is available at
* https://opensource.org/licenses/MIT.
*
* SPDX-License-Identifier: MIT
**************************************************************************/
#include "csr.h"
#include "tx_port.h"
.section .text
.align 4
/**************************************************************************/
/* */
/* FUNCTION RELEASE */
/* */
/* trap_entry RISC-V64/GNU */
/* 6.2.1 */
/* AUTHOR */
/* */
/* Jer6y , luojun@oerv.isrc.iscas.ac.cn */
/* */
/* 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 */
/* RELEASE HISTORY */
/* */
/* DATE NAME DESCRIPTION */
/* */
/* 10-25-2024 Jerry Luo */
/* */
/**************************************************************************/
/**************************************************************************/
/**************************************************************************/
/** */
/** ThreadX Component */
/** */
/** Initialize */
/** */
/**************************************************************************/
/**************************************************************************/
.global trap_entry
.extern _tx_thread_context_restore
trap_entry:
#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
addi sp, sp, -65*REGBYTES // Allocate space for all registers - with floating point enabled
#else
addi sp, sp, -32*REGBYTES // Allocate space for all registers - without floating point enabled
#endif
STORE x1, 28*REGBYTES(sp) // Store RA, 28*REGBYTES(because call will override ra [ra is a calle register in riscv])
call _tx_thread_context_save
csrr a0, mcause
csrr a1, mepc
csrr a2, mtval
addi sp, sp, -8
STORE ra, 0(sp)
call trap_handler
LOAD ra, 0(sp)
addi sp, sp, 8
call _tx_thread_context_restore
// it will nerver return
.weak trap_handler
trap_handler:
1:
j 1b
.section .text
/**************************************************************************/
/* */
/* FUNCTION RELEASE */
/* */
/* _tx_initialize_low_level RISC-V64/GNU */
/* 6.2.1 */
/* AUTHOR */
/* */
/* Scott Larson, Microsoft Corporation */
/* */
/* 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 */
/* */
/* 03-08-2023 Scott Larson Initial Version 6.2.1 */
/* */
/**************************************************************************/
/* VOID _tx_initialize_low_level(VOID)
*/
.global _tx_initialize_low_level
.weak _tx_initialize_low_level
.extern __heap_start
.extern board_init
_tx_initialize_low_level:
STORE sp, _tx_thread_system_stack_ptr, t0 // Save system stack pointer
la t0, __heap_start // Pickup first free address
STORE t0, _tx_initialize_unused_memory, t1 // Save unused memory address
li t0, MSTATUS_MIE
csrrc zero, mstatus, t0 // clear MSTATUS_MIE bit
li t0, (MSTATUS_MPP_M | MSTATUS_MPIE )
csrrs zero, mstatus, t0 // set MSTATUS_MPP, MPIE bit
li t0, (MIE_MTIE | MIE_MSIE | MIE_MEIE)
csrrs zero, mie, t0 // set mie
#ifdef __riscv_flen
li t0, MSTATUS_FS
csrrs zero, mstatus, t0 // set MSTATUS_FS bit to open f/d isa in riscv
fscsr x0
#endif
addi sp, sp, -8
STORE ra, 0(sp)
call board_init
LOAD ra, 0(sp)
addi sp, sp, 8
la t0, trap_entry
csrw mtvec, t0
ret

View File

@@ -0,0 +1,402 @@
/***************************************************************************
* Copyright (c) 2024 Microsoft Corporation
*
* 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-V64/GNU */
/* 6.2.1 */
/* AUTHOR */
/* */
/* Scott Larson, Microsoft Corporation */
/* */
/* 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 */
/* */
/* 03-08-2023 Scott Larson Initial Version 6.2.1 */
/* */
/**************************************************************************/
/* 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)
{ */
csrr t3, mhartid // Pickup current hart ID
slli t4, t3, 2 // Build per-hart ULONG offset
slli t5, t3, LOG_REGBYTES // Build per-hart pointer offset
la t0, _tx_thread_system_state // Pickup base of system-state array
add t0, t0, t4 // Select this hart's system-state slot
lw t1, 0(t0) // Pickup nested interrupt count
addi t1, t1, -1 // Decrement the nested interrupt counter
sw t1, 0(t0) // Store new nested count
beqz t1, _tx_thread_not_nested_restore // If 0, not nested restore
/* Interrupts are nested. */
/* Just recover the saved registers and return to the point of
interrupt. */
/* Recover floating point registers. */
#if defined(__riscv_float_abi_single)
flw f0, 31*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))
{ */
la t0, _tx_thread_current_ptr // Pickup base of current-thread array
add t0, t0, t5 // Select this hart's current-thread slot
LOAD t1, 0(t0) // Pickup current thread pointer
beqz t1, _tx_thread_idle_system_restore // If NULL, idle system restore
la t0, _tx_thread_execute_ptr // Pickup base of execute-thread array
add t0, t0, t5 // Select this hart's execute-thread slot
LOAD t2, 0(t0) // Pickup thread execute pointer
beq t1, t2, _tx_thread_no_preempt_restore // Same thread selected, no preemption
la t0, _tx_thread_smp_protection // Pickup protection structure
lw t2, 4(t0) // Pickup owning hart
bne t2, t3, _tx_thread_preempt_restore // If owned by another hart, preempt
LOAD t2, _tx_thread_preempt_disable // Pickup preempt disable flag
bgtz t2, _tx_thread_no_preempt_restore // If set, restore interrupted thread
_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 base of time-slice array
add t0, t0, t4 // Select this hart's time-slice slot
lw t2, 0(t0) // Pickup time slice
beqz t2, _tx_thread_dont_save_ts // If 0, skip time slice processing
/* _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice
_tx_timer_time_slice = 0; */
sw t2, TX_THREAD_TIME_SLICE_OFFSET(t1) // Save current time slice
sw x0, 0(t0) // Clear global time slice
/* } */
_tx_thread_dont_save_ts:
/* Clear the current task pointer. */
/* _tx_thread_current_ptr = TX_NULL; */
/* Return to the scheduler. */
/* _tx_thread_schedule(); */
la t0, _tx_thread_current_ptr // Pickup base of current-thread array
add t0, t0, t5 // Select this hart's current-thread slot
STORE x0, 0(t0) // Clear current thread pointer
fence rw, rw // Publish current-thread clear before ready token
addi t0, t1, TX_THREAD_SMP_LOCK_READY_BIT_OFFSET // Pickup lock/ready-bit address
li t2, 1 // Rebuild ready token
amoswap.w.rl x0, t2, (t0) // Set thread ready token for reschedule
/* } */
_tx_thread_idle_system_restore:
/* Just return back to the scheduler! */
j _tx_thread_schedule // Return to scheduler
/* } */

View File

@@ -0,0 +1,289 @@
/***************************************************************************
* Copyright (c) 2024 Microsoft Corporation
*
* 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-V64/GNU */
/* 6.2.1 */
/* AUTHOR */
/* */
/* Scott Larson, Microsoft Corporation */
/* */
/* 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 */
/* */
/* 03-08-2023 Scott Larson Initial Version 6.2.1 */
/* */
/**************************************************************************/
/* 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 t0, 19*REGBYTES(sp) // Save t0
STORE t1, 18*REGBYTES(sp) // Save t1
STORE x7, 17*REGBYTES(sp) // Save t2 before reusing it
STORE x28, 16*REGBYTES(sp) // Save t3 before reusing it
STORE x29, 15*REGBYTES(sp) // Save t4 before reusing it
csrr t2, mhartid // Pickup current hart ID
slli t3, t2, 2 // Build per-hart ULONG offset
slli t4, t2, LOG_REGBYTES // Build per-hart pointer offset
la t1, _tx_thread_system_state // Pickup base of system state array
add t0, t1, t3 // Select this hart's system-state slot
lw t1, 0(t0) // Pickup system state
/* Check for a nested interrupt condition. */
/* if (_tx_thread_system_state++)
{ */
beqz t1, _tx_thread_not_nested_save // If 0, first interrupt condition
addi t1, t1, 1 // Increment the interrupt counter
sw t1, 0(t0) // Store the interrupt counter
/* Nested interrupt condition.
Save the reset of the scratch registers on the stack and return to the
calling ISR. */
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 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 t1, t1, 1 // Increment the interrupt counter
sw t1, 0(t0) // Store the interrupt counter
/* Not nested: Find the user thread that was running and load our SP */
la t1, _tx_thread_current_ptr // Pickup base of current-thread array
add t0, t1, t4 // Select this hart's current-thread slot
LOAD t0, 0(t0) // Pickup current thread pointer
beqz t0, _tx_thread_idle_system_save // If NULL, idle system was interrupted
/* Save the standard scratch registers. */
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 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; */
la t0, _tx_thread_current_ptr // Pickup base of current-thread array
add t0, t0, t4 // Select this hart's current-thread slot
LOAD t1, 0(t0) // 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
la t0, _tx_thread_system_stack_ptr // Pickup base of system-stack array
add t0, t0, t4 // Select this hart's system-stack slot
LOAD sp, 0(t0) // 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

View File

@@ -0,0 +1,337 @@
/***************************************************************************
* Copyright (c) 2024 Microsoft Corporation
*
* 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-V64/GNU */
/* 6.2.1 */
/* AUTHOR */
/* */
/* Scott Larson, Microsoft Corporation */
/* */
/* 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 */
/* */
/* 03-08-2023 Scott Larson Initial Version 6.2.1 */
/* */
/**************************************************************************/
/* VOID _tx_thread_schedule(VOID)
{ */
.global _tx_thread_schedule
_tx_thread_schedule:
/* Enable interrupts. */
csrsi mstatus, 0x08 // Enable interrupts
csrr t4, mhartid // Pickup the current hart ID
slli t4, t4, LOG_REGBYTES // Build per-hart byte offset
/* Wait for a thread to execute. */
/* do
{ */
la t0, _tx_thread_execute_ptr // Pickup address of execute ptr
add t0, t0, t4 // Select this hart's execute slot
_tx_thread_schedule_loop:
#ifdef TX_ENABLE_WFI
csrci mstatus, 0x08 // Lockout interrupts
LOAD t1, 0(t0) // Pickup next thread to execute
bnez t1, _tx_thread_schedule_thread // If non-NULL, continue
csrsi mstatus, 0x08 // Enable interrupts
wfi // Wait for interrupt
j _tx_thread_schedule_loop // Keep looking for a thread
#else
csrci mstatus, 0x08 // Lockout interrupts
LOAD t1, 0(t0) // Pickup next thread to execute
bnez t1, _tx_thread_schedule_thread // If non-NULL, continue
csrsi mstatus, 0x08 // Enable interrupts
j _tx_thread_schedule_loop // Keep looking for a thread
#endif
_tx_thread_schedule_thread:
/* Atomically claim the thread's ready token so only one hart can
dispatch this TCB at a time. */
addi t2, t1, TX_THREAD_SMP_LOCK_READY_BIT_OFFSET // Pickup lock/ready-bit address
amoswap.w.aq t3, x0, (t2) // Clear it and fetch prior state
beqz t3, _tx_thread_schedule // If not ready, retry scheduling
/* }
while(_tx_thread_execute_ptr == TX_NULL); */
/* Publish the current thread pointer for this hart, then re-check the
execute slot to avoid missing a concurrent execute-pointer update. */
la t5, _tx_thread_current_ptr // Pickup current thread pointer address
add t5, t5, t4 // Select this hart's current slot
STORE t1, 0(t5) // Set current thread pointer
fence rw, rw // Publish current thread pointer before re-check
LOAD t6, 0(t0) // Reload execute pointer for this hart
beq t1, t6, _execute_pointer_did_not_change // If unchanged, continue
/* Another core changed the execute slot after this hart claimed the
thread but before current-thread publication completed. Roll back
and restart so the new selection is not missed. */
STORE x0, 0(t5) // Clear current thread pointer
li t3, 1 // Rebuild ready token
amoswap.w.rl x0, t3, (t2) // Restore ready token with release ordering
j _tx_thread_schedule_loop // Restart scheduling
_execute_pointer_did_not_change:
/* 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 for this hart, if present. */
la t2, _tx_timer_time_slice // Pickup time-slice variable address
add t2, t2, t4 // Select this hart's time-slice slot
/* 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
/* } */

View File

@@ -0,0 +1,12 @@
#include "tx_port.h"
.section .text
.align 2
.global _tx_thread_smp_core_get
.type _tx_thread_smp_core_get, @function
_tx_thread_smp_core_get:
csrr a0, mhartid // Pickup the current hart ID
ret

View File

@@ -0,0 +1,6 @@
#include <aclint_ipi.h>
#include <tx_port.h>
void _tx_thread_smp_core_preempt(UINT target_core)
{
send_ipi(target_core);
}

View File

@@ -0,0 +1,14 @@
#include "tx_port.h"
.section .text
.align 2
.global _tx_thread_smp_current_state_get
.type _tx_thread_smp_current_state_get, @function
_tx_thread_smp_current_state_get:
csrr t0, mhartid // Pickup current hart ID
la t1, _tx_thread_system_state // Base of per-hart system-state array
slli t0, t0, 2 // Build offset into array
add t1, t1, t0 // Select this hart's slot
LWU a0, 0(t1) // Return current system state
ret

View File

@@ -0,0 +1,19 @@
#include "tx_port.h"
.section .text
.align 2
.global _tx_thread_smp_current_thread_get
.type _tx_thread_smp_current_thread_get, @function
_tx_thread_smp_current_thread_get:
csrr t0, mstatus // Pickup current interrupt posture
csrci mstatus, 0x08 // Lockout interrupts
csrr t1, mhartid // Pickup current hart ID
la t2, _tx_thread_current_ptr // Base of current-thread pointer array
slli t1, t1, LOG_REGBYTES // Build per-hart byte offset
add t2, t2, t1 // Select this hart's slot
LOAD a0, 0(t2) // Pickup current thread pointer
csrw mstatus, t0 // Restore interrupt posture
ret

View File

@@ -0,0 +1,71 @@
#include "tx_port.h"
#include "csr.h"
.section .text
.align 2
.global _tx_thread_smp_initialize_wait
.type _tx_thread_smp_initialize_wait, @function
.extern _tx_thread_schedule
_tx_thread_smp_initialize_wait:
/* Lockout interrupts while startup synchronization is in progress. */
csrci mstatus, 0x08 // Lockout interrupts
/* Pickup current hart ID. */
csrr t0, mhartid // Pickup current hart ID
beqz t0, _tx_thread_smp_initialize_done // Core 0 does not wait
/* Build per-hart offsets for ULONG and pointer arrays. */
slli t1, t0, 2 // ULONG array offset
slli t2, t0, LOG_REGBYTES // Pointer array offset
/* Wait until ThreadX has acknowledged this hart by setting its
system state to TX_INITIALIZE_IN_PROGRESS. */
li t3, 0xF0F0F0F0 // TX_INITIALIZE_IN_PROGRESS
la t4, _tx_thread_system_state // Base of system state array
add t5, t4, t1 // This hart's system state slot
_tx_thread_smp_wait_for_initialize:
LWU t6, 0(t5) // Pickup current hart's system state
bne t6, t3, _tx_thread_smp_wait_for_initialize
/* Save the system stack pointer for this hart. */
la t3, _tx_thread_system_stack_ptr // Base of system stack pointer array
add t3, t3, t2 // Select this hart's slot
STORE sp, 0(t3) // Save system stack pointer
/* Wait for core 0 to release the secondary harts. */
la t3, _tx_thread_smp_release_cores_flag // Release flag address
_tx_thread_smp_wait_for_release:
LWU t6, 0(t3) // Pickup release flag
beqz t6, _tx_thread_smp_wait_for_release
/* Acknowledge the release by clearing this hart's system state. */
sw x0, 0(t5) // Set this hart's system state to zero
/* Wait for core 0 to finish initialization. */
_tx_thread_smp_wait_for_core0:
LWU t6, 0(t4) // Pickup core 0 system state
bnez t6, _tx_thread_smp_wait_for_core0
/* Prepare interrupt state */
li t0, MSTATUS_MIE
csrrc zero, mstatus, t0 // clear MSTATUS_MIE bit
li t0, (MSTATUS_MPP_M | MSTATUS_MPIE )
csrrs zero, mstatus, t0 // set MSTATUS_MPP, MPIE bit
li t0, (MIE_MTIE | MIE_MSIE | MIE_MEIE)
csrrs zero, mie, t0 // set mie
#ifdef __riscv_flen
li t0, MSTATUS_FS
csrrs zero, mstatus, t0 // set MSTATUS_FS bit to open f/d isa in riscv
fscsr x0
#endif
la t0, trap_entry
csrw mtvec, t0
/* Initialization is complete for this hart, enter the scheduler. */
j _tx_thread_schedule
_tx_thread_smp_initialize_done:
ret

View File

@@ -0,0 +1,9 @@
#include "tx_port.h"
.section .text
.align 2
.global _tx_thread_smp_low_level_initialize
.type _tx_thread_smp_low_level_initialize, @function
_tx_thread_smp_low_level_initialize:
ret

View File

@@ -0,0 +1,50 @@
#include "tx_port.h"
.section .text
.align 2
.global _tx_thread_smp_protect
.type _tx_thread_smp_protect, @function
_tx_thread_smp_protect:
/* Disable interrupts so we don't get preempted. */
csrr a0, mstatus // Pickup current interrupt posture
csrci mstatus, 0x08 // Lockout interrupts
/* Pickup the hart ID. */
csrr t2, mhartid // Pickup the current hart ID
/* Build address to protection structure. */
la t1, _tx_thread_smp_protection
/* If this hart already owns protection, just nest the count. */
LWU t3, 4(t1) // Pickup owning hart
beq t3, t2, _owned // Already owned by this hart
/* Try to get the protection. */
LWU t4, 0(t1) // Pickup protection flag
beqz t4, _get_protection // If clear, try to claim it
/* Protection is busy. Restore interrupts and retry. */
csrw mstatus, a0 // Restore interrupts
j _tx_thread_smp_protect // Restart the protection attempt
_get_protection:
li t4, 1 // Build lock value
amoswap.w.aq t5, t4, (t1) // Attempt to get protection
bnez t5, _protection_busy // If old value != 0, retry
_got_protection:
fence rw, rw // Ensure lock acquisition is visible
sw t2, 4(t1) // Save owning hart
_owned:
LWU t5, 8(t1) // Pickup ownership count
addi t5, t5, 1 // Increment ownership count
sw t5, 8(t1) // Store ownership count
fence rw, rw // Publish owner/count before return
ret
_protection_busy:
csrw mstatus, a0 // Restore interrupts
j _tx_thread_smp_protect // Restart the protection attempt

View File

@@ -0,0 +1,43 @@
#include "tx_port.h"
.section .text
.align 2
.global _tx_thread_smp_unprotect
.type _tx_thread_smp_unprotect, @function
_tx_thread_smp_unprotect:
/* Lockout interrupts while protection state is updated. */
csrci mstatus, 0x08 // Lockout interrupts
/* Pickup the current hart ID. */
csrr t1, mhartid // Pickup hart ID
/* Build address of protection structure. */
la t2, _tx_thread_smp_protection
/* Only the owning hart may release the protection. */
LWU t3, 4(t2) // Pickup owning hart
bne t1, t3, _still_protected // Not owner, skip release
/* Pickup and decrement the protection count. */
LWU t3, 8(t2) // Pickup protection count
beqz t3, _still_protected // Already cleared
addi t3, t3, -1 // Decrement protection count
sw t3, 8(t2) // Store new count
bnez t3, _still_protected // Still nested, stay protected
/* If preemption is disabled, keep protection in force. */
la t4, _tx_thread_preempt_disable
LWU t5, 0(t4) // Pickup preempt disable
bnez t5, _still_protected // Skip protection release
/* Release the protection. */
li t3, -1 // Invalid owner value
sw t3, 4(t2) // Mark owning hart invalid
fence rw, rw // Ensure shared accesses complete
amoswap.w.rl x0, x0, (t2) // Release protection flag
_still_protected:
csrw mstatus, a0 // Restore interrupt posture
ret

View File

@@ -0,0 +1,229 @@
/***************************************************************************
* Copyright (c) 2024 Microsoft Corporation
*
* 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-V64/GNU */
/* 6.2.1 */
/* AUTHOR */
/* */
/* Scott Larson, Microsoft Corporation */
/* */
/* 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 */
/* */
/* 03-08-2023 Scott Larson Initial Version 6.2.1 */
/* */
/**************************************************************************/
/* 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, TX_THREAD_STACK_END_OFFSET(a0) // Pickup end of stack area
andi t0, t0, -4*REGBYTES // Ensure alignment (16-byte for RV32 & 32-byte for RV64)
/* 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
addi t1, x0, 1 // Build ready flag
sw t1, TX_THREAD_SMP_LOCK_READY_BIT_OFFSET(a0) // Set ready flag
ret // control block and return
/* } */

View File

@@ -0,0 +1,198 @@
/***************************************************************************
* Copyright (c) 2024 Microsoft Corporation
*
* 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-V64/GNU */
/* 6.2.1 */
/* AUTHOR */
/* */
/* Scott Larson, Microsoft Corporation */
/* */
/* 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 */
/* */
/* 03-08-2023 Scott Larson Initial Version 6.2.1 */
/* */
/**************************************************************************/
/* 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
csrr t5, mhartid // Pickup current hart ID
slli t6, t5, 2 // Build per-hart ULONG offset
slli t5, t5, LOG_REGBYTES // Build per-hart pointer offset
la t0, _tx_thread_current_ptr // Pickup base of current-thread array
add t0, t0, t5 // Select this hart's current-thread slot
LOAD t1, 0(t0) // Pickup current thread pointer
la t2, _tx_thread_system_stack_ptr // Pickup base of system-stack array
/* 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
add t2, t2, t5 // Select this hart's system-stack slot
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 base of time-slice array
add t4, t4, t6 // Select this hart's time-slice slot
lw t3, 0(t4) // Pickup time slice value
la t2, _tx_thread_schedule // Pickup address of scheduling loop
beqz t3, _tx_thread_dont_save_ts // If no time-slice, don't save it
/* Save time-slice for the thread and clear the current time-slice. */
/* _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice;
_tx_timer_time_slice = 0; */
sw t3, TX_THREAD_TIME_SLICE_OFFSET(t1) // Save current time-slice for thread
sw 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
/* Make the thread runnable again before returning to the scheduler. */
fence rw, rw // Publish current-thread clear before ready token
addi t3, t1, TX_THREAD_SMP_LOCK_READY_BIT_OFFSET // Pickup lock/ready-bit address
li t4, 1 // Build ready token
amoswap.w.rl x0, t4, (t3) // Restore ready token
/* Clear protection state. */
la t3, _tx_thread_preempt_disable // Pickup preempt-disable address
sw x0, 0(t3) // Clear preempt disable flag
la t3, _tx_thread_smp_protection // Pickup protection structure
sw x0, 8(t3) // Clear protection count
li t4, -1 // Build invalid owner value
sw t4, 4(t3) // Invalidate owning hart
fence rw, rw // Ensure shared accesses complete before unlock
sw x0, 0(t3) // Clear protection in-force flag
jr t2 // Return to thread scheduler
/* } */

View File

@@ -0,0 +1,129 @@
/***************************************************************************
* Copyright (c) 2024 Microsoft Corporation
*
* 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
#define TX_THREAD_SMP_SOURCE_CODE
/* Include necessary system files. */
#include "tx_api.h"
#include "tx_thread.h"
#include "tx_timer.h"
/**************************************************************************/
/* */
/* FUNCTION RELEASE */
/* */
/* _tx_timer_interrupt RISC-V64/GNU */
/* 6.2.1 */
/* AUTHOR */
/* */
/* Scott Larson, Microsoft Corporation */
/* */
/* 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 */
/* */
/* 03-08-2023 Scott Larson Initial Version 6.2.1 */
/* */
/**************************************************************************/
VOID _tx_timer_interrupt(VOID)
{
UINT saved_posture;
/* Only core 0 advances the global timer wheel. */
if (TX_SMP_CORE_ID != ((UINT) 0))
{
return;
}
/* Get the protection. */
saved_posture = _tx_thread_smp_protect();
/* Indicate timer interrupt processing is active. */
_tx_timer_interrupt_active++;
/* Increment system clock. */
_tx_timer_system_clock++;
/* 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;
}
}
/* Did a timer expire? */
if (_tx_timer_expired)
{
/* Process timer expiration. */
_tx_timer_expiration_process();
}
/* Process time-slice expiration for all cores. */
_tx_thread_time_slice();
/* Timer interrupt processing is no longer active. */
_tx_timer_interrupt_active--;
/* Release the protection. */
_tx_thread_smp_unprotect(saved_posture);
}