• nucleus 学习 中断


    1. ;************************************************************************
    2. ;*
    3. ;* FUNCTION
    4. ;*
    5. ;* INT_IRQ
    6. ;*
    7. ;* DESCRIPTION
    8. ;*
    9. ;* This routine is the board-specific section for
    10. ;* level 1 interrupt handling
    11. ;*
    12. ;* CALLED BY
    13. ;*
    14. ;* None
    15. ;*
    16. ;* CALLS
    17. ;*
    18. ;* TMT_Timer_Interrupt
    19. ;*
    20. ;* INPUTS
    21. ;*
    22. ;* None
    23. ;*
    24. ;*
    25. ;* OUTPUTS
    26. ;*
    27. ;* None
    28. ;*
    29. ;* HISTORY
    30. ;*
    31. ;* NAME DATE REMARKS
    32. ;*
    33. ;* B. Ronquillo 05-10-00 Created initial version 1.0
    34. ;*
    35. ;************************************************************************
    36.         .def _INT_IRQ
    37. _INT_IRQ

    38. ; ARM Core Check                                                                 //确认CPSR,这里应该是确认是否在IRQ模式下
    39.     STMDB {r1}
    40.     MRS r1, SPSR
    41.     TST r1, #IRQ_BIT
    42.     LDMIA {r1}
    43.     SUBNES pc,lr,#4

    44.     STMDB sp!,{r0-r4} ; Save r0-r4 on temporary IRQ stack                         //sp=sp-4 保存R0~R4到栈
    45.     SUB lr,lr,#4 ; Adjust IRQ return address                                      //lr=lr-4就是把lr下移了一格

    46. ;********************************
    47. ;* Begin Hardware Specific Code *                                                 //开始硬件特性代码?
    48. ;********************************

    49.     LDR r3, INT_CNTRL_BASE_1 ; load Interrupt Control Base                        //装载中断控制器基地址
    50.     LDR r4, [r3,#INT_CNTRL_MIR] ; Get enable register value                       //获取enable register的值,INT_CNTRL_MIR的值

    51. ;******************************
    52. ;* End Hardware Specific Code *
    53. ;******************************

    54.     STMDB sp!,{r4} ; Put the enable register value on the IRQ stack               //把r4压入栈,r4是enable register的值
    55.     MVN r4,#0 ; Start with 0xFFFFFFFF to allow nesting of interrupts              //全部置1

    56. ;********************************
    57. ;* Begin Hardware Specific Code *
    58. ;********************************

    59.     LDR r2, [r3,#INT_CNTRL_ITR] ; Read Pending reg                                 //这次读取了INT_CNTRL_ITR的值
    60.     
    61. ;******************************
    62. ;* End Hardware Specific Code *
    63. ;******************************

    64.     LDR r3, IRQ_Priority ; Get the Priority table address                          //获取IRQ优先级表的地址

    65. IRQ_VECTOR_LOOP
    66.     LDR r0, [r3,#0] ; Load first vector to be checked from priority table
    67.     MOV r1, #1 ; Build mask
    68.     MOV r1, r1, LSL r0 ; Use vector number to set mask to correct bit position            //逻辑左移优先级个数,不会溢出吗
    69.     TST r1, r2 ; Test if pending bit is set                                               //判断
    70.     BNE IRQ_VECTOR_FOUND ; If bit is set, branch to found section...                      //找到IRQ_VECTOR

    71.     BIC r4,r4,r1 ; Clear mask bit to keep higher priority ints active                     //如果没找到查下一个向量表?
    72.     ADD r3, r3, #4 ; Move to next word in the priority table
    73.     ADR r0, Priority_End ; Load the end address for the priority table
    74.     CMP r0, r3 ; Make sure not at the end of the table (shouldn't happen!)
    75.     BNE     IRQ_VECTOR_LOOP                 ; Continue to loop if not at the end of the table


          ; No bits in pending register set, restore registers and exit interrupt servicing                         //恢复上下文
          ADD     sp,sp,#4                        ; Adjust sp above IRQ enable value
          LDMIA   sp!,{r0-r4}                     ; Restore r0-r4
          STMDB   sp!,{lr}                        ; Put return address for IRQ on stack
          LDMIA   sp!,{pc}^                       ; return to the point of the exception and restore SPSR
       
      IRQ_VECTOR_FOUND


      ;********************************
      ;* Begin Hardware Specific Code *
      ;********************************


          LDR     r3, INT_CNTRL_BASE_1            ; load Interrupt Control Base                              //获取INT_CNTRL_BASE

        
          MVN     r2, r1                          ; Get the inverse of the interrupt vector                  //之前r1和r2应该是一样的,取了反
          STR     r2, [r3,#INT_CNTRL_ITR]         ; Write a zero to the interrupt being handled              //写一个0到中断处理中


          LDR     r2, [r3,#INT_CNTRL_MIR]         ; Read the Mask reg
          ORR     r4, r2, r4                      ; Turn off lower priority pending bits and currently masked bits
          STR     r4, [r3,#INT_CNTRL_MIR]         ; Disable(Mask) all lower priority interrupts and currently masked interrupts


          MOV     r1, #1                          ; Clear the pending interrupt                              //清除pending中断位
          STR     r1, [r3,#INT_CNTRL_CONTROL_REG] ; by writing a 1 to the Control Reg                        //写入1清除


      ;******************************
      ;* End Hardware Specific Code *
      ;******************************


          LDR     r3, IRQ_Vectors                 ; Get IRQ vector table address
          MOV     r2, r0, LSL #2                  ; Multiply vector by 4 to get offset into table
          ADD     r3, r3, r2                      ; Adjust vector table address to correct offset
          LDR     r2, [r3,#0]                     ; Load branch address from vector table


          MOV     PC, r2                          ; Jump to correct branch location based on vector table          //跳转到中断向量表,这个跳转应该是找到对应的处理程序?


      ; END: INT_IRQ


    中断向量表:
    1. ; Define vector table used by INT_IRQ to branch to necessary ISR
    2. INT_IRQ_Vectors:
    3.     .word _INT_IRQ_2 ; Vector 0
    4.     .word INT_Interrupt_Shell ; Vector 1
    5.     .word INT_Interrupt_Shell ; Vector 2
    6.     .word INT_Interrupt_Shell ; Vector 3
    7.     .word INT_Interrupt_Shell ; Vector 4
    8.     .word INT_Interrupt_Shell ; Vector 5
    9.     .word INT_Interrupt_Shell ; Vector 6
    10.     .word INT_Interrupt_Shell ; Vector 7
    11.     .word INT_Interrupt_Shell ; Vector 8
    12.     .word INT_Interrupt_Shell ; Vector 9
    13.     .word INT_Interrupt_Shell ; Vector 10
    14.     .word INT_Interrupt_Shell ; Vector 11
    15.     .word INT_Interrupt_Shell ; Vector 12
    16.     .word INT_Interrupt_Shell ; Vector 13
    17.     .word INT_Interrupt_Shell ; Vector 14
    18.     .word INT_Interrupt_Shell ; Vector 15
    19.     .word INT_Interrupt_Shell ; Vector 16
    20.     .word INT_Interrupt_Shell ; Vector 17
    21.     .word INT_Interrupt_Shell ; Vector 18
    22.     .word INT_Interrupt_Shell ; Vector 19
    23.     .word INT_Interrupt_Shell ; Vector 20
    24.     .word INT_Interrupt_Shell ; Vector 21
    25.     .word INT_Interrupt_Shell ; Vector 22
    26.     .word INT_Interrupt_Shell ; Vector 23
    27.     .word INT_Interrupt_Shell ; Vector 24
    28.     .word INT_Interrupt_Shell ; Vector 25
    29.     .word INT_Interrupt_Shell ; Vector 26
    30.     .word INT_Interrupt_Shell ; Vector 27
    31.     .word INT_Interrupt_Shell ; Vector 28
    32.     .word INT_Interrupt_Shell ; Vector 29
    33.     .word INT_Timer_Interrupt ; Vector 30
    34.     .word INT_Interrupt_Shell ; Vector 31
    35. 。。。。。。。。。。。。。。。
    INT_Interruopt_Shell(感觉只是跳转到_TCT_Interrupt_Context_Save):
    1. ;************************************************************************
    2. ;*
    3. ;* FUNCTION
    4. ;*
    5. ;* INT_Interrupt_Shell
    6. ;*
    7. ;* DESCRIPTION
    8. ;*
    9. ;* Handles all interrupts which use NU_Register_LISR.
    10. ;*
    11. ;*
    12. ;* CALLED BY
    13. ;*
    14. ;* INT_IRQ
    15. ;*
    16. ;* CALLS
    17. ;*
    18. ;* TCT_Dispatch_LISR
    19. ;* TCT_Interrupt_Context_Restore
    20. ;*
    21. ;* INPUTS
    22. ;*
    23. ;* vector (register r0)
    24. ;*
    25. ;* OUTPUTS
    26. ;*
    27. ;* None
    28. ;************************************************************************
    29.     .def INT_Interrupt_Shell
    30. INT_Interrupt_Shell

    31.     MOV r4,lr ; Put IRQ return address into r4                                                   //这里r4是PC的值

    32.     BL _TCT_Interrupt_Context_Save                                                               //什么直接跳转了?

    33.     BL _TCC_Dispatch_LISR                                                                        //怎么两个跳转?LISR?

    34.     MRS r1,CPSR ; Pickup current CPSR                                                            //清除了中断,关了位
    35.     BIC r1,r1,#MODE_MASK ; Clear the mode bits
    36.     ORR r1,r1,#(IRQ_MODE_OR_LOCKOUT) ; Set the IRQ mode bits and Lockout interrupts
    37.     MSR CPSR,r1 ; Lockout interrupts/change to IRQ mode

    38. ;********************************
    39. ;* Begin Hardware Specific Code *
    40. ;********************************
    41.     LDMIA sp!,{r1} ; Get IRQ enable value off IRQ stack                                              //提取栈中的值到sp

    42.     LDR r2, INT_CNTRL_BASE_1 ; Get IRQ0 base register address                                        //获取IRQ0基地址
    43.     STR r1,[r2,#INT_CNTRL_MIR] ; Re-enable all lower priority interrupts                             //重新开启低优先级中断
    44. ;******************************
    45. ;* End Hardware Specific Code *
    46. ;******************************
    47.     
    48.     MRS r1,CPSR ; Pickup current CPSR
    49.     BIC r1,r1,#MODE_MASK ; Clear the mode bits
    50.     ORR r1,r1,#SUP_MODE ; Set the SVC mode bits
    51.     MSR CPSR,r1 ; Change to SVC mode                                                                 //切换成svc模式

    52.     B _TCT_Interrupt_Context_Restore
    既然跳转到_TCT_Interrupt_Context_Save那就继续学习吧:     
    1. ;************************************************************************
    2. ;*
    3. ;* FUNCTION
    4. ;*
    5. ;* TCT_Interrupt_Context_Save
    6. ;*
    7. ;* DESCRIPTION
    8. ;*
    9. ;* This function saves the interrupted thread's context. Nested
    10. ;* interrupts are also supported. If a task or HISR thread was
    11. ;* interrupted, the stack pointer is switched to the system stack
    12. ;* after the context is saved.
    13. ;*
    14. ;* CALLED BY
    15. ;*
    16. ;* Application ISRs Assembly language ISRs
    17. ;* INT_Interrupt_Shell Interrupt handler shell
    18. ;*
    19. ;* CALLS
    20. ;*
    21. ;* None
    22. ;*
    23. ;* INPUTS
    24. ;*
    25. ;* vector Interrupt's vector number
    26. ;*
    27. ;* OUTPUTS
    28. ;*
    29. ;* None
    30. ;*
    31. ;* HISTORY
    32. ;*
    33. ;* NAME DATE REMARKS
    34. ;*
    35. ;* W. Lamie 02-15-1994 Created initial version 1.0
    36. ;* D. Lamie 02-15-1994 Verified version 1.0
    37. ;* D. Driscoll 01-04-2002 Released version 1.13.3.
    38. ;* Updated to handle nested /
    39. ;* prioritized IRQs
    40. ;************************************************************************
    41. ;VOID TCT_Interrupt_Context_Save(INT vector)
    42. ;{
    43.     .def $TCT_Interrupt_Context_Save
    44. $TCT_Interrupt_Context_Save ; Dual-state interworking veneer
    45.     .state16
    46.     BX r15
    47.     NOP
    48.     .state32
    49.     B _TCT_Interrupt_Context_Save

    50.         .def _TCT_Interrupt_Context_Save
    51. _TCT_Interrupt_Context_Save
    52.         ; Determine if this is a nested interrupt.                                                          //确定是不是嵌套中断
    53.         LDR r1,Int_Count ; Pickup address of interrupt count                                                //Int_Count是中断计数
    54.         LDR r2,[r1, #0] ; Pickup interrupt counter                     
    55.         ADD r2,r2,#1 ; Add 1 to interrupt counter
    56.         STR r2,[r1, #0] ; Store new interrupt counter value                                                 //更新中断计数
    57.         CMP r2,#1 ; Is it nested?
    58.         BEQ TCT_Not_Nested_Save ; No                                                                        //如果不是嵌套就跳转

    59. ; Nested interrupt. Save complete context on the current stack.
    60. TCT_Nested_Save

    61. ; 1. Save another register on the exception stack so we have enough to work with                            //保存另一个寄存器(r5)到异常栈,腾出空间来。r13是sp指针
    62.         STMDB r13!,{r5}

    63. ; 2. Save the necessary exception registers into r1-r3                                                      //保存必要的异常寄存器到r1-r3
    64.         MOV r1,r13 ; Put the exception r13 into r1
    65.         MOV r2,r14 ; Move the return address for the caller
    66.                                             ; of this function into r2
    67.         MRS r3,spsr ; Put the exception spsr into r3

    68. ; 3. Adjust the exception stack pointer for future exceptions                                               //调整异常栈指针为之后的异常
    69.         ADD r13,r13,#24 ; r13 will point to enable reg value when done

    70. ; 4. Switch CPU modes to save context on system stack                                                       //转换CPU模式到system stack(之前初始化过的)
    71.         MRS r5,CPSR ; Pickup the current CPSR
    72.         BIC r5,r5,#MODE_MASK ; Clear the mode bits
    73.         
    74.         ORR r5,r5,#SUP_MODE ; Change to supervisor mode (SVD)
    75.         
    76.         MSR CPSR,r5 ; Switch modes (IRQ->SVC)

    77. ; 5. Store the SVC r13 into r5 so the r13 can be saved as is.                                               //这里明明是把r5放进r13(sp)
    78.         MOV r5,r13

    79. ; 6. Save the exception return address on the stack (r15).                                                  //保存r4进栈
    80.         STMDB r5!,{r4}

    81. ; 7. Save r6-r14 on stack                                                                                   //保存r6-r14进栈
    82.         STMDB r5!,{r6-r14}

    83. ; 8. Switch back to using r13 now that the original r13 has been saved.                                     //又把r13换回来了,回到sp回到原来的位置了
    84.         MOV r13,r5

    85. ; 9. Get r5 and exception enable registers off of exception stack and                                       //
    86. ; save r5 (stored in r4) back to the system stack.                                                          //保存r5(存在r4中)到system stack中,好吧之前的mov都理解错了,mov 目的,源
    87.         LDMIA r1!,{r4-r5}
    88.         STMDB r13!,{r4}
    89.         MOV r4,r5 ; Put exception enable value into r4

    90. ; 10. Get the rest of the registers off the exception stack and                                             //获取剩下的exception stack到system stack中
    91. ; save them onto the system stack.
    92.         LDMIA r1!,{r5-r8,r11} ; Get r0-r4 off exception stack
    93.         STMDB r13!,{r5-r8,r11} ; Put r0-r4 on system stack

    94. ; 11. Store the exception enable value back on the exception stack.
    95.         STMDB r1,{r4}

    96. ; 12. Save the SPSR on the system stack (CPSR)
    97.         STMDB r13!,{r3}

    98. ; 13. Re-enable interrupts                                                                                   //重新开启中断
    99.         MRS r1,CPSR
    100.         BIC r1,r1,#(IRQ_BIT_OR_FIQ_BIT)
    101.         MSR CPSR,r1

    102.         BX r2 ; Return to calling ISR
    103. ; }
    104. ; else
    105. ; {
    106. TCT_Not_Nested_Save

    107.         ; Determine if a thread was interrupted.                                //判断是否进程产生了中断
    108. ; if (TCD_Current_Thread)                                               //如果TCD_Current_Thread是空,则在schedule中因为初始化线程是关中断的
    109. ; {

    110.         LDR r1,Current_Thread ; Pickup current thread ptr address
    111.         LDR r1,[r1, #0] ; Pickup the current thread pointer
    112.         CMP r1,#0 ; Is it NU_NULL?
    113.         BEQ TCT_Idle_Context_Save ; If no, no real save is necessary


    114.         ; Yes, a thread was interrupted. Save complete context on the
    115.         ; thread's stack.

    116. ; 1. Save another register on the exception stack so we have enough to work with
    117.         STMDB r13!,{r5}

    118. ; 2. Save the necessary exception registers into r1-r3
    119.         MOV r1,r13 ; Put the exception r13 into r1
    120.         MOV r2,r14 ; Move the return address for the caller
    121.                                             ; of this function into r2
    122.         MRS r3,spsr ; Put the exception spsr into r3

    123. ; 3. Adjust the exception stack pointer for future exceptions
    124.         ADD r13,r13,#24 ; r13 will point to enable reg value when done

    125. ; 4. Switch CPU modes to save context on system stack
    126.         MRS r5,CPSR ; Pickup the current CPSR
    127.         BIC r5,r5,#MODE_MASK ; Clear the mode bits
    128.         
    129.         ORR r5,r5,#SUP_MODE ; Change to supervisor mode (SVD)
    130.         
    131.         MSR CPSR,r5 ; Switch modes (IRQ->SVC)

    132. ; 5. Store the SVC r13 into r5 so the r13 can be saved as is.
    133.         MOV r5,r13

    134. ; 6. Save the exception return address on the stack (r15).
    135.         STMDB r5!,{r4}

    136. ; 7. Save r6-r14 on stack
    137.         STMDB r5!,{r6-r14}

    138. ; 8. Switch back to using r13 now that the original r13 has been saved.
    139.         MOV r13,r5

    140. ; 9. Get r5 and exception enable registers off of exception stack and
    141. ; save r5 (stored in r4) back to the system stack.
    142.         LDMIA r1!,{r4-r5}
    143.         STMDB r13!,{r4}
    144.         MOV r4,r5 ; Put exception enable value into r4

    145. ; 10. Get the rest of the registers off the exception stack and
    146. ; save them onto the system stack.
    147.         LDMIA r1!,{r5-r8,r11} ; Get r0-r4 off exception stack
    148.         STMDB r13!,{r5-r8,r11} ; Put r0-r4 on system stack

    149. ; 11. Store the exception enable value back on the exception stack.
    150.         STMDB r1,{r4}

    151. ; 12. Save the SPSR on the system stack (CPSR)
    152.         STMDB r13!,{r3}

    153. ; 13. Save stack type to the task stack (1=interrupt stack)
    154.         MOV r1,#1 ; Interrupt stack type
    155.         STMDB r13!,{r1}

    156.         ; Save the thread's stack pointer in the control block.
    157. ; REG_Thread_Ptr = (TC_TCB *) TCD_Current_Thread
    158. ; REG_Thread_Ptr -> tc_stack_pointer = (VOID *) REG_Stack_Ptr

    159.         LDR r1,Current_Thread ; Pickup current thread ptr address
    160.         LDR r3,[r1, #0] ; Pickup current thread pointer
    161.         STR r13,[r3, #TC_STACK_POINTER] ; Save stack pointer

    162.         ; Switch to the system stack.
    163. ; REG_Stack_Ptr = TCD_System_Stack

    164.         LDR r1,System_Stack ; Pickup address of stack pointer
    165.         LDR r3,System_Limit ; Pickup address of stack limit ptr
    166.         LDR r13,[r1, #0] ; Switch to system stack
    167.         LDR r10,[r3, #0] ; Setup system stack limit

    168.         ; Re-enable interrupts
    169.         MRS r1,CPSR
    170.         BIC r1,r1,#(IRQ_BIT_OR_FIQ_BIT)
    171.         MSR CPSR,r1
    172.  
    173. ; Return to caller ISR.

    174.         BX r2 ; Return to caller ISR

    175. ; }

    176. TCT_Idle_Context_Save

    177.         MOV r2,r14 ; Save r14 in r2
    178.         LDR r3,[r13] ; Get exception enable value from stack
    179.         ADD r13,r13,#20 ; Adjust exception r13 for future interrupts
    180.         STR r3,[r13] ; Put exception enable value back on stack

    181.         MRS r1,CPSR ; Pickup current CPSR
    182.         BIC r1,r1,#MODE_MASK ; Clear the current mode
    183.         BIC r1,r1,#(IRQ_BIT_OR_FIQ_BIT) ; Re-enable interrupts
    184.         
    185.         ORR r1,r1,#SUP_MODE ; Prepare to switch to supervisor
    186.                                             ; mode (SVC)
    187.         MSR CPSR,r1 ; Switch to supervisor mode (SVC)

    188.         BX r2 ; Return to caller ISR

    189. ; }
    190. ;}
    NU对于中断上下文的保存具体操作如下:
    (1)在中断发生后执行的入口函数INT_IRQ()中,将r0-r4保存至irq的栈中
    (2)查找到对应的interrupt_shell(),clear中断源,更新全局的中断计数器,然后进行interrupt_contex_save
    (3)首先利用r1,r2,r3保存irq模式下的sp,lr,spsr,这里sp是用来切换至系统栈后拷贝lr和spsr的,这里保存lr和spsr是目的是task被抢占后,当再次schedule时可以返回task之前的状态。
    (4)切换至SVC模式,如果是非嵌套的中断则保存上下文至task stack中,将irq模式下的lr作为顶端PC的返回值入栈,将SVC模式下的r6-r14入栈,将irq模式下的sp保存至r4中入栈,最后将保存在irq_stack中的r0-r4入栈
    (5)如果是嵌套中断,中断的嵌套发生在LISR中,在执行LISR时已经切换至system stack,因此嵌套中断要将中断的上下文保存至system stack中,与task stack中interrupt stack相比只是少了栈顶用来标记嵌套的标志(1 not nested)
    (6)有一个分支判断,就是如果当前线程是空,即TCD_Current_Thread == NULL,表明当前是schedule中,因为初始化线程是关中断的,这样就不为schedule线程建立栈帧,因为schedule不需要保存上下文,在restore中断上下文时直接跳转至schedule。

    中断上下文的恢复
    全局的中断计数器INT_Count是否为0来判定当前出栈的信息,如果是嵌套则返回LISR中,否则切换至system stack执行schedule
      



    无欲速,无见小利。欲速,则不达;见小利,则大事不成。
  • 相关阅读:
    NET 6开发TodoList
    TensorFlow.NET机器学习
    存储资源QPS估算
    动态创建表达式C# Expression
    DNN Platform网站迁移纪实:从Web Form 到 Asp.Net Core (Abp vNext 自定义开发)
    Serilog 来记录日志
    上周热点回顾(1.101.16)
    keycloak~自定义directgrant直接认证
    springboot~容器化环境获取真实IP地址
    Lind.DDD.Repositories.EF层介绍~实现DbContext的动态注入
  • 原文地址:https://www.cnblogs.com/ch122633/p/7363280.html
Copyright © 2020-2023  润新知