Interrupt and Exception Handling File: intexc_<Device>.S
Caution
Please be informed that the NMSIS-Core Device Templates may not be updated in a timely manner and thus could become outdated. We suggest referring to the specific implementation of evalsoc in the Nuclei SDK for the latest reference template. This reference template may not be actively maintained in the future.
- The intexc File intexc_<Device>.S contains:
Macro to save caller register.
Macro to restore caller register.
Default Exception/NMI routine implementation.
Default Non-Vector Interrupt routine implementation.
Nuclei processors provide NMI(Non-Maskable Interrupt), Exception, Vector Interrupt and Non-Vector Interrupt features.
Note
To provide S-Mode interrupt and exception handling feature, we also provide a template file called intexc_<Device>_s.S
NMI(Non-Maskable Interrupt)
Click NMI to learn about Nuclei Processor Core NMI in Nuclei ISA Spec.
NMI is used for urgent external HW error. It can’t be masked and disabled.
When NMI happened, bit 9 of CSR MMSIC_CTL
will be checked.
If this bit value is 1, then NMI entry address will be the same as exception(CSR_MTVEC),
and exception code for NMI will be 0xFFF, otherwise NMI entry will be same as reset_vector.
In NMSIS-Core, the bit 9 of CSR MMISC_CTL
is set to 1 during core startup,
so NMI will be treated as Exception and handled.
Exception
Click Exception to learn about Nuclei Processor Core Exception in Nuclei ISA Spec.
For CPU exception, the entry for exception will be exc_entry
, in this entry code,
it will call default exception handler core_exception_handler()
.
In the common exception routine(exc_entry
) to get more information like exception code.
Exception handle flow show as below picture:

Exception Handling Flow
NMI and exception could support nesting. Two levels of NMI/Exception state save stacks are supported.
We support three nesting mode as below:
NMI nesting exception
Exception nesting exception
Exception nesting NMI
For software, we have provided the common entry for NMI and exception. Silicon vendor only need adapt the interface defined in Interrupt Exception NMI Handling.
Context save and restore have been handled by exc_entry
interface.
When exception exception return it will run the instruction which trigger the exception again.
It will cause software dead loop. So in the exception handler for each exception code,
we propose to set CSR MEPC
to be MEPC+4
, then it will start from next instruction of MEPC.
Interrupt
Click Interrupt to learn about Nuclei Processor Core Interrupt in Nuclei Spec.
Interrupt could be configured as CLINT mode or ECILC mode.
In NMSIS-Core, Interrupt has been configured as ECLIC mode during startup in startup_<Devices>.S, which is also recommended setting using Nuclei Processors.
ECLIC managed interrupt could configured as vector and non-vector mode.
Detail interrupt handling process as below picture:

Interrupt Handling Flow
To get highest priority interrupt we need compare the interrupt level first.If level is the same then compare the priority.
High level interrupt could interrupt low level ISR and trigger interrupt nesting. If different priority with same level
interrupt pending higher priority will be served first. Interrupt could be configured as vector mode and non-vector mode by vendor.
For non-vector mode interrupt handler entry get from MTVT2 and exception/NMI handler entry get from MTVEC. If Vendor need set non vector mode
interrupt handler entry from MTVVEC
you need set MTVT2.BIT0
as 0.
Non-Vector Interrupt SW handler
For non-vector mode interrupt it will make the necessary CSR registers and context save and restore. Non-vector mode software handle flow show as below pciture:

Non-vector mode interrupt software handle flow
Detail description for non-vector mode interrupt handler as below steps:
Get non-vector mode handler entry from
MTVT2
ifMTVT2.BIT0
is 1(proposed configuration).Context save to stack for cpu registers.
Save CSR registers
MEPC/MCAUSE/MSUBM
to stack.
4. Run instruction csrrw ra, CSR_JALMNXTI, ra
. It will enable interrupt, check interrupt pending.
If interrupt is pending then get highest priority interrupt and jump to interrupt handler entry in the vector table,
otherwise it will go to step 6.
Execute the interrupt handler routine, when return from isr routine it will jump to step 4.
Global interrupt disable.
Restore CSR registers
MEPC/MCAUSE/MSUBM
.Context restore from stack for cpu registers.
Execute
mret
to return from handler.
For non-vector mode iterrupt it could support interrupt nesting.
Interrupt nesting handle flow show as below picture:

Nesting interrupt handling flow
Vector Interrupt SW handler
If vector interrupt handler need support nesting or making function call Vector mode software handling flow show as below picture:

Vector mode nesting interrupt handling flow
Detail description for nested vector mode interrupt handler as below steps:
Get vector mode handler from address of vector table entry
MTVT
added offset.Context save to stack for cpu registers, done in each vector interrupt handler via
__INTERRUPT
Save CSR registers
MEPC/MCAUSE/MSUBM
to stack, done in each vector interrupt handler by read and save these CSRs into variables.Execute the interrupt handling.
Restore CSR registers
MEPC/MCAUSE/MSUBM
from stack.CSR registers restore from saved variables used in step 3.
Execute
mret
to return from handler
Here is sample code for above nested vector interrupt handling process:
1// Vector interrupt handler for on-board button
2__INTERRUPT void SOC_BUTTON_1_HANDLER(void)
3{
4 // save mepc,mcause,msubm enable interrupts
5 SAVE_IRQ_CSR_CONTEXT();
6
7 printf("%s", "----Begin button1 handler----Vector mode\r\n");
8
9 // Green LED toggle
10 gpio_toggle(GPIO, SOC_LED_GREEN_GPIO_MASK);
11
12 // Clear the GPIO Pending interrupt by writing 1.
13 gpio_clear_interrupt(GPIO, SOC_BUTTON_1_GPIO_OFS, GPIO_INT_RISE);
14
15 wait_seconds(1); // Wait for a while
16
17 printf("%s", "----End button1 handler\r\n");
18
19 // disable interrupts,restore mepc,mcause,msubm
20 RESTORE_IRQ_CSR_CONTEXT();
21}
Detail description for non-nested vector mode interrupt handler as below
To improve the software response latency for vector mode vendor could remove context save/restore and MEPC/MCAUSE/MSUBM save/restore.
If so vector mode interrupt will not support nesting and interrupt handler can only be a leaf function which doesn’t make any function calls.
Then the vector mode interrupt software flow will be described as below:
Get vector mode handler from address of vector table entry
MTVT
added offset.Execute the interrupt handler(leaf function).
Execute
mret
to return from handler
Here is sample code for above non-nested vector interrupt handler which is a leaf function handling process:
1static uint32_t btn_pressed = 0;
2// Vector interrupt handler for on-board button
3// This function is an leaf function, no function call is allowed
4__INTERRUPT void SOC_BUTTON_1_HANDLER(void)
5{
6 btn_pressed ++;
7}
intexc_Device.S Template File
The file exists for each supported toolchain and is the only toolchain specific NMSIS file.
Normally this file needn’t adapt for different device. If CPU CSR registers have done some changes you may need some adaption.
Here we provided intexc_Device.S
template file as below:
1/*
2 * Copyright (c) 2019 Nuclei Limited. All rights reserved.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Licensed under the Apache License, Version 2.0 (the License); you may
7 * not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an AS IS BASIS, WITHOUT
14 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18/******************************************************************************
19 * \file intexc_evalsoc.S
20 * \brief NMSIS Interrupt and Exception Handling Template File
21 * for Nuclei Eval SoC which support Nuclei N/NX class cores
22 * \version V1.00
23 * \date 17 Dec 2019
24 *
25 ******************************************************************************/
26
27#include "riscv_encoding.h"
28
29/* TODO: Require Nuclei SDK >= 0.6.0, which introduced this cpufeature.h */
30#include "cpufeature.h"
31
32/**
33 * \brief Global interrupt disabled
34 * \details
35 * This function disable global interrupt.
36 * \remarks
37 * - All the interrupt requests will be ignored by CPU.
38 */
39.macro DISABLE_MIE
40 csrc CSR_MSTATUS, MSTATUS_MIE
41.endm
42
43/**
44 * \brief Macro for context save
45 * \details
46 * This macro save ABI defined caller saved registers in the stack.
47 * \remarks
48 * - This Macro could use to save context when you enter to interrupt
49 * or exception
50*/
51/* Save caller registers */
52.macro SAVE_CONTEXT
53 /* Allocate stack space for context saving */
54#ifndef __riscv_32e
55 addi sp, sp, -20*REGBYTES
56#else
57 addi sp, sp, -14*REGBYTES
58#endif /* __riscv_32e */
59
60 STORE x1, 0*REGBYTES(sp)
61 STORE x4, 1*REGBYTES(sp)
62 STORE x5, 2*REGBYTES(sp)
63 STORE x6, 3*REGBYTES(sp)
64 STORE x7, 4*REGBYTES(sp)
65 STORE x10, 5*REGBYTES(sp)
66 STORE x11, 6*REGBYTES(sp)
67 STORE x12, 7*REGBYTES(sp)
68 STORE x13, 8*REGBYTES(sp)
69 STORE x14, 9*REGBYTES(sp)
70 STORE x15, 10*REGBYTES(sp)
71#ifndef __riscv_32e
72 STORE x16, 14*REGBYTES(sp)
73 STORE x17, 15*REGBYTES(sp)
74 STORE x28, 16*REGBYTES(sp)
75 STORE x29, 17*REGBYTES(sp)
76 STORE x30, 18*REGBYTES(sp)
77 STORE x31, 19*REGBYTES(sp)
78#endif /* __riscv_32e */
79.endm
80
81/**
82 * \brief Macro for restore caller registers
83 * \details
84 * This macro restore ABI defined caller saved registers from stack.
85 * \remarks
86 * - You could use this macro to restore context before you want return
87 * from interrupt or exeception
88 */
89/* Restore caller registers */
90.macro RESTORE_CONTEXT
91 LOAD x1, 0*REGBYTES(sp)
92 LOAD x4, 1*REGBYTES(sp)
93 LOAD x5, 2*REGBYTES(sp)
94 LOAD x6, 3*REGBYTES(sp)
95 LOAD x7, 4*REGBYTES(sp)
96 LOAD x10, 5*REGBYTES(sp)
97 LOAD x11, 6*REGBYTES(sp)
98 LOAD x12, 7*REGBYTES(sp)
99 LOAD x13, 8*REGBYTES(sp)
100 LOAD x14, 9*REGBYTES(sp)
101 LOAD x15, 10*REGBYTES(sp)
102#ifndef __riscv_32e
103 LOAD x16, 14*REGBYTES(sp)
104 LOAD x17, 15*REGBYTES(sp)
105 LOAD x28, 16*REGBYTES(sp)
106 LOAD x29, 17*REGBYTES(sp)
107 LOAD x30, 18*REGBYTES(sp)
108 LOAD x31, 19*REGBYTES(sp)
109
110 /* De-allocate the stack space */
111 addi sp, sp, 20*REGBYTES
112#else
113 /* De-allocate the stack space */
114 addi sp, sp, 14*REGBYTES
115#endif /* __riscv_32e */
116
117.endm
118
119/**
120 * \brief Macro for save necessary CSRs to stack
121 * \details
122 * This macro store MCAUSE, MEPC, MSUBM to stack.
123 */
124.macro SAVE_CSR_CONTEXT
125#ifdef CFG_HAS_CLIC
126 /* Store CSR mcause to stack using pushmcause */
127 csrrwi x0, CSR_PUSHMCAUSE, 11
128 /* Store CSR mepc to stack using pushmepc */
129 csrrwi x0, CSR_PUSHMEPC, 12
130 /* Store CSR msub to stack using pushmsub */
131 csrrwi x0, CSR_PUSHMSUBM, 13
132#else
133 csrr x5, CSR_MEPC
134 STORE x5, 12*REGBYTES(sp)
135 csrr x5, CSR_MCAUSE
136 STORE x5, 11*REGBYTES(sp)
137#endif
138.endm
139
140/**
141 * \brief Macro for restore necessary CSRs from stack
142 * \details
143 * This macro restore MSUBM, MEPC, MCAUSE from stack.
144 */
145.macro RESTORE_CSR_CONTEXT
146#ifdef CFG_HAS_CLIC
147 LOAD x5, 13*REGBYTES(sp)
148 csrw CSR_MSUBM, x5
149#endif
150 LOAD x5, 12*REGBYTES(sp)
151 csrw CSR_MEPC, x5
152 LOAD x5, 11*REGBYTES(sp)
153 csrw CSR_MCAUSE, x5
154.endm
155
156/**
157 * \brief Exception/NMI Entry
158 * \details
159 * This function provide common entry functions for exception/nmi and interrupt of plic mode.
160 * \remarks
161 * This function provide a default exception/nmi entry.
162 * ABI defined caller save register and some CSR registers
163 * to be saved before enter interrupt handler and be restored before return.
164 */
165.section .text.trap
166/* In CLIC mode, the exeception entry must be 64bytes aligned */
167.align 6
168# gnu let .weak override .globl, but llvm will show warning
169# see https://reviews.llvm.org/D90108
170.weak exc_entry
171.type exc_entry, @function
172exc_entry:
173 /* Save the caller saving registers (context) */
174 SAVE_CONTEXT
175 /* Save the necessary CSR registers */
176 SAVE_CSR_CONTEXT
177
178 /*
179 * Set the exception handler function arguments
180 * argument 1: mcause value
181 * argument 2: current stack point(SP) value
182 */
183 csrr a0, mcause
184 mv a1, sp
185 /*
186 * TODO: Call the exception handler function
187 * By default, the function template is provided in
188 * system_Device.c, you can adjust it as you want
189 */
190 call core_exception_handler
191
192 /* Restore the necessary CSR registers */
193 RESTORE_CSR_CONTEXT
194 /* Restore the caller saving registers (context) */
195 RESTORE_CONTEXT
196
197 /* Return to regular code */
198 mret
199
200 .size exc_entry, . - exc_entry
201
202/**
203 * \brief Non-Vector Interrupt Entry
204 * \details
205 * This function provide common entry functions for handling
206 * non-vector interrupts
207 * \remarks
208 * This function provide a default non-vector interrupt entry.
209 * ABI defined caller save register and some CSR registers need
210 * to be saved before enter interrupt handler and be restored before return.
211 */
212.section .text.irq
213/* In CLIC mode, the interrupt entry must be 4bytes aligned */
214.align 2
215# gnu let .weak override .globl, but llvm will show warning
216# see https://reviews.llvm.org/D90108
217.weak irq_entry
218.type irq_entry, @function
219/* This label will be set to MTVT2 register */
220irq_entry:
221 /* Save the caller saving registers (context) */
222 SAVE_CONTEXT
223 /* Save the necessary CSR registers */
224 SAVE_CSR_CONTEXT
225
226 /* This special CSR read/write operation, which is actually
227 * claim the CLIC to find its pending highest ID, if the ID
228 * is not 0, then automatically enable the mstatus.MIE, and
229 * jump to its vector-entry-label, and update the link register
230 */
231 csrrw ra, CSR_JALMNXTI, ra
232
233 /* Critical section with interrupts disabled */
234 DISABLE_MIE
235
236 /* Restore the necessary CSR registers */
237 RESTORE_CSR_CONTEXT
238 /* Restore the caller saving registers (context) */
239 RESTORE_CONTEXT
240
241 /* Return to regular code */
242 mret
243
244 .size irq_entry, . - irq_entry
245
246/* Default Handler for Exceptions / Interrupts */
247# gnu let .weak override .globl, but llvm will show warning
248# see https://reviews.llvm.org/D90108
249.weak default_intexc_handler
250.type default_intexc_handler, @function
251Undef_Handler:
252default_intexc_handler:
2531:
254 j 1b
255
256 .size default_intexc_handler, . - default_intexc_handler