Nuclei SDK定时器应用示例

欢迎关注“安全有理”微信公众号。

安全有理

概述

定时器应用demo_timer application展示了如何使用定时器中断和软件中断,实现了如下功能:

  • 所有中断均注册为非向量中断
  • 定时器中断运行5次
  • 软件中断运行5次

中断与异常

在RISC-V体系架构中,中断处理和异常处理都属于异常。严格来说,异常分为同步异常和异步异常(中断)。同步异常是处理器执行某条指令而直接导致的异常,如执行非法指令,对齐错误,系统调用ECALL,调试异常等。异步异常是处理器顺序执行程序指令的过程中突然被打断,其与当前正在执行的指令无关,中断属于异步异常的一种。因此通常所说的术语-异常大多数指的是同步异常,而异步异常通常就使用术语-中断。

ECLIC

ECLIC是芯来在RISC-V标准CLIC基础上优化而来的改进型内核中断控制器(Enhanced Core Local Interrupt Controller,ECLIC),用于管理所有的中断源。ECLIC用于对多个内部和外部中断源进行仲裁、发送请求并支持中断嵌套。

ECLIC关系结构图

ECLIC为每个中断源分配了一个独一无二的编号(ID),其中ID=3为软件中断,ID=7为定时器中断,ID=19-4095为外部中断。

ECLC中断源分配

ECLIC的每个中断源均可以设置成向量或者非向量处理:

  • 如果被配置成为向量处理模式,则该中断被处理器内核响应后,处理器直接跳入该中断的向量入口(Vector Table Entry)存储的目标地址。
  • 如果被配置成为非向量处理模式,则该中断被处理器内核响应后,处理器直接跳入所有中断共享的入口地址。

ECLIC是一个存储器地址映射的单元,寄存器有:

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决定。

mtv2寄存器

非向量中断入口地址函数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寄存器,主要是mcausemepcmsubm,保存这几个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) 

参考

  1. Nuclei_N级别指令架构手册
  • 23
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值