context.c

Go to the documentation of this file.
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 

© 2000-2007 by egnite Software GmbH - visit http://www.ethernut.de/