pinebuds/rtos/rtx5/GCC/irq_ca.S
2022-08-15 17:20:27 +08:00

465 lines
22 KiB
ArmAsm

/*
* Copyright (c) 2013-2018 Arm 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.
*
* -----------------------------------------------------------------------------
*
* Project: CMSIS-RTOS RTX
* Title: Cortex-A Exception handlers
*
* -----------------------------------------------------------------------------
*/
.file "irq_ca.S"
.syntax unified
.equ MODE_FIQ, 0x11
.equ MODE_IRQ, 0x12
.equ MODE_SVC, 0x13
.equ MODE_ABT, 0x17
.equ MODE_UND, 0x1B
.equ CPSR_BIT_T, 0x20
.equ K_STATE_RUNNING, 2 // osKernelState_t::osKernelRunning
.equ I_K_STATE_OFS, 8 // osRtxInfo.kernel.state offset
.equ I_TICK_IRQN_OFS, 16 // osRtxInfo.tick_irqn offset
.equ I_T_RUN_OFS, 20 // osRtxInfo.thread.run offset
.equ TCB_SP_FRAME, 34 // osRtxThread_t.stack_frame offset
.equ TCB_SP_OFS, 56 // osRtxThread_t.sp offset
.section ".rodata"
.global irqRtxLib // Non weak library reference
irqRtxLib:
.byte 0
.section ".data"
.global IRQ_PendSV
IRQ_NestLevel:
.word 0 // IRQ nesting level counter
IRQ_PendSV:
.byte 0 // Pending SVC flag
.arm
.section ".text"
.align 4
.type Undef_Handler, %function
.global Undef_Handler
.fnstart
.cantunwind
Undef_Handler:
SRSFD SP!, #MODE_UND
PUSH {R0-R4, R12} // Save APCS corruptible registers to UND mode stack
MRS R0, SPSR
TST R0, #CPSR_BIT_T // Check mode
MOVEQ R1, #4 // R1 = 4 ARM mode
MOVNE R1, #2 // R1 = 2 Thumb mode
SUB R0, LR, R1
LDREQ R0, [R0] // ARM mode - R0 points to offending instruction
BEQ Undef_Cont
// Thumb instruction
// Determine if it is a 32-bit Thumb instruction
LDRH R0, [R0]
MOV R2, #0x1C
CMP R2, R0, LSR #11
BHS Undef_Cont // 16-bit Thumb instruction
// 32-bit Thumb instruction. Unaligned - reconstruct the offending instruction
LDRH R2, [LR]
ORR R0, R2, R0, LSL #16
Undef_Cont:
MOV R2, LR // Set LR to third argument
AND R12, SP, #4 // Ensure stack is 8-byte aligned
SUB SP, SP, R12 // Adjust stack
PUSH {R12, LR} // Store stack adjustment and dummy LR
// R0 =Offending instruction, R1 =2(Thumb) or =4(ARM)
BL CUndefHandler
POP {R12, LR} // Get stack adjustment & discard dummy LR
ADD SP, SP, R12 // Unadjust stack
LDR LR, [SP, #24] // Restore stacked LR and possibly adjust for retry
SUB LR, LR, R0
LDR R0, [SP, #28] // Restore stacked SPSR
MSR SPSR_cxsf, R0
CLREX // Clear exclusive monitor
POP {R0-R4, R12} // Restore stacked APCS registers
ADD SP, SP, #8 // Adjust SP for already-restored banked registers
MOVS PC, LR
.fnend
.size Undef_Handler, .-Undef_Handler
.type PAbt_Handler, %function
.global PAbt_Handler
.fnstart
.cantunwind
PAbt_Handler:
SUB LR, LR, #4 // Pre-adjust LR
SRSFD SP!, #MODE_ABT // Save LR and SPRS to ABT mode stack
PUSH {R0-R4, R12} // Save APCS corruptible registers to ABT mode stack
MRC p15, 0, R0, c5, c0, 1 // IFSR
MRC p15, 0, R1, c6, c0, 2 // IFAR
MOV R2, LR // Set LR to third argument
AND R12, SP, #4 // Ensure stack is 8-byte aligned
SUB SP, SP, R12 // Adjust stack
PUSH {R12, LR} // Store stack adjustment and dummy LR
BL CPAbtHandler
POP {R12, LR} // Get stack adjustment & discard dummy LR
ADD SP, SP, R12 // Unadjust stack
CLREX // Clear exclusive monitor
POP {R0-R4, R12} // Restore stack APCS registers
RFEFD SP! // Return from exception
.fnend
.size PAbt_Handler, .-PAbt_Handler
.type DAbt_Handler, %function
.global DAbt_Handler
.fnstart
.cantunwind
DAbt_Handler:
SUB LR, LR, #8 // Pre-adjust LR
SRSFD SP!, #MODE_ABT // Save LR and SPRS to ABT mode stack
PUSH {R0-R4, R12} // Save APCS corruptible registers to ABT mode stack
MRC p15, 0, R0, c5, c0, 0 // DFSR
MRC p15, 0, R1, c6, c0, 0 // DFAR
MOV R2, LR // Set LR to third argument
AND R12, SP, #4 // Ensure stack is 8-byte aligned
SUB SP, SP, R12 // Adjust stack
PUSH {R12, LR} // Store stack adjustment and dummy LR
BL CDAbtHandler
POP {R12, LR} // Get stack adjustment & discard dummy LR
ADD SP, SP, R12 // Unadjust stack
CLREX // Clear exclusive monitor
POP {R0-R4, R12} // Restore stacked APCS registers
RFEFD SP! // Return from exception
.fnend
.size DAbt_Handler, .-DAbt_Handler
.type IRQ_Handler, %function
.global IRQ_Handler
.fnstart
.cantunwind
IRQ_Handler:
SUB LR, LR, #4 // Pre-adjust LR
SRSFD SP!, #MODE_SVC // Save LR_irq and SPSR_irq on to the SVC stack
CPS #MODE_SVC // Change to SVC mode
PUSH {R0-R3, R12, LR} // Save APCS corruptible registers
LDR R0, =IRQ_NestLevel
LDR R1, [R0]
ADD R1, R1, #1 // Increment IRQ nesting level
STR R1, [R0]
MOV R3, SP // Move SP into R3
AND R3, R3, #4 // Get stack adjustment to ensure 8-byte alignment
SUB SP, SP, R3 // Adjust stack
PUSH {R3, R4} // Store stack adjustment(R3) and user data(R4)
BLX IRQ_GetActiveIRQ // Retrieve interrupt ID into R0
MOV R4, R0 // Move interrupt ID to R4
BLX IRQ_GetHandler // Retrieve interrupt handler address for current ID
CMP R0, #0 // Check if handler address is 0
BEQ IRQ_End // If 0, end interrupt and return
CPSIE i // Re-enable interrupts
BLX R0 // Call IRQ handler
CPSID i // Disable interrupts
IRQ_End:
MOV R0, R4 // Move interrupt ID to R0
BLX IRQ_EndOfInterrupt // Signal end of interrupt
POP {R3, R4} // Restore stack adjustment(R3) and user data(R4)
ADD SP, SP, R3 // Unadjust stack
BL osRtxContextSwitch // Continue in context switcher
LDR R0, =IRQ_NestLevel
LDR R1, [R0]
SUBS R1, R1, #1 // Decrement IRQ nesting level
STR R1, [R0]
CLREX // Clear exclusive monitor for interrupted code
POP {R0-R3, R12, LR} // Restore stacked APCS registers
RFEFD SP! // Return from IRQ handler
.fnend
.size IRQ_Handler, .-IRQ_Handler
.type SVC_Handler, %function
.global SVC_Handler
.fnstart
.cantunwind
SVC_Handler:
SRSFD SP!, #MODE_SVC // Store SPSR_svc and LR_svc onto SVC stack
PUSH {R12, LR}
MRS R12, SPSR // Load SPSR
TST R12, #CPSR_BIT_T // Thumb bit set?
LDRHNE R12, [LR,#-2] // Thumb: load halfword
BICNE R12, R12, #0xFF00 // extract SVC number
LDREQ R12, [LR,#-4] // ARM: load word
BICEQ R12, R12, #0xFF000000 // extract SVC number
CMP R12, #0 // Compare SVC number
BNE SVC_User // Branch if User SVC
PUSH {R0-R3}
LDR R0, =IRQ_NestLevel
LDR R1, [R0]
ADD R1, R1, #1 // Increment IRQ nesting level
STR R1, [R0]
LDR R0, =osRtxInfo
LDR R1, [R0, #I_K_STATE_OFS] // Load RTX5 kernel state
CMP R1, #K_STATE_RUNNING // Check osKernelRunning
BLT SVC_FuncCall // Continue if kernel is not running
LDR R0, [R0, #I_TICK_IRQN_OFS] // Load OS Tick irqn
BLX IRQ_Disable // Disable OS Tick interrupt
SVC_FuncCall:
POP {R0-R3}
LDR R12, [SP] // Reload R12 from stack
CPSIE i // Re-enable interrupts
BLX R12 // Branch to SVC function
CPSID i // Disable interrupts
SUB SP, SP, #4
STM SP, {SP}^ // Store SP_usr onto stack
POP {R12} // Pop SP_usr into R12
SUB R12, R12, #16 // Adjust pointer to SP_usr
LDMDB R12, {R2,R3} // Load return values from SVC function
PUSH {R0-R3} // Push return values to stack
LDR R0, =osRtxInfo
LDR R1, [R0, #I_K_STATE_OFS] // Load RTX5 kernel state
CMP R1, #K_STATE_RUNNING // Check osKernelRunning
BLT SVC_ContextCheck // Continue if kernel is not running
LDR R0, [R0, #I_TICK_IRQN_OFS] // Load OS Tick irqn
BLX IRQ_Enable // Enable OS Tick interrupt
SVC_ContextCheck:
BL osRtxContextSwitch // Continue in context switcher
LDR R0, =IRQ_NestLevel
LDR R1, [R0]
SUB R1, R1, #1 // Decrement IRQ nesting level
STR R1, [R0]
CLREX // Clear exclusive monitor
POP {R0-R3, R12, LR} // Restore stacked APCS registers
RFEFD SP! // Return from exception
SVC_User:
PUSH {R4, R5}
LDR R5,=osRtxUserSVC // Load address of SVC table
LDR R4,[R5] // Load SVC maximum number
CMP R12,R4 // Check SVC number range
BHI SVC_Done // Branch if out of range
LDR R12,[R5,R12,LSL #2] // Load SVC Function Address
BLX R12 // Call SVC Function
SVC_Done:
CLREX // Clear exclusive monitor
POP {R4, R5, R12, LR}
RFEFD SP! // Return from exception
.fnend
.size SVC_Handler, .-SVC_Handler
.type osRtxContextSwitch, %function
.global osRtxContextSwitch
.fnstart
.cantunwind
osRtxContextSwitch:
PUSH {LR}
// Check interrupt nesting level
LDR R0, =IRQ_NestLevel
LDR R1, [R0] // Load IRQ nest level
CMP R1, #1
BNE osRtxContextExit // Nesting interrupts, exit context switcher
LDR R12, =osRtxInfo+I_T_RUN_OFS // Load address of osRtxInfo.run
LDM R12, {R0, R1} // Load osRtxInfo.thread.run: curr & next
LDR R2, =IRQ_PendSV // Load address of IRQ_PendSV flag
LDRB R3, [R2] // Load PendSV flag
CMP R0, R1 // Check if context switch is required
BNE osRtxContextCheck // Not equal, check if context save required
CMP R3, #1 // Compare IRQ_PendSV value
BNE osRtxContextExit // No post processing (and no context switch requested)
osRtxContextCheck:
STR R1, [R12] // Store run.next as run.curr
// R0 = curr, R1 = next, R2 = &IRQ_PendSV, R3 = IRQ_PendSV, R12 = &osRtxInfo.thread.run
PUSH {R1-R3, R12}
CMP R0, #0 // Is osRtxInfo.thread.run.curr == 0
BEQ osRtxPostProcess // Current deleted, skip context save
osRtxContextSave:
MOV LR, R0 // Move &osRtxInfo.thread.run.curr to LR
MOV R0, SP // Move SP_svc into R0
ADD R0, R0, #20 // Adjust SP_svc to R0 of the basic frame
SUB SP, SP, #4
STM SP, {SP}^ // Save SP_usr to current stack
POP {R1} // Pop SP_usr into R1
SUB R1, R1, #64 // Adjust SP_usr to R4 of the basic frame
STMIA R1!, {R4-R11} // Save R4-R11 to user stack
LDMIA R0!, {R4-R8} // Load stacked R0-R3,R12 into R4-R8
STMIA R1!, {R4-R8} // Store them to user stack
STM R1, {LR}^ // Store LR_usr directly
ADD R1, R1, #4 // Adjust user sp to PC
LDMIB R0!, {R5-R6} // Load current PC, CPSR
STMIA R1!, {R5-R6} // Restore user PC and CPSR
SUB R1, R1, #64 // Adjust SP_usr to stacked R4
// Check if VFP state need to be saved
MRC p15, 0, R2, c1, c0, 2 // VFP/NEON access enabled? (CPACR)
AND R2, R2, #0x00F00000
CMP R2, #0x00F00000
BNE osRtxContextSave1 // Continue, no VFP
VMRS R2, FPSCR
STMDB R1!, {R2,R12} // Push FPSCR, maintain 8-byte alignment
VSTMDB R1!, {D0-D15} // Save D0-D15
#if __ARM_NEON == 1
VSTMDB R1!, {D16-D31} // Save D16-D31
#endif
LDRB R2, [LR, #TCB_SP_FRAME] // Load osRtxInfo.thread.run.curr frame info
#if __ARM_NEON == 1
ORR R2, R2, #4 // NEON state
#else
ORR R2, R2, #2 // VFP state
#endif
STRB R2, [LR, #TCB_SP_FRAME] // Store VFP/NEON state
osRtxContextSave1:
STR R1, [LR, #TCB_SP_OFS] // Store user sp to osRtxInfo.thread.run.curr
osRtxPostProcess:
// RTX IRQ post processing check
POP {R8-R11} // Pop R8 = run.next, R9 = &IRQ_PendSV, R10 = IRQ_PendSV, R11 = &osRtxInfo.thread.run
CMP R10, #1 // Compare PendSV value
BNE osRtxContextRestore // Skip post processing if not pending
MOV R4, SP // Move SP_svc into R4
AND R4, R4, #4 // Get stack adjustment to ensure 8-byte alignment
SUB SP, SP, R4 // Adjust stack
// Disable OS Tick
LDR R5, =osRtxInfo // Load address of osRtxInfo
LDR R5, [R5, #I_TICK_IRQN_OFS] // Load OS Tick irqn
MOV R0, R5 // Set it as function parameter
BLX IRQ_Disable // Disable OS Tick interrupt
MOV R6, #0 // Set PendSV clear value
B osRtxPendCheck
osRtxPendExec:
STRB R6, [R9] // Clear PendSV flag
CPSIE i // Re-enable interrupts
BLX osRtxPendSV_Handler // Post process pending objects
CPSID i // Disable interrupts
osRtxPendCheck:
LDR R8, [R11, #4] // Load osRtxInfo.thread.run.next
STR R8, [R11] // Store run.next as run.curr
LDRB R0, [R9] // Load PendSV flag
CMP R0, #1 // Compare PendSV value
BEQ osRtxPendExec // Branch to PendExec if PendSV is set
// Re-enable OS Tick
MOV R0, R5 // Restore irqn as function parameter
BLX IRQ_Enable // Enable OS Tick interrupt
ADD SP, SP, R4 // Restore stack adjustment
osRtxContextRestore:
LDR LR, [R8, #TCB_SP_OFS] // Load next osRtxThread_t.sp
LDRB R2, [R8, #TCB_SP_FRAME] // Load next osRtxThread_t.stack_frame
ANDS R2, R2, #0x6 // Check stack frame for VFP context
MRC p15, 0, R2, c1, c0, 2 // Read CPACR
ANDEQ R2, R2, #0xFF0FFFFF // VFP/NEON state not stacked, disable VFP/NEON
ORRNE R2, R2, #0x00F00000 // VFP/NEON state is stacked, enable VFP/NEON
MCR p15, 0, R2, c1, c0, 2 // Write CPACR
BEQ osRtxContextRestore1 // No VFP
ISB // Sync if VFP was enabled
#if __ARM_NEON == 1
VLDMIA LR!, {D16-D31} // Restore D16-D31
#endif
VLDMIA LR!, {D0-D15} // Restore D0-D15
LDR R2, [LR]
VMSR FPSCR, R2 // Restore FPSCR
ADD LR, LR, #8 // Adjust sp pointer to R4
osRtxContextRestore1:
LDMIA LR!, {R4-R11} // Restore R4-R11
ADD R12, LR, #32 // Adjust sp and save it into R12
PUSH {R12} // Push sp onto stack
LDM SP, {SP}^ // Restore SP_usr directly
ADD SP, SP, #4 // Adjust SP_svc
LDMIA LR!, {R0-R3, R12} // Load user registers R0-R3,R12
STMIB SP!, {R0-R3, R12} // Store them to SP_svc
LDM LR, {LR}^ // Restore LR_usr directly
LDMIB LR!, {R0-R1} // Load user registers PC,CPSR
ADD SP, SP, #4
STMIB SP!, {R0-R1} // Store them to SP_svc
SUB SP, SP, #32 // Adjust SP_svc to stacked LR
osRtxContextExit:
POP {PC} // Return
.fnend
.size osRtxContextSwitch, .-osRtxContextSwitch
.end