Nut/OS  4.10.3
API Reference
context_icc.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.11  2009/01/17 11:26:38  haraldkipp
00037  * Getting rid of two remaining BSD types in favor of stdint.
00038  * Replaced 'u_int' by 'unsinged int' and 'uptr_t' by 'uintptr_t'.
00039  *
00040  * Revision 1.10  2008/08/11 11:51:19  thiagocorrea
00041  * Preliminary Atmega2560 compile options, but not yet supported.
00042  * It builds, but doesn't seam to run properly at this time.
00043  *
00044  * Revision 1.9  2008/08/11 06:59:39  haraldkipp
00045  * BSD types replaced by stdint types (feature request #1282721).
00046  *
00047  * Revision 1.8  2008/06/15 17:00:21  haraldkipp
00048  * Rolled back to version 1.6.
00049  *
00050  * Revision 1.6  2006/09/29 12:27:31  haraldkipp
00051  * All code should use dedicated stack allocation routines. For targets
00052  * allocating stack from the normal heap the API calls are remapped by
00053  * preprocessor macros.
00054  *
00055  * Revision 1.5  2006/07/10 08:46:52  haraldkipp
00056  * Properly set 3 byte return address for extended AVR.
00057  *
00058  * Revision 1.4  2006/02/08 15:20:21  haraldkipp
00059  * ATmega2561 Support
00060  *
00061  * Revision 1.3  2005/10/04 05:17:15  hwmaier
00062  * Added support for separating stack and conventional heap as required by AT09CAN128 MCUs
00063  *
00064  * Revision 1.2  2005/08/02 17:46:46  haraldkipp
00065  * Major API documentation update.
00066  *
00067  * Revision 1.1  2005/07/26 18:10:49  haraldkipp
00068  * Moved from os/thread.c
00069  *
00070  * Revision 1.2  2005/07/14 08:55:57  freckle
00071  * Rewrote CS in NutThreadCreate
00072  *
00073  * Revision 1.1  2005/05/27 17:17:31  drsung
00074  * Moved the file
00075  *
00076  * Revision 1.6  2005/04/30 16:42:42  chaac
00077  * Fixed bug in handling of NUTDEBUG. Added include for cfg/os.h. If NUTDEBUG
00078  * is defined in NutConf, it will make effect where it is used.
00079  *
00080  * Revision 1.5  2005/02/16 19:55:18  haraldkipp
00081  * Ready-to-run queue handling removed from interrupt context.
00082  * Avoid AVRGCC prologue and epilogue code. Thanks to Pete Allinson.
00083  *
00084  * Revision 1.4  2005/02/10 07:06:48  hwmaier
00085  * Changes to incorporate support for AT90CAN128 CPU
00086  *
00087  * Revision 1.3  2004/09/22 08:15:56  haraldkipp
00088  * Speparate IRQ stack configurable
00089  *
00090  * Revision 1.2  2004/04/25 17:06:17  drsung
00091  * Separate IRQ stack now compatible with nested interrupts.
00092  *
00093  * Revision 1.1  2004/03/16 16:48:46  haraldkipp
00094  * Added Jan Dubiec's H8/300 port.
00095  *
00096  * Revision 1.2  2004/02/18 16:32:48  drsung
00097  * Bugfix in NutThreadCreate. Thanks to Mike Cornelius.
00098  *
00099  * Revision 1.1  2004/02/01 18:49:48  haraldkipp
00100  * Added CPU family support
00101  *
00102  */
00103 
00104 #include <cfg/os.h>
00105 #include <cfg/memory.h>
00106 
00107 #include <string.h>
00108 
00109 #include <sys/atom.h>
00110 #include <sys/heap.h>
00111 #include <sys/thread.h>
00112 
00117 
00124 typedef struct {
00125     uint8_t csf_r29;
00126     uint8_t csf_r28;
00127     uint8_t csf_r23;
00128     uint8_t csf_r22;
00129     uint8_t csf_r21;
00130     uint8_t csf_r20;
00131     uint8_t csf_r15;
00132     uint8_t csf_r14;
00133     uint8_t csf_r13;
00134     uint8_t csf_r12;
00135     uint8_t csf_r11;
00136     uint8_t csf_r10;
00137 #ifdef __AVR_ATmega2561__
00138     uint8_t csf_pcex;
00139 #endif
00140     uint8_t csf_pchi;
00141     uint8_t csf_pclo;
00142 } SWITCHFRAME;
00143 
00149 typedef struct {
00150     uint8_t cef_arghi;
00151     uint8_t cef_arglo;
00155     uint8_t cef_yhi;
00156     uint8_t cef_ylo;
00157     uint8_t cef_rampz;
00158     uint8_t cef_sreg;
00159     uint8_t cef_r1;
00160 #ifdef __AVR_3_BYTE_PC__
00161     uint8_t cef_pcex;
00162 #endif
00163     uint8_t cef_pchi;
00164     uint8_t cef_pclo;
00165 } ENTERFRAME;
00166 
00167 
00168 /*
00169  * This code is executed when entering a thread.
00170  */
00171 static void NutThreadEntry(void)
00172 {
00173     asm("pop r17");             // first parameter for ICC
00174     asm("pop r16");
00175     asm("pop r29");             // SW-Stack; Y-Register
00176     asm("pop r28");
00177     asm("pop r0");              // r0 = _tmp_reg_
00178     asm("out 0x3B, r0");        // RAMPZ; replace with define later
00179     asm("pop r0");
00180     asm("pop r1");              // r1 = _zero_reg_
00181     asm("out 0x3F, r0");        // SREG; replace with define later
00182     asm("reti");
00183 }
00184 
00196 void NutThreadSwitch(void)
00197 {
00198     /*
00199      * Save all CPU registers.
00200      */
00201     register uint8_t i = 0;
00202     register uint8_t j = 0;
00203 
00204     asm("push r10");
00205     asm("push r11");
00206     asm("push r12");
00207     asm("push r13");
00208     asm("push r14");
00209     asm("push r15");
00210     asm("push r20");
00211     asm("push r21");
00212     asm("push r22");
00213     asm("push r23");
00214     asm("push r28");
00215     asm("push r29");
00216     asm("in %i, $3D");          // SPL
00217     asm("in %j, $3E");          // SPH
00218 
00219     runningThread->td_sp = (((uint16_t) j) << 8) & 0xFF00 | (i & 0xFF);
00220 
00221     /*
00222      * This defines a global label, which may be called
00223      * as an entry point into this function.
00224      */
00225     asm(".globl thread_start");
00226     asm("thread_start:");
00227 
00228     /*
00229      * Reload CPU registers from the thread in front
00230      * of the run queue.
00231      */
00232     runningThread = runQueue;
00233     runningThread->td_state = TDS_RUNNING;
00234 
00235     i = (uint8_t) (runningThread->td_sp & 0xFF);
00236     j = (uint8_t) ((runningThread->td_sp >> 8) & 0xFF);
00237 
00238     asm("out $3D, %i");         // SPL
00239     asm("out $3E, %j");         // SPH
00240     asm("pop r29");
00241     asm("pop r28");
00242     asm("pop r23");
00243     asm("pop r22");
00244     asm("pop r21");
00245     asm("pop r20");
00246     asm("pop r15");
00247     asm("pop r14");
00248     asm("pop r13");
00249     asm("pop r12");
00250     asm("pop r11");
00251     asm("pop r10");
00252 }
00253 
00272 HANDLE NutThreadCreate(uint8_t * name, void (*fn) (void *), void *arg, size_t stackSize)
00273 {
00274     uint8_t *threadMem;
00275     SWITCHFRAME *sf;
00276     ENTERFRAME *ef;
00277     NUTTHREADINFO *td;
00278     uint16_t yreg;
00279     const uint8_t *paddr;
00280 
00281     /*
00282      * Allocate stack and thread info structure in one block.
00283      */
00284     if ((threadMem = NutStackAlloc(stackSize + sizeof(NUTTHREADINFO))) == 0) {
00285         return 0;
00286     }
00287 
00288     td = (NUTTHREADINFO *) (threadMem + stackSize);
00289     ef = (ENTERFRAME *) ((uint16_t) td - sizeof(ENTERFRAME));
00290     sf = (SWITCHFRAME *) ((uint16_t) ef - sizeof(SWITCHFRAME));
00291 
00292 
00293     memcpy(td->td_name, name, sizeof(td->td_name) - 1);
00294     td->td_name[sizeof(td->td_name) - 1] = 0;
00295     td->td_sp = (uint16_t) sf - 1;
00296     td->td_memory = threadMem;
00297 #if defined(NUTDEBUG_CHECK_STACKMIN) || defined(NUTDEBUG_CHECK_STACK)
00298     {
00299         uint32_t *fip = (uint32_t *)threadMem;
00300         while (fip < (uint32_t *)sf) {
00301             *fip++ = DEADBEEF;
00302         }
00303     }
00304 #else
00305     *((uint32_t *) threadMem) = DEADBEEF;
00306     *((uint32_t *) (threadMem + 4)) = DEADBEEF;
00307     *((uint32_t *) (threadMem + 8)) = DEADBEEF;
00308     *((uint32_t *) (threadMem + 12)) = DEADBEEF;
00309 #endif
00310     td->td_priority = 64;
00311 
00312     /*
00313      * Setup entry frame to simulate C function entry.
00314      */
00315     paddr = (const uint8_t *) fn;
00316     ef->cef_pclo = *paddr;
00317     ef->cef_pchi = *(paddr + 1);
00318 #ifdef __AVR_3_BYTE_PC__
00319     ef->cef_pcex = *(paddr + 2);
00320 #endif
00321     ef->cef_sreg = 0x80;
00322     ef->cef_rampz = 0;
00323     ef->cef_r1 = 0;
00324 
00325     ef->cef_arglo = (uint8_t) (((uint16_t) arg) & 0xff);
00326     ef->cef_arghi = (uint8_t) (((uint16_t) arg) >> 8);
00327 
00328     yreg = td->td_sp - 40;
00329     ef->cef_yhi = (uint8_t) (yreg >> 8);
00330     ef->cef_ylo = (uint8_t) (yreg & 0xFF);
00331 
00332     paddr = (const uint8_t *) NutThreadEntry;
00333     sf->csf_pclo = *paddr;
00334     sf->csf_pchi = *(paddr + 1);
00335 #ifdef __AVR_3_BYTE_PC__
00336     sf->csf_pcex = *(paddr + 2);
00337 #endif
00338 
00339     /*
00340      * Insert into the thread list and the run queue.
00341      */
00342 
00343     td->td_next = nutThreadList;
00344     nutThreadList = td;
00345     td->td_state = TDS_READY;
00346     td->td_timer = 0;
00347     td->td_queue = 0;
00348 #ifdef NUTDEBUG
00349     if (__os_trf)
00350         fprintf(__os_trs, "Cre<%04x>", (uintptr_t) td);
00351 #endif
00352 
00353     NutThreadAddPriQueue(td, (NUTTHREADINFO **) & runQueue);
00354 
00355 #ifdef NUTDEBUG
00356     if (__os_trf) {
00357         NutDumpThreadList(__os_trs);
00358         //NutDumpThreadQueue(__os_trs, runQueue);
00359     }
00360 #endif
00361 
00362     /*
00363      * If no thread is active, switch to new thread.
00364      */
00365     if (runningThread == 0) {
00366         NutEnterCritical();
00367         asm("rjmp thread_start");
00368         /* we will never come back here .. */
00369     }
00370 
00371     /*
00372      * If current context is not in front of
00373      * the run queue (highest priority), then
00374      * switch to the thread in front.
00375      */
00376     if (runningThread != runQueue) {
00377         runningThread->td_state = TDS_READY;
00378 #ifdef NUTDEBUG
00379         if (__os_trf)
00380             fprintf(__os_trs, "New<%04x %04x>", (uintptr_t) runningThread, (uintptr_t) runQueue);
00381 #endif
00382         NutEnterCritical();
00383         NutThreadSwitch();
00384         NutExitCritical();
00385     }
00386 
00387     return td;
00388 }
00389