Difference between revisions of "Preemptive Multitasking (ARM)"

From Embedded Xinu
Jump to navigation Jump to search
(Created page (still needs diagram of context record and improved explanation))
 
(Remove content now duplicated in "Preemptive multitasking")
Line 1: Line 1:
Like virtually all modern operating systems, Embedded Xinu supports [http://en.wikipedia.org/wiki/Preemption_(computing) preemptive multitasking], which makes it appear that multiple threads are executing at the same time on the same processor.  This page documents how this is implemented on ARM-architecture platforms that Embedded Xinu has been ported to (as of this writing, only the [[Raspberry Pi]]).
+
This page documents how [[Embedded Xinu]] implements [[Preemptive multitasking|preemptive multitasking]] on [http://en.wikipedia.org/wiki/ARM_Architecture ARM-architecture] platforms that it has been ported to, such as the [[Raspberry Pi]].
  
== Creating and Changing Thread Contexts ==
+
== Thread context ==
  
Embedded Xinu platforms must implement create(), which is responsible for creating new threads, and ctxsw(), which is responsible for switching between thread contexts.  Platforms implement their own thread context format, which is necessary because the thread context consists of saved CPU registers, which are different on different CPU architectures.  Thread contexts are created, on the stack of the relevant thread, in two places: in create() when creating a new thread's context, and in ctxsw() when saving the currently executing thread's context.  These two locations must create contexts that are compatible, at least to the extent that the thread can be switched to using ctxsw() either to start the thread or to resume the thread.
+
The format and size of the [[Preemptive_multitasking#Multiple_threads|thread context]] used for ARM ports of Embedded Xinu rests on two factors:  the registers available on the ARM architecture, and the standard ARM calling convention<ref>http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042e/IHI0042E_aapcs.pdf</ref>:
 
 
The actual thread context we chose to use for ARM rests on two factors:  the registers available on the ARM architecture, and the ARM calling convention:
 
  
 
* ARM processors have 16 "general-purpose" registers numbered r0-r15, as well as the Current Program Status Register (cpsr).  However, despite being considered "general-purpose" registers, r13-r15 actually have special purposes; namely, r13 is used as the stack pointer (sp), r14 is used as the link register (lr), and r15 is used as the program counter (pc).
 
* ARM processors have 16 "general-purpose" registers numbered r0-r15, as well as the Current Program Status Register (cpsr).  However, despite being considered "general-purpose" registers, r13-r15 actually have special purposes; namely, r13 is used as the stack pointer (sp), r14 is used as the link register (lr), and r15 is used as the program counter (pc).
  
* The ARM calling convention specifies which registers are callee-save (r4-r11,r13-r14) and which are caller-save (r0-r3, r11).
+
* The standard ARM calling convention specifies which registers are callee-save (r4-r11, r13-r14) and which are caller-save (r0-r3, r11), as well as how arguments are passed to procedures (up to four arguments in r0-r3; additional arguments spill onto stack).
  
 
The thread context we chose is 15 words long and is the following:
 
The thread context we chose is 15 words long and is the following:
Line 15: Line 13:
 
TODO
 
TODO
  
We reproduce the ctxsw() code below, since it only contains a few instructions and has a detailed explanation.
+
We reproduce the <code>ctxsw()</code> code below, since it only contains a few instructions and has a detailed explanation.
  
 
  <nowiki>
 
  <nowiki>
Line 65: Line 63:
 
== Preemption ==
 
== Preemption ==
  
Preemption occurs when the timer interrupt occurs and Xinu attempts to reschedule the currently executing thread, which results in a call to ctxsw(), described above, that may switch to a different thread context.  (We say ''may'' because the code is written such that ctxsw() is called when the same thread is rescheduled to itself, in which case ctxsw() restores the saved context immediately and is a no-op.)  The means of generating a timer interrupt is not standard to the ARM architecture; instead, software must make use of a board-specific or chip-specific device such as the [[BCM2835 System Timer]].
+
The means of generating a timer interrupt for [[Preemptive_multitasking#Preemption|preemption]] is not standard to the ARM architecture; instead, software must make use of a board-specific or chip-specific device such as the [[BCM2835 System Timer]].
 +
 
 +
== Notes ==
 +
 
 +
<references />

Revision as of 15:08, 8 September 2013

This page documents how Embedded Xinu implements preemptive multitasking on ARM-architecture platforms that it has been ported to, such as the Raspberry Pi.

Thread context

The format and size of the thread context used for ARM ports of Embedded Xinu rests on two factors: the registers available on the ARM architecture, and the standard ARM calling convention[1]:

  • ARM processors have 16 "general-purpose" registers numbered r0-r15, as well as the Current Program Status Register (cpsr). However, despite being considered "general-purpose" registers, r13-r15 actually have special purposes; namely, r13 is used as the stack pointer (sp), r14 is used as the link register (lr), and r15 is used as the program counter (pc).
  • The standard ARM calling convention specifies which registers are callee-save (r4-r11, r13-r14) and which are caller-save (r0-r3, r11), as well as how arguments are passed to procedures (up to four arguments in r0-r3; additional arguments spill onto stack).

The thread context we chose is 15 words long and is the following:

TODO

We reproduce the ctxsw() code below, since it only contains a few instructions and has a detailed explanation.

/*------------------------------------------------------------------------
 *  ctxsw  -  Switch from one thread context to another.
 *------------------------------------------------------------------------
 *
 * This is the ARM version.  How it works: we have to save r4-r11 and lr, since
 * r4-r11 are callee-save and lr needs to be loaded into the pc when this
 * context is switched to again.  Registers r0-r3 are caller-save so they do
 * *not* need not be saved, but they are pushed anyway to leave space since they
 * are part of the context constructed by create() to pass thread arguments.
 *
 * When restoring a context, we pop both the lr and pc.  These are both set to
 * appropriate values in create().  But when saving a context below, we only
 * have an appropriate value for pc--- namely, the lr, a.k.a. the address
 * ctxsw() will return to.  The lr at that instruction is unknown.  However,
 * this is irrelevant because the lr is caller-save, and we can simply push a
 * garbage value from r13 instead.
 *
 * We almost don't need to do anything about the CPSR here, since:
 *
 *   - We do all our context switches to/from the same mode (namely, SYS mode).
 *   - The ARM ABI does not expect comparison flags in the CPSR to be preserved
 *         across function calls.
 *   - resched() takes care of saving/restoring whether interrupts are enabled
 *         or not when resuming a thread that has been switched out.
 *   - Xinu never makes changes to the CPSR not already covered above, such as
 *         switching to executing Thumb instructions.
 *
 * However, interrupts are disabled when ctxsw() is called from resched(), but
 * we want interrupts to be enabled when starting a *new* thread, which
 * resched() does not take care of.  We solve this by including the control bits
 * of the current program status register in the context and adding a line of
 * code to create() that sets the control bits of new threads such that
 * interrupts are enabled.
 *------------------------------------------------------------------------*/
ctxsw:
	mrs r12, cpsr
	push {r0-r14}

	str sp, [r0]
	ldr sp, [r1]

	pop {r0-r12}
	msr cpsr_c, r12
	pop {lr, pc}

Preemption

The means of generating a timer interrupt for preemption is not standard to the ARM architecture; instead, software must make use of a board-specific or chip-specific device such as the BCM2835 System Timer.

Notes