diff --git a/ports/arm/atomport.c b/ports/arm/atomport.c index 7bbb05b..48bda2a 100644 --- a/ports/arm/atomport.c +++ b/ports/arm/atomport.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Natie van Rooyen. All rights reserved. + * Copyright (c) 2013, Natie van Rooyen. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -31,23 +31,40 @@ #include "atomport.h" -/** - * Functions defined in atomport_s.s - */ -typedef void * SYSCONTEXT ; -extern void contextSwitch (SYSCONTEXT* save_context, SYSCONTEXT* new_context) ; -extern void contextStart (SYSCONTEXT* context) ; -extern void contextEnableInterrupts (void) ; +/** Functions defined in atomport_s.s */ +typedef void * SYSCONTEXT; +extern void contextSwitch (SYSCONTEXT* save_context, SYSCONTEXT* new_context); +extern void contextStart (SYSCONTEXT* context); +extern void contextEnableInterrupts (void); + + +/** Forward declarations */ +static void thread_shell (void); /** * \b thread_shell * - * Documented in Atomthreads. + * Shell routine which is used to call all thread entry points. * + * This routine is called whenever a new thread is starting, and + * provides a simple wrapper around the thread entry point that + * allows us to carry out any actions we want to do on thread's + * first starting up, or returning after completion. + * + * We mainly just want to make sure interrupts are enabled when a + * thread is run for the first time. This can be done via stack + * restores when threads are first run, but it's handy to have this + * wrapper anyway to run some common code if threads run to + * completion. + * + * A thread shell is also handy for providing port users with a place + * to do any other initialisation that must be done for each thread + * (e.g. opening stdio files etc). + * + * @return None */ -void -thread_shell (void) +static void thread_shell (void) { ATOM_TCB *curr_tcb; @@ -58,7 +75,7 @@ thread_shell (void) * Enable interrupts - these will not be enabled when a thread * is first restored. */ - contextEnableInterrupts () ; + contextEnableInterrupts (); /* Call the thread entry point */ if (curr_tcb && curr_tcb->entry_point) @@ -69,47 +86,76 @@ thread_shell (void) /* Thread has run to completion: remove it from the ready list */ curr_tcb->suspended = TRUE; atomSched (FALSE); - } /** * \b archThreadContextInit * - * Documented in Atomthreads. + * Architecture-specific thread context initialisation routine. * + * This function must set up a thread's context ready for restoring + * and running the thread via archFirstThreadRestore() or + * archContextSwitch(). + * + * The layout required to fill the correct register values is + * described in archContextSwitch(). Note that not all registers + * are restored by archContextSwitch() and archFirstThreadRestore() + * as this port takes advantage of the fact that not all registers + * must be stored by ARM gcc C subroutines. This means that we don't + * provide start values for those registers, as they are "don't cares". + * + * @param[in] tcb_ptr Pointer to the TCB of the thread being created + * @param[in] stack_top Pointer to the top of the new thread's stack + * @param[in] entry_point Pointer to the thread entry point function + * @param[in] entry_param Parameter to be passed to the thread entry point + * + * @return None */ -void -archThreadContextInit (ATOM_TCB *tcb_ptr, void *stack_top, void (*entry_point)(uint32_t), uint32_t entry_param) +void archThreadContextInit (ATOM_TCB *tcb_ptr, void *stack_top, void (*entry_point)(uint32_t), uint32_t entry_param) { - uint32_t * stack_ptr ; + uint32_t *stack_ptr; - tcb_ptr->sp_save_ptr = stack_top; - tcb_ptr->entry_param = entry_param ; - tcb_ptr->entry_point = entry_point ; + /** Start at stack top */ + tcb_ptr->sp_save_ptr = stack_ptr = stack_top; - stack_ptr = (uint32_t *)stack_top; //-- Load stack pointer + /** + * After restoring all of the context registers, the thread restore + * routines will return to the address of the calling routine on the + * stack. In this case (the first time a thread is run) we "return" + * to the entry point for the thread. That is, we store the thread + * entry point in the place that return will look for the return + * address: the stack. + * + * Note that we are using the thread_shell() routine to start all + * threads, so we actually store the address of thread_shell() + * here. Other ports may store the real thread entry point here + * and call it directly from the thread restore routines. + * + * Because we are filling the stack from top to bottom, this goes + * on the stack first (at the top). + */ + *stack_ptr-- = (uint32_t)thread_shell; - *stack_ptr = ( uint32_t ) thread_shell ; - stack_ptr--; + /** + * Store starting register values for R4-R11 + */ + *stack_ptr-- = (uint32_t) 0x00001111; /* R11 */ + *stack_ptr-- = (uint32_t) 0x00001010; /* R10 */ + *stack_ptr-- = (uint32_t) 0x00000909; /* R9 */ + *stack_ptr-- = (uint32_t) 0x00000808; /* R8 */ + *stack_ptr-- = (uint32_t) 0x00000707; /* R7 */ + *stack_ptr-- = (uint32_t) 0x00000606; /* R6 */ + *stack_ptr-- = (uint32_t) 0x00000505; /* R5 */ + *stack_ptr = (uint32_t) 0x00000404; /* R4 */ - *stack_ptr = ( uint32_t ) 0x00001111; /* R11 */ - stack_ptr--; - *stack_ptr = ( uint32_t ) 0x00001010; /* R10 */ - stack_ptr--; - *stack_ptr = ( uint32_t ) 0x00000909; /* R9 */ - stack_ptr--; - *stack_ptr = ( uint32_t ) 0x00000808; /* R8 */ - stack_ptr--; - *stack_ptr = ( uint32_t ) 0x00000707; /* R7 */ - stack_ptr--; - *stack_ptr = ( uint32_t ) 0x00000606; /* R6 */ - stack_ptr--; - *stack_ptr = ( uint32_t ) 0x00000505; /* R5 */ - stack_ptr--; - *stack_ptr = ( uint32_t ) 0x00000404; /* R4 */ + /** + * All thread context has now been initialised. Save the current + * stack pointer to the thread's TCB so it knows where to start + * looking when the thread is started. + */ + tcb_ptr->sp_save_ptr = stack_ptr; - tcb_ptr->sp_save_ptr = stack_ptr ; }