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