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:

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_<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
159exc_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 */
204irq_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
232Undef_Handler:
233default_intexc_handler:
2341:
235    j 1b