context.c 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. /*
  2. * Copyright (C) 2001-2010 by egnite Software GmbH
  3. *
  4. * All rights reserved.
  5. *
  6. * Redistribution and use in source and binary forms, with or without
  7. * modification, are permitted provided that the following conditions
  8. * are met:
  9. *
  10. * 1. Redistributions of source code must retain the above copyright
  11. * notice, this list of conditions and the following disclaimer.
  12. * 2. Redistributions in binary form must reproduce the above copyright
  13. * notice, this list of conditions and the following disclaimer in the
  14. * documentation and/or other materials provided with the distribution.
  15. * 3. Neither the name of the copyright holders nor the names of
  16. * contributors may be used to endorse or promote products derived
  17. * from this software without specific prior written permission.
  18. *
  19. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  20. * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  21. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  22. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  23. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  24. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  25. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  26. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  27. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  28. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
  29. * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  30. * SUCH DAMAGE.
  31. *
  32. * For additional information see http://www.ethernut.de/
  33. */
  34. /*!
  35. * \file arch/avr32/os/context.c
  36. * \brief Context switching ported to AVR32.
  37. *
  38. * \verbatim File version $Id$ \endverbatim
  39. */
  40. #include <cfg/os.h>
  41. #include <string.h>
  42. #include <sys/atom.h>
  43. #include <sys/heap.h>
  44. #include <sys/thread.h>
  45. #include <avr32/io.h>
  46. /*!
  47. * \addtogroup xgNutAvr32OsContext Context Switching for AVR32 CPUs
  48. * \ingroup xgNutArchAvr32
  49. * \brief Thread context switching for avr-gcc.
  50. */
  51. /*@{*/
  52. /*!
  53. * \brief AVR32 GCC context switch frame layout.
  54. *
  55. * This is the layout of the stack after a thread's context has been
  56. * switched-out. The stack pointer is stored in the thread info and
  57. * points to this structure.
  58. */
  59. typedef struct {
  60. uint32_t csf_cpsr;
  61. uint32_t csf_lr;
  62. uint32_t csf_r7;
  63. uint32_t csf_r6;
  64. uint32_t csf_r5;
  65. uint32_t csf_r4;
  66. uint32_t csf_r3;
  67. uint32_t csf_r2;
  68. uint32_t csf_r1;
  69. uint32_t csf_r0;
  70. } SWITCHFRAME;
  71. /*!
  72. * \brief Thread entry frame layout.
  73. *
  74. * This is the stack layout being build to enter a new thread.
  75. */
  76. typedef struct {
  77. uint32_t cef_pc;
  78. uint32_t cef_r12;
  79. } ENTERFRAME;
  80. /*!
  81. * \brief Enter a new thread.
  82. */
  83. static void NutThreadEntry(void) NUT_NAKED_FUNC;
  84. void NutThreadEntry(void)
  85. {
  86. /* Load argument in r12 and jump to thread entry. */
  87. __asm__ volatile ("popm r12, lr\n\t" /* put the value of cef_r12 in R12 and the value of cef_pc in lr */
  88. "mov pc,lr\n\t" /* then pc recieve lr */
  89. :::"r12", "lr", "pc");
  90. }
  91. /*!
  92. * \brief Switch to another thread.
  93. *
  94. * Stop the current thread, saving its context. Then start the
  95. * one with the highest priority, which is ready to run.
  96. *
  97. * Application programs typically do not call this function.
  98. *
  99. * \note CPU interrupts must be disabled before calling this function.
  100. *
  101. */
  102. void NutThreadSwitch(void) NUT_NAKED_FUNC;
  103. void NutThreadSwitch(void)
  104. {
  105. /* Save CPU context. */
  106. __asm__ volatile ("pushm r0-r7, lr \n\t" /* Save registers in the stack */
  107. "mfsr r10, %1 \n\t" /* Save status in R10 */
  108. "pushm r10 \n\t" /* Save r10 in the stack */
  109. "st.w %0, sp \n\t" /* the address contained in R10 contain the address of the stack pointer. */
  110. :"=m" (runningThread->td_sp)
  111. :"i"(AVR32_SR)
  112. :"r10");
  113. /* Select thread on top of the run queue. */
  114. runningThread = runQueue;
  115. runningThread->td_state = TDS_RUNNING;
  116. /* Restore context. */
  117. /* When changing this, remember to keep NutThreadCreate copy in sync */
  118. __asm__ volatile ("ld.w sp, %0\n\t" /* Restore stack pointer. */
  119. "popm r10 \n\t" /* Get saved status... */
  120. "mtsr %1, r10\n\t" "popm r0-r7, lr\n\t" /* Restore registers. */
  121. "mov pc, lr\n\t" /* Restore status and return. */
  122. ::"m" (runningThread->td_sp), "i"(AVR32_SR)
  123. :"r10");
  124. #if defined(NUT_CRITICAL_NESTING) && !defined(NUT_CRITICAL_NESTING_STACK)
  125. critical_nesting_level = 0;
  126. #endif
  127. }
  128. /*!
  129. * \brief Create a new thread.
  130. *
  131. * If the current thread's priority is lower or equal than the default
  132. * priority (64), then the current thread is stopped and the new one
  133. * is started.
  134. *
  135. * \param name String containing the symbolic name of the new thread,
  136. * up to 8 characters long.
  137. * \param fn The thread's entry point, typically created by the
  138. * THREAD macro.
  139. * \param arg Argument pointer passed to the new thread.
  140. * \param stackSize Number of bytes of the stack space allocated for
  141. * the new thread.
  142. *
  143. * \note The thread must run in ARM mode. Thumb mode is not supported.
  144. *
  145. * \return Pointer to the NUTTHREADINFO structure or 0 to indicate an
  146. * error.
  147. */
  148. HANDLE NutThreadCreate(char *name, void (*fn) (void *), void *arg, size_t stackSize)
  149. {
  150. uint8_t *threadMem;
  151. SWITCHFRAME *sf;
  152. ENTERFRAME *ef;
  153. NUTTHREADINFO *td;
  154. size_t alloc_size;
  155. /*
  156. * Allocate stack and thread info structure in one block.
  157. * We sill setup the following layout:
  158. *
  159. * Upper memory addresses.
  160. *
  161. * +--------------------+
  162. * I I
  163. * I NUTTHREADINFO I
  164. * I I
  165. * td -> +-----+--------------+ <- Stack top
  166. * I I I
  167. * I T I ENTERFRAME I
  168. * I H I I
  169. * ef -> I R +--------------+
  170. * I E I I ^
  171. * I A I SWITCHFRAME I I
  172. * I D I I I pop moves up
  173. * sf -> I +--------------+ <- Initial stack pointer
  174. * I S I I I push moves down
  175. * I T I Application I I
  176. * I A I Stack I V
  177. * I C I I
  178. * I K I I
  179. * threadMem -> +-----+--------------+ <- Stack bottom
  180. *
  181. * Lower memory addresses.
  182. */
  183. alloc_size = stackSize + sizeof(NUTTHREADINFO);
  184. alloc_size += 7;
  185. alloc_size &= ~7;
  186. if ((threadMem = NutHeapAlloc(alloc_size)) == 0) {
  187. return 0;
  188. }
  189. td = (NUTTHREADINFO *) (((size_t)threadMem + stackSize) & ~7);
  190. ef = (ENTERFRAME *) ((uptr_t) td - sizeof(ENTERFRAME));
  191. sf = (SWITCHFRAME *) ((uptr_t) ef - sizeof(SWITCHFRAME));
  192. /*
  193. * Set predefined values at the stack bottom. May be used to detect
  194. * stack overflows.
  195. */
  196. #if defined(NUTDEBUG_CHECK_STACKMIN) || defined(NUTDEBUG_CHECK_STACK)
  197. {
  198. uint32_t *fip = (uint32_t *) threadMem;
  199. while (fip < (uint32_t *) sf) {
  200. *fip++ = DEADBEEF;
  201. }
  202. }
  203. #else
  204. *((uint32_t *) threadMem) = DEADBEEF;
  205. *((uint32_t *) (threadMem + 4)) = DEADBEEF;
  206. *((uint32_t *) (threadMem + 8)) = DEADBEEF;
  207. *((uint32_t *) (threadMem + 12)) = DEADBEEF;
  208. #endif
  209. /*
  210. * Setup the entry frame to simulate C function entry.
  211. */
  212. ef->cef_pc = (uptr_t) fn;
  213. ef->cef_r12 = (uptr_t) arg;
  214. /*
  215. * Setup the switch frame.
  216. */
  217. sf->csf_lr = (uptr_t) NutThreadEntry;
  218. sf->csf_cpsr = (AVR32_SR_M_SUP << AVR32_SR_M_OFFSET); /* [M2:M0]=001 - Supervisor Mode I1M=0 I0M=0, GM=0 */
  219. /*
  220. * Initialize the thread info structure and insert it into the
  221. * thread list and the run queue.
  222. */
  223. memcpy(td->td_name, name, sizeof(td->td_name) - 1);
  224. td->td_name[sizeof(td->td_name) - 1] = 0;
  225. td->td_state = TDS_READY;
  226. td->td_sp = (uptr_t) sf;
  227. td->td_priority = 64;
  228. td->td_memory = threadMem;
  229. td->td_timer = 0;
  230. td->td_queue = 0;
  231. NutEnterCritical();
  232. td->td_next = nutThreadList;
  233. nutThreadList = td;
  234. NutThreadAddPriQueue(td, (NUTTHREADINFO **) & runQueue);
  235. /*
  236. * If no thread is running, then this is the first thread ever
  237. * created. In Nut/OS, the idle thread is created first.
  238. */
  239. if (runningThread == 0) {
  240. /* This will never return. */
  241. runningThread = runQueue;
  242. runningThread->td_state = TDS_RUNNING;
  243. /* Restore context. */
  244. __asm__ volatile ( /* */
  245. "ld.w sp, %0\n\t" /* Restore stack pointer. */
  246. "popm r10 \n\t" /* Get saved status... */
  247. "mtsr %1, r10\n\t" "popm r0-r7, lr\n\t" /* Restore registers. */
  248. "mov pc, lr\n\t" /* Restore status and return. */
  249. ::"m" (runningThread->td_sp), "i"(AVR32_SR)
  250. :"r10");
  251. }
  252. /*
  253. * If current context is not in front of the run queue (highest
  254. * priority), then switch to the thread in front.
  255. */
  256. if (runningThread != runQueue) {
  257. runningThread->td_state = TDS_READY;
  258. NutThreadSwitch();
  259. }
  260. NutExitCritical();
  261. return td;
  262. }
  263. /*@}*/