I am trying to implement a simple RTOS with round robin scheduling. Since I do not have a physical board yet, I am running the ELF file on QEMU (qemu-system-gnuarmlinux). For development I am using Eclipse CDT. I use the following command to run the code on QEMU:
/opt/xpack-qemu-arm-7.0.0-1/bin/qemu-system-gnuarmeclipse -M STM32F4-Discovery -kernel /mnt/d/eclipse-workspace/rtos/Debug/rtos.elf
Each task has an associated struct:
struct TCB {
int32_t *stackPt;
struct TCB *nextPt;
};
At initialization, the structs are chained up in a circular linked list via the nextPt
, their stacks (stackPt
) are set as TCB_STACK[threadNumber][STACK_SIZE-16];
and the stack's program counter is set up as TCB_STACK[0][STACK_SIZE - 2] = (int32_t)(taskA);
. The current thread's pointer is maintained as: currentTcbPt
.
Then the systick is set up to interrupt at every 10ms. An assembly setup function sets up initial stack pointer to the thread stack pointed to by currentTcbPt
. This function is as follows:
osSchedulerLaunch: // This routine loads up the first thread's stack pointer into SP
CPSID I
LDR R0,=currentTcbPt
LDR R2,[R0] // R2 = address of current TCB
LDR SP,[R2]
POP {R4-R11}
POP {R0-R3}
POP {R12}
ADD SP,SP,#4 // Skip 4 bytes to discard LR
POP {LR}
ADD SP,SP,#4 // Skip 4 bytes to discard PSR
CPSIE I
BX LR
Now, my SysTick_Handler looks like this:
__attribute__( ( naked ) ) void SysTick_Handler(void) {
__asm(
"CPSID I \n"
"PUSH {R0-R12} \n"
"LDR R0,=currentTcbPt \n"
"LDR R1,[R0] \n"
"STR SP,[R1] \n"
"LDR R1,[R1,#4] \n"
"STR R1,[R0] \n"
"LDR SP,[R1] \n"
"POP {R4-R11} \n"
"POP {R0-R3} \n"
"POP {R12} \n"
"ADD SP,SP,#4 \n"
"POP {LR} \n"
"ADD SP,SP,#4 \n"
"CPSIE I \n"
"BX LR \n"
:[currentTcbPt] "=&r" (currentTcbPt)
);
}
I have added extra register operations so I can use it as a normal function.
OS init
Launching scheduler
t2
t2
[UsageFault]
Stack frame:
R0 = 00000003
R1 = 2000008C
R2 = 00000000
R3 = 000004B8
R12 = 00000000
LR = 0800148D
PC = 000004B8
PSR = 20000000
FSR/FAR:
CFSR = 00000000
HFSR = 00000000
DFSR = 00000000
AFSR = 00000000
Misc
LR/EXC_RETURN= FFFFFFF9
On examining the asm code using -d in_asm
option in QEMU and also using remote gdb, The problem seems to happen at the first line of the next task (the same address in PC
above).
EDIT: See the full code to reproduce https://gist.github.com/shivangsgangadia/b78c7c66492d5332c7b4d1806be9c5f6
The order of execution of function would be something like:
RTOS rtos();
rtos.addThreads(&task_a, &task_b, &task_c);
rtos.osKernelLaunch();
The problem was setting the T-bit in the Execution PSR register for individual task stacks. The course content that I was following skipped over the fact that the PSR is composed of 4 bytes, and the T-bit in the most significant byte.
Initially, I was setting it as: TCB_STACK[threadNumber][STACK_SIZE-1] = (1U << 6);
. This was causing the BX LR
to not pick up the return address properly.
Setting the 6th bit in the 4th byte, i.e., TCB_STACK[threadNumber][STACK_SIZE-1] = (1U << 24);
solved the problem and now the scheduler works flawlessly.