diff options
Diffstat (limited to 'test/tx/cmake/riscv/bsp')
| -rw-r--r-- | test/tx/cmake/riscv/bsp/entry.S | 6 | ||||
| -rw-r--r-- | test/tx/cmake/riscv/bsp/hwtimer.c | 84 | ||||
| -rw-r--r-- | test/tx/cmake/riscv/bsp/hwtimer.h | 4 | ||||
| -rw-r--r-- | test/tx/cmake/riscv/bsp/link.lds | 32 | ||||
| -rw-r--r-- | test/tx/cmake/riscv/bsp/plic.c | 25 | ||||
| -rw-r--r-- | test/tx/cmake/riscv/bsp/syscalls.c | 49 | ||||
| -rw-r--r-- | test/tx/cmake/riscv/bsp/trap.c | 3 | ||||
| -rw-r--r-- | test/tx/cmake/riscv/bsp/tx_initialize_low_level.S | 35 |
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 |
