riscv32: spec compliance and regression test fix (#691)

* riscv32: spec compliance and regression test fix

Signed-off-by: Akif Ejaz <akifejaz40@gmail.com>

* Derived the RISC-V32 frame sizes from the port contract in one place

tx_port.h published TX_RISCV_TRAP_FRAME_SIZE for the GNU BSP assembly, but
nothing in the port consumed it. Six .S files each rebuilt the same numbers
from their own #if, so the interrupt frame size was written out in seven
places and the solicited frame size in three.

That is the shape that produced the RISC-V64 fault fixed in #708, where the
port moved to a padded frame and one copy of the constant did not. The
sources now include tx_port.h and take both sizes from it, and no literal
frame size remains in the port. TX_RISCV_SOL_FRAME_SIZE joins the contract,
since the solicited frame was never published at all.

The emitted code is unchanged: 400 and 176 bytes for ILP32D, 128 for
soft-float, confirmed by disassembly before and after.

Two further corrections:

_tx_initialize_low_level carried .global immediately followed by .weak, so
the symbol stayed weak and the .global did nothing. Weak is what the port
wants, because the example and regression BSPs both provide their own
definition, so the stray .global is removed rather than the .weak. Verified
with nm that the symbol is still W.

The QEMU runner seeded fpu_verified from skip_fpu, so a soft-float run
satisfied the FPU gate whether or not the script ever reported the skip. It
now starts false and is set only when the skip marker is present, so a run
that dies before reaching that point fails instead of passing.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>

---------

Signed-off-by: Akif Ejaz <akifejaz40@gmail.com>
Co-authored-by: Frédéric Desbiens <frederic.desbiens@eclipse-foundation.org>
This commit is contained in:
Akif Ejaz
2026-09-09 11:40:05 -04:00
committed by GitHub
co-authored by Frédéric Desbiens
parent 3fc28d8979
commit 40db27e843
27 changed files with 766 additions and 627 deletions
+1 -1
View File
@@ -20,7 +20,7 @@ set(CMAKE_CXX_FLAGS "${CXXFLAGS}" CACHE INTERNAL "cxx compiler flags")
if(DEFINED SOFT_FLOAT)
set(CMAKE_ASM_FLAGS "${ASFLAGS} -D__ASSEMBLER__" CACHE INTERNAL "asm compiler flags")
else()
set(CMAKE_ASM_FLAGS "${ASFLAGS} -D__ASSEMBLER__ -D__riscv_float_abi_single" CACHE INTERNAL "asm compiler flags")
set(CMAKE_ASM_FLAGS "${ASFLAGS} -D__ASSEMBLER__" CACHE INTERNAL "asm compiler flags")
endif()
set(CMAKE_EXE_LINKER_FLAGS "${LDFLAGS}" CACHE INTERNAL "exe link flags")
+1 -1
View File
@@ -5,7 +5,7 @@ set(THREADX_ARCH "risc-v32")
set(THREADX_TOOLCHAIN "gnu")
if(DEFINED SOFT_FLOAT)
set(ARCH_FLAGS "-g -march=rv32ima_zicsr -mabi=ilp32 -mcmodel=medany")
set(CACHE{SOFT_FLOAT} FORCE 1)
set(SOFT_FLOAT 1 CACHE BOOL "build the soft-float rv32ima_zicsr/ilp32 configuration" FORCE)
else()
set(ARCH_FLAGS "-g -march=rv32gc -mabi=ilp32d -mcmodel=medany -mrelax")
endif()
@@ -0,0 +1,4 @@
# Ignore generated QEMU test files.
kernel.elf
qemu-riscv32.log
test_cmds.gdb
@@ -11,7 +11,7 @@ add_executable(kernel.elf EXCLUDE_FROM_ALL
${QEMU_DEMO_DIR}/tx_initialize_low_level.S
)
target_link_libraries(kernel.elf PRIVATE threadx)
target_link_libraries(kernel.elf PRIVATE threadx gcc)
target_include_directories(kernel.elf PRIVATE
${CMAKE_SOURCE_DIR}/common/inc
@@ -28,18 +28,32 @@ target_link_options(kernel.elf PRIVATE
# QEMU/GDB functional test runner. Optional: skipped silently if the
# host has no Python 3 interpreter on PATH.
find_package(Python3 COMPONENTS Interpreter)
if(Python3_FOUND)
# The runner needs a GDB that understands riscv:rv32 remote targets
find_program(RISCV32_GDB NAMES riscv32-unknown-elf-gdb gdb-multiarch)
find_program(RISCV32_QEMU NAMES qemu-system-riscv32)
if(Python3_FOUND AND RISCV32_GDB AND RISCV32_QEMU)
set(RISCV32_QEMU_TEST_ARGS)
if(SOFT_FLOAT)
list(APPEND RISCV32_QEMU_TEST_ARGS --skip-fpu)
endif()
add_custom_target(check-functional-riscv32
COMMAND ${Python3_EXECUTABLE}
${QEMU_DEMO_DIR}/test/threadx_test_tx_gnu_riscv32_qemu.py
--elf $<TARGET_FILE:kernel.elf>
--qemu qemu-system-riscv32
--gdb gdb
--qemu ${RISCV32_QEMU}
--gdb ${RISCV32_GDB}
${RISCV32_QEMU_TEST_ARGS}
DEPENDS kernel.elf
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Running RISC-V32 QEMU/GDB functional test runner..."
)
else()
elseif(NOT Python3_FOUND)
message(STATUS
"Python3 not found; check-functional-riscv32 target unavailable.")
elseif(NOT RISCV32_GDB)
message(STATUS
"RISC-V GDB not found; check-functional-riscv32 target unavailable.")
else()
message(STATUS
"qemu-system-riscv32 not found; check-functional-riscv32 target unavailable.")
endif()
@@ -14,15 +14,13 @@
#include <stdint.h>
#include <stddef.h>
void *memset(const void *des, int c,size_t n)
void *memset(void *des, int c, size_t n)
{
if((des == NULL) || n <=0)
return (void*)des;
char* t = (char*)des;
int i;
for(i=0;i<n;i++)
t[i]=c;
return t;
unsigned char *target = des;
for (size_t i = 0; i < n; i++)
target[i] = (unsigned char)c;
return des;
}
@@ -20,7 +20,9 @@
#define DEMO_BYTE_POOL_SIZE 9120
#define DEMO_BLOCK_POOL_SIZE 100
#define DEMO_QUEUE_SIZE 100
#ifdef __riscv_flen
float fpu_test_val = 0.0f;
#endif
char *_to_str(ULONG val)
{
@@ -375,8 +377,10 @@ UINT status;
if (status != TX_SUCCESS)
break;
#ifdef __riscv_flen
/* FPU Test */
fpu_test_val += 1.1f;
#endif
/* Get the mutex again with suspension. This shows
that an owning thread may retrieve the mutex it
owns multiple times. */
@@ -8,7 +8,7 @@
* SPDX-License-Identifier: MIT
**************************************************************************/
#include "tx_port.h"
#include "tx_api.h"
#include "csr.h"
#include "hwtimer.h"
@@ -16,20 +16,69 @@
#define CLINT_TIME (CLINT+0xBFF8)
#define CLINT_TIMECMP(hart_id) (CLINT+0x4000+8*(hart_id))
/* RV32 has no 64-bit load/store, so the 64-bit CLINT registers are
accessed as two volatile 32-bit MMIO words. */
#define MTIME_LO (*(volatile uint32_t *)(CLINT_TIME))
#define MTIME_HI (*(volatile uint32_t *)(CLINT_TIME + 4))
#define MTIMECMP_LO(hart) (*(volatile uint32_t *)(CLINT_TIMECMP(hart)))
#define MTIMECMP_HI(hart) (*(volatile uint32_t *)(CLINT_TIMECMP(hart) + 4))
/* RV32 mtime re-read the high word until it is stable, so a
low-word rollover between the two loads cannot tear the value. */
static uint64_t clint_time_read(void)
{
uint32_t hi;
uint32_t lo;
do
{
hi = MTIME_HI;
lo = MTIME_LO;
} while (hi != MTIME_HI);
return ((uint64_t)hi << 32) | lo;
}
/* Only this hart writes its own mtimecmp, so a plain two-word read
cannot tear. */
static uint64_t clint_timecmp_read(int hart)
{
uint32_t lo = MTIMECMP_LO(hart);
uint32_t hi = MTIMECMP_HI(hart);
return ((uint64_t)hi << 32) | lo;
}
/* RV32 mtimecmp write, safe sequence: park the low word at all-ones
first, so no intermediate 64-bit value compares below mtime and
raises a spurious timer interrupt. */
static void clint_timecmp_write(int hart, uint64_t value)
{
MTIMECMP_LO(hart) = 0xFFFFFFFF;
MTIMECMP_HI(hart) = (uint32_t)(value >> 32);
MTIMECMP_LO(hart) = (uint32_t)value;
}
int hwtimer_init(void)
{
int hart = riscv_get_core();
uint64_t time = *((uint64_t*)CLINT_TIME);
*((uint64_t*)CLINT_TIMECMP(hart)) = time + TICKNUM_PER_TIMER;
return 0;
int hart = riscv_get_core();
clint_timecmp_write(hart, clint_time_read() + TICKNUM_PER_TIMER);
return 0;
}
int hwtimer_handler(void)
{
int hart = riscv_get_core();
uint64_t time = *((uint64_t*)CLINT_TIME);
*((uint64_t*)CLINT_TIMECMP(hart)) = time + TICKNUM_PER_TIMER;
return 0;
}
int hart = riscv_get_core();
/* Advance from the previous compare value, so trap
latency does not accumulate as tick drift */
uint64_t next = clint_timecmp_read(hart) + TICKNUM_PER_TIMER;
uint64_t now = clint_time_read();
if (next <= now)
next = now + TICKNUM_PER_TIMER;
clint_timecmp_write(hart, next);
return 0;
}
@@ -14,8 +14,8 @@
#include <stdint.h>
#define TICKNUM_PER_SECOND 10000000
#define TICKNUM_PER_TIMER (TICKNUM_PER_SECOND / 10)
#define TICKNUM_PER_SECOND 10000000UL
#define TICKNUM_PER_TIMER (TICKNUM_PER_SECOND / TX_TIMER_TICKS_PER_SECOND)
int hwtimer_init(void);
int hwtimer_handler(void);
@@ -1,6 +1,12 @@
OUTPUT_ARCH( "riscv" )
ENTRY( _start )
PHDRS
{
text PT_LOAD FLAGS(5);
data PT_LOAD FLAGS(6);
}
SECTIONS
{
/*
@@ -14,15 +20,16 @@ SECTIONS
*(.text .text.*)
. = ALIGN(0x1000);
PROVIDE(etext = .);
}
} :text
.rodata : {
. = ALIGN(16);
*(.srodata .srodata.*) /* do not need to distinguish this from .rodata */
. = ALIGN(16);
*(.rodata .rodata.*)
}
} :text
. = ALIGN(0x1000);
.data : {
. = ALIGN(16);
/* Centre __global_pointer$ in the small-data window so the +/-2 KiB
@@ -31,23 +38,23 @@ SECTIONS
*(.sdata .sdata.*) /* do not need to distinguish this from .data */
. = ALIGN(16);
*(.data .data.*)
}
} :data
.bss : {
.bss (NOLOAD) : {
. = ALIGN(16);
_bss_start = .;
*(.sbss .sbss.*) /* do not need to distinguish this from .bss */
. = ALIGN(16);
*(.bss .bss.*)
_bss_end = .;
}
} :data
.stack : {
.stack (NOLOAD) : {
. = ALIGN(4096);
_sysstack_start = .;
. += 0x1000;
_sysstack_end = .;
}
} :data
PROVIDE(_end = .);
}
File diff suppressed because it is too large Load Diff
@@ -59,12 +59,12 @@
.global trap_entry
.extern trap_handler
.extern _tx_thread_context_restore
/* Trap frame: integer slots 0-31 at i*4; FP build adds f0-f31 as doubles
at 128 + i*8, fcsr at 384, pad to 400 (total mod 16 = 0). */
#include "tx_port.h"
.equ TX_TRAP_FRAME_SIZE, TX_RISCV_TRAP_FRAME_SIZE
trap_entry:
#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
addi sp, sp, -260 // Allocate space for all registers - with floating point enabled (65*4)
#else
addi sp, sp, -128 // Allocate space for all registers - without floating point enabled (32*4)
#endif
addi sp, sp, -TX_TRAP_FRAME_SIZE // Allocate space for all registers (400 with FP double, 128 without)
sw x1, 112(sp) // Store RA (28*4 = 112, because call will override ra [ra is a callee register in riscv])
@@ -73,11 +73,11 @@
csrr a0, mcause
csrr a1, mepc
csrr a2, mtval
addi sp, sp, -4
addi sp, sp, -16 // 16-byte temporary frame: ilp32 psABI requires sp = 0 mod 16 at the call
sw ra, 0(sp)
call trap_handler
lw ra, 0(sp)
addi sp, sp, 4
addi sp, sp, 16
call _tx_thread_context_restore
// it will nerver return
_err:
@@ -128,7 +128,6 @@ _err:
/* VOID _tx_initialize_low_level(VOID)
{ */
.global _tx_initialize_low_level
.weak _tx_initialize_low_level
.extern _end
.extern board_init
_tx_initialize_low_level:
@@ -147,16 +146,16 @@ _tx_initialize_low_level:
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
#if defined(__riscv_float_abi_double)
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, -4
addi sp, sp, -16 // 16-byte temporary frame: ilp32 psABI requires sp = 0 mod 16 at the call
sw ra, 0(sp)
call board_init
lw ra, 0(sp)
addi sp, sp, 4
addi sp, sp, 16
la t0, trap_entry
csrw mtvec, t0
ret
+41 -1
View File
@@ -25,7 +25,7 @@
/* PORT SPECIFIC C INFORMATION RELEASE */
/* */
/* tx_port.h RISC-V32/GNU */
/* 6.4.x */
/* 6.5.1.202602a */
/* */
/* AUTHOR */
/* */
@@ -47,6 +47,46 @@
#ifndef TX_PORT_H
#define TX_PORT_H
#ifdef __riscv_vector
#error "The ThreadX RISC-V32 GNU port does not save or restore vector state. Build without the V extension."
#endif
#ifdef __riscv_32e
#error "The ThreadX RISC-V32 GNU port requires the RV32I register set. RV32E is not supported."
#endif
#ifdef __riscv_float_abi_single
#error "The ThreadX RISC-V32 GNU port does not support ILP32F. Use ILP32D or a soft-float ABI."
#endif
#ifdef __riscv_float_abi_quad
#error "The ThreadX RISC-V32 GNU port does not support ILP32Q."
#endif
#if defined(__riscv_flen) && !defined(__riscv_float_abi_double)
#error "The ThreadX RISC-V32 GNU port requires ILP32D when the ISA enables floating-point state."
#endif
#if defined(__riscv_float_abi_double) && (!defined(__riscv_flen) || (__riscv_flen != 64))
#error "The ThreadX RISC-V32 GNU ILP32D port requires FLEN=64."
#endif
/* Publish the interrupt frame contract to GNU BSP assembly files. */
#if defined(__riscv_float_abi_double)
#define TX_RISCV_TRAP_FRAME_SIZE 400
#else
#define TX_RISCV_TRAP_FRAME_SIZE 128
#endif
#define TX_RISCV_TRAP_CALL_FRAME_SIZE 16
/* Solicited (cooperative) frame built by _tx_thread_system_return: fs0-fs11
as doubles plus fcsr with FP, callee-saved integer state without. */
#if defined(__riscv_float_abi_double)
#define TX_RISCV_SOL_FRAME_SIZE 176
#else
#define TX_RISCV_SOL_FRAME_SIZE 128
#endif
/* Include shared RISC-V32 port definitions common to all toolchain ports. */
#include "../../common/tx_port_riscv32_common.h"
@@ -62,8 +62,7 @@ __tx_free_memory_start:
/**************************************************************************/
/* VOID _tx_initialize_low_level(VOID)
{ */
.weak _tx_initialize_low_level
.weak _tx_initialize_low_level
.weak _tx_initialize_low_level // Weak: a BSP definition overrides this default
_tx_initialize_low_level:
/* Save the system stack pointer. */
@@ -79,8 +78,10 @@ _tx_initialize_low_level:
la t1, _tx_initialize_unused_memory // Pickup address of unused memory
sw t0, 0(t1) // Save unused memory address
/* Initialize floating point control/status register if floating point is enabled. */
#ifdef __riscv_flen
/* Initialize floating point state if floating point is enabled. */
#if defined(__riscv_float_abi_double)
li t0, 0x2000 // mstatus.FS = Initial (bits 14:13 = 01)
csrrs zero, mstatus, t0 // Enable the FP unit before touching fcsr
li t0, 0
csrw fcsr, t0 // Clear FP control/status register
#endif
File diff suppressed because it is too large Load Diff
+52 -102
View File
@@ -22,6 +22,10 @@
/**************************************************************************/
.section .text
#include "tx_port.h"
.equ TX_TRAP_FRAME_SIZE, TX_RISCV_TRAP_FRAME_SIZE
/**************************************************************************/
/* */
/* FUNCTION RELEASE */
@@ -63,7 +67,7 @@ _tx_thread_context_save:
/* Upon entry to this routine, RA/x1 has been saved on the stack
and the stack has been already allocated for the entire context:
addi sp, sp, -32*4 (or -65*4)
addi sp, sp, -TX_TRAP_FRAME_SIZE (400 with FP, 128 without)
sw ra, 28*4(sp)
*/
@@ -102,59 +106,34 @@ _tx_thread_context_save:
/* Save mstatus and skip FP state if FS is Off. */
csrr t0, mstatus
sw t0, 29*4(sp)
#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
#if defined(__riscv_float_abi_double)
srli t1, t0, 13
andi t1, t1, 0x3
beqz t1, _tx_thread_skip_fpu_save
#endif
/* Save floating point registers. */
#if defined(__riscv_float_abi_single)
fsw f0, 31*4(sp) // Store ft0
fsw f1, 32*4(sp) // Store ft1
fsw f2, 33*4(sp) // Store ft2
fsw f3, 34*4(sp) // Store ft3
fsw f4, 35*4(sp) // Store ft4
fsw f5, 36*4(sp) // Store ft5
fsw f6, 37*4(sp) // Store ft6
fsw f7, 38*4(sp) // Store ft7
fsw f10, 41*4(sp) // Store fa0
fsw f11, 42*4(sp) // Store fa1
fsw f12, 43*4(sp) // Store fa2
fsw f13, 44*4(sp) // Store fa3
fsw f14, 45*4(sp) // Store fa4
fsw f15, 46*4(sp) // Store fa5
fsw f16, 47*4(sp) // Store fa6
fsw f17, 48*4(sp) // Store fa7
fsw f28, 59*4(sp) // Store ft8
fsw f29, 60*4(sp) // Store ft9
fsw f30, 61*4(sp) // Store ft10
fsw f31, 62*4(sp) // Store ft11
/* Save floating point registers (doubles at 128 + i*8). */
fsd f0, 16*8(sp) // Store ft0
fsd f1, 17*8(sp) // Store ft1
fsd f2, 18*8(sp) // Store ft2
fsd f3, 19*8(sp) // Store ft3
fsd f4, 20*8(sp) // Store ft4
fsd f5, 21*8(sp) // Store ft5
fsd f6, 22*8(sp) // Store ft6
fsd f7, 23*8(sp) // Store ft7
fsd f10, 26*8(sp) // Store fa0
fsd f11, 27*8(sp) // Store fa1
fsd f12, 28*8(sp) // Store fa2
fsd f13, 29*8(sp) // Store fa3
fsd f14, 30*8(sp) // Store fa4
fsd f15, 31*8(sp) // Store fa5
fsd f16, 32*8(sp) // Store fa6
fsd f17, 33*8(sp) // Store fa7
fsd f28, 44*8(sp) // Store ft8
fsd f29, 45*8(sp) // Store ft9
fsd f30, 46*8(sp) // Store ft10
fsd f31, 47*8(sp) // Store ft11
csrr t0, fcsr
sw t0, 63*4(sp) // Store fcsr
#elif defined(__riscv_float_abi_double)
fsd f0, 31*4(sp) // Store ft0
fsd f1, 32*4(sp) // Store ft1
fsd f2, 33*4(sp) // Store ft2
fsd f3, 34*4(sp) // Store ft3
fsd f4, 35*4(sp) // Store ft4
fsd f5, 36*4(sp) // Store ft5
fsd f6, 37*4(sp) // Store ft6
fsd f7, 38*4(sp) // Store ft7
fsd f10, 41*4(sp) // Store fa0
fsd f11, 42*4(sp) // Store fa1
fsd f12, 43*4(sp) // Store fa2
fsd f13, 44*4(sp) // Store fa3
fsd f14, 45*4(sp) // Store fa4
fsd f15, 46*4(sp) // Store fa5
fsd f16, 47*4(sp) // Store fa6
fsd f17, 48*4(sp) // Store fa7
fsd f28, 59*4(sp) // Store ft8
fsd f29, 60*4(sp) // Store ft9
fsd f30, 61*4(sp) // Store ft10
fsd f31, 62*4(sp) // Store ft11
csrr t0, fcsr
sw t0, 63*4(sp) // Store fcsr
sw t0, 96*4(sp) // Store fcsr
#endif
_tx_thread_skip_fpu_save:
@@ -206,59 +185,34 @@ _tx_thread_nested_save:
/* Save mstatus and skip FP state if FS is Off. */
csrr t0, mstatus
sw t0, 29*4(sp)
#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
#if defined(__riscv_float_abi_double)
srli t1, t0, 13
andi t1, t1, 0x3
beqz t1, _tx_thread_skip_nested_fpu_save
#endif
/* Save floating point registers. */
#if defined(__riscv_float_abi_single)
fsw f0, 31*4(sp) // Store ft0
fsw f1, 32*4(sp) // Store ft1
fsw f2, 33*4(sp) // Store ft2
fsw f3, 34*4(sp) // Store ft3
fsw f4, 35*4(sp) // Store ft4
fsw f5, 36*4(sp) // Store ft5
fsw f6, 37*4(sp) // Store ft6
fsw f7, 38*4(sp) // Store ft7
fsw f10, 41*4(sp) // Store fa0
fsw f11, 42*4(sp) // Store fa1
fsw f12, 43*4(sp) // Store fa2
fsw f13, 44*4(sp) // Store fa3
fsw f14, 45*4(sp) // Store fa4
fsw f15, 46*4(sp) // Store fa5
fsw f16, 47*4(sp) // Store fa6
fsw f17, 48*4(sp) // Store fa7
fsw f28, 59*4(sp) // Store ft8
fsw f29, 60*4(sp) // Store ft9
fsw f30, 61*4(sp) // Store ft10
fsw f31, 62*4(sp) // Store ft11
/* Save floating point registers (doubles at 128 + i*8). */
fsd f0, 16*8(sp) // Store ft0
fsd f1, 17*8(sp) // Store ft1
fsd f2, 18*8(sp) // Store ft2
fsd f3, 19*8(sp) // Store ft3
fsd f4, 20*8(sp) // Store ft4
fsd f5, 21*8(sp) // Store ft5
fsd f6, 22*8(sp) // Store ft6
fsd f7, 23*8(sp) // Store ft7
fsd f10, 26*8(sp) // Store fa0
fsd f11, 27*8(sp) // Store fa1
fsd f12, 28*8(sp) // Store fa2
fsd f13, 29*8(sp) // Store fa3
fsd f14, 30*8(sp) // Store fa4
fsd f15, 31*8(sp) // Store fa5
fsd f16, 32*8(sp) // Store fa6
fsd f17, 33*8(sp) // Store fa7
fsd f28, 44*8(sp) // Store ft8
fsd f29, 45*8(sp) // Store ft9
fsd f30, 46*8(sp) // Store ft10
fsd f31, 47*8(sp) // Store ft11
csrr t0, fcsr
sw t0, 63*4(sp) // Store fcsr
#elif defined(__riscv_float_abi_double)
fsd f0, 31*4(sp) // Store ft0
fsd f1, 32*4(sp) // Store ft1
fsd f2, 33*4(sp) // Store ft2
fsd f3, 34*4(sp) // Store ft3
fsd f4, 35*4(sp) // Store ft4
fsd f5, 36*4(sp) // Store ft5
fsd f6, 37*4(sp) // Store ft6
fsd f7, 38*4(sp) // Store ft7
fsd f10, 41*4(sp) // Store fa0
fsd f11, 42*4(sp) // Store fa1
fsd f12, 43*4(sp) // Store fa2
fsd f13, 44*4(sp) // Store fa3
fsd f14, 45*4(sp) // Store fa4
fsd f15, 46*4(sp) // Store fa5
fsd f16, 47*4(sp) // Store fa6
fsd f17, 48*4(sp) // Store fa7
fsd f28, 59*4(sp) // Store ft8
fsd f29, 60*4(sp) // Store ft9
fsd f30, 61*4(sp) // Store ft10
fsd f31, 62*4(sp) // Store ft11
csrr t0, fcsr
sw t0, 63*4(sp) // Store fcsr
sw t0, 96*4(sp) // Store fcsr
#endif
_tx_thread_skip_nested_fpu_save:
@@ -291,9 +245,5 @@ _tx_thread_idle_system_save:
/* }
} */
#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
addi sp, sp, 65*4 // Recover stack frame - with floating point enabled
#else
addi sp, sp, 32*4 // Recover the reserved stack space
#endif
addi sp, sp, TX_TRAP_FRAME_SIZE // Recover the reserved stack space
ret // Return to calling ISR
File diff suppressed because it is too large Load Diff
+27 -77
View File
@@ -21,6 +21,10 @@
.section .text
#include "tx_port.h"
.equ TX_TRAP_FRAME_SIZE, TX_RISCV_TRAP_FRAME_SIZE
/**************************************************************************/
/* */
/* FUNCTION RELEASE */
@@ -92,42 +96,14 @@ _tx_thread_stack_build:
x11 26 Initial a1
x10 27 Initial a0
x1 28 Initial ra
-- 29 reserved
mstatus 29 Initial mstatus seed
mepc 30 Initial mepc
If floating point support:
f0 31 Initial ft0
f1 32 Initial ft1
f2 33 Initial ft2
f3 34 Initial ft3
f4 35 Initial ft4
f5 36 Initial ft5
f6 37 Initial ft6
f7 38 Initial ft7
f8 39 Initial fs0
f9 40 Initial fs1
f10 41 Initial fa0
f11 42 Initial fa1
f12 43 Initial fa2
f13 44 Initial fa3
f14 45 Initial fa4
f15 46 Initial fa5
f16 47 Initial fa6
f17 48 Initial fa7
f18 49 Initial fs2
f19 50 Initial fs3
f20 51 Initial fs4
f21 52 Initial fs5
f22 53 Initial fs6
f23 54 Initial fs7
f24 55 Initial fs8
f25 56 Initial fs9
f26 57 Initial fs10
f27 58 Initial fs11
f28 59 Initial ft8
f29 60 Initial ft9
f30 61 Initial ft10
f31 62 Initial ft11
fscr 63 Initial fscr
-- 31 pad (0)
If floating point support (double ABI), byte offsets from
the frame base:
f0-f31 128 + i*8 Initial FP registers (all 0)
fcsr 384 Initial fcsr (0)
pad 388-399 (0)
Stack Bottom: (higher memory address) */
@@ -137,11 +113,7 @@ If floating point support:
/* Actually build the stack frame. */
#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
addi t0, t0, -65*4
#else
addi t0, t0, -32*4 // Allocate space for the stack frame
#endif
addi t0, t0, -TX_TRAP_FRAME_SIZE // Allocate space for the stack frame
li t1, 1 // Build stack type
sw t1, 0*4(t0) // Place stack type on the top
sw zero, 1*4(t0) // Initial s11
@@ -173,44 +145,22 @@ If floating point support:
sw zero, 27*4(t0) // Initial a0
sw zero, 28*4(t0) // Initial ra
sw a1, 30*4(t0) // Initial mepc (thread entry point)
#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
sw zero, 31*4(t0) // Initial ft0
sw zero, 32*4(t0) // Initial ft1
sw zero, 33*4(t0) // Initial ft2
sw zero, 34*4(t0) // Initial ft3
sw zero, 35*4(t0) // Initial ft4
sw zero, 36*4(t0) // Initial ft5
sw zero, 37*4(t0) // Initial ft6
sw zero, 38*4(t0) // Initial ft7
sw zero, 39*4(t0) // Initial fs0
sw zero, 40*4(t0) // Initial fs1
sw zero, 41*4(t0) // Initial fa0
sw zero, 42*4(t0) // Initial fa1
sw zero, 43*4(t0) // Initial fa2
sw zero, 44*4(t0) // Initial fa3
sw zero, 45*4(t0) // Initial fa4
sw zero, 46*4(t0) // Initial fa5
sw zero, 47*4(t0) // Initial fa6
sw zero, 48*4(t0) // Initial fa7
sw zero, 49*4(t0) // Initial fs2
sw zero, 50*4(t0) // Initial fs3
sw zero, 51*4(t0) // Initial fs4
sw zero, 52*4(t0) // Initial fs5
sw zero, 53*4(t0) // Initial fs6
sw zero, 54*4(t0) // Initial fs7
sw zero, 55*4(t0) // Initial fs8
sw zero, 56*4(t0) // Initial fs9
sw zero, 57*4(t0) // Initial fs10
sw zero, 58*4(t0) // Initial fs11
sw zero, 59*4(t0) // Initial ft8
sw zero, 60*4(t0) // Initial ft9
sw zero, 61*4(t0) // Initial ft10
sw zero, 62*4(t0) // Initial ft11
csrr a1, fcsr // Read fcsr for initial value
sw a1, 63*4(t0) // Initial fcsr
sw zero, 64*4(t0) // Reserved word (0)
sw zero, 31*4(t0) // Pad word (0)
#if defined(__riscv_float_abi_double)
li t1, 0x6000 // mstatus seed: FS = Dirty (bits 14:13 = 11)
sw t1, 29*4(t0) // so the first dispatch restores the clean FP state
/* Zero the whole FP area: f0-f31 double slots (128-383), fcsr (384)
and the pad words up to the frame end. Threads start with clean
FP registers and fcsr = 0. */
addi t1, t0, 32*4 // First FP-area word (offset 128)
addi t2, t0, TX_TRAP_FRAME_SIZE // One past the frame end (offset 400)
_tx_thread_stack_build_fp_zero:
sw zero, 0(t1) // Clear FP-area word
addi t1, t1, 4
bne t1, t2, _tx_thread_stack_build_fp_zero
#else
sw zero, 31*4(t0) // Reserved word (0)
sw zero, 29*4(t0) // Initial mstatus slot (0, no FP gate)
#endif
/* Setup stack pointer. */
@@ -21,6 +21,10 @@
.section .text
#include "tx_port.h"
.equ TX_SOL_FRAME_SIZE, TX_RISCV_SOL_FRAME_SIZE
/**************************************************************************/
/* */
/* FUNCTION RELEASE */
@@ -63,54 +67,33 @@ _tx_thread_system_return:
/* Save minimal context on the stack. */
/* sp -= sizeof(stack_frame); */
#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
addi sp, sp, -29*4 // Allocate space on the stack - with floating point enabled
#else
addi sp, sp, -16*4 // Allocate space on the stack - without floating point enabled
#endif
addi sp, sp, -TX_SOL_FRAME_SIZE // Allocate the solicited stack frame
sw zero, 0(sp) // Solicited stack type (store early so slot is always written)
sw zero, 0*4(sp) // Solicited stack type (store early so slot is always written)
/* Read and save mstatus first; use it to guard FP register save. */
csrr t0, mstatus // Pickup current mstatus
sw t0, 14*4(sp) // Save mstatus
/* Store floating point preserved registers — only if FS != Off. */
#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
#if defined(__riscv_float_abi_double)
srli t1, t0, 13 // Shift FS field to bits [1:0]
andi t1, t1, 0x3 // Isolate FS bits
beqz t1, _tx_thread_system_return_skip_fp // Skip FP save if FS == Off
#if defined(__riscv_float_abi_single)
fsw f8, 15*4(sp) // Store fs0
fsw f9, 16*4(sp) // Store fs1
fsw f18, 17*4(sp) // Store fs2
fsw f19, 18*4(sp) // Store fs3
fsw f20, 19*4(sp) // Store fs4
fsw f21, 20*4(sp) // Store fs5
fsw f22, 21*4(sp) // Store fs6
fsw f23, 22*4(sp) // Store fs7
fsw f24, 23*4(sp) // Store fs8
fsw f25, 24*4(sp) // Store fs9
fsw f26, 25*4(sp) // Store fs10
fsw f27, 26*4(sp) // Store fs11
fsd f8, 8*8(sp) // Store fs0
fsd f9, 9*8(sp) // Store fs1
fsd f18, 10*8(sp) // Store fs2
fsd f19, 11*8(sp) // Store fs3
fsd f20, 12*8(sp) // Store fs4
fsd f21, 13*8(sp) // Store fs5
fsd f22, 14*8(sp) // Store fs6
fsd f23, 15*8(sp) // Store fs7
fsd f24, 16*8(sp) // Store fs8
fsd f25, 17*8(sp) // Store fs9
fsd f26, 18*8(sp) // Store fs10
fsd f27, 19*8(sp) // Store fs11
csrr t0, fcsr
sw t0, 27*4(sp) // Store fcsr
#elif defined(__riscv_float_abi_double)
fsd f8, 15*4(sp) // Store fs0
fsd f9, 16*4(sp) // Store fs1
fsd f18, 17*4(sp) // Store fs2
fsd f19, 18*4(sp) // Store fs3
fsd f20, 19*4(sp) // Store fs4
fsd f21, 20*4(sp) // Store fs5
fsd f22, 21*4(sp) // Store fs6
fsd f23, 22*4(sp) // Store fs7
fsd f24, 23*4(sp) // Store fs8
fsd f25, 24*4(sp) // Store fs9
fsd f26, 25*4(sp) // Store fs10
fsd f27, 26*4(sp) // Store fs11
csrr t0, fcsr
sw t0, 27*4(sp) // Store fcsr
#endif
sw t0, 40*4(sp) // Store fcsr
_tx_thread_system_return_skip_fp:
#endif
+2 -2
View File
@@ -88,7 +88,7 @@ _tx_timer_interrupt:
/* Check for expiration. */
/* if (_tx_timer_time_slice == 0) */
bgtz t3, _tx_timer_no_time_slice // If not 0, has not expired yet
bnez t3, _tx_timer_no_time_slice // If not 0, has not expired yet
li t1, 1 // Build expired flag
/* Set the time-slice expired flag. */
@@ -181,7 +181,7 @@ _tx_timer_dont_activate:
/* if (_tx_timer_expired_time_slice)
{ */
andi t2, t6, 1 // Is the timer expired bit set?
andi t2, t6, 1 // Is the time-slice expired bit set?
beqz t2, _tx_timer_not_ts_expiration // If not, skip time slice processing
/* Time slice interrupted thread. */
+6
View File
@@ -18,6 +18,7 @@
.extern _sysstack_end
.extern _bss_start
.extern _bss_end
.extern __global_pointer$
_start:
csrr t0, mhartid
@@ -56,6 +57,11 @@ _start:
li x30, 0
li x31, 0
.option push
.option norelax
la gp, __global_pointer$
.option pop
/* Set up stack pointer from linker symbol. */
la sp, _sysstack_end
+79 -5
View File
@@ -9,25 +9,99 @@
**************************************************************************/
#include "csr.h"
#include "tx_api.h"
#include "hwtimer.h"
#include <tx_port.h>
#define CLINT (0x02000000L)
#define CLINT_TIME (CLINT + 0xBFF8)
#define CLINT_TIMECMP(id) (CLINT + 0x4000 + 8 * (id))
#if __riscv_xlen == 32
/* RV32 has no 64-bit load/store, so the 64-bit CLINT registers are
accessed as two volatile 32-bit MMIO words. */
#define MTIME_LO (*(volatile uint32_t *)(CLINT_TIME))
#define MTIME_HI (*(volatile uint32_t *)(CLINT_TIME + 4))
#define MTIMECMP_LO(id) (*(volatile uint32_t *)(CLINT_TIMECMP(id)))
#define MTIMECMP_HI(id) (*(volatile uint32_t *)(CLINT_TIMECMP(id) + 4))
/* RV32 mtime read: re-read the high word until it is stable, so a
low-word rollover between the two loads cannot tear the value. */
static uint64_t clint_time_read(void)
{
uint32_t hi;
uint32_t lo;
do
{
hi = MTIME_HI;
lo = MTIME_LO;
} while (hi != MTIME_HI);
return ((uint64_t)hi << 32) | lo;
}
/* Only this hart writes its own mtimecmp, so a plain two-word read
cannot tear. */
static uint64_t clint_timecmp_read(uintptr_t hart)
{
uint32_t lo = MTIMECMP_LO(hart);
uint32_t hi = MTIMECMP_HI(hart);
return ((uint64_t)hi << 32) | lo;
}
/* RV32 mtimecmp write, safe sequence: park the low word at all-ones
first, so no intermediate 64-bit value compares below mtime and
raises a spurious timer interrupt. */
static void clint_timecmp_write(uintptr_t hart, uint64_t value)
{
MTIMECMP_LO(hart) = 0xFFFFFFFF;
MTIMECMP_HI(hart) = (uint32_t)(value >> 32);
MTIMECMP_LO(hart) = (uint32_t)value;
}
#else /* __riscv_xlen == 64 */
/* RV64: naturally aligned 64-bit MMIO accesses are single loads/stores. */
static uint64_t clint_time_read(void)
{
return *(volatile uint64_t *)CLINT_TIME;
}
static uint64_t clint_timecmp_read(uintptr_t hart)
{
return *(volatile uint64_t *)CLINT_TIMECMP(hart);
}
static void clint_timecmp_write(uintptr_t hart, uint64_t value)
{
*(volatile uint64_t *)CLINT_TIMECMP(hart) = value;
}
#endif /* __riscv_xlen */
int hwtimer_init(void)
{
uintptr_t hart = riscv_get_core();
uint64_t time = *((volatile uint64_t *)CLINT_TIME);
*((volatile uint64_t *)CLINT_TIMECMP(hart)) = time + TICKNUM_PER_TIMER;
clint_timecmp_write(hart, clint_time_read() + TICKNUM_PER_TIMER);
return 0;
}
int hwtimer_handler(void)
{
uintptr_t hart = riscv_get_core();
uint64_t time = *((volatile uint64_t *)CLINT_TIME);
*((volatile uint64_t *)CLINT_TIMECMP(hart)) = time + TICKNUM_PER_TIMER;
/* Absolute re-arm: advance from the previous compare value, so trap
latency does not accumulate as tick drift; catch up if the next
compare already passed. */
uint64_t next = clint_timecmp_read(hart) + TICKNUM_PER_TIMER;
uint64_t now = clint_time_read();
if (next <= now)
next = now + TICKNUM_PER_TIMER;
clint_timecmp_write(hart, next);
return 0;
}
+2 -2
View File
@@ -13,8 +13,8 @@
#include <stdint.h>
#define TICKNUM_PER_SECOND 10000000
#define TICKNUM_PER_TIMER (TICKNUM_PER_SECOND / 10)
#define TICKNUM_PER_SECOND 10000000UL
#define TICKNUM_PER_TIMER (TICKNUM_PER_SECOND / TX_TIMER_TICKS_PER_SECOND)
int hwtimer_init(void);
int hwtimer_handler(void);
+24 -8
View File
@@ -12,6 +12,12 @@ MEMORY
RAM (rwx) : ORIGIN = 0x80000000, LENGTH = 128M
}
PHDRS
{
text PT_LOAD FLAGS(5);
data PT_LOAD FLAGS(6);
}
SECTIONS
{
. = 0x80000000;
@@ -21,23 +27,24 @@ SECTIONS
*(.text .text.*)
. = ALIGN(0x1000);
PROVIDE(etext = .);
} > RAM
} > RAM :text
.rodata : {
. = ALIGN(16);
*(.srodata .srodata.*)
. = ALIGN(16);
*(.rodata .rodata.*)
} > RAM
} > RAM :text
.data : {
. = ALIGN(16);
. = ALIGN(0x1000);
PROVIDE(__global_pointer$ = . + 0x800);
*(.sdata .sdata.*)
. = ALIGN(16);
*(.data .data.*)
} > RAM
} > RAM :data
.bss : {
.bss (NOLOAD) : {
. = ALIGN(16);
_bss_start = .;
*(.sbss .sbss.*)
@@ -45,14 +52,23 @@ SECTIONS
*(.bss .bss.*)
*(COMMON)
_bss_end = .;
} > RAM
} > RAM :data
.stack : {
.stack (NOLOAD) : {
. = ALIGN(4096);
_sysstack_start = .;
. += 0x4000;
_sysstack_end = .;
} > RAM
} > RAM :data
/* Fixed 64 KiB newlib sbrk heap window. _end stays after it, so the
ThreadX first-unused-memory pointer starts past the heap. */
.heap (NOLOAD) : {
. = ALIGN(16);
_heap_start = .;
. += 0x10000;
_heap_end = .;
} > RAM :data
PROVIDE(_end = .);
+20 -5
View File
@@ -52,32 +52,47 @@ int plic_unregister_callback(int irqno)
int plic_init(void)
{
uintptr_t hart = riscv_get_core();
for (int i = 0; i < MAX_CALLBACK_NUM; i++)
{
callbacks[i] = NULL;
}
/* Do not depend on the reset state or on a prior boot stage: accept
every priority (threshold 0) and start with all sources for this
hart disabled. */
*(volatile uint32_t *)PLIC_MPRIORITY(hart) = 0;
for (int w = 0; w < MAX_CALLBACK_NUM / 32; w++)
*(volatile uint32_t *)(PLIC_MENABLE(hart) + w * 4) = 0;
return 0;
}
int plic_claim(void)
{
uintptr_t hart = riscv_get_core();
return (int)(*(uint32_t *)PLIC_MCLAIM(hart));
return (int)(*(volatile uint32_t *)PLIC_MCLAIM(hart));
}
void plic_complete(int irqno)
{
uintptr_t hart = riscv_get_core();
*(uint32_t *)(PLIC_MCOMPLETE(hart)) = (uint32_t)irqno;
*(volatile uint32_t *)(PLIC_MCOMPLETE(hart)) = (uint32_t)irqno;
}
int plic_irq_intr(void)
{
int ret = -1;
int irqno = plic_claim();
if (irqno <= 0 || irqno >= MAX_CALLBACK_NUM) {
plic_complete(irqno);
return 0; // spurious or out-of-range, not an error
if (irqno == 0)
return 0;
if (irqno < 0 || irqno >= MAX_CALLBACK_NUM) {
if (irqno > 0)
plic_complete(irqno);
return -1;
}
if (callbacks[irqno] != NULL)
ret = (callbacks[irqno])(irqno);
+47 -2
View File
@@ -15,6 +15,7 @@
#include <sys/stat.h>
#include <errno.h>
#include <stdint.h>
#include <time.h>
#define UART0_THR (*(volatile unsigned char *)0x10000000L)
#define UART0_LSR (*(volatile unsigned char *)0x10000005L)
@@ -25,6 +26,39 @@
#define VIRT_TEST_PASS 0x5555u
#define VIRT_TEST_FAIL 0x3333u
#define QEMU_TIMEBASE_HZ 10000000ULL
static uint64_t read_time_counter(void)
{
#if __riscv_xlen == 64
uint64_t value;
__asm__ volatile("rdtime %0" : "=r" (value));
return value;
#else
uint32_t high;
uint32_t low;
uint32_t high_check;
do
{
__asm__ volatile("rdtimeh %0" : "=r" (high));
__asm__ volatile("rdtime %0" : "=r" (low));
__asm__ volatile("rdtimeh %0" : "=r" (high_check));
} while (high != high_check);
return ((uint64_t)high << 32) | low;
#endif
}
time_t time(time_t *timer)
{
time_t seconds = (time_t)(read_time_counter() / QEMU_TIMEBASE_HZ);
if (timer != NULL)
*timer = seconds;
return seconds;
}
/* Called by testcontrol.c when EXTERNAL_EXIT is defined. */
__attribute__((noreturn)) void external_exit(unsigned int code)
{
@@ -50,13 +84,24 @@ int _write(int fd, const char *buf, int count)
return count;
}
extern char _end[];
/* Fixed 64 KiB heap window from the linker script. It keeps the newlib
heap out of the ThreadX first-unused-memory area, which starts at
_end (placed after _heap_end). */
extern char _heap_start[];
extern char _heap_end[];
static char *heap_ptr = 0;
void *_sbrk(int incr)
{
if (heap_ptr == 0)
heap_ptr = _end;
heap_ptr = _heap_start;
if ((incr > 0 && _heap_end - heap_ptr < incr) ||
(incr < 0 && heap_ptr - _heap_start < -incr))
{
errno = ENOMEM;
return (void *)-1;
}
char *prev = heap_ptr;
heap_ptr += incr;
-3
View File
@@ -36,9 +36,6 @@ extern void _exit(int code) __attribute__((noreturn));
void trap_handler(uintptr_t mcause, uintptr_t mepc, uintptr_t mtval)
{
(void)mepc;
(void)mtval;
if (OS_IS_INTERUPT(mcause))
{
if (OS_IS_TICK_INT(mcause))
@@ -99,7 +99,7 @@ _tx_initialize_low_level:
csrrc zero, mstatus, t0
li t0, (MSTATUS_MPP_M | MSTATUS_MPIE)
csrrs zero, mstatus, t0
li t0, (MIE_MTIE | MIE_MSIE | MIE_MEIE)
li t0, (MIE_MTIE | MIE_MEIE) // No MSIE: software interrupts are never dispatched here
csrrs zero, mie, t0
#ifdef __riscv_flen