Skip to content

Commit

Permalink
arch: riscv32: provide a general mechanism for saving SoC context
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
Marti Bolivar authored and nashif committed Dec 5, 2018
1 parent b85d893 commit f4c3163
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 63 deletions.
40 changes: 37 additions & 3 deletions arch/riscv32/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
11 changes: 4 additions & 7 deletions arch/riscv32/core/fatal.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
};

Expand Down
42 changes: 24 additions & 18 deletions arch/riscv32/core/isr.S
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2016 Jean-Paul Etienne <[email protected]>
* Copyright (c) 2018 Foundries.io Ltd
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand All @@ -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)
Expand All @@ -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.
*/

/*
Expand Down Expand Up @@ -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 */

Expand Down Expand Up @@ -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 */

Expand Down
14 changes: 7 additions & 7 deletions arch/riscv32/core/offsets/offsets.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
#include <kernel_structs.h>
#include <kernel_offsets.h>

#ifdef CONFIG_RISCV_SOC_CONTEXT_SAVE
#include <soc_context.h>
#endif

/* thread_arch_t member offsets */
GEN_OFFSET_SYM(_thread_arch_t, swap_return_value);

Expand Down Expand Up @@ -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

/*
Expand Down
32 changes: 24 additions & 8 deletions include/arch/riscv32/exp.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2016 Jean-Paul Etienne <[email protected]>
* Copyright (c) 2018 Foundries.io Ltd
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand All @@ -22,6 +23,24 @@ extern "C" {
#include <zephyr/types.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 {
u32_t ra; /* return address */
u32_t gp; /* global pointer */
Expand All @@ -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,
Expand Down
41 changes: 41 additions & 0 deletions soc/riscv32/pulpino/soc_context.h
Original file line number Diff line number Diff line change
@@ -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
44 changes: 24 additions & 20 deletions soc/riscv32/pulpino/soc_irq.S
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2016 Jean-Paul Etienne <[email protected]>
* Copyright (c) 2018 Foundries.io Ltd
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand All @@ -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.
Expand Down

0 comments on commit f4c3163

Please sign in to comment.