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:
Marti Bolivar 2018-11-01 22:25:13 -06:00 committed by Anas Nashif
commit f4c3163d3b
7 changed files with 161 additions and 63 deletions

View file

@ -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"

View file

@ -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
}; };

View file

@ -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 */

View file

@ -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
/* /*

View file

@ -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,

View 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

View file

@ -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.