diff --git a/bsp/qemu-virt64-riscv/.ci/attachconfig/ci.attachconfig.yml b/bsp/qemu-virt64-riscv/.ci/attachconfig/ci.attachconfig.yml new file mode 100644 index 000000000000..9385b3498fa6 --- /dev/null +++ b/bsp/qemu-virt64-riscv/.ci/attachconfig/ci.attachconfig.yml @@ -0,0 +1,20 @@ +utest.rvv.vlen128: + scons_arg: + - '--strict' + kconfig: + - CONFIG_ENABLE_VECTOR=y + - CONFIG_RT_USING_HEAP=y + - CONFIG_RT_USING_UTEST=y + - CONFIG_ARCH_VECTOR_VLEN_128=y + - CONFIG_ARCH_VECTOR_VLEN_256=n + - CONFIG_RT_USING_SMP=n +utest.rvv.vlen256: + scons_arg: + - '--strict' + kconfig: + - CONFIG_ENABLE_VECTOR=y + - CONFIG_RT_USING_HEAP=y + - CONFIG_RT_USING_UTEST=y + - CONFIG_ARCH_VECTOR_VLEN_128=n + - CONFIG_ARCH_VECTOR_VLEN_256=y + - CONFIG_RT_USING_SMP=n diff --git a/bsp/qemu-virt64-riscv/SConstruct b/bsp/qemu-virt64-riscv/SConstruct index ae0e3375e111..06d2a468df75 100644 --- a/bsp/qemu-virt64-riscv/SConstruct +++ b/bsp/qemu-virt64-riscv/SConstruct @@ -27,6 +27,9 @@ rtconfig.ARCH='risc-v' # prepare building environment objs = PrepareBuilding(env, RTT_ROOT, has_libcpu = False) +if GetDepend('ENABLE_VECTOR'): + env['ASFLAGS'] = str(env['ASFLAGS']).replace('-march=rv64imafdc ', '-march=rv64imafdcv ') + stack_size = 4096 if GetDepend('RT_USING_SMART'): diff --git a/bsp/qemu-virt64-riscv/applications/test/test_vector/SConscript b/bsp/qemu-virt64-riscv/applications/test/test_vector/SConscript index 0827b048b281..0a287adadb21 100644 --- a/bsp/qemu-virt64-riscv/applications/test/test_vector/SConscript +++ b/bsp/qemu-virt64-riscv/applications/test/test_vector/SConscript @@ -3,7 +3,12 @@ from building import * cwd = GetCurrentDir() src = Glob('*.c') + Glob('*.cpp') CPPPATH = [cwd] +LOCAL_CCFLAGS = '' -group = DefineGroup('Vector', src, depend = [''], CPPPATH = CPPPATH) +if GetDepend(['RT_USING_UTEST', 'ENABLE_VECTOR']): + src += ['vector_context_gcc.S'] + LOCAL_CCFLAGS = ' -march=rv64gcv' + +group = DefineGroup('Vector', src, depend = [''], CPPPATH = CPPPATH, LOCAL_CCFLAGS = LOCAL_CCFLAGS) Return('group') diff --git a/bsp/qemu-virt64-riscv/applications/test/test_vector/test_vector.c b/bsp/qemu-virt64-riscv/applications/test/test_vector/test_vector.c index 50aae8024803..b84e1c42b302 100644 --- a/bsp/qemu-virt64-riscv/applications/test/test_vector/test_vector.c +++ b/bsp/qemu-virt64-riscv/applications/test/test_vector/test_vector.c @@ -55,8 +55,8 @@ static void test_feature(void) * not modified by each other * ============================================================== */ -#define TEST_THREAD 2 -#define VECTOR_CTX_BYTES (CTX_VECTOR_REG_NR * REGBYTES) +#define TEST_THREAD 2 +#define VECTOR_CTX_BYTES (CTX_VECTOR_REG_NR * sizeof(rt_ubase_t)) void *ctx_vector[TEST_THREAD * 2]; static rt_sem_t sem; diff --git a/bsp/qemu-virt64-riscv/applications/test/test_vector/vector_context_gcc.S b/bsp/qemu-virt64-riscv/applications/test/test_vector/vector_context_gcc.S new file mode 100644 index 000000000000..42e60f323df1 --- /dev/null +++ b/bsp/qemu-virt64-riscv/applications/test/test_vector/vector_context_gcc.S @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __ASSEMBLY__ +#define __ASSEMBLY__ +#endif +#define REGBYTES 8 +#define STORE sd +#define LOAD ld + +#include +#include "vector_encoding.h" +#include "rvv_context.h" + + .text + .align 2 + + /* Test-only helpers; SAVE_VECTOR must not change the caller's controls. */ + .globl rt_hw_vector_ctx_save + .type rt_hw_vector_ctx_save, @function +rt_hw_vector_ctx_save: + csrrci a3, sstatus, 2 + li t0, SSTATUS_VS + csrs sstatus, t0 + SAVE_VECTOR a0 + RESTORE_VECTOR a0 + csrw sstatus, a3 + ret + .size rt_hw_vector_ctx_save, . - rt_hw_vector_ctx_save + + .globl rt_hw_vector_ctx_restore + .type rt_hw_vector_ctx_restore, @function +rt_hw_vector_ctx_restore: + csrrci a3, sstatus, 2 + li t0, SSTATUS_VS + csrs sstatus, t0 + RESTORE_VECTOR a0 + csrw sstatus, a3 + ret + .size rt_hw_vector_ctx_restore, . - rt_hw_vector_ctx_restore diff --git a/bsp/qemu-virt64-riscv/applications/utest/SConscript b/bsp/qemu-virt64-riscv/applications/utest/SConscript new file mode 100644 index 000000000000..8e56d71ed7bc --- /dev/null +++ b/bsp/qemu-virt64-riscv/applications/utest/SConscript @@ -0,0 +1,10 @@ +from building import * + +cwd = GetCurrentDir() +src = ['rvv_vstart_tc.c', 'rvv_vstart_probe_gcc.S'] + +group = DefineGroup('RVV utestcases', src, + depend = ['RT_USING_UTEST', 'ENABLE_VECTOR', 'RT_USING_HEAP'], + CPPPATH = [cwd]) + +Return('group') diff --git a/bsp/qemu-virt64-riscv/applications/utest/rvv_vstart_probe_gcc.S b/bsp/qemu-virt64-riscv/applications/utest/rvv_vstart_probe_gcc.S new file mode 100644 index 000000000000..e02cbaf393d4 --- /dev/null +++ b/bsp/qemu-virt64-riscv/applications/utest/rvv_vstart_probe_gcc.S @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-10-10 Piyush Patle Add RVV restore regression probe + */ + +#ifndef __ASSEMBLY__ +#define __ASSEMBLY__ +#endif +#define REGBYTES 8 +#define STORE sd +#define LOAD ld + +#include +#include "vector_encoding.h" +#include "rvv_context.h" + + .text + .align 2 + .option push + .option arch, +v + + /* a0: expected frame, a1: observed frame, a2: caller's vector frame. */ + .globl rvv_vstart_probe + .type rvv_vstart_probe, @function +rvv_vstart_probe: + /* Keep interrupts masked until the caller's complete state is restored. */ + csrrci a3, sstatus, 2 + li t0, SSTATUS_VS + csrs sstatus, t0 + SAVE_VECTOR a2 + + RESTORE_VECTOR a0 + /* SAVE_VECTOR records vstart before issuing any vector instruction. */ + SAVE_VECTOR a1 + + RESTORE_VECTOR a2 + csrw sstatus, a3 + ret + .size rvv_vstart_probe, . - rvv_vstart_probe + + .option pop diff --git a/bsp/qemu-virt64-riscv/applications/utest/rvv_vstart_tc.c b/bsp/qemu-virt64-riscv/applications/utest/rvv_vstart_tc.c new file mode 100644 index 000000000000..13776447184e --- /dev/null +++ b/bsp/qemu-virt64-riscv/applications/utest/rvv_vstart_tc.c @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-10-10 Piyush Patle Test zero and nonzero RVV restart indices + */ + +#include + +/* Run with: utest_run testcases.libcpu.rvv_vstart */ +#if defined(ENABLE_VECTOR) && defined(RT_USING_HEAP) && defined(RT_USING_UTEST) +#include "rvv_context.h" + +/* RV64 vector frames contain four control words followed by v0 through v31. */ +#define VECTOR_CONTROL_WORDS 4 +#define VECTOR_GUARD 0x5a5a5a5aUL + +struct vector_test_frame +{ + rt_ubase_t before[2]; + rt_ubase_t image[CTX_VECTOR_REG_NR]; + rt_ubase_t after[2]; +}; + +void rvv_vstart_probe(const rt_ubase_t *input, rt_ubase_t *output, rt_ubase_t *backup); + +static void frame_init(struct vector_test_frame *frame) +{ + rt_memset(frame, 0, sizeof(*frame)); + frame->before[0] = frame->before[1] = VECTOR_GUARD; + frame->after[0] = frame->after[1] = VECTOR_GUARD; +} + +static rt_bool_t frame_guards_valid(const struct vector_test_frame *frame) +{ + return frame->before[0] == VECTOR_GUARD && frame->before[1] == VECTOR_GUARD && + frame->after[0] == VECTOR_GUARD && frame->after[1] == VECTOR_GUARD; +} + +static int rvv_vstart_test(void) +{ + rt_ubase_t vlenb; + rt_ubase_t starts[3]; + struct vector_test_frame *frames; + struct vector_test_frame *expected; + struct vector_test_frame *observed; + struct vector_test_frame *saved; + rt_uint8_t *payload; + rt_size_t payload_size = sizeof(expected->image) - VECTOR_CONTROL_WORDS * sizeof(rt_ubase_t); + rt_size_t i; + rt_size_t j; + + __asm__ volatile("csrr %0, vlenb" : "=r"(vlenb)); + if (payload_size != 32 * vlenb) + { + rt_kprintf("RVV test: configured frame does not match vlenb=%lu\n", vlenb); + return -RT_ERROR; + } + + /* Each invocation owns its frames, including the caller-state backup. */ + frames = rt_malloc(3 * sizeof(*frames)); + if (frames == RT_NULL) + { + return -RT_ENOMEM; + } + expected = &frames[0]; + observed = &frames[1]; + saved = &frames[2]; + payload = (rt_uint8_t *)&expected->image[VECTOR_CONTROL_WORDS]; + frame_init(expected); + for (i = 0; i < payload_size; i++) + { + payload[i] = (rt_uint8_t)(i * 37 + i / vlenb + 0x5a); + } + + starts[0] = 0; + starts[1] = 1; + starts[2] = vlenb - 1; + + for (i = 0; i < sizeof(starts) / sizeof(starts[0]); i++) + { + for (j = 0; j < 3; j++) + { + expected->image[0] = starts[i]; + expected->image[1] = 0; /* e8, m1, tail/mask undisturbed */ + expected->image[2] = vlenb; + expected->image[3] = j * 3; /* Exercise vxrm and vxsat through vcsr. */ + frame_init(observed); + frame_init(saved); + + rvv_vstart_probe(expected->image, observed->image, saved->image); + + if (!frame_guards_valid(expected) || !frame_guards_valid(observed) || + !frame_guards_valid(saved) || + rt_memcmp(expected->image, observed->image, sizeof(expected->image)) != 0) + { + rt_kprintf("RVV restore failed: vstart expected=%lu observed=%lu\n", + starts[i], observed->image[0]); + rt_free(frames); + return -RT_ERROR; + } + } + } + + rt_free(frames); + rt_kprintf("RVV restore passed: vlenb=%lu, 9 control/register checks\n", vlenb); + return RT_EOK; +} + +#include + +static void testcase(void) +{ + uassert_int_equal(rvv_vstart_test(), RT_EOK); +} + +UTEST_TC_EXPORT(testcase, "testcases.libcpu.rvv_vstart", RT_NULL, RT_NULL, 10); +#endif diff --git a/libcpu/risc-v/vector/rvv-1.0/rvv_context.h b/libcpu/risc-v/vector/rvv-1.0/rvv_context.h index 4466b3aeff1d..f5ec4d4ae8d3 100644 --- a/libcpu/risc-v/vector/rvv-1.0/rvv_context.h +++ b/libcpu/risc-v/vector/rvv-1.0/rvv_context.h @@ -12,17 +12,20 @@ #define __RVV_CONTEXT_1_0_H__ #if defined(ARCH_VECTOR_VLEN_128) - #define CTX_VECTOR_REGS 64 +#define CTX_VECTOR_REGS 64 #elif defined(ARCH_VECTOR_VLEN_256) - #define CTX_VECTOR_REGS 128 +#define CTX_VECTOR_REGS 128 #else #error "No supported VLEN" #endif /* VLEN */ -#define CTX_VECTOR_REG_NR (CTX_VECTOR_REGS + 4) +#define CTX_VECTOR_REG_NR (CTX_VECTOR_REGS + 4) #ifdef __ASSEMBLY__ +/* The following macros contain assembler, not C. */ +/* clang-format off */ + /** * ================================== * VECTOR EXTENSION @@ -97,16 +100,19 @@ mv t1, t0 - LOAD t0, VEC_FRAME_VSTART(t1) - csrw vstart, t0 LOAD t0, VEC_FRAME_VCSR(t1) csrw vcsr, t0 LOAD t0, VEC_FRAME_VTYPE(t1) LOAD t3, VEC_FRAME_VL(t1) VEC_CONFIG_SET_VL_VTYPE(t3, t0) + + /* vsetvl resets vstart, so restore the restart index last. */ + LOAD t0, VEC_FRAME_VSTART(t1) + csrw vstart, t0 .endm +/* clang-format on */ #endif #endif /* __RVV_CONTEXT_H__ */