From 28044f7d5a9d26f9778f07b24a1ecd043354f33d Mon Sep 17 00:00:00 2001 From: chao an Date: Mon, 22 Apr 2024 10:10:25 +0800 Subject: [PATCH] arch/risc-v: add support of save/restore vector registers Reference: https://github.com/riscv/riscv-v-spec/blob/master/v-spec.adoc https://github.com/torvalds/linux/blob/master/arch/riscv/include/asm/vector.h Signed-off-by: chao an --- arch/risc-v/Kconfig | 17 ++ arch/risc-v/include/irq.h | 49 ++++++ arch/risc-v/src/common/CMakeLists.txt | 4 + arch/risc-v/src/common/Make.defs | 4 + arch/risc-v/src/common/riscv_getnewintctx.c | 3 + arch/risc-v/src/common/riscv_initialstate.c | 27 +++ arch/risc-v/src/common/riscv_internal.h | 30 ++++ arch/risc-v/src/common/riscv_macros.S | 83 ++++++++++ arch/risc-v/src/common/riscv_releasestack.c | 9 + arch/risc-v/src/common/riscv_vpu.S | 156 ++++++++++++++++++ arch/risc-v/src/qemu-rv/Kconfig | 5 + .../qemu-rv/rv-virt/configs/smp64/defconfig | 1 + 12 files changed, 388 insertions(+) create mode 100644 arch/risc-v/src/common/riscv_vpu.S diff --git a/arch/risc-v/Kconfig b/arch/risc-v/Kconfig index 7f6f0c3954eea..3b563d35f338c 100644 --- a/arch/risc-v/Kconfig +++ b/arch/risc-v/Kconfig @@ -357,6 +357,23 @@ config ARCH_RV_ISA_V default n depends on ARCH_FPU +if ARCH_RV_ISA_V + +config ARCH_RV_VECTOR_BYTE_LENGTH + int "Vector Register Length in bytes" + default 32 + ---help--- + Predefined vector register length. If CSR vlenb is greater than the + current reserved value, appropriate memory will be allocated to + save/restore the vector registers. + The XLEN-bit-wide read-only CSR vlenb holds the value VLEN/8, i.e., + the vector register length in bytes. The value in vlenb is a + design-time constant in any implementation. Without this CSR, several + instructions are needed to calculate VLEN in bytes. The code has to + disturb current vl and vtype settings which require them to be saved and restored. + +endif + config ARCH_RV_ISA_ZICSR_ZIFENCEI bool default y diff --git a/arch/risc-v/include/irq.h b/arch/risc-v/include/irq.h index c55458692f5de..ed3da17edb641 100644 --- a/arch/risc-v/include/irq.h +++ b/arch/risc-v/include/irq.h @@ -257,6 +257,29 @@ #define XCPTCONTEXT_SIZE (INT_XCPT_SIZE + FPU_XCPT_SIZE) #endif +#ifdef CONFIG_ARCH_RV_ISA_V +# define REG_VSTART_NDX (0) +# define REG_VTYPE_NDX (1) +# define REG_VL_NDX (2) +# define REG_VCSR_NDX (3) +# define REG_VLENB_NDX (4) + +# define VPU_XCPT_REGS (5) +# define VPU_XCPT_SIZE (INT_REG_SIZE * VPU_XCPT_REGS) + +# if CONFIG_ARCH_RV_VECTOR_BYTE_LENGTH > 0 + +/* There are 32 vector registers(v0 - v31) with vlenb length. */ + +# define VPU_XCPTC_SIZE (CONFIG_ARCH_RV_VECTOR_BYTE_LENGTH * 32 + VPU_XCPT_SIZE) + +# endif +#else /* !CONFIG_ARCH_RV_ISA_V */ +# define VPU_XCPT_REGS (0) +# define VPU_XCPT_SIZE (0) +# define VPU_XCPTC_SIZE (0) +#endif /* CONFIG_ARCH_RV_ISA_V */ + /* In assembly language, values have to be referenced as byte address * offsets. But in C, it is more convenient to reference registers as * register save table offsets. @@ -333,6 +356,14 @@ # define REG_FCSR (INT_REG_SIZE*REG_FCSR_NDX) #endif +#ifdef CONFIG_ARCH_RV_ISA_V +# define REG_VSTART (INT_REG_SIZE*REG_VSTART_NDX) +# define REG_VTYPE (INT_REG_SIZE*REG_VTYPE_NDX) +# define REG_VL (INT_REG_SIZE*REG_VL_NDX) +# define REG_VCSR (INT_REG_SIZE*REG_VCSR_NDX) +# define REG_VLENB (INT_REG_SIZE*REG_VLENB_NDX) +#endif + #else # define REG_EPC REG_EPC_NDX # define REG_X1 REG_X1_NDX @@ -404,6 +435,14 @@ # define REG_FCSR REG_FCSR_NDX #endif +#ifdef CONFIG_ARCH_RV_ISA_V +# define REG_VSTART REG_VSTART_NDX +# define REG_VTYPE REG_VTYPE_NDX +# define REG_VL REG_VL_NDX +# define REG_VCSR REG_VCSR_NDX +# define REG_VLENB REG_VLENB_NDX +#endif + #endif /* Now define more user friendly alternative name that can be used either @@ -579,6 +618,16 @@ struct xcptcontext #if defined(CONFIG_ARCH_FPU) && defined(CONFIG_ARCH_LAZYFPU) uintptr_t fregs[FPU_XCPT_REGS]; #endif + +#ifdef CONFIG_ARCH_RV_ISA_V +# if CONFIG_ARCH_RV_VECTOR_BYTE_LENGTH > 0 + /* There are 32 vector registers(v0 - v31) with vlenb length. */ + + uintptr_t vregs[VPU_XCPTC_SIZE]; +# else + uintptr_t *vregs; +# endif +#endif }; #endif /* __ASSEMBLY__ */ diff --git a/arch/risc-v/src/common/CMakeLists.txt b/arch/risc-v/src/common/CMakeLists.txt index c83f576c98bcb..28b6be70abed1 100644 --- a/arch/risc-v/src/common/CMakeLists.txt +++ b/arch/risc-v/src/common/CMakeLists.txt @@ -76,6 +76,10 @@ if(CONFIG_ARCH_FPU) list(APPEND SRCS riscv_fpu.S riscv_fpucmp.c) endif() +if(CONFIG_ARCH_RV_ISA_V) + list(APPEND SRCS riscv_vpu.S) +endif() + if(CONFIG_ARCH_RV_ISA_A) list(APPEND SRCS riscv_testset.S) endif() diff --git a/arch/risc-v/src/common/Make.defs b/arch/risc-v/src/common/Make.defs index 7431752c944cd..9088cfbe10a35 100644 --- a/arch/risc-v/src/common/Make.defs +++ b/arch/risc-v/src/common/Make.defs @@ -79,6 +79,10 @@ CMN_ASRCS += riscv_fpu.S CMN_CSRCS += riscv_fpucmp.c endif +ifeq ($(CONFIG_ARCH_RV_ISA_V),y) +CMN_ASRCS += riscv_vpu.S +endif + ifeq ($(CONFIG_ARCH_RV_ISA_A),y) CMN_ASRCS += riscv_testset.S endif diff --git a/arch/risc-v/src/common/riscv_getnewintctx.c b/arch/risc-v/src/common/riscv_getnewintctx.c index 2588ff2841dc4..cd6a98c7ac34c 100644 --- a/arch/risc-v/src/common/riscv_getnewintctx.c +++ b/arch/risc-v/src/common/riscv_getnewintctx.c @@ -67,6 +67,9 @@ uintptr_t riscv_get_newintctx(void) return (status | STATUS_PPP | STATUS_SUM | STATUS_PIE #ifdef CONFIG_ARCH_FPU | MSTATUS_FS_INIT +#endif +#ifdef CONFIG_ARCH_RV_ISA_V + | MSTATUS_VS_INIT #endif ); } diff --git a/arch/risc-v/src/common/riscv_initialstate.c b/arch/risc-v/src/common/riscv_initialstate.c index 08895958befdb..475bea69a4dd1 100644 --- a/arch/risc-v/src/common/riscv_initialstate.c +++ b/arch/risc-v/src/common/riscv_initialstate.c @@ -30,6 +30,7 @@ #include #include +#include #include #include "addrenv.h" @@ -56,6 +57,9 @@ void up_initial_state(struct tcb_s *tcb) { struct xcptcontext *xcp = &tcb->xcp; +#if defined(CONFIG_ARCH_RV_ISA_V) && (CONFIG_ARCH_RV_VECTOR_BYTE_LENGTH == 0) + uintptr_t *vregs = tcb->vregs; +#endif uintptr_t regval; uintptr_t topstack; #ifdef CONFIG_ARCH_KERNEL_STACK @@ -66,6 +70,29 @@ void up_initial_state(struct tcb_s *tcb) memset(xcp, 0, sizeof(struct xcptcontext)); +#if defined(CONFIG_ARCH_RV_ISA_V) && (CONFIG_ARCH_RV_VECTOR_BYTE_LENGTH == 0) + + /* Initialize vector registers */ + + if (vregs == NULL) + { + regval = READ_CSR(CSR_VLENB); + if (regval != 0) + { + /* There are 32 vector registers(v0 - v31) with vlenb length. */ + + xcp->vregs = kmm_calloc(1, regval * 32 + VPU_XCPT_SIZE); + DEBUGASSERT(xcp->vregs != NULL); + } + } + else + { + /* Keep the vector region if task restart */ + + xcp->vregs = vregs; + } +#endif + /* Initialize the idle thread stack */ if (tcb->pid == IDLE_PROCESS_ID) diff --git a/arch/risc-v/src/common/riscv_internal.h b/arch/risc-v/src/common/riscv_internal.h index b8364605b6eee..68e3c5c8af61b 100644 --- a/arch/risc-v/src/common/riscv_internal.h +++ b/arch/risc-v/src/common/riscv_internal.h @@ -231,6 +231,24 @@ static inline uintptr_t *riscv_fpuregs(struct tcb_s *tcb) # define riscv_fpuregs(tcb) #endif +#ifdef CONFIG_ARCH_RV_ISA_V +void riscv_vpuconfig(void); +void riscv_savevpu(uintptr_t *regs, uintptr_t *vregs); +void riscv_restorevpu(uintptr_t *regs, uintptr_t *vregs); + +/* Get VPU register save area */ + +static inline uintptr_t *riscv_vpuregs(struct tcb_s *tcb) +{ + return tcb->xcp.vregs; +} +#else +# define riscv_vpuconfig() +# define riscv_savevpu(regs, vregs) +# define riscv_restorevpu(regs, vregs) +# define riscv_vpuregs(tcb) +#endif + /* Save / restore context of task */ static inline void riscv_savecontext(struct tcb_s *tcb) @@ -242,6 +260,12 @@ static inline void riscv_savecontext(struct tcb_s *tcb) riscv_savefpu(tcb->xcp.regs, riscv_fpuregs(tcb)); #endif + +#ifdef CONFIG_ARCH_RV_ISA_V + /* Save current process VPU state to TCB */ + + riscv_savevpu(tcb->xcp.regs, riscv_vpuregs(tcb)); +#endif } static inline void riscv_restorecontext(struct tcb_s *tcb) @@ -253,6 +277,12 @@ static inline void riscv_restorecontext(struct tcb_s *tcb) riscv_restorefpu(tcb->xcp.regs, riscv_fpuregs(tcb)); #endif + +#ifdef CONFIG_ARCH_RV_ISA_V + /* Restore VPU state for next process */ + + riscv_restorevpu(tcb->xcp.regs, riscv_vpuregs(tcb)); +#endif } /* RISC-V PMP Config ********************************************************/ diff --git a/arch/risc-v/src/common/riscv_macros.S b/arch/risc-v/src/common/riscv_macros.S index fd10ce8bc20c2..34b2971e287d0 100644 --- a/arch/risc-v/src/common/riscv_macros.S +++ b/arch/risc-v/src/common/riscv_macros.S @@ -140,6 +140,48 @@ .endm +/**************************************************************************** + * Name: riscv_savevpu + * + * Parameter: + * in - Pointer to where the save is performed (e.g. sp) + * + * Description: + * Save the VPU context registers (i.e. work / temp / etc). + * + ****************************************************************************/ + +.macro riscv_savevpu in + + /* Store all vector registers */ + + mv t1, \in + + csrr t0, CSR_VSTART + REGSTORE t0, REG_VSTART(t1) + csrr t0, CSR_VTYPE + REGSTORE t0, REG_VTYPE(t1) + csrr t0, CSR_VL + REGSTORE t0, REG_VL(t1) + csrr t0, CSR_VCSR + REGSTORE t0, REG_VCSR(t1) + csrr t0, CSR_VLENB + REGSTORE t0, REG_VLENB(t1) + + addi t1, t1, VPU_XCPT_SIZE + + vsetvli t2, x0, e8, m8, ta, ma + + vse8.v v0, (t1) + add t1, t1, t2 + vse8.v v8, (t1) + add t1, t1, t2 + vse8.v v16, (t1) + add t1, t1, t2 + vse8.v v24, (t1) + +.endm + /**************************************************************************** * Name: load_ctx * @@ -243,6 +285,47 @@ .endm +/**************************************************************************** + * Name: riscv_loadvpu + * + * Parameter: + * out - Pointer to where the load is performed (e.g. sp) + * + * Description: + * Load the VPU context registers (i.e. work / temp / etc). + * + ****************************************************************************/ + +.macro riscv_loadvpu out + + /* Load all vector registers */ + + mv t0, \out + addi t1, t0, VPU_XCPT_SIZE + + vsetvli t2, x0, e8, m8, ta, ma + + vle8.v v0, (t1) + add t1, t1, t2 + vle8.v v8, (t1) + add t1, t1, t2 + vle8.v v16, (t1) + add t1, t1, t2 + vle8.v v24, (t1) + + mv t1, t0 + + REGLOAD t0, REG_VTYPE(t1) + REGLOAD t3, REG_VL(t1) + vsetvl x0, t3, t0 + + REGLOAD t0, REG_VSTART(t1) + csrw CSR_VSTART, t0 + REGLOAD t0, REG_VCSR(t1) + csrw CSR_VCSR, t0 + +.endm + /**************************************************************************** * Name: setintstack * diff --git a/arch/risc-v/src/common/riscv_releasestack.c b/arch/risc-v/src/common/riscv_releasestack.c index 72c22f5b36c42..884b52c723834 100644 --- a/arch/risc-v/src/common/riscv_releasestack.c +++ b/arch/risc-v/src/common/riscv_releasestack.c @@ -103,4 +103,13 @@ void up_release_stack(struct tcb_s *dtcb, uint8_t ttype) dtcb->stack_alloc_ptr = NULL; dtcb->stack_base_ptr = NULL; dtcb->adj_stack_size = 0; + + /* Release vector register context */ + +#if defined(CONFIG_ARCH_RV_ISA_V) && (CONFIG_ARCH_RV_VECTOR_BYTE_LENGTH == 0) + if (dtcb->xcp.vregs != NULL) + { + kmm_free(dtcb->xcp.vregs); + } +#endif } diff --git a/arch/risc-v/src/common/riscv_vpu.S b/arch/risc-v/src/common/riscv_vpu.S new file mode 100644 index 0000000000000..4725617755d74 --- /dev/null +++ b/arch/risc-v/src/common/riscv_vpu.S @@ -0,0 +1,156 @@ +/**************************************************************************** + * arch/risc-v/src/common/riscv_vpu.S + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include "riscv_macros.S" + +#ifdef CONFIG_ARCH_RV_ISA_V + +/**************************************************************************** + * Public Symbols + ****************************************************************************/ + + .globl riscv_vpuconfig + .globl riscv_savevpu + .globl riscv_restorevpu + .file "riscv_vpu.S" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: riscv_vpuconfig + * + * Description: + * init vpu + * + * C Function Prototype: + * void riscv_vpuconfig(void); + * + * Input Parameters: + * None + * + * Returned Value: + * This function does not return anything explicitly. + * + ****************************************************************************/ + + .type riscv_vpuconfig, function + +riscv_vpuconfig: + li a0, MSTATUS_VS_INIT + csrs CSR_STATUS, a0 + + fence.i + ret + +/**************************************************************************** + * Name: riscv_savevpu + * + * Description: + * Given the pointer to a register save area (in A0), save the state of the + * floating point registers. + * + * C Function Prototype: + * void riscv_savevpu(uintptr_t *regs, uintptr_t *fregs); + * + * Input Parameters: + * regs - A pointer to the integer registers that contain the status + * fregs - A pointer to the register save area in which to save the + * floating point registers + * + * Returned Value: + * None + * + ****************************************************************************/ + + .type riscv_savevpu, function + +riscv_savevpu: + + REGLOAD t0, REG_INT_CTX(a0) + li t1, MSTATUS_VS + and t2, t0, t1 + li t1, MSTATUS_VS_DIRTY +#ifdef CONFIG_ARCH_LAZYVPU + bne t2, t1, 1f +#else + blt t2, t1, 1f +#endif + li t1, ~MSTATUS_VS + and t0, t0, t1 + li t1, MSTATUS_VS_CLEAN + or t0, t0, t1 + REGSTORE t0, REG_INT_CTX(a0) + + riscv_savevpu a1 + +1: + ret + +/**************************************************************************** + * Name: riscv_restorevpu + * + * Description: + * Given the pointer to a register save area (in A0), restore the state of + * the floating point registers. + * + * C Function Prototype: + * void riscv_restorevpu(uintptr_t *regs, uintptr_t *fregs); + * + * Input Parameters: + * regs - A pointer to the integer registers that contain the status + * fregs - A pointer to the register save area containing the floating + * point registers. + * + * Returned Value: + * This function does not return anything explicitly. However, it is + * called from interrupt level assembly logic that assumes that r0 is + * preserved. + * + ****************************************************************************/ + + .type riscv_restorevpu, function + +riscv_restorevpu: + + REGLOAD t0, REG_INT_CTX(a0) + li t1, MSTATUS_VS + and t2, t0, t1 + li t1, MSTATUS_VS_INIT + ble t2, t1, 1f + + riscv_loadvpu a1 + +1: + ret + +#endif /* CONFIG_ARCH_RV_ISA_V */ diff --git a/arch/risc-v/src/qemu-rv/Kconfig b/arch/risc-v/src/qemu-rv/Kconfig index 00ce77171c691..5d5b8b2dd41ec 100644 --- a/arch/risc-v/src/qemu-rv/Kconfig +++ b/arch/risc-v/src/qemu-rv/Kconfig @@ -35,4 +35,9 @@ config ARCH_CHIP_QEMU_RV_ISA_C default n select ARCH_RV_ISA_C +config ARCH_CHIP_QEMU_RV_ISA_V + bool "Standard Extension for Vector Instructions" + default n + select ARCH_RV_ISA_V + endif diff --git a/boards/risc-v/qemu-rv/rv-virt/configs/smp64/defconfig b/boards/risc-v/qemu-rv/rv-virt/configs/smp64/defconfig index 49a91dd3c4154..13fa9bf746e0b 100644 --- a/boards/risc-v/qemu-rv/rv-virt/configs/smp64/defconfig +++ b/boards/risc-v/qemu-rv/rv-virt/configs/smp64/defconfig @@ -43,6 +43,7 @@ CONFIG_ARCH_CHIP_QEMU_RV=y CONFIG_ARCH_CHIP_QEMU_RV_ISA_A=y CONFIG_ARCH_CHIP_QEMU_RV_ISA_C=y CONFIG_ARCH_CHIP_QEMU_RV_ISA_M=y +CONFIG_ARCH_CHIP_QEMU_RV_ISA_V=y CONFIG_ARCH_INTERRUPTSTACK=2048 CONFIG_ARCH_RISCV=y CONFIG_ARCH_STACKDUMP=y