Add hardware_exception for setting exception handlers at runtime (#380)
This commit is contained in:
parent
7fc75d8c90
commit
53f1915a6b
@ -17,6 +17,7 @@
|
|||||||
* \defgroup hardware_clocks hardware_clocks
|
* \defgroup hardware_clocks hardware_clocks
|
||||||
* \defgroup hardware_divider hardware_divider
|
* \defgroup hardware_divider hardware_divider
|
||||||
* \defgroup hardware_dma hardware_dma
|
* \defgroup hardware_dma hardware_dma
|
||||||
|
* \defgroup hardware_exception hardware_exception
|
||||||
* \defgroup hardware_flash hardware_flash
|
* \defgroup hardware_flash hardware_flash
|
||||||
* \defgroup hardware_gpio hardware_gpio
|
* \defgroup hardware_gpio hardware_gpio
|
||||||
* \defgroup hardware_i2c hardware_i2c
|
* \defgroup hardware_i2c hardware_i2c
|
||||||
|
@ -10,6 +10,7 @@ pico_add_subdirectory(hardware_adc)
|
|||||||
pico_add_subdirectory(hardware_clocks)
|
pico_add_subdirectory(hardware_clocks)
|
||||||
pico_add_subdirectory(hardware_dma)
|
pico_add_subdirectory(hardware_dma)
|
||||||
pico_add_subdirectory(hardware_divider)
|
pico_add_subdirectory(hardware_divider)
|
||||||
|
pico_add_subdirectory(hardware_exception)
|
||||||
pico_add_subdirectory(hardware_flash)
|
pico_add_subdirectory(hardware_flash)
|
||||||
pico_add_subdirectory(hardware_gpio)
|
pico_add_subdirectory(hardware_gpio)
|
||||||
pico_add_subdirectory(hardware_i2c)
|
pico_add_subdirectory(hardware_i2c)
|
||||||
|
1
src/rp2_common/hardware_exception/CMakeLists.txt
Normal file
1
src/rp2_common/hardware_exception/CMakeLists.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
pico_simple_hardware_target(exception)
|
65
src/rp2_common/hardware_exception/exception.c
Normal file
65
src/rp2_common/hardware_exception/exception.c
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "hardware/exception.h"
|
||||||
|
#include "hardware/regs/m0plus.h"
|
||||||
|
#include "hardware/platform_defs.h"
|
||||||
|
#include "hardware/structs/scb.h"
|
||||||
|
|
||||||
|
#include "pico/mutex.h"
|
||||||
|
#include "pico/assert.h"
|
||||||
|
|
||||||
|
#ifndef exception_is_compile_time_default
|
||||||
|
static bool exception_is_compile_time_default(exception_handler_t handler) {
|
||||||
|
extern char __default_isrs_start;
|
||||||
|
extern char __default_isrs_end;
|
||||||
|
return ((uintptr_t)handler) >= (uintptr_t)&__default_isrs_start &&
|
||||||
|
((uintptr_t)handler) < (uintptr_t)&__default_isrs_end;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static inline exception_handler_t *get_vtable(void) {
|
||||||
|
return (exception_handler_t *) scb_hw->vtor;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_raw_exception_handler_and_restore_interrupts(enum exception_number num, exception_handler_t handler, uint32_t save) {
|
||||||
|
// update vtable (vtable_handler may be same or updated depending on cases, but we do it anyway for compactness)
|
||||||
|
get_vtable()[16 + num] = handler;
|
||||||
|
__dmb();
|
||||||
|
restore_interrupts(save);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void check_exception_param(__unused enum exception_number num) {
|
||||||
|
invalid_params_if(EXCEPTION, num < NMI_EXCEPTION || num >=0);
|
||||||
|
}
|
||||||
|
|
||||||
|
exception_handler_t exception_get_vtable_handler(enum exception_number num) {
|
||||||
|
check_exception_param(num);
|
||||||
|
return get_vtable()[16 + num];
|
||||||
|
}
|
||||||
|
|
||||||
|
exception_handler_t exception_set_exclusive_handler(enum exception_number num, exception_handler_t handler) {
|
||||||
|
check_exception_param(num);
|
||||||
|
#if !PICO_NO_RAM_VECTOR_TABLE
|
||||||
|
uint32_t save = save_and_disable_interrupts();
|
||||||
|
exception_handler_t current = exception_get_vtable_handler(num);
|
||||||
|
hard_assert(exception_is_compile_time_default(current));
|
||||||
|
set_raw_exception_handler_and_restore_interrupts(num, handler, save);
|
||||||
|
#else
|
||||||
|
panic_unsupported();
|
||||||
|
#endif
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
void exception_restore_handler(enum exception_number num, exception_handler_t original_handler) {
|
||||||
|
hard_assert(exception_is_compile_time_default(original_handler));
|
||||||
|
#if !PICO_NO_RAM_VECTOR_TABLE
|
||||||
|
uint32_t save = save_and_disable_interrupts();
|
||||||
|
set_raw_exception_handler_and_restore_interrupts(num, original_handler, save);
|
||||||
|
#else
|
||||||
|
panic_unsupported();
|
||||||
|
#endif
|
||||||
|
}
|
106
src/rp2_common/hardware_exception/include/hardware/exception.h
Normal file
106
src/rp2_common/hardware_exception/include/hardware/exception.h
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _HARDWARE_EXCEPTION_H_
|
||||||
|
#define _HARDWARE_EXCEPTION_H_
|
||||||
|
|
||||||
|
#include "pico.h"
|
||||||
|
#include "hardware/address_mapped.h"
|
||||||
|
#include "hardware/regs/m0plus.h"
|
||||||
|
|
||||||
|
/** \file exception.h
|
||||||
|
* \defgroup hardware_exception hardware_exception
|
||||||
|
*
|
||||||
|
* Methods for setting processor exception handlers
|
||||||
|
*
|
||||||
|
* Exceptions are identified by a \ref exception_num which is a number from -15 to -1; these are the numbers relative to
|
||||||
|
* the index of the first IRQ vector in the vector table. (i.e. vector table index is exception_num plus 16)
|
||||||
|
*
|
||||||
|
* There is one set of exception handlers per core, so the exception handlers for each core as set by these methods are independent.
|
||||||
|
*
|
||||||
|
* \note That all exception APIs affect the executing core only (i.e. the core calling the function).
|
||||||
|
*/
|
||||||
|
|
||||||
|
// PICO_CONFIG: PARAM_ASSERTIONS_ENABLED_EXCEPTION, Enable/disable assertions in the exception module, type=bool, default=0, group=hardware_exception
|
||||||
|
#ifndef PARAM_ASSERTIONS_ENABLED_EXCEPTION
|
||||||
|
#define PARAM_ASSERTIONS_ENABLED_EXCEPTION 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*! \brief Exception number definitions
|
||||||
|
*
|
||||||
|
* Note for consistency with irq numbers, these numbers are defined to be negative. The VTABLE index is
|
||||||
|
* the number here plus 16.
|
||||||
|
*
|
||||||
|
* Name | Value | Exception
|
||||||
|
* ---------------------|-------|----------
|
||||||
|
* NMI_EXCEPTION | -14 | Non Maskable Interrupt
|
||||||
|
* HARDFAULT_EXCEPTION | -13 | HardFault
|
||||||
|
* SVCALL_EXCEPTION | -5 | SV Call
|
||||||
|
* PENDSV_EXCEPTION | -2 | Pend SV
|
||||||
|
* SYSTICK_EXCEPTION | -1 | System Tick
|
||||||
|
*
|
||||||
|
* \ingroup hardware_exception
|
||||||
|
*/
|
||||||
|
enum exception_number {
|
||||||
|
NMI_EXCEPTION = -14, /* Non Maskable Interrupt */
|
||||||
|
HARDFAULT_EXCEPTION = -13, /* HardFault Interrupt */
|
||||||
|
SVCALL_EXCEPTION = -5, /* SV Call Interrupt */
|
||||||
|
PENDSV_EXCEPTION = -2, /* Pend SV Interrupt */
|
||||||
|
SYSTICK_EXCEPTION = -1, /* System Tick Interrupt */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*! \brief Exception handler function type
|
||||||
|
* \ingroup hardware_exception
|
||||||
|
*
|
||||||
|
* All exceptions handlers should be of this type, and follow normal ARM EABI register saving conventions
|
||||||
|
*/
|
||||||
|
typedef void (*exception_handler_t)(void);
|
||||||
|
|
||||||
|
/*! \brief Set the exception handler for an exception on the executing core.
|
||||||
|
* \ingroup hardware_exception
|
||||||
|
*
|
||||||
|
* This method will assert if an exception handler has been set for this exception number on this core via
|
||||||
|
* this method, without an intervening restore via exception_restore_handler.
|
||||||
|
*
|
||||||
|
* \note this method may not be used to override an exception handler that was specified at link time by
|
||||||
|
* providing a strong replacement for the weakly defined stub exception handlers. It will assert in this case too.
|
||||||
|
*
|
||||||
|
* \param num Exception number
|
||||||
|
* \param handler The handler to set
|
||||||
|
* \see exception_number
|
||||||
|
*/
|
||||||
|
exception_handler_t exception_set_exclusive_handler(enum exception_number num, exception_handler_t handler);
|
||||||
|
|
||||||
|
/*! \brief Restore the original exception handler for an exception on this core
|
||||||
|
* \ingroup hardware_exception
|
||||||
|
*
|
||||||
|
* This method may be used to restore the exception handler for an exception on this core to the state
|
||||||
|
* prior to the call to exception_set_exclusive_handler(), so that exception_set_exclusive_handler()
|
||||||
|
* may be called again in the future.
|
||||||
|
*
|
||||||
|
* \param num Exception number \ref exception_nums
|
||||||
|
* \param original_handler The original handler returned from \ref exception_set_exclusive_handler
|
||||||
|
* \see exception_set_exclusive_handler()
|
||||||
|
*/
|
||||||
|
void exception_restore_handler(enum exception_number, exception_handler_t original_handler);
|
||||||
|
|
||||||
|
/*! \brief Get the current exception handler for the specified exception from the currently installed vector table
|
||||||
|
* of the execution core
|
||||||
|
* \ingroup hardware_exception
|
||||||
|
*
|
||||||
|
* \param num Exception number
|
||||||
|
* \return the address stored in the VTABLE for the given exception number
|
||||||
|
*/
|
||||||
|
exception_handler_t exception_get_vtable_handler(enum exception_number num);
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
@ -255,7 +255,7 @@ static inline void irq_clear(uint int_num) {
|
|||||||
void irq_set_pending(uint num);
|
void irq_set_pending(uint num);
|
||||||
|
|
||||||
|
|
||||||
/*! \brief Perform IRQ priority intiialization for the current core
|
/*! \brief Perform IRQ priority initialization for the current core
|
||||||
*
|
*
|
||||||
* \note This is an internal method and user should generally not call it.
|
* \note This is an internal method and user should generally not call it.
|
||||||
*/
|
*/
|
||||||
|
@ -74,6 +74,11 @@ __vectors:
|
|||||||
.word isr_irq30
|
.word isr_irq30
|
||||||
.word isr_irq31
|
.word isr_irq31
|
||||||
|
|
||||||
|
// all default exception handlers do nothing, and we can check for them being set to our
|
||||||
|
// default values by them pointing to between __defaults_isrs_start and __default_isrs_end
|
||||||
|
.global __default_isrs_start
|
||||||
|
__default_isrs_start:
|
||||||
|
|
||||||
// Declare a weak symbol for each ISR.
|
// Declare a weak symbol for each ISR.
|
||||||
// By default, they will fall through to the undefined IRQ handler below (breakpoint),
|
// By default, they will fall through to the undefined IRQ handler below (breakpoint),
|
||||||
// but can be overridden by C functions with correct name.
|
// but can be overridden by C functions with correct name.
|
||||||
@ -94,6 +99,9 @@ decl_isr_bkpt isr_svcall
|
|||||||
decl_isr_bkpt isr_pendsv
|
decl_isr_bkpt isr_pendsv
|
||||||
decl_isr_bkpt isr_systick
|
decl_isr_bkpt isr_systick
|
||||||
|
|
||||||
|
.global __default_isrs_end
|
||||||
|
__default_isrs_end:
|
||||||
|
|
||||||
.macro decl_isr name
|
.macro decl_isr name
|
||||||
.weak \name
|
.weak \name
|
||||||
.type \name,%function
|
.type \name,%function
|
||||||
|
@ -6,6 +6,7 @@ target_link_libraries(kitchen_sink_libs INTERFACE
|
|||||||
hardware_adc
|
hardware_adc
|
||||||
hardware_clocks
|
hardware_clocks
|
||||||
hardware_divider
|
hardware_divider
|
||||||
|
hardware_exception
|
||||||
hardware_dma
|
hardware_dma
|
||||||
hardware_flash
|
hardware_flash
|
||||||
hardware_gpio
|
hardware_gpio
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include "hardware/clocks.h"
|
#include "hardware/clocks.h"
|
||||||
#include "hardware/divider.h"
|
#include "hardware/divider.h"
|
||||||
#include "hardware/dma.h"
|
#include "hardware/dma.h"
|
||||||
|
#include "hardware/exception.h"
|
||||||
#include "hardware/flash.h"
|
#include "hardware/flash.h"
|
||||||
#include "hardware/gpio.h"
|
#include "hardware/gpio.h"
|
||||||
#include "hardware/i2c.h"
|
#include "hardware/i2c.h"
|
||||||
|
Loading…
Reference in New Issue
Block a user