mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
feat(riscv): implement coprocessors save area and FPU support
This commit mainly targets the ESP32-P4. It adds supports for coprocessors on RISC-V based targets. The coprocessor save area, describing the used coprocessors is stored at the end of the stack of each task (highest address) whereas each coprocessor save area is allocated at the beginning of the task (lowest address). The context of each coprocessor is saved lazily, by the task that want to use it.
This commit is contained in:
parent
b0124b9b9f
commit
a8b1475fe7
@ -259,11 +259,28 @@ static inline void print_memprot_err_details(const void *frame __attribute__((un
|
||||
}
|
||||
#endif
|
||||
|
||||
static void panic_print_register_array(const char* names[], const uint32_t* regs, int size)
|
||||
{
|
||||
const int regs_per_line = 4;
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (i % regs_per_line == 0) {
|
||||
panic_print_str("\r\n");
|
||||
}
|
||||
panic_print_str(names[i]);
|
||||
panic_print_str(": 0x");
|
||||
panic_print_hex(regs[i]);
|
||||
panic_print_str(" ");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void panic_print_registers(const void *f, int core)
|
||||
{
|
||||
uint32_t *regs = (uint32_t *)f;
|
||||
const RvExcFrame *frame = (RvExcFrame *)f;
|
||||
|
||||
// only print ABI name
|
||||
/**
|
||||
* General Purpose context, only print ABI name
|
||||
*/
|
||||
const char *desc[] = {
|
||||
"MEPC ", "RA ", "SP ", "GP ", "TP ", "T0 ", "T1 ", "T2 ",
|
||||
"S0/FP ", "S1 ", "A0 ", "A1 ", "A2 ", "A3 ", "A4 ", "A5 ",
|
||||
@ -273,20 +290,9 @@ void panic_print_registers(const void *f, int core)
|
||||
};
|
||||
|
||||
panic_print_str("Core ");
|
||||
panic_print_dec(((RvExcFrame *)f)->mhartid);
|
||||
panic_print_dec(frame->mhartid);
|
||||
panic_print_str(" register dump:");
|
||||
|
||||
for (int x = 0; x < sizeof(desc) / sizeof(desc[0]); x += 4) {
|
||||
panic_print_str("\r\n");
|
||||
for (int y = 0; y < 4 && x + y < sizeof(desc) / sizeof(desc[0]); y++) {
|
||||
if (desc[x + y][0] != 0) {
|
||||
panic_print_str(desc[x + y]);
|
||||
panic_print_str(": 0x");
|
||||
panic_print_hex(regs[x + y]);
|
||||
panic_print_str(" ");
|
||||
}
|
||||
}
|
||||
}
|
||||
panic_print_register_array(desc, f, DIM(desc));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -191,16 +191,6 @@ void IRAM_ATTR call_start_cpu1(void)
|
||||
);
|
||||
#endif //#ifdef __riscv
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32P4
|
||||
//TODO: IDF-7770
|
||||
//set mstatus.fs=2'b01, floating-point unit in the initialization state
|
||||
asm volatile(
|
||||
"li t0, 0x2000\n"
|
||||
"csrrs t0, mstatus, t0\n"
|
||||
:::"t0"
|
||||
);
|
||||
#endif //#if CONFIG_IDF_TARGET_ESP32P4
|
||||
|
||||
#if SOC_BRANCH_PREDICTOR_SUPPORTED
|
||||
esp_cpu_branch_prediction_enable();
|
||||
#endif //#if SOC_BRANCH_PREDICTOR_SUPPORTED
|
||||
@ -387,16 +377,6 @@ void IRAM_ATTR call_start_cpu0(void)
|
||||
);
|
||||
#endif
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32P4
|
||||
//TODO: IDF-7770
|
||||
//set mstatus.fs=2'b01, floating-point unit in the initialization state
|
||||
asm volatile(
|
||||
"li t0, 0x2000\n"
|
||||
"csrrs t0, mstatus, t0\n"
|
||||
:::"t0"
|
||||
);
|
||||
#endif //#if CONFIG_IDF_TARGET_ESP32P4
|
||||
|
||||
#if SOC_BRANCH_PREDICTOR_SUPPORTED
|
||||
esp_cpu_branch_prediction_enable();
|
||||
#endif
|
||||
|
@ -71,11 +71,19 @@ rtos_enter_end:
|
||||
ret
|
||||
|
||||
/**
|
||||
* Restores the context of the next task.
|
||||
* @brief Restore the stack pointer of the next task to run.
|
||||
*
|
||||
* @param a0 Former mstatus
|
||||
*
|
||||
* @returns New mstatus
|
||||
*/
|
||||
.global rtos_int_exit
|
||||
.type rtos_int_exit, @function
|
||||
rtos_int_exit:
|
||||
/* To speed up this routine and because this current routine is only meant to be called from the interrupt
|
||||
* handler, let's use callee-saved registers instead of stack space. Registers `s3-s11` are not used by
|
||||
* the caller */
|
||||
mv s11, a0
|
||||
/* may skip RTOS aware interrupt since scheduler was not started */
|
||||
lw t0, uxSchedulerRunning
|
||||
beq t0,zero, rtos_exit_end
|
||||
@ -137,4 +145,5 @@ no_switch:
|
||||
#endif /* CONFIG_ESP_SYSTEM_HW_STACK_GUARD */
|
||||
|
||||
rtos_exit_end:
|
||||
mv a0, s11 /* a0 = new mstatus */
|
||||
ret
|
||||
|
@ -59,6 +59,18 @@
|
||||
#include "soc/hp_system_reg.h"
|
||||
#endif
|
||||
|
||||
#if ( SOC_CPU_COPROC_NUM > 0 )
|
||||
|
||||
#include "esp_private/panic_internal.h"
|
||||
|
||||
/* Since `portFORCE_INLINE` is not defined in `portmacro.h`, we must define it here since it is
|
||||
* used by `atomic.h`. */
|
||||
#define portFORCE_INLINE inline
|
||||
#include "freertos/atomic.h"
|
||||
|
||||
#endif // ( SOC_CPU_COPROC_NUM > 0 )
|
||||
|
||||
|
||||
_Static_assert(portBYTE_ALIGNMENT == 16, "portBYTE_ALIGNMENT must be set to 16");
|
||||
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
|
||||
/**
|
||||
@ -82,6 +94,13 @@ volatile UBaseType_t port_uxCriticalNesting[portNUM_PROCESSORS] = {0};
|
||||
volatile UBaseType_t port_uxOldInterruptState[portNUM_PROCESSORS] = {0};
|
||||
volatile UBaseType_t xPortSwitchFlag[portNUM_PROCESSORS] = {0};
|
||||
|
||||
#if ( SOC_CPU_COPROC_NUM > 0 )
|
||||
|
||||
/* Current owner of the coprocessors for each core */
|
||||
StaticTask_t* port_uxCoprocOwner[portNUM_PROCESSORS][SOC_CPU_COPROC_NUM];
|
||||
|
||||
#endif /* SOC_CPU_COPROC_NUM > 0 */
|
||||
|
||||
/*
|
||||
*******************************************************************************
|
||||
* Interrupt stack. The size of the interrupt stack is determined by the config
|
||||
@ -104,6 +123,10 @@ StackType_t *xIsrStackBottom[portNUM_PROCESSORS] = {0};
|
||||
|
||||
BaseType_t xPortStartScheduler(void)
|
||||
{
|
||||
#if ( SOC_CPU_COPROC_NUM > 0 )
|
||||
/* Disable FPU so that the first task to use it will trigger an exception */
|
||||
rv_utils_disable_fpu();
|
||||
#endif
|
||||
/* Initialize all kernel state tracking variables */
|
||||
BaseType_t coreID = xPortGetCoreID();
|
||||
port_uxInterruptNesting[coreID] = 0;
|
||||
@ -238,6 +261,58 @@ static void vPortTaskWrapper(TaskFunction_t pxCode, void *pvParameters)
|
||||
}
|
||||
#endif // CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER
|
||||
|
||||
|
||||
#if ( SOC_CPU_COPROC_NUM > 0 )
|
||||
|
||||
/**
|
||||
* @brief Retrieve or allocate coprocessors save area from the given pxTopOfStack address.
|
||||
*
|
||||
* @param pxTopOfStack End of the stack address. This represents the highest address of a Task's stack.
|
||||
*/
|
||||
FORCE_INLINE_ATTR RvCoprocSaveArea* pxRetrieveCoprocSaveAreaFromStackPointer(UBaseType_t pxTopOfStack)
|
||||
{
|
||||
return (RvCoprocSaveArea*) STACKPTR_ALIGN_DOWN(16, pxTopOfStack - sizeof(RvCoprocSaveArea));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Allocate and initialize the coprocessors save area on the stack
|
||||
*
|
||||
* @param[in] uxStackPointer Current stack pointer address
|
||||
*
|
||||
* @return Stack pointer that points to allocated and initialized the coprocessor save area
|
||||
*/
|
||||
FORCE_INLINE_ATTR UBaseType_t uxInitialiseCoprocSaveArea(UBaseType_t uxStackPointer)
|
||||
{
|
||||
RvCoprocSaveArea* sa = pxRetrieveCoprocSaveAreaFromStackPointer(uxStackPointer);
|
||||
memset(sa, 0, sizeof(RvCoprocSaveArea));
|
||||
return (UBaseType_t) sa;
|
||||
}
|
||||
|
||||
|
||||
static void vPortCleanUpCoprocArea(void *pvTCB)
|
||||
{
|
||||
StaticTask_t* task = (StaticTask_t*) pvTCB;
|
||||
|
||||
/* Get a pointer to the task's coprocessor save area */
|
||||
const UBaseType_t bottomstack = (UBaseType_t) task->pxDummy8;
|
||||
RvCoprocSaveArea* sa = pxRetrieveCoprocSaveAreaFromStackPointer(bottomstack);
|
||||
|
||||
/* If the Task used any coprocessor, check if it is the actual owner of any.
|
||||
* If yes, reset the owner. */
|
||||
if (sa->sa_enable != 0) {
|
||||
/* Get the core the task is pinned on */
|
||||
const BaseType_t coreID = task->xDummyCoreID;
|
||||
|
||||
for (int i = 0; i < SOC_CPU_COPROC_NUM; i++) {
|
||||
StaticTask_t** owner = &port_uxCoprocOwner[coreID][i];
|
||||
/* If the owner is `task`, replace it with NULL atomically */
|
||||
Atomic_CompareAndSwapPointers_p32((void**) owner, NULL, task);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* SOC_CPU_COPROC_NUM > 0 */
|
||||
|
||||
|
||||
/**
|
||||
* @brief Initialize the task's starting interrupt stack frame
|
||||
*
|
||||
@ -304,12 +379,38 @@ StackType_t *pxPortInitialiseStack(StackType_t *pxTopOfStack, TaskFunction_t pxC
|
||||
|
||||
- All stack areas are aligned to 16 byte boundary
|
||||
- We use UBaseType_t for all of stack area initialization functions for more convenient pointer arithmetic
|
||||
|
||||
In the case of targets that have coprocessors, the stack is presented as follows:
|
||||
HIGH ADDRESS
|
||||
|---------------------------| <- pxTopOfStack on entry
|
||||
| Coproc. Save Area | <- RvCoprocSaveArea
|
||||
| ------------------------- |
|
||||
| TLS Variables |
|
||||
| ------------------------- | <- Start of useable stack
|
||||
| Starting stack frame |
|
||||
| ------------------------- | <- pxTopOfStack on return (which is the tasks current SP)
|
||||
| | |
|
||||
| | |
|
||||
| V |
|
||||
|---------------------------|
|
||||
| Coproc. m Saved Context | <- Coprocessor context save area after allocation
|
||||
|---------------------------|
|
||||
| Coproc. n Saved Context | <- Another coprocessor context save area after allocation
|
||||
----------------------------- <- Bottom of stack
|
||||
LOW ADDRESS
|
||||
|
||||
Where m != n, n < SOC_CPU_COPROC_NUM, m < SOC_CPU_COPROC_NUM
|
||||
|
||||
*/
|
||||
|
||||
UBaseType_t uxStackPointer = (UBaseType_t)pxTopOfStack;
|
||||
configASSERT((uxStackPointer & portBYTE_ALIGNMENT_MASK) == 0);
|
||||
|
||||
// IDF-7770: Support FPU context save area for P4
|
||||
#if ( SOC_CPU_COPROC_NUM > 0 )
|
||||
// Initialize the coprocessors save area
|
||||
uxStackPointer = uxInitialiseCoprocSaveArea(uxStackPointer);
|
||||
configASSERT((uxStackPointer & portBYTE_ALIGNMENT_MASK) == 0);
|
||||
#endif // SOC_CPU_COPROC_NUM > 0
|
||||
|
||||
// Initialize GCC TLS area
|
||||
uint32_t threadptr_reg_init;
|
||||
@ -647,8 +748,104 @@ void vPortTCBPreDeleteHook( void *pxTCB )
|
||||
/* Call TLS pointers deletion callbacks */
|
||||
vPortTLSPointersDelCb( pxTCB );
|
||||
#endif /* CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS */
|
||||
|
||||
#if ( SOC_CPU_COPROC_NUM > 0 )
|
||||
/* Cleanup coproc save area */
|
||||
vPortCleanUpCoprocArea( pxTCB );
|
||||
#endif /* SOC_CPU_COPROC_NUM > 0 */
|
||||
}
|
||||
|
||||
|
||||
#if ( SOC_CPU_COPROC_NUM > 0 )
|
||||
|
||||
// ----------------------- Coprocessors --------------------------
|
||||
|
||||
/**
|
||||
* @brief Pin the given task to the given core
|
||||
*
|
||||
* This function is called when a task uses a coprocessor. Since the coprocessors registers
|
||||
* are saved lazily, as soon as a task starts using one, it must always be scheduled on the core
|
||||
* it is currently executing on.
|
||||
*/
|
||||
void vPortTaskPinToCore(StaticTask_t* task, int coreid)
|
||||
{
|
||||
task->xDummyCoreID = coreid;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get coprocessor save area out of the given task. If the coprocessor area is not created,
|
||||
* it shall be allocated.
|
||||
*/
|
||||
RvCoprocSaveArea* pxPortGetCoprocArea(StaticTask_t* task, int coproc)
|
||||
{
|
||||
const UBaseType_t bottomstack = (UBaseType_t) task->pxDummy8;
|
||||
RvCoprocSaveArea* sa = pxRetrieveCoprocSaveAreaFromStackPointer(bottomstack);
|
||||
/* Check if the allocator is NULL. Since we don't have a way to get the end of the stack
|
||||
* during its initialization, we have to do this here */
|
||||
if (sa->sa_allocator == 0) {
|
||||
sa->sa_allocator = (UBaseType_t) task->pxDummy6;
|
||||
}
|
||||
|
||||
/* Check if coprocessor area is allocated */
|
||||
if (sa->sa_coprocs[coproc] == NULL) {
|
||||
const uint32_t coproc_sa_sizes[] = {
|
||||
RV_COPROC0_SIZE, RV_COPROC1_SIZE
|
||||
};
|
||||
/* Allocate the save area at end of the allocator */
|
||||
UBaseType_t allocated = sa->sa_allocator + coproc_sa_sizes[coproc];
|
||||
sa->sa_coprocs[coproc] = (void*) allocated;
|
||||
/* Update the allocator address for next use */
|
||||
sa->sa_allocator = allocated;
|
||||
}
|
||||
return sa;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Update given coprocessor owner and get the address of former owner's save area.
|
||||
*
|
||||
* This function is called when the current running task has poked a coprocessor's register which
|
||||
* was used by a previous task. We have to save the coprocessor context (registers) inside the
|
||||
* current owner's save area and change the ownership. The coprocessor will be marked as used in
|
||||
* the new owner's coprocessor save area.
|
||||
*
|
||||
* @param coreid Current core
|
||||
* @param coproc Coprocessor to save context of
|
||||
*
|
||||
* @returns Coprocessor former owner's save area
|
||||
*/
|
||||
RvCoprocSaveArea* pxPortUpdateCoprocOwner(int coreid, int coproc, StaticTask_t* owner)
|
||||
{
|
||||
RvCoprocSaveArea* sa = NULL;
|
||||
/* Address of coprocessor owner */
|
||||
StaticTask_t** owner_addr = &port_uxCoprocOwner[ coreid ][ coproc ];
|
||||
/* Atomically exchange former owner with the new one */
|
||||
StaticTask_t* former = Atomic_SwapPointers_p32((void**) owner_addr, owner);
|
||||
/* Get the save area of former owner */
|
||||
if (former != NULL) {
|
||||
sa = pxPortGetCoprocArea(former, coproc);
|
||||
}
|
||||
return sa;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Aborts execution when a coprocessor was used in an ISR context
|
||||
*/
|
||||
void vPortCoprocUsedInISR(void* frame)
|
||||
{
|
||||
extern void xt_unhandled_exception(void*);
|
||||
/* Since this function is called from an exception handler, the interrupts are disabled,
|
||||
* as such, it is not possible to trigger another exception as would `abort` do.
|
||||
* Simulate an abort without actually triggering an exception. */
|
||||
g_panic_abort = true;
|
||||
g_panic_abort_details = (char *) "ERROR: Coprocessors must not be used in ISRs!\n";
|
||||
xt_unhandled_exception(frame);
|
||||
}
|
||||
|
||||
#endif /* SOC_CPU_COPROC_NUM > 0 */
|
||||
|
||||
/* ---------------------------------------------- Misc Implementations -------------------------------------------------
|
||||
*
|
||||
* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
@ -7,8 +7,9 @@
|
||||
#include "portmacro.h"
|
||||
#include "freertos/FreeRTOSConfig.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "riscv/rvruntime-frames.h"
|
||||
|
||||
.extern pxCurrentTCBs
|
||||
.extern pxCurrentTCBs
|
||||
|
||||
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
|
||||
#include "esp_private/hw_stack_guard.h"
|
||||
@ -22,8 +23,6 @@
|
||||
.global xPortSwitchFlag
|
||||
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
|
||||
.global xIsrStackBottom
|
||||
.global port_offset_pxStack
|
||||
.global port_offset_pxEndOfStack
|
||||
.global esp_hw_stack_guard_monitor_stop
|
||||
.global esp_hw_stack_guard_monitor_start
|
||||
.global esp_hw_stack_guard_set_bounds
|
||||
@ -31,6 +30,210 @@
|
||||
|
||||
.section .text
|
||||
|
||||
|
||||
#if SOC_CPU_COPROC_NUM > 0
|
||||
|
||||
#if SOC_CPU_HAS_FPU
|
||||
|
||||
/* Bit to set in mstatus to enable the FPU */
|
||||
#define CSR_MSTATUS_FPU_ENABLE (1 << 13)
|
||||
/* Bit to clear in mstatus to disable the FPU */
|
||||
#define CSR_MSTATUS_FPU_DISABLE (3 << 13)
|
||||
|
||||
.macro save_fpu_regs frame=sp
|
||||
fsw ft0, RV_FPU_FT0(\frame)
|
||||
fsw ft1, RV_FPU_FT1(\frame)
|
||||
fsw ft2, RV_FPU_FT2(\frame)
|
||||
fsw ft3, RV_FPU_FT3(\frame)
|
||||
fsw ft4, RV_FPU_FT4(\frame)
|
||||
fsw ft5, RV_FPU_FT5(\frame)
|
||||
fsw ft6, RV_FPU_FT6(\frame)
|
||||
fsw ft7, RV_FPU_FT7(\frame)
|
||||
fsw fs0, RV_FPU_FS0(\frame)
|
||||
fsw fs1, RV_FPU_FS1(\frame)
|
||||
fsw fa0, RV_FPU_FA0(\frame)
|
||||
fsw fa1, RV_FPU_FA1(\frame)
|
||||
fsw fa2, RV_FPU_FA2(\frame)
|
||||
fsw fa3, RV_FPU_FA3(\frame)
|
||||
fsw fa4, RV_FPU_FA4(\frame)
|
||||
fsw fa5, RV_FPU_FA5(\frame)
|
||||
fsw fa6, RV_FPU_FA6(\frame)
|
||||
fsw fa7, RV_FPU_FA7(\frame)
|
||||
fsw fs2, RV_FPU_FS2(\frame)
|
||||
fsw fs3, RV_FPU_FS3(\frame)
|
||||
fsw fs4, RV_FPU_FS4(\frame)
|
||||
fsw fs5, RV_FPU_FS5(\frame)
|
||||
fsw fs6, RV_FPU_FS6(\frame)
|
||||
fsw fs7, RV_FPU_FS7(\frame)
|
||||
fsw fs8, RV_FPU_FS8(\frame)
|
||||
fsw fs9, RV_FPU_FS9(\frame)
|
||||
fsw fs10, RV_FPU_FS10(\frame)
|
||||
fsw fs11, RV_FPU_FS11(\frame)
|
||||
fsw ft8, RV_FPU_FT8 (\frame)
|
||||
fsw ft9, RV_FPU_FT9 (\frame)
|
||||
fsw ft10, RV_FPU_FT10(\frame)
|
||||
fsw ft11, RV_FPU_FT11(\frame)
|
||||
.endm
|
||||
|
||||
.macro restore_fpu_regs frame=sp
|
||||
flw ft0, RV_FPU_FT0(\frame)
|
||||
flw ft1, RV_FPU_FT1(\frame)
|
||||
flw ft2, RV_FPU_FT2(\frame)
|
||||
flw ft3, RV_FPU_FT3(\frame)
|
||||
flw ft4, RV_FPU_FT4(\frame)
|
||||
flw ft5, RV_FPU_FT5(\frame)
|
||||
flw ft6, RV_FPU_FT6(\frame)
|
||||
flw ft7, RV_FPU_FT7(\frame)
|
||||
flw fs0, RV_FPU_FS0(\frame)
|
||||
flw fs1, RV_FPU_FS1(\frame)
|
||||
flw fa0, RV_FPU_FA0(\frame)
|
||||
flw fa1, RV_FPU_FA1(\frame)
|
||||
flw fa2, RV_FPU_FA2(\frame)
|
||||
flw fa3, RV_FPU_FA3(\frame)
|
||||
flw fa4, RV_FPU_FA4(\frame)
|
||||
flw fa5, RV_FPU_FA5(\frame)
|
||||
flw fa6, RV_FPU_FA6(\frame)
|
||||
flw fa7, RV_FPU_FA7(\frame)
|
||||
flw fs2, RV_FPU_FS2(\frame)
|
||||
flw fs3, RV_FPU_FS3(\frame)
|
||||
flw fs4, RV_FPU_FS4(\frame)
|
||||
flw fs5, RV_FPU_FS5(\frame)
|
||||
flw fs6, RV_FPU_FS6(\frame)
|
||||
flw fs7, RV_FPU_FS7(\frame)
|
||||
flw fs8, RV_FPU_FS8(\frame)
|
||||
flw fs9, RV_FPU_FS9(\frame)
|
||||
flw fs10, RV_FPU_FS10(\frame)
|
||||
flw fs11, RV_FPU_FS11(\frame)
|
||||
flw ft8, RV_FPU_FT8(\frame)
|
||||
flw ft9, RV_FPU_FT9(\frame)
|
||||
flw ft10, RV_FPU_FT10(\frame)
|
||||
flw ft11, RV_FPU_FT11(\frame)
|
||||
.endm
|
||||
|
||||
|
||||
.macro fpu_read_dirty_bit reg
|
||||
csrr \reg, mstatus
|
||||
srli \reg, \reg, 13
|
||||
andi \reg, \reg, 1
|
||||
.endm
|
||||
|
||||
|
||||
.macro fpu_clear_dirty_bit reg
|
||||
li \reg, 1 << 13
|
||||
csrc mstatus, \reg
|
||||
.endm
|
||||
|
||||
|
||||
.macro fpu_enable reg
|
||||
li \reg, CSR_MSTATUS_FPU_ENABLE
|
||||
csrs mstatus, \reg
|
||||
.endm
|
||||
|
||||
|
||||
.macro fpu_disable reg
|
||||
li \reg, CSR_MSTATUS_FPU_DISABLE
|
||||
csrc mstatus, \reg
|
||||
.endm
|
||||
|
||||
.global vPortTaskPinToCore
|
||||
.global vPortCoprocUsedInISR
|
||||
.global pxPortUpdateCoprocOwner
|
||||
|
||||
/**
|
||||
* @brief Save the current FPU context in the FPU owner's save area
|
||||
*
|
||||
* @param sp Interuptee's RvExcFrame address
|
||||
*
|
||||
* Note: Since this routine is ONLY meant to be called from _panic_handler routine,
|
||||
* it is possible to alter `s0-s11` registers
|
||||
*/
|
||||
.global rtos_save_fpu_coproc
|
||||
.type rtos_save_fpu_coproc, @function
|
||||
rtos_save_fpu_coproc:
|
||||
/* If we are in an interrupt context, we have to abort. We don't allow using the FPU from ISR */
|
||||
#if ( configNUM_CORES > 1 )
|
||||
csrr a2, mhartid /* a2 = coreID */
|
||||
slli a2, a2, 2 /* a2 = coreID * 4 */
|
||||
la a1, port_uxInterruptNesting /* a1 = &port_uxInterruptNesting */
|
||||
add a1, a1, a2 /* a1 = &port_uxInterruptNesting[coreID] */
|
||||
lw a1, 0(a1) /* a1 = port_uxInterruptNesting[coreID] */
|
||||
#else /* ( configNUM_CORES <= 1 ) */
|
||||
lw a1, (port_uxInterruptNesting) /* a1 = port_uxInterruptNesting */
|
||||
#endif /* ( configNUM_CORES > 1 ) */
|
||||
/* SP still contains the RvExcFrame address */
|
||||
mv a0, sp
|
||||
bnez a1, vPortCoprocUsedInISR
|
||||
/* Enable the FPU needed by the current task */
|
||||
fpu_enable a1
|
||||
mv s0, ra
|
||||
call rtos_current_tcb
|
||||
/* If the current TCB is NULL, the FPU is used during initialization, even before
|
||||
* the scheduler started. Consider this a valid usage, the FPU will be disabled
|
||||
* as soon as the scheduler is started anyway*/
|
||||
beqz a0, rtos_save_fpu_coproc_norestore
|
||||
mv s1, a0 /* s1 = pxCurrentTCBs */
|
||||
/* Prepare parameters of pxPortUpdateCoprocOwner */
|
||||
mv a2, a0
|
||||
li a1, FPU_COPROC_IDX
|
||||
csrr a0, mhartid
|
||||
call pxPortUpdateCoprocOwner
|
||||
/* If the save area is NULL, no need to save context */
|
||||
beqz a0, rtos_save_fpu_coproc_nosave
|
||||
/* Save the FPU context in the structure */
|
||||
lw a0, RV_COPROC_SA+FPU_COPROC_IDX*4(a0) /* a0 = RvCoprocSaveArea->sa_coprocs[FPU_COPROC_IDX] */
|
||||
save_fpu_regs a0
|
||||
csrr a1, fcsr
|
||||
sw a1, RV_FPU_FCSR(a0)
|
||||
rtos_save_fpu_coproc_nosave:
|
||||
/* Pin current task to current core */
|
||||
mv a0, s1
|
||||
csrr a1, mhartid
|
||||
call vPortTaskPinToCore
|
||||
/* Check if we have to restore a previous FPU context from the current TCB */
|
||||
mv a0, s1
|
||||
call pxPortGetCoprocArea
|
||||
/* Get the enable flags from the coprocessor save area */
|
||||
lw a1, RV_COPROC_ENABLE(a0)
|
||||
/* To avoid having branches below, set the FPU enable flag now */
|
||||
ori a2, a1, 1 << FPU_COPROC_IDX
|
||||
sw a2, RV_COPROC_ENABLE(a0)
|
||||
/* Check if the former FPU enable bit was set */
|
||||
andi a2, a1, 1 << FPU_COPROC_IDX
|
||||
beqz a2, rtos_save_fpu_coproc_norestore
|
||||
/* FPU enable bit was set, restore the FPU context */
|
||||
lw a0, RV_COPROC_SA+FPU_COPROC_IDX*4(a0) /* a0 = RvCoprocSaveArea->sa_coprocs[FPU_COPROC_IDX] */
|
||||
restore_fpu_regs a0
|
||||
lw a1, RV_FPU_FCSR(a0)
|
||||
csrw fcsr, a1
|
||||
rtos_save_fpu_coproc_norestore:
|
||||
/* Return from routine via s0, instead of ra */
|
||||
jr s0
|
||||
.size rtos_save_fpu_coproc, .-rtos_save_fpu_coproc
|
||||
|
||||
#endif /* SOC_CPU_HAS_FPU */
|
||||
|
||||
#endif /* SOC_CPU_COPROC_NUM > 0 */
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get current TCB on current core
|
||||
*/
|
||||
.type rtos_current_tcb, @function
|
||||
rtos_current_tcb:
|
||||
#if ( configNUM_CORES > 1 )
|
||||
csrr a1, mhartid
|
||||
slli a1, a1, 2
|
||||
la a0, pxCurrentTCBs /* a0 = &pxCurrentTCBs */
|
||||
add a0, a0, a1 /* a0 = &pxCurrentTCBs[coreID] */
|
||||
lw a0, 0(a0) /* a0 = pxCurrentTCBs[coreID] */
|
||||
#else
|
||||
/* Recover the stack of next task */
|
||||
lw a0, pxCurrentTCBs
|
||||
#endif /* ( configNUM_CORES > 1 ) */
|
||||
ret
|
||||
.size, .-rtos_current_tcb
|
||||
|
||||
|
||||
/**
|
||||
* This function makes the RTOS aware about an ISR entering. It takes the
|
||||
* current task stack pointer and places it into the pxCurrentTCBs.
|
||||
@ -65,6 +268,13 @@ rtos_int_enter:
|
||||
/* If we reached here from another low-priority ISR, i.e, port_uxInterruptNesting[coreID] > 0, then skip stack pushing to TCB */
|
||||
bnez a1, rtos_int_enter_end /* if (port_uxInterruptNesting[coreID] > 0) jump to rtos_int_enter_end */
|
||||
|
||||
#if SOC_CPU_COPROC_NUM > 0
|
||||
/* Disable the FPU to forbid the ISR from using it. We don't need to re-enable it manually since the caller
|
||||
* will restore `mstatus` before returning from interrupt. */
|
||||
fpu_disable a0
|
||||
#endif /* SOC_CPU_COPROC_NUM > 0 */
|
||||
|
||||
|
||||
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
|
||||
/* esp_hw_stack_guard_monitor_stop(); pass the scratch registers */
|
||||
ESP_HW_STACK_GUARD_MONITOR_STOP_CUR_CORE a0 a1
|
||||
@ -106,11 +316,19 @@ rtos_int_enter_end:
|
||||
ret
|
||||
|
||||
/**
|
||||
* Restore the stack pointer of the next task to run.
|
||||
* @brief Restore the stack pointer of the next task to run.
|
||||
*
|
||||
* @param a0 Former mstatus
|
||||
*
|
||||
* @returns New mstatus (potentially with coprocessors disabled)
|
||||
*/
|
||||
.global rtos_int_exit
|
||||
.type rtos_int_exit, @function
|
||||
rtos_int_exit:
|
||||
/* To speed up this routine and because this current routine is only meant to be called from the interrupt
|
||||
* handler, let's use callee-saved registers instead of stack space. Registers `s3-s11` are not used by
|
||||
* the caller */
|
||||
mv s11, a0
|
||||
#if ( configNUM_CORES > 1 )
|
||||
csrr a1, mhartid /* a1 = coreID */
|
||||
slli a1, a1, 2 /* a1 = a1 * 4 */
|
||||
@ -120,21 +338,21 @@ rtos_int_exit:
|
||||
#else
|
||||
lw a0, port_xSchedulerRunning /* a0 = port_xSchedulerRunning */
|
||||
#endif /* ( configNUM_CORES > 1 ) */
|
||||
beqz a0, rtos_int_exit_end /* if (port_uxSchewdulerRunning == 0) jump to rtos_int_exit_end */
|
||||
beqz a0, rtos_int_exit_end /* if (port_uxSchedulerRunning == 0) jump to rtos_int_exit_end */
|
||||
|
||||
/* Update nesting interrupts counter */
|
||||
la a0, port_uxInterruptNesting /* a0 = &port_uxInterruptNesting */
|
||||
la a2, port_uxInterruptNesting /* a2 = &port_uxInterruptNesting */
|
||||
#if ( configNUM_CORES > 1 )
|
||||
add a0, a0, a1 /* a0 = &port_uxInterruptNesting[coreID] // a1 already contains coreID * 4 */
|
||||
add a2, a2, a1 /* a2 = &port_uxInterruptNesting[coreID] // a1 already contains coreID * 4 */
|
||||
#endif /* ( configNUM_CORES > 1 ) */
|
||||
lw a2, 0(a0) /* a2 = port_uxInterruptNesting[coreID] */
|
||||
lw a0, 0(a2) /* a0 = port_uxInterruptNesting[coreID] */
|
||||
|
||||
/* Already zero, protect against underflow */
|
||||
beqz a2, isr_skip_decrement /* if (port_uxInterruptNesting[coreID] == 0) jump to isr_skip_decrement */
|
||||
addi a2, a2, -1 /* a2 = a2 - 1 */
|
||||
sw a2, 0(a0) /* port_uxInterruptNesting[coreID] = a2 */
|
||||
beqz a0, isr_skip_decrement /* if (port_uxInterruptNesting[coreID] == 0) jump to isr_skip_decrement */
|
||||
addi a0, a0, -1 /* a0 = a0 - 1 */
|
||||
sw a0, 0(a2) /* port_uxInterruptNesting[coreID] = a0 */
|
||||
/* May still have interrupts pending, skip section below and exit */
|
||||
bnez a2, rtos_int_exit_end
|
||||
bnez a0, rtos_int_exit_end
|
||||
|
||||
isr_skip_decrement:
|
||||
/* If the CPU reached this label, a2 (uxInterruptNesting) is 0 for sure */
|
||||
@ -147,11 +365,27 @@ isr_skip_decrement:
|
||||
lw a2, 0(a0) /* a2 = xPortSwitchFlag[coreID] */
|
||||
beqz a2, no_switch /* if (xPortSwitchFlag[coreID] == 0) jump to no_switch */
|
||||
|
||||
/* Preserve return address and schedule next task. To speed up the process, instead of allocating stack
|
||||
* space, let's use a callee-saved register: s0. Since the caller is not using it, let's use it. */
|
||||
mv s0, ra
|
||||
/* Preserve return address and schedule next task. To speed up the process, and because this current routine
|
||||
* is only meant to be called from the interrupt handle, let's save some speed and space by using callee-saved
|
||||
* registers instead of stack space. Registers `s3-s11` are not used by the caller */
|
||||
mv s10, ra
|
||||
#if ( SOC_CPU_COPROC_NUM > 0 )
|
||||
/* In the cases where the newly scheduled task is different from the previously running one,
|
||||
* we have to disable the coprocessor(s) to let them trigger an exception on first use.
|
||||
* Else, if the same task is scheduled, do not change the coprocessor(s) state. */
|
||||
call rtos_current_tcb
|
||||
mv s9, a0
|
||||
call vTaskSwitchContext
|
||||
mv ra, s0
|
||||
call rtos_current_tcb
|
||||
beq a0, s9, rtos_int_exit_no_change
|
||||
/* Disable the coprocessors in s11 register (former mstatus) */
|
||||
li a0, ~CSR_MSTATUS_FPU_DISABLE
|
||||
and s11, s11, a0
|
||||
rtos_int_exit_no_change:
|
||||
#else /* ( SOC_CPU_COPROC_NUM == 0 ) */
|
||||
call vTaskSwitchContext
|
||||
#endif /* ( SOC_CPU_COPROC_NUM > 0 ) */
|
||||
mv ra, s10
|
||||
|
||||
/* Clears the switch pending flag */
|
||||
la a0, xPortSwitchFlag /* a0 = &xPortSwitchFlag */
|
||||
@ -198,4 +432,5 @@ no_switch:
|
||||
#endif /* CONFIG_ESP_SYSTEM_HW_STACK_GUARD */
|
||||
|
||||
rtos_int_exit_end:
|
||||
mv a0, s11 /* a0 = new mstatus */
|
||||
ret
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -22,26 +22,31 @@ static const char *TAG = "flash_hal";
|
||||
|
||||
static uint32_t get_flash_clock_divider(const spi_flash_hal_config_t *cfg)
|
||||
{
|
||||
int clk_source = cfg->clock_src_freq;
|
||||
const int clk_source = cfg->clock_src_freq;
|
||||
const int clk_freq_mhz = cfg->freq_mhz;
|
||||
// On ESP32, ESP32-S2, ESP32-C3, we allow specific frequency 26.666MHz
|
||||
// If user passes freq_mhz like 26 or 27, it's allowed to use integer divider 3.
|
||||
// However on other chips or on other frequency, we only allow user pass frequency which
|
||||
// can be integer divided. If no, the following strategy is round up the division and
|
||||
// round down flash frequency to keep it safe.
|
||||
int best_div = 0;
|
||||
if (clk_source < cfg->freq_mhz) {
|
||||
HAL_LOGE(TAG, "Target frequency %dMHz higher than supported.", cfg->freq_mhz);
|
||||
if (clk_source < clk_freq_mhz) {
|
||||
HAL_LOGE(TAG, "Target frequency %dMHz higher than supported.", clk_freq_mhz);
|
||||
abort();
|
||||
}
|
||||
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32C3
|
||||
if (cfg->freq_mhz == 26 || cfg->freq_mhz == 27) {
|
||||
if (clk_freq_mhz == 26 || clk_freq_mhz == 27) {
|
||||
best_div = 3;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
best_div = (int)ceil((double)clk_source / (double)cfg->freq_mhz);
|
||||
if ((cfg->clock_src_freq % cfg->freq_mhz) != 0) {
|
||||
HAL_LOGW(TAG, "Flash clock frequency round down to %d", (int)floor((double)clk_source / (double)best_div));
|
||||
/* Do not use float/double as the FPU may not have been initialized yet on startup.
|
||||
* The values are in MHz, so for sure we won't have an overflow by adding them. */
|
||||
best_div = (clk_source + clk_freq_mhz - 1) / clk_freq_mhz;
|
||||
/* Perform a division that returns both quotient and remainder */
|
||||
const div_t res = div(clk_source, clk_freq_mhz);
|
||||
if (res.rem != 0) {
|
||||
HAL_LOGW(TAG, "Flash clock frequency round down to %d", res.quot);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,21 @@ extern "C" {
|
||||
#define CSR_PCMR_MACHINE 0x7e1
|
||||
#define CSR_PCCR_MACHINE 0x7e2
|
||||
|
||||
#if SOC_CPU_HAS_FPU
|
||||
|
||||
/* FPU bits in mstatus start at bit 13 */
|
||||
#define CSR_MSTATUS_FPU_SHIFT 13
|
||||
/* FPU registers are clean if bits are 0b10 */
|
||||
#define CSR_MSTATUS_FPU_CLEAN_STATE 2
|
||||
/* FPU status in mstatus are represented with two bits */
|
||||
#define CSR_MSTATUS_FPU_MASK 3
|
||||
/* FPU is enabled when writing 1 to FPU bits */
|
||||
#define CSR_MSTATUS_FPU_ENA BIT(13)
|
||||
/* Set FPU registers state to clean (after being dirty) */
|
||||
#define CSR_MSTATUS_FPU_CLEAR BIT(13)
|
||||
|
||||
#endif /* SOC_CPU_HAS_FPU */
|
||||
|
||||
/* SW defined level which the interrupt module will mask interrupt with priority less than threshold during critical sections
|
||||
and spinlocks */
|
||||
#define RVHAL_EXCM_LEVEL 4
|
||||
@ -222,6 +237,37 @@ FORCE_INLINE_ATTR void rv_utils_intr_global_disable(void)
|
||||
RV_CLEAR_CSR(mstatus, MSTATUS_MIE);
|
||||
}
|
||||
|
||||
|
||||
#if SOC_CPU_HAS_FPU
|
||||
|
||||
/* ------------------------------------------------- FPU Related ----------------------------------------------------
|
||||
*
|
||||
* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
FORCE_INLINE_ATTR bool rv_utils_enable_fpu(void)
|
||||
{
|
||||
/* Set mstatus[14:13] to 0b01 to start the floating-point unit initialization */
|
||||
RV_SET_CSR(mstatus, CSR_MSTATUS_FPU_ENA);
|
||||
/* On the ESP32-P4, the FPU can be used directly after setting `mstatus` bit 13.
|
||||
* Since the interrupt handler expects the FPU states to be either 0b10 or 0b11,
|
||||
* let's write the FPU CSR and clear the dirty bit afterwards. */
|
||||
RV_WRITE_CSR(fcsr, 1);
|
||||
RV_CLEAR_CSR(mstatus, CSR_MSTATUS_FPU_CLEAR);
|
||||
const uint32_t mstatus = RV_READ_CSR(mstatus);
|
||||
/* Make sure the FPU state is 0b10 (clean registers) */
|
||||
return ((mstatus >> CSR_MSTATUS_FPU_SHIFT) & CSR_MSTATUS_FPU_MASK) == CSR_MSTATUS_FPU_CLEAN_STATE;
|
||||
}
|
||||
|
||||
|
||||
FORCE_INLINE_ATTR void rv_utils_disable_fpu(void)
|
||||
{
|
||||
/* Clear mstatus[14:13] bits to disable the floating-point unit */
|
||||
RV_CLEAR_CSR(mstatus, CSR_MSTATUS_FPU_MASK << CSR_MSTATUS_FPU_SHIFT);
|
||||
}
|
||||
|
||||
#endif /* SOC_CPU_HAS_FPU */
|
||||
|
||||
|
||||
/* -------------------------------------------------- Memory Ports -----------------------------------------------------
|
||||
*
|
||||
* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -7,6 +7,8 @@
|
||||
#ifndef __RVRUNTIME_FRAMES_H__
|
||||
#define __RVRUNTIME_FRAMES_H__
|
||||
|
||||
#include "soc/soc_caps.h"
|
||||
|
||||
/* Align a value up to nearest n-byte boundary, where n is a power of 2. */
|
||||
#define ALIGNUP(n, val) (((val) + (n) - 1) & -(n))
|
||||
|
||||
@ -82,6 +84,80 @@ STRUCT_FIELD (long, 4, RV_STK_MTVAL, mtval) /* Machine Trap Value */
|
||||
STRUCT_FIELD (long, 4, RV_STK_MHARTID, mhartid) /* Hardware Thread ID in machine mode */
|
||||
STRUCT_END(RvExcFrame)
|
||||
|
||||
#if SOC_CPU_COPROC_NUM > 0
|
||||
|
||||
#if SOC_CPU_HAS_FPU
|
||||
/**
|
||||
* @brief Floating-Point Unit save area
|
||||
*/
|
||||
STRUCT_BEGIN
|
||||
STRUCT_FIELD (long, 4, RV_FPU_FT0, ft0) /* ft0-ft7: Floating Point temporaries */
|
||||
STRUCT_FIELD (long, 4, RV_FPU_FT1, ft1)
|
||||
STRUCT_FIELD (long, 4, RV_FPU_FT2, ft2)
|
||||
STRUCT_FIELD (long, 4, RV_FPU_FT3, ft3)
|
||||
STRUCT_FIELD (long, 4, RV_FPU_FT4, ft4)
|
||||
STRUCT_FIELD (long, 4, RV_FPU_FT5, ft5)
|
||||
STRUCT_FIELD (long, 4, RV_FPU_FT6, ft6)
|
||||
STRUCT_FIELD (long, 4, RV_FPU_FT7, ft7)
|
||||
STRUCT_FIELD (long, 4, RV_FPU_FS0, fs0) /* fs0-fs1: Floating Point saved registers */
|
||||
STRUCT_FIELD (long, 4, RV_FPU_FS1, fs1)
|
||||
STRUCT_FIELD (long, 4, RV_FPU_FA0, fa0) /* fa0-fa1: Floating Point arguments/return values */
|
||||
STRUCT_FIELD (long, 4, RV_FPU_FA1, fa1)
|
||||
STRUCT_FIELD (long, 4, RV_FPU_FA2, fa2) /* fa2-fa7: Floating Point arguments */
|
||||
STRUCT_FIELD (long, 4, RV_FPU_FA3, fa3)
|
||||
STRUCT_FIELD (long, 4, RV_FPU_FA4, fa4)
|
||||
STRUCT_FIELD (long, 4, RV_FPU_FA5, fa5)
|
||||
STRUCT_FIELD (long, 4, RV_FPU_FA6, fa6)
|
||||
STRUCT_FIELD (long, 4, RV_FPU_FA7, fa7)
|
||||
STRUCT_FIELD (long, 4, RV_FPU_FS2, fs2) /* fs2-fs11: Floating Point saved registers */
|
||||
STRUCT_FIELD (long, 4, RV_FPU_FS3, fs3)
|
||||
STRUCT_FIELD (long, 4, RV_FPU_FS4, fs4)
|
||||
STRUCT_FIELD (long, 4, RV_FPU_FS5, fs5)
|
||||
STRUCT_FIELD (long, 4, RV_FPU_FS6, fs6)
|
||||
STRUCT_FIELD (long, 4, RV_FPU_FS7, fs7)
|
||||
STRUCT_FIELD (long, 4, RV_FPU_FS8, fs8)
|
||||
STRUCT_FIELD (long, 4, RV_FPU_FS9, fs9)
|
||||
STRUCT_FIELD (long, 4, RV_FPU_FS10, fs10)
|
||||
STRUCT_FIELD (long, 4, RV_FPU_FS11, fs11)
|
||||
STRUCT_FIELD (long, 4, RV_FPU_FT8, ft8) /* ft8-ft11: Floating Point temporary registers */
|
||||
STRUCT_FIELD (long, 4, RV_FPU_FT9, ft9)
|
||||
STRUCT_FIELD (long, 4, RV_FPU_FT10, ft10)
|
||||
STRUCT_FIELD (long, 4, RV_FPU_FT11, ft11)
|
||||
STRUCT_FIELD (long, 4, RV_FPU_FCSR, fcsr) /* fcsr special register */
|
||||
STRUCT_END(RvFPUSaveArea)
|
||||
|
||||
/* Floating-Point Unit coprocessor is now considered coprocessor 0 */
|
||||
#define FPU_COPROC_IDX 0
|
||||
/* PIE/AIA coprocessor is coprocessor 1 */
|
||||
#define PIE_COPROC_IDX 1
|
||||
|
||||
/* Define the size of each coprocessor save area */
|
||||
#if defined(_ASMLANGUAGE) || defined(__ASSEMBLER__)
|
||||
#define RV_COPROC0_SIZE RvFPUSaveAreaSize
|
||||
#define RV_COPROC1_SIZE 0 // PIE/AIA coprocessor area
|
||||
#else
|
||||
#define RV_COPROC0_SIZE sizeof(RvFPUSaveArea)
|
||||
#define RV_COPROC1_SIZE 0 // PIE/AIA coprocessor area
|
||||
#endif /* defined(_ASMLANGUAGE) || defined(__ASSEMBLER__) */
|
||||
|
||||
#endif /* SOC_CPU_HAS_FPU */
|
||||
|
||||
/**
|
||||
* @brief Coprocessors save area, containing each coprocessor save area
|
||||
*/
|
||||
STRUCT_BEGIN
|
||||
/* Enable bitmap: BIT(i) represents coprocessor i, 1 is used, 0 else */
|
||||
STRUCT_FIELD (long, 4, RV_COPROC_ENABLE, sa_enable)
|
||||
/* Address of the pool of memory used to allocate coprocessors save areas */
|
||||
STRUCT_FIELD (long, 4, RV_COPROC_ALLOCATOR, sa_allocator)
|
||||
/* Pointer to the coprocessors save areas */
|
||||
STRUCT_AFIELD (void*, 4, RV_COPROC_SA, sa_coprocs, SOC_CPU_COPROC_NUM)
|
||||
STRUCT_END(RvCoprocSaveArea)
|
||||
|
||||
|
||||
#endif /* SOC_CPU_COPROC_NUM > 0 */
|
||||
|
||||
|
||||
#if defined(_ASMLANGUAGE) || defined(__ASSEMBLER__)
|
||||
#define RV_STK_SZ1 RvExcFrameSize
|
||||
#else
|
||||
|
@ -14,9 +14,15 @@
|
||||
|
||||
.equ SAVE_REGS, 32
|
||||
.equ CONTEXT_SIZE, (SAVE_REGS * 4)
|
||||
.equ EXC_ILLEGAL_INSTRUCTION, 0x2
|
||||
.equ panic_from_exception, xt_unhandled_exception
|
||||
.equ panic_from_isr, panicHandler
|
||||
|
||||
#if ( SOC_CPU_COPROC_NUM > 0 )
|
||||
/* Targets with coprocessors present a special CSR to get Illegal Instruction exception reason */
|
||||
.equ EXT_ILL_CSR, 0x7F0
|
||||
#endif /* SOC_CPU_COPROC_NUM > 0 */
|
||||
|
||||
/* Macro which first allocates space on the stack to save general
|
||||
* purpose registers, and then save them. GP register is excluded.
|
||||
* The default size allocated on the stack is CONTEXT_SIZE, but it
|
||||
@ -100,8 +106,10 @@
|
||||
csrw mepc, t0
|
||||
.endm
|
||||
|
||||
|
||||
.global rtos_int_enter
|
||||
.global rtos_int_exit
|
||||
.global rtos_save_fpu_coproc
|
||||
.global _global_interrupt_handler
|
||||
#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
|
||||
.global gdbstub_handle_debug_int
|
||||
@ -130,10 +138,56 @@ _panic_handler:
|
||||
sw t0, RV_STK_MSTATUS(sp)
|
||||
csrr t0, mtvec
|
||||
sw t0, RV_STK_MTVEC(sp)
|
||||
csrr t0, mtval
|
||||
sw t0, RV_STK_MTVAL(sp)
|
||||
csrr t0, mhartid
|
||||
sw t0, RV_STK_MHARTID(sp)
|
||||
csrr t0, mtval
|
||||
sw t0, RV_STK_MTVAL(sp)
|
||||
|
||||
/* Keep mcause in s0, only the exception code and interrupt bit are relevant */
|
||||
csrr s0, mcause
|
||||
li t1, VECTORS_MCAUSE_INTBIT_MASK | VECTORS_MCAUSE_REASON_MASK
|
||||
and s0, s0, t1
|
||||
|
||||
#if ( SOC_CPU_COPROC_NUM > 0 )
|
||||
/* Check if the exception was cause by a coprocessor instruction. If this is the case, we have
|
||||
* to lazily save the registers inside the current owner's save area */
|
||||
/* Check if the exception is Illegal instruction */
|
||||
li a1, EXC_ILLEGAL_INSTRUCTION
|
||||
bne s0, a1, _panic_handler_not_coproc
|
||||
/* In case this is due to a coprocessor, set ra right now to simplify the logic below */
|
||||
la ra, _return_from_exception
|
||||
/* EXT_ILL CSR should contain the reason for the Illegal Instruction. */
|
||||
csrr a0, EXT_ILL_CSR
|
||||
bnez a0, _panic_handler_coproc
|
||||
#if SOC_CPU_HAS_FPU_EXT_ILL_BUG && SOC_CPU_HAS_FPU
|
||||
/* If the SOC present the hardware EXT_ILL CSR bug, it doesn't support FPU load/store detection
|
||||
* so we have to check the instruction's opcode (in `mtval` = `t0`) */
|
||||
andi a0, t0, 0b1011111
|
||||
li a1, 0b0000111
|
||||
/* If opcode is of the form 0b0x00111, the instruction is FLW or FSW */
|
||||
beq a0, a1, rtos_save_fpu_coproc
|
||||
/* Check the compressed instructions: C.FLW, C.FSW, C.FLWSP and C.FSWP.
|
||||
* All of them have their highest 3 bits to x11 and the lowest bit to 0 */
|
||||
li a0, 0x6001
|
||||
and a0, t0, a0 /* a0 = mtval & 0x6001 */
|
||||
li a1, 0x6000
|
||||
beq a0, a1, rtos_save_fpu_coproc
|
||||
/* The instruction was not an FPU one, continue the exception */
|
||||
#endif /* SOC_CPU_HAS_FPU_EXT_ILL_BUG && SOC_CPU_HAS_FPU */
|
||||
j _panic_handler_not_coproc
|
||||
_panic_handler_coproc:
|
||||
/* EXT_ILL CSR reasons are stored as follows:
|
||||
* - Bit 0: FPU core instruction (Load/Store instructions NOT concerned)
|
||||
* - Bit 1: Low-power core
|
||||
* - Bit 2: PIE core
|
||||
*/
|
||||
#if SOC_CPU_HAS_FPU
|
||||
li a1, 1
|
||||
beq a0, a1, rtos_save_fpu_coproc
|
||||
#endif /* SOC_CPU_HAS_FPU */
|
||||
/* Ignore LP and PIE for now, continue the exception */
|
||||
_panic_handler_not_coproc:
|
||||
#endif /* ( SOC_CPU_COPROC_NUM > 0 ) */
|
||||
|
||||
/* Call panic_from_exception(sp) or panic_from_isr(sp)
|
||||
* depending on whether we have a pseudo excause or not.
|
||||
@ -141,11 +195,7 @@ _panic_handler:
|
||||
* so we have a pseudo excause. Else, it is due to a exception, we don't
|
||||
* have an pseudo excause */
|
||||
mv a0, sp
|
||||
csrr a1, mcause
|
||||
|
||||
/* Only keep the interrupt bit and the source cause of the trap */
|
||||
li t1, VECTORS_MCAUSE_INTBIT_MASK | VECTORS_MCAUSE_REASON_MASK
|
||||
and a1, a1, t1
|
||||
mv a1, s0
|
||||
|
||||
/* Branches instructions don't accept immediate values, so use t1 to
|
||||
* store our comparator */
|
||||
@ -156,10 +206,9 @@ _panic_handler:
|
||||
li t0, 3
|
||||
beq a1, t0, _call_gdbstub_handler
|
||||
#endif
|
||||
/* exception_from_panic never returns */
|
||||
jal panic_from_exception
|
||||
call panic_from_exception
|
||||
/* We arrive here if the exception handler has returned. */
|
||||
j _return_from_exception
|
||||
j _return_from_exception
|
||||
|
||||
#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
|
||||
_call_gdbstub_handler:
|
||||
@ -168,17 +217,15 @@ _call_gdbstub_handler:
|
||||
#endif
|
||||
|
||||
_call_panic_handler:
|
||||
/* Remove highest bit from mcause (a1) register and save it in the
|
||||
* structure */
|
||||
/* Remove highest bit from mcause (a1) register and save it in the structure */
|
||||
not t0, t0
|
||||
and a1, a1, t0
|
||||
#if CONFIG_SOC_INT_CLIC_SUPPORTED
|
||||
/* When CLIC is supported, external interrupts are shifted by 16, deduct this difference from mcause */
|
||||
add a1, a1, -16
|
||||
add a1, a1, -16
|
||||
#endif // CONFIG_SOC_INT_CLIC_SUPPORTED
|
||||
sw a1, RV_STK_MCAUSE(sp)
|
||||
jal panic_from_isr
|
||||
|
||||
call panic_from_isr
|
||||
/* We arrive here if the exception handler has returned. This means that
|
||||
* the exception was handled, and the execution flow should resume.
|
||||
* Restore the registers and return from the exception.
|
||||
@ -195,10 +242,9 @@ _return_from_exception:
|
||||
|
||||
|
||||
/* This is the interrupt handler.
|
||||
* It saves the registers on the stack,
|
||||
* prepares for interrupt nesting,
|
||||
* re-enables the interrupts,
|
||||
* then jumps to the C dispatcher in interrupt.c.
|
||||
* It saves the registers on the stack, prepares for interrupt nesting, re-enables the interrupts,
|
||||
* then jumps to the C dispatcher in interrupt.c. Upon return, the register context will be restored
|
||||
* from the stack.
|
||||
*/
|
||||
.global _interrupt_handler
|
||||
.type _interrupt_handler, @function
|
||||
@ -213,11 +259,12 @@ _interrupt_handler:
|
||||
* the backtrace of threads preempted by interrupts (OS tick etc.).
|
||||
* GP is saved just to have its proper value in GDB. */
|
||||
/* As gp register is not saved by the macro, save it here */
|
||||
sw gp, RV_STK_GP(sp)
|
||||
sw gp, RV_STK_GP(sp)
|
||||
/* Same goes for the SP value before trapping */
|
||||
addi t0, sp, CONTEXT_SIZE /* restore sp with the value when interrupt happened */
|
||||
/* Save SP */
|
||||
sw t0, RV_STK_SP(sp)
|
||||
addi a0, sp, CONTEXT_SIZE /* restore sp with the value when interrupt happened */
|
||||
|
||||
/* Save SP former value */
|
||||
sw a0, RV_STK_SP(sp)
|
||||
|
||||
/* Notify the RTOS that an interrupt ocurred, it will save the current stack pointer
|
||||
* in the running TCB, no need to pass it as a parameter */
|
||||
@ -245,8 +292,7 @@ _interrupt_handler:
|
||||
fence
|
||||
#endif // !SOC_INT_HW_NESTED_SUPPORTED
|
||||
|
||||
li t0, 0x8
|
||||
csrrs t0, mstatus, t0
|
||||
csrsi mstatus, 0x8
|
||||
/* MIE set. Nested interrupts can now occur */
|
||||
|
||||
#ifdef CONFIG_PM_TRACE
|
||||
@ -275,8 +321,7 @@ _interrupt_handler:
|
||||
|
||||
/* After dispatch c handler, disable interrupt to make freertos make context switch */
|
||||
|
||||
li t0, 0x8
|
||||
csrrc t0, mstatus, t0
|
||||
csrci mstatus, 0x8
|
||||
/* MIE cleared. Nested interrupts are disabled */
|
||||
|
||||
#if !SOC_INT_HW_NESTED_SUPPORTED
|
||||
@ -286,7 +331,9 @@ _interrupt_handler:
|
||||
fence
|
||||
#endif // !SOC_INT_HW_NESTED_SUPPORTED
|
||||
|
||||
/* The RTOS will restore the current TCB stack pointer. This routine will preserve s1 and s2 but alter s0. */
|
||||
/* The RTOS will restore the current TCB stack pointer. This routine will preserve s1 and s2.
|
||||
* Returns the new `mstatus` value. */
|
||||
mv a0, s2 /* a0 = mstatus */
|
||||
call rtos_int_exit
|
||||
|
||||
/* Restore the rest of the registers.
|
||||
@ -294,10 +341,9 @@ _interrupt_handler:
|
||||
* the former CPU priority. When executing `mret`, the hardware will restore the former threshold,
|
||||
* from `mcause` to `mintstatus` CSR */
|
||||
csrw mcause, s1
|
||||
csrw mstatus, s2
|
||||
csrw mstatus, a0
|
||||
restore_mepc
|
||||
restore_general_regs
|
||||
|
||||
/* exit, this will also re-enable the interrupts */
|
||||
mret
|
||||
.size _interrupt_handler, .-_interrupt_handler
|
||||
|
@ -287,6 +287,18 @@ config SOC_BRANCH_PREDICTOR_SUPPORTED
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_CPU_HAS_FPU
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_CPU_HAS_FPU_EXT_ILL_BUG
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_CPU_COPROC_NUM
|
||||
int
|
||||
default 2
|
||||
|
||||
config SOC_CPU_BREAKPOINTS_NUM
|
||||
int
|
||||
default 4
|
||||
|
@ -149,6 +149,9 @@
|
||||
#define SOC_INT_CLIC_SUPPORTED 1
|
||||
#define SOC_INT_HW_NESTED_SUPPORTED 1 // Support for hardware interrupts nesting
|
||||
#define SOC_BRANCH_PREDICTOR_SUPPORTED 1
|
||||
#define SOC_CPU_HAS_FPU 1
|
||||
#define SOC_CPU_HAS_FPU_EXT_ILL_BUG 1 // EXT_ILL CSR doesn't support FLW/FSW
|
||||
#define SOC_CPU_COPROC_NUM 2
|
||||
|
||||
#define SOC_CPU_BREAKPOINTS_NUM 4
|
||||
#define SOC_CPU_WATCHPOINTS_NUM 4
|
||||
|
Loading…
Reference in New Issue
Block a user