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 <anchao@lixiang.com>
This commit is contained in:
chao an 2024-04-22 10:10:25 +08:00 committed by Alan Carvalho de Assis
parent c093514cea
commit 28044f7d5a
12 changed files with 388 additions and 0 deletions

View File

@ -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

View File

@ -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__ */

View File

@ -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()

View File

@ -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

View File

@ -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
);
}

View File

@ -30,6 +30,7 @@
#include <nuttx/arch.h>
#include <nuttx/tls.h>
#include <nuttx/kmalloc.h>
#include <arch/irq.h>
#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)

View File

@ -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 ********************************************************/

View File

@ -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
*

View File

@ -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
}

View File

@ -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 <nuttx/config.h>
#include <arch/arch.h>
#include <arch/csr.h>
#include <arch/irq.h>
#include <arch/mode.h>
#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 */

View File

@ -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

View File

@ -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