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.7 2008/08/11 06:59:14 haraldkipp 00037 * BSD types replaced by stdint types (feature request #1282721). 00038 * 00039 * Revision 1.6 2008/07/07 11:04:27 haraldkipp 00040 * Configurable ways of handling critical sections for ARM targets. 00041 * 00042 * Revision 1.5 2006/03/16 19:06:16 haraldkipp 00043 * Use link register to jump into thread and use dedicated routine to 00044 * jump into the idle thread. The way we did start the idle thread 00045 * before, jumping into the middle of NutThreadSwitch(), doesn't work 00046 * with optimized code. 00047 * 00048 * Revision 1.4 2006/03/16 15:25:00 haraldkipp 00049 * Changed human readable strings from u_char to char to stop GCC 4 from 00050 * nagging about signedness. 00051 * 00052 * Revision 1.3 2005/10/24 09:09:41 haraldkipp 00053 * Switch frame reduced. 00054 * NutThreadEntry included in the execution path. 00055 * The 'cc' globber had been removed from the context switching asm macro, 00056 * after Michael Fischer found out that this fixes a problem with optimized 00057 * compilation. Unfortunately, compiler optimized binaries still seem to 00058 * run unreliable. 00059 * 00060 * Revision 1.2 2005/08/02 17:46:45 haraldkipp 00061 * Major API documentation update. 00062 * 00063 * Revision 1.1 2005/07/26 18:10:48 haraldkipp 00064 * Moved from os/thread.c 00065 * 00066 * Revision 1.1 2005/05/27 17:16:40 drsung 00067 * Moved the file. 00068 * 00069 * Revision 1.4 2005/04/05 17:54:36 haraldkipp 00070 * Moved from user mode to system mode. Probably breaks the GBA port. 00071 * 00072 * Revision 1.3 2004/11/08 19:15:33 haraldkipp 00073 * Made assembly includes look nicer. 00074 * Changed mode from supervisory to user supervisory, which seems to work 00075 * with the GBA. 00076 * Skipped entry frame, because it simply confuses me. :-) 00077 * 00078 * Revision 1.2 2004/09/08 10:19:31 haraldkipp 00079 * Tyou's support for the ARM7 00080 * 00081 * Revision 1.1 2004/03/16 16:48:46 haraldkipp 00082 * Added Jan Dubiec's H8/300 port. 00083 * 00084 * Revision 1.2 2004/02/18 16:32:48 drsung 00085 * Bugfix in NutThreadCreate. Thanks to Mike Cornelius. 00086 * 00087 * Revision 1.1 2004/02/01 18:49:48 haraldkipp 00088 * Added CPU family support 00089 * 00090 */ 00091 00092 #include <cfg/os.h> 00093 00094 #include <string.h> 00095 00096 #include <sys/atom.h> 00097 #include <sys/heap.h> 00098 #include <sys/thread.h> 00099 00104 00105 00113 typedef struct { 00114 uint32_t csf_cpsr; 00115 uint32_t csf_r4; 00116 uint32_t csf_r5; 00117 uint32_t csf_r6; 00118 uint32_t csf_r7; 00119 uint32_t csf_r8; 00120 uint32_t csf_r9; 00121 uint32_t csf_r10; 00122 uint32_t csf_r11; /* AKA fp */ 00123 uint32_t csf_lr; 00124 } SWITCHFRAME; 00125 00131 typedef struct { 00132 uint32_t cef_r0; 00133 uint32_t cef_pc; 00134 } ENTERFRAME; 00135 00139 static void NutThreadEntry(void) __attribute__ ((naked)); 00140 void NutThreadEntry(void) 00141 { 00142 /* Load argument in r0 and jump to thread entry. */ 00143 asm volatile ("ldmfd sp!, {r0, lr}\n\tbx lr":::"r0", "lr"); 00144 } 00145 00146 00158 void NutThreadSwitch(void) __attribute__ ((naked)); 00159 void NutThreadSwitch(void) 00160 { 00161 /* Save CPU context. */ 00162 asm volatile ( /* */ 00163 "@ Save context\n\t" /* */ 00164 "stmfd sp!, {r4-r11, lr}\n\t" /* Save registers. */ 00165 "mrs r4, cpsr\n\t" /* Save status. */ 00166 "stmfd sp!, {r4}\n\t" /* */ 00167 "str sp, %0" /* Save stack pointer. */ 00168 ::"m" (runningThread->td_sp) /* */ 00169 ); 00170 00171 /* Select thread on top of the run queue. */ 00172 runningThread = runQueue; 00173 runningThread->td_state = TDS_RUNNING; 00174 00175 /* Restore context. */ 00176 __asm__ __volatile__( /* */ 00177 "@ Load context\n\t" /* */ 00178 "ldr sp, %0\n\t" /* Restore stack pointer. */ 00179 "ldmfd sp!, {r4}\n\t" /* Get saved status... */ 00180 "bic r4, r4, #0xC0" "\n\t" /* ...enable interrupts */ 00181 "msr spsr, r4\n\t" /* ...and save in spsr. */ 00182 "ldmfd sp!, {r4-r11, lr}\n\t" /* Restore registers. */ 00183 "movs pc, lr" /* Restore status and return. */ 00184 ::"m"(runningThread->td_sp) /* */ 00185 ); 00186 #if defined(NUT_CRITICAL_NESTING) && !defined(NUT_CRITICAL_NESTING_STACK) 00187 critical_nesting_level = 0; 00188 #endif 00189 } 00190 00211 HANDLE NutThreadCreate(char * name, void (*fn) (void *), void *arg, size_t stackSize) 00212 { 00213 uint8_t *threadMem; 00214 SWITCHFRAME *sf; 00215 ENTERFRAME *ef; 00216 NUTTHREADINFO *td; 00217 00218 /* 00219 * Allocate stack and thread info structure in one block. 00220 * We sill setup the following layout: 00221 * 00222 * Upper memory addresses. 00223 * 00224 * +--------------------+ 00225 * I I 00226 * I NUTTHREADINFO I 00227 * I I 00228 * td -> +-----+--------------+ <- Stack top 00229 * I I I 00230 * I T I ENTERFRAME I 00231 * I H I I 00232 * ef -> I R +--------------+ 00233 * I E I I ^ 00234 * I A I SWITCHFRAME I I 00235 * I D I I I pop moves up 00236 * sf -> I +--------------+ <- Initial stack pointer 00237 * I S I I I push moves down 00238 * I T I Application I I 00239 * I A I Stack I V 00240 * I C I I 00241 * I K I I 00242 * threadMem -> +-----+--------------+ <- Stack bottom 00243 * 00244 * Lower memory addresses. 00245 */ 00246 if ((threadMem = NutHeapAlloc(stackSize + sizeof(NUTTHREADINFO))) == 0) { 00247 return 0; 00248 } 00249 td = (NUTTHREADINFO *) (threadMem + stackSize); 00250 ef = (ENTERFRAME *) ((uptr_t) td - sizeof(ENTERFRAME)); 00251 sf = (SWITCHFRAME *) ((uptr_t) ef - sizeof(SWITCHFRAME)); 00252 00253 /* 00254 * Set predefined values at the stack bottom. May be used to detect 00255 * stack overflows. 00256 */ 00257 *((uint32_t *) threadMem) = DEADBEEF; 00258 *((uint32_t *) (threadMem + 4)) = DEADBEEF; 00259 *((uint32_t *) (threadMem + 8)) = DEADBEEF; 00260 *((uint32_t *) (threadMem + 12)) = DEADBEEF; 00261 00262 /* 00263 * Setup the entry frame to simulate C function entry. 00264 */ 00265 ef->cef_pc = (uptr_t) fn; 00266 ef->cef_r0 = (uptr_t) arg; 00267 00268 /* 00269 * Setup the switch frame. 00270 */ 00271 sf->csf_lr = (uptr_t) NutThreadEntry; 00272 sf->csf_cpsr = I_BIT | F_BIT | ARM_MODE_SVC; 00273 00274 /* 00275 * Initialize the thread info structure and insert it into the 00276 * thread list and the run queue. 00277 */ 00278 memcpy(td->td_name, name, sizeof(td->td_name) - 1); 00279 td->td_name[sizeof(td->td_name) - 1] = 0; 00280 td->td_state = TDS_READY; 00281 td->td_sp = (uptr_t) sf; 00282 td->td_priority = 64; 00283 td->td_memory = threadMem; 00284 td->td_timer = 0; 00285 td->td_queue = 0; 00286 00287 NutEnterCritical(); 00288 td->td_next = nutThreadList; 00289 nutThreadList = td; 00290 NutThreadAddPriQueue(td, (NUTTHREADINFO **) & runQueue); 00291 00292 /* 00293 * If no thread is running, then this is the first thread ever 00294 * created. In Nut/OS, the idle thread is created first. 00295 */ 00296 if (runningThread == 0) { 00297 /* This will never return. */ 00298 runningThread = runQueue; 00299 runningThread->td_state = TDS_RUNNING; 00300 /* Restore context. */ 00301 __asm__ __volatile__( /* */ 00302 "@ Load context\n\t" /* */ 00303 "ldr sp, %0\n\t" /* Restore stack pointer. */ 00304 "ldmfd sp!, {r4}\n\t" /* Get saved status... */ 00305 "bic r4, r4, #0xC0" "\n\t" /* ...enable interrupts */ 00306 "msr spsr, r4\n\t" /* ...and save in spsr. */ 00307 "ldmfd sp!, {r4-r11, lr}\n\t" /* Restore registers. */ 00308 "movs pc, lr" /* Restore status and return. */ 00309 ::"m"(runningThread->td_sp) /* */ 00310 ); 00311 } 00312 00313 /* 00314 * If current context is not in front of the run queue (highest 00315 * priority), then switch to the thread in front. 00316 */ 00317 if (runningThread != runQueue) { 00318 runningThread->td_state = TDS_READY; 00319 NutThreadSwitch(); 00320 } 00321 NutExitCritical(); 00322 00323 return td; 00324 } 00325