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

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_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