context.c 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. /*
  2. * Copyright 2012 by Embedded Technologies s.r.o
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions
  6. * are met:
  7. *
  8. * 1. Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. * 2. Redistributions in binary form must reproduce the above copyright
  11. * notice, this list of conditions and the following disclaimer in the
  12. * documentation and/or other materials provided with the distribution.
  13. * 3. Neither the name of the copyright holders nor the names of
  14. * contributors may be used to endorse or promote products derived
  15. * from this software without specific prior written permission.
  16. *
  17. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  18. * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  19. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  20. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  21. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  22. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  23. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  24. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  25. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  26. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
  27. * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  28. * SUCH DAMAGE.
  29. *
  30. * For additional information see http://www.ethernut.de/
  31. */
  32. #include <cfg/os.h>
  33. #include <string.h>
  34. #include <stdio.h>
  35. #include <sys/atom.h>
  36. #include <sys/heap.h>
  37. #include <sys/thread.h>
  38. /*!
  39. * \addtogroup xgNutArchM68kOsContext
  40. */
  41. /*@{*/
  42. /*!
  43. * \brief M68k GCC context switch frame layout.
  44. *
  45. * This is the layout of the stack after a thread's context has been
  46. * switched-out. The stack pointer is stored in the thread info and
  47. * points to this structure.
  48. */
  49. typedef struct {
  50. uint32_t d[6]; // d3-d7 (NOTE: d0-d2, a0-a1 are working registers for C language)
  51. uint32_t a[4]; // a2-a5
  52. uint16_t pad; // alignment to 32 bit
  53. uint16_t sr; // status register
  54. } SWITCHFRAME;
  55. /*!
  56. * \brief Thread entry frame layout.
  57. *
  58. * This is the stack layout being build to enter a new thread.
  59. */
  60. typedef struct {
  61. uint32_t fp_entry; // value fp register
  62. uint32_t pc_entry; // function NutThreadEntry
  63. uint16_t pad; // alignment to 32 bit
  64. uint16_t sr; // status register
  65. uint32_t d0; // arg function
  66. uint32_t pc_fn; // thread's entry funciton
  67. } ENTERFRAME;
  68. /*!
  69. * \ Remove thread from thread list, set tread to be killed and yield
  70. * After this call function NutThreadDestroy in NutIdle to release memory back to the OS.
  71. */
  72. void NutThreadExitAndYield(void)
  73. {
  74. NutThreadExit();
  75. NutThreadYield();
  76. }
  77. /*!
  78. * \brief Enter a new thread.
  79. */
  80. void NutThreadEntry(void)
  81. {
  82. /* The thread's entry point ef->pc_fn to jsr */
  83. __asm__ volatile("move.l 12(%sp), %a0");
  84. /* Move sp and fp to save place in stack. */
  85. __asm__ volatile("add.l #12, %sp");
  86. __asm__ volatile("move.l %sp, %fp");
  87. /* The thread's arg copy ef->d0 to use in sub rutine */
  88. __asm__ volatile("move.l -4(%sp), (%sp)");
  89. /* NutExitCritical - load sr */
  90. __asm__ volatile("move.l -8(%sp), %d0");
  91. __asm__ volatile("move.w %d0, %sr");
  92. /* Jump to thread's entry point ef->pc_fn */
  93. __asm__ volatile("jsr (%a0)");
  94. /* If returned from subrutine terminate thread */
  95. NutThreadExitAndYield();
  96. }
  97. /*!
  98. * \brief Switch to another thread.
  99. *
  100. * Stop the current thread, saving its context. Then start the
  101. * one with the highest priority, which is ready to run.
  102. *
  103. * Application programs typically do not call this function.
  104. *
  105. * \note CPU interrupts must be disabled before calling this function.
  106. *
  107. */
  108. void NutThreadSwitch(void)
  109. {
  110. #define LOCAL_VARIABLES_SIZE (sizeof(SWITCHFRAME))
  111. SWITCHFRAME sf;
  112. /* Save CPU context. */
  113. __asm__ volatile("movem.l %%d2-%%d7/%%a2-%%a5, %[sf_d0]":[sf_d0] "=m" (sf.d[0]));
  114. __asm__ volatile("move.w %sr,%d0");
  115. __asm__ volatile("move.w %%d0,%[sf_sr]" :[sf_sr] "=m" (sf.sr));
  116. __asm__ volatile("move.l %%sp,%[td_sp]" :[td_sp] "=m" (runningThread->td_sp));
  117. /*
  118. * This defines a global label, which may be called
  119. * as an entry point into this function (used in NutThreadCreate())
  120. */
  121. asm volatile(".global thread_start\n"
  122. "thread_start:\n\t"::);
  123. /* Select thread on top of the run queue. */
  124. runningThread = runQueue;
  125. runningThread->td_state = TDS_RUNNING;
  126. /* Restore context. */
  127. /* load stack pointer from NUTTHREADINFO */
  128. // NOTE: "sf" variable shows garbage in Eclipse Variables View (frame pointer is not configured yet)
  129. __asm__ volatile("move.l %[td_sp],%%sp" ::[td_sp] "m" (runningThread->td_sp));
  130. __asm__ volatile("move.l %sp,%a0");
  131. __asm__ volatile("adda.l %[size],%%a0" ::[size] "i" (LOCAL_VARIABLES_SIZE));
  132. __asm__ volatile("move.l %a0,%fp");
  133. // now you can use the "sf" variable (local variables)
  134. __asm__ volatile("move.w %[sf_sr],%%d0" ::[sf_sr] "m" (sf.sr));
  135. __asm__ volatile("move.w %d0,%sr");
  136. __asm__ volatile("movem.l %[sf_d0], %%d2-%%d7/%%a2-%%a5"::[sf_d0] "m" (sf.d[0]));
  137. }
  138. /*!
  139. * \brief Create a new thread.
  140. *
  141. * If the current thread's priority is lower or equal than the default
  142. * priority (64), then the current thread is stopped and the new one
  143. * is started.
  144. *
  145. * \param name String containing the symbolic name of the new thread,
  146. * up to 8 characters long.
  147. * \param fn The thread's entry point, typically created by the
  148. * THREAD macro.
  149. * \param arg Argument pointer passed to the new thread.
  150. * \param stackSize Number of bytes of the stack space allocated for
  151. * the new thread.
  152. *
  153. * \note The thread must run in ARM mode. Thumb mode is not supported.
  154. *
  155. * \return Pointer to the NUTTHREADINFO structure or 0 to indicate an
  156. * error.
  157. */
  158. HANDLE NutThreadCreate(char * name, void(*fn)(void *), void *arg, size_t stackSize)
  159. {
  160. uint8_t *threadMem;
  161. SWITCHFRAME *sf;
  162. ENTERFRAME *ef;
  163. NUTTHREADINFO *td;
  164. size_t alloc_size;
  165. /*
  166. * Allocate stack and thread info structure in one block.
  167. * We sill setup the following layout:
  168. *
  169. * Upper memory addresses.
  170. *
  171. * +--------------------+
  172. * I I
  173. * I NUTTHREADINFO I
  174. * I I
  175. * td -> +-----+--------------+ <- Stack top
  176. * I I I
  177. * I T I ENTERFRAME I
  178. * I H I I
  179. * ef -> I R +--------------+
  180. * I E I I ^
  181. * I A I SWITCHFRAME I I
  182. * I D I I I pop moves up
  183. * sf -> I +--------------+ <- Initial stack pointer
  184. * I S I I I push moves down
  185. * I T I Application I I
  186. * I A I Stack I V
  187. * I C I I
  188. * I K I I
  189. * threadMem -> +-----+--------------+ <- Stack bottom
  190. *
  191. * Lower memory addresses.
  192. */
  193. /* Align to the 4 byte boundary */
  194. alloc_size = stackSize + sizeof(NUTTHREADINFO);
  195. alloc_size += 3;
  196. alloc_size &= ~3;
  197. if ((threadMem = NutStackAlloc(alloc_size)) == 0) {
  198. if ((threadMem = NutHeapAlloc(alloc_size)) == 0) {
  199. return 0;
  200. }
  201. }
  202. td = (NUTTHREADINFO *) ((threadMem + stackSize) & ~3);
  203. ef = (ENTERFRAME *) ((uintptr_t) td - sizeof(ENTERFRAME));
  204. sf = (SWITCHFRAME *) ((uintptr_t) ef - sizeof(SWITCHFRAME));
  205. /*
  206. * Set predefined values at the stack bottom. May be used to detect
  207. * stack overflows.
  208. */
  209. #if defined(NUTDEBUG_CHECK_STACKMIN) || defined(NUTDEBUG_CHECK_STACK)
  210. {
  211. uint32_t *fip = (uint32_t *)threadMem;
  212. while (fip < (uint32_t *)sf) {
  213. *fip++ = DEADBEEF;
  214. }
  215. }
  216. #else
  217. *((uint32_t *) threadMem) = DEADBEEF;
  218. *((uint32_t *) (threadMem + 4)) = DEADBEEF;
  219. *((uint32_t *) (threadMem + 8)) = DEADBEEF;
  220. *((uint32_t *) (threadMem + 12)) = DEADBEEF;
  221. #endif
  222. /*
  223. * Setup the entry frame to simulate C function entry.
  224. */
  225. ef->fp_entry = (uintptr_t) &ef->pc_entry; // no local variables fp = sp
  226. ef->pc_entry = (uintptr_t) NutThreadEntry;
  227. ef->sr = 0x2000; // supervisor mode + allow all interrupts (exit critical section)
  228. ef->d0 = (uintptr_t) arg;
  229. ef->pc_fn = (uintptr_t) fn;
  230. /*
  231. * Setup the switch frame.
  232. */
  233. sf->sr = 0x2700; // supervisor mode + disallow interrupts (enter critical section)
  234. /*
  235. * Initialize the thread info structure and insert it into the
  236. * thread list and the run queue.
  237. */
  238. memcpy(td->td_name, name, sizeof(td->td_name) - 1);
  239. td->td_name[sizeof(td->td_name) - 1] = 0;
  240. td->td_state = TDS_READY;
  241. td->td_sp = (uintptr_t) sf;
  242. td->td_priority = 64;
  243. td->td_memory = threadMem;
  244. td->td_timer = 0;
  245. td->td_queue = 0;
  246. NutEnterCritical();
  247. td->td_next = nutThreadList;
  248. nutThreadList = td;
  249. NutThreadAddPriQueue(td, (NUTTHREADINFO **) &runQueue);
  250. /*
  251. * If no thread is active, switch to new thread.
  252. */
  253. if (runningThread == NULL) {
  254. NutEnterCritical();
  255. asm volatile ("jmp thread_start\n\t"::);
  256. /* we will never come back here .. */
  257. }
  258. /*
  259. * If current context is not in front of the run queue (highest
  260. * priority), then switch to the thread in front.
  261. */
  262. if (runningThread != runQueue) {
  263. runningThread->td_state = TDS_READY;
  264. NutThreadSwitch();
  265. }
  266. NutExitCritical();
  267. return td;
  268. }
  269. /*@}*/