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.

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

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

  6. Global interrupt disable.

  7. Restore CSR registers MEPC/MCAUSE/MSUBM.

  8. Context restore from stack for cpu registers.

  9. 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
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// Vector interrupt handler for on-board button
__INTERRUPT void SOC_BUTTON_1_HANDLER(void)
{
    // save mepc,mcause,msubm enable interrupts
    SAVE_IRQ_CSR_CONTEXT();

    printf("%s", "----Begin button1 handler----Vector mode\r\n");

    // Green LED toggle
    gpio_toggle(GPIO, SOC_LED_GREEN_GPIO_MASK);

    // Clear the GPIO Pending interrupt by writing 1.
    gpio_clear_interrupt(GPIO, SOC_BUTTON_1_GPIO_OFS, GPIO_INT_RISE);

    wait_seconds(1); // Wait for a while

    printf("%s", "----End button1 handler\r\n");

    // disable interrupts,restore mepc,mcause,msubm
    RESTORE_IRQ_CSR_CONTEXT();
}

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
2
3
4
5
6
7
static uint32_t btn_pressed = 0;
// Vector interrupt handler for on-board button
// This function is an leaf function, no function call is allowed
__INTERRUPT void SOC_BUTTON_1_HANDLER(void)
{
    btn_pressed ++;
}

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
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
/*
 * Copyright (c) 2019 Nuclei Limited. All rights reserved.
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the License); you may
 * not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an AS IS BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/******************************************************************************
 * \file     intexc_<Device>.S
 * \brief    NMSIS Interrupt and Exception Handling Template File
 *           for Nuclei N/NX Class Device
 * \version  V1.00
 * \date     25 Dec 2019
 *
 ******************************************************************************/

#include "riscv_encoding.h"

/**
 * \brief  Global interrupt disabled
 * \details
 *  This function disable global interrupt.
 * \remarks
 *  - All the interrupt requests will be ignored by CPU.
 */
.macro DISABLE_MIE
    csrc CSR_MSTATUS, MSTATUS_MIE
.endm

/**
 * \brief  Macro for context save
 * \details
 * This macro save ABI defined caller saved registers in the stack.
 * \remarks
 * - This Macro could use to save context when you enter to interrupt
 * or exception
*/
/* Save caller registers */
.macro SAVE_CONTEXT
    /* Allocate stack space for context saving */
#ifndef __riscv_32e
    addi sp, sp, -20*REGBYTES
#else
    addi sp, sp, -14*REGBYTES
#endif /* __riscv_32e */

    STORE x1, 0*REGBYTES(sp)
    STORE x4, 1*REGBYTES(sp)
    STORE x5, 2*REGBYTES(sp)
    STORE x6, 3*REGBYTES(sp)
    STORE x7, 4*REGBYTES(sp)
    STORE x10, 5*REGBYTES(sp)
    STORE x11, 6*REGBYTES(sp)
    STORE x12, 7*REGBYTES(sp)
    STORE x13, 8*REGBYTES(sp)
    STORE x14, 9*REGBYTES(sp)
    STORE x15, 10*REGBYTES(sp)
#ifndef __riscv_32e
    STORE x16, 14*REGBYTES(sp)
    STORE x17, 15*REGBYTES(sp)
    STORE x28, 16*REGBYTES(sp)
    STORE x29, 17*REGBYTES(sp)
    STORE x30, 18*REGBYTES(sp)
    STORE x31, 19*REGBYTES(sp)
#endif /* __riscv_32e */
.endm

/**
 * \brief  Macro for restore caller registers
 * \details
 * This macro restore ABI defined caller saved registers from stack.
 * \remarks
 * - You could use this macro to restore context before you want return
 * from interrupt or exeception
 */
/* Restore caller registers */
.macro RESTORE_CONTEXT
    LOAD x1, 0*REGBYTES(sp)
    LOAD x4, 1*REGBYTES(sp)
    LOAD x5, 2*REGBYTES(sp)
    LOAD x6, 3*REGBYTES(sp)
    LOAD x7, 4*REGBYTES(sp)
    LOAD x10, 5*REGBYTES(sp)
    LOAD x11, 6*REGBYTES(sp)
    LOAD x12, 7*REGBYTES(sp)
    LOAD x13, 8*REGBYTES(sp)
    LOAD x14, 9*REGBYTES(sp)
    LOAD x15, 10*REGBYTES(sp)
#ifndef __riscv_32e
    LOAD x16, 14*REGBYTES(sp)
    LOAD x17, 15*REGBYTES(sp)
    LOAD x28, 16*REGBYTES(sp)
    LOAD x29, 17*REGBYTES(sp)
    LOAD x30, 18*REGBYTES(sp)
    LOAD x31, 19*REGBYTES(sp)

    /* De-allocate the stack space */
    addi sp, sp, 20*REGBYTES
#else
    /* De-allocate the stack space */
    addi sp, sp, 14*REGBYTES
#endif /* __riscv_32e */

.endm

/**
 * \brief  Macro for save necessary CSRs to stack
 * \details
 * This macro store MCAUSE, MEPC, MSUBM to stack.
 */
.macro SAVE_CSR_CONTEXT
    /* Store CSR mcause to stack using pushmcause */
    csrrwi  x0, CSR_PUSHMCAUSE, 11
    /* Store CSR mepc to stack using pushmepc */
    csrrwi  x0, CSR_PUSHMEPC, 12
    /* Store CSR msub to stack using pushmsub */
    csrrwi  x0, CSR_PUSHMSUBM, 13
.endm

/**
 * \brief  Macro for restore necessary CSRs from stack
 * \details
 * This macro restore MSUBM, MEPC, MCAUSE from stack.
 */
.macro RESTORE_CSR_CONTEXT
    LOAD x5,  13*REGBYTES(sp)
    csrw CSR_MSUBM, x5
    LOAD x5,  12*REGBYTES(sp)
    csrw CSR_MEPC, x5
    LOAD x5,  11*REGBYTES(sp)
    csrw CSR_MCAUSE, x5
.endm

/**
 * \brief  Exception/NMI Entry
 * \details
 * This function provide common entry functions for exception/nmi.
 * \remarks
 * This function provide a default exception/nmi entry.
 * ABI defined caller save register and some CSR registers
 * to be saved before enter interrupt handler and be restored before return.
 */
.section .text.trap
/* In CLIC mode, the exeception entry must be 64bytes aligned */
.align 6
.global exc_entry
.weak exc_entry
exc_entry:
    /* Save the caller saving registers (context) */
    SAVE_CONTEXT
    /* Save the necessary CSR registers */
    SAVE_CSR_CONTEXT

    /*
     * Set the exception handler function arguments
     * argument 1: mcause value
     * argument 2: current stack point(SP) value
     */
    csrr a0, mcause
    mv a1, sp
    /*
     * TODO: Call the exception handler function
     * By default, the function template is provided in
     * system_Device.c, you can adjust it as you want
     */
    call core_exception_handler

    /* Restore the necessary CSR registers */
    RESTORE_CSR_CONTEXT
    /* Restore the caller saving registers (context) */
    RESTORE_CONTEXT

    /* Return to regular code */
    mret

/**
 * \brief  Non-Vector Interrupt Entry
 * \details
 * This function provide common entry functions for handling
 * non-vector interrupts
 * \remarks
 * This function provide a default non-vector interrupt entry.
 * ABI defined caller save register and some CSR registers need
 * to be saved before enter interrupt handler and be restored before return.
 */
.section      .text.irq
/* In CLIC mode, the interrupt entry must be 4bytes aligned */
.align 2
.global irq_entry
.weak irq_entry
/* This label will be set to MTVT2 register */
irq_entry:
    /* Save the caller saving registers (context) */
    SAVE_CONTEXT
    /* Save the necessary CSR registers */
    SAVE_CSR_CONTEXT

    /* This special CSR read/write operation, which is actually
     * claim the CLIC to find its pending highest ID, if the ID
     * is not 0, then automatically enable the mstatus.MIE, and
     * jump to its vector-entry-label, and update the link register
     */
    csrrw ra, CSR_JALMNXTI, ra

    /* Critical section with interrupts disabled */
    DISABLE_MIE

    /* Restore the necessary CSR registers */
    RESTORE_CSR_CONTEXT
    /* Restore the caller saving registers (context) */
    RESTORE_CONTEXT

    /* Return to regular code */
    mret

/* Default Handler for Exceptions / Interrupts */
.global default_intexc_handler
.weak default_intexc_handler
Undef_Handler:
default_intexc_handler:
1:
    j 1b