/* * This file is subject to the terms of the GFX License. If a copy of * the license was not distributed with this file, you can obtain one at: * * http://ugfx.org/license.html */ /** * Thread Switching Functions for the Cortex M4 & M7 with hardware floating point * * Use the EABI calling standard (ARM's AAPCS) - Save r4 - r11 and floating point * The context is saved at the current stack location and a pointer is maintained in the thread structure. */ #if !CORTEX_USE_FPU #warning "GOS Threads: You have specified GFX_CPU=GFX_CPU_CORTX_M?_FP with hardware floating point support but CORTEX_USE_FPU is FALSE. Try using GFX_CPU_GFX_CPU_CORTEX_M? instead" #endif #if GFX_COMPILER == GFX_COMPILER_GCC || GFX_COMPILER == GFX_COMPILER_CYGWIN || GFX_COMPILER == GFX_COMPILER_MINGW32 || GFX_COMPILER == GFX_COMPILER_MINGW64 #define GFX_THREADS_DONE #define _gfxThreadsInit() static __attribute__((pcs("aapcs-vfp"),naked)) void _gfxTaskSwitch(thread *oldt, thread *newt) { __asm__ volatile ( "push {r4, r5, r6, r7, r8, r9, r10, r11, lr} \n\t" "vpush {s16-s31} \n\t" "str sp, %[oldtcxt] \n\t" "ldr sp, %[newtcxt] \n\t" "vpop {s16-s31} \n\t" "pop {r4, r5, r6, r7, r8, r9, r10, r11, pc} \n\t" : [newtcxt] "=m" (newt->cxt) : [oldtcxt] "m" (oldt->cxt) : "memory"); } static __attribute__((pcs("aapcs-vfp"),naked)) void _gfxStartThread(thread *oldt, thread *newt) { newt->cxt = (char *)newt + newt->size; __asm__ volatile ( "push {r4, r5, r6, r7, r8, r9, r10, r11, lr} \n\t" "vpush {s16-s31} \n\t" "str sp, %[oldtcxt] \n\t" "ldr sp, %[newtcxt] \n\t" : [newtcxt] "=m" (newt->cxt) : [oldtcxt] "m" (oldt->cxt) : "memory"); // Run the users function gfxThreadExit(_gfxCurrentThread->fn(_gfxCurrentThread->param)); } #elif GFX_COMPILER == GFX_COMPILER_KEIL || GFX_COMPILER == GFX_COMPILER_ARMCC static __asm void _gfxTaskSwitch(thread *oldt, thread *newt) { // Save the old context push {r4, r5, r6, r7, r8, r9, r10, r11, lr} vpush {s16-s31} str sp, [r0,#__cpp(offsetof(thread,cxt))] // oldt->cxt // Load the new context ldr sp, [r1,#__cpp(offsetof(thread,cxt))] // newt->cxt vpop {s16-s31} pop {r4, r5, r6, r7, r8, r9, r10, r11, pc} } static __asm void _gfxStartThread(thread *oldt, thread *newt) { // Calculate where to generate the new context // newt->cxt = (char *)newt + newt->size; ldr r2,[r1,#__cpp(offsetof(thread,size))] add r2,r2,r1 str r2,[r1,#__cpp(offsetof(thread,cxt))] // Save the old context push {r4, r5, r6, r7, r8, r9, r10, r11, lr} vpush {s16-s31} str sp, [r0,#__cpp(offsetof(thread,cxt))] // oldt->cxt // Load the new (imcomplete) context ldr sp, [r1,#__cpp(offsetof(thread,cxt))] // newt->cxt // Run the users function - we save some code because gfxThreadExit() never returns // gfxThreadExit(_gfxCurrentThread->fn(_gfxCurrentThread->param)); ldr r2,__cpp(&_gfxCurrentThread) ldr r2,[r2,#0] ldr r0,[r2,#__cpp(offsetof(thread,param))] ldr r1,[r2,#__cpp(offsetof(thread,fn))] blx r1 mov r4,r0 bl gfxThreadExit } #else #warning "GOS: Threads: You have specified a specific CPU but your compiler is not supported. Defaulting to CLIB switching" #endif