1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2002 by Ulf Ralberg
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
30 unsigned int macsr
; /* EMAC status register */
31 unsigned int d
[6]; /* d2-d7 */
32 unsigned int a
[5]; /* a2-a6 */
33 void *sp
; /* Stack pointer (a7) */
34 void *start
; /* Thread start address, or NULL when started */
36 #elif CONFIG_CPU == SH7034
39 unsigned int r
[7]; /* Registers r8 thru r14 */
40 void *sp
; /* Stack pointer (r15) */
41 void *pr
; /* Procedure register */
42 void *start
; /* Thread start address, or NULL when started */
44 #elif defined(CPU_ARM)
47 unsigned int r
[8]; /* Registers r4-r11 */
48 void *sp
; /* Stack pointer (r13) */
49 unsigned int lr
; /* r14 (lr) */
50 void *start
; /* Thread start address, or NULL when started */
52 #elif CONFIG_CPU == TCC730
55 void *sp
; /* Stack pointer (a15) */
56 void *start
; /* Thread start address */
57 int started
; /* 0 when not started */
61 #define DEADBEEF ((unsigned int)0xdeadbeef)
62 /* Cast to the the machine int type, whose size could be < 4. */
66 static volatile int num_sleepers
;
67 static int current_thread
;
68 static struct regs thread_contexts
[MAXTHREADS
] IBSS_ATTR
;
69 const char *thread_name
[MAXTHREADS
];
70 void *thread_stack
[MAXTHREADS
];
71 int thread_stack_size
[MAXTHREADS
];
72 static const char main_thread_name
[] = "main";
74 extern int stackbegin
[];
75 extern int stackend
[];
77 void switch_thread(void) ICODE_ATTR
;
78 static inline void store_context(void* addr
) __attribute__ ((always_inline
));
79 static inline void load_context(const void* addr
) __attribute__ ((always_inline
));
83 void profile_thread(void) {
84 profstart(current_thread
);
89 /*---------------------------------------------------------------------------
90 * Store non-volatile context.
91 *---------------------------------------------------------------------------
93 static inline void store_context(void* addr
)
96 "stmia %0, { r4-r11, sp, lr }\n"
101 /*---------------------------------------------------------------------------
102 * Load non-volatile context.
103 *---------------------------------------------------------------------------
105 static inline void load_context(const void* addr
)
108 "ldmia %0, { r4-r11, sp, lr }\n" /* load regs r4 to r14 from context */
109 "ldr r0, [%0, #40] \n" /* load start pointer */
111 "cmp r0, r1 \n" /* check for NULL */
112 "strne r1, [%0, #40] \n" /* if it's NULL, we're already running */
113 "movne pc, r0 \n" /* not already running, so jump to start */
114 : : "r" (addr
) : "r0", "r1"
118 #elif defined(CPU_COLDFIRE)
119 /*---------------------------------------------------------------------------
120 * Store non-volatile context.
121 *---------------------------------------------------------------------------
123 static inline void store_context(void* addr
)
126 "move.l %%macsr,%%d0 \n"
127 "movem.l %%d0/%%d2-%%d7/%%a2-%%a7,(%0) \n"
128 : : "a" (addr
) : "d0" /* only! */
132 /*---------------------------------------------------------------------------
133 * Load non-volatile context.
134 *---------------------------------------------------------------------------
136 static inline void load_context(const void* addr
)
139 "movem.l (%0),%%d0/%%d2-%%d7/%%a2-%%a7 \n" /* Load context */
140 "move.l %%d0,%%macsr \n"
141 "move.l (52,%0),%%d0 \n" /* Get start address */
142 "beq.b .running \n" /* NULL -> already running */
143 "clr.l (52,%0) \n" /* Clear start address.. */
145 "jmp (%0) \n" /* ..and start the thread */
147 : : "a" (addr
) : "d0" /* only! */
151 #elif CONFIG_CPU == SH7034
152 /*---------------------------------------------------------------------------
153 * Store non-volatile context.
154 *---------------------------------------------------------------------------
156 static inline void store_context(void* addr
)
173 /*---------------------------------------------------------------------------
174 * Load non-volatile context.
175 *---------------------------------------------------------------------------
177 static inline void load_context(const void* addr
)
189 "mov.l @%0,r0 \n" /* Get start address */
191 "bt .running \n" /* NULL -> already running */
194 "rts \n" /* Start the thread */
195 "mov.l r0,@%0 \n" /* Clear start address */
197 : : "r" (addr
) : "r0" /* only! */
201 #elif CONFIG_CPU == TCC730
202 /*---------------------------------------------------------------------------
203 * Store non-volatile context.
204 *---------------------------------------------------------------------------
206 #define store_context(addr) \
216 "ldw @[%0+0], a15\n\t" : : "a" (addr) );
218 /*---------------------------------------------------------------------------
219 * Load non-volatile context.
220 *---------------------------------------------------------------------------
222 #define load_context(addr) \
224 if (!(addr)->started) { \
225 (addr)->started = 1; \
227 "ldw a15, @[%0+0]\n\t" \
228 "ldw a14, @[%0+4]\n\t" \
229 "jmp a14\n\t" : : "a" (addr) \
233 "ldw a15, @[%0+0]\n\t" \
241 "pop r1,r0\n\t" : : "a" (addr) \
248 /*---------------------------------------------------------------------------
249 * Switch thread in round robin fashion.
250 *---------------------------------------------------------------------------
252 void switch_thread(void)
255 profile_thread_stopped(current_thread
);
258 unsigned int *stackptr
;
263 while (num_sleepers
== num_threads
)
265 /* Enter sleep mode, woken up on interrupt */
267 asm volatile ("stop #0x2000");
268 #elif CONFIG_CPU == SH7034
270 asm volatile ("sleep");
271 #elif CONFIG_CPU == PP5020
272 /* This should sleep the CPU. It appears to wake by itself on
274 CPU_CTL
= 0x80000000;
275 #elif CONFIG_CPU == TCC730
276 /* Sleep mode is triggered by the SYS instr on CalmRisc16.
277 * Unfortunately, the manual doesn't specify which arg to use.
278 __asm__ volatile ("sys #0x0f");
279 0x1f seems to trigger a reset;
280 0x0f is the only one other argument used by Archos.
285 current
= current_thread
;
286 store_context(&thread_contexts
[current
]);
288 #if CONFIG_CPU != TCC730
289 /* Check if the current thread stack is overflown */
290 stackptr
= thread_stack
[current
];
291 if(stackptr
[0] != DEADBEEF
)
292 panicf("Stkov %s", thread_name
[current
]);
295 if (++current
>= num_threads
)
298 current_thread
= current
;
299 load_context(&thread_contexts
[current
]);
301 profile_thread_started(current_thread
);
305 void sleep_thread(void)
311 void wake_up_thread(void)
316 /*---------------------------------------------------------------------------
318 * Return ID if context area could be allocated, else -1.
319 *---------------------------------------------------------------------------
321 int create_thread(void (*function
)(void), void* stack
, int stack_size
,
325 unsigned int stacklen
;
326 unsigned int *stackptr
;
329 if (num_threads
>= MAXTHREADS
)
332 /* Munge the stack to make it easy to spot stack overflows */
333 stacklen
= stack_size
/ sizeof(int);
335 for(i
= 0;i
< stacklen
;i
++)
337 stackptr
[i
] = DEADBEEF
;
340 /* Store interesting information */
341 thread_name
[num_threads
] = name
;
342 thread_stack
[num_threads
] = stack
;
343 thread_stack_size
[num_threads
] = stack_size
;
344 regs
= &thread_contexts
[num_threads
];
345 #if defined(CPU_COLDFIRE) || (CONFIG_CPU == SH7034) || defined(CPU_ARM)
346 /* Align stack to an even 32 bit boundary */
347 regs
->sp
= (void*)(((unsigned int)stack
+ stack_size
) & ~3);
348 #elif CONFIG_CPU == TCC730
349 /* Align stack on word boundary */
350 regs
->sp
= (void*)(((unsigned long)stack
+ stack_size
- 2) & ~1);
353 regs
->start
= (void*)function
;
356 return num_threads
++; /* return the current ID, e.g for remove_thread() */
359 /*---------------------------------------------------------------------------
360 * Remove a thread from the scheduler.
361 * Parameter is the ID as returned from create_thread().
362 *---------------------------------------------------------------------------
364 void remove_thread(int threadnum
)
368 if(threadnum
>= num_threads
)
372 for (i
=threadnum
; i
<num_threads
-1; i
++)
373 { /* move all entries which are behind */
374 thread_name
[i
] = thread_name
[i
+1];
375 thread_stack
[i
] = thread_stack
[i
+1];
376 thread_stack_size
[i
] = thread_stack_size
[i
+1];
377 thread_contexts
[i
] = thread_contexts
[i
+1];
380 if (current_thread
== threadnum
) /* deleting the current one? */
381 current_thread
= num_threads
; /* set beyond last, avoid store harm */
382 else if (current_thread
> threadnum
) /* within the moved positions? */
383 current_thread
--; /* adjust it, point to same context again */
386 void init_threads(void)
388 num_threads
= 1; /* We have 1 thread to begin with */
389 current_thread
= 0; /* The current thread is number 0 */
390 thread_name
[0] = main_thread_name
;
391 thread_stack
[0] = stackbegin
;
392 thread_stack_size
[0] = (int)stackend
- (int)stackbegin
;
393 #if CONFIG_CPU == TCC730
394 thread_contexts
[0].started
= 1;
396 thread_contexts
[0].start
= 0; /* thread 0 already running */
401 int thread_stack_usage(int threadnum
)
404 unsigned int *stackptr
= thread_stack
[threadnum
];
406 if(threadnum
>= num_threads
)
409 for(i
= 0;i
< thread_stack_size
[threadnum
]/sizeof(int);i
++)
411 if(stackptr
[i
] != DEADBEEF
)
415 return ((thread_stack_size
[threadnum
] - i
* sizeof(int)) * 100) /
416 thread_stack_size
[threadnum
];