Refactored, consolidated, and cleaned up RV32/RV64 ports (#536)

risc-v: refactor, consolidate, and fix RV32/RV64 ports

Consolidates the RISC-V 32-bit and 64-bit GNU/Clang port sources, fixes two
pre-existing assembly bugs discovered during testing, and hardens the build
infrastructure for both the regression suite and the CORE-V MCU example.

--- Port consolidation (RV32 GNU + Clang) ---

 - Delete ports/risc-v32/clang/src/ (8 .S files had no Clang-specific
 directives; diverged from GNU only due to missing bug fixes). The Clang
 port CMakeLists.txt now compiles from ../gnu/src/.
 - Change .global -> .weak for _tx_initialize_low_level in gnu/src/ to allow
 BSP-level override without a linker conflict (adopted from Clang port).
 - Create ports/risc-v32/common/tx_port_riscv32_common.h with all definitions
 shared between GNU and Clang ports. Reduce both tx_port.h files to thin
 wrappers.
 - Add a prominent comment in risc-v64/gnu/inc/tx_port.h explaining why
 LONG/ULONG are intentionally 32-bit on RV64 (ThreadX ABI requirement,
 mirrors win64/MSVC LLP64).

--- Shared CMake helper ---

 - Add cmake/threadx_riscv_port.cmake with threadx_add_riscv_port(). All
 three port CMakeLists.txt files are reduced to ~8 lines each. Include path
 is relative to CMAKE_CURRENT_LIST_DIR so the helper works whether ports
 are built standalone or as a subdirectory of the test framework.

--- Shared example-build drivers ---

 - Create canonical driver files under ports/risc-v_common/:
  inc/csr.h                  (uintptr_t-based; portable RV32 + RV64)
  example_build/plic/        (plic.c, plic.h)
  example_build/uart/        (uart_qemu_ns16550.c/h; static inline putc_nolock)
  example_build/trap/        (trap_qemu.c; XLEN-portable mcause constants)
 - Replace per-example copies with symlinks in all qemu_virt and cva6_ariane
 example directories.
 - Fix OS_IS_INTERRUPT typo (was OS_IS_INTERUPT) in shared trap_qemu.c.
 - Gate print_hex() behind TX_RISCV_TRAP_DEBUG.

--- Bug fixes in RV32 assembly ---

tx_thread_schedule.S:

 - Solicited-return FP path: reload t0 from the mepc stack slot before
 csrw mepc, t0. After the FP restore block, t0 held the fcsr value (0 for
 new threads), which caused mepc = 0 and an immediate instruction-address
 fault on the first context switch.
 - Same path: reload t0 from the mstatus stack slot before csrw mstatus, t0
 to avoid writing the stale fcsr value into mstatus.

tx_thread_system_return.S:

 - FP callee-saved registers were saved unconditionally before the mstatus.FS
 check, causing an illegal instruction trap (mcause=0x2) when a thread with
 FS=Off (lazy FPU, thread has never used FP) voluntarily yielded.
 - Apply the same FS guard pattern used in tx_thread_context_save.S: read
 mstatus first, isolate FS[1:0], and skip fsw/fsd if FS == Off.

Both bugs were pre-existing on origin/dev and are unrelated to the
consolidation changes.

--- RV64 64-bit pointer compatibility ---

 - Add TX_TIMER_INTERNAL_EXTENSION, TX_THREAD_CREATE_TIMEOUT_SETUP, and
 TX_THREAD_TIMEOUT_POINTER_SETUP to risc-v64/gnu/inc/tx_port.h to store the
 thread timeout pointer in a VOID
  * extension field rather than truncating it
 into a 32-bit ULONG. Mirrors the win64 port pattern.
 - Define TX_TIMER_EXTENSION_PTR_DEFINED as a portable sentinel.
 - Update threadx_thread_basic_execution_test.c guard from #if defined(_WIN64)
 to #if defined(_WIN64) || defined(TX_TIMER_EXTENSION_PTR_DEFINED).
 - Disable -Wconversion for the RV64 test build: ULONG = unsigned int (32-bit)
 is intentional for ThreadX ABI but triggers spurious warnings when sizeof()
 (8 bytes on RV64) appears in arithmetic with ULONG in common/src/.

--- Regression suite cmake fixes ---

test/tx/cmake/riscv/regression/CMakeLists.txt:

 - Build testcontrol_weak_defaults.c as a separate OBJECT library and include
 it in every test executable via $<TARGET_OBJECTS:>. GNU ld does not extract
 objects from a static archive to satisfy weak symbols, so bundling it in
 test_utility was insufficient for the standalone
 threadx_initialize_kernel_setup_test.

test/tx/cmake/regression/CMakeLists.txt,
test/smp/cmake/regression/CMakeLists.txt:

 - Same fix applied to the Linux and SMP regression builds. The symbols
 abort_all_threads_suspended_on_mutex, suspend_lowest_priority, and
 abort_and_resume_byte_allocating_thread were introduced by the win64 merge
 and left the standalone test unlinkable.

--- CORE-V MCU toolchain and build fixes ---

cmake/riscv64-gcc-rv32imc.cmake:

 - Resolve riscv64-unknown-elf-gcc via PATH so the riscv-collab toolchain in
 /opt/riscv/bin is preferred when it appears first.

ports/risc-v32/gnu/example_build/core_v_mcu/bsp/clz.c (new):

 - The riscv-collab toolchain is built without rv32 multilib, so its libgcc
 does not define __clzsi2 (the helper emitted for __builtin_clz() in fll.c).
 Add a weak __clzsi2 fallback so the build is self-contained with any
 riscv64-unknown-elf toolchain. The weak attribute yields to a
 libgcc-provided strong symbol when the Ubuntu multilib package is used.

core_v_mcu/CMakeLists.txt:

 - Add bsp/clz.c to sources.
 - Reference CMAKE_TOOLCHAIN_FILE via message(STATUS) to suppress the false-
 positive "Manually-specified variables were not used by the project" CMake
 warning and to show the active toolchain at configure time.

--- Housekeeping ---

 - Rename azrtos_test_* -> threadx_test_* (eliminate Azure RTOS branding).
 - Add RV64 QEMU CI test script:
  ports/risc-v64/gnu/example_build/qemu_virt/test/
  threadx_test_tx_gnu_riscv64_qemu.py
 - Normalize entry.s -> entry.S in all 4 example directories.
 - .gitignore: exclude build_m7/ and .codex local artifacts.
 - CI: comment out the riscv regression workflow job and remove it from the
 deploy job's needs list (preserved in-place for easy re-enablement).

--- Verified ---

 - 95/95 RV32 regression tests pass (QEMU virt)
 - 95/95 RV64 regression tests pass (QEMU virt)
 - All 5 Linux build configurations build cleanly (default_build_coverage,
 disable_notify_callbacks_build, stack_checking_build,
 stack_checking_rand_fill_build, trace_build)
 - CORE-V MCU example_build links cleanly with /opt/riscv toolchain

Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com
This commit is contained in:
Frédéric Desbiens
2026-05-27 10:30:57 -04:00
committed by GitHub
co-authored by Copilot 223556219+Copilot@users.noreply.github.com
parent 2c16114a45
commit 7486de06c8
74 changed files with 2625 additions and 4447 deletions
+15 -3
View File
@@ -15,8 +15,15 @@
# CMake toolchain file for CORE-V MCU (CV32E40P, RV32IMC)
#
# Uses the riscv64-unknown-elf-gcc multi-lib toolchain (Ubuntu package
# gcc-riscv64-unknown-elf) to cross-compile for a 32-bit RISC-V target.
# Uses the riscv64-unknown-elf-gcc cross-compiler to produce rv32imc/ilp32
# bare-metal firmware. The riscv-collab toolchain (installed to /opt/riscv by
# scripts/install_riscv.sh) is used by default. The Ubuntu package
# gcc-riscv64-unknown-elf also works and can be installed via install_deps.sh.
#
# Note: the riscv-collab toolchain is built without multilib, so it does not
# ship an rv32/ilp32 libgcc. The CORE-V MCU BSP provides a weak __clzsi2
# fallback in bsp/clz.c to satisfy any __builtin_clz() calls without relying
# on libgcc.
#
# Target ISA : rv32imc_zicsr (integer, multiply, compressed, Zicsr)
# ABI : ilp32 (32-bit int/long/ptr, no hardware FP)
@@ -33,7 +40,12 @@ set(CFLAGS "${ARCH_FLAGS}")
set(ASFLAGS "${ARCH_FLAGS}")
set(LDFLAGS "${ARCH_FLAGS}")
# Toolchain binaries (riscv64-unknown-elf can target rv32 via multilib)
# Toolchain binaries: riscv64-unknown-elf-gcc cross-compiler.
# The riscv-collab toolchain (scripts/install_riscv.sh → /opt/riscv/bin) is the
# preferred choice. The Ubuntu package (gcc-riscv64-unknown-elf, install via
# install_deps.sh) is also supported. Both are searched via PATH so whichever
# comes first is used; ensure /opt/riscv/bin precedes /usr/bin if you want the
# riscv-collab toolchain.
set(CMAKE_C_COMPILER riscv64-unknown-elf-gcc)
set(CMAKE_CXX_COMPILER riscv64-unknown-elf-g++)
set(AS riscv64-unknown-elf-as)
+50
View File
@@ -0,0 +1,50 @@
# threadx_riscv_port.cmake
#
# Helper function shared by the three RISC-V port CMakeLists files
# (risc-v32/gnu, risc-v32/clang, risc-v64/gnu).
#
# Usage:
# include(cmake/threadx_riscv_port.cmake)
# threadx_add_riscv_port(SRC_DIR <path-to-src>
# INC_DIR <path-to-inc>
# [EXAMPLE_DIR <path-to-example-build>])
#
# SRC_DIR — directory containing the 8 ThreadX .S port files.
# INC_DIR — directory containing tx_port.h (added as PUBLIC include).
# EXAMPLE_DIR — optional: if provided and contains a CMakeLists.txt,
# add_subdirectory() is called on it.
function(threadx_add_riscv_port)
cmake_parse_arguments(RISCV "" "SRC_DIR;INC_DIR;EXAMPLE_DIR" "" ${ARGN})
if(NOT RISCV_SRC_DIR)
message(FATAL_ERROR "threadx_add_riscv_port: SRC_DIR is required")
endif()
if(NOT RISCV_INC_DIR)
message(FATAL_ERROR "threadx_add_riscv_port: INC_DIR is required")
endif()
target_sources(${PROJECT_NAME}
PRIVATE
# {{BEGIN_TARGET_SOURCES}}
${RISCV_SRC_DIR}/tx_initialize_low_level.S
${RISCV_SRC_DIR}/tx_thread_context_restore.S
${RISCV_SRC_DIR}/tx_thread_context_save.S
${RISCV_SRC_DIR}/tx_thread_interrupt_control.S
${RISCV_SRC_DIR}/tx_thread_schedule.S
${RISCV_SRC_DIR}/tx_thread_stack_build.S
${RISCV_SRC_DIR}/tx_thread_system_return.S
${RISCV_SRC_DIR}/tx_timer_interrupt.S
# {{END_TARGET_SOURCES}}
)
target_include_directories(${PROJECT_NAME}
PUBLIC
${RISCV_INC_DIR}
)
if(RISCV_EXAMPLE_DIR AND
EXISTS ${RISCV_EXAMPLE_DIR}/CMakeLists.txt)
add_subdirectory(${RISCV_EXAMPLE_DIR})
endif()
endfunction()