Nut/OS  4.10.3
API Reference
context_gcc.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.13  2009/02/06 15:41:00  haraldkipp
00037  * Added stack checking code for AVR.
00038  *
00039  * Revision 1.12  2009/01/17 11:26:38  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.11  2008/08/11 06:59:39  haraldkipp
00044  * BSD types replaced by stdint types (feature request #1282721).
00045  *
00046  * Revision 1.10  2008/06/15 16:58:39  haraldkipp
00047  * Rolled back to version 1.8.
00048  *
00049  * Revision 1.8  2007/05/02 11:25:08  haraldkipp
00050  * Minor typo with big impact. Extended PC never set in context switch
00051  * frame.
00052  * Replaced __AVR_ATmega2561__ by __AVR_3_BYTE_PC__.
00053  * Removed unused local variable paddr.
00054  *
00055  * Revision 1.7  2007/04/12 09:19:00  haraldkipp
00056  * Added extended program counter byte for the ATmega2561. Code above 128k not
00057  * working, though.
00058  *
00059  * Revision 1.6  2006/09/29 12:27:31  haraldkipp
00060  * All code should use dedicated stack allocation routines. For targets
00061  * allocating stack from the normal heap the API calls are remapped by
00062  * preprocessor macros.
00063  *
00064  * Revision 1.5  2006/03/16 15:25:09  haraldkipp
00065  * Changed human readable strings from u_char to char to stop GCC 4 from
00066  * nagging about signedness.
00067  *
00068  * Revision 1.4  2005/10/04 05:17:15  hwmaier
00069  * Added support for separating stack and conventional heap as required by AT09CAN128 MCUs
00070  *
00071  * Revision 1.3  2005/08/18 15:36:36  christianwelzel
00072  * Fixed bug in handling of NUTDEBUG.
00073  *
00074  * Revision 1.2  2005/08/02 17:46:46  haraldkipp
00075  * Major API documentation update.
00076  *
00077  * Revision 1.1  2005/07/26 18:10:48  haraldkipp
00078  * Moved from os/thread.c
00079  *
00080  * Revision 1.2  2005/07/14 08:55:57  freckle
00081  * Rewrote CS in NutThreadCreate
00082  *
00083  * Revision 1.1  2005/05/27 17:17:31  drsung
00084  * Moved the file
00085  *
00086  * Revision 1.6  2005/04/30 16:42:42  chaac
00087  * Fixed bug in handling of NUTDEBUG. Added include for cfg/os.h. If NUTDEBUG
00088  * is defined in NutConf, it will make effect where it is used.
00089  *
00090  * Revision 1.5  2005/02/16 19:55:18  haraldkipp
00091  * Ready-to-run queue handling removed from interrupt context.
00092  * Avoid AVRGCC prologue and epilogue code. Thanks to Pete Allinson.
00093  *
00094  * Revision 1.4  2005/02/10 07:06:48  hwmaier
00095  * Changes to incorporate support for AT90CAN128 CPU
00096  *
00097  * Revision 1.3  2004/09/22 08:15:56  haraldkipp
00098  * Speparate IRQ stack configurable
00099  *
00100  * Revision 1.2  2004/04/25 17:06:17  drsung
00101  * Separate IRQ stack now compatible with nested interrupts.
00102  *
00103  * Revision 1.1  2004/03/16 16:48:46  haraldkipp
00104  * Added Jan Dubiec's H8/300 port.
00105  *
00106  * Revision 1.2  2004/02/18 16:32:48  drsung
00107  * Bugfix in NutThreadCreate. Thanks to Mike Cornelius.
00108  *
00109  * Revision 1.1  2004/02/01 18:49:48  haraldkipp
00110  * Added CPU family support
00111  *
00112  */
00113 
00114 #include <cfg/os.h>
00115 #include <cfg/memory.h>
00116 
00117 #include <string.h>
00118 
00119 #include <sys/atom.h>
00120 #include <sys/heap.h>
00121 #include <sys/thread.h>
00122 
00123 /* Support for separate irq stack only for avr-gcc */
00124 #include <dev/irqstack.h>
00125 
00126 #ifdef NUTDEBUG
00127 #include <sys/osdebug.h>
00128 #endif
00129 
00134 
00135 #ifdef IRQSTACK_SIZE
00136 
00143 uint16_t _irqstackdec = 128;
00144 #endif /* #ifdef IRQSTACK_SIZE */
00145 
00152 typedef struct {
00153     uint8_t csf_r29;
00154     uint8_t csf_r28;
00155     uint8_t csf_r17;
00156     uint8_t csf_r16;
00157     uint8_t csf_r15;
00158     uint8_t csf_r14;
00159     uint8_t csf_r13;
00160     uint8_t csf_r12;
00161     uint8_t csf_r11;
00162     uint8_t csf_r10;
00163     uint8_t csf_r9;
00164     uint8_t csf_r8;
00165     uint8_t csf_r7;
00166     uint8_t csf_r6;
00167     uint8_t csf_r5;
00168     uint8_t csf_r4;
00169     uint8_t csf_r3;
00170     uint8_t csf_r2;
00171 #ifdef __AVR_3_BYTE_PC__
00172     uint8_t csf_pcex;
00173 #endif
00174     uint8_t csf_pchi;
00175     uint8_t csf_pclo;
00176 } SWITCHFRAME;
00177 
00183 typedef struct {
00184     uint8_t cef_arghi;
00185     uint8_t cef_arglo;
00186     uint8_t cef_rampz;
00187     uint8_t cef_sreg;
00188     uint8_t cef_r1;
00189 #ifdef __AVR_3_BYTE_PC__
00190     uint8_t cef_pcex;
00191 #endif
00192     uint8_t cef_pchi;
00193     uint8_t cef_pclo;
00194 } ENTERFRAME;
00195 
00196 #define LONG_PTR_P(lp, mem_p) \
00197     __asm__ __volatile__("ldi %A0, lo8("#mem_p ")" "\n\t" \
00198                          "ldi %B0, hi8("#mem_p ")" "\n\t" \
00199                          "ldi %C0, hh8("#mem_p ")" "\n\t" \
00200                          "clr %D0" \
00201                          :"=d" (lp))
00202 
00203 /*
00204  * This code is executed when entering a thread.
00205  */
00206 static void NutThreadEntry(void) __attribute__ ((naked));
00207 static void NutThreadEntry(void)
00208 {
00209     __asm__ __volatile__("pop r25" "\n\t"       /* first parameter hi-byte */
00210                          "pop r24" "\n\t"       /* first parameter lo-byte */
00211                          "pop __tmp_reg__" "\n\t"       /* Get RAMPZ */
00212                          "out %0, __tmp_reg__" "\n\t"   /* Restore RAMPZ */
00213                          "pop __tmp_reg__" "\n\t"       /* Get SREG */
00214                          "out %1, __tmp_reg__" "\n\t"   /* Restore SREG */
00215                          "pop __zero_reg__" "\n\t"      /* Zero register */
00216                          "reti" "\n\t"  /* enables interrupts */
00217                          ::"I" _SFR_IO_ADDR(RAMPZ), "I" _SFR_IO_ADDR(SREG)
00218         );
00219 }
00220 
00221 
00233 void NutThreadSwitch(void) __attribute__ ((noinline)) __attribute__ ((naked));
00234 void NutThreadSwitch(void)
00235 {
00236     /*
00237      * Save all CPU registers.
00238      */
00239     asm volatile ("push r2" "\n\t"              /* */
00240               "push r3" "\n\t"              /* */
00241               "push r4" "\n\t"              /* */
00242               "push r5" "\n\t"              /* */
00243               "push r6" "\n\t"              /* */
00244               "push r7" "\n\t"              /* */
00245               "push r8" "\n\t"              /* */
00246               "push r9" "\n\t"              /* */
00247               "push r10" "\n\t"             /* */
00248               "push r11" "\n\t"             /* */
00249               "push r12" "\n\t"             /* */
00250               "push r13" "\n\t"             /* */
00251               "push r14" "\n\t"             /* */
00252               "push r15" "\n\t"             /* */
00253               "push r16" "\n\t"             /* */
00254               "push r17" "\n\t"             /* */
00255               "push r28" "\n\t"             /* */
00256               "push r29" "\n\t"             /* */
00257               "in %A0, %1" "\n\t"           /* */
00258               "in %B0, %2" "\n\t"           /* */
00259               :"=r" (runningThread->td_sp)  /* */
00260               :"I" _SFR_IO_ADDR(SPL),       /* */
00261                "I" _SFR_IO_ADDR(SPH)        /* */
00262     );
00263 
00264     /*
00265      * This defines a global label, which may be called
00266      * as an entry point into this function.
00267      */
00268     asm volatile (".global thread_start\n"  /* */
00269                   "thread_start:\n\t"::);
00270 
00271     /*
00272      * Reload CPU registers from the thread on top of the run queue.
00273      */
00274     runningThread = runQueue;
00275     runningThread->td_state = TDS_RUNNING;
00276     asm volatile ("out %1, %A0" "\n\t"          /* */
00277               "out %2, %B0" "\n\t"          /* */
00278               "pop r29" "\n\t"              /* */
00279               "pop r28" "\n\t"              /* */
00280               "pop r17" "\n\t"              /* */
00281               "pop r16" "\n\t"              /* */
00282               "pop r15" "\n\t"              /* */
00283               "pop r14" "\n\t"              /* */
00284               "pop r13" "\n\t"              /* */
00285               "pop r12" "\n\t"              /* */
00286               "pop r11" "\n\t"              /* */
00287               "pop r10" "\n\t"              /* */
00288               "pop r9" "\n\t"               /* */
00289               "pop r8" "\n\t"               /* */
00290               "pop r7" "\n\t"               /* */
00291               "pop r6" "\n\t"               /* */
00292               "pop r5" "\n\t"               /* */
00293               "pop r4" "\n\t"               /* */
00294               "pop r3" "\n\t"               /* */
00295               "pop r2" "\n\t"               /* */
00296               "ret" "\n\t"                  /* */
00297               ::"r" (runningThread->td_sp), /* */
00298                 "I" _SFR_IO_ADDR(SPL),      /* */
00299                 "I" _SFR_IO_ADDR(SPH)       /* */
00300     );
00301 }
00302 
00321 HANDLE NutThreadCreate(char * name, void (*fn) (void *), void *arg, size_t stackSize)
00322 {
00323     uint8_t *threadMem;
00324     SWITCHFRAME *sf;
00325     ENTERFRAME *ef;
00326     NUTTHREADINFO *td;
00327 #ifdef IRQSTACK_SIZE
00328     if (stackSize > _irqstackdec + 128) stackSize -= _irqstackdec;
00329 #endif
00330 
00331     /*
00332      * Allocate stack and thread info structure in one block.
00333      */
00334     if ((threadMem = NutStackAlloc(stackSize + sizeof(NUTTHREADINFO))) == 0) {
00335         return 0;
00336     }
00337 
00338     td = (NUTTHREADINFO *) (threadMem + stackSize);
00339     ef = (ENTERFRAME *) ((uint16_t) td - sizeof(ENTERFRAME));
00340     sf = (SWITCHFRAME *) ((uint16_t) ef - sizeof(SWITCHFRAME));
00341 
00342 
00343     memcpy(td->td_name, name, sizeof(td->td_name) - 1);
00344     td->td_name[sizeof(td->td_name) - 1] = 0;
00345     td->td_sp = (uint16_t) sf - 1;
00346     td->td_memory = threadMem;
00347 
00348     /* 
00349      * Set predefined values at the stack bottom. May be used to detect
00350      * stack overflows.
00351      */
00352 #if defined(NUTDEBUG_CHECK_STACKMIN) || defined(NUTDEBUG_CHECK_STACK)
00353     {
00354         uint32_t *fip = (uint32_t *)threadMem;
00355         while (fip < (uint32_t *)sf) {
00356             *fip++ = DEADBEEF;
00357         }
00358     }
00359 #else
00360     *((uint32_t *) threadMem) = DEADBEEF;
00361     *((uint32_t *) (threadMem + 4)) = DEADBEEF;
00362     *((uint32_t *) (threadMem + 8)) = DEADBEEF;
00363     *((uint32_t *) (threadMem + 12)) = DEADBEEF;
00364 #endif
00365     td->td_priority = 64;
00366 
00367     /*
00368      * Setup entry frame to simulate C function entry.
00369      */
00370 #ifdef __AVR_3_BYTE_PC__
00371     ef->cef_pcex = 0;
00372 #endif
00373     ef->cef_pchi = (uint8_t) (((uint16_t) fn) >> 8);
00374     ef->cef_pclo = (uint8_t) (((uint16_t) fn) & 0xff);
00375     ef->cef_sreg = 0x80;
00376     ef->cef_rampz = 0;
00377     ef->cef_r1 = 0;
00378 
00379     ef->cef_arglo = (uint8_t) (((uint16_t) arg) & 0xff);
00380     ef->cef_arghi = (uint8_t) (((uint16_t) arg) >> 8);
00381 
00382 #ifdef __AVR_3_BYTE_PC__
00383     sf->csf_pcex = 0;
00384 #endif
00385     sf->csf_pchi = (uint8_t) (((uint16_t) NutThreadEntry) >> 8);
00386     sf->csf_pclo = (uint8_t) (((uint16_t) NutThreadEntry) & 0xff);
00387 
00388     /*
00389      * Insert into the thread list and the run queue.
00390      */
00391 
00392     td->td_next = nutThreadList;
00393     nutThreadList = td;
00394     td->td_state = TDS_READY;
00395     td->td_timer = 0;
00396     td->td_queue = 0;
00397 #ifdef NUTDEBUG
00398     if (__os_trf)
00399         fprintf(__os_trs, "Cre<%04x>", (uintptr_t) td);
00400 #endif
00401 
00402     NutThreadAddPriQueue(td, (NUTTHREADINFO **) & runQueue);
00403 
00404 #ifdef NUTDEBUG
00405     if (__os_trf) {
00406         NutDumpThreadList(__os_trs);
00407         //NutDumpThreadQueue(__os_trs, runQueue);
00408     }
00409 #endif
00410 
00411     /*
00412      * If no thread is active, switch to new thread.
00413      */
00414     if (runningThread == 0) {
00415         NutEnterCritical();
00416         asm volatile ("rjmp thread_start\n\t"::);
00417         /* we will never come back here .. */
00418     }
00419 
00420     /*
00421      * If current context is not in front of
00422      * the run queue (highest priority), then
00423      * switch to the thread in front.
00424      */
00425     if (runningThread != runQueue) {
00426         runningThread->td_state = TDS_READY;
00427 #ifdef NUTDEBUG
00428         if (__os_trf)
00429             fprintf(__os_trs, "New<%04x %04x>", (uintptr_t) runningThread, (uintptr_t) runQueue);
00430 #endif
00431         NutEnterCritical();
00432         NutThreadSwitch();
00433         NutExitCritical();
00434     }
00435 
00436     return td;
00437 }
00438