mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
217 lines
7.7 KiB
C
217 lines
7.7 KiB
C
/*
|
|
* Copyright (C) 2022 Gunar Schorcht
|
|
*
|
|
* This file is subject to the terms and conditions of the GNU Lesser
|
|
* General Public License v2.1. See the file LICENSE in the top level
|
|
* directory for more details.
|
|
*/
|
|
|
|
/**
|
|
* @ingroup cpu_esp_common
|
|
* @{
|
|
*
|
|
* @file
|
|
* @brief Implementation of kernel's architecture dependent interface
|
|
*
|
|
* This file implements kernel's architecture dependent interface for RISC-V
|
|
* based ESP32x SoCs
|
|
*
|
|
* @note The implementation in `$(RIOTCPU)/risc_common/thread_arch.c` cannot
|
|
* be used because
|
|
* - it requires some modifications for compatibility with ESP-IDF code
|
|
* - the ESP-IDF uses a different context frame
|
|
*
|
|
* @author Gunar Schorcht <gunar@schorcht.net>
|
|
*
|
|
* @}
|
|
*/
|
|
|
|
#include <string.h>
|
|
|
|
#include "esp_attr.h"
|
|
|
|
#include "irq.h"
|
|
#include "thread.h"
|
|
#include "sched.h"
|
|
#include "syscalls.h"
|
|
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "riscv/rvruntime-frames.h"
|
|
#include "soc/soc.h"
|
|
#include "soc/system_reg.h"
|
|
|
|
/*
|
|
* The FreeRTOS implementation for interrupt/exception handling of ESP-IDF,
|
|
* which is also used for RIOT to ensure compatibility with other ESP-IDF code,
|
|
* uses a separate stack in the interrupt context. Therefore, the stack and
|
|
* a pointer to the top of this stack are defined here.
|
|
*
|
|
* For compatibility reasons with xtensa implementation, we call them
|
|
* port_IntStack and port_IntStackTop.
|
|
*/
|
|
|
|
/* bottom and top of ISR stack */
|
|
extern uint8_t port_IntStack;
|
|
extern uint8_t port_IntStackTop;
|
|
|
|
/* pointer the top of the ISR stack as required by ESP-IDF */
|
|
uint8_t *xIsrStackTop = &port_IntStackTop;
|
|
|
|
/* context frame as used by the ESP-IDF */
|
|
typedef struct context_switch_frame {
|
|
uint32_t mepc; /**< machine exception program counter instead of x0 */
|
|
uint32_t ra; /**< x1 - return address (caller saved) */
|
|
uint32_t gp; /**< x3 - global pointer (-) */
|
|
uint32_t sp; /**< x2 - stack pointer (callee saved) */
|
|
uint32_t tp; /**< x4 - thread pointer (-) */
|
|
uint32_t t0; /**< x5 - temporary register (caller saved) */
|
|
uint32_t t1; /**< x6 - temporary register (caller saved) */
|
|
uint32_t t2; /**< x7 - temporary register (caller saved) */
|
|
uint32_t s0; /**< x8 - saved register / frame pointer (callee saved) */
|
|
uint32_t s1; /**< x9 - saved register (callee saved) */
|
|
uint32_t a0; /**< x10 - function argument / return value (caller saved) */
|
|
uint32_t a1; /**< x11 - function argument / return value (caller saved) */
|
|
uint32_t a2; /**< x12 - function argument (caller saved) */
|
|
uint32_t a3; /**< x13 - function argument (caller saved) */
|
|
uint32_t a4; /**< x14 - function argument (caller saved) */
|
|
uint32_t a5; /**< x15 - function argument (caller saved) */
|
|
uint32_t a6; /**< x16 - function argument (caller saved) */
|
|
uint32_t a7; /**< x17 - function argument (caller saved) */
|
|
uint32_t s2; /**< x18 - saved register (callee saved) */
|
|
uint32_t s3; /**< x19 - saved register (callee saved) */
|
|
uint32_t s4; /**< x20 - saved register (callee saved) */
|
|
uint32_t s5; /**< x21 - saved register (callee saved) */
|
|
uint32_t s6; /**< x22 - saved register (callee saved) */
|
|
uint32_t s7; /**< x23 - saved register (callee saved) */
|
|
uint32_t s8; /**< x24 - saved register (callee saved) */
|
|
uint32_t s9; /**< x25 - saved register (callee saved) */
|
|
uint32_t s10; /**< x26 - saved register (callee saved) */
|
|
uint32_t s11; /**< x27 - saved register (callee saved) */
|
|
uint32_t t3; /**< x28 - temporary register (caller saved) */
|
|
uint32_t t4; /**< x29 - temporary register (caller saved) */
|
|
uint32_t t5; /**< x30 - temporary register (caller saved) */
|
|
uint32_t t6; /**< x31 - temporary register (caller saved) */
|
|
} context_switch_frame_t;
|
|
|
|
/*
|
|
* The following function is a modified copy of function `thread_stack_init`
|
|
* in `$(RIOTCPU)/risc_common/thread_arch.c`, which is under the following
|
|
* copyright:
|
|
*
|
|
* Copyright (C) 2017, 2019 Ken Rabold, JP Bonn
|
|
*
|
|
* Modifications:
|
|
* - For compatibility with ESP-IDF, the context frame is defined as
|
|
* used by the ESP-IDF.
|
|
* - The `STACK_MARKER` is not used to identify the top of the stack.
|
|
* Instead, the stack is initialized in the `thread_create` function in
|
|
* `$(RIOTBASE)/core/thread.c` as expected in the `thread_measure_stack_free`
|
|
* function.
|
|
* - Support for `__global_pointer$` and TLS has been added.
|
|
*/
|
|
char* thread_stack_init(thread_task_func_t task_func, void *arg, void *stack_start, int stack_size)
|
|
{
|
|
_Static_assert(sizeof(context_switch_frame_t) == 32 * sizeof(uint32_t),
|
|
"context frame has to store 32 registers");
|
|
|
|
context_switch_frame_t *sf;
|
|
uint8_t *stk_top;
|
|
|
|
/* calculate the top of the stack */
|
|
stk_top = (uint8_t *)stack_start + stack_size;
|
|
/* per ABI align stack pointer to 16 byte boundary. */
|
|
stk_top = (uint8_t *)((uintptr_t)stk_top & ~((uintptr_t)0xf));
|
|
|
|
/* prepare thread local storage and the thread pointer */
|
|
extern uint32_t __global_pointer$;
|
|
|
|
#if !defined(RISCV_NO_RELAX)
|
|
extern char _thread_local_start, _thread_local_end, _flash_rodata_start;
|
|
|
|
uint8_t *_local_start;
|
|
uint32_t _local_size = (uint32_t)(&_thread_local_end - &_thread_local_start);
|
|
|
|
_local_size = ALIGNUP(0x10, _local_size);
|
|
stk_top -= _local_size;
|
|
|
|
_local_start = stk_top;
|
|
memcpy(_local_start, &_thread_local_start, _local_size);
|
|
|
|
uint8_t *tp;
|
|
tp = _local_start - (&_thread_local_start - &_flash_rodata_start);
|
|
#else
|
|
uint8_t *tp = NULL;
|
|
#endif
|
|
|
|
/* reserve space for the stack frame. */
|
|
stk_top = stk_top - sizeof(*sf);
|
|
|
|
/* populate the stack frame with default values for starting the thread. */
|
|
sf = (struct context_switch_frame *)((uintptr_t)stk_top);
|
|
|
|
/* Clear stack frame */
|
|
memset(sf, 0, sizeof(*sf));
|
|
|
|
/* set initial reg values */
|
|
sf->mepc = (uint32_t)task_func;
|
|
sf->a0 = (uint32_t)arg;
|
|
sf->gp = (uint32_t)__global_pointer$;
|
|
sf->tp = (uint32_t)tp;
|
|
/* if the thread exits go to sched_task_exit() */
|
|
sf->ra = (uint32_t)sched_task_exit;
|
|
|
|
return (char *)stk_top;
|
|
}
|
|
|
|
void IRAM_ATTR thread_yield_isr(void* arg)
|
|
{
|
|
(void)arg;
|
|
/**
|
|
* Context switches are realized using software interrupts since interrupt
|
|
* entry and exit functions are used save and restore complete
|
|
* context.
|
|
*/
|
|
/* clear the interrupt first */
|
|
WRITE_PERI_REG(SYSTEM_CPU_INTR_FROM_CPU_0_REG, 0);
|
|
/* set the context switch flag (indicates that context has to be switched
|
|
is switch on exit from interrupt in rtos_int_exit */
|
|
sched_context_switch_request = 1;
|
|
}
|
|
|
|
void IRAM_ATTR thread_yield_higher(void)
|
|
{
|
|
/* reset hardware watchdog */
|
|
system_wdt_feed();
|
|
|
|
/**
|
|
* If we are already in an interrupt handler, the function simply sets the
|
|
* context switch flag, which indicates that the context has to be switched
|
|
* in the rtos_int_exit function when exiting the interrupt. Otherwise, we
|
|
* will generate a software interrupt to force the context switch when
|
|
* exiting from the software interrupt (see thread_yield_isr).
|
|
*/
|
|
if (irq_is_in()) {
|
|
/* if already in ISR, only set the sched_context_switch_request flag */
|
|
sched_context_switch_request = 1;
|
|
}
|
|
else {
|
|
/* otherwise trigger a software interrupt for context switch */
|
|
WRITE_PERI_REG(SYSTEM_CPU_INTR_FROM_CPU_0_REG, 1);
|
|
/* small delay of 3-4 instructions required here before we return */
|
|
__asm__ volatile ( "nop" );
|
|
__asm__ volatile ( "nop" );
|
|
__asm__ volatile ( "nop" );
|
|
__asm__ volatile ( "nop" );
|
|
}
|
|
}
|
|
|
|
NORETURN void cpu_switch_context_exit(void)
|
|
{
|
|
/* enable interrupts */
|
|
irq_enable();
|
|
|
|
/* force a context switch to another thread */
|
|
thread_yield_higher();
|
|
UNREACHABLE();
|
|
}
|