|void||archThreadContextInit (ATOM_TCB *tcb_ptr, void *stack_top, void(*entry_point)(uint32_t), uint32_t entry_param)|
|void archThreadContextInit||(||ATOM_TCB *||tcb_ptr,|
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 gcc-avr C subroutines. This means that we don't provide start values for those registers, as they are "don't cares".
Because we don't actually save the parameter registers (R25-R24) for this particular architecture, we use a separate thread shell. The thread shell is always used as the thread entry point stored in the thread context, and it does the actual calling of the proper thread entry point, passing the thread entry parameter. This allows us to pass the entry parameter without actually storing it on the stack (the thread shell routine takes the entry point and parameter from the thread's TCB). On other ports you may instead choose to store the entry point and parameter in the thread context and use no thread shell routine.
Similarly we use the thread shell in this case to enable interrupts. When a thread is restored and started for the first time, it must also enable interrupts. This might be done by setting up the appropriate value in the SREG register for enabled interrupts, which would then be restored when the thread is first started. But to reduce register-saves we do not save SREG on the AVR port, and instead we use the thread shell routine to enable interrupts the first time a thread is started.
|[in]||tcb_ptr||Pointer to the TCB of the thread being created|
|[in]||stack_top||Pointer to the top of the new thread's stack|
|[in]||entry_point||Pointer to the thread entry point function|
|[in]||entry_param||Parameter to be passed to the thread entry point|
Start at stack top
After restoring all of the context registers, the thread restore routines will perform a RET or RETI which expect to find 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 RET and RETI 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).
Devices with 3 byte program counters (e.g. Atmega25x, Xmega) must have 3 bytes stacked for the entry point. In GCC function pointers are still 16-bits, however, so we cannot actually pass entry points at > 64K in instruction space (128KB in real terms) and just set the top byte to zero here. This means that the thread_shell() function must be located in the bottom 128KB. You may need to modify linker scripts to force this.
For the AVR port the parameter registers (R25-R24) are not saved and restored by the context switch routines. This means that they cannot be used to pass a parameter to the entry point the first time a thread is restored. Rather than incur the overhead of saving them (just for the benefit of starting threads) we can either use a flag here to notify the context restore routines (archThreadFirstRestore() and archContextSwitch()) that they should restore R25-R24 this one time, or we can use a thread shell routine which replaces that actual thread entry point. In this case we use a thread shell which is responsible for passing the parameters to the actual thread entry point, without adding extra processing to the context switch routines.
Other ports may wish to store entry_param in the appropriate parameter registers when creating a thread's context, particularly if that port saves those registers anyway.
Similarly, although interrupts must be enabled when starting new threads, we also defer this to the thread shell because we don't save the SREG contents for normal context switches. Other ports may choose to context switch the relevant interrupt enable register, so that the first context switch is able to enable interrupts during its normal context restore.
Store starting register values for R2-R17, R28-R29
On devices with large program space we also context switch RAMPZ, EIND.
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.