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.