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$ 00036 * Revision 1.10 2009/01/19 18:55:12 haraldkipp 00037 * Added stack checking code. 00038 * 00039 * Revision 1.9 2009/01/17 11:26:37 haraldkipp 00040 * Getting rid of two remaining BSD types in favor of stdint. 00041 * Replaced 'u_int' by 'unsinged int' and 'uptr_t' by 'uintptr_t'. 00042 * 00043 * Revision 1.8 2009/01/16 19:45:42 haraldkipp 00044 * All ARM code is now running in system mode. 00045 * 00046 * Revision 1.7 2008/08/11 06:59:14 haraldkipp 00047 * BSD types replaced by stdint types (feature request #1282721). 00048 * 00049 * Revision 1.6 2008/07/07 11:04:27 haraldkipp 00050 * Configurable ways of handling critical sections for ARM targets. 00051 * 00052 * Revision 1.5 2006/03/16 19:06:16 haraldkipp 00053 * Use link register to jump into thread and use dedicated routine to 00054 * jump into the idle thread. The way we did start the idle thread 00055 * before, jumping into the middle of NutThreadSwitch(), doesn't work 00056 * with optimized code. 00057 * 00058 * Revision 1.4 2006/03/16 15:25:00 haraldkipp 00059 * Changed human readable strings from u_char to char to stop GCC 4 from 00060 * nagging about signedness. 00061 * 00062 * Revision 1.3 2005/10/24 09:09:41 haraldkipp 00063 * Switch frame reduced. 00064 * NutThreadEntry included in the execution path. 00065 * The 'cc' globber had been removed from the context switching asm macro, 00066 * after Michael Fischer found out that this fixes a problem with optimized 00067 * compilation. Unfortunately, compiler optimized binaries still seem to 00068 * run unreliable. 00069 * 00070 * Revision 1.2 2005/08/02 17:46:45 haraldkipp 00071 * Major API documentation update. 00072 * 00073 * Revision 1.1 2005/07/26 18:10:48 haraldkipp 00074 * Moved from os/thread.c 00075 * 00076 * Revision 1.1 2005/05/27 17:16:40 drsung 00077 * Moved the file. 00078 * 00079 * Revision 1.4 2005/04/05 17:54:36 haraldkipp 00080 * Moved from user mode to system mode. Probably breaks the GBA port. 00081 * 00082 * Revision 1.3 2004/11/08 19:15:33 haraldkipp 00083 * Made assembly includes look nicer. 00084 * Changed mode from supervisory to user supervisory, which seems to work 00085 * with the GBA. 00086 * Skipped entry frame, because it simply confuses me. :-) 00087 * 00088 * Revision 1.2 2004/09/08 10:19:31 haraldkipp 00089 * Tyou's support for the ARM7 00090 * 00091 * Revision 1.1 2004/03/16 16:48:46 haraldkipp 00092 * Added Jan Dubiec's H8/300 port. 00093 * 00094 * Revision 1.2 2004/02/18 16:32:48 drsung 00095 * Bugfix in NutThreadCreate. Thanks to Mike Cornelius. 00096 * 00097 * Revision 1.1 2004/02/01 18:49:48 haraldkipp 00098 * Added CPU family support 00099 * 00100 */ 00101 00102 #include <cfg/os.h> 00103 00104 #include <string.h> 00105 00106 #include <sys/atom.h> 00107 #include <sys/heap.h> 00108 #include <sys/thread.h> 00109 00114 00115 00123 typedef struct { 00124 uint32_t csf_cpsr; 00125 uint32_t csf_r4; 00126 uint32_t csf_r5; 00127 uint32_t csf_r6; 00128 uint32_t csf_r7; 00129 uint32_t csf_r8; 00130 uint32_t csf_r9; 00131 uint32_t csf_r10; 00132 uint32_t csf_r11; /* AKA fp */ 00133 uint32_t csf_lr; 00134 } SWITCHFRAME; 00135 00141 typedef struct { 00142 uint32_t cef_r0; 00143 uint32_t cef_pc; 00144 } ENTERFRAME; 00145 00149 static void NutThreadEntry(void) __attribute__ ((naked)); 00150 void NutThreadEntry(void) 00151 { 00152 /* Load argument in r0 and jump to thread entry. */ 00153 asm volatile ("ldmfd sp!, {r0, lr}\n\tbx lr":::"r0", "lr"); 00154 } 00155 00167 void NutThreadSwitch(void) __attribute__ ((naked)); 00168 void NutThreadSwitch(void) 00169 { 00170 __asm__ __volatile__ 00171 /* Save CPU context. */ 00172 ("@ Save context\n\t" 00173 /* Save registers. */ 00174 "stmfd sp!, {r4-r11, lr}\n\t" 00175 /* Save status. */ 00176 "mrs r0, cpsr\n\t" 00177 /* */ 00178 "stmfd sp!, {r0}\n\t" 00179 /* Save stack pointer. */ 00180 "str sp, %[td_sp]" 00181 /* Output. */ 00182 : 00183 /* Input. */ 00184 :[td_sp] "o"(runningThread->td_sp) 00185 /* Clobbers. */ 00186 :"r0", "memory"); 00187 00188 /* Select thread on top of the run queue. */ 00189 runningThread = runQueue; 00190 runningThread->td_state = TDS_RUNNING; 00191 #if defined(NUT_CRITICAL_NESTING) && !defined(NUT_CRITICAL_NESTING_STACK) 00192 critical_nesting_level = 0; 00193 #endif 00194 00195 __asm__ __volatile__ 00196 /* Restore context. */ 00197 ("@ Reload context\n\t" 00198 /* Restore stack pointer. */ 00199 "ldr sp, %[td_sp]\n\t" 00200 /* Get saved status... */ 00201 "ldmfd sp!, {r0}\n\t" 00202 /* ...enable interrupts */ 00203 "bic r0, r0, #0xC0\n\t" 00204 /* ...and save in spsr. */ 00205 "msr spsr, r0\n\t" 00206 /* Restore registers. */ 00207 "ldmfd sp!, {r4-r11, lr}\n\t" 00208 /* Restore status and return. */ 00209 "movs pc, lr" 00210 /* Output. */ 00211 : 00212 /* Input. */ 00213 :[td_sp] "m"(runningThread->td_sp) 00214 /* Clobbers. */ 00215 :"r0", "memory"); 00216 } 00217 00238 HANDLE NutThreadCreate(char * name, void (*fn) (void *), void *arg, size_t stackSize) 00239 { 00240 uint8_t *threadMem; 00241 SWITCHFRAME *sf; 00242 ENTERFRAME *ef; 00243 NUTTHREADINFO *td; 00244 00245 /* 00246 * Allocate stack and thread info structure in one block. 00247 * We sill setup the following layout: 00248 * 00249 * Upper memory addresses. 00250 * 00251 * +--------------------+ 00252 * I I 00253 * I NUTTHREADINFO I 00254 * I I 00255 * td -> +-----+--------------+ <- Stack top 00256 * I I I 00257 * I T I ENTERFRAME I 00258 * I H I I 00259 * ef -> I R +--------------+ 00260 * I E I I ^ 00261 * I A I SWITCHFRAME I I 00262 * I D I I I pop moves up 00263 * sf -> I +--------------+ <- Initial stack pointer 00264 * I S I I I push moves down 00265 * I T I Application I I 00266 * I A I Stack I V 00267 * I C I I 00268 * I K I I 00269 * threadMem -> +-----+--------------+ <- Stack bottom 00270 * 00271 * Lower memory addresses. 00272 */ 00273 if ((threadMem = NutHeapAlloc(stackSize + sizeof(NUTTHREADINFO))) == 0) { 00274 return 0; 00275 } 00276 td = (NUTTHREADINFO *) (threadMem + stackSize); 00277 ef = (ENTERFRAME *) ((uintptr_t) td - sizeof(ENTERFRAME)); 00278 sf = (SWITCHFRAME *) ((uintptr_t) ef - sizeof(SWITCHFRAME)); 00279 00280 /* 00281 * Set predefined values at the stack bottom. May be used to detect 00282 * stack overflows. 00283 */ 00284 #if defined(NUTDEBUG_CHECK_STACKMIN) || defined(NUTDEBUG_CHECK_STACK) 00285 { 00286 uint32_t *fip = (uint32_t *)threadMem; 00287 while (fip < (uint32_t *)sf) { 00288 *fip++ = DEADBEEF; 00289 } 00290 } 00291 #else 00292 *((uint32_t *) threadMem) = DEADBEEF; 00293 *((uint32_t *) (threadMem + 4)) = DEADBEEF; 00294 *((uint32_t *) (threadMem + 8)) = DEADBEEF; 00295 *((uint32_t *) (threadMem + 12)) = DEADBEEF; 00296 #endif 00297 00298 /* 00299 * Setup the entry frame to simulate C function entry. 00300 */ 00301 ef->cef_pc = (uintptr_t) fn; 00302 ef->cef_r0 = (uintptr_t) arg; 00303 00304 /* 00305 * Setup the switch frame. 00306 */ 00307 sf->csf_lr = (uintptr_t) NutThreadEntry; 00308 sf->csf_cpsr = ARM_CPSR_I_BIT | ARM_CPSR_F_BIT | ARM_MODE_SYS; 00309 00310 /* 00311 * Initialize the thread info structure and insert it into the 00312 * thread list and the run queue. 00313 */ 00314 memcpy(td->td_name, name, sizeof(td->td_name) - 1); 00315 td->td_name[sizeof(td->td_name) - 1] = 0; 00316 td->td_state = TDS_READY; 00317 td->td_sp = (uintptr_t) sf; 00318 td->td_priority = 64; 00319 td->td_memory = threadMem; 00320 td->td_timer = 0; 00321 td->td_queue = 0; 00322 00323 NutEnterCritical(); 00324 td->td_next = nutThreadList; 00325 nutThreadList = td; 00326 NutThreadAddPriQueue(td, (NUTTHREADINFO **) & runQueue); 00327 00328 /* 00329 * If no thread is running, then this is the first thread ever 00330 * created. In Nut/OS, the idle thread is created first. 00331 */ 00332 if (runningThread == 0) { 00333 /* This will never return. */ 00334 runningThread = runQueue; 00335 runningThread->td_state = TDS_RUNNING; 00336 00337 __asm__ __volatile__ 00338 /* Load initial idle thread context. */ 00339 ("@ Load context\n\t" 00340 /* Restore stack pointer. */ 00341 "ldr sp, %[td_sp]\n\t" 00342 /* Get saved status... */ 00343 "ldmfd sp!, {r0}\n\t" 00344 /* ...enable interrupts */ 00345 "bic r0, r0, #0xC0\n\t" 00346 /* ...and save in spsr. */ 00347 "msr spsr, r0\n\t" 00348 /* Restore registers. */ 00349 "ldmfd sp!, {r4-r11, lr}\n\t" 00350 /* Restore status and return. */ 00351 "movs pc, lr" 00352 /* Input. */ 00353 : 00354 /* Output. */ 00355 :[td_sp] "m" (runningThread->td_sp) 00356 /* Clobbers. */ 00357 :"r0", "memory"); 00358 } 00359 00360 /* 00361 * If current context is not in front of the run queue (highest 00362 * priority), then switch to the thread in front. 00363 */ 00364 if (runningThread != runQueue) { 00365 runningThread->td_state = TDS_READY; 00366 NutThreadSwitch(); 00367 } 00368 NutExitCritical(); 00369 00370 return td; 00371 } 00372