From f4c3163d3bb4827a0266089495c056dd0cf982af Mon Sep 17 00:00:00 2001 From: Marti Bolivar Date: Thu, 1 Nov 2018 22:25:13 -0600 Subject: [PATCH] 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 --- arch/riscv32/Kconfig | 40 ++++++++++++++++++++++++-- arch/riscv32/core/fatal.c | 11 +++----- arch/riscv32/core/isr.S | 42 +++++++++++++++------------ arch/riscv32/core/offsets/offsets.c | 14 ++++----- include/arch/riscv32/exp.h | 32 +++++++++++++++------ soc/riscv32/pulpino/soc_context.h | 41 +++++++++++++++++++++++++++ soc/riscv32/pulpino/soc_irq.S | 44 ++++++++++++++++------------- 7 files changed, 161 insertions(+), 63 deletions(-) create mode 100644 soc/riscv32/pulpino/soc_context.h diff --git a/arch/riscv32/Kconfig b/arch/riscv32/Kconfig index 49a9661a78e9..c81dab9349c4 100644 --- a/arch/riscv32/Kconfig +++ b/arch/riscv32/Kconfig @@ -24,10 +24,44 @@ config INCLUDE_RESET_VECTOR prepares for running C code. config RISCV_SOC_CONTEXT_SAVE - bool "Enable SOC-based context saving in IRQ handler" + bool "Enable SOC-based context saving in IRQ handlers" help - Enable SOC-based context saving, for SOCS which require saving of - extra registers when entering an interrupt/exception + Enable low-level SOC-specific context management, for SOCs + 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 bool "Enable SOC-based interrupt initialization" diff --git a/arch/riscv32/core/fatal.c b/arch/riscv32/core/fatal.c index c76def963723..2145a5330f19 100644 --- a/arch/riscv32/core/fatal.c +++ b/arch/riscv32/core/fatal.c @@ -31,13 +31,10 @@ const NANO_ESF _default_esf = { 0xdeadbaad, 0xdeadbaad, 0xdeadbaad, -#if defined(CONFIG_SOC_RISCV32_PULPINO) - 0xdeadbaad, - 0xdeadbaad, - 0xdeadbaad, - 0xdeadbaad, - 0xdeadbaad, - 0xdeadbaad, +#if defined(CONFIG_RISCV_SOC_CONTEXT_SAVE) + { + SOC_ESF_INIT, + }, #endif }; diff --git a/arch/riscv32/core/isr.S b/arch/riscv32/core/isr.S index 6bd6819c4edf..e3caac4f8b24 100644 --- a/arch/riscv32/core/isr.S +++ b/arch/riscv32/core/isr.S @@ -1,5 +1,6 @@ /* * Copyright (c) 2016 Jean-Paul Etienne + * Copyright (c) 2018 Foundries.io Ltd * * SPDX-License-Identifier: Apache-2.0 */ @@ -11,11 +12,13 @@ /* imports */ GDATA(_sw_isr_table) -GTEXT(__soc_save_context) -GTEXT(__soc_restore_context) GTEXT(__soc_is_irq) GTEXT(__soc_handle_irq) 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(_is_next_thread_current) @@ -36,23 +39,24 @@ GTEXT(__irq_wrapper) /* use ABI name of registers for the sake of simplicity */ /* - * ISR is handled at both ARCH and SOC levels. - * At the ARCH level, ISR handles basic context saving/restore of registers - * 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 + * Generic architecture-level IRQ handling, along with callouts to + * SoC-specific routines. + * + * Architecture level IRQ handling includes basic context save/restore + * of standard registers and calling ISRs registered at Zephyr's driver + * level. + * + * Since RISC-V does not completely prescribe IRQ handling behavior, + * implementations vary (some implementations also deviate from + * 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) * - * Moreover, given that RISC-V architecture does not provide a clear ISA - * specification about interrupt handling, each RISC-V SOC handles it in - * its own way. Hence, the generic RISC-V ISR handler expects the following - * functions to be provided at the SOC level: - * __soc_is_irq: to check if the exception is the result of an interrupt or not. - * __soc_handle_irq: handle pending IRQ at SOC level (ex: clear pending IRQ in - * SOC-specific IRQ 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 /* Handle context saving at SOC level. */ + addi a0, sp, __NANO_ESF_soc_context_OFFSET jal ra, __soc_save_context #endif /* CONFIG_RISCV_SOC_CONTEXT_SAVE */ @@ -409,6 +414,7 @@ reschedule: no_reschedule: #ifdef CONFIG_RISCV_SOC_CONTEXT_SAVE /* Restore context at SOC level */ + addi a0, sp, __NANO_ESF_soc_context_OFFSET jal ra, __soc_restore_context #endif /* CONFIG_RISCV_SOC_CONTEXT_SAVE */ diff --git a/arch/riscv32/core/offsets/offsets.c b/arch/riscv32/core/offsets/offsets.c index 3063d4f8fe54..666e8f595994 100644 --- a/arch/riscv32/core/offsets/offsets.c +++ b/arch/riscv32/core/offsets/offsets.c @@ -17,6 +17,10 @@ #include #include +#ifdef CONFIG_RISCV_SOC_CONTEXT_SAVE +#include +#endif + /* thread_arch_t member offsets */ 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, mstatus); -#if defined(CONFIG_SOC_RISCV32_PULPINO) -GEN_OFFSET_SYM(NANO_ESF, lpstart0); -GEN_OFFSET_SYM(NANO_ESF, lpend0); -GEN_OFFSET_SYM(NANO_ESF, lpcount0); -GEN_OFFSET_SYM(NANO_ESF, lpstart1); -GEN_OFFSET_SYM(NANO_ESF, lpend1); -GEN_OFFSET_SYM(NANO_ESF, lpcount1); +#if defined(CONFIG_RISCV_SOC_CONTEXT_SAVE) +GEN_OFFSET_SYM(NANO_ESF, soc_context); +GEN_SOC_OFFSET_SYMS(); #endif /* diff --git a/include/arch/riscv32/exp.h b/include/arch/riscv32/exp.h index ec91ce59425a..7d91e2bb18c6 100644 --- a/include/arch/riscv32/exp.h +++ b/include/arch/riscv32/exp.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2016 Jean-Paul Etienne + * Copyright (c) 2018 Foundries.io Ltd * * SPDX-License-Identifier: Apache-2.0 */ @@ -22,6 +23,24 @@ extern "C" { #include #include +#ifdef CONFIG_RISCV_SOC_CONTEXT_SAVE +#include +#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 { u32_t ra; /* return address */ u32_t gp; /* global pointer */ @@ -47,18 +66,15 @@ struct __esf { u32_t mepc; /* machine exception program counter */ u32_t mstatus; /* machine status register */ -#if defined(CONFIG_SOC_RISCV32_PULPINO) - /* pulpino hardware loop registers */ - u32_t lpstart0; - u32_t lpend0; - u32_t lpcount0; - u32_t lpstart1; - u32_t lpend1; - u32_t lpcount1; +#ifdef CONFIG_RISCV_SOC_CONTEXT_SAVE + struct soc_esf soc_context; #endif }; 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 FUNC_NORETURN void _NanoFatalErrorHandler(unsigned int reason, diff --git a/soc/riscv32/pulpino/soc_context.h b/soc/riscv32/pulpino/soc_context.h new file mode 100644 index 000000000000..4033bc8151c1 --- /dev/null +++ b/soc/riscv32/pulpino/soc_context.h @@ -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 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 diff --git a/soc/riscv32/pulpino/soc_irq.S b/soc/riscv32/pulpino/soc_irq.S index fef1a002ec97..3f4b5e62dd55 100644 --- a/soc/riscv32/pulpino/soc_irq.S +++ b/soc/riscv32/pulpino/soc_irq.S @@ -1,5 +1,6 @@ /* * Copyright (c) 2016 Jean-Paul Etienne + * Copyright (c) 2018 Foundries.io Ltd * * SPDX-License-Identifier: Apache-2.0 */ @@ -19,51 +20,54 @@ GTEXT(__soc_irq_unlock) /* 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 - * prior to handling an interrupt/exception. + * The Pulpino core has ISA extensions for faster loop performance + * that use extra registers. * - * NOTE: Stack space allocation is not needed here, as already allocated at - * architecture level with __NANO_ESF_SIZEOF value (including space for the - * pulpino-specific registers) + * If the toolchain generates instructions that use them, they must be saved + * prior to handling an interrupt/exception. This case is handled using + * 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) - /* Save hardware loop registers to stack */ + /* Save hardware loop registers to the soc_esf_t passed in a0. */ csrr t0, PULP_LPSTART0 csrr t1, PULP_LPEND0 csrr t2, PULP_LPCOUNT0 - sw t0, __NANO_ESF_lpstart0_OFFSET(sp) - sw t1, __NANO_ESF_lpend0_OFFSET(sp) - sw t2, __NANO_ESF_lpcount0_OFFSET(sp) + sw t0, __soc_esf_t_lpstart0_OFFSET(a0) + sw t1, __soc_esf_t_lpend0_OFFSET(a0) + sw t2, __soc_esf_t_lpcount0_OFFSET(a0) csrr t0, PULP_LPSTART1 csrr t1, PULP_LPEND1 csrr t2, PULP_LPCOUNT1 - sw t0, __NANO_ESF_lpstart1_OFFSET(sp) - sw t1, __NANO_ESF_lpend1_OFFSET(sp) - sw t2, __NANO_ESF_lpcount1_OFFSET(sp) + sw t0, __soc_esf_t_lpstart1_OFFSET(a0) + sw t1, __soc_esf_t_lpend1_OFFSET(a0) + sw t2, __soc_esf_t_lpcount1_OFFSET(a0) /* Return */ jalr x0, ra SECTION_FUNC(exception.other, __soc_restore_context) - /* Restore hardloop registers from stack */ - lw t0, __NANO_ESF_lpstart0_OFFSET(sp) - lw t1, __NANO_ESF_lpend0_OFFSET(sp) - lw t2, __NANO_ESF_lpcount0_OFFSET(sp) + /* Restore hardware loop registers from soc_esf_t in a0. */ + lw t0, __soc_esf_t_lpstart0_OFFSET(a0) + lw t1, __soc_esf_t_lpend0_OFFSET(a0) + lw t2, __soc_esf_t_lpcount0_OFFSET(a0) csrw PULP_LPSTART0, t0 csrw PULP_LPEND0, t1 csrw PULP_LPCOUNT0, t2 - lw t0, __NANO_ESF_lpstart1_OFFSET(sp) - lw t1, __NANO_ESF_lpend1_OFFSET(sp) - lw t2, __NANO_ESF_lpcount1_OFFSET(sp) + lw t0, __soc_esf_t_lpstart1_OFFSET(a0) + lw t1, __soc_esf_t_lpend1_OFFSET(a0) + lw t2, __soc_esf_t_lpcount1_OFFSET(a0) csrw PULP_LPSTART1, t0 csrw PULP_LPEND1, t1 csrw PULP_LPCOUNT1, t2 /* Return */ jalr x0, ra - +#endif /* CONFIG_RISCV_SOC_CONTEXT_SAVE */ /* * SOC-specific function to handle pending IRQ number generating the interrupt.