Setting IEBLE
may cause an interrupt to be issued, but only if an interrupt has occurred. That's not the point. Interrupts work in my application, even when suspending with waiteu. Again, my problem is, that I want to resume after the waiteu instruction, once an interrupt has been handled.
Reading the architecture manual again opened my eyes:
WAITEU
Waits for an event. This instruction sets EEBLE and, if no event is ready it will suspend
the thread until an event becomes ready. When an event is available, the thread will
continue at the address specified by the event. The current PC is not saved anywhere.
That's it! The waiteu instruction is semantically a leaf in the thread control flow graph. In no case, the thread is supposed to resume after this instruction.
[edit] Of course it's not really a leaf, but a wall or something else my limited vocabulary does not contain[edit]
But, wait, there's an important information:
The current PC is not saved anywhere.
Really? This brought me to the following experiment:
Code: Select all
/*============================================================================
* Interrupt handler for a single-stack system.
*==========================================================================*/
.cc_top interruptHandler.function, interruptHandler
.align 4
interruptHandler:
// The program is in kernel mode now. That means, we are bound to kret
// for return and can not use retsp. kret requires krestsp and therefore
// ksp has to be sp to achieve a single-stack.
// I have not found a better way, yet.
stw r0, dp[tempR0] // Store r0 into scratch location
ldw r0, sp[0] // r0 = sp[0]
stw sp, sp[0] // Prepare modification of ksp
krestsp 0 // ksp=sp+0, sp=ksp[0]
stw r0, sp[0] // Restore sp[0]
ldw r0, dp[tempR0] // Restore r0
// Everything is sane now and ksp=sp
kentsp 10 // Switch to kernel stack and allocate 10 words
// ksp[0]=sp, ksp-=10
stw spc, sp[1] // Store service registers...
stw ssr, sp[2]
stw sed, sp[3]
stw lr, sp[4]
stw r0, sp[5] // ...and caller-save registers
stw r1, sp[6]
stw r2, sp[7]
stw r3, sp[8]
stw r11, sp[9]
get r11, ed // Load handler index from 16bit event-data. ed has been
// loaded with an index i. Each interrupt source
// got its own index.
ldc r0, 0xff // Mask i because the high nibble in ed contains crap.
and r0, r11, r0
bl handle // Finally call a high-level C-function handle(i)
// Look, if the thread has been suspended by waiteu. In that case,
// manipulate SPC to point to the next instruction in order to execute
// a dispatcher. Otherwise just return as ususal.
ldw r0, sp[1]
ldap r11, __waiteuInstruction
eq r11, r11, r0
bf r11, __epilogue
add r0, r0, 2 // waiteu is 2 bytes long.
stw r0, sp[1]
__epilogue:
ldw spc, sp[1] // Restore service registers...
ldw ssr, sp[2]
ldw sed, sp[3]
ldw lr, sp[4]
ldw r0, sp[5] // ...and caller-save registers
ldw r1, sp[6]
ldw r2, sp[7]
ldw r3, sp[8]
ldw r11, sp[9]
krestsp 10 // Contract the kernel stack and switch to user stack
// ksp+=10, sp=ksp[0]
kret // Return from kernel mode interrupt handler
// either to the _suspend function or to another
// interrupted program location.
.cc_bottom interruptHandler.function
/*============================================================================
* Suspend the thread.
*
* Interrupts are enabled while sleeping and disabled when waking up.
*==========================================================================*/
_suspend:
setsr SR_IEBLE
__waiteuInstruction:
waiteu // Usually a thread is not supposed to resume after
// this instruction. Only _interruptHandler can do that
clrsr SR_IEBLE
retsp 0
In my high-level program I have something like a task loop:
Code: Select all
_disableInterrupts();
while(true)
{
for (Task* task = readyList.takeFirst(); task != 0; task = readyList.takeFirst())
{
_enableInterrupts();
dispatch(task);
_disableInterrupts();
// some more house keeping stuff...
}
_suspend();
}
Yes. It works! The saved program counter in the interrupt handler indeed points to the waiteu instruction where the thread has been suspended. After executing the interrupt handler, the program resumes after the waiteu instruction.
I think, this solves my original problem in an unorthodox way. :)
But one serious problem remains. There is still a possible race condition in _suspend because an interrupt could happen between setsr and waiteu while the program counter still points to _suspend and not _waiteu.