diff --git a/.gitmodules b/.gitmodules index 0fd4ff1..cd8f876 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "third-party/threadx"] path = third-party/threadx - url = https://github.com/eclipse-threadx/threadx.git + url = https://github.com/Minres/threadx.git [submodule "third-party/netxduo"] path = third-party/netxduo url = https://github.com/eclipse-threadx/netxduo.git diff --git a/CMakeLists.txt b/CMakeLists.txt index f0fcd06..80b54cb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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,69 +33,58 @@ 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") +add_subdirectory(port/moonlight) + function(setup_target TARGET) set(options) - set(oneValueArgs) # none for now + set(oneValueArgs PLATFORM_TARGET) set(multiValueArgs LIBRARIES SOURCES) cmake_parse_arguments(ST "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) if(ST_UNPARSED_ARGUMENTS) - message(FATAL_ERROR "setup_target(${target} ...): unknown args: ${ST_UNPARSED_ARGUMENTS}") + message(FATAL_ERROR "setup_target(${TARGET} ...): unknown args: ${ST_UNPARSED_ARGUMENTS}") 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 + set_target_properties(${TARGET} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR} ) + if(ST_PLATFORM_TARGET) + target_add_moonlight_platform(${TARGET} PLATFORM_TARGET ${ST_PLATFORM_TARGET}) + else() + target_add_moonlight_platform(${TARGET}) + endif() + if("netxduo" IN_LIST ST_LIBRARIES) - target_sources(${TARGET} PRIVATE port/moonlight/mnrs_network_driver.c) + target_add_moonlight_network_driver(${TARGET}) endif() if(ST_SOURCES) target_sources(${TARGET} PRIVATE ${ST_SOURCES}) endif() - target_include_directories(${TARGET} PRIVATE port/moonlight src) - target_compile_options(${TARGET} PRIVATE - -ffreestanding - -fno-builtin - -fdata-sections - -ffunction-sections - ) - if(NX_DEBUG) - target_compile_definitions(${TARGET} PRIVATE NX_DEBUG NX_DEBUG_PACKET) + if(ST_LIBRARIES) + target_link_libraries(${TARGET} PRIVATE ${ST_LIBRARIES}) endif() - target_link_directories(${TARGET} PRIVATE src) # needed for linker script includes + target_link_options(${TARGET} PRIVATE -nostartfiles -nostdlib -T ${CMAKE_SOURCE_DIR}/src/${TARGET_MEM}.lds -Wl,--gc-sections - -Wl,-Map=${CMAKE_BINARY_DIR}/${TARGET}.map - ) - if(ST_LIBRARIES) - target_link_libraries(${TARGET} PRIVATE ${ST_LIBRARIES}) - endif() - target_link_libraries(${TARGET} PRIVATE threadx) + -Wl,-Map=${CMAKE_BINARY_DIR}/${TARGET}.map) add_custom_command(TARGET ${TARGET} POST_BUILD - COMMAND ${OBJCOPY} -O ihex $ ${CMAKE_BINARY_DIR}/${TARGET}.hex + COMMAND ${OBJCOPY} -O ihex $ ${CMAKE_BINARY_DIR}/${TARGET}.hex COMMAND ${OBJCOPY} -O binary $ ${CMAKE_BINARY_DIR}/${TARGET}.bin COMMAND ${SIZE} $ - COMMAND ${OBJDUMP} -S $ > ${TARGET}.dis + COMMAND ${OBJDUMP} -S $ > ${CMAKE_BINARY_DIR}/${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 PLATFORM_TARGET moonlight_platform_common_smp LIBRARIES threadx_smp SOURCES src/thread_demo/main.c) diff --git a/CMakePresets.json b/CMakePresets.json index ac9f2c7..c84eddd 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -63,5 +63,31 @@ "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" } } + ], + "buildPresets": [ + { + "name": "Debug", + "configurePreset": "Debug" + }, + { + "name": "Debug32", + "configurePreset": "Debug32" + }, + { + "name": "Release", + "configurePreset": "Release" + }, + { + "name": "Release32", + "configurePreset": "Release32" + }, + { + "name": "MinSizeRel", + "configurePreset": "MinSizeRel" + }, + { + "name": "MinSizeRel32", + "configurePreset": "MinSizeRel32" + } ] } \ No newline at end of file diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..d83ddde --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,158 @@ +def suites = ['threadx', 'smp'] +def presets = ['Debug32', 'Release32', 'MinSizeRel32','Debug', 'Release', 'MinSizeRel'] +def simulatorImageLatest = 'git.minres.com/here/here-vp:latest' +def threadxTestSimulator = '/usr/local/bin/riscv-vp' + +def runOneRegression(String suite, String preset, String simulatorPath) { + def suiteDir = "test/${suite}" + def buildDir = "../../build/${preset}/test/${suite}" + + stage("${suite}-${preset}") { + dir(suiteDir) { + sh """ + set -eu + test -n "${simulatorPath}" + test -x "${simulatorPath}" + cmake --fresh --preset "${preset}" \\ + -DTHREADX_TEST_SIMULATOR="${simulatorPath}" + cmake --build --preset "${preset}" --parallel "\$(nproc)" + ctest \\ + --test-dir "${buildDir}" \\ + --output-on-failure \\ + --output-junit "${buildDir}/ctest-results.xml" \\ + --parallel "4" + """ + } + } +} + +def resolveImageCommit(String image) { + + withCredentials([usernamePassword( + credentialsId: 'gitea-jenkins', + usernameVariable: 'REGISTRY_USER', + passwordVariable: 'REGISTRY_PASS' + )]) { + return sh( + script: """ + set -eu + echo "\$REGISTRY_PASS" | docker login "git.minres.com" -u "\$REGISTRY_USER" --password-stdin >/dev/null + docker pull "${image}" >/dev/null + docker image inspect --format='{{ index .Config.Labels "git-commit" }}' "${image}" + """, + returnStdout: true + ).trim() + } +} + +def runRegressionLane(String image, String simulatorPath, boolean allowFailure, List suiteNames, List presetNames) { + docker.image(image).inside { + sh ''' + set -eu + cmake --version + ctest --version + ''' + + for (String suite : suiteNames) { + def suiteName = suite + def parallelTasks = [:] + + for (String preset : presetNames) { + def presetName = preset + def taskName = "${suiteName}-${presetName}" + parallelTasks[taskName] = { + if (allowFailure) { + catchError(buildResult: 'UNSTABLE', stageResult: 'UNSTABLE') { + runOneRegression(suiteName, presetName, simulatorPath) + } + } else { + runOneRegression(suiteName, presetName, simulatorPath) + } + } + } + + stage("${suiteName} Suite") { + parallel parallelTasks + } + } + } +} + +properties([ + parameters([ + string( + name: 'SIMULATOR_IMAGE_PINNED', + defaultValue: 'git.minres.com/here/here-vp:4fcc192', + description: 'Version-pinned Docker image used for the blocking regression lane' + ) + ]) +]) + +def canaryShouldRun = true + +node { + timestamps { + try { + stage('Checkout') { + checkout scm + sh ''' + git submodule sync --recursive + git submodule update --init --recursive + ''' + } + + stage('Check Canary Divergence') { + def pinnedCommit = resolveImageCommit(params.SIMULATOR_IMAGE_PINNED) + def latestCommit = resolveImageCommit(simulatorImageLatest) + + if (!pinnedCommit) { + error "Missing git-commit label on ${params.SIMULATOR_IMAGE_PINNED}" + } + if (!latestCommit) { + error "Missing git-commit label on ${simulatorImageLatest}" + } + + canaryShouldRun = (pinnedCommit != latestCommit) + + if (canaryShouldRun) { + echo "Canary enabled: ${simulatorImageLatest} (${latestCommit}) diverges from ${params.SIMULATOR_IMAGE_PINNED} (${pinnedCommit})" + } else { + echo "Canary skipped: ${simulatorImageLatest} and ${params.SIMULATOR_IMAGE_PINNED} both use git-commit ${pinnedCommit}" + } + } + + stage('Stable Regression') { + runRegressionLane(params.SIMULATOR_IMAGE_PINNED, threadxTestSimulator, false, suites, presets) + } + + stage('Simulator Canary') { + if (canaryShouldRun) { + runRegressionLane(simulatorImageLatest, threadxTestSimulator, true, suites, presets) + } else { + echo 'Skipping canary lane because latest and pinned images are identical.' + } + } + } catch (err) { + currentBuild.result = 'FAILURE' + throw err + } finally { + junit allowEmptyResults: true, testResults: 'build/*/test/*/ctest-results.xml' + archiveArtifacts artifacts: 'build/*/test/*/Testing/**', allowEmptyArchive: true + + if (currentBuild.currentResult == 'SUCCESS') { + rocketSend ":thumbsup: ThreadX regression run passed, results at ${env.RUN_DISPLAY_URL} " + } else if (currentBuild.currentResult == 'UNSTABLE') { + rocketSend ":warning: ThreadX canary regression is unstable on ${simulatorImageLatest}, please check ${env.RUN_DISPLAY_URL} " + } else if (currentBuild.currentResult == 'FAILURE') { + archiveArtifacts artifacts: 'failed_seeds_*.txt', followSymlinks: false, onlyIfSuccessful: false + rocketSend ":thumbsdown: ThreadX regression failed, please check ${env.RUN_DISPLAY_URL} " + emailext recipientProviders: [culprits(), requestor()], + subject: "ThreadX Pipeline Failed: ${currentBuild.fullDisplayName}", + body: """ +

Build Status: ${currentBuild.currentResult}

+

Check logs at Build Console Logs or at Overview

+ """ + } + } + } +} diff --git a/README.md b/README.md index c8b70d9..985b848 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,46 @@ -# Running the RTOS +# ThreadX Port for the TGFS cores and accompanying Virtual prototypes -This repositiory contains prototypical RISC-V implementation of ThreadX designed to run on the RISC-V VP by MINRES (tested on commit [d96cd4a](https://github.com/Minres/RISCV-VP/tree/d96cd4a01de1f4bcc77db3eceb57f5c144395847)). +This repositiory contains a RISC-V implementation of ThreadX designed to run on the TGFS cores or the RISC-V VP by MINRES (available on [Github](https://github.com/Minres/RISCV-VP/tree/d96cd4a01de1f4bcc77db3eceb57f5c144395847)). + +**Note**: SMP support additionally requires a VP variant that allows the number of cores to be configured. ## Building the RTOS -Four presets are provided, targetting RV32 and RV64 combined with GC or IMAC. The presets use rv32imac and rv64imac. +A set of presets is provided, targetting 32 and 64-bit systems. + +E.g. building for the RV64IMAC configuration: + +```bash +cmake --preset Debug +cmake --build --preset Debug --parallel +``` ## Running on the VP A run command can look like this: ```bash -riscv-vp --isa=rv64gc_m -f build/Debug/main.elf +riscv-vp --isa=rv64imac_m -f build/Debug/thread_demo.elf ``` -## What is running? +## Running the Regression Suites -The current implementation is just a demo, taken from the existing threadx qemu implementation. +The regression suites live in test/. Both suites register their cases with CTest and require the path to the MINRES `riscv-vp` simulator to be provided through `THREADX_TEST_SIMULATOR`. + +The SMP regression suite additionally requires a `riscv-vp` variant that supports a configurable number of cores. + +Examples: + +```bash +cmake -S test/threadx --preset Debug32 -DTHREADX_TEST_SIMULATOR=/opt/riscv-vp/bin/riscv-vp +cmake --build --preset Debug32 --parallel +ctest --test-dir ../../build/Debug32/test/threadx --output-on-failure --parallel 4 +``` + +## Demo Applications + +The repository currently provides three demo applications: + +- `thread_demo`: a basic single-kernel ThreadX demo based on the standard ThreadX sample application +- `smp_demo`: the same basic demo application for running on a ThreadX SMP kernel +- `tcp_demo`: a NetX Duo demo that exercises the TCP/IP stack on top of ThreadX diff --git a/port/moonlight/CMakeLists.txt b/port/moonlight/CMakeLists.txt new file mode 100644 index 0000000..f56fa22 --- /dev/null +++ b/port/moonlight/CMakeLists.txt @@ -0,0 +1,91 @@ +set(MOONLIGHT_ROOT ${CMAKE_CURRENT_LIST_DIR}) +set(THREADX4TGFS_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../..) +set(MOONLIGHT_SRC_DIR ${MOONLIGHT_ROOT}/src) +set(MOONLIGHT_INC_DIR ${MOONLIGHT_ROOT}/inc) + +if(NOT DEFINED MOONLIGHT_TRAP_SOURCE) # Hook to use vectored traps, also used in regression testing + set(MOONLIGHT_TRAP_SOURCE ${MOONLIGHT_SRC_DIR}/trap_non_vectored.c) +endif() + +set(MOONLIGHT_PLATFORM_SOURCES + ${THREADX4TGFS_ROOT}/port/picolibc/port.c + ${MOONLIGHT_SRC_DIR}/bootup.c + ${MOONLIGHT_SRC_DIR}/board.c + ${MOONLIGHT_TRAP_SOURCE} + ${MOONLIGHT_SRC_DIR}/exception.c + ${MOONLIGHT_SRC_DIR}/vector_table.c) + +# define a basic library containing all generic board components +add_library(moonlight_platform_defaults INTERFACE) +target_include_directories(moonlight_platform_defaults INTERFACE + ${MOONLIGHT_INC_DIR} + ${THREADX4TGFS_ROOT}/src) +target_compile_options(moonlight_platform_defaults INTERFACE + -ffreestanding + -fno-builtin + -fdata-sections + -ffunction-sections) +target_link_directories(moonlight_platform_defaults INTERFACE + ${THREADX4TGFS_ROOT}/src) +if(NX_DEBUG) + target_compile_definitions(moonlight_platform_defaults INTERFACE + NX_DEBUG + NX_DEBUG_PACKET) +endif() + +# helper to tie in different threadx runtimes +function(moonlight_define_platform TARGET THREADX_TARGET) + add_library(${TARGET} OBJECT + ${MOONLIGHT_PLATFORM_SOURCES}) + target_link_libraries(${TARGET} PUBLIC + moonlight_platform_defaults + c + ${THREADX_TARGET}) +endfunction() + +if(TARGET threadx) + moonlight_define_platform(moonlight_platform_common threadx) +endif() + +if(TARGET threadx_smp) + moonlight_define_platform(moonlight_platform_common_smp threadx_smp) +endif() + +if(DEFINED NETXDUO_CUSTOM_PORT) + add_library(moonlight_network_driver OBJECT + ${MOONLIGHT_SRC_DIR}/mnrs_network_driver.c) + target_link_libraries(moonlight_network_driver PUBLIC + moonlight_platform_defaults + c + threadx + netxduo) +endif() + +# helper for consumers to inject moonlight as a dependency into a target +function(target_add_moonlight_platform TARGET) + set(options) + set(oneValueArgs PLATFORM_TARGET) + cmake_parse_arguments(TAMP "${options}" "${oneValueArgs}" "" ${ARGN}) + + if(TAMP_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "target_add_moonlight_platform(${TARGET} ...): unknown args: ${TAMP_UNPARSED_ARGUMENTS}") + endif() + + if(NOT TAMP_PLATFORM_TARGET) + if(NOT TARGET moonlight_platform_common) + message(FATAL_ERROR "moonlight_platform_common is not available in this build; pass PLATFORM_TARGET explicitly") + endif() + set(TAMP_PLATFORM_TARGET moonlight_platform_common) + endif() + + target_link_libraries(${TARGET} PRIVATE ${TAMP_PLATFORM_TARGET}) +endfunction() + +# helper to inject the network driver into a target +function(target_add_moonlight_network_driver TARGET) + if(NOT TARGET moonlight_network_driver) + message(FATAL_ERROR "moonlight_network_driver requires netxduo to be available in this build") + endif() + + target_link_libraries(${TARGET} PRIVATE moonlight_network_driver) +endfunction() diff --git a/port/moonlight/aclint.h b/port/moonlight/inc/aclint.h similarity index 100% rename from port/moonlight/aclint.h rename to port/moonlight/inc/aclint.h diff --git a/port/moonlight/inc/aclint_ipi.h b/port/moonlight/inc/aclint_ipi.h new file mode 100644 index 0000000..bed9eb6 --- /dev/null +++ b/port/moonlight/inc/aclint_ipi.h @@ -0,0 +1,12 @@ +#ifndef _DEVICES_ACLINT_IPI +#define _DEVICES_ACLINT_IPI + +#include "gen/aclint.h" +#include "platform.h" +#include + +static void send_ipi(uint32_t target_core) +{ + set_aclint_msip(aclint, target_core, 1); +} +#endif /* _DEVICES_ACLINT_IPI */ diff --git a/port/moonlight/inc/csr.h b/port/moonlight/inc/csr.h new file mode 100644 index 0000000..3b64b4f --- /dev/null +++ b/port/moonlight/inc/csr.h @@ -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 + +static inline uint64_t riscv_get_core() +{ + uint64_t x; + asm volatile("csrr %0, mhartid" : "=r"(x)); + return x; +} + +static inline uint64_t riscv_get_mstatus() +{ + uint64_t x; + asm volatile("csrr %0, mstatus" : "=r"(x)); + return x; +} + +static inline void riscv_writ_mstatus(uint64_t x) +{ + asm volatile("csrw mstatus, %0" : : "r"(x)); +} + +// machine exception program counter, holds the +// instruction address to which a return from +// exception will go. +static inline void riscv_writ_mepc(uint64_t x) +{ + asm volatile("csrw mepc, %0" : : "r"(x)); +} + +static inline uint64_t riscv_get_sstatus() +{ + uint64_t x; + asm volatile("csrr %0, sstatus" : "=r"(x)); + return x; +} + +static inline void riscv_writ_sstatus(uint64_t x) +{ + asm volatile("csrw sstatus, %0" : : "r"(x)); +} + +// Supervisor Interrupt Pending +static inline uint64_t riscv_get_sip() +{ + uint64_t x; + asm volatile("csrr %0, sip" : "=r"(x)); + return x; +} + +static inline void riscv_writ_sip(uint64_t x) +{ + asm volatile("csrw sip, %0" : : "r"(x)); +} + +static inline uint64_t riscv_get_sie() +{ + uint64_t x; + asm volatile("csrr %0, sie" : "=r"(x)); + return x; +} + +static inline void riscv_writ_sie(uint64_t x) +{ + asm volatile("csrw sie, %0" : : "r"(x)); +} + +static inline uint64_t riscv_get_mie() +{ + uint64_t x; + asm volatile("csrr %0, mie" : "=r"(x)); + return x; +} + +static inline void riscv_writ_mie(uint64_t x) +{ + asm volatile("csrw mie, %0" : : "r"(x)); +} + +// supervisor exception program counter, holds the +// instruction address to which a return from +// exception will go. +static inline void riscv_writ_sepc(uint64_t x) +{ + asm volatile("csrw sepc, %0" : : "r"(x)); +} + +static inline uint64_t riscv_get_sepc() +{ + uint64_t x; + asm volatile("csrr %0, sepc" : "=r"(x)); + return x; +} + +// Machine Exception Delegation +static inline uint64_t riscv_get_medeleg() +{ + uint64_t x; + asm volatile("csrr %0, medeleg" : "=r"(x)); + return x; +} + +static inline void riscv_writ_medeleg(uint64_t x) +{ + asm volatile("csrw medeleg, %0" : : "r"(x)); +} + +// Machine Interrupt Delegation +static inline uint64_t riscv_get_mideleg() +{ + uint64_t x; + asm volatile("csrr %0, mideleg" : "=r"(x)); + return x; +} + +static inline void riscv_writ_mideleg(uint64_t x) +{ + asm volatile("csrw mideleg, %0" : : "r"(x)); +} + +// Supervisor Trap-Vector Base Address +// low two bits are mode. +static inline void riscv_writ_stvec(uint64_t x) +{ + asm volatile("csrw stvec, %0" : : "r"(x)); +} + +static inline uint64_t riscv_get_stvec() +{ + uint64_t x; + asm volatile("csrr %0, stvec" : "=r"(x)); + return x; +} + +// Supervisor Timer Comparison Register +static inline uint64_t riscv_get_stimecmp() +{ + uint64_t x; + // asm volatile("csrr %0, stimecmp" : "=r" (x) ); + asm volatile("csrr %0, 0x14d" : "=r"(x)); + return x; +} + +static inline void riscv_writ_stimecmp(uint64_t x) +{ + // asm volatile("csrw stimecmp, %0" : : "r" (x)); + asm volatile("csrw 0x14d, %0" : : "r"(x)); +} + +// Machine Environment Configuration Register +static inline uint64_t riscv_get_menvcfg() +{ + uint64_t x; + // asm volatile("csrr %0, menvcfg" : "=r" (x) ); + asm volatile("csrr %0, 0x30a" : "=r"(x)); + return x; +} + +static inline void riscv_writ_menvcfg(uint64_t x) +{ + // asm volatile("csrw menvcfg, %0" : : "r" (x)); + asm volatile("csrw 0x30a, %0" : : "r"(x)); +} + +// Physical Memory Protection +static inline void riscv_writ_pmpcfg0(uint64_t x) +{ + asm volatile("csrw pmpcfg0, %0" : : "r"(x)); +} + +static inline void riscv_writ_pmpaddr0(uint64_t x) +{ + asm volatile("csrw pmpaddr0, %0" : : "r"(x)); +} + +// supervisor address translation and protection; +// holds the address of the page table. +static inline void riscv_writ_satp(uint64_t x) +{ + asm volatile("csrw satp, %0" : : "r"(x)); +} + +static inline uint64_t riscv_get_satp() +{ + uint64_t x; + asm volatile("csrr %0, satp" : "=r"(x)); + return x; +} + +// Supervisor Trap Cause +static inline uint64_t riscv_get_scause() +{ + uint64_t x; + asm volatile("csrr %0, scause" : "=r"(x)); + return x; +} + +// Supervisor Trap Value +static inline uint64_t riscv_get_stval() +{ + uint64_t x; + asm volatile("csrr %0, stval" : "=r"(x)); + return x; +} + +// Machine-mode Counter-Enable +static inline void riscv_writ_mcounteren(uint64_t x) +{ + asm volatile("csrw mcounteren, %0" : : "r"(x)); +} + +static inline uint64_t riscv_get_mcounteren() +{ + uint64_t x; + asm volatile("csrr %0, mcounteren" : "=r"(x)); + return x; +} + +// machine-mode cycle counter +static inline uint64_t riscv_get_time() +{ + uint64_t x; + asm volatile("csrr %0, time" : "=r"(x)); + return x; +} + +// enable device interrupts +static inline void riscv_sintr_on() +{ + uint64_t sstatus = riscv_get_sstatus(); + sstatus |= SSTATUS_SIE; + riscv_writ_sstatus(sstatus); +} + +// disable device interrupts +static inline void riscv_sintr_off() +{ + uint64_t sstatus = riscv_get_sstatus(); + sstatus &= (~SSTATUS_SIE); + riscv_writ_sstatus(sstatus); +} + +// are device interrupts enabled? +static inline int riscv_sintr_get() +{ + uint64_t x = riscv_get_sstatus(); + return (x & SSTATUS_SIE) != 0; +} + +static inline void riscv_sintr_restore(int x) +{ + if (x) + riscv_sintr_on(); + else + riscv_sintr_off(); +} + +// enable device interrupts +static inline void riscv_mintr_on() +{ + uint64_t mstatus = riscv_get_mstatus(); + mstatus |= MSTATUS_MIE; + riscv_writ_mstatus(mstatus); +} + +// disable device interrupts +static inline void riscv_mintr_off() +{ + uint64_t mstatus = riscv_get_mstatus(); + mstatus &= (~MSTATUS_MIE); + riscv_writ_mstatus(mstatus); +} + +// are device interrupts enabled? +static inline int riscv_mintr_get() +{ + uint64_t x = riscv_get_mstatus(); + return (x & MSTATUS_MIE) != 0; +} + +static inline void riscv_mintr_restore(int x) +{ + if (x) + riscv_mintr_on(); + else + riscv_mintr_off(); +} + +static inline uint64_t riscv_get_sp() +{ + uint64_t x; + asm volatile("mv %0, sp" : "=r"(x)); + return x; +} + +// read and write tp, the thread pointer, which xv6 uses to hold +// this core's hartid (core number), the index into cpus[]. +static inline uint64_t riscv_get_tp() +{ + uint64_t x; + asm volatile("mv %0, tp" : "=r"(x)); + return x; +} + +static inline void riscv_writ_tp(uint64_t x) +{ + asm volatile("mv tp, %0" : : "r"(x)); +} + +static inline uint64_t riscv_get_ra() +{ + uint64_t x; + asm volatile("mv %0, ra" : "=r"(x)); + return x; +} + +// flush the TLB. +static inline void sfence_vma() +{ + // the zero, zero means flush all TLB entries. + asm volatile("sfence.vma zero, zero"); +} + +#endif // __ASSEMBLER__ + +#endif diff --git a/port/moonlight/gen/aclint.h b/port/moonlight/inc/gen/aclint.h similarity index 100% rename from port/moonlight/gen/aclint.h rename to port/moonlight/inc/gen/aclint.h diff --git a/port/moonlight/gen/ethmac.h b/port/moonlight/inc/gen/ethmac.h similarity index 100% rename from port/moonlight/gen/ethmac.h rename to port/moonlight/inc/gen/ethmac.h diff --git a/port/moonlight/gen/uart.h b/port/moonlight/inc/gen/uart.h similarity index 100% rename from port/moonlight/gen/uart.h rename to port/moonlight/inc/gen/uart.h diff --git a/port/moonlight/hwtimer.h b/port/moonlight/inc/hwtimer.h similarity index 92% rename from port/moonlight/hwtimer.h rename to port/moonlight/inc/hwtimer.h index fb7e7a9..0c339fd 100644 --- a/port/moonlight/hwtimer.h +++ b/port/moonlight/inc/hwtimer.h @@ -15,7 +15,7 @@ #include "platform.h" #define TICKNUM_PER_SECOND 32768 -#define TICKNUM_PER_TIMER (TICKNUM_PER_SECOND / 100) // ~ 1ms timer +#define TICKNUM_PER_TIMER (TICKNUM_PER_SECOND / 1000) // ~ 1ms timer static inline int hwtimer_init(void) { diff --git a/port/moonlight/platform.h b/port/moonlight/inc/platform.h similarity index 100% rename from port/moonlight/platform.h rename to port/moonlight/inc/platform.h diff --git a/port/moonlight/riscv-csr.h b/port/moonlight/inc/riscv-csr.h similarity index 100% rename from port/moonlight/riscv-csr.h rename to port/moonlight/inc/riscv-csr.h diff --git a/port/moonlight/riscv-traps.h b/port/moonlight/inc/riscv-traps.h similarity index 100% rename from port/moonlight/riscv-traps.h rename to port/moonlight/inc/riscv-traps.h diff --git a/port/moonlight/uart.h b/port/moonlight/inc/uart.h similarity index 100% rename from port/moonlight/uart.h rename to port/moonlight/inc/uart.h diff --git a/port/moonlight/vector_table.h b/port/moonlight/inc/vector_table.h similarity index 100% rename from port/moonlight/vector_table.h rename to port/moonlight/inc/vector_table.h diff --git a/port/moonlight/board.c b/port/moonlight/src/board.c similarity index 100% rename from port/moonlight/board.c rename to port/moonlight/src/board.c diff --git a/port/moonlight/bootup.c b/port/moonlight/src/bootup.c similarity index 54% rename from port/moonlight/bootup.c rename to port/moonlight/src/bootup.c index d4f15f7..aeaf38e 100644 --- a/port/moonlight/bootup.c +++ b/port/moonlight/src/bootup.c @@ -9,12 +9,17 @@ #include #include #include - +#include +#include +#include #ifdef __cplusplus #define EXTERN_C extern "C" #else #define EXTERN_C extern #endif +#if defined(TX_THREAD_SMP_MAX_CORES) && (TX_THREAD_SMP_MAX_CORES > 1) +#define BOOTUP_SMP_ENABLED +#endif // Generic C function pointer. typedef void(*function_t)(void) ; @@ -36,6 +41,7 @@ EXTERN_C function_t __init_array_start; EXTERN_C function_t __init_array_end; EXTERN_C function_t __fini_array_start; EXTERN_C function_t __fini_array_end; +EXTERN_C volatile uintptr_t tohost; // This function will be placed by the linker script according to the section // Raw function 'called' by the CPU with no runtime. @@ -43,14 +49,60 @@ EXTERN_C void _start(void) __attribute__ ((naked,section(".text.boot"))); // Entry and exit points as C functions. EXTERN_C void _initialize(void) __attribute__ ((noreturn,section(".text.boot"))); +EXTERN_C void _secondary_sleep_forever(void) __attribute__ ((noreturn,section(".text.boot"))); EXTERN_C void _exit(int exit_code) __attribute__ ((noreturn,noinline,weak)); // Standard entry point, no arguments. extern int main(void); +#if defined BOOTUP_SMP_ENABLED +EXTERN_C void _secondary_initialize(void) __attribute__ ((noreturn,section(".text.boot"))); +EXTERN_C void _secondary_ipi_trap(void) __attribute__ ((naked,noreturn,section(".text.boot"))); +EXTERN_C void _tx_thread_smp_initialize_wait(void) __attribute__ ((noreturn)); + +void _secondary_initialize(void) { + csr_write_mtvec((uint_xlen_t)_secondary_ipi_trap); + csr_set_bits_mie(MIE_MSI_BIT_MASK); + csr_set_bits_mstatus(MSTATUS_MIE_BIT_MASK); + + __asm__ volatile ("wfi"); + + csr_clr_bits_mstatus(MSTATUS_MIE_BIT_MASK); + + _tx_thread_smp_initialize_wait(); +} + +void _secondary_ipi_trap(void) { +#if __riscv_xlen == 64 + __asm__ volatile ("addi sp, sp, -16;" + "sd ra, 8(sp);" + "call bootup_ipi_clear_handler;" + "ld ra, 8(sp);" + "addi sp, sp, 16;" + "mret"); +#else + __asm__ volatile ("addi sp, sp, -8;" + "sw ra, 4(sp);" + "call bootup_ipi_clear_handler;" + "lw ra, 4(sp);" + "addi sp, sp, 8;" + "mret"); +#endif +} + +static void bootup_wake_secondary_cores(void) { + for (UINT core = 1; core < TX_THREAD_SMP_MAX_CORES; ++core) { + send_ipi(core); + } +} + +__attribute__((used)) void bootup_ipi_clear_handler(void) { + set_aclint_msip(aclint, csr_read_mhartid(), 0); +} +#endif + // 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 @@ -60,10 +112,33 @@ void _start(void) { // The 'norelax' option is critical here. // Without 'norelax' the global pointer will // be loaded relative to the global pointer! - ".option norelax;" + ".option norelax;" "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 + "mv t2, t0;" + "1:;" + "beqz t2, 2f;" + "sub sp, sp, t1;" + "addi t2, t2, -1;" + "j 1b;" + "2:;" + "beqz t0, 3f;" +#ifdef BOOTUP_SMP_ENABLED + "jal zero, _secondary_initialize;" +#else + "jal zero, _secondary_sleep_forever;" +#endif + "3:;" "jal zero, _initialize;" : /* output: none %0 */ : /* input: none */ @@ -88,6 +163,9 @@ void _initialize(void) { ++entry) { (*entry)(); } +#ifdef BOOTUP_SMP_ENABLED + bootup_wake_secondary_cores(); +#endif #ifdef __THREAD_LOCAL_STORAGE _set_tls(__tls_base) #endif @@ -101,10 +179,21 @@ void _initialize(void) { _exit(rc); } -// This should never be called. Busy loop with the CPU in idle state. +void _secondary_sleep_forever(void) { + csr_clr_bits_mie(MIE_MTI_BIT_MASK); + csr_clr_bits_mstatus(MSTATUS_MIE_BIT_MASK); + while (1) { + __asm__ volatile ("wfi"); + } +} + +// This should never be called. Report the exit code through HTIF and idle the CPU. void _exit(int exit_code) { - (void)exit_code; - // Halt + uintptr_t htif_exit_code = (((uintptr_t)(unsigned int)exit_code) << 1) | 1u; + + tohost = htif_exit_code; + csr_clr_bits_mie(MIE_MTI_BIT_MASK); + csr_clr_bits_mstatus(MSTATUS_MIE_BIT_MASK); while (1) { __asm__ volatile ("wfi"); } diff --git a/port/moonlight/exception.c b/port/moonlight/src/exception.c similarity index 100% rename from port/moonlight/exception.c rename to port/moonlight/src/exception.c diff --git a/port/moonlight/mnrs_network_driver.c b/port/moonlight/src/mnrs_network_driver.c similarity index 100% rename from port/moonlight/mnrs_network_driver.c rename to port/moonlight/src/mnrs_network_driver.c diff --git a/port/moonlight/trap_non_vectored.c b/port/moonlight/src/trap_non_vectored.c similarity index 90% rename from port/moonlight/trap_non_vectored.c rename to port/moonlight/src/trap_non_vectored.c index 513778c..0f891d7 100644 --- a/port/moonlight/trap_non_vectored.c +++ b/port/moonlight/src/trap_non_vectored.c @@ -1,4 +1,5 @@ #include "hwtimer.h" +#include "platform.h" #include "riscv-traps.h" #include #include @@ -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) diff --git a/port/moonlight/trap_vectored.c b/port/moonlight/src/trap_vectored.c similarity index 100% rename from port/moonlight/trap_vectored.c rename to port/moonlight/src/trap_vectored.c diff --git a/port/moonlight/vector_table.c b/port/moonlight/src/vector_table.c similarity index 100% rename from port/moonlight/vector_table.c rename to port/moonlight/src/vector_table.c diff --git a/port/threadx/CMakeLists.txt b/port/threadx/CMakeLists.txt index e9c2e6a..f05a060 100644 --- a/port/threadx/CMakeLists.txt +++ b/port/threadx/CMakeLists.txt @@ -5,6 +5,7 @@ target_sources(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR}/src/tx_initialize_low_level.S ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_context_restore.S ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_context_save.S + ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_interrupt_control.S ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_schedule.S ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_stack_build.S ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_system_return.S diff --git a/port/threadx/inc/nx_port.h b/port/threadx/inc/nx_port.h index 10875a0..2605d8d 100644 --- a/port/threadx/inc/nx_port.h +++ b/port/threadx/inc/nx_port.h @@ -47,7 +47,6 @@ #ifndef NX_PORT_H #define NX_PORT_H -#include "tx_port.h" /* Determine if the optional NetX user define file should be used. */ #ifdef NX_INCLUDE_USER_DEFINE_FILE diff --git a/port/threadx/inc/tx_port.h b/port/threadx/inc/tx_port.h index c0ac1cd..2da25c8 100644 --- a/port/threadx/inc/tx_port.h +++ b/port/threadx/inc/tx_port.h @@ -66,17 +66,10 @@ #define LWU lw #define LOG_REGBYTES 2 #endif -#define REGBYTES (1 << LOG_REGBYTES) -#define TX_THREAD_STACK_END_OFFSET 2 * 4 + 2 * REGBYTES -#define TX_THREAD_TIME_SLICE_OFFSET 3 * 4 + 3 * REGBYTES +#define REGBYTES (1 << LOG_REGBYTES) #else /*not __ASSEMBLER__ */ -/* Include for memset. */ -#include -/* include for strtoul*/ -#include - /* Determine if the optional ThreadX user define file should be used. */ #ifdef TX_INCLUDE_USER_DEFINE_FILE @@ -90,6 +83,9 @@ /* Define compiler library include files. */ +#include +#include + /* Define ThreadX basic types for this port. */ #define VOID void @@ -98,7 +94,7 @@ typedef unsigned char UCHAR; typedef int INT; typedef unsigned int UINT; typedef int LONG; -typedef unsigned int ULONG; +typedef unsigned long ULONG; typedef unsigned long long ULONG64; typedef short SHORT; typedef unsigned short USHORT; @@ -205,6 +201,109 @@ typedef unsigned short USHORT; #define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) #define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) +/* Define automated regression test extensions. These are required for the + ThreadX regression tests. */ + +typedef unsigned int TEST_FLAG; +extern TEST_FLAG threadx_byte_allocate_loop_test; +extern TEST_FLAG threadx_byte_release_loop_test; +extern TEST_FLAG threadx_mutex_suspension_put_test; +extern TEST_FLAG threadx_mutex_suspension_priority_test; +#ifndef TX_TIMER_PROCESS_IN_ISR +extern TEST_FLAG threadx_delete_timer_thread; +#endif + +extern void abort_and_resume_byte_allocating_thread(void); +extern void abort_all_threads_suspended_on_mutex(void); +extern void suspend_lowest_priority(void); +#ifndef TX_TIMER_PROCESS_IN_ISR +extern void delete_timer_thread(void); +#endif +extern TEST_FLAG test_stack_analyze_flag; +extern TEST_FLAG test_initialize_flag; +extern TEST_FLAG test_forced_mutex_timeout; + +#ifdef TX_REGRESSION_TEST + +/* Define extension macros for automated coverage tests. */ + +#define TX_BYTE_ALLOCATE_EXTENSION \ + if (threadx_byte_allocate_loop_test == ((TEST_FLAG)1)) \ + { \ + pool_ptr->tx_byte_pool_owner = TX_NULL; \ + threadx_byte_allocate_loop_test = ((TEST_FLAG)0); \ + } + +#define TX_BYTE_RELEASE_EXTENSION \ + if (threadx_byte_release_loop_test == ((TEST_FLAG)1)) \ + { \ + threadx_byte_release_loop_test = ((TEST_FLAG)0); \ + abort_and_resume_byte_allocating_thread(); \ + } + +#define TX_MUTEX_PUT_EXTENSION_1 \ + if (threadx_mutex_suspension_put_test == ((TEST_FLAG)1)) \ + { \ + threadx_mutex_suspension_put_test = ((TEST_FLAG)0); \ + abort_all_threads_suspended_on_mutex(); \ + } + +#define TX_MUTEX_PUT_EXTENSION_2 \ + if (test_forced_mutex_timeout == ((TEST_FLAG)1)) \ + { \ + test_forced_mutex_timeout = ((TEST_FLAG)0); \ + _tx_thread_wait_abort(mutex_ptr->tx_mutex_suspension_list); \ + } + +#define TX_MUTEX_PRIORITY_CHANGE_EXTENSION \ + if (threadx_mutex_suspension_priority_test == ((TEST_FLAG)1)) \ + { \ + threadx_mutex_suspension_priority_test = ((TEST_FLAG)0); \ + suspend_lowest_priority(); \ + } + +#ifndef TX_TIMER_PROCESS_IN_ISR + +#define TX_TIMER_INITIALIZE_EXTENSION(a) \ + if (threadx_delete_timer_thread == ((TEST_FLAG)1)) \ + { \ + threadx_delete_timer_thread = ((TEST_FLAG)0); \ + delete_timer_thread(); \ + (a) = ((UINT)1); \ + } + +#endif + +#define TX_THREAD_STACK_ANALYZE_EXTENSION \ + if (test_stack_analyze_flag == ((TEST_FLAG)1)) \ + { \ + thread_ptr->tx_thread_id = ((TEST_FLAG)0); \ + test_stack_analyze_flag = ((TEST_FLAG)0); \ + } \ + else if (test_stack_analyze_flag == ((TEST_FLAG)2)) \ + { \ + stack_ptr = thread_ptr->tx_thread_stack_start; \ + test_stack_analyze_flag = ((TEST_FLAG)0); \ + } \ + else if (test_stack_analyze_flag == ((TEST_FLAG)3)) \ + { \ + *stack_ptr = TX_STACK_FILL; \ + test_stack_analyze_flag = ((TEST_FLAG)0); \ + } \ + else \ + { \ + test_stack_analyze_flag = ((TEST_FLAG)0); \ + } + +#define TX_INITIALIZE_KERNEL_ENTER_EXTENSION \ + if (test_initialize_flag == ((TEST_FLAG)1)) \ + { \ + test_initialize_flag = ((TEST_FLAG)0); \ + return; \ + } + +#endif + /* Define the ThreadX object creation extensions for the remaining objects. */ #define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) @@ -234,26 +333,26 @@ typedef unsigned short USHORT; #ifdef TX_DISABLE_INLINE -ULONG64 _tx_thread_interrupt_control(unsigned int new_posture); +ULONG _tx_thread_interrupt_control(unsigned int new_posture); -#define TX_INTERRUPT_SAVE_AREA register ULONG64 interrupt_save; +#define TX_INTERRUPT_SAVE_AREA register ULONG tx_saved_posture; -#define TX_DISABLE interrupt_save = _tx_thread_interrupt_control(TX_INT_DISABLE); -#define TX_RESTORE _tx_thread_interrupt_control(interrupt_save); +#define TX_DISABLE tx_saved_posture = _tx_thread_interrupt_control(TX_INT_DISABLE); +#define TX_RESTORE _tx_thread_interrupt_control(tx_saved_posture); #else -#define TX_INTERRUPT_SAVE_AREA ULONG64 interrupt_save; -/* Atomically read mstatus into interrupt_save and clear bit 3 of mstatus. */ +#define TX_INTERRUPT_SAVE_AREA ULONG tx_saved_posture; +/* Atomically read mstatus into tx_saved_posture and clear bit 3 of mstatus. */ #define TX_DISABLE \ { \ - __asm__("csrrci %0, mstatus, 0x08" : "=r"(interrupt_save) :); \ + __asm__ volatile("csrrci %0, mstatus, 0x08" : "=r"(tx_saved_posture) : : "memory"); \ }; -/* We only care about mstatus.mie (bit 3), so mask interrupt_save and write to mstatus. */ +/* We only care about mstatus.mie (bit 3), so mask tx_saved_posture and write to mstatus. */ #define TX_RESTORE \ { \ - register ULONG64 __tempmask = interrupt_save & 0x08; \ - __asm__("csrrs x0, mstatus, %0 \n\t" : : "r"(__tempmask) :); \ + register ULONG __tempmask = tx_saved_posture & 0x08; \ + __asm__ volatile("csrrs x0, mstatus, %0" : : "r"(__tempmask) : "memory"); \ }; #endif diff --git a/port/threadx/src/tx_initialize_low_level.S b/port/threadx/src/tx_initialize_low_level.S index 558d705..48ed119 100644 --- a/port/threadx/src/tx_initialize_low_level.S +++ b/port/threadx/src/tx_initialize_low_level.S @@ -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 diff --git a/port/threadx/src/tx_thread_context_restore.S b/port/threadx/src/tx_thread_context_restore.S index 200f230..29f9452 100644 --- a/port/threadx/src/tx_thread_context_restore.S +++ b/port/threadx/src/tx_thread_context_restore.S @@ -80,9 +80,9 @@ _tx_thread_context_restore: { */ la t0, _tx_thread_system_state // Pickup addr of nested interrupt count - lw t1, 0(t0) // Pickup nested interrupt count + LOAD t1, 0(t0) // Pickup nested interrupt count addi t1, t1, -1 // Decrement the nested interrupt counter - sw t1, 0(t0) // Store new nested count + STORE t1, 0(t0) // Store new nested count beqz t1, _tx_thread_not_nested_restore // If 0, not nested restore /* Interrupts are nested. */ @@ -190,7 +190,7 @@ _tx_thread_not_nested_restore: LOAD t1, _tx_thread_current_ptr // Pickup current thread pointer beqz t1, _tx_thread_idle_system_restore // If NULL, idle system restore - LOAD t2, _tx_thread_preempt_disable // Pickup preempt disable flag + LWU t2, _tx_thread_preempt_disable // Pickup preempt disable flag bgtz t2, _tx_thread_no_preempt_restore // If set, restore interrupted thread LOAD t2, _tx_thread_execute_ptr // Pickup thread execute pointer @@ -354,14 +354,14 @@ _tx_thread_preempt_restore: { */ la t0, _tx_timer_time_slice // Pickup time slice variable address - lw t2, 0(t0) // Pickup time slice + LOAD t2, 0(t0) // Pickup time slice beqz t2, _tx_thread_dont_save_ts // If 0, skip time slice processing /* _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice _tx_timer_time_slice = 0; */ - sw t2, TX_THREAD_TIME_SLICE_OFFSET(t1) // Save current time slice - sw x0, 0(t0) // Clear global time slice + STORE t2, 6*REGBYTES(t1) // Save current time slice + STORE x0, 0(t0) // Clear global time slice /* } */ diff --git a/port/threadx/src/tx_thread_context_save.S b/port/threadx/src/tx_thread_context_save.S index 0de69be..c07ecf0 100644 --- a/port/threadx/src/tx_thread_context_save.S +++ b/port/threadx/src/tx_thread_context_save.S @@ -74,14 +74,14 @@ _tx_thread_context_save: STORE t1, 18*REGBYTES(sp) la t0, _tx_thread_system_state // Pickup address of system state - lw t1, 0(t0) // Pickup system state + LOAD 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 + STORE t1, 0(t0) // Store the interrupt counter /* Nested interrupt condition. Save the reset of the scratch registers on the stack and return to the diff --git a/port/threadx/src/tx_thread_schedule.S b/port/threadx/src/tx_thread_schedule.S index c9be4c6..1169443 100644 --- a/port/threadx/src/tx_thread_schedule.S +++ b/port/threadx/src/tx_thread_schedule.S @@ -105,12 +105,12 @@ _tx_thread_schedule_loop: /* _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; */ la t2, _tx_timer_time_slice // Pickup time-slice variable address + STORE t3, 0(t2) // Store new time-slice*/ /* 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 diff --git a/port/threadx/src/tx_thread_stack_build.S b/port/threadx/src/tx_thread_stack_build.S index ec6f90b..d7d54f3 100644 --- a/port/threadx/src/tx_thread_stack_build.S +++ b/port/threadx/src/tx_thread_stack_build.S @@ -138,8 +138,8 @@ If floating point support: 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) + LOAD t0, 4*REGBYTES(a0) // Pickup end of stack area + andi t0, t0, -16 // Ensure 16-byte alignment /* Actually build the stack frame. */ diff --git a/port/threadx/src/tx_thread_system_return.S b/port/threadx/src/tx_thread_system_return.S index ec6ff78..2b05c2a 100644 --- a/port/threadx/src/tx_thread_system_return.S +++ b/port/threadx/src/tx_thread_system_return.S @@ -151,7 +151,7 @@ _tx_thread_system_return: { */ la t4, _tx_timer_time_slice // Pickup time slice variable addr - lw t3, 0(t4) // Pickup time slice value + LOAD t3, 0(t4) // Pickup time slice value la t2, _tx_thread_schedule // Pickup address of scheduling loop beqz t3, _tx_thread_dont_save_ts // If no time-slice, don't save it @@ -159,8 +159,8 @@ _tx_thread_system_return: /* _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 + STORE t3, 6*REGBYTES(t1) // Save current time-slice for thread + STORE x0, 0(t4) // Clear time-slice variable /* } */ _tx_thread_dont_save_ts: diff --git a/port/threadx_smp/CMakeLists.txt b/port/threadx_smp/CMakeLists.txt new file mode 100644 index 0000000..6095b7e --- /dev/null +++ b/port/threadx_smp/CMakeLists.txt @@ -0,0 +1,238 @@ +cmake_minimum_required(VERSION 3.24) +project(smp_demo LANGUAGES C ASM) + +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/inc # needed for Moonlight SMP support headers +) +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_interrupt_control.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 +) + +set(THREADX_SMP_SOURCES + ${THREADX_COMMON_SMP_DIR}/src/tx_block_allocate.c + ${THREADX_COMMON_SMP_DIR}/src/tx_block_pool_cleanup.c + ${THREADX_COMMON_SMP_DIR}/src/tx_block_pool_create.c + ${THREADX_COMMON_SMP_DIR}/src/tx_block_pool_delete.c + ${THREADX_COMMON_SMP_DIR}/src/tx_block_pool_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_block_pool_initialize.c + ${THREADX_COMMON_SMP_DIR}/src/tx_block_pool_performance_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_block_pool_performance_system_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_block_pool_prioritize.c + ${THREADX_COMMON_SMP_DIR}/src/tx_block_release.c + ${THREADX_COMMON_SMP_DIR}/src/tx_byte_allocate.c + ${THREADX_COMMON_SMP_DIR}/src/tx_byte_pool_cleanup.c + ${THREADX_COMMON_SMP_DIR}/src/tx_byte_pool_create.c + ${THREADX_COMMON_SMP_DIR}/src/tx_byte_pool_delete.c + ${THREADX_COMMON_SMP_DIR}/src/tx_byte_pool_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_byte_pool_initialize.c + ${THREADX_COMMON_SMP_DIR}/src/tx_byte_pool_performance_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_byte_pool_performance_system_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_byte_pool_prioritize.c + ${THREADX_COMMON_SMP_DIR}/src/tx_byte_pool_search.c + ${THREADX_COMMON_SMP_DIR}/src/tx_byte_release.c + ${THREADX_COMMON_SMP_DIR}/src/txe_block_allocate.c + ${THREADX_COMMON_SMP_DIR}/src/txe_block_pool_create.c + ${THREADX_COMMON_SMP_DIR}/src/txe_block_pool_delete.c + ${THREADX_COMMON_SMP_DIR}/src/txe_block_pool_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/txe_block_pool_prioritize.c + ${THREADX_COMMON_SMP_DIR}/src/txe_block_release.c + ${THREADX_COMMON_SMP_DIR}/src/txe_byte_allocate.c + ${THREADX_COMMON_SMP_DIR}/src/txe_byte_pool_create.c + ${THREADX_COMMON_SMP_DIR}/src/txe_byte_pool_delete.c + ${THREADX_COMMON_SMP_DIR}/src/txe_byte_pool_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/txe_byte_pool_prioritize.c + ${THREADX_COMMON_SMP_DIR}/src/txe_byte_release.c + ${THREADX_COMMON_SMP_DIR}/src/txe_event_flags_create.c + ${THREADX_COMMON_SMP_DIR}/src/txe_event_flags_delete.c + ${THREADX_COMMON_SMP_DIR}/src/txe_event_flags_get.c + ${THREADX_COMMON_SMP_DIR}/src/txe_event_flags_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/txe_event_flags_set.c + ${THREADX_COMMON_SMP_DIR}/src/txe_event_flags_set_notify.c + ${THREADX_COMMON_SMP_DIR}/src/txe_mutex_create.c + ${THREADX_COMMON_SMP_DIR}/src/txe_mutex_delete.c + ${THREADX_COMMON_SMP_DIR}/src/txe_mutex_get.c + ${THREADX_COMMON_SMP_DIR}/src/txe_mutex_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/txe_mutex_prioritize.c + ${THREADX_COMMON_SMP_DIR}/src/txe_mutex_put.c + ${THREADX_COMMON_SMP_DIR}/src/txe_queue_create.c + ${THREADX_COMMON_SMP_DIR}/src/txe_queue_delete.c + ${THREADX_COMMON_SMP_DIR}/src/txe_queue_flush.c + ${THREADX_COMMON_SMP_DIR}/src/txe_queue_front_send.c + ${THREADX_COMMON_SMP_DIR}/src/txe_queue_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/txe_queue_prioritize.c + ${THREADX_COMMON_SMP_DIR}/src/txe_queue_receive.c + ${THREADX_COMMON_SMP_DIR}/src/txe_queue_send.c + ${THREADX_COMMON_SMP_DIR}/src/txe_queue_send_notify.c + ${THREADX_COMMON_SMP_DIR}/src/txe_semaphore_ceiling_put.c + ${THREADX_COMMON_SMP_DIR}/src/txe_semaphore_create.c + ${THREADX_COMMON_SMP_DIR}/src/txe_semaphore_delete.c + ${THREADX_COMMON_SMP_DIR}/src/txe_semaphore_get.c + ${THREADX_COMMON_SMP_DIR}/src/txe_semaphore_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/txe_semaphore_prioritize.c + ${THREADX_COMMON_SMP_DIR}/src/txe_semaphore_put.c + ${THREADX_COMMON_SMP_DIR}/src/txe_semaphore_put_notify.c + ${THREADX_COMMON_SMP_DIR}/src/txe_thread_create.c + ${THREADX_COMMON_SMP_DIR}/src/txe_thread_delete.c + ${THREADX_COMMON_SMP_DIR}/src/txe_thread_entry_exit_notify.c + ${THREADX_COMMON_SMP_DIR}/src/txe_thread_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/txe_thread_preemption_change.c + ${THREADX_COMMON_SMP_DIR}/src/txe_thread_priority_change.c + ${THREADX_COMMON_SMP_DIR}/src/txe_thread_relinquish.c + ${THREADX_COMMON_SMP_DIR}/src/txe_thread_reset.c + ${THREADX_COMMON_SMP_DIR}/src/txe_thread_resume.c + ${THREADX_COMMON_SMP_DIR}/src/txe_thread_suspend.c + ${THREADX_COMMON_SMP_DIR}/src/txe_thread_terminate.c + ${THREADX_COMMON_SMP_DIR}/src/txe_thread_time_slice_change.c + ${THREADX_COMMON_SMP_DIR}/src/txe_thread_wait_abort.c + ${THREADX_COMMON_SMP_DIR}/src/txe_timer_activate.c + ${THREADX_COMMON_SMP_DIR}/src/txe_timer_change.c + ${THREADX_COMMON_SMP_DIR}/src/txe_timer_create.c + ${THREADX_COMMON_SMP_DIR}/src/txe_timer_deactivate.c + ${THREADX_COMMON_SMP_DIR}/src/txe_timer_delete.c + ${THREADX_COMMON_SMP_DIR}/src/txe_timer_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_event_flags_cleanup.c + ${THREADX_COMMON_SMP_DIR}/src/tx_event_flags_create.c + ${THREADX_COMMON_SMP_DIR}/src/tx_event_flags_delete.c + ${THREADX_COMMON_SMP_DIR}/src/tx_event_flags_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_event_flags_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_event_flags_initialize.c + ${THREADX_COMMON_SMP_DIR}/src/tx_event_flags_performance_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_event_flags_performance_system_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_event_flags_set.c + ${THREADX_COMMON_SMP_DIR}/src/tx_event_flags_set_notify.c + ${THREADX_COMMON_SMP_DIR}/src/tx_initialize_high_level.c + ${THREADX_COMMON_SMP_DIR}/src/tx_initialize_kernel_enter.c + ${THREADX_COMMON_SMP_DIR}/src/tx_initialize_kernel_setup.c + ${THREADX_COMMON_SMP_DIR}/src/tx_misra.c + ${THREADX_COMMON_SMP_DIR}/src/tx_mutex_cleanup.c + ${THREADX_COMMON_SMP_DIR}/src/tx_mutex_create.c + ${THREADX_COMMON_SMP_DIR}/src/tx_mutex_delete.c + ${THREADX_COMMON_SMP_DIR}/src/tx_mutex_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_mutex_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_mutex_initialize.c + ${THREADX_COMMON_SMP_DIR}/src/tx_mutex_performance_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_mutex_performance_system_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_mutex_prioritize.c + ${THREADX_COMMON_SMP_DIR}/src/tx_mutex_priority_change.c + ${THREADX_COMMON_SMP_DIR}/src/tx_mutex_put.c + ${THREADX_COMMON_SMP_DIR}/src/tx_queue_cleanup.c + ${THREADX_COMMON_SMP_DIR}/src/tx_queue_create.c + ${THREADX_COMMON_SMP_DIR}/src/tx_queue_delete.c + ${THREADX_COMMON_SMP_DIR}/src/tx_queue_flush.c + ${THREADX_COMMON_SMP_DIR}/src/tx_queue_front_send.c + ${THREADX_COMMON_SMP_DIR}/src/tx_queue_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_queue_initialize.c + ${THREADX_COMMON_SMP_DIR}/src/tx_queue_performance_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_queue_performance_system_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_queue_prioritize.c + ${THREADX_COMMON_SMP_DIR}/src/tx_queue_receive.c + ${THREADX_COMMON_SMP_DIR}/src/tx_queue_send.c + ${THREADX_COMMON_SMP_DIR}/src/tx_queue_send_notify.c + ${THREADX_COMMON_SMP_DIR}/src/tx_semaphore_ceiling_put.c + ${THREADX_COMMON_SMP_DIR}/src/tx_semaphore_cleanup.c + ${THREADX_COMMON_SMP_DIR}/src/tx_semaphore_create.c + ${THREADX_COMMON_SMP_DIR}/src/tx_semaphore_delete.c + ${THREADX_COMMON_SMP_DIR}/src/tx_semaphore_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_semaphore_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_semaphore_initialize.c + ${THREADX_COMMON_SMP_DIR}/src/tx_semaphore_performance_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_semaphore_performance_system_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_semaphore_prioritize.c + ${THREADX_COMMON_SMP_DIR}/src/tx_semaphore_put.c + ${THREADX_COMMON_SMP_DIR}/src/tx_semaphore_put_notify.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_create.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_delete.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_entry_exit_notify.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_identify.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_initialize.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_performance_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_performance_system_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_preemption_change.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_priority_change.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_relinquish.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_reset.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_resume.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_shell_entry.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_sleep.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_smp_core_exclude.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_smp_core_exclude_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_smp_current_state_set.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_smp_debug_entry_insert.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_smp_high_level_initialize.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_smp_rebalance_execute_list.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_smp_utilities.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_stack_analyze.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_stack_error_handler.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_stack_error_notify.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_suspend.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_system_preempt_check.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_system_resume.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_system_suspend.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_terminate.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_timeout.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_time_slice.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_time_slice_change.c + ${THREADX_COMMON_SMP_DIR}/src/tx_thread_wait_abort.c + ${THREADX_COMMON_SMP_DIR}/src/tx_time_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_timer_activate.c + ${THREADX_COMMON_SMP_DIR}/src/tx_timer_change.c + ${THREADX_COMMON_SMP_DIR}/src/tx_timer_create.c + ${THREADX_COMMON_SMP_DIR}/src/tx_timer_deactivate.c + ${THREADX_COMMON_SMP_DIR}/src/tx_timer_delete.c + ${THREADX_COMMON_SMP_DIR}/src/tx_timer_expiration_process.c + ${THREADX_COMMON_SMP_DIR}/src/tx_timer_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_timer_initialize.c + ${THREADX_COMMON_SMP_DIR}/src/tx_timer_performance_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_timer_performance_system_info_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_timer_smp_core_exclude.c + ${THREADX_COMMON_SMP_DIR}/src/tx_timer_smp_core_exclude_get.c + ${THREADX_COMMON_SMP_DIR}/src/tx_timer_system_activate.c + ${THREADX_COMMON_SMP_DIR}/src/tx_timer_system_deactivate.c + ${THREADX_COMMON_SMP_DIR}/src/tx_timer_thread_entry.c + ${THREADX_COMMON_SMP_DIR}/src/tx_time_set.c + ${THREADX_COMMON_SMP_DIR}/src/tx_trace_buffer_full_notify.c + ${THREADX_COMMON_SMP_DIR}/src/tx_trace_disable.c + ${THREADX_COMMON_SMP_DIR}/src/tx_trace_enable.c + ${THREADX_COMMON_SMP_DIR}/src/tx_trace_event_filter.c + ${THREADX_COMMON_SMP_DIR}/src/tx_trace_event_unfilter.c + ${THREADX_COMMON_SMP_DIR}/src/tx_trace_initialize.c + ${THREADX_COMMON_SMP_DIR}/src/tx_trace_interrupt_control.c + ${THREADX_COMMON_SMP_DIR}/src/tx_trace_isr_enter_insert.c + ${THREADX_COMMON_SMP_DIR}/src/tx_trace_isr_exit_insert.c + ${THREADX_COMMON_SMP_DIR}/src/tx_trace_object_register.c + ${THREADX_COMMON_SMP_DIR}/src/tx_trace_object_unregister.c + ${THREADX_COMMON_SMP_DIR}/src/tx_trace_user_event_insert.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_compile_definitions(threadx_smp PRIVATE TX_QUEUE_MESSAGE_MAX_SIZE=16) #This is addressed in PR #503 diff --git a/port/threadx_smp/cmake/BuildAsmOffsets.cmake b/port/threadx_smp/cmake/BuildAsmOffsets.cmake new file mode 100644 index 0000000..83d1ffa --- /dev/null +++ b/port/threadx_smp/cmake/BuildAsmOffsets.cmake @@ -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() diff --git a/port/threadx_smp/cmake/GenerateAsmOffsets.cmake b/port/threadx_smp/cmake/GenerateAsmOffsets.cmake new file mode 100644 index 0000000..4b598e3 --- /dev/null +++ b/port/threadx_smp/cmake/GenerateAsmOffsets.cmake @@ -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}") diff --git a/port/threadx_smp/inc/csr.h b/port/threadx_smp/inc/csr.h new file mode 100644 index 0000000..3b64b4f --- /dev/null +++ b/port/threadx_smp/inc/csr.h @@ -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 + +static inline uint64_t riscv_get_core() +{ + uint64_t x; + asm volatile("csrr %0, mhartid" : "=r"(x)); + return x; +} + +static inline uint64_t riscv_get_mstatus() +{ + uint64_t x; + asm volatile("csrr %0, mstatus" : "=r"(x)); + return x; +} + +static inline void riscv_writ_mstatus(uint64_t x) +{ + asm volatile("csrw mstatus, %0" : : "r"(x)); +} + +// machine exception program counter, holds the +// instruction address to which a return from +// exception will go. +static inline void riscv_writ_mepc(uint64_t x) +{ + asm volatile("csrw mepc, %0" : : "r"(x)); +} + +static inline uint64_t riscv_get_sstatus() +{ + uint64_t x; + asm volatile("csrr %0, sstatus" : "=r"(x)); + return x; +} + +static inline void riscv_writ_sstatus(uint64_t x) +{ + asm volatile("csrw sstatus, %0" : : "r"(x)); +} + +// Supervisor Interrupt Pending +static inline uint64_t riscv_get_sip() +{ + uint64_t x; + asm volatile("csrr %0, sip" : "=r"(x)); + return x; +} + +static inline void riscv_writ_sip(uint64_t x) +{ + asm volatile("csrw sip, %0" : : "r"(x)); +} + +static inline uint64_t riscv_get_sie() +{ + uint64_t x; + asm volatile("csrr %0, sie" : "=r"(x)); + return x; +} + +static inline void riscv_writ_sie(uint64_t x) +{ + asm volatile("csrw sie, %0" : : "r"(x)); +} + +static inline uint64_t riscv_get_mie() +{ + uint64_t x; + asm volatile("csrr %0, mie" : "=r"(x)); + return x; +} + +static inline void riscv_writ_mie(uint64_t x) +{ + asm volatile("csrw mie, %0" : : "r"(x)); +} + +// supervisor exception program counter, holds the +// instruction address to which a return from +// exception will go. +static inline void riscv_writ_sepc(uint64_t x) +{ + asm volatile("csrw sepc, %0" : : "r"(x)); +} + +static inline uint64_t riscv_get_sepc() +{ + uint64_t x; + asm volatile("csrr %0, sepc" : "=r"(x)); + return x; +} + +// Machine Exception Delegation +static inline uint64_t riscv_get_medeleg() +{ + uint64_t x; + asm volatile("csrr %0, medeleg" : "=r"(x)); + return x; +} + +static inline void riscv_writ_medeleg(uint64_t x) +{ + asm volatile("csrw medeleg, %0" : : "r"(x)); +} + +// Machine Interrupt Delegation +static inline uint64_t riscv_get_mideleg() +{ + uint64_t x; + asm volatile("csrr %0, mideleg" : "=r"(x)); + return x; +} + +static inline void riscv_writ_mideleg(uint64_t x) +{ + asm volatile("csrw mideleg, %0" : : "r"(x)); +} + +// Supervisor Trap-Vector Base Address +// low two bits are mode. +static inline void riscv_writ_stvec(uint64_t x) +{ + asm volatile("csrw stvec, %0" : : "r"(x)); +} + +static inline uint64_t riscv_get_stvec() +{ + uint64_t x; + asm volatile("csrr %0, stvec" : "=r"(x)); + return x; +} + +// Supervisor Timer Comparison Register +static inline uint64_t riscv_get_stimecmp() +{ + uint64_t x; + // asm volatile("csrr %0, stimecmp" : "=r" (x) ); + asm volatile("csrr %0, 0x14d" : "=r"(x)); + return x; +} + +static inline void riscv_writ_stimecmp(uint64_t x) +{ + // asm volatile("csrw stimecmp, %0" : : "r" (x)); + asm volatile("csrw 0x14d, %0" : : "r"(x)); +} + +// Machine Environment Configuration Register +static inline uint64_t riscv_get_menvcfg() +{ + uint64_t x; + // asm volatile("csrr %0, menvcfg" : "=r" (x) ); + asm volatile("csrr %0, 0x30a" : "=r"(x)); + return x; +} + +static inline void riscv_writ_menvcfg(uint64_t x) +{ + // asm volatile("csrw menvcfg, %0" : : "r" (x)); + asm volatile("csrw 0x30a, %0" : : "r"(x)); +} + +// Physical Memory Protection +static inline void riscv_writ_pmpcfg0(uint64_t x) +{ + asm volatile("csrw pmpcfg0, %0" : : "r"(x)); +} + +static inline void riscv_writ_pmpaddr0(uint64_t x) +{ + asm volatile("csrw pmpaddr0, %0" : : "r"(x)); +} + +// supervisor address translation and protection; +// holds the address of the page table. +static inline void riscv_writ_satp(uint64_t x) +{ + asm volatile("csrw satp, %0" : : "r"(x)); +} + +static inline uint64_t riscv_get_satp() +{ + uint64_t x; + asm volatile("csrr %0, satp" : "=r"(x)); + return x; +} + +// Supervisor Trap Cause +static inline uint64_t riscv_get_scause() +{ + uint64_t x; + asm volatile("csrr %0, scause" : "=r"(x)); + return x; +} + +// Supervisor Trap Value +static inline uint64_t riscv_get_stval() +{ + uint64_t x; + asm volatile("csrr %0, stval" : "=r"(x)); + return x; +} + +// Machine-mode Counter-Enable +static inline void riscv_writ_mcounteren(uint64_t x) +{ + asm volatile("csrw mcounteren, %0" : : "r"(x)); +} + +static inline uint64_t riscv_get_mcounteren() +{ + uint64_t x; + asm volatile("csrr %0, mcounteren" : "=r"(x)); + return x; +} + +// machine-mode cycle counter +static inline uint64_t riscv_get_time() +{ + uint64_t x; + asm volatile("csrr %0, time" : "=r"(x)); + return x; +} + +// enable device interrupts +static inline void riscv_sintr_on() +{ + uint64_t sstatus = riscv_get_sstatus(); + sstatus |= SSTATUS_SIE; + riscv_writ_sstatus(sstatus); +} + +// disable device interrupts +static inline void riscv_sintr_off() +{ + uint64_t sstatus = riscv_get_sstatus(); + sstatus &= (~SSTATUS_SIE); + riscv_writ_sstatus(sstatus); +} + +// are device interrupts enabled? +static inline int riscv_sintr_get() +{ + uint64_t x = riscv_get_sstatus(); + return (x & SSTATUS_SIE) != 0; +} + +static inline void riscv_sintr_restore(int x) +{ + if (x) + riscv_sintr_on(); + else + riscv_sintr_off(); +} + +// enable device interrupts +static inline void riscv_mintr_on() +{ + uint64_t mstatus = riscv_get_mstatus(); + mstatus |= MSTATUS_MIE; + riscv_writ_mstatus(mstatus); +} + +// disable device interrupts +static inline void riscv_mintr_off() +{ + uint64_t mstatus = riscv_get_mstatus(); + mstatus &= (~MSTATUS_MIE); + riscv_writ_mstatus(mstatus); +} + +// are device interrupts enabled? +static inline int riscv_mintr_get() +{ + uint64_t x = riscv_get_mstatus(); + return (x & MSTATUS_MIE) != 0; +} + +static inline void riscv_mintr_restore(int x) +{ + if (x) + riscv_mintr_on(); + else + riscv_mintr_off(); +} + +static inline uint64_t riscv_get_sp() +{ + uint64_t x; + asm volatile("mv %0, sp" : "=r"(x)); + return x; +} + +// read and write tp, the thread pointer, which xv6 uses to hold +// this core's hartid (core number), the index into cpus[]. +static inline uint64_t riscv_get_tp() +{ + uint64_t x; + asm volatile("mv %0, tp" : "=r"(x)); + return x; +} + +static inline void riscv_writ_tp(uint64_t x) +{ + asm volatile("mv tp, %0" : : "r"(x)); +} + +static inline uint64_t riscv_get_ra() +{ + uint64_t x; + asm volatile("mv %0, ra" : "=r"(x)); + return x; +} + +// flush the TLB. +static inline void sfence_vma() +{ + // the zero, zero means flush all TLB entries. + asm volatile("sfence.vma zero, zero"); +} + +#endif // __ASSEMBLER__ + +#endif diff --git a/port/threadx_smp/inc/tx_port.h b/port/threadx_smp/inc/tx_port.h new file mode 100644 index 0000000..1b0f0c2 --- /dev/null +++ b/port/threadx_smp/inc/tx_port.h @@ -0,0 +1,452 @@ +/*************************************************************************** + * 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 AMOSWAP_AQ amoswap.d.aq +#define AMOSWAP_RL amoswap.d.rl +#define LOG_REGBYTES 3 +#define TX_THREAD_SMP_LOCK_READY_BIT_OFFSET 312 // This changes if thread or timer internal extensions are used +#else +#define SLL32 sll +#define STORE sw +#define LOAD lw +#define LWU lw +#define AMOSWAP_AQ amoswap.w.aq +#define AMOSWAP_RL amoswap.w.rl +#define LOG_REGBYTES 2 +#define TX_THREAD_SMP_LOCK_READY_BIT_OFFSET 168 // This changes if thread or timer internal extensions are used +#endif +#define REGBYTES (1 << LOG_REGBYTES) + +#else /*not __ASSEMBLER__ */ + +/************* Define ThreadX SMP constants. *************/ + +#define TX_DISABLE_INLINE + +/* 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 +#include + +/* 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 long 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 automated regression test extensions. These are required for the + ThreadX regression tests. */ + +typedef unsigned int TEST_FLAG; +extern TEST_FLAG threadx_byte_allocate_loop_test; +extern TEST_FLAG threadx_byte_release_loop_test; +extern TEST_FLAG threadx_mutex_suspension_put_test; +extern TEST_FLAG threadx_mutex_suspension_priority_test; +#ifndef TX_TIMER_PROCESS_IN_ISR +extern TEST_FLAG threadx_delete_timer_thread; +#endif + +extern void abort_and_resume_byte_allocating_thread(void); +extern void abort_all_threads_suspended_on_mutex(void); +extern void suspend_lowest_priority(void); +#ifndef TX_TIMER_PROCESS_IN_ISR +extern void delete_timer_thread(void); +#endif +extern TEST_FLAG test_stack_analyze_flag; +extern TEST_FLAG test_initialize_flag; +extern TEST_FLAG test_forced_mutex_timeout; + +#ifdef TX_REGRESSION_TEST + +/* Define extension macros for automated coverage tests. */ + +#define TX_BYTE_ALLOCATE_EXTENSION \ + if (threadx_byte_allocate_loop_test == ((TEST_FLAG)1)) \ + { \ + pool_ptr->tx_byte_pool_owner = TX_NULL; \ + threadx_byte_allocate_loop_test = ((TEST_FLAG)0); \ + } + +#define TX_BYTE_RELEASE_EXTENSION \ + if (threadx_byte_release_loop_test == ((TEST_FLAG)1)) \ + { \ + threadx_byte_release_loop_test = ((TEST_FLAG)0); \ + abort_and_resume_byte_allocating_thread(); \ + } + +#define TX_MUTEX_PUT_EXTENSION_1 \ + if (threadx_mutex_suspension_put_test == ((TEST_FLAG)1)) \ + { \ + threadx_mutex_suspension_put_test = ((TEST_FLAG)0); \ + abort_all_threads_suspended_on_mutex(); \ + } + +#define TX_MUTEX_PUT_EXTENSION_2 \ + if (test_forced_mutex_timeout == ((TEST_FLAG)1)) \ + { \ + test_forced_mutex_timeout = ((TEST_FLAG)0); \ + _tx_thread_wait_abort(mutex_ptr->tx_mutex_suspension_list); \ + } + +#define TX_MUTEX_PRIORITY_CHANGE_EXTENSION \ + if (threadx_mutex_suspension_priority_test == ((TEST_FLAG)1)) \ + { \ + threadx_mutex_suspension_priority_test = ((TEST_FLAG)0); \ + suspend_lowest_priority(); \ + } + +#ifndef TX_TIMER_PROCESS_IN_ISR + +#define TX_TIMER_INITIALIZE_EXTENSION(a) \ + if (threadx_delete_timer_thread == ((TEST_FLAG)1)) \ + { \ + threadx_delete_timer_thread = ((TEST_FLAG)0); \ + delete_timer_thread(); \ + (a) = ((UINT)1); \ + } + +#endif + +#define TX_THREAD_STACK_ANALYZE_EXTENSION \ + if (test_stack_analyze_flag == ((TEST_FLAG)1)) \ + { \ + thread_ptr->tx_thread_id = ((TEST_FLAG)0); \ + test_stack_analyze_flag = ((TEST_FLAG)0); \ + } \ + else if (test_stack_analyze_flag == ((TEST_FLAG)2)) \ + { \ + stack_ptr = thread_ptr->tx_thread_stack_start; \ + test_stack_analyze_flag = ((TEST_FLAG)0); \ + } \ + else if (test_stack_analyze_flag == ((TEST_FLAG)3)) \ + { \ + *stack_ptr = TX_STACK_FILL; \ + test_stack_analyze_flag = ((TEST_FLAG)0); \ + } \ + else \ + { \ + test_stack_analyze_flag = ((TEST_FLAG)0); \ + } + +#define TX_INITIALIZE_KERNEL_ENTER_EXTENSION \ + if (test_initialize_flag == ((TEST_FLAG)1)) \ + { \ + test_initialize_flag = ((TEST_FLAG)0); \ + return; \ + } + +#endif + +/* 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 ULONG tx_saved_posture; + +#define TX_DISABLE tx_saved_posture = _tx_thread_smp_protect(); +#define TX_RESTORE _tx_thread_smp_unprotect(tx_saved_posture); + +/************* 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 diff --git a/port/threadx_smp/src/tx_initialize_low_level.S b/port/threadx_smp/src/tx_initialize_low_level.S new file mode 100644 index 0000000..5a4951f --- /dev/null +++ b/port/threadx_smp/src/tx_initialize_low_level.S @@ -0,0 +1,164 @@ +/*************************************************************************** + * 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 diff --git a/port/threadx_smp/src/tx_thread_context_restore.S b/port/threadx_smp/src/tx_thread_context_restore.S new file mode 100644 index 0000000..0335047 --- /dev/null +++ b/port/threadx_smp/src/tx_thread_context_restore.S @@ -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, LOG_REGBYTES // 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 + LOAD t1, 0(t0) // Pickup nested interrupt count + addi t1, t1, -1 // Decrement the nested interrupt counter + STORE t1, 0(t0) // Store new nested count + beqz t1, _tx_thread_not_nested_restore // If 0, not nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + /* Recover floating point registers. */ +#if defined(__riscv_float_abi_single) + flw f0, 31*REGBYTES(sp) // Recover ft0 + flw f1, 32*REGBYTES(sp) // Recover ft1 + flw f2, 33*REGBYTES(sp) // Recover ft2 + flw f3, 34*REGBYTES(sp) // Recover ft3 + flw f4, 35*REGBYTES(sp) // Recover ft4 + flw f5, 36*REGBYTES(sp) // Recover ft5 + flw f6, 37*REGBYTES(sp) // Recover ft6 + flw f7, 38*REGBYTES(sp) // Recover ft7 + flw f10,41*REGBYTES(sp) // Recover fa0 + flw f11,42*REGBYTES(sp) // Recover fa1 + flw f12,43*REGBYTES(sp) // Recover fa2 + flw f13,44*REGBYTES(sp) // Recover fa3 + flw f14,45*REGBYTES(sp) // Recover fa4 + flw f15,46*REGBYTES(sp) // Recover fa5 + flw f16,47*REGBYTES(sp) // Recover fa6 + flw f17,48*REGBYTES(sp) // Recover fa7 + flw f28,59*REGBYTES(sp) // Recover ft8 + flw f29,60*REGBYTES(sp) // Recover ft9 + flw f30,61*REGBYTES(sp) // Recover ft10 + flw f31,62*REGBYTES(sp) // Recover ft11 + lw t0, 63*REGBYTES(sp) // Recover fcsr + csrw fcsr, t0 // +#elif defined(__riscv_float_abi_double) + fld f0, 31*REGBYTES(sp) // Recover ft0 + fld f1, 32*REGBYTES(sp) // Recover ft1 + fld f2, 33*REGBYTES(sp) // Recover ft2 + fld f3, 34*REGBYTES(sp) // Recover ft3 + fld f4, 35*REGBYTES(sp) // Recover ft4 + fld f5, 36*REGBYTES(sp) // Recover ft5 + fld f6, 37*REGBYTES(sp) // Recover ft6 + fld f7, 38*REGBYTES(sp) // Recover ft7 + fld f10,41*REGBYTES(sp) // Recover fa0 + fld f11,42*REGBYTES(sp) // Recover fa1 + fld f12,43*REGBYTES(sp) // Recover fa2 + fld f13,44*REGBYTES(sp) // Recover fa3 + fld f14,45*REGBYTES(sp) // Recover fa4 + fld f15,46*REGBYTES(sp) // Recover fa5 + fld f16,47*REGBYTES(sp) // Recover fa6 + fld f17,48*REGBYTES(sp) // Recover fa7 + fld f28,59*REGBYTES(sp) // Recover ft8 + fld f29,60*REGBYTES(sp) // Recover ft9 + fld f30,61*REGBYTES(sp) // Recover ft10 + fld f31,62*REGBYTES(sp) // Recover ft11 + LOAD t0, 63*REGBYTES(sp) // Recover fcsr + csrw fcsr, t0 // +#endif + + /* Recover standard registers. */ + + /* Restore registers, + Skip global pointer because that does not change. + Also skip the saved registers since they have been restored by any function we called, + except s0 since we use it ourselves. */ + + LOAD t0, 30*REGBYTES(sp) // Recover mepc + csrw mepc, t0 // Setup mepc + li t0, 0x1880 // Prepare MPIP +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + li t1, 1<<13 + or t0, t1, t0 +#endif + csrw mstatus, t0 // Enable MPIP + + LOAD x1, 28*REGBYTES(sp) // Recover RA + LOAD x5, 19*REGBYTES(sp) // Recover t0 + LOAD x6, 18*REGBYTES(sp) // Recover t1 + LOAD x7, 17*REGBYTES(sp) // Recover t2 + LOAD x8, 12*REGBYTES(sp) // Recover s0 + LOAD x10, 27*REGBYTES(sp) // Recover a0 + LOAD x11, 26*REGBYTES(sp) // Recover a1 + LOAD x12, 25*REGBYTES(sp) // Recover a2 + LOAD x13, 24*REGBYTES(sp) // Recover a3 + LOAD x14, 23*REGBYTES(sp) // Recover a4 + LOAD x15, 22*REGBYTES(sp) // Recover a5 + LOAD x16, 21*REGBYTES(sp) // Recover a6 + LOAD x17, 20*REGBYTES(sp) // Recover a7 + LOAD x28, 16*REGBYTES(sp) // Recover t3 + LOAD x29, 15*REGBYTES(sp) // Recover t4 + LOAD x30, 14*REGBYTES(sp) // Recover t5 + LOAD x31, 13*REGBYTES(sp) // Recover t6 + +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + addi sp, sp, 65*REGBYTES // Recover stack frame - with floating point enabled +#else + addi sp, sp, 32*REGBYTES // Recover stack frame - without floating point enabled +#endif + mret // Return to point of interrupt + + /* } */ +_tx_thread_not_nested_restore: + /* Determine if a thread was interrupted and no preemption is required. */ + /* else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + || (_tx_thread_preempt_disable)) + { */ + + 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 + LOAD t2, 1*REGBYTES(t0) // Pickup owning hart + bne t2, t3, _tx_thread_preempt_restore // If owned by another hart, preempt + + LWU 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 + LOAD t2, 0(t0) // Pickup time slice + beqz t2, _tx_thread_dont_save_ts // If 0, skip time slice processing + + /* _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice + _tx_timer_time_slice = 0; */ + + STORE t2, 6*REGBYTES(t1) // Save current time slice + STORE x0, 0(t0) // Clear global time slice + + + /* } */ +_tx_thread_dont_save_ts: + /* Clear the current task pointer. */ + /* _tx_thread_current_ptr = TX_NULL; */ + + /* Return to the scheduler. */ + /* _tx_thread_schedule(); */ + + 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_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 + +/* } */ diff --git a/port/threadx_smp/src/tx_thread_context_save.S b/port/threadx_smp/src/tx_thread_context_save.S new file mode 100644 index 0000000..13b8d28 --- /dev/null +++ b/port/threadx_smp/src/tx_thread_context_save.S @@ -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, LOG_REGBYTES // 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 + LOAD 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 + STORE 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 + STORE 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 diff --git a/port/threadx_smp/src/tx_thread_interrupt_control.S b/port/threadx_smp/src/tx_thread_interrupt_control.S new file mode 100644 index 0000000..c75c7c4 --- /dev/null +++ b/port/threadx_smp/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/*************************************************************************** + * 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 */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + RETURN_MASK = 0x000000000000000F + SET_SR_MASK = 0xFFFFFFFFFFFFFFF0 + + .section .text +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control RISC-V64/GNU */ +/* 6.2.1 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 03-08-2023 Scott Larson Initial Version 6.2.1 */ +/* */ +/**************************************************************************/ +/* UINT _tx_thread_interrupt_control(UINT new_posture) +{ */ + .global _tx_thread_interrupt_control +_tx_thread_interrupt_control: + /* Pickup current interrupt lockout posture. */ + + csrr t0, mstatus + mv t1, t0 // Save original mstatus for return + + /* Apply the new interrupt posture. */ + + li t2, SET_SR_MASK // Build set SR mask + and t0, t0, t2 // Isolate interrupt lockout bits + or t0, t0, a0 // Put new lockout bits in + csrw mstatus, t0 + andi a0, t1, RETURN_MASK // Return original mstatus. + ret +/* } */ diff --git a/port/threadx_smp/src/tx_thread_schedule.S b/port/threadx_smp/src/tx_thread_schedule.S new file mode 100644 index 0000000..38f891d --- /dev/null +++ b/port/threadx_smp/src/tx_thread_schedule.S @@ -0,0 +1,332 @@ +/*************************************************************************** + * 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: + 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 +#ifdef TX_ENABLE_WFI + wfi // Wait for interrupt +#endif + j _tx_thread_schedule_loop // Keep looking for a thread + +_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_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_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 + +/* } */ diff --git a/port/threadx_smp/src/tx_thread_smp_core_get.S b/port/threadx_smp/src/tx_thread_smp_core_get.S new file mode 100644 index 0000000..fd89b7a --- /dev/null +++ b/port/threadx_smp/src/tx_thread_smp_core_get.S @@ -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 + + diff --git a/port/threadx_smp/src/tx_thread_smp_core_preempt.c b/port/threadx_smp/src/tx_thread_smp_core_preempt.c new file mode 100644 index 0000000..cf11396 --- /dev/null +++ b/port/threadx_smp/src/tx_thread_smp_core_preempt.c @@ -0,0 +1,6 @@ +#include +#include +void _tx_thread_smp_core_preempt(UINT target_core) +{ + send_ipi(target_core); +} \ No newline at end of file diff --git a/port/threadx_smp/src/tx_thread_smp_current_state_get.S b/port/threadx_smp/src/tx_thread_smp_current_state_get.S new file mode 100644 index 0000000..4fa5ae1 --- /dev/null +++ b/port/threadx_smp/src/tx_thread_smp_current_state_get.S @@ -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, LOG_REGBYTES // Build offset into array + add t1, t1, t0 // Select this hart's slot + LOAD a0, 0(t1) // Return current system state + ret diff --git a/port/threadx_smp/src/tx_thread_smp_current_thread_get.S b/port/threadx_smp/src/tx_thread_smp_current_thread_get.S new file mode 100644 index 0000000..6f6ec97 --- /dev/null +++ b/port/threadx_smp/src/tx_thread_smp_current_thread_get.S @@ -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 diff --git a/port/threadx_smp/src/tx_thread_smp_initialize_wait.S b/port/threadx_smp/src/tx_thread_smp_initialize_wait.S new file mode 100644 index 0000000..64b55c5 --- /dev/null +++ b/port/threadx_smp/src/tx_thread_smp_initialize_wait.S @@ -0,0 +1,75 @@ +#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, LOG_REGBYTES // 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 +#if __riscv_xlen == 64 + slli t3, t3, 32 + srli t3, t3, 32 +#endif + 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: + LOAD 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: + LOAD t6, 0(t3) // Pickup release flag + beqz t6, _tx_thread_smp_wait_for_release + + /* Acknowledge the release by clearing this hart's system state. */ + STORE x0, 0(t5) // Set this hart's system state to zero + + /* Wait for core 0 to finish initialization. */ +_tx_thread_smp_wait_for_core0: + LOAD 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 diff --git a/port/threadx_smp/src/tx_thread_smp_low_level_initialize.S b/port/threadx_smp/src/tx_thread_smp_low_level_initialize.S new file mode 100644 index 0000000..fb8536e --- /dev/null +++ b/port/threadx_smp/src/tx_thread_smp_low_level_initialize.S @@ -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 diff --git a/port/threadx_smp/src/tx_thread_smp_protect.S b/port/threadx_smp/src/tx_thread_smp_protect.S new file mode 100644 index 0000000..8057bda --- /dev/null +++ b/port/threadx_smp/src/tx_thread_smp_protect.S @@ -0,0 +1,43 @@ +#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. */ + LOAD t3, 1*REGBYTES(t1) // Pickup owning hart + beq t3, t2, _owned // Already owned by this hart + + /* Try to get the protection. */ + LOAD t4, 0(t1) // Pickup protection flag + bnez t4, _protection_busy // If set, protection is busy + li t4, 1 // Build lock value + AMOSWAP_AQ t5, t4, (t1) // Attempt to get protection + bnez t5, _protection_busy // If old value != 0, retry + + fence rw, rw // Ensure lock acquisition is visible + STORE t2, 1*REGBYTES(t1) // Save owning hart + +_owned: + LOAD t5, 2*REGBYTES(t1) // Pickup ownership count + addi t5, t5, 1 // Increment ownership count + STORE t5, 2*REGBYTES(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 diff --git a/port/threadx_smp/src/tx_thread_smp_unprotect.S b/port/threadx_smp/src/tx_thread_smp_unprotect.S new file mode 100644 index 0000000..2a21a5a --- /dev/null +++ b/port/threadx_smp/src/tx_thread_smp_unprotect.S @@ -0,0 +1,42 @@ +#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. */ + LOAD t3, 1*REGBYTES(t2) // Pickup owning hart + bne t1, t3, _still_protected // Not owner, skip release + + /* Pickup and decrement the protection count. */ + LOAD t3, 2*REGBYTES(t2) // Pickup protection count + beqz t3, _still_protected // Already cleared + addi t3, t3, -1 // Decrement protection count + STORE t3, 2*REGBYTES(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 + STORE t3, 1*REGBYTES(t2) // Mark owning hart invalid + AMOSWAP_RL x0, x0, (t2) // Release protection flag + +_still_protected: + csrw mstatus, a0 // Restore interrupt posture + ret diff --git a/port/threadx_smp/src/tx_thread_stack_build.S b/port/threadx_smp/src/tx_thread_stack_build.S new file mode 100644 index 0000000..8eef6dd --- /dev/null +++ b/port/threadx_smp/src/tx_thread_stack_build.S @@ -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, 4*REGBYTES(a0) // Pickup end of stack area + andi t0, t0, -16 // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + addi t0, t0, -65*REGBYTES +#else + addi t0, t0, -32*REGBYTES // Allocate space for the stack frame +#endif + li t1, 1 // Build stack type + STORE t1, 0*REGBYTES(t0) // Place stack type on the top + STORE x0, 1*REGBYTES(t0) // Initial s11 + STORE x0, 2*REGBYTES(t0) // Initial s10 + STORE x0, 3*REGBYTES(t0) // Initial s9 + STORE x0, 4*REGBYTES(t0) // Initial s8 + STORE x0, 5*REGBYTES(t0) // Initial s7 + STORE x0, 6*REGBYTES(t0) // Initial s6 + STORE x0, 7*REGBYTES(t0) // Initial s5 + STORE x0, 8*REGBYTES(t0) // Initial s4 + STORE x0, 9*REGBYTES(t0) // Initial s3 + STORE x0, 10*REGBYTES(t0) // Initial s2 + STORE x0, 11*REGBYTES(t0) // Initial s1 + STORE x0, 12*REGBYTES(t0) // Initial s0 + STORE x0, 13*REGBYTES(t0) // Initial t6 + STORE x0, 14*REGBYTES(t0) // Initial t5 + STORE x0, 15*REGBYTES(t0) // Initial t4 + STORE x0, 16*REGBYTES(t0) // Initial t3 + STORE x0, 17*REGBYTES(t0) // Initial t2 + STORE x0, 18*REGBYTES(t0) // Initial t1 + STORE x0, 19*REGBYTES(t0) // Initial t0 + STORE x0, 20*REGBYTES(t0) // Initial a7 + STORE x0, 21*REGBYTES(t0) // Initial a6 + STORE x0, 22*REGBYTES(t0) // Initial a5 + STORE x0, 23*REGBYTES(t0) // Initial a4 + STORE x0, 24*REGBYTES(t0) // Initial a3 + STORE x0, 25*REGBYTES(t0) // Initial a2 + STORE x0, 26*REGBYTES(t0) // Initial a1 + STORE x0, 27*REGBYTES(t0) // Initial a0 + STORE x0, 28*REGBYTES(t0) // Initial ra + STORE a1, 30*REGBYTES(t0) // Initial mepc +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + STORE x0, 31*REGBYTES(t0) // Inital ft0 + STORE x0, 32*REGBYTES(t0) // Inital ft1 + STORE x0, 33*REGBYTES(t0) // Inital ft2 + STORE x0, 34*REGBYTES(t0) // Inital ft3 + STORE x0, 35*REGBYTES(t0) // Inital ft4 + STORE x0, 36*REGBYTES(t0) // Inital ft5 + STORE x0, 37*REGBYTES(t0) // Inital ft6 + STORE x0, 38*REGBYTES(t0) // Inital ft7 + STORE x0, 39*REGBYTES(t0) // Inital fs0 + STORE x0, 40*REGBYTES(t0) // Inital fs1 + STORE x0, 41*REGBYTES(t0) // Inital fa0 + STORE x0, 42*REGBYTES(t0) // Inital fa1 + STORE x0, 43*REGBYTES(t0) // Inital fa2 + STORE x0, 44*REGBYTES(t0) // Inital fa3 + STORE x0, 45*REGBYTES(t0) // Inital fa4 + STORE x0, 46*REGBYTES(t0) // Inital fa5 + STORE x0, 47*REGBYTES(t0) // Inital fa6 + STORE x0, 48*REGBYTES(t0) // Inital fa7 + STORE x0, 49*REGBYTES(t0) // Inital fs2 + STORE x0, 50*REGBYTES(t0) // Inital fs3 + STORE x0, 51*REGBYTES(t0) // Inital fs4 + STORE x0, 52*REGBYTES(t0) // Inital fs5 + STORE x0, 53*REGBYTES(t0) // Inital fs6 + STORE x0, 54*REGBYTES(t0) // Inital fs7 + STORE x0, 55*REGBYTES(t0) // Inital fs8 + STORE x0, 56*REGBYTES(t0) // Inital fs9 + STORE x0, 57*REGBYTES(t0) // Inital fs10 + STORE x0, 58*REGBYTES(t0) // Inital fs11 + STORE x0, 59*REGBYTES(t0) // Inital ft8 + STORE x0, 60*REGBYTES(t0) // Inital ft9 + STORE x0, 61*REGBYTES(t0) // Inital ft10 + STORE x0, 62*REGBYTES(t0) // Inital ft11 + csrr a1, fcsr // Read fcsr and use it for initial value for each thread + STORE a1, 63*REGBYTES(t0) // Initial fscr + STORE x0, 64*REGBYTES(t0) // Reserved word (0) +#else + STORE x0, 31*REGBYTES(t0) // Reserved word (0) +#endif + + /* Setup stack pointer. */ + /* thread_ptr -> tx_thread_stack_ptr = t0; */ + + STORE t0, 2*REGBYTES(a0) // Save stack pointer in thread's + addi t1, x0, 1 // Build ready flag + STORE t1, TX_THREAD_SMP_LOCK_READY_BIT_OFFSET(a0) // Set ready flag + ret // control block and return +/* } */ diff --git a/port/threadx_smp/src/tx_thread_system_return.S b/port/threadx_smp/src/tx_thread_system_return.S new file mode 100644 index 0000000..f752462 --- /dev/null +++ b/port/threadx_smp/src/tx_thread_system_return.S @@ -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, LOG_REGBYTES // 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 + LOAD t3, 0(t4) // Pickup time slice value + la t2, _tx_thread_schedule // Pickup address of scheduling loop + beqz t3, _tx_thread_dont_save_ts // If no time-slice, don't save it + + /* Save time-slice for the thread and clear the current time-slice. */ + /* _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + _tx_timer_time_slice = 0; */ + + STORE t3, 6*REGBYTES(t1) // Save current time-slice for thread + STORE x0, 0(t4) // Clear time-slice variable + + /* } */ +_tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + /* _tx_thread_current_ptr = TX_NULL; */ + + STORE x0, 0(t0) // Clear current thread pointer + + /* 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_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 + STORE x0, 2*REGBYTES(t3) // Clear protection count + li t4, -1 // Build invalid owner value + STORE t4, 1*REGBYTES(t3) // Invalidate owning hart + fence rw, rw // Ensure shared accesses complete before unlock + STORE x0, 0(t3) // Clear protection in-force flag + jr t2 // Return to thread scheduler + +/* } */ diff --git a/port/threadx_smp/src/tx_timer_interrupt.c b/port/threadx_smp/src/tx_timer_interrupt.c new file mode 100644 index 0000000..bbb07ae --- /dev/null +++ b/port/threadx_smp/src/tx_timer_interrupt.c @@ -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); +} diff --git a/test/smp/CMakeLists.txt b/test/smp/CMakeLists.txt new file mode 100644 index 0000000..fe543e0 --- /dev/null +++ b/test/smp/CMakeLists.txt @@ -0,0 +1,275 @@ +cmake_minimum_required(VERSION 3.21) + +set(BUILD_SHARED_LIBS OFF) +get_filename_component(THREADX4TGFS_ROOT "${CMAKE_CURRENT_LIST_DIR}/../.." ABSOLUTE) + +if(NOT DEFINED CMAKE_TOOLCHAIN_FILE) + set(CMAKE_TOOLCHAIN_FILE + ${THREADX4TGFS_ROOT}/cmake/rv32imac_gnu.cmake + CACHE FILEPATH "Toolchain file") +endif() +include(${CMAKE_TOOLCHAIN_FILE}) + +project(threadx_smp_regression LANGUAGES C ASM) +enable_testing() + +set(TARGET_MEM "ram_dram" CACHE STRING "memory map to use") +set(CMAKE_EXECUTABLE_SUFFIX_C ".elf") + +set(THREADX_TEST_SIMULATOR + "" + CACHE FILEPATH "Path to the RISC-V VP executable used by CTest") +if(NOT THREADX_TEST_SIMULATOR) + message(FATAL_ERROR + "THREADX_TEST_SIMULATOR is not set. Configure with -DTHREADX_TEST_SIMULATOR=/path/to/riscv-vp") +endif() + +if(CMAKE_SYSTEM_PROCESSOR STREQUAL "risc-v32") + set(THREADX_TEST_ISA "rv32imac_m") +elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "risc-v64") + set(THREADX_TEST_ISA "rv64imac_m") +else() + message(FATAL_ERROR + "Unsupported CMAKE_SYSTEM_PROCESSOR for VP ISA selection: ${CMAKE_SYSTEM_PROCESSOR}") +endif() + +set(__THREAD_LOCAL_STORAGE OFF) +add_subdirectory(${THREADX4TGFS_ROOT}/third-party/picolibc ${CMAKE_BINARY_DIR}/picolibc) +target_link_libraries(c PUBLIC gcc) + +add_subdirectory(${THREADX4TGFS_ROOT}/port/threadx_smp ${CMAKE_BINARY_DIR}/port/threadx_smp) +target_link_libraries(threadx_smp PUBLIC c) +target_compile_definitions(threadx_smp PUBLIC TX_REGRESSION_TEST TX_THREAD_SMP_ONLY_CORE_0_DEFAULT TX_SMP_NOT_POSSIBLE) + +set(MOONLIGHT_TRAP_SOURCE_INPUT ${THREADX4TGFS_ROOT}/port/moonlight/src/trap_non_vectored.c) +set(MOONLIGHT_TRAP_SOURCE ${CMAKE_BINARY_DIR}/generated/trap_non_vectored.c) +add_custom_command( + OUTPUT ${MOONLIGHT_TRAP_SOURCE} + COMMAND bash ${THREADX4TGFS_ROOT}/test/threadx/generate_trap_file.sh + ${MOONLIGHT_TRAP_SOURCE_INPUT} + ${MOONLIGHT_TRAP_SOURCE} + DEPENDS ${MOONLIGHT_TRAP_SOURCE_INPUT} + ${THREADX4TGFS_ROOT}/test/threadx/generate_trap_file.sh + VERBATIM +) +add_custom_target(threadx_regression_generated_trap DEPENDS ${MOONLIGHT_TRAP_SOURCE}) +set_source_files_properties(${MOONLIGHT_TRAP_SOURCE} PROPERTIES GENERATED TRUE) + +add_subdirectory(${THREADX4TGFS_ROOT}/port/moonlight ${CMAKE_BINARY_DIR}/port/moonlight) +add_dependencies(moonlight_platform_common_smp threadx_regression_generated_trap) + +function(setup_target TARGET) + set(options) + set(oneValueArgs PLATFORM_TARGET) + set(multiValueArgs LIBRARIES SOURCES) + cmake_parse_arguments(ST "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + if(ST_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "setup_target(${TARGET} ...): unknown args: ${ST_UNPARSED_ARGUMENTS}") + endif() + + add_executable(${TARGET}) + set_target_properties(${TARGET} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR} + ) + if(ST_PLATFORM_TARGET) + target_add_moonlight_platform(${TARGET} PLATFORM_TARGET ${ST_PLATFORM_TARGET}) + else() + target_add_moonlight_platform(${TARGET}) + endif() + + if(ST_SOURCES) + target_sources(${TARGET} PRIVATE ${ST_SOURCES}) + endif() + + if(ST_LIBRARIES) + target_link_libraries(${TARGET} PRIVATE ${ST_LIBRARIES}) + endif() + + target_link_options(${TARGET} PRIVATE + -nostartfiles + -nostdlib + -T ${THREADX4TGFS_ROOT}/src/${TARGET_MEM}.lds + -Wl,--gc-sections + -Wl,-Map=${CMAKE_BINARY_DIR}/${TARGET}.map) + + add_custom_command(TARGET ${TARGET} POST_BUILD + COMMAND ${OBJCOPY} -O ihex $ ${CMAKE_BINARY_DIR}/${TARGET}.hex + COMMAND ${OBJCOPY} -O binary $ ${CMAKE_BINARY_DIR}/${TARGET}.bin + COMMAND ${OBJDUMP} -S $ > ${CMAKE_BINARY_DIR}/${TARGET}.dis + ) +endfunction() + +set(TX_REGRESSION_DIR + ${THREADX4TGFS_ROOT}/third-party/threadx/test/smp/regression +) +set(TX_CMAKE_DIR + ${THREADX4TGFS_ROOT}/third-party/threadx/test/tx/cmake +) + +set(TX_REGRESSION_CASES + ${TX_REGRESSION_DIR}/threadx_block_memory_basic_test.c + ${TX_REGRESSION_DIR}/threadx_block_memory_error_detection_test.c + ${TX_REGRESSION_DIR}/threadx_block_memory_information_test.c + ${TX_REGRESSION_DIR}/threadx_block_memory_prioritize_test.c + ${TX_REGRESSION_DIR}/threadx_block_memory_suspension_test.c + ${TX_REGRESSION_DIR}/threadx_block_memory_suspension_timeout_test.c + ${TX_REGRESSION_DIR}/threadx_block_memory_thread_terminate_test.c + ${TX_REGRESSION_DIR}/threadx_byte_memory_basic_test.c + ${TX_REGRESSION_DIR}/threadx_byte_memory_information_test.c + ${TX_REGRESSION_DIR}/threadx_byte_memory_prioritize_test.c + ${TX_REGRESSION_DIR}/threadx_byte_memory_suspension_test.c + ${TX_REGRESSION_DIR}/threadx_byte_memory_suspension_timeout_test.c + ${TX_REGRESSION_DIR}/threadx_byte_memory_thread_contention_test.c + ${TX_REGRESSION_DIR}/threadx_byte_memory_thread_terminate_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_basic_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_information_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_isr_set_clear_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_isr_wait_abort_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_single_thread_terminate_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_suspension_consume_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_suspension_different_bits_consume_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_suspension_different_bits_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_suspension_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_suspension_timeout_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_thread_terminate_test.c + ${TX_REGRESSION_DIR}/threadx_initialize_kernel_setup_test.c + ${TX_REGRESSION_DIR}/threadx_interrupt_control_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_basic_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_delete_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_information_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_nested_priority_inheritance_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_no_preemption_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_preemption_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_priority_inheritance_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_proritize_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_suspension_timeout_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_thread_terminate_test.c + ${TX_REGRESSION_DIR}/threadx_queue_basic_eight_word_test.c + ${TX_REGRESSION_DIR}/threadx_queue_basic_four_word_test.c + ${TX_REGRESSION_DIR}/threadx_queue_basic_max_message_size_test.c + ${TX_REGRESSION_DIR}/threadx_queue_basic_one_word_test.c + ${TX_REGRESSION_DIR}/threadx_queue_basic_sixteen_word_test.c + ${TX_REGRESSION_DIR}/threadx_queue_basic_two_word_test.c + ${TX_REGRESSION_DIR}/threadx_queue_empty_suspension_test.c + ${TX_REGRESSION_DIR}/threadx_queue_flush_no_suspension_test.c + ${TX_REGRESSION_DIR}/threadx_queue_flush_test.c + ${TX_REGRESSION_DIR}/threadx_queue_front_send_test.c + ${TX_REGRESSION_DIR}/threadx_queue_full_suspension_test.c + ${TX_REGRESSION_DIR}/threadx_queue_information_test.c + ${TX_REGRESSION_DIR}/threadx_queue_prioritize.c + ${TX_REGRESSION_DIR}/threadx_queue_suspension_timeout_test.c + ${TX_REGRESSION_DIR}/threadx_queue_thread_terminate_test.c + ${TX_REGRESSION_DIR}/threadx_semaphore_basic_test.c + ${TX_REGRESSION_DIR}/threadx_semaphore_ceiling_put_test.c + ${TX_REGRESSION_DIR}/threadx_semaphore_delete_test.c + ${TX_REGRESSION_DIR}/threadx_semaphore_information_test.c + ${TX_REGRESSION_DIR}/threadx_semaphore_non_preemption_test.c + ${TX_REGRESSION_DIR}/threadx_semaphore_preemption_test.c + ${TX_REGRESSION_DIR}/threadx_semaphore_prioritize.c + ${TX_REGRESSION_DIR}/threadx_semaphore_thread_terminate_test.c + ${TX_REGRESSION_DIR}/threadx_semaphore_timeout_test.c + ${TX_REGRESSION_DIR}/threadx_smp_multiple_threads_one_core_test.c + ${TX_REGRESSION_DIR}/threadx_smp_non_trivial_scheduling_test.c + ${TX_REGRESSION_DIR}/threadx_smp_one_thread_dynamic_exclusion_test.c + ${TX_REGRESSION_DIR}/threadx_smp_preemption_threshold_test.c + ${TX_REGRESSION_DIR}/threadx_smp_random_resume_suspend_exclusion_pt_test.c + ${TX_REGRESSION_DIR}/threadx_smp_random_resume_suspend_exclusion_test.c + ${TX_REGRESSION_DIR}/threadx_smp_random_resume_suspend_test.c + ${TX_REGRESSION_DIR}/threadx_smp_rebalance_exclusion_test.c + ${TX_REGRESSION_DIR}/threadx_smp_relinquish_test.c + ${TX_REGRESSION_DIR}/threadx_smp_resume_suspend_accending_order_test.c + ${TX_REGRESSION_DIR}/threadx_smp_resume_suspend_decending_order_test.c + ${TX_REGRESSION_DIR}/threadx_smp_time_slice_test.c + ${TX_REGRESSION_DIR}/threadx_smp_two_threads_one_core_test.c + ${TX_REGRESSION_DIR}/threadx_thread_basic_execution_test.c + ${TX_REGRESSION_DIR}/threadx_thread_basic_time_slice_test.c + ${TX_REGRESSION_DIR}/threadx_thread_completed_test.c + ${TX_REGRESSION_DIR}/threadx_thread_create_preemption_threshold_test.c + ${TX_REGRESSION_DIR}/threadx_thread_delayed_suspension_test.c + ${TX_REGRESSION_DIR}/threadx_thread_information_test.c + ${TX_REGRESSION_DIR}/threadx_thread_multi_level_preemption_threshold_test.c + ${TX_REGRESSION_DIR}/threadx_thread_multiple_non_current_test.c + ${TX_REGRESSION_DIR}/threadx_thread_multiple_sleep_test.c + ${TX_REGRESSION_DIR}/threadx_thread_multiple_suspension_test.c + ${TX_REGRESSION_DIR}/threadx_thread_multiple_time_slice_test.c + ${TX_REGRESSION_DIR}/threadx_thread_preemptable_suspension_test.c + ${TX_REGRESSION_DIR}/threadx_thread_preemption_change_test.c + ${TX_REGRESSION_DIR}/threadx_thread_priority_change.c + ${TX_REGRESSION_DIR}/threadx_thread_relinquish_test.c + ${TX_REGRESSION_DIR}/threadx_thread_reset_test.c + ${TX_REGRESSION_DIR}/threadx_thread_simple_sleep_non_clear_test.c + ${TX_REGRESSION_DIR}/threadx_thread_simple_sleep_test.c + ${TX_REGRESSION_DIR}/threadx_thread_simple_suspend_test.c + ${TX_REGRESSION_DIR}/threadx_thread_sleep_for_100ticks_test.c + ${TX_REGRESSION_DIR}/threadx_thread_sleep_terminate_test.c + ${TX_REGRESSION_DIR}/threadx_thread_stack_checking_test.c + ${TX_REGRESSION_DIR}/threadx_thread_terminate_delete_test.c + ${TX_REGRESSION_DIR}/threadx_thread_time_slice_change_test.c + ${TX_REGRESSION_DIR}/threadx_thread_wait_abort_and_isr_test.c + ${TX_REGRESSION_DIR}/threadx_thread_wait_abort_test.c + ${TX_REGRESSION_DIR}/threadx_time_get_set_test.c + ${TX_REGRESSION_DIR}/threadx_timer_activate_deactivate_test.c + ${TX_REGRESSION_DIR}/threadx_timer_deactivate_accuracy_test.c + ${TX_REGRESSION_DIR}/threadx_timer_information_test.c + ${TX_REGRESSION_DIR}/threadx_timer_large_timer_accuracy_test.c + ${TX_REGRESSION_DIR}/threadx_timer_multiple_accuracy_test.c + ${TX_REGRESSION_DIR}/threadx_timer_multiple_test.c + ${TX_REGRESSION_DIR}/threadx_timer_simple_test.c + #${TX_REGRESSION_DIR}/threadx_trace_basic_test.c #windows specifics in the test +) + +add_library( + threadx_smp_regression_support STATIC + ${TX_REGRESSION_DIR}/testcontrol.c + ${TX_CMAKE_DIR}/samples/fake.c +) +target_link_libraries(threadx_smp_regression_support PUBLIC threadx_smp c) +target_compile_definitions( + threadx_smp_regression_support + PUBLIC + TX_REGRESSION_TEST + TX_THREAD_SMP_ONLY_CORE_0_DEFAULT + TX_SMP_NOT_POSSIBLE + CTEST + BATCH_TEST + TEST_STACK_SIZE_PRINTF=4096 +) + +function(add_threadx_regression_test TEST_SOURCE) + get_filename_component(TEST_NAME ${TEST_SOURCE} NAME_WE) + + if(TEST_NAME STREQUAL "threadx_initialize_kernel_setup_test") + set(test_libraries threadx_smp) + else() + set(test_libraries threadx_smp_regression_support) + endif() + + setup_target( + ${TEST_NAME} + PLATFORM_TARGET moonlight_platform_common_smp + LIBRARIES ${test_libraries} + SOURCES ${TEST_SOURCE} + ) + + list(APPEND TX_REGRESSION_TARGETS ${TEST_NAME}) + set(TX_REGRESSION_TARGETS ${TX_REGRESSION_TARGETS} PARENT_SCOPE) + + add_test( + NAME ${TEST_NAME} + COMMAND ${THREADX_TEST_SIMULATOR} + --isa=${THREADX_TEST_ISA} + -f $ + -m 60s + -p tb.top.num_cores=4 + ) + set_tests_properties(${TEST_NAME} PROPERTIES TIMEOUT 60) +endfunction() + +foreach(test_case ${TX_REGRESSION_CASES}) + add_threadx_regression_test(${test_case}) +endforeach() + +add_custom_target( + threadx_smp_regression_build + DEPENDS ${TX_REGRESSION_TARGETS} +) diff --git a/test/smp/CMakePresets.json b/test/smp/CMakePresets.json new file mode 100644 index 0000000..4ecfd90 --- /dev/null +++ b/test/smp/CMakePresets.json @@ -0,0 +1,72 @@ +{ + "version": 3, + "cmakeMinimumRequired": { + "major": 3, + "minor": 24, + "patch": 0 + }, + "configurePresets": [ + { + "name": "Debug", + "binaryDir": "${sourceDir}/../../build/${presetName}/test/smp", + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/../../cmake/rv64imac_gnu.cmake", + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" + } + }, + { + "name": "Debug32", + "binaryDir": "${sourceDir}/../../build/${presetName}/test/smp", + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/../../cmake/rv32imac_gnu.cmake", + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" + } + }, + { + "name": "Release", + "binaryDir": "${sourceDir}/../../build/${presetName}/test/smp", + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/../../cmake/rv64imac_gnu.cmake", + "CMAKE_BUILD_TYPE": "RelWithDebInfo", + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" + } + }, + { + "name": "Release32", + "binaryDir": "${sourceDir}/../../build/${presetName}/test/smp", + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/../../cmake/rv32imac_gnu.cmake", + "CMAKE_BUILD_TYPE": "RelWithDebInfo", + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" + } + }, + { + "name": "MinSizeRel", + "binaryDir": "${sourceDir}/../../build/${presetName}/test/smp", + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/../../cmake/rv64imac_gnu.cmake", + "CMAKE_BUILD_TYPE": "MinSizeRel", + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" + } + }, + { + "name": "MinSizeRel32", + "binaryDir": "${sourceDir}/../../build/${presetName}/test/smp", + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/../../cmake/rv32imac_gnu.cmake", + "CMAKE_BUILD_TYPE": "MinSizeRel", + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" + } + } + ], + "buildPresets": [ + { "name": "Debug", "configurePreset": "Debug" }, + { "name": "Debug32", "configurePreset": "Debug32" }, + { "name": "Release", "configurePreset": "Release" }, + { "name": "Release32", "configurePreset": "Release32" }, + { "name": "MinSizeRel", "configurePreset": "MinSizeRel" }, + { "name": "MinSizeRel32", "configurePreset": "MinSizeRel32" } + ] +} diff --git a/test/threadx/CMakeLists.txt b/test/threadx/CMakeLists.txt new file mode 100644 index 0000000..0404a00 --- /dev/null +++ b/test/threadx/CMakeLists.txt @@ -0,0 +1,261 @@ +cmake_minimum_required(VERSION 3.21) + +set(BUILD_SHARED_LIBS OFF) +get_filename_component(THREADX4TGFS_ROOT "${CMAKE_CURRENT_LIST_DIR}/../.." ABSOLUTE) + +if(NOT DEFINED CMAKE_TOOLCHAIN_FILE) + set(CMAKE_TOOLCHAIN_FILE + ${THREADX4TGFS_ROOT}/cmake/rv32imac_gnu.cmake + CACHE FILEPATH "Toolchain file") +endif() +include(${CMAKE_TOOLCHAIN_FILE}) + +project(threadx_regression LANGUAGES C ASM) +enable_testing() + +set(TARGET_MEM "ram_dram" CACHE STRING "memory map to use") +set(CMAKE_EXECUTABLE_SUFFIX_C ".elf") + +set(THREADX_TEST_SIMULATOR + "" + CACHE FILEPATH "Path to the RISC-V VP executable used by CTest") +if(NOT THREADX_TEST_SIMULATOR) + message(FATAL_ERROR + "THREADX_TEST_SIMULATOR is not set. Configure with -DTHREADX_TEST_SIMULATOR=/path/to/riscv-vp") +endif() + +if(CMAKE_SYSTEM_PROCESSOR STREQUAL "risc-v32") + set(THREADX_TEST_ISA "rv32imac_m") +elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "risc-v64") + set(THREADX_TEST_ISA "rv64imac_m") +else() + message(FATAL_ERROR + "Unsupported CMAKE_SYSTEM_PROCESSOR for VP ISA selection: ${CMAKE_SYSTEM_PROCESSOR}") +endif() + +set(__THREAD_LOCAL_STORAGE OFF) +add_subdirectory(${THREADX4TGFS_ROOT}/third-party/picolibc ${CMAKE_BINARY_DIR}/picolibc) +target_link_libraries(c PUBLIC gcc) + +set(THREADX_CUSTOM_PORT ${THREADX4TGFS_ROOT}/port/threadx) +add_subdirectory(${THREADX4TGFS_ROOT}/third-party/threadx ${CMAKE_BINARY_DIR}/threadx) +target_link_libraries(threadx PUBLIC c) +target_compile_definitions(threadx PUBLIC TX_REGRESSION_TEST) + +set(MOONLIGHT_TRAP_SOURCE_INPUT ${THREADX4TGFS_ROOT}/port/moonlight/src/trap_non_vectored.c) +set(MOONLIGHT_TRAP_SOURCE ${CMAKE_BINARY_DIR}/generated/trap_non_vectored.c) +add_custom_command( + OUTPUT ${MOONLIGHT_TRAP_SOURCE} + COMMAND bash ${CMAKE_CURRENT_LIST_DIR}/generate_trap_file.sh + ${MOONLIGHT_TRAP_SOURCE_INPUT} + ${MOONLIGHT_TRAP_SOURCE} + DEPENDS ${MOONLIGHT_TRAP_SOURCE_INPUT} + ${CMAKE_CURRENT_LIST_DIR}/generate_trap_file.sh + VERBATIM +) +add_custom_target(threadx_regression_generated_trap DEPENDS ${MOONLIGHT_TRAP_SOURCE}) +set_source_files_properties(${MOONLIGHT_TRAP_SOURCE} PROPERTIES GENERATED TRUE) + +add_subdirectory(${THREADX4TGFS_ROOT}/port/moonlight ${CMAKE_BINARY_DIR}/port/moonlight) +add_dependencies(moonlight_platform_common threadx_regression_generated_trap) + +function(setup_target TARGET) + set(options) + set(oneValueArgs PLATFORM_TARGET) + set(multiValueArgs LIBRARIES SOURCES) + cmake_parse_arguments(ST "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + if(ST_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "setup_target(${TARGET} ...): unknown args: ${ST_UNPARSED_ARGUMENTS}") + endif() + + add_executable(${TARGET}) + set_target_properties(${TARGET} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR} + ) + if(ST_PLATFORM_TARGET) + target_add_moonlight_platform(${TARGET} PLATFORM_TARGET ${ST_PLATFORM_TARGET}) + else() + target_add_moonlight_platform(${TARGET}) + endif() + + if(ST_SOURCES) + target_sources(${TARGET} PRIVATE ${ST_SOURCES}) + endif() + + if(ST_LIBRARIES) + target_link_libraries(${TARGET} PRIVATE ${ST_LIBRARIES}) + endif() + + target_link_options(${TARGET} PRIVATE + -nostartfiles + -nostdlib + -T ${THREADX4TGFS_ROOT}/src/${TARGET_MEM}.lds + -Wl,--gc-sections + -Wl,-Map=${CMAKE_BINARY_DIR}/${TARGET}.map) + + add_custom_command(TARGET ${TARGET} POST_BUILD + COMMAND ${OBJCOPY} -O ihex $ ${CMAKE_BINARY_DIR}/${TARGET}.hex + COMMAND ${OBJCOPY} -O binary $ ${CMAKE_BINARY_DIR}/${TARGET}.bin + COMMAND ${OBJDUMP} -S $ > ${CMAKE_BINARY_DIR}/${TARGET}.dis + ) +endfunction() + +set(TX_REGRESSION_DIR + ${THREADX4TGFS_ROOT}/third-party/threadx/test/tx/regression +) +set(TX_CMAKE_DIR + ${THREADX4TGFS_ROOT}/third-party/threadx/test/tx/cmake +) + +set(TX_REGRESSION_CASES + ${TX_REGRESSION_DIR}/threadx_block_memory_basic_test.c + ${TX_REGRESSION_DIR}/threadx_block_memory_error_detection_test.c + ${TX_REGRESSION_DIR}/threadx_block_memory_information_test.c + ${TX_REGRESSION_DIR}/threadx_block_memory_prioritize_test.c + ${TX_REGRESSION_DIR}/threadx_block_memory_suspension_test.c + ${TX_REGRESSION_DIR}/threadx_block_memory_suspension_timeout_test.c + ${TX_REGRESSION_DIR}/threadx_block_memory_thread_terminate_test.c + ${TX_REGRESSION_DIR}/threadx_byte_memory_basic_test.c + ${TX_REGRESSION_DIR}/threadx_byte_memory_information_test.c + ${TX_REGRESSION_DIR}/threadx_byte_memory_prioritize_test.c + ${TX_REGRESSION_DIR}/threadx_byte_memory_suspension_test.c + ${TX_REGRESSION_DIR}/threadx_byte_memory_suspension_timeout_test.c + ${TX_REGRESSION_DIR}/threadx_byte_memory_thread_contention_test.c + ${TX_REGRESSION_DIR}/threadx_byte_memory_thread_terminate_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_basic_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_information_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_isr_set_clear_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_isr_wait_abort_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_single_thread_terminate_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_suspension_consume_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_suspension_different_bits_consume_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_suspension_different_bits_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_suspension_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_suspension_timeout_test.c + ${TX_REGRESSION_DIR}/threadx_event_flag_thread_terminate_test.c + ${TX_REGRESSION_DIR}/threadx_interrupt_control_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_basic_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_delete_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_information_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_nested_priority_inheritance_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_no_preemption_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_preemption_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_priority_inheritance_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_proritize_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_suspension_timeout_test.c + ${TX_REGRESSION_DIR}/threadx_mutex_thread_terminate_test.c + ${TX_REGRESSION_DIR}/threadx_queue_basic_eight_word_test.c + ${TX_REGRESSION_DIR}/threadx_queue_basic_four_word_test.c + ${TX_REGRESSION_DIR}/threadx_queue_basic_one_word_test.c + ${TX_REGRESSION_DIR}/threadx_queue_basic_sixteen_word_test.c + ${TX_REGRESSION_DIR}/threadx_queue_basic_two_word_test.c + ${TX_REGRESSION_DIR}/threadx_queue_basic_max_message_size_test.c + ${TX_REGRESSION_DIR}/threadx_queue_empty_suspension_test.c + ${TX_REGRESSION_DIR}/threadx_queue_flush_no_suspension_test.c + ${TX_REGRESSION_DIR}/threadx_queue_flush_test.c + ${TX_REGRESSION_DIR}/threadx_queue_front_send_test.c + ${TX_REGRESSION_DIR}/threadx_queue_full_suspension_test.c + ${TX_REGRESSION_DIR}/threadx_queue_information_test.c + ${TX_REGRESSION_DIR}/threadx_queue_prioritize.c + ${TX_REGRESSION_DIR}/threadx_queue_suspension_timeout_test.c + ${TX_REGRESSION_DIR}/threadx_queue_thread_terminate_test.c + ${TX_REGRESSION_DIR}/threadx_semaphore_basic_test.c + ${TX_REGRESSION_DIR}/threadx_semaphore_ceiling_put_test.c + ${TX_REGRESSION_DIR}/threadx_semaphore_delete_test.c + ${TX_REGRESSION_DIR}/threadx_semaphore_information_test.c + ${TX_REGRESSION_DIR}/threadx_semaphore_non_preemption_test.c + ${TX_REGRESSION_DIR}/threadx_semaphore_preemption_test.c + ${TX_REGRESSION_DIR}/threadx_semaphore_prioritize.c + ${TX_REGRESSION_DIR}/threadx_semaphore_thread_terminate_test.c + ${TX_REGRESSION_DIR}/threadx_semaphore_timeout_test.c + ${TX_REGRESSION_DIR}/threadx_thread_basic_execution_test.c + ${TX_REGRESSION_DIR}/threadx_thread_basic_time_slice_test.c + ${TX_REGRESSION_DIR}/threadx_thread_completed_test.c + ${TX_REGRESSION_DIR}/threadx_thread_create_preemption_threshold_test.c + ${TX_REGRESSION_DIR}/threadx_thread_delayed_suspension_test.c + ${TX_REGRESSION_DIR}/threadx_thread_information_test.c + ${TX_REGRESSION_DIR}/threadx_thread_multi_level_preemption_threshold_test.c + ${TX_REGRESSION_DIR}/threadx_thread_multiple_non_current_test.c + ${TX_REGRESSION_DIR}/threadx_thread_multiple_sleep_test.c + ${TX_REGRESSION_DIR}/threadx_thread_multiple_suspension_test.c + ${TX_REGRESSION_DIR}/threadx_thread_multiple_time_slice_test.c + ${TX_REGRESSION_DIR}/threadx_thread_preemptable_suspension_test.c + ${TX_REGRESSION_DIR}/threadx_thread_preemption_change_test.c + ${TX_REGRESSION_DIR}/threadx_thread_priority_change.c + ${TX_REGRESSION_DIR}/threadx_thread_relinquish_test.c + ${TX_REGRESSION_DIR}/threadx_thread_reset_test.c + ${TX_REGRESSION_DIR}/threadx_thread_simple_sleep_non_clear_test.c + ${TX_REGRESSION_DIR}/threadx_thread_simple_sleep_test.c + ${TX_REGRESSION_DIR}/threadx_thread_simple_suspend_test.c + ${TX_REGRESSION_DIR}/threadx_thread_sleep_for_100ticks_test.c + ${TX_REGRESSION_DIR}/threadx_thread_sleep_terminate_test.c + ${TX_REGRESSION_DIR}/threadx_thread_stack_checking_test.c + ${TX_REGRESSION_DIR}/threadx_thread_terminate_delete_test.c + ${TX_REGRESSION_DIR}/threadx_thread_time_slice_change_test.c + ${TX_REGRESSION_DIR}/threadx_thread_wait_abort_and_isr_test.c + ${TX_REGRESSION_DIR}/threadx_thread_wait_abort_test.c + ${TX_REGRESSION_DIR}/threadx_time_get_set_test.c + ${TX_REGRESSION_DIR}/threadx_timer_activate_deactivate_test.c + ${TX_REGRESSION_DIR}/threadx_timer_deactivate_accuracy_test.c + ${TX_REGRESSION_DIR}/threadx_timer_information_test.c + ${TX_REGRESSION_DIR}/threadx_timer_large_timer_accuracy_test.c + ${TX_REGRESSION_DIR}/threadx_timer_multiple_accuracy_test.c + ${TX_REGRESSION_DIR}/threadx_timer_multiple_test.c + ${TX_REGRESSION_DIR}/threadx_timer_simple_test.c + ${TX_REGRESSION_DIR}/threadx_trace_basic_test.c + ${TX_REGRESSION_DIR}/threadx_initialize_kernel_setup_test.c +) + +set(TX_REGRESSION_TARGETS) + +add_library( + threadx_regression_support STATIC + ${TX_REGRESSION_DIR}/testcontrol.c + ${TX_CMAKE_DIR}/samples/fake.c +) +target_link_libraries(threadx_regression_support PUBLIC threadx c) +target_compile_definitions( + threadx_regression_support + PUBLIC + TX_REGRESSION_TEST + CTEST + BATCH_TEST + TEST_STACK_SIZE_PRINTF=4096 +) + +function(add_threadx_regression_test TEST_SOURCE) + get_filename_component(TEST_NAME ${TEST_SOURCE} NAME_WE) + + if(TEST_NAME STREQUAL "threadx_initialize_kernel_setup_test") + set(test_libraries threadx) + else() + set(test_libraries threadx_regression_support) + endif() + + setup_target( + ${TEST_NAME} + LIBRARIES ${test_libraries} + SOURCES ${TEST_SOURCE} + ) + + list(APPEND TX_REGRESSION_TARGETS ${TEST_NAME}) + set(TX_REGRESSION_TARGETS ${TX_REGRESSION_TARGETS} PARENT_SCOPE) + + add_test( + NAME ${TEST_NAME} + COMMAND ${THREADX_TEST_SIMULATOR} + --isa=${THREADX_TEST_ISA} + -f $ + -m 60s + ) + set_tests_properties(${TEST_NAME} PROPERTIES TIMEOUT 60) +endfunction() + +foreach(test_case ${TX_REGRESSION_CASES}) + add_threadx_regression_test(${test_case}) +endforeach() + +add_custom_target( + threadx_regression_build + DEPENDS ${TX_REGRESSION_TARGETS} +) diff --git a/test/threadx/CMakePresets.json b/test/threadx/CMakePresets.json new file mode 100644 index 0000000..afbc481 --- /dev/null +++ b/test/threadx/CMakePresets.json @@ -0,0 +1,72 @@ +{ + "version": 3, + "cmakeMinimumRequired": { + "major": 3, + "minor": 24, + "patch": 0 + }, + "configurePresets": [ + { + "name": "Debug", + "binaryDir": "${sourceDir}/../../build/${presetName}/test/threadx", + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/../../cmake/rv64imac_gnu.cmake", + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" + } + }, + { + "name": "Debug32", + "binaryDir": "${sourceDir}/../../build/${presetName}/test/threadx", + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/../../cmake/rv32imac_gnu.cmake", + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" + } + }, + { + "name": "Release", + "binaryDir": "${sourceDir}/../../build/${presetName}/test/threadx", + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/../../cmake/rv64imac_gnu.cmake", + "CMAKE_BUILD_TYPE": "RelWithDebInfo", + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" + } + }, + { + "name": "Release32", + "binaryDir": "${sourceDir}/../../build/${presetName}/test/threadx", + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/../../cmake/rv32imac_gnu.cmake", + "CMAKE_BUILD_TYPE": "RelWithDebInfo", + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" + } + }, + { + "name": "MinSizeRel", + "binaryDir": "${sourceDir}/../../build/${presetName}/test/threadx", + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/../../cmake/rv64imac_gnu.cmake", + "CMAKE_BUILD_TYPE": "MinSizeRel", + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" + } + }, + { + "name": "MinSizeRel32", + "binaryDir": "${sourceDir}/../../build/${presetName}/test/threadx", + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/../../cmake/rv32imac_gnu.cmake", + "CMAKE_BUILD_TYPE": "MinSizeRel", + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" + } + } + ], + "buildPresets": [ + { "name": "Debug", "configurePreset": "Debug" }, + { "name": "Debug32", "configurePreset": "Debug32" }, + { "name": "Release", "configurePreset": "Release" }, + { "name": "Release32", "configurePreset": "Release32" }, + { "name": "MinSizeRel", "configurePreset": "MinSizeRel" }, + { "name": "MinSizeRel32", "configurePreset": "MinSizeRel32" } + ] +} diff --git a/test/threadx/generate_trap_file.sh b/test/threadx/generate_trap_file.sh new file mode 100755 index 0000000..2e0f430 --- /dev/null +++ b/test/threadx/generate_trap_file.sh @@ -0,0 +1,29 @@ +#!/bin/bash +set -eu + +src="$1" +dst="$2" + +extern_anchor='extern void _tx_timer_interrupt(void);' +call_anchor='_tx_timer_interrupt();' + +mkdir -p "$(dirname "$dst")" +tmp1="${dst}.tmp1" + +line=$(grep -n -F "$extern_anchor" "$src" | head -n 1 | cut -d: -f1) +if [ -z "$line" ]; then + echo "failed to find _tx_timer_interrupt declaration anchor in trap source" >&2 + exit 1 +fi +sed "${line}a\\ +void test_interrupt_dispatch(void) __attribute__((weak));\nvoid test_interrupt_dispatch(void) {}" "$src" > "$tmp1" + +line=$(grep -n -F "$call_anchor" "$tmp1" | head -n 1 | cut -d: -f1) +if [ -z "$line" ]; then + echo "failed to find _tx_timer_interrupt call anchor in trap source" >&2 + rm -f "$tmp1" + exit 1 +fi +sed "${line}i\\ + test_interrupt_dispatch();" "$tmp1" > "$dst" +rm -f "$tmp1" diff --git a/third-party/threadx b/third-party/threadx index 4b6e810..c9ff0ce 160000 --- a/third-party/threadx +++ b/third-party/threadx @@ -1 +1 @@ -Subproject commit 4b6e8100d932a3a67b34c6eb17f84f3bffb9e2ae +Subproject commit c9ff0ce2c837a57f2bc5bf6d5d7b3876b01a5aa8