Preemptive multi-tasking
I changed the previous cooperative threads into preemptive threads.
It changes contexts during timer interrupts every 16ms with non-inteligent round robin scheduling.
I noticed that ARM swaps R13/R14 during IRQ, and needed to go back and force between IRQ and SVC mode to get the original (banked) registers as below. When IRQ_hander C function returns non null, it’ll switch contexts.
_IRQ_iterrupt: //-- irq mode sub lr, lr, #4 // in IRQ mode, r14_irq(lr_irq) points to PC+#4 in user mode // save context push {r0-r12, lr} // save user mode registers mrs r0, spsr // spsr -> r0 cps #0x13 //-- svc mode mov r1, sp mov r2, lr cps #0x12 //-- irq mode push {r0-r2} // save spsr, user mode sp, lr // call IRQ_hander(user-mode-sp) mov r0, r2 bl IRQ_handler cmp r0, #0 bne _IRQ_interrupt_context_switch pop {r0-r2} // restore spsr, user mode sp, lr msr spsr, r0 // r0 -> spsr cps #0x13 //-- svc mode mov sp, r1 // restore sp mov lr, r2 // restore lr cps #0x12 //-- irq mode pop {r0-r12,lr} movs pc, lr _IRQ_interrupt_context_switch: //-- irq mode // r0 is next thread's SP pop {r1-r3} // restore spsr, user mode sp, lr // save registers in user mode stack // r1: user mode cpsr // r2: user mode sp // r3: user mode lr sub r2, r2, #4 str r1, [r2] // spsr ldr r4, [r13, #4*13] sub r2, r2, #4 str r4, [r2] // user mode pc (r14_irq) sub r2, r2, #4 str r3, [r2] // user mode lr ldr r4, [r13, #4*12] sub r2, r2, #4 str r4, [r2] // user mode r12 ldr r4, [r13, #4*11] sub r2, r2, #4 str r4, [r2] // user mode r11 ldr r4, [r13, #4*10] sub r2, r2, #4 str r4, [r2] // user mode r10 ldr r4, [r13, #4*9] sub r2, r2, #4 str r4, [r2] // user mode r9 ldr r4, [r13, #4*8] sub r2, r2, #4 str r4, [r2] // user mode r8 ldr r4, [r13, #4*7] sub r2, r2, #4 str r4, [r2] // user mode r7 ldr r4, [r13, #4*6] sub r2, r2, #4 str r4, [r2] // user mode r6 ldr r4, [r13, #4*5] sub r2, r2, #4 str r4, [r2] // user mode r5 ldr r4, [r13, #4*4] sub r2, r2, #4 str r4, [r2] // user mode r4 ldr r4, [r13, #4*3] sub r2, r2, #4 str r4, [r2] // user mode r3 ldr r4, [r13, #4*2] sub r2, r2, #4 str r4, [r2] // user mode r2 ldr r4, [r13, #4*1] sub r2, r2, #4 str r4, [r2] // user mode r1 ldr r4, [r13] sub r2, r2, #4 str r4, [r2] // user mode r0 mov r4, sp // r4 <- r13_irq msr spsr, r1 // r1 -> spsr cps #0x13 //@ svc mode //-- svc mode // change user mode stack to next thread's stack mov sp, r0 // push into r13_irq (r4) ldr r2, [sp, #4*14] // user mode pc sub r4, r4, #4 str r2, [r4] ldr r2, [sp, #4*15] // spsr sub r4, r4, #4 str r2, [r4] // restore registers pop {r0-r12,lr} add sp, sp, #4*2 // pop pc, spsr cps #0x12 //@ irq mode //-- irq mode sub sp, sp, #4*2 pop {lr} // enable irq and restore spsr bic lr, lr, #0x80 msr spsr, lr // lr -> spsr // restore lr (user mode pc) pop {lr} // discard pushed registers add r13, r13, #4*14 // movs pc, * ... mov's' pc restores status register movs pc, lr
I also tried to implement and add critical section but the one in ARM reference’s hanged probably because I haven’t setup MMU.
// http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dht0008a/ch01s03s02.html .equ locked, 1 .equ unlocked, 0 // BUG: lock_mutex hangs // LDREX doesn't work when MMU is disabled. Don't use this. // Declare for use from C as extern void lock_mutex(void * mutex); .global _lock_mutex_mmu _lock_mutex_mmu: LDR r1, =locked 1: LDREX r2, [r0] CMP r2, r1 // Test if mutex is locked or unlocked BEQ 2f STREXNE r2, r1, [r0] // Not locked, attempt to lock it CMPNE r2, #1 // Check if Store-Exclusive failed BEQ 1b // Failed - retry from 1 // Lock acquired DMB // Required before accessing protected resource BX lr 2: // Take appropriate action while waiting for mutex to become unlocked //wfi nop B 1b // Retry from 1 // BUG: unlock_mutex // Declare for use from C as extern void unlock_mutex(void * mutex); .global _unlock_mutex_mmu _unlock_mutex_mmu: LDR r1, =unlocked DMB // Required before releasing protected resource STR r1, [r0] // Unlock mutex // SIGNAL_UPDATE: none BX lr // BUG: SWP version // http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dht0008a/CJHBGBBJ.html // still hangs .global _lock_mutex_swp _lock_mutex_swp: LDR r2, =locked SWP r1, r2, [r0] // Swap R2 with location [R0], [R0] value placed in R1 CMP r1, r2 // Check if memory value was ‘locked’ BEQ _lock_mutex_swp // If so, retry immediately BX lr // If not, lock successful, return // BUG: not really excusive when context swithes after ldr befor str .global _lock_mutex_simple _lock_mutex_simple: ldr r1, =unlocked ldr r3, =locked ldr r2, [r0] cmp r2, r3 beq _lock_mutex_simple str r1, [r0] bx lr .global _unlock_mutex_simple _unlock_mutex_simple: LDR r1, =unlocked STR r1, [r0] // Write value ‘unlocked’ to location [R0] BX lr
Today’s code -> https://github.com/sokoide/rpi-baremetal -> 009_context_switch2