| /* |
| * FreeRTOS Kernel <DEVELOPMENT BRANCH> |
| * Copyright (C) 2020 Synopsys, Inc. or its affiliates. All Rights Reserved. |
| * |
| * SPDX-License-Identifier: MIT |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a copy of |
| * this software and associated documentation files (the "Software"), to deal in |
| * the Software without restriction, including without limitation the rights to |
| * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of |
| * the Software, and to permit persons to whom the Software is furnished to do so, |
| * subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be included in all |
| * copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS |
| * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR |
| * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER |
| * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
| * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| * |
| * https://www.FreeRTOS.org |
| * https://github.com/FreeRTOS |
| * |
| */ |
| |
| /** |
| * \file |
| * \ingroup OS_FREERTOS |
| * \brief freertos support for arc processor |
| * like task dispatcher, interrupt handler |
| */ |
| /** @cond OS_FREERTOS_ASM_ARC_SUPPORT */ |
| |
| /* |
| * core-dependent part in assemble language (for arc) |
| */ |
| #define __ASSEMBLY__ |
| #include "arc/arc.h" |
| #include "arc/arc_asm_common.h" |
| |
| /* |
| * task dispatcher |
| * |
| */ |
| .text |
| .align 4 |
| .global dispatch |
| dispatch: |
| /* |
| * the pre-conditions of this routine are task context, CPU is |
| * locked, dispatch is enabled. |
| */ |
| SAVE_NONSCRATCH_REGS /* save callee save registers */ |
| mov r1, dispatch_r |
| PUSH r1 /* save return address */ |
| ld r0, [pxCurrentTCB] |
| bl dispatcher |
| |
| /* return routine when task dispatch happened in task context */ |
| dispatch_r: |
| RESTORE_NONSCRATCH_REGS /* recover registers */ |
| j [blink] |
| |
| /* |
| * start dispatch |
| */ |
| .global start_dispatch |
| .align 4 |
| start_dispatch: |
| /* |
| * this routine is called in the non-task context during the startup of the kernel |
| * , and all the interrupts are locked. |
| * |
| * when the dispatcher is called, the cpu is locked, no nest exception (CPU exception/interrupt). |
| * In target_initialize, all interrupt priority mask should be cleared, cpu should be |
| * locked, the interrupts outside the kernel such as fiq can be |
| * enabled. |
| */ |
| clri |
| mov r0, 0 |
| st r0, [exc_nest_count] |
| b dispatcher_0 |
| /* |
| * dispatcher |
| */ |
| dispatcher: |
| ld r1, [ulCriticalNesting] |
| PUSH r1 /* save critical nesting */ |
| st sp, [r0] /* save stack pointer of current task, r0->pxCurrentTCB */ |
| jl vTaskSwitchContext /* change the value of pxCurrentTCB */ |
| /* |
| * before dispatcher is called, task context | cpu locked | dispatch enabled |
| * should be satisfied. In this routine, the processor will jump |
| * into the entry of next to run task |
| * |
| * i.e. kernel mode, IRQ disabled, dispatch enabled |
| */ |
| dispatcher_0: |
| ld r1, [pxCurrentTCB] |
| ld sp, [r1] /* recover task stack */ |
| #if ARC_FEATURE_STACK_CHECK |
| lr r0, [AUX_STATUS32] |
| bclr r0, r0, AUX_STATUS_BIT_SC |
| flag r0 |
| jl vPortSetStackCheck |
| lr r0, [AUX_STATUS32] |
| bset r0, r0, AUX_STATUS_BIT_SC |
| flag r0 |
| #endif |
| POP r0 /* get critical nesting */ |
| st r0, [ulCriticalNesting] |
| POP r0 /* get return address */ |
| j [r0] |
| |
| /* |
| * task startup routine |
| * |
| */ |
| .text |
| .global start_r |
| .align 4 |
| start_r: |
| seti /* unlock cpu */ |
| mov blink, vPortEndTask /* set return address */ |
| POP r1 /* get task function body */ |
| POP r0 /* get task parameters */ |
| j [r1] |
| |
| /****** exceptions and interrupts handing ******/ |
| /****** entry for exception handling ******/ |
| .global exc_entry_cpu |
| .align 4 |
| exc_entry_cpu: |
| |
| EXCEPTION_PROLOGUE |
| |
| |
| mov blink, sp |
| mov r3, sp /* as exception handler's para(p_excinfo) */ |
| |
| ld r1, [exc_nest_count] |
| add r1, r1, 1 |
| st r1, [exc_nest_count] |
| brne r1, 0, exc_handler_1 |
| /* change to exception stack if interrupt happened in task context */ |
| mov sp, _e_stack |
| exc_handler_1: |
| PUSH blink |
| |
| /* find the exception cause */ |
| #if ARC_FEATURE_CORE_700 |
| lr r0, [AUX_ECR] |
| lsr r0, r0, 16 |
| bmsk r0, r0, 7 |
| #endif |
| mov r1, exc_int_handler_table |
| ld.as r2, [r1, r0] |
| |
| mov r0, r3 |
| jl [r2] /* !!!!jump to exception handler where interrupts are not allowed! */ |
| |
| /* interrupts are not allowed */ |
| ret_exc: |
| POP sp |
| mov r1, exc_nest_count |
| ld r0, [r1] |
| sub r0, r0, 1 |
| st r0, [r1] |
| brne r0, 0, ret_exc_1 /* nested exception case */ |
| lr r1, [AUX_IRQ_LV12] |
| brne r1, 0, ret_exc_1 /* nested or pending interrupt case */ |
| |
| ld r0, [context_switch_reqflg] |
| brne r0, 0, ret_exc_2 |
| ret_exc_1: /* return from non-task context, interrupts or exceptions are nested */ |
| |
| EXCEPTION_EPILOGUE |
| #if ARC_FEATURE_CORE_600 |
| rtie ilink2 |
| #else |
| rtie |
| #endif |
| |
| /* there is a dispatch request */ |
| ret_exc_2: |
| /* clear dispatch request */ |
| mov r0, 0 |
| st r0, [context_switch_reqflg] |
| |
| ld r0, [pxCurrentTCB] |
| breq r0, 0, ret_exc_1 |
| |
| SAVE_CALLEE_REGS /* save callee save registers */ |
| |
| lr r0, [AUX_STATUS32] |
| bclr r0, r0, AUX_STATUS_BIT_AE /* clear exception bit */ |
| flag r0 |
| |
| mov r1, ret_exc_r /* save return address */ |
| PUSH r1 |
| |
| bl dispatcher /* r0->pxCurrentTCB */ |
| |
| ret_exc_r: |
| /* recover exception status */ |
| lr r0, [AUX_STATUS32] |
| bset r0, r0, AUX_STATUS_BIT_AE |
| flag r0 |
| |
| RESTORE_CALLEE_REGS /* recover registers */ |
| EXCEPTION_EPILOGUE |
| #if ARC_FEATURE_CORE_600 |
| rtie ilink2 |
| #else |
| rtie |
| #endif |
| |
| /****** entry for normal interrupt exception handling ******/ |
| .global exc_entry_int /* entry for interrupt handling */ |
| .align 4 |
| exc_entry_int: |
| |
| INTERRUPT_PROLOGUE |
| |
| mov blink, sp |
| |
| /* disable interrupt */ |
| push r0 |
| lr r0, [AUX_STATUS32] |
| push r0 |
| bclr r0, r0, AUX_STATUS_BIT_E1 |
| bclr r0, r0, AUX_STATUS_BIT_E2 |
| flag r0 |
| ld r3, [exc_nest_count] |
| add r2, r3, 1 |
| st r2, [exc_nest_count] |
| /* enable interrupt */ |
| pop r0 |
| flag r0 |
| pop r0 |
| |
| brne r3, 0, irq_handler_1 |
| /* change to exception stack if interrupt happened in task context */ |
| mov sp, _e_stack |
| #if ARC_FEATURE_STACK_CHECK |
| lr r0, [AUX_STATUS32] |
| bclr r0, r0, AUX_STATUS_BIT_SC |
| flag r0 |
| #endif |
| irq_handler_1: |
| PUSH blink |
| |
| /* critical area */ |
| #if ARC_FEATURE_CORE_700 |
| lr r0, [AUX_IRQ_CAUSE1] |
| #endif |
| mov r1, exc_int_handler_table |
| ld.as r2, [r1, r0] /* r2 = exc_int_handler_table + irqno *4 */ |
| /* handle software triggered interrupt */ |
| lr r3, [AUX_IRQ_HINT] |
| cmp r3, r0 |
| bne.d irq_hint_handled |
| xor r3, r3, r3 |
| sr r3, [AUX_IRQ_HINT] |
| irq_hint_handled: |
| |
| jl [r2] /* jump to interrupt handler */ |
| /* no interrupts are allowed from here */ |
| ret_int: |
| clri /* disable interrupt */ |
| |
| POP sp |
| mov r1, exc_nest_count |
| ld r0, [r1] |
| sub r0, r0, 1 |
| st r0, [r1] |
| /* if there are multi-bits set in IRQ_LV12, it's still in nest interrupt */ |
| lr r1, [AUX_IRQ_LV12] |
| |
| ld r0, [context_switch_reqflg] |
| brne r0, 0, ret_int_2 |
| ret_int_1: /* return from non-task context */ |
| INTERRUPT_EPILOGUE |
| #if ARC_FEATURE_CORE_600 |
| /* TODO: series 600 IRQ6 and IRQ7 uses ilink2 */ |
| rtie ilink1 |
| #else |
| rtie |
| #endif |
| /* there is a dispatch request */ |
| ret_int_2: |
| /* clear dispatch request */ |
| mov r0, 0 |
| st r0, [context_switch_reqflg] |
| |
| ld r0, [pxCurrentTCB] |
| breq r0, 0, ret_int_1 |
| |
| /* r1 has old AUX_IRQ_LV12 */ |
| PUSH r1 |
| /* clear related bits in IRQ_ACT manually to simulate a irq return */ |
| |
| SAVE_CALLEE_REGS /* save callee save registers */ |
| mov r1, ret_int_r /* save return address */ |
| PUSH r1 |
| |
| bl dispatcher /* r0->pxCurrentTCB */ |
| |
| ret_int_r: |
| RESTORE_CALLEE_REGS /* recover registers */ |
| POPAX AUX_IRQ_LV12 |
| INTERRUPT_EPILOGUE |
| #if ARC_FEATURE_CORE_600 |
| rtie ilink1 |
| #else |
| rtie |
| #endif |
| |
| /** @endcond */ |