Nut/OS  4.10.3
API Reference
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$
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