mirror of
https://github.com/eclipse-threadx/threadx.git
synced 2026-10-06 06:59:08 +08:00
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:
co-authored by
Frédéric Desbiens
parent
3fc28d8979
commit
40db27e843
@@ -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")
|
||||
|
||||
@@ -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 = .);
|
||||
}
|
||||
|
||||
+193
-84
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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 = .);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user