arch: riscv32: provide a general mechanism for saving SoC context
RISC-V permits myriad extensions to the ISA, any of which may imply additional context that must be saved and restored on ISR entry and exit. The current in-tree example is the Pulpino core, which has extra registers used by ISA extensions for running loops that shouldn't get clobbered by an ISR. This is currently supported by including pulpino-specific definitions in the generic architecture code. This works, but it's a bit inelegant and is something of a layering violation. A more generic mechanism is required to support other RISC-V SoCs with similar requirements without cluttering the arch code too much. Provide that by extending the semantics of the existing CONFIG_RISCV_SOC_CONTEXT_SAVE option to allow other SoCs to allocate space for saving and restoring their own state, promoting the currently pulpino-specific __soc_save_context / __soc_restore_context routines to a RISC-V arch API. The cost of making this generic is two more instructions in each ISR to pass the SoC specific context to these routines in a0 rather than just assuming the stack points to the right place. This is minimal, and should have been done anyway to keep with the ABI. As a first (and currently only in-tree) customer, convert the Pulpino SoC code to this new mechanism. Signed-off-by: Marti Bolivar <marti@foundries.io>
This commit is contained in:
parent
b85d893d60
commit
f4c3163d3b
7 changed files with 161 additions and 63 deletions
|
@ -24,10 +24,44 @@ config INCLUDE_RESET_VECTOR
|
||||||
prepares for running C code.
|
prepares for running C code.
|
||||||
|
|
||||||
config RISCV_SOC_CONTEXT_SAVE
|
config RISCV_SOC_CONTEXT_SAVE
|
||||||
bool "Enable SOC-based context saving in IRQ handler"
|
bool "Enable SOC-based context saving in IRQ handlers"
|
||||||
help
|
help
|
||||||
Enable SOC-based context saving, for SOCS which require saving of
|
Enable low-level SOC-specific context management, for SOCs
|
||||||
extra registers when entering an interrupt/exception
|
with extra state that must be saved when entering an
|
||||||
|
interrupt/exception, and restored on exit. If unsure, leave
|
||||||
|
this at the default value.
|
||||||
|
|
||||||
|
Enabling this option requires that the SoC provide a
|
||||||
|
soc_context.h header which defines the following macros:
|
||||||
|
|
||||||
|
- SOC_ESF_MEMBERS: structure component declarations to
|
||||||
|
allocate space for. The last such declaration should not
|
||||||
|
end in a semicolon, for portability. The generic RISC-V
|
||||||
|
architecture code will allocate space for these members in
|
||||||
|
a "struct soc_esf" type (typedefed to soc_esf_t), which will
|
||||||
|
be available if arch.h is included.
|
||||||
|
|
||||||
|
- SOC_ESF_INIT: structure contents initializer for struct soc_esf
|
||||||
|
state. The last initialized member should not end in a comma.
|
||||||
|
|
||||||
|
- GEN_SOC_OFFSET_SYMS(): a macro which expands to
|
||||||
|
GEN_OFFSET_SYM(soc_esf_t, soc_specific_member) calls
|
||||||
|
to ensure offset macros for SOC_ESF_MEMBERS are defined
|
||||||
|
in offsets.h. The last one should not end in a semicolon.
|
||||||
|
See gen_offset.h for more details.
|
||||||
|
|
||||||
|
The generic architecture IRQ wrapper will also call
|
||||||
|
__soc_save_context and __soc_restore_context routines at
|
||||||
|
ISR entry and exit, respectively. These should typically
|
||||||
|
be implemented in assembly. If they were C functions, they
|
||||||
|
would have these signatures:
|
||||||
|
|
||||||
|
``void __soc_save_context(soc_esf_t *state);``
|
||||||
|
|
||||||
|
``void __soc_restore_context(soc_esf_t *state);``
|
||||||
|
|
||||||
|
The calls obey standard calling conventions; i.e., the state
|
||||||
|
pointer address is in a0, and ra contains the return address.
|
||||||
|
|
||||||
config RISCV_SOC_INTERRUPT_INIT
|
config RISCV_SOC_INTERRUPT_INIT
|
||||||
bool "Enable SOC-based interrupt initialization"
|
bool "Enable SOC-based interrupt initialization"
|
||||||
|
|
|
@ -31,13 +31,10 @@ const NANO_ESF _default_esf = {
|
||||||
0xdeadbaad,
|
0xdeadbaad,
|
||||||
0xdeadbaad,
|
0xdeadbaad,
|
||||||
0xdeadbaad,
|
0xdeadbaad,
|
||||||
#if defined(CONFIG_SOC_RISCV32_PULPINO)
|
#if defined(CONFIG_RISCV_SOC_CONTEXT_SAVE)
|
||||||
0xdeadbaad,
|
{
|
||||||
0xdeadbaad,
|
SOC_ESF_INIT,
|
||||||
0xdeadbaad,
|
},
|
||||||
0xdeadbaad,
|
|
||||||
0xdeadbaad,
|
|
||||||
0xdeadbaad,
|
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2016 Jean-Paul Etienne <fractalclone@gmail.com>
|
* Copyright (c) 2016 Jean-Paul Etienne <fractalclone@gmail.com>
|
||||||
|
* Copyright (c) 2018 Foundries.io Ltd
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
@ -11,11 +12,13 @@
|
||||||
|
|
||||||
/* imports */
|
/* imports */
|
||||||
GDATA(_sw_isr_table)
|
GDATA(_sw_isr_table)
|
||||||
GTEXT(__soc_save_context)
|
|
||||||
GTEXT(__soc_restore_context)
|
|
||||||
GTEXT(__soc_is_irq)
|
GTEXT(__soc_is_irq)
|
||||||
GTEXT(__soc_handle_irq)
|
GTEXT(__soc_handle_irq)
|
||||||
GTEXT(_Fault)
|
GTEXT(_Fault)
|
||||||
|
#ifdef CONFIG_RISCV_SOC_CONTEXT_SAVE
|
||||||
|
GTEXT(__soc_save_context)
|
||||||
|
GTEXT(__soc_restore_context)
|
||||||
|
#endif /* CONFIG_RISCV_SOC_CONTEXT_SAVE */
|
||||||
|
|
||||||
GTEXT(_k_neg_eagain)
|
GTEXT(_k_neg_eagain)
|
||||||
GTEXT(_is_next_thread_current)
|
GTEXT(_is_next_thread_current)
|
||||||
|
@ -36,23 +39,24 @@ GTEXT(__irq_wrapper)
|
||||||
/* use ABI name of registers for the sake of simplicity */
|
/* use ABI name of registers for the sake of simplicity */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ISR is handled at both ARCH and SOC levels.
|
* Generic architecture-level IRQ handling, along with callouts to
|
||||||
* At the ARCH level, ISR handles basic context saving/restore of registers
|
* SoC-specific routines.
|
||||||
* onto/from the thread stack and calls corresponding IRQ function registered
|
|
||||||
* at driver level.
|
|
||||||
|
|
||||||
* At SOC level, ISR handles saving/restoring of SOC-specific registers
|
|
||||||
* onto/from the thread stack (handled via __soc_save_context and
|
|
||||||
* __soc_restore_context functions). SOC level save/restore context
|
|
||||||
* is accounted for only if CONFIG_RISCV_SOC_CONTEXT_SAVE variable is set
|
|
||||||
*
|
*
|
||||||
* Moreover, given that RISC-V architecture does not provide a clear ISA
|
* Architecture level IRQ handling includes basic context save/restore
|
||||||
* specification about interrupt handling, each RISC-V SOC handles it in
|
* of standard registers and calling ISRs registered at Zephyr's driver
|
||||||
* its own way. Hence, the generic RISC-V ISR handler expects the following
|
* level.
|
||||||
* functions to be provided at the SOC level:
|
*
|
||||||
* __soc_is_irq: to check if the exception is the result of an interrupt or not.
|
* Since RISC-V does not completely prescribe IRQ handling behavior,
|
||||||
* __soc_handle_irq: handle pending IRQ at SOC level (ex: clear pending IRQ in
|
* implementations vary (some implementations also deviate from
|
||||||
* SOC-specific IRQ register)
|
* what standard behavior is defined). Hence, the arch level code expects
|
||||||
|
* the following functions to be provided at the SOC level:
|
||||||
|
*
|
||||||
|
* - __soc_is_irq: decide if we're handling an interrupt or an exception
|
||||||
|
* - __soc_handle_irq: handle SoC-specific details for a pending IRQ
|
||||||
|
* (e.g. clear a pending bit in a SoC-specific register)
|
||||||
|
*
|
||||||
|
* If CONFIG_RISCV_SOC_CONTEXT=y, calls to SoC-level context save/restore
|
||||||
|
* routines are also made here. For details, see the Kconfig help text.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -99,6 +103,7 @@ SECTION_FUNC(exception.entry, __irq_wrapper)
|
||||||
|
|
||||||
#ifdef CONFIG_RISCV_SOC_CONTEXT_SAVE
|
#ifdef CONFIG_RISCV_SOC_CONTEXT_SAVE
|
||||||
/* Handle context saving at SOC level. */
|
/* Handle context saving at SOC level. */
|
||||||
|
addi a0, sp, __NANO_ESF_soc_context_OFFSET
|
||||||
jal ra, __soc_save_context
|
jal ra, __soc_save_context
|
||||||
#endif /* CONFIG_RISCV_SOC_CONTEXT_SAVE */
|
#endif /* CONFIG_RISCV_SOC_CONTEXT_SAVE */
|
||||||
|
|
||||||
|
@ -409,6 +414,7 @@ reschedule:
|
||||||
no_reschedule:
|
no_reschedule:
|
||||||
#ifdef CONFIG_RISCV_SOC_CONTEXT_SAVE
|
#ifdef CONFIG_RISCV_SOC_CONTEXT_SAVE
|
||||||
/* Restore context at SOC level */
|
/* Restore context at SOC level */
|
||||||
|
addi a0, sp, __NANO_ESF_soc_context_OFFSET
|
||||||
jal ra, __soc_restore_context
|
jal ra, __soc_restore_context
|
||||||
#endif /* CONFIG_RISCV_SOC_CONTEXT_SAVE */
|
#endif /* CONFIG_RISCV_SOC_CONTEXT_SAVE */
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,10 @@
|
||||||
#include <kernel_structs.h>
|
#include <kernel_structs.h>
|
||||||
#include <kernel_offsets.h>
|
#include <kernel_offsets.h>
|
||||||
|
|
||||||
|
#ifdef CONFIG_RISCV_SOC_CONTEXT_SAVE
|
||||||
|
#include <soc_context.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
/* thread_arch_t member offsets */
|
/* thread_arch_t member offsets */
|
||||||
GEN_OFFSET_SYM(_thread_arch_t, swap_return_value);
|
GEN_OFFSET_SYM(_thread_arch_t, swap_return_value);
|
||||||
|
|
||||||
|
@ -58,13 +62,9 @@ GEN_OFFSET_SYM(NANO_ESF, a7);
|
||||||
GEN_OFFSET_SYM(NANO_ESF, mepc);
|
GEN_OFFSET_SYM(NANO_ESF, mepc);
|
||||||
GEN_OFFSET_SYM(NANO_ESF, mstatus);
|
GEN_OFFSET_SYM(NANO_ESF, mstatus);
|
||||||
|
|
||||||
#if defined(CONFIG_SOC_RISCV32_PULPINO)
|
#if defined(CONFIG_RISCV_SOC_CONTEXT_SAVE)
|
||||||
GEN_OFFSET_SYM(NANO_ESF, lpstart0);
|
GEN_OFFSET_SYM(NANO_ESF, soc_context);
|
||||||
GEN_OFFSET_SYM(NANO_ESF, lpend0);
|
GEN_SOC_OFFSET_SYMS();
|
||||||
GEN_OFFSET_SYM(NANO_ESF, lpcount0);
|
|
||||||
GEN_OFFSET_SYM(NANO_ESF, lpstart1);
|
|
||||||
GEN_OFFSET_SYM(NANO_ESF, lpend1);
|
|
||||||
GEN_OFFSET_SYM(NANO_ESF, lpcount1);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2016 Jean-Paul Etienne <fractalclone@gmail.com>
|
* Copyright (c) 2016 Jean-Paul Etienne <fractalclone@gmail.com>
|
||||||
|
* Copyright (c) 2018 Foundries.io Ltd
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
@ -22,6 +23,24 @@ extern "C" {
|
||||||
#include <zephyr/types.h>
|
#include <zephyr/types.h>
|
||||||
#include <toolchain.h>
|
#include <toolchain.h>
|
||||||
|
|
||||||
|
#ifdef CONFIG_RISCV_SOC_CONTEXT_SAVE
|
||||||
|
#include <soc_context.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The name of the structure which contains soc-specific state, if
|
||||||
|
* any, as well as the soc_esf_t typedef below, are part of the RISC-V
|
||||||
|
* arch API.
|
||||||
|
*
|
||||||
|
* The contents of the struct are provided by a SOC-specific
|
||||||
|
* definition in soc_context.h.
|
||||||
|
*/
|
||||||
|
#ifdef CONFIG_RISCV_SOC_CONTEXT_SAVE
|
||||||
|
struct soc_esf {
|
||||||
|
SOC_ESF_MEMBERS;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
struct __esf {
|
struct __esf {
|
||||||
u32_t ra; /* return address */
|
u32_t ra; /* return address */
|
||||||
u32_t gp; /* global pointer */
|
u32_t gp; /* global pointer */
|
||||||
|
@ -47,18 +66,15 @@ struct __esf {
|
||||||
u32_t mepc; /* machine exception program counter */
|
u32_t mepc; /* machine exception program counter */
|
||||||
u32_t mstatus; /* machine status register */
|
u32_t mstatus; /* machine status register */
|
||||||
|
|
||||||
#if defined(CONFIG_SOC_RISCV32_PULPINO)
|
#ifdef CONFIG_RISCV_SOC_CONTEXT_SAVE
|
||||||
/* pulpino hardware loop registers */
|
struct soc_esf soc_context;
|
||||||
u32_t lpstart0;
|
|
||||||
u32_t lpend0;
|
|
||||||
u32_t lpcount0;
|
|
||||||
u32_t lpstart1;
|
|
||||||
u32_t lpend1;
|
|
||||||
u32_t lpcount1;
|
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct __esf NANO_ESF;
|
typedef struct __esf NANO_ESF;
|
||||||
|
#ifdef CONFIG_RISCV_SOC_CONTEXT_SAVE
|
||||||
|
typedef struct soc_esf soc_esf_t;
|
||||||
|
#endif
|
||||||
extern const NANO_ESF _default_esf;
|
extern const NANO_ESF _default_esf;
|
||||||
|
|
||||||
extern FUNC_NORETURN void _NanoFatalErrorHandler(unsigned int reason,
|
extern FUNC_NORETURN void _NanoFatalErrorHandler(unsigned int reason,
|
||||||
|
|
41
soc/riscv32/pulpino/soc_context.h
Normal file
41
soc/riscv32/pulpino/soc_context.h
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Foundries.io Ltd
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Extra definitions required for CONFIG_RISCV_SOC_CONTEXT_SAVE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PULPINO_SOC_CONTEXT_H_
|
||||||
|
#define PULPINO_SOC_CONTEXT_H_
|
||||||
|
|
||||||
|
/* Extra state for pulpino hardware loop registers. */
|
||||||
|
#define SOC_ESF_MEMBERS \
|
||||||
|
u32_t lpstart0; \
|
||||||
|
u32_t lpend0; \
|
||||||
|
u32_t lpcount0; \
|
||||||
|
u32_t lpstart1; \
|
||||||
|
u32_t lpend1; \
|
||||||
|
u32_t lpcount1
|
||||||
|
|
||||||
|
/* Initial saved state. */
|
||||||
|
#define SOC_ESF_INIT \
|
||||||
|
0xdeadbaad, \
|
||||||
|
0xdeadbaad, \
|
||||||
|
0xdeadbaad, \
|
||||||
|
0xdeadbaad, \
|
||||||
|
0xdeadbaad, \
|
||||||
|
0xdeadbaad
|
||||||
|
|
||||||
|
/* Ensure offset macros are available in <offsets.h> for the above. */
|
||||||
|
#define GEN_SOC_OFFSET_SYMS() \
|
||||||
|
GEN_OFFSET_SYM(soc_esf_t, lpstart0); \
|
||||||
|
GEN_OFFSET_SYM(soc_esf_t, lpend0); \
|
||||||
|
GEN_OFFSET_SYM(soc_esf_t, lpcount0); \
|
||||||
|
GEN_OFFSET_SYM(soc_esf_t, lpstart1); \
|
||||||
|
GEN_OFFSET_SYM(soc_esf_t, lpend1); \
|
||||||
|
GEN_OFFSET_SYM(soc_esf_t, lpcount1)
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2016 Jean-Paul Etienne <fractalclone@gmail.com>
|
* Copyright (c) 2016 Jean-Paul Etienne <fractalclone@gmail.com>
|
||||||
|
* Copyright (c) 2018 Foundries.io Ltd
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
@ -19,51 +20,54 @@ GTEXT(__soc_irq_unlock)
|
||||||
|
|
||||||
/* Use ABI name of registers for the sake of simplicity */
|
/* Use ABI name of registers for the sake of simplicity */
|
||||||
|
|
||||||
|
#ifdef CONFIG_RISCV_SOC_CONTEXT_SAVE
|
||||||
/*
|
/*
|
||||||
* Pulpino core has hardware loops registers that need to be saved
|
* The Pulpino core has ISA extensions for faster loop performance
|
||||||
* prior to handling an interrupt/exception.
|
* that use extra registers.
|
||||||
*
|
*
|
||||||
* NOTE: Stack space allocation is not needed here, as already allocated at
|
* If the toolchain generates instructions that use them, they must be saved
|
||||||
* architecture level with __NANO_ESF_SIZEOF value (including space for the
|
* prior to handling an interrupt/exception. This case is handled using
|
||||||
* pulpino-specific registers)
|
* Zephyr's generic RISC-V mechanism for soc-specific context.
|
||||||
|
*
|
||||||
|
* For details, see the Kconfig help for CONFIG_RISCV_SOC_CONTEXT_SAVE.
|
||||||
*/
|
*/
|
||||||
SECTION_FUNC(exception.other, __soc_save_context)
|
SECTION_FUNC(exception.other, __soc_save_context)
|
||||||
/* Save hardware loop registers to stack */
|
/* Save hardware loop registers to the soc_esf_t passed in a0. */
|
||||||
csrr t0, PULP_LPSTART0
|
csrr t0, PULP_LPSTART0
|
||||||
csrr t1, PULP_LPEND0
|
csrr t1, PULP_LPEND0
|
||||||
csrr t2, PULP_LPCOUNT0
|
csrr t2, PULP_LPCOUNT0
|
||||||
sw t0, __NANO_ESF_lpstart0_OFFSET(sp)
|
sw t0, __soc_esf_t_lpstart0_OFFSET(a0)
|
||||||
sw t1, __NANO_ESF_lpend0_OFFSET(sp)
|
sw t1, __soc_esf_t_lpend0_OFFSET(a0)
|
||||||
sw t2, __NANO_ESF_lpcount0_OFFSET(sp)
|
sw t2, __soc_esf_t_lpcount0_OFFSET(a0)
|
||||||
csrr t0, PULP_LPSTART1
|
csrr t0, PULP_LPSTART1
|
||||||
csrr t1, PULP_LPEND1
|
csrr t1, PULP_LPEND1
|
||||||
csrr t2, PULP_LPCOUNT1
|
csrr t2, PULP_LPCOUNT1
|
||||||
sw t0, __NANO_ESF_lpstart1_OFFSET(sp)
|
sw t0, __soc_esf_t_lpstart1_OFFSET(a0)
|
||||||
sw t1, __NANO_ESF_lpend1_OFFSET(sp)
|
sw t1, __soc_esf_t_lpend1_OFFSET(a0)
|
||||||
sw t2, __NANO_ESF_lpcount1_OFFSET(sp)
|
sw t2, __soc_esf_t_lpcount1_OFFSET(a0)
|
||||||
|
|
||||||
/* Return */
|
/* Return */
|
||||||
jalr x0, ra
|
jalr x0, ra
|
||||||
|
|
||||||
|
|
||||||
SECTION_FUNC(exception.other, __soc_restore_context)
|
SECTION_FUNC(exception.other, __soc_restore_context)
|
||||||
/* Restore hardloop registers from stack */
|
/* Restore hardware loop registers from soc_esf_t in a0. */
|
||||||
lw t0, __NANO_ESF_lpstart0_OFFSET(sp)
|
lw t0, __soc_esf_t_lpstart0_OFFSET(a0)
|
||||||
lw t1, __NANO_ESF_lpend0_OFFSET(sp)
|
lw t1, __soc_esf_t_lpend0_OFFSET(a0)
|
||||||
lw t2, __NANO_ESF_lpcount0_OFFSET(sp)
|
lw t2, __soc_esf_t_lpcount0_OFFSET(a0)
|
||||||
csrw PULP_LPSTART0, t0
|
csrw PULP_LPSTART0, t0
|
||||||
csrw PULP_LPEND0, t1
|
csrw PULP_LPEND0, t1
|
||||||
csrw PULP_LPCOUNT0, t2
|
csrw PULP_LPCOUNT0, t2
|
||||||
lw t0, __NANO_ESF_lpstart1_OFFSET(sp)
|
lw t0, __soc_esf_t_lpstart1_OFFSET(a0)
|
||||||
lw t1, __NANO_ESF_lpend1_OFFSET(sp)
|
lw t1, __soc_esf_t_lpend1_OFFSET(a0)
|
||||||
lw t2, __NANO_ESF_lpcount1_OFFSET(sp)
|
lw t2, __soc_esf_t_lpcount1_OFFSET(a0)
|
||||||
csrw PULP_LPSTART1, t0
|
csrw PULP_LPSTART1, t0
|
||||||
csrw PULP_LPEND1, t1
|
csrw PULP_LPEND1, t1
|
||||||
csrw PULP_LPCOUNT1, t2
|
csrw PULP_LPCOUNT1, t2
|
||||||
|
|
||||||
/* Return */
|
/* Return */
|
||||||
jalr x0, ra
|
jalr x0, ra
|
||||||
|
#endif /* CONFIG_RISCV_SOC_CONTEXT_SAVE */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SOC-specific function to handle pending IRQ number generating the interrupt.
|
* SOC-specific function to handle pending IRQ number generating the interrupt.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue