mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
cpu/esp_common: platform-specific code for RISC-V based ESP32x SoCs
This commit is contained in:
parent
b59f8b59da
commit
c4132573f4
75
cpu/esp_common/esp-riscv/exceptions.c
Normal file
75
cpu/esp_common/esp-riscv/exceptions.c
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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 Exception handling for RISC-V-based ESP SoCs
|
||||
*
|
||||
* @author Gunar Schorcht <gunar@schorcht.net>
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "kernel_defines.h"
|
||||
#include "panic.h"
|
||||
#include "periph/pm.h"
|
||||
|
||||
#include "esp_attr.h"
|
||||
#include "riscv/rvruntime-frames.h"
|
||||
#include "rom/ets_sys.h"
|
||||
|
||||
static const char *exceptions[] = {
|
||||
"nil",
|
||||
"0x1: PMP Instruction access fault",
|
||||
"0x2: Illegal Instruction",
|
||||
"0x3: Hardware Breakpoint/Watchpoint or EBREAK",
|
||||
"nil",
|
||||
"0x5: PMP Load access fault",
|
||||
"nil",
|
||||
"0x7: PMP Store access fault",
|
||||
"0x8: ECALL from U mode",
|
||||
"nil",
|
||||
"nil",
|
||||
"0xb: ECALL from M mode",
|
||||
};
|
||||
|
||||
static RvExcFrame *_frame = NULL;
|
||||
|
||||
void init_exceptions (void)
|
||||
{
|
||||
}
|
||||
|
||||
void IRAM_ATTR xt_unhandled_exception(RvExcFrame *frame)
|
||||
{
|
||||
_frame = frame;
|
||||
core_panic(PANIC_GENERAL_ERROR, "Unhandled exception");
|
||||
}
|
||||
|
||||
void IRAM_ATTR panicHandler(RvExcFrame *frame)
|
||||
{
|
||||
_frame = frame;
|
||||
core_panic(PANIC_GENERAL_ERROR, "Panic handler");
|
||||
}
|
||||
|
||||
extern void heap_stats(void);
|
||||
|
||||
void panic_arch(void)
|
||||
{
|
||||
if (_frame) {
|
||||
/* TODO */
|
||||
ets_printf("Exception @0x%08"PRIx32", cause %s\n",
|
||||
_frame->mepc, exceptions[_frame->mcause]);
|
||||
}
|
||||
#if defined(DEVELHELP)
|
||||
heap_stats();
|
||||
#endif
|
||||
}
|
97
cpu/esp_common/esp-riscv/irq_arch.c
Normal file
97
cpu/esp_common/esp-riscv/irq_arch.c
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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_esp32
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Implementation of the kernels irq interface
|
||||
*
|
||||
* @author Gunar Schorcht <gunar@schorcht.net>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "irq_arch.h"
|
||||
|
||||
#include "esp_attr.h"
|
||||
#include "hal/interrupt_controller_types.h"
|
||||
#include "hal/interrupt_controller_ll.h"
|
||||
#include "soc/periph_defs.h"
|
||||
|
||||
#define ENABLE_DEBUG 0
|
||||
#include "debug.h"
|
||||
|
||||
#define RVHAL_EXCM_LEVEL 4
|
||||
|
||||
/**
|
||||
* @brief Disable all maskable interrupts
|
||||
*/
|
||||
unsigned int IRAM_ATTR irq_disable(void)
|
||||
{
|
||||
uint32_t mstatus;
|
||||
/* clear MIE bit in register mstatus */
|
||||
__asm__ volatile ("csrrc %0, mstatus, %1" : "=r"(mstatus) : "rK"(MSTATUS_MIE) : "memory");
|
||||
|
||||
/* save interrupt priority level threshold */
|
||||
uint32_t state = *((volatile uint32_t *)INTERRUPT_CORE0_CPU_INT_THRESH_REG);
|
||||
/* set interrupt priority level threshold to exception level */
|
||||
*((volatile uint32_t *)INTERRUPT_CORE0_CPU_INT_THRESH_REG) = RVHAL_EXCM_LEVEL;
|
||||
|
||||
/* set MIE bit in register mstatus */
|
||||
__asm__ volatile ("csrrs %0, mstatus, %1" : "=r"(mstatus) : "rK"(MSTATUS_MIE) : "memory");
|
||||
|
||||
DEBUG("%s %02x(%02x)\n", __func__, RVHAL_EXCM_LEVEL, (unsigned)state);
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable all maskable interrupts
|
||||
*/
|
||||
unsigned int IRAM_ATTR irq_enable(void)
|
||||
{
|
||||
uint32_t state = *((volatile uint32_t *)INTERRUPT_CORE0_CPU_INT_THRESH_REG);
|
||||
|
||||
/* set interrupt priority level threshold to 0 */
|
||||
*((volatile uint32_t *)INTERRUPT_CORE0_CPU_INT_THRESH_REG) = 0;
|
||||
|
||||
/* small delay needed here */
|
||||
__asm__ volatile ( "nop" );
|
||||
__asm__ volatile ( "nop" );
|
||||
__asm__ volatile ( "nop" );
|
||||
|
||||
DEBUG("%s %02x(%02x)\n", __func__, 0, (unsigned)state);
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Restore the state of the IRQ flags
|
||||
*/
|
||||
void IRAM_ATTR irq_restore(unsigned int state)
|
||||
{
|
||||
uint32_t old = *((volatile uint32_t *)INTERRUPT_CORE0_CPU_INT_THRESH_REG);
|
||||
|
||||
/* set interrupt priority level threshold to old level */
|
||||
*((volatile uint32_t *)INTERRUPT_CORE0_CPU_INT_THRESH_REG) = state;
|
||||
|
||||
/* small delay needed here */
|
||||
__asm__ volatile ( "nop" );
|
||||
__asm__ volatile ( "nop" );
|
||||
__asm__ volatile ( "nop" );
|
||||
|
||||
DEBUG("%s %02x(%02x)\n", __func__, (unsigned)state, (unsigned)old);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test if IRQs are currently enabled
|
||||
*/
|
||||
bool IRAM_ATTR irq_is_enabled(void)
|
||||
{
|
||||
return *((volatile uint32_t *)INTERRUPT_CORE0_CPU_INT_THRESH_REG) == 0;
|
||||
}
|
216
cpu/esp_common/esp-riscv/thread_arch.c
Normal file
216
cpu/esp_common/esp-riscv/thread_arch.c
Normal file
@ -0,0 +1,216 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
Loading…
Reference in New Issue
Block a user