Interrupt and Exception Handling File: intexc_<Device>.S

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

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

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

Non-vector mode interrupt software handle flow

Detail description for non-vector mode interrupt handler as below steps:

  1. Get non-vector mode handler entry from MTVT2 if MTVT2.BIT0 is 1(proposed configuration).

  2. Context save to stack for cpu registers.

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

  1. Execute the interrupt handler routine, when return from isr routine it will jump to step 4.

  2. Global interrupt disable.

  3. Restore CSR registers MEPC/MCAUSE/MSUBM.

  4. Context restore from stack for cpu registers.

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

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

Vector mode nesting interrupt handling flow

Detail description for nested vector mode interrupt handler as below steps:

  1. Get vector mode handler from address of vector table entry MTVT added offset.

  2. Context save to stack for cpu registers, done in each vector interrupt handler via __INTERRUPT

  3. Save CSR registers MEPC/MCAUSE/MSUBM to stack, done in each vector interrupt handler by read and save these CSRs into variables.

  4. Execute the interrupt handling.

  5. Restore CSR registers MEPC/MCAUSE/MSUBM from stack.

  6. CSR registers restore from saved variables used in step 3.

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

  1. Get vector mode handler from address of vector table entry MTVT added offset.

  2. Execute the interrupt handler(leaf function).

  3. Execute mret to return from handler

Here is sample code for above non-nested vector interrupt handler which is a leaf function handling process:

1 static 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_<Device>.S
 20  * \brief    NMSIS Interrupt and Exception Handling Template File
 21  *           for Device <Device>
 22  * \version  V2.1.0
 23  * \date     19. Dec 2023
 24  *
 25  ******************************************************************************/
 26 
 27 #include "riscv_encoding.h"
 28 
 29 /**
 30  * \brief  Global interrupt disabled
 31  * \details
 32  *  This function disable global interrupt.
 33  * \remarks
 34  *  - All the interrupt requests will be ignored by CPU.
 35  */
 36 .macro DISABLE_MIE
 37     csrc CSR_MSTATUS, MSTATUS_MIE
 38 .endm
 39 
 40 /**
 41  * \brief  Macro for context save
 42  * \details
 43  * This macro save ABI defined caller saved registers in the stack.
 44  * \remarks
 45  * - This Macro could use to save context when you enter to interrupt
 46  * or exception
 47 */
 48 /* Save caller registers */
 49 .macro SAVE_CONTEXT
 50     /* Allocate stack space for context saving */
 51 #ifndef __riscv_32e
 52     addi sp, sp, -20*REGBYTES
 53 #else
 54     addi sp, sp, -14*REGBYTES
 55 #endif /* __riscv_32e */
 56 
 57     STORE x1, 0*REGBYTES(sp)
 58     STORE x4, 1*REGBYTES(sp)
 59     STORE x5, 2*REGBYTES(sp)
 60     STORE x6, 3*REGBYTES(sp)
 61     STORE x7, 4*REGBYTES(sp)
 62     STORE x10, 5*REGBYTES(sp)
 63     STORE x11, 6*REGBYTES(sp)
 64     STORE x12, 7*REGBYTES(sp)
 65     STORE x13, 8*REGBYTES(sp)
 66     STORE x14, 9*REGBYTES(sp)
 67     STORE x15, 10*REGBYTES(sp)
 68 #ifndef __riscv_32e
 69     STORE x16, 14*REGBYTES(sp)
 70     STORE x17, 15*REGBYTES(sp)
 71     STORE x28, 16*REGBYTES(sp)
 72     STORE x29, 17*REGBYTES(sp)
 73     STORE x30, 18*REGBYTES(sp)
 74     STORE x31, 19*REGBYTES(sp)
 75 #endif /* __riscv_32e */
 76 .endm
 77 
 78 /**
 79  * \brief  Macro for restore caller registers
 80  * \details
 81  * This macro restore ABI defined caller saved registers from stack.
 82  * \remarks
 83  * - You could use this macro to restore context before you want return
 84  * from interrupt or exeception
 85  */
 86 /* Restore caller registers */
 87 .macro RESTORE_CONTEXT
 88     LOAD x1, 0*REGBYTES(sp)
 89     LOAD x4, 1*REGBYTES(sp)
 90     LOAD x5, 2*REGBYTES(sp)
 91     LOAD x6, 3*REGBYTES(sp)
 92     LOAD x7, 4*REGBYTES(sp)
 93     LOAD x10, 5*REGBYTES(sp)
 94     LOAD x11, 6*REGBYTES(sp)
 95     LOAD x12, 7*REGBYTES(sp)
 96     LOAD x13, 8*REGBYTES(sp)
 97     LOAD x14, 9*REGBYTES(sp)
 98     LOAD x15, 10*REGBYTES(sp)
 99 #ifndef __riscv_32e
100     LOAD x16, 14*REGBYTES(sp)
101     LOAD x17, 15*REGBYTES(sp)
102     LOAD x28, 16*REGBYTES(sp)
103     LOAD x29, 17*REGBYTES(sp)
104     LOAD x30, 18*REGBYTES(sp)
105     LOAD x31, 19*REGBYTES(sp)
106 
107     /* De-allocate the stack space */
108     addi sp, sp, 20*REGBYTES
109 #else
110     /* De-allocate the stack space */
111     addi sp, sp, 14*REGBYTES
112 #endif /* __riscv_32e */
113 
114 .endm
115 
116 /**
117  * \brief  Macro for save necessary CSRs to stack
118  * \details
119  * This macro store MCAUSE, MEPC, MSUBM to stack.
120  */
121 .macro SAVE_CSR_CONTEXT
122     /* Store CSR mcause to stack using pushmcause */
123     csrrwi  x0, CSR_PUSHMCAUSE, 11
124     /* Store CSR mepc to stack using pushmepc */
125     csrrwi  x0, CSR_PUSHMEPC, 12
126     /* Store CSR msub to stack using pushmsub */
127     csrrwi  x0, CSR_PUSHMSUBM, 13
128 .endm
129 
130 /**
131  * \brief  Macro for restore necessary CSRs from stack
132  * \details
133  * This macro restore MSUBM, MEPC, MCAUSE from stack.
134  */
135 .macro RESTORE_CSR_CONTEXT
136     LOAD x5,  13*REGBYTES(sp)
137     csrw CSR_MSUBM, x5
138     LOAD x5,  12*REGBYTES(sp)
139     csrw CSR_MEPC, x5
140     LOAD x5,  11*REGBYTES(sp)
141     csrw CSR_MCAUSE, x5
142 .endm
143 
144 /**
145  * \brief  Exception/NMI Entry
146  * \details
147  * This function provide common entry functions for exception/nmi.
148  * \remarks
149  * This function provide a default exception/nmi entry.
150  * ABI defined caller save register and some CSR registers
151  * to be saved before enter interrupt handler and be restored before return.
152  */
153 .section .text.trap
154 /* In CLIC mode, the exeception entry must be 64bytes aligned */
155 .align 6
156 # gnu let .weak override .globl, but llvm will show warning
157 # see https://reviews.llvm.org/D90108
158 .weak exc_entry
159 exc_entry:
160     /* Save the caller saving registers (context) */
161     SAVE_CONTEXT
162     /* Save the necessary CSR registers */
163     SAVE_CSR_CONTEXT
164 
165     /*
166      * Set the exception handler function arguments
167      * argument 1: mcause value
168      * argument 2: current stack point(SP) value
169      */
170     csrr a0, mcause
171     mv a1, sp
172     /*
173      * TODO: Call the exception handler function
174      * By default, the function template is provided in
175      * system_Device.c, you can adjust it as you want
176      */
177     call core_exception_handler
178 
179     /* Restore the necessary CSR registers */
180     RESTORE_CSR_CONTEXT
181     /* Restore the caller saving registers (context) */
182     RESTORE_CONTEXT
183 
184     /* Return to regular code */
185     mret
186 
187 /**
188  * \brief  Non-Vector Interrupt Entry
189  * \details
190  * This function provide common entry functions for handling
191  * non-vector interrupts
192  * \remarks
193  * This function provide a default non-vector interrupt entry.
194  * ABI defined caller save register and some CSR registers need
195  * to be saved before enter interrupt handler and be restored before return.
196  */
197 .section      .text.irq
198 /* In CLIC mode, the interrupt entry must be 4bytes aligned */
199 .align 2
200 # gnu let .weak override .globl, but llvm will show warning
201 # see https://reviews.llvm.org/D90108
202 .weak irq_entry
203 /* This label will be set to MTVT2 register */
204 irq_entry:
205     /* Save the caller saving registers (context) */
206     SAVE_CONTEXT
207     /* Save the necessary CSR registers */
208     SAVE_CSR_CONTEXT
209 
210     /* This special CSR read/write operation, which is actually
211      * claim the CLIC to find its pending highest ID, if the ID
212      * is not 0, then automatically enable the mstatus.MIE, and
213      * jump to its vector-entry-label, and update the link register
214      */
215     csrrw ra, CSR_JALMNXTI, ra
216 
217     /* Critical section with interrupts disabled */
218     DISABLE_MIE
219 
220     /* Restore the necessary CSR registers */
221     RESTORE_CSR_CONTEXT
222     /* Restore the caller saving registers (context) */
223     RESTORE_CONTEXT
224 
225     /* Return to regular code */
226     mret
227 
228 /* Default Handler for Exceptions / Interrupts */
229 # gnu let .weak override .globl, but llvm will show warning
230 # see https://reviews.llvm.org/D90108
231 .weak default_intexc_handler
232 Undef_Handler:
233 default_intexc_handler:
234 1:
235     j 1b