summaryrefslogtreecommitdiff
path: root/test/tx/cmake/riscv/bsp
diff options
context:
space:
mode:
authorFrédéric Desbiens <[email protected]>2026-10-02 10:42:44 -0400
committerGitHub <[email protected]>2026-10-02 10:42:44 -0400
commit93387b0a60382a29579bbad5045c68328e3751df (patch)
tree42be790d602cb76f55090e5a7c418df7ea2b3428 /test/tx/cmake/riscv/bsp
parent44d7c95c582d415c4ad84527180b29c93c3bf664 (diff)
parent28281cd5647d4671236ca08d19bd18ed15b2265a (diff)
Merge pull request #783 from eclipse-threadx/devv6.5.2.202603_rel
Merged changes ahead of the v6.5.2.202603 release
Diffstat (limited to 'test/tx/cmake/riscv/bsp')
-rw-r--r--test/tx/cmake/riscv/bsp/entry.S6
-rw-r--r--test/tx/cmake/riscv/bsp/hwtimer.c84
-rw-r--r--test/tx/cmake/riscv/bsp/hwtimer.h4
-rw-r--r--test/tx/cmake/riscv/bsp/link.lds32
-rw-r--r--test/tx/cmake/riscv/bsp/plic.c25
-rw-r--r--test/tx/cmake/riscv/bsp/syscalls.c49
-rw-r--r--test/tx/cmake/riscv/bsp/trap.c3
-rw-r--r--test/tx/cmake/riscv/bsp/tx_initialize_low_level.S35
8 files changed, 205 insertions, 33 deletions
diff --git a/test/tx/cmake/riscv/bsp/entry.S b/test/tx/cmake/riscv/bsp/entry.S
index 2f16c052..5abe9852 100644
--- a/test/tx/cmake/riscv/bsp/entry.S
+++ b/test/tx/cmake/riscv/bsp/entry.S
@@ -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
diff --git a/test/tx/cmake/riscv/bsp/hwtimer.c b/test/tx/cmake/riscv/bsp/hwtimer.c
index 99821f1e..8b81268a 100644
--- a/test/tx/cmake/riscv/bsp/hwtimer.c
+++ b/test/tx/cmake/riscv/bsp/hwtimer.c
@@ -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;
}
diff --git a/test/tx/cmake/riscv/bsp/hwtimer.h b/test/tx/cmake/riscv/bsp/hwtimer.h
index e6b9efa4..bde0bba5 100644
--- a/test/tx/cmake/riscv/bsp/hwtimer.h
+++ b/test/tx/cmake/riscv/bsp/hwtimer.h
@@ -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);
diff --git a/test/tx/cmake/riscv/bsp/link.lds b/test/tx/cmake/riscv/bsp/link.lds
index 42f08fb6..717530b3 100644
--- a/test/tx/cmake/riscv/bsp/link.lds
+++ b/test/tx/cmake/riscv/bsp/link.lds
@@ -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 = .);
diff --git a/test/tx/cmake/riscv/bsp/plic.c b/test/tx/cmake/riscv/bsp/plic.c
index a2b44fec..16dad815 100644
--- a/test/tx/cmake/riscv/bsp/plic.c
+++ b/test/tx/cmake/riscv/bsp/plic.c
@@ -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);
diff --git a/test/tx/cmake/riscv/bsp/syscalls.c b/test/tx/cmake/riscv/bsp/syscalls.c
index 802fe8af..40ca86f3 100644
--- a/test/tx/cmake/riscv/bsp/syscalls.c
+++ b/test/tx/cmake/riscv/bsp/syscalls.c
@@ -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;
diff --git a/test/tx/cmake/riscv/bsp/trap.c b/test/tx/cmake/riscv/bsp/trap.c
index 0ac7990a..f54ea2ff 100644
--- a/test/tx/cmake/riscv/bsp/trap.c
+++ b/test/tx/cmake/riscv/bsp/trap.c
@@ -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))
diff --git a/test/tx/cmake/riscv/bsp/tx_initialize_low_level.S b/test/tx/cmake/riscv/bsp/tx_initialize_low_level.S
index 8d474d66..d72c9b6a 100644
--- a/test/tx/cmake/riscv/bsp/tx_initialize_low_level.S
+++ b/test/tx/cmake/riscv/bsp/tx_initialize_low_level.S
@@ -1,5 +1,6 @@
/***************************************************************************
* Copyright (c) 2026 10xEngineers
+ * Copyright (c) 2026 Eclipse ThreadX contributors
*
* This program and the accompanying materials are made available under the
* terms of the MIT License which is available at
@@ -8,10 +9,13 @@
* SPDX-License-Identifier: MIT
**************************************************************************/
+// Portions of this file were generated with AI assistance.
+
/* Trap entry and low-level init for RISC-V QEMU virt regression tests.
Supports both RV32 and RV64 via __riscv_xlen conditionals. */
#include "csr.h"
+#include "tx_port.h"
#if __riscv_xlen == 64
#define STORE sd
@@ -33,13 +37,28 @@
.extern trap_handler
.extern _tx_thread_context_restore
-trap_entry:
+/* Take the interrupt frame size from the port that this BSP is linked
+ against, so the two can never disagree. A port that publishes no
+ contract keeps the historical layout. */
+#ifndef TX_RISCV_TRAP_FRAME_SIZE
#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double)
- addi sp, sp, -(65 * REGBYTES)
+#define TX_RISCV_TRAP_FRAME_SIZE (65 * REGBYTES)
#else
- addi sp, sp, -(32 * REGBYTES)
+#define TX_RISCV_TRAP_FRAME_SIZE (32 * REGBYTES)
+#endif
+#endif
+
+/* The RISC-V psABI requires sp to be 16-byte aligned at a call. */
+#ifndef TX_RISCV_TRAP_CALL_FRAME_SIZE
+#define TX_RISCV_TRAP_CALL_FRAME_SIZE 16
#endif
+ .equ TX_TRAP_FRAME_SIZE, TX_RISCV_TRAP_FRAME_SIZE
+ .equ TX_TRAP_CALL_FRAME_SIZE, TX_RISCV_TRAP_CALL_FRAME_SIZE
+
+trap_entry:
+ addi sp, sp, -TX_TRAP_FRAME_SIZE
+
STORE x1, (28 * REGBYTES)(sp)
call _tx_thread_context_save
@@ -47,11 +66,11 @@ trap_entry:
csrr a0, mcause
csrr a1, mepc
csrr a2, mtval
- addi sp, sp, -REGBYTES
+ addi sp, sp, -TX_TRAP_CALL_FRAME_SIZE
STORE ra, 0(sp)
call trap_handler
LOAD ra, 0(sp)
- addi sp, sp, REGBYTES
+ addi sp, sp, TX_TRAP_CALL_FRAME_SIZE
call _tx_thread_context_restore
@@ -80,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
@@ -89,11 +108,11 @@ _tx_initialize_low_level:
fscsr x0
#endif
- addi sp, sp, -REGBYTES
+ addi sp, sp, -TX_TRAP_CALL_FRAME_SIZE
STORE ra, 0(sp)
call board_init
LOAD ra, 0(sp)
- addi sp, sp, REGBYTES
+ addi sp, sp, TX_TRAP_CALL_FRAME_SIZE
la t0, trap_entry
csrw mtvec, t0