概述
定时器应用demo_timer application展示了如何使用定时器中断和软件中断,实现了如下功能:
- 所有中断均注册为非向量中断
- 定时器中断运行5次
- 软件中断运行5次
中断与异常
在RISC-V体系架构中,中断处理和异常处理都属于异常。严格来说,异常分为同步异常和异步异常(中断)。同步异常是处理器执行某条指令而直接导致的异常,如执行非法指令,对齐错误,系统调用ECALL,调试异常等。异步异常是处理器顺序执行程序指令的过程中突然被打断,其与当前正在执行的指令无关,中断属于异步异常的一种。因此通常所说的术语-异常大多数指的是同步异常,而异步异常通常就使用术语-中断。
ECLIC
ECLIC是芯来在RISC-V标准CLIC基础上优化而来的改进型内核中断控制器(Enhanced Core Local Interrupt Controller,ECLIC),用于管理所有的中断源。ECLIC用于对多个内部和外部中断源进行仲裁、发送请求并支持中断嵌套。
ECLIC为每个中断源分配了一个独一无二的编号(ID),其中ID=3
为软件中断,ID=7
为定时器中断,ID=19-4095
为外部中断。
ECLIC的每个中断源均可以设置成向量或者非向量处理:
- 如果被配置成为向量处理模式,则该中断被处理器内核响应后,处理器直接跳入该中断的向量入口(Vector Table Entry)存储的目标地址。
- 如果被配置成为非向量处理模式,则该中断被处理器内核响应后,处理器直接跳入所有中断共享的入口地址。
ECLIC是一个存储器地址映射的单元,寄存器有:
cliccfg
:全局性的配置寄存器,用于指定clicintctl[i]
寄存器中Level域的比特数clicinfo
:全局性的信息寄存器,如硬件支持的中断源的数量,硬件实现的版本号等mth
:中断目标的阈值级别寄存器,ECLIC最终仲裁出的中断的“级别(Level)数字值”只有高于mth配置的值,该中断才能够被发送给处理器内核clicintip[i]
:中断源的等待标志寄存器,如果该位为高,则表示该中断源被触发clicintie[i]
:中断源的使能寄存器,使能和关闭中断clicintattr[i]
:中断源的属性寄存器,配置中断源的电平或边沿属性,中断源使用向量处理模式还是非向量处理模式
代码分析
使用qemu模拟定时器应用,输出的日志结果如下:
GDB Server listening on: 'tcp::1234'...
Nuclei SDK Build Time: Mar 1 2024, 10:23:49
Download Mode: ILM
CPU Frequency 1000004648 Hz
CPU HartID: 0
init timer and start
MTimer IRQ handler 1
MTimer IRQ handler 2
MTimer IRQ handler 3
MTimer IRQ handler 4
MTimer IRQ handler 5
MTimer SW IRQ handler 1
MTimer SW IRQ handler 2
MTimer SW IRQ handler 3
MTimer SW IRQ handler 4
MTimer SW IRQ handler 5
MTimer msip and mtip interrupt test finish and pass
应用主函数代码如下:
// See LICENSE for license details.
#include <stdio.h>
#include "nuclei_sdk_soc.h"
/* Define the interrupt handler name same as vector table in case download mode is flashxip. */
#define mtimer_irq_handler eclic_mtip_handler
#define mtimer_sw_irq_handler eclic_msip_handler
static volatile uint32_t int0_cnt = 0; /* mtip timer interrupt test counter */
static volatile uint32_t int1_cnt = 0; /* msip timer interrupt test counter */
volatile unsigned int msip_trig_flag = 1; /* sw trigger mtimer sw interrupt flag */
#define LOOP_COUNT 5
void mtimer_irq_handler(void)
{
int0_cnt++;
printf("MTimer IRQ handler %d\n\r", int0_cnt);
uint64_t now = SysTimer_GetLoadValue();
SysTimer_SetCompareValue(now + SOC_TIMER_FREQ / 10);
}
void mtimer_sw_irq_handler(void)
{
SysTimer_ClearSWIRQ();
int1_cnt++;
printf("MTimer SW IRQ handler %d\n\r", int1_cnt);
msip_trig_flag = 1;
}
void setup_timer()
{
printf("init timer and start\n\r");
uint64_t now = SysTimer_GetLoadValue();
uint64_t then = now + SOC_TIMER_FREQ / 10;
SysTimer_SetCompareValue(then);
}
int main(void)
{
uint32_t returnCode;
returnCode = ECLIC_Register_IRQ(
SysTimer_IRQn, ECLIC_NON_VECTOR_INTERRUPT, ECLIC_LEVEL_TRIGGER, 1, 0,
(void *)mtimer_irq_handler); /* register system timer interrupt */
__enable_irq(); /* enable global interrupt */
setup_timer(); /* initialize timer */
while (int0_cnt < 5);
ECLIC_DisableIRQ(SysTimer_IRQn); /* Disable MTIP iterrupt */
returnCode = ECLIC_Register_IRQ(
SysTimerSW_IRQn, ECLIC_NON_VECTOR_INTERRUPT,
ECLIC_POSTIVE_EDGE_TRIGGER, 2, 0,
(void *)mtimer_sw_irq_handler); /* register system timer SW interrupt */
do {
if (msip_trig_flag == 1) {
msip_trig_flag = 0;
SysTimer_SetSWIRQ(); /* trigger timer sw interrupt */
delay_1ms(10);
}
} while (int1_cnt < 5); /* check test end condition */
printf("MTimer msip and mtip interrupt test finish and pass\r\n");
if (returnCode != 0) { /* Check return code for errors */
return -1;
}
return 0;
}
ECLIC_Register_IRQ(SysTimer_IRQn, ...)
:注册定时器中断SysTimer_IRQn
,中断号为7,非向量中断模式,电平触发,中断源的级别Level和优先级Priority分别为1和0,中断处理函数为mtimer_irq_handler
__enable_irq
:使能全局中断setup_timer
:初始化定时器while (int0_cnt < 5)
:等待产生5次定时器中断ECLIC_DisableIRQ(SysTimer_IRQn)
:关闭定时器中断ECLIC_Register_IRQ(SysTimerSW_IRQn, ...)
:接着注册软件中断SysTimerSW_IRQn
,中断号为3,非向量中断模式,上升沿触发,中断源的级别Level和优先级Priority分别为2和0,中断处理函数为mtimer_sw_irq_handler
SysTimer_SetSWIRQ
:每隔10ms去触发软件中断,总共触发5次,至此结束
注册中断函数
ECLIC中断注册函数用来配置中断向量模式,触发模式,优先级等,实现如下:
/**
* \brief Initialize a specific IRQ and register the handler
* \details
* This function set vector mode, trigger mode and polarity, interrupt level and priority,
* assign handler for specific IRQn.
* \param [in] IRQn NMI interrupt handler address
* \param [in] shv \ref ECLIC_NON_VECTOR_INTERRUPT means non-vector mode, and \ref ECLIC_VECTOR_INTERRUPT is vector mode
* \param [in] trig_mode see \ref ECLIC_TRIGGER_Type
* \param [in] lvl interupt level
* \param [in] priority interrupt priority
* \param [in] handler interrupt handler, if NULL, handler will not be installed
* \return -1 means invalid input parameter. 0 means successful.
* \remarks
* - This function use to configure specific eclic interrupt and register its interrupt handler and enable its interrupt.
* - If the vector table is placed in read-only section(FLASHXIP mode), handler could not be installed
*/
int32_t ECLIC_Register_IRQ(IRQn_Type IRQn, uint8_t shv, ECLIC_TRIGGER_Type trig_mode, uint8_t lvl, uint8_t priority, void* handler)
{
if ((IRQn > SOC_INT_MAX) || (shv > ECLIC_VECTOR_INTERRUPT) \
|| (trig_mode > ECLIC_NEGTIVE_EDGE_TRIGGER)) {
return -1;
}
/* set interrupt vector mode */
ECLIC_SetShvIRQ(IRQn, shv);
/* set interrupt trigger mode and polarity */
ECLIC_SetTrigIRQ(IRQn, trig_mode);
/* set interrupt level */
ECLIC_SetLevelIRQ(IRQn, lvl);
/* set interrupt priority */
ECLIC_SetPriorityIRQ(IRQn, priority);
if (handler != NULL) {
/* set interrupt handler entry to vector table */
ECLIC_SetVector(IRQn, (rv_csr_t)handler);
}
/* enable interrupt */
ECLIC_EnableIRQ(IRQn);
return 0;
}
-
ECLIC_SetShvIRQ
:配置中断的向量模式 -
ECLIC_SetTrigIRQ
:配置中断的触发模式 -
ECLIC_SetLevelIRQ
:配置中断的级别Level -
ECLIC_SetPriorityIRQ
:配置中断的优先级Priority -
ECLIC_SetVector
:配置中断的处理入口函数,实现如下,__STATIC_FORCEINLINE void __ECLIC_SetVector(IRQn_Type IRQn, rv_csr_t vector) { volatile unsigned long vec_base; vec_base = ((unsigned long)__RV_CSR_READ(CSR_MTVT)); vec_base += ((unsigned long)IRQn) * sizeof(unsigned long); (* (unsigned long *) vec_base) = vector; }
首先从
CSR_MTVT
获取中断向量的基地址,这个是startup_evalsoc.S
汇编代码配置的vector_base
,这个地址位于代码段的起始位置,根据链接脚本中的定义,其值为0x80000000
。/* * Intialize ECLIC vector interrupt * base address mtvt to vector_base */ la t0, vector_base csrw CSR_MTVT, t0
MEMORY { ilm (rxa!w) : ORIGIN = 0x80000000, LENGTH = 64K ram (wxa!r) : ORIGIN = 0x90000000, LENGTH = 64K }
假设此时配置定时器中断
IRQn=7
,由于每个中断向量分配4字节,这里就相当于配置0x8000001c
处的中断复位处理程序为mtimer_irq_handler
,使用GDB调试看下:(gdb) b __ECLIC_SetVector Breakpoint 1 at 0x80001172: file ../../../NMSIS/Core/Include/core_feature_eclic.h, line 766. (gdb) target remote localhost:1234 Remote debugging using localhost:1234 0x00001004 in ?? () (gdb) c Continuing. Breakpoint 1, __ECLIC_SetVector (vector=2147488692, IRQn=SysTimer_IRQn) at ../../../NMSIS/Core/Include/core_feature_eclic.h:766 766 vec_base = ((unsigned long)__RV_CSR_READ(CSR_MTVT)); (gdb) p /x IRQn $1 = 0x7 (gdb) n 767 vec_base += ((unsigned long)IRQn) * sizeof(unsigned long); (gdb) p /x vec_base $2 = 0x80000000 (gdb) n 768 (* (unsigned long *) vec_base) = vector; (gdb) p /x vec_base $3 = 0x8000001c (gdb)
-
ECLIC_EnableIRQ(IRQn)
:使能定时器中断
创建定时器
setup_timer
用于创建定时器,实现10ms后产生定时器中断,实现如下
void setup_timer()
{
printf("init timer and start\n\r");
uint64_t now = SysTimer_GetLoadValue();
uint64_t then = now + SOC_TIMER_FREQ / 10;
SysTimer_SetCompareValue(then);
}
SysTimer_GetLoadValue
:获取系统定时器当前值,TIMER中实现了一个64位的mtime
寄存器,该寄存器反映了64位计时器的值SysTimer_SetCompareValue
:配置系统定时器的比较值,TIMER中实现了一个64位的mtimecmp
寄存器,该寄存器作为计时器的比较值,假设计时器的值mtime
大于或者等于mtimecmp
的值,则产生定时器中断
定时器中断处理函数
在定时器中断处理函数中,再次配置10ms后产生定时器中断。
void mtimer_irq_handler(void)
{
int0_cnt++;
printf("MTimer IRQ handler %d\n\r", int0_cnt);
uint64_t now = SysTimer_GetLoadValue();
SysTimer_SetCompareValue(now + SOC_TIMER_FREQ / 10);
}
软件中断处理函数
在软件中断处理函数中,关闭中断,触发标志置1。
void mtimer_sw_irq_handler(void)
{
SysTimer_ClearSWIRQ();
int1_cnt++;
printf("MTimer SW IRQ handler %d\n\r", int1_cnt);
msip_trig_flag = 1;
}
中断处理流程
在startup_evalsoc.S
汇编代码中配置了非向量中断的入口函数为irq_entry
,并且和异常的入口地址(由mtvec
的值指定)分开:
/*
* Set ECLIC non-vector entry to be controlled
* by mtvt2 CSR register.
* Intialize ECLIC non-vector interrupt
* base address mtvt2 to irq_entry.
*/
la t0, irq_entry
csrw CSR_MTVT2, t0
csrs CSR_MTVT2, 0x1
mtvt2
用于指定ECLIC非向量模式的中断入口地址,最低位置1表示非向量模式中断入口地址由mtvt2.COMMON-CODE-ENTRY
决定。
非向量中断入口地址函数irq_entry
实现位于intexc_evalsoc.S
文件中:
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
-
SAVE_CONTEXT
:保存调用者的上下文,主要是通用寄存器x1-x7,x10-x17,x28-x31。(x8-x9,x19-x27是函数调用过程中使用的寄存器,这里是中断,不需要保存)/* 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
-
SAVE_CSR_CONTEXT
:保存CSR寄存器,主要是mcause
、mepc
和msubm
,保存这几个CSR寄存器是为了保证后续的中断嵌套能够功能正确,因为新的中断响应会重新覆盖mepc、mcause、msubm的值,因此需要将它们先保存入堆栈.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
-
csrrw ra, CSR_JALMNXTI, ra
:这是芯来自定义的一条特殊指令,如果有中断在等待(Pending),执行该指令后处理器会:- 直接跳入该中断的向量入口(Vector Table Entry)存储的目标地址,即该中断源的中断服务程序(Interrupt Service Routine,ISR)中去。
- 在跳入中断服务程序的同时,硬件也会同时打开中断的全局使能,即,设置mstatus寄存器的MIE域为1。打开中断全局使能后,新的中断便可以被响应,从而达到中断嵌套的效果。
- 在跳入中断服务程序的同时,
csrrw ra, CSR_JALMNXTI, ra
指令还会达到JAL(Jump and Link)的效果,硬件同时更新Link寄存器的值为该指令的PC自身作为函数调用的返回地址。因此,从中断服务程序函数返回后会回到该csrrw ra, CSR_JALMNXTI, ra
指令重新执行,重新判断是否还有中断在等待(Pending),从而达到中断咬尾的效果。
-
DISABLE_MIE
:由于接下来需要恢复CSR和上下文,这里关闭全局中断.macro DISABLE_MIE csrc CSR_MSTATUS, MSTATUS_MIE .endm
-
RESTORE_CSR_CONTEXT
:恢复CSR寄存器 -
RESTORE_CONTEXT
:恢复调用上下文 -
mret
:退出中断服务程序, 并返回主程序
使用GDB和qemu调试一下中断处理流程,当产生定时器中断时,跳转到irq_entry
函数,执行上下文和CSR寄存器保存后,跳转到中断处理复位函数mtimer_irq_handler
中,处理完成后返回到irq_entry
继续执行,恢复上下文和CSR寄存器,最终返回到主函数中。
(gdb) target remote localhost:1234
Remote debugging using localhost:1234
0x00001004 in ?? ()
(gdb) b irq_entry
Breakpoint 1 at 0x80000364
(gdb) b eclic_mtip_handler
Breakpoint 2 at 0x800013d0: file main.c, line 16.
(gdb) c
Continuing.
Breakpoint 1, 0x80000364 in irq_entry ()
(gdb) set disassemble-next-line on
(gdb) si
0x80000368 in irq_entry ()
=> 0x80000368 <irq_entry+40>: 73 50 f6 7e csrwi 0x7ef,12
(gdb)
0x8000036c in irq_entry ()
=> 0x8000036c <irq_entry+44>: 73 d0 b6 7e csrwi 0x7eb,13
(gdb)
0x80000370 in irq_entry ()
=> 0x80000370 <irq_entry+48>: f3 90 d0 7e csrrw ra,0x7ed,ra
(gdb)
Breakpoint 2, eclic_mtip_handler () at main.c:16
16 int0_cnt++;
=> 0x800013d0 <eclic_mtip_handler+28>: 83 a7 c1 be lw a5,-1044(gp)
0x800013d4 <eclic_mtip_handler+32>: 13 87 17 00 addi a4,a5,1
0x800013d8 <eclic_mtip_handler+36>: 23 a6 e1 be sw a4,-1044(gp)
(gdb) n
17 printf("MTimer IRQ handler %d\n\r", int0_cnt);
=> 0x800013dc <eclic_mtip_handler+40>: 83 a7 c1 be lw a5,-1044(gp)
0x800013e0 <eclic_mtip_handler+44>: be 85 mv a1,a5
0x800013e2 <eclic_mtip_handler+46>: 13 85 01 9d addi a0,gp,-1584
0x800013e6 <eclic_mtip_handler+50>: a5 25 jal 0x80001a4e <printf>
(gdb)
18 uint64_t now = SysTimer_GetLoadValue();
(gdb)
19 SysTimer_SetCompareValue(now + SOC_TIMER_FREQ / 10);
=> 0x800014a0 <eclic_mtip_handler+236>: 03 27 84 fc lw a4,-56(s0)
0x800014a4 <eclic_mtip_handler+240>: 83 27 c4 fc lw a5,-52(s0)
0x800014a8 <eclic_mtip_handler+244>: 05 65 lui a0,0x1
0x800014aa <eclic_mtip_handler+246>: 13 05 c5 cc addi a0,a0,-820 # 0xccc
0x800014ae <eclic_mtip_handler+250>: 81 45 li a1,0
0x800014b0 <eclic_mtip_handler+252>: 33 06 a7 00 add a2,a4,a0
0x800014b4 <eclic_mtip_handler+256>: 32 88 mv a6,a2
0x800014b6 <eclic_mtip_handler+258>: 33 38 e8 00 sltu a6,a6,a4
0x800014ba <eclic_mtip_handler+262>: b3 86 b7 00 add a3,a5,a1
0x800014be <eclic_mtip_handler+266>: b3 07 d8 00 add a5,a6,a3
0x800014c2 <eclic_mtip_handler+270>: be 86 mv a3,a5
0x800014c4 <eclic_mtip_handler+272>: 32 87 mv a4,a2
0x800014c6 <eclic_mtip_handler+274>: b6 87 mv a5,a3
0x800014c8 <eclic_mtip_handler+276>: 23 20 e4 fc sw a4,-64(s0)
0x800014cc <eclic_mtip_handler+280>: 23 22 f4 fc sw a5,-60(s0)
(gdb)
20 }
=> 0x800015e4 <eclic_mtip_handler+560>: 01 00 nop
0x800015e6 <eclic_mtip_handler+562>: be 40 lw ra,204(sp)
0x800015e8 <eclic_mtip_handler+564>: 2e 44 lw s0,200(sp)
0x800015ea <eclic_mtip_handler+566>: 1e 49 lw s2,196(sp)
0x800015ec <eclic_mtip_handler+568>: 8e 49 lw s3,192(sp)
0x800015ee <eclic_mtip_handler+570>: 7a 5a lw s4,188(sp)
0x800015f0 <eclic_mtip_handler+572>: ea 5a lw s5,184(sp)
0x800015f2 <eclic_mtip_handler+574>: 5a 5b lw s6,180(sp)
0x800015f4 <eclic_mtip_handler+576>: ca 5b lw s7,176(sp)
0x800015f6 <eclic_mtip_handler+578>: 3a 5c lw s8,172(sp)
0x800015f8 <eclic_mtip_handler+580>: aa 5c lw s9,168(sp)
0x800015fa <eclic_mtip_handler+582>: 1a 5d lw s10,164(sp)
0x800015fc <eclic_mtip_handler+584>: 8a 5d lw s11,160(sp)
0x800015fe <eclic_mtip_handler+586>: 69 61 addi sp,sp,208
0x80001600 <eclic_mtip_handler+588>: 82 80 ret
(gdb)
0x80000374 in irq_entry ()
=> 0x80000374 <irq_entry+52>: 73 70 04 30 csrci mstatus,8
(gdb) si
0x80000378 in irq_entry ()
=> 0x80000378 <irq_entry+56>: d2 52 lw t0,52(sp)
(gdb)
0x8000037a in irq_entry ()
=> 0x8000037a <irq_entry+58>: 73 90 42 7c csrw 0x7c4,t0
(gdb)
0x8000037e in irq_entry ()
=> 0x8000037e <irq_entry+62>: c2 52 lw t0,48(sp)
(gdb)
0x80000380 in irq_entry ()
=> 0x80000380 <irq_entry+64>: 73 90 12 34 csrw mepc,t0
(gdb)
0x80000384 in irq_entry ()
=> 0x80000384 <irq_entry+68>: b2 52 lw t0,44(sp)
(gdb)
0x80000386 in irq_entry ()
=> 0x80000386 <irq_entry+70>: 73 90 22 34 csrw mcause,t0
(gdb)
0x8000038a in irq_entry ()
=> 0x8000038a <irq_entry+74>: 82 40 lw ra,0(sp)
(gdb)
0x8000038c in irq_entry ()
=> 0x8000038c <irq_entry+76>: 12 42 lw tp,4(sp)
(gdb)
0x8000038e in irq_entry ()
=> 0x8000038e <irq_entry+78>: a2 42 lw t0,8(sp)
(gdb)
0x80000390 in irq_entry ()
=> 0x80000390 <irq_entry+80>: 32 43 lw t1,12(sp)
(gdb)
0x80000392 in irq_entry ()
=> 0x80000392 <irq_entry+82>: c2 43 lw t2,16(sp)
(gdb)
0x80000394 in irq_entry ()
=> 0x80000394 <irq_entry+84>: 52 45 lw a0,20(sp)
(gdb)
0x80000396 in irq_entry ()
=> 0x80000396 <irq_entry+86>: e2 45 lw a1,24(sp)
(gdb)
0x80000398 in irq_entry ()
=> 0x80000398 <irq_entry+88>: 72 46 lw a2,28(sp)
(gdb)
0x8000039a in irq_entry ()
=> 0x8000039a <irq_entry+90>: 82 56 lw a3,32(sp)
(gdb)
0x8000039c in irq_entry ()
=> 0x8000039c <irq_entry+92>: 12 57 lw a4,36(sp)
(gdb)
0x8000039e in irq_entry ()
=> 0x8000039e <irq_entry+94>: a2 57 lw a5,40(sp)
(gdb)
0x800003a0 in irq_entry ()
=> 0x800003a0 <irq_entry+96>: 62 58 lw a6,56(sp)
(gdb)
0x800003a2 in irq_entry ()
=> 0x800003a2 <irq_entry+98>: f2 58 lw a7,60(sp)
(gdb)
0x800003a4 in irq_entry ()
=> 0x800003a4 <irq_entry+100>: 06 4e lw t3,64(sp)
(gdb)
0x800003a6 in irq_entry ()
=> 0x800003a6 <irq_entry+102>: 96 4e lw t4,68(sp)
(gdb)
0x800003a8 in irq_entry ()
=> 0x800003a8 <irq_entry+104>: 26 4f lw t5,72(sp)
(gdb)
0x800003aa in irq_entry ()
=> 0x800003aa <irq_entry+106>: b6 4f lw t6,76(sp)
(gdb)
0x800003ac in irq_entry ()
=> 0x800003ac <irq_entry+108>: 61 61 addi sp,sp,80
(gdb)
0x800003ae in irq_entry ()
=> 0x800003ae <irq_entry+110>: 73 00 20 30 mret
(gdb)
Breakpoint 1, 0x80000364 in irq_entry ()
=> 0x80000364 <irq_entry+36>: 73 d0 e5 7e csrwi 0x7ee,11
(gdb)