Files
E3_boot/drivers/source/vic/sdrv_vic_freertos.S
2025-11-07 10:05:24 +08:00

335 lines
8.3 KiB
ArmAsm

/*
* sd_vic_freertos.S
*
* Copyright (c) 2020 Semidrive Semiconductor.
* All rights reserved.
*
* Description: SemiDrive VIC interrupt handlers for freertos.
*
* Revision History:
* -----------------
*/
#if __ICCARM__
EXTERN ulCriticalNesting
EXTERN ulMaxAPIPriority
EXTERN pxCurrentTCB
EXTERN vTaskSwitchContext
EXTERN vApplicationIRQHandler
EXTERN ulPortInterruptNesting
EXTERN ulPortTaskHasFPUContext
EXTERN ulPortYieldRequired
EXTERN vPortSetInterruptMaskAsm
EXTERN get_vic_data_base
EXTERN sdrv_vic_lld_get_priority
EXTERN sdrv_vic_lld_ack_slow_path
EXTERN g_vic_user_isr
EXTERN int_nest_errata_enabled
EXTERN sdrv_vic_lld_mask_low_priority
#endif
#if __GNUC__
.macro portSAVE_CONTEXT
#elif __ICCARM__
portSAVE_CONTEXT MACRO
#endif
/* Save the LR and SPSR onto the system mode stack before switching to
system mode to save the remaining system mode registers. */
SRSDB sp!, #SYS_MODE
CPS #SYS_MODE
PUSH {R0-R12, R14}
/* Push the critical nesting count. */
LDR R2, =ulCriticalNesting
LDR R1, [R2]
PUSH {R1}
/* Does the task have a floating point context that needs saving? If
ulPortTaskHasFPUContext is 0 then no. */
LDR R2, =ulPortTaskHasFPUContext
LDR R3, [R2]
CMP R3, #0
/* Save the floating point context, if any. */
FMRXNE R1, FPSCR
VPUSHNE {D0-D15}
/*VPUSHNE {D16-D31}*/
PUSHNE {R1}
/* Save ulPortTaskHasFPUContext itself. */
PUSH {R3}
/* Save the stack pointer in the TCB. */
LDR R0, =pxCurrentTCB
LDR R1, [R0]
STR SP, [R1]
#if __GNUC__
.endm
#elif __ICCARM__
ENDM
#endif
; /**********************************************************************/
#if __GNUC__
.macro portRESTORE_CONTEXT
#elif __ICCARM__
portRESTORE_CONTEXT MACRO
#endif
/* Set the SP to point to the stack of the task being restored. */
LDR R0, =pxCurrentTCB
LDR R1, [R0]
LDR SP, [R1]
/* Is there a floating point context to restore? If the restored
ulPortTaskHasFPUContext is zero then no. */
LDR R0, =ulPortTaskHasFPUContext
POP {R1}
STR R1, [R0]
CMP R1, #0
/* Restore the floating point context, if any. */
POPNE {R0}
/*VPOPNE {D16-D31}*/
VPOPNE {D0-D15}
VMSRNE FPSCR, R0
/* Restore the critical section nesting depth. */
LDR R0, =ulCriticalNesting
POP {R1}
STR R1, [R0]
/* Ensure the priority mask is correct for the critical nesting depth. */
CMP R1, #0
MOVEQ R0, #255
LDRNE R0, =ulMaxAPIPriority
LDRNE R0, [R0]
LDR R4, =vPortSetInterruptMaskAsm
BLX R4
/* Restore all system mode registers other than the SP (which is already
being used). */
POP {R0-R12, R14}
/* Return to the task code, loading CPSR on the way. */
RFEIA sp!
#if __GNUC__
.endm
#elif __ICCARM__
ENDM
#endif
#if __GNUC__
.macro vectored_irq_handler vector_num
.globl vectored_irq\vector_num\()_handler
vectored_irq\vector_num\()_handler:
/* Return to the interrupted instruction. */
SUB lr, lr, #4
/* Push the return address and SPSR. */
PUSH {lr}
MRS lr, SPSR
PUSH {lr}
/* Change to supervisor mode to allow reentry. */
CPS #SVC_MODE
/* Push used registers. */
PUSH {r0-r4, r12}
MOV r0, #\vector_num
B .Lcommon_irq_handler
.endm
#elif __ICCARM__
vectored_irq_handler MACRO vector_num
PUBLIC vectored_irq\1_handler
vectored_irq\1_handler:
/* Return to the interrupted instruction. */
SUB lr, lr, #4
/* Push the return address and SPSR. */
PUSH {lr}
MRS lr, SPSR
PUSH {lr}
/* Change to supervisor mode to allow reentry. */
CPS #SVC_MODE
/* Push used registers. */
PUSH {r0-r4, r12}
MOV r0, #vector_num
B .Lcommon_irq_handler
ENDM
#endif
.Lcommon_irq_handler:
/* Save old running priority. */
PUSH {r0, lr} /* Save vectore number (r0) & svc return address (lr) */
BLX get_vic_data_base
ADD r2, r0, #8 /* r2 now holds address of {struct vic_data}.cur_rp */
POP {r0, lr} /* Restore vectore number (r0) & svc return address (lr) */
LDR r1, [r2]
PUSH {r1, r2}
/* Increment nesting count. r3 holds the address of ulPortInterruptNesting
for future use. r1 holds the original ulPortInterruptNesting value for
future use. */
LDR r3, =ulPortInterruptNesting
LDR r1, [r3]
ADD r4, r1, #1
STR r4, [r3]
/* Save address of {struct vic_data}.cur_rp into callee-saved r4. */
MOV r4, r2
/* Use r5 to record errata enabled flag, save it before corrupted. */
PUSH {r5}
/* Ensure bit 2 of the stack pointer is clear. r2 holds the bit 2 value for
future use. _RB_ Is this ever needed provided the start of the stack is
alligned on an 8-byte boundary? */
MOV r2, sp
AND r2, r2, #4
SUB sp, sp, r2
/* Call the interrupt handler. */
PUSH {r0-r4, lr}
/* Update current running priority. */
BLX sdrv_vic_lld_get_priority
STR r0, [r4]
/* Get previous masked priority. */
LDR r1, [r4, #8]
PUSH {r0, r1}
BLX int_nest_errata_enabled
MOVS r5, r0
BEQ .Lno_errata
LDR r1, [sp]
BLX sdrv_vic_lld_ack_slow_path
.Lno_errata:
POP {r0, r1}
/* Make sure only unmasked interrupt can be taken. */
CMP r0, r1
BGE .Lirq_out
#if CONFIG_VIC_INT_NEST_AUTO_ENABLE
LDR r0, [sp, #4]
CMP r0, #(CONFIG_VIC_INT_NEST_MAX_CNT - 1)
BGE .Lskip_reenable_irq
CPSIE i
.Lskip_reenable_irq:
#endif
/* Restore vector number */
LDR r0, [sp]
/* Call user ISR. */
LDR r1, =g_vic_user_isr
LDR r1, [r1]
BLX r1
.Lirq_out:
POP {r0-r4, lr}
ADD sp, sp, r2
CPSID i
DSB
ISB
CMP r5, #INT_NEST_ERRATA_TYPE_0
BEQ .Lerrata_skip_wr_isr
LDR r2, [r4, #-8]
ADD r2, r2, #0xF00
/* Perform a dummy write to VICADDRESS register to finish this interrupt */
STR r0, [r2]
.Lerrata_skip_wr_isr:
/* Restore the old nesting count. */
STR r1, [r3]
CMP r5, #NO_INT_NEST_ERRATA
/* Restore r5. */
POP {r5}
/* Restore current running priority. */
POP {r0, r2}
STR r0, [r2]
BEQ .Lrestore_context
/* Save previous nesting counter and supervisor lr (preempted isr lr). */
PUSH {r1, lr}
MOV r1, #0
/* Unmask priority to previous state. */
BLX sdrv_vic_lld_mask_low_priority
/* Restore previous nesting counter and supervisor lr. */
POP {r1, lr}
.Lrestore_context:
/* A context switch is never performed if the nesting count is not 0. */
CMP r1, #0
BNE .Lexit_without_switch
/* Did the interrupt request a context switch? r1 holds the address of
ulPortYieldRequired and r0 the value of ulPortYieldRequired for future
use. */
LDR r1, =ulPortYieldRequired
LDR r0, [r1]
CMP r0, #0
BNE .Lswitch_before_exit
B .Lexit_without_switch
#if __GNUC__
.ltorg
#elif __ICCARM__
LTORG
#endif
.Lexit_without_switch:
/* No context switch. Restore used registers, LR_irq and SPSR before
returning. */
POP {r0-r4, r12}
CPS #IRQ_MODE
POP {LR}
MSR SPSR_cxsf, LR
POP {LR}
MOVS PC, LR
.Lswitch_before_exit:
/* A context swtich is to be performed. Clear the context switch pending
flag. */
MOV r0, #0
STR r0, [r1]
/* Restore used registers, LR-irq and SPSR before saving the context
to the task stack. */
POP {r0-r4, r12}
CPS #IRQ_MODE
POP {LR}
MSR SPSR_cxsf, LR
POP {LR}
portSAVE_CONTEXT
/* Call the function that selects the new task to execute.
vTaskSwitchContext() if vTaskSwitchContext() uses LDRD or STRD
instructions, or 8 byte aligned stack allocated data. LR does not need
saving as a new LR will be loaded by portRESTORE_CONTEXT anyway. */
LDR R0, =vTaskSwitchContext
BLX R0
/* Restore the context of, and branch to, the task selected to execute
next. */
portRESTORE_CONTEXT
#if __GNUC__
.ltorg
#elif __ICCARM__
LTORG
#endif