00001 /* 00002 * Copyright (C) 2001-2005 by egnite Software GmbH. All rights reserved. 00003 * 00004 * Redistribution and use in source and binary forms, with or without 00005 * modification, are permitted provided that the following conditions 00006 * are met: 00007 * 00008 * 1. Redistributions of source code must retain the above copyright 00009 * notice, this list of conditions and the following disclaimer. 00010 * 2. Redistributions in binary form must reproduce the above copyright 00011 * notice, this list of conditions and the following disclaimer in the 00012 * documentation and/or other materials provided with the distribution. 00013 * 3. Neither the name of the copyright holders nor the names of 00014 * contributors may be used to endorse or promote products derived 00015 * from this software without specific prior written permission. 00016 * 00017 * THIS SOFTWARE IS PROVIDED BY EGNITE SOFTWARE GMBH AND CONTRIBUTORS 00018 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 00019 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 00020 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL EGNITE 00021 * SOFTWARE GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 00022 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 00023 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 00024 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 00025 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 00026 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 00027 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 00028 * SUCH DAMAGE. 00029 * 00030 * For additional information see http://www.ethernut.de/ 00031 * 00032 */ 00033 00034 /* 00035 * $Log: context.c,v $ 00036 * Revision 1.6 2008/07/07 11:04:27 haraldkipp 00037 * Configurable ways of handling critical sections for ARM targets. 00038 * 00039 * Revision 1.5 2006/03/16 19:06:16 haraldkipp 00040 * Use link register to jump into thread and use dedicated routine to 00041 * jump into the idle thread. The way we did start the idle thread 00042 * before, jumping into the middle of NutThreadSwitch(), doesn't work 00043 * with optimized code. 00044 * 00045 * Revision 1.4 2006/03/16 15:25:00 haraldkipp 00046 * Changed human readable strings from u_char to char to stop GCC 4 from 00047 * nagging about signedness. 00048 * 00049 * Revision 1.3 2005/10/24 09:09:41 haraldkipp 00050 * Switch frame reduced. 00051 * NutThreadEntry included in the execution path. 00052 * The 'cc' globber had been removed from the context switching asm macro, 00053 * after Michael Fischer found out that this fixes a problem with optimized 00054 * compilation. Unfortunately, compiler optimized binaries still seem to 00055 * run unreliable. 00056 * 00057 * Revision 1.2 2005/08/02 17:46:45 haraldkipp 00058 * Major API documentation update. 00059 * 00060 * Revision 1.1 2005/07/26 18:10:48 haraldkipp 00061 * Moved from os/thread.c 00062 * 00063 * Revision 1.1 2005/05/27 17:16:40 drsung 00064 * Moved the file. 00065 * 00066 * Revision 1.4 2005/04/05 17:54:36 haraldkipp 00067 * Moved from user mode to system mode. Probably breaks the GBA port. 00068 * 00069 * Revision 1.3 2004/11/08 19:15:33 haraldkipp 00070 * Made assembly includes look nicer. 00071 * Changed mode from supervisory to user supervisory, which seems to work 00072 * with the GBA. 00073 * Skipped entry frame, because it simply confuses me. :-) 00074 * 00075 * Revision 1.2 2004/09/08 10:19:31 haraldkipp 00076 * Tyou's support for the ARM7 00077 * 00078 * Revision 1.1 2004/03/16 16:48:46 haraldkipp 00079 * Added Jan Dubiec's H8/300 port. 00080 * 00081 * Revision 1.2 2004/02/18 16:32:48 drsung 00082 * Bugfix in NutThreadCreate. Thanks to Mike Cornelius. 00083 * 00084 * Revision 1.1 2004/02/01 18:49:48 haraldkipp 00085 * Added CPU family support 00086 * 00087 */ 00088 00089 #include <cfg/os.h> 00090 00091 #include <string.h> 00092 00093 #include <sys/atom.h> 00094 #include <sys/heap.h> 00095 #include <sys/thread.h> 00096 00101 00102 00110 typedef struct { 00111 u_long csf_cpsr; 00112 u_long csf_r4; 00113 u_long csf_r5; 00114 u_long csf_r6; 00115 u_long csf_r7; 00116 u_long csf_r8; 00117 u_long csf_r9; 00118 u_long csf_r10; 00119 u_long csf_r11; /* AKA fp */ 00120 u_long csf_lr; 00121 } SWITCHFRAME; 00122 00128 typedef struct { 00129 u_long cef_r0; 00130 u_long cef_pc; 00131 } ENTERFRAME; 00132 00136 static void NutThreadEntry(void) __attribute__ ((naked)); 00137 void NutThreadEntry(void) 00138 { 00139 /* Load argument in r0 and jump to thread entry. */ 00140 asm volatile ("ldmfd sp!, {r0, lr}\n\tbx lr":::"r0", "lr"); 00141 } 00142 00143 00155 void NutThreadSwitch(void) __attribute__ ((naked)); 00156 void NutThreadSwitch(void) 00157 { 00158 /* Save CPU context. */ 00159 asm volatile ( /* */ 00160 "@ Save context\n\t" /* */ 00161 "stmfd sp!, {r4-r11, lr}\n\t" /* Save registers. */ 00162 "mrs r4, cpsr\n\t" /* Save status. */ 00163 "stmfd sp!, {r4}\n\t" /* */ 00164 "str sp, %0" /* Save stack pointer. */ 00165 ::"m" (runningThread->td_sp) /* */ 00166 ); 00167 00168 /* Select thread on top of the run queue. */ 00169 runningThread = runQueue; 00170 runningThread->td_state = TDS_RUNNING; 00171 00172 /* Restore context. */ 00173 __asm__ __volatile__( /* */ 00174 "@ Load context\n\t" /* */ 00175 "ldr sp, %0\n\t" /* Restore stack pointer. */ 00176 "ldmfd sp!, {r4}\n\t" /* Get saved status... */ 00177 "bic r4, r4, #0xC0" "\n\t" /* ...enable interrupts */ 00178 "msr spsr, r4\n\t" /* ...and save in spsr. */ 00179 "ldmfd sp!, {r4-r11, lr}\n\t" /* Restore registers. */ 00180 "movs pc, lr" /* Restore status and return. */ 00181 ::"m"(runningThread->td_sp) /* */ 00182 ); 00183 #if defined(NUT_CRITICAL_NESTING) && !defined(NUT_CRITICAL_NESTING_STACK) 00184 critical_nesting_level = 0; 00185 #endif 00186 } 00187 00208 HANDLE NutThreadCreate(char * name, void (*fn) (void *), void *arg, size_t stackSize) 00209 { 00210 u_char *threadMem; 00211 SWITCHFRAME *sf; 00212 ENTERFRAME *ef; 00213 NUTTHREADINFO *td; 00214 00215 /* 00216 * Allocate stack and thread info structure in one block. 00217 * We sill setup the following layout: 00218 * 00219 * Upper memory addresses. 00220 * 00221 * +--------------------+ 00222 * I I 00223 * I NUTTHREADINFO I 00224 * I I 00225 * td -> +-----+--------------+ <- Stack top 00226 * I I I 00227 * I T I ENTERFRAME I 00228 * I H I I 00229 * ef -> I R +--------------+ 00230 * I E I I ^ 00231 * I A I SWITCHFRAME I I 00232 * I D I I I pop moves up 00233 * sf -> I +--------------+ <- Initial stack pointer 00234 * I S I I I push moves down 00235 * I T I Application I I 00236 * I A I Stack I V 00237 * I C I I 00238 * I K I I 00239 * threadMem -> +-----+--------------+ <- Stack bottom 00240 * 00241 * Lower memory addresses. 00242 */ 00243 if ((threadMem = NutHeapAlloc(stackSize + sizeof(NUTTHREADINFO))) == 0) { 00244 return 0; 00245 } 00246 td = (NUTTHREADINFO *) (threadMem + stackSize); 00247 ef = (ENTERFRAME *) ((uptr_t) td - sizeof(ENTERFRAME)); 00248 sf = (SWITCHFRAME *) ((uptr_t) ef - sizeof(SWITCHFRAME)); 00249 00250 /* 00251 * Set predefined values at the stack bottom. May be used to detect 00252 * stack overflows. 00253 */ 00254 *((u_long *) threadMem) = DEADBEEF; 00255 *((u_long *) (threadMem + 4)) = DEADBEEF; 00256 *((u_long *) (threadMem + 8)) = DEADBEEF; 00257 *((u_long *) (threadMem + 12)) = DEADBEEF; 00258 00259 /* 00260 * Setup the entry frame to simulate C function entry. 00261 */ 00262 ef->cef_pc = (uptr_t) fn; 00263 ef->cef_r0 = (uptr_t) arg; 00264 00265 /* 00266 * Setup the switch frame. 00267 */ 00268 sf->csf_lr = (uptr_t) NutThreadEntry; 00269 sf->csf_cpsr = I_BIT | F_BIT | ARM_MODE_SVC; 00270 00271 /* 00272 * Initialize the thread info structure and insert it into the 00273 * thread list and the run queue. 00274 */ 00275 memcpy(td->td_name, name, sizeof(td->td_name) - 1); 00276 td->td_name[sizeof(td->td_name) - 1] = 0; 00277 td->td_state = TDS_READY; 00278 td->td_sp = (uptr_t) sf; 00279 td->td_priority = 64; 00280 td->td_memory = threadMem; 00281 td->td_timer = 0; 00282 td->td_queue = 0; 00283 00284 NutEnterCritical(); 00285 td->td_next = nutThreadList; 00286 nutThreadList = td; 00287 NutThreadAddPriQueue(td, (NUTTHREADINFO **) & runQueue); 00288 00289 /* 00290 * If no thread is running, then this is the first thread ever 00291 * created. In Nut/OS, the idle thread is created first. 00292 */ 00293 if (runningThread == 0) { 00294 /* This will never return. */ 00295 runningThread = runQueue; 00296 runningThread->td_state = TDS_RUNNING; 00297 /* Restore context. */ 00298 __asm__ __volatile__( /* */ 00299 "@ Load context\n\t" /* */ 00300 "ldr sp, %0\n\t" /* Restore stack pointer. */ 00301 "ldmfd sp!, {r4}\n\t" /* Get saved status... */ 00302 "bic r4, r4, #0xC0" "\n\t" /* ...enable interrupts */ 00303 "msr spsr, r4\n\t" /* ...and save in spsr. */ 00304 "ldmfd sp!, {r4-r11, lr}\n\t" /* Restore registers. */ 00305 "movs pc, lr" /* Restore status and return. */ 00306 ::"m"(runningThread->td_sp) /* */ 00307 ); 00308 } 00309 00310 /* 00311 * If current context is not in front of the run queue (highest 00312 * priority), then switch to the thread in front. 00313 */ 00314 if (runningThread != runQueue) { 00315 runningThread->td_state = TDS_READY; 00316 NutThreadSwitch(); 00317 } 00318 NutExitCritical(); 00319 00320 return td; 00321 } 00322