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: context_icc.c,v $
00036  * Revision 1.8  2008/06/15 17:00:21  haraldkipp
00037  * Rolled back to version 1.6.
00038  *
00039  * Revision 1.6  2006/09/29 12:27:31  haraldkipp
00040  * All code should use dedicated stack allocation routines. For targets
00041  * allocating stack from the normal heap the API calls are remapped by
00042  * preprocessor macros.
00043  *
00044  * Revision 1.5  2006/07/10 08:46:52  haraldkipp
00045  * Properly set 3 byte return address for extended AVR.
00046  *
00047  * Revision 1.4  2006/02/08 15:20:21  haraldkipp
00048  * ATmega2561 Support
00049  *
00050  * Revision 1.3  2005/10/04 05:17:15  hwmaier
00051  * Added support for separating stack and conventional heap as required by AT09CAN128 MCUs
00052  *
00053  * Revision 1.2  2005/08/02 17:46:46  haraldkipp
00054  * Major API documentation update.
00055  *
00056  * Revision 1.1  2005/07/26 18:10:49  haraldkipp
00057  * Moved from os/thread.c
00058  *
00059  * Revision 1.2  2005/07/14 08:55:57  freckle
00060  * Rewrote CS in NutThreadCreate
00061  *
00062  * Revision 1.1  2005/05/27 17:17:31  drsung
00063  * Moved the file
00064  *
00065  * Revision 1.6  2005/04/30 16:42:42  chaac
00066  * Fixed bug in handling of NUTDEBUG. Added include for cfg/os.h. If NUTDEBUG
00067  * is defined in NutConf, it will make effect where it is used.
00068  *
00069  * Revision 1.5  2005/02/16 19:55:18  haraldkipp
00070  * Ready-to-run queue handling removed from interrupt context.
00071  * Avoid AVRGCC prologue and epilogue code. Thanks to Pete Allinson.
00072  *
00073  * Revision 1.4  2005/02/10 07:06:48  hwmaier
00074  * Changes to incorporate support for AT90CAN128 CPU
00075  *
00076  * Revision 1.3  2004/09/22 08:15:56  haraldkipp
00077  * Speparate IRQ stack configurable
00078  *
00079  * Revision 1.2  2004/04/25 17:06:17  drsung
00080  * Separate IRQ stack now compatible with nested interrupts.
00081  *
00082  * Revision 1.1  2004/03/16 16:48:46  haraldkipp
00083  * Added Jan Dubiec's H8/300 port.
00084  *
00085  * Revision 1.2  2004/02/18 16:32:48  drsung
00086  * Bugfix in NutThreadCreate. Thanks to Mike Cornelius.
00087  *
00088  * Revision 1.1  2004/02/01 18:49:48  haraldkipp
00089  * Added CPU family support
00090  *
00091  */
00092 
00093 #include <cfg/os.h>
00094 #include <cfg/memory.h>
00095 
00096 #include <string.h>
00097 
00098 #include <sys/atom.h>
00099 #include <sys/heap.h>
00100 #include <sys/thread.h>
00101 
00106 
00113 typedef struct {
00114     u_char csf_r29;
00115     u_char csf_r28;
00116     u_char csf_r23;
00117     u_char csf_r22;
00118     u_char csf_r21;
00119     u_char csf_r20;
00120     u_char csf_r15;
00121     u_char csf_r14;
00122     u_char csf_r13;
00123     u_char csf_r12;
00124     u_char csf_r11;
00125     u_char csf_r10;
00126 #ifdef __AVR_ATmega2561__
00127     u_char csf_pcex;
00128 #endif
00129     u_char csf_pchi;
00130     u_char csf_pclo;
00131 } SWITCHFRAME;
00132 
00138 typedef struct {
00139     u_char cef_arghi;
00140     u_char cef_arglo;
00144     u_char cef_yhi;
00145     u_char cef_ylo;
00146     u_char cef_rampz;
00147     u_char cef_sreg;
00148     u_char cef_r1;
00149 #ifdef __AVR_ATmega2561__
00150     u_char cef_pcex;
00151 #endif
00152     u_char cef_pchi;
00153     u_char cef_pclo;
00154 } ENTERFRAME;
00155 
00156 
00157 /*
00158  * This code is executed when entering a thread.
00159  */
00160 static void NutThreadEntry(void)
00161 {
00162     asm("pop r17");             // first parameter for ICC
00163     asm("pop r16");
00164     asm("pop r29");             // SW-Stack; Y-Register
00165     asm("pop r28");
00166     asm("pop r0");              // r0 = _tmp_reg_
00167     asm("out 0x3B, r0");        // RAMPZ; replace with define later
00168     asm("pop r0");
00169     asm("pop r1");              // r1 = _zero_reg_
00170     asm("out 0x3F, r0");        // SREG; replace with define later
00171     asm("reti");
00172 }
00173 
00185 void NutThreadSwitch(void)
00186 {
00187     /*
00188      * Save all CPU registers.
00189      */
00190     register u_char i = 0;
00191     register u_char j = 0;
00192 
00193     asm("push r10");
00194     asm("push r11");
00195     asm("push r12");
00196     asm("push r13");
00197     asm("push r14");
00198     asm("push r15");
00199     asm("push r20");
00200     asm("push r21");
00201     asm("push r22");
00202     asm("push r23");
00203     asm("push r28");
00204     asm("push r29");
00205     asm("in %i, $3D");          // SPL
00206     asm("in %j, $3E");          // SPH
00207 
00208     runningThread->td_sp = (((u_short) j) << 8) & 0xFF00 | (i & 0xFF);
00209 
00210     /*
00211      * This defines a global label, which may be called
00212      * as an entry point into this function.
00213      */
00214     asm(".globl thread_start");
00215     asm("thread_start:");
00216 
00217     /*
00218      * Reload CPU registers from the thread in front
00219      * of the run queue.
00220      */
00221     runningThread = runQueue;
00222     runningThread->td_state = TDS_RUNNING;
00223 
00224     i = (u_char) (runningThread->td_sp & 0xFF);
00225     j = (u_char) ((runningThread->td_sp >> 8) & 0xFF);
00226 
00227     asm("out $3D, %i");         // SPL
00228     asm("out $3E, %j");         // SPH
00229     asm("pop r29");
00230     asm("pop r28");
00231     asm("pop r23");
00232     asm("pop r22");
00233     asm("pop r21");
00234     asm("pop r20");
00235     asm("pop r15");
00236     asm("pop r14");
00237     asm("pop r13");
00238     asm("pop r12");
00239     asm("pop r11");
00240     asm("pop r10");
00241 }
00242 
00261 HANDLE NutThreadCreate(u_char * name, void (*fn) (void *), void *arg, size_t stackSize)
00262 {
00263     u_char *threadMem;
00264     SWITCHFRAME *sf;
00265     ENTERFRAME *ef;
00266     NUTTHREADINFO *td;
00267     u_short yreg;
00268     const u_char *paddr;
00269 
00270     /*
00271      * Allocate stack and thread info structure in one block.
00272      */
00273     if ((threadMem = NutStackAlloc(stackSize + sizeof(NUTTHREADINFO))) == 0) {
00274         return 0;
00275     }
00276 
00277     td = (NUTTHREADINFO *) (threadMem + stackSize);
00278     ef = (ENTERFRAME *) ((u_short) td - sizeof(ENTERFRAME));
00279     sf = (SWITCHFRAME *) ((u_short) ef - sizeof(SWITCHFRAME));
00280 
00281 
00282     memcpy(td->td_name, name, sizeof(td->td_name) - 1);
00283     td->td_name[sizeof(td->td_name) - 1] = 0;
00284     td->td_sp = (u_short) sf - 1;
00285     td->td_memory = threadMem;
00286     *((u_long *) threadMem) = DEADBEEF;
00287     *((u_long *) (threadMem + 4)) = DEADBEEF;
00288     *((u_long *) (threadMem + 8)) = DEADBEEF;
00289     *((u_long *) (threadMem + 12)) = DEADBEEF;
00290     td->td_priority = 64;
00291 
00292     /*
00293      * Setup entry frame to simulate C function entry.
00294      */
00295     paddr = (const u_char *) fn;
00296     ef->cef_pclo = *paddr;
00297     ef->cef_pchi = *(paddr + 1);
00298 #ifdef __AVR_ATmega2561__
00299     ef->cef_pcex = *(paddr + 2);
00300 #endif
00301     ef->cef_sreg = 0x80;
00302     ef->cef_rampz = 0;
00303     ef->cef_r1 = 0;
00304 
00305     ef->cef_arglo = (u_char) (((u_short) arg) & 0xff);
00306     ef->cef_arghi = (u_char) (((u_short) arg) >> 8);
00307 
00308     yreg = td->td_sp - 40;
00309     ef->cef_yhi = (u_char) (yreg >> 8);
00310     ef->cef_ylo = (u_char) (yreg & 0xFF);
00311 
00312     paddr = (const u_char *) NutThreadEntry;
00313     sf->csf_pclo = *paddr;
00314     sf->csf_pchi = *(paddr + 1);
00315 #ifdef __AVR_ATmega2561__
00316     sf->csf_pcex = *(paddr + 2);
00317 #endif
00318 
00319     /*
00320      * Insert into the thread list and the run queue.
00321      */
00322 
00323     td->td_next = nutThreadList;
00324     nutThreadList = td;
00325     td->td_state = TDS_READY;
00326     td->td_timer = 0;
00327     td->td_queue = 0;
00328 #ifdef NUTDEBUG
00329     if (__os_trf)
00330         fprintf(__os_trs, "Cre<%04x>", (uptr_t) td);
00331 #endif
00332 
00333     NutThreadAddPriQueue(td, (NUTTHREADINFO **) & runQueue);
00334 
00335 #ifdef NUTDEBUG
00336     if (__os_trf) {
00337         NutDumpThreadList(__os_trs);
00338         //NutDumpThreadQueue(__os_trs, runQueue);
00339     }
00340 #endif
00341 
00342     /*
00343      * If no thread is active, switch to new thread.
00344      */
00345     if (runningThread == 0) {
00346         NutEnterCritical();
00347         asm("rjmp thread_start");
00348         /* we will never come back here .. */
00349     }
00350 
00351     /*
00352      * If current context is not in front of
00353      * the run queue (highest priority), then
00354      * switch to the thread in front.
00355      */
00356     if (runningThread != runQueue) {
00357         runningThread->td_state = TDS_READY;
00358 #ifdef NUTDEBUG
00359         if (__os_trf)
00360             fprintf(__os_trs, "New<%04x %04x>", (uptr_t) runningThread, (uptr_t) runQueue);
00361 #endif
00362         NutEnterCritical();
00363         NutThreadSwitch();
00364         NutExitCritical();
00365     }
00366 
00367     return td;
00368 }
00369 

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