2 * @file context_switch.s
3 * @brief ICARUS OS Context Switch Implementation (ARM Cortex-M7)
6 * @details Low-level assembly routines for task context switching.
7 * Implements PendSV-based preemptive context switch.
9 * @note This code runs in ITCM for zero wait-state execution.
11 * @see ARMv7-M Architecture Reference Manual
12 * @see docs/do178c/design/SDD.md Section 5.2 - Context Switch
14 * @author Souham Biswas
17 * @copyright Copyright (c) 2026 ICARUS Project
24/* Place in ITCM for zero wait-state execution */
25.section .itcm,"ax",%progbits
27/* ============================================================================
28 * TCB FIELD OFFSETS (must match icarus_types.h)
29 * ========================================================================= */
30.equ TCB_STACK_PTR, 12 /* offsetof(icarus_task_t, stack_pointer) */
31.equ TCB_TASK_STATE, 16 /* offsetof(icarus_task_t, task_state) */
32.equ TCB_TICK_PAUSED, 20 /* offsetof(icarus_task_t, global_tick_paused) */
33.equ TCB_TICKS_PAUSE, 24 /* offsetof(icarus_task_t, ticks_to_pause) */
34.equ TCB_DATA_PTR, 28 /* offsetof(icarus_task_t, data_pointer) */
36/* Task state constants */
44/* ============================================================================
45 * os_yield_pendsv - PendSV Context Switch Handler
46 * ============================================================================
48 * Called from PendSV_Handler to perform context switch.
49 * Saves current task context, selects next ready task, restores its context.
52 * r0 - current_task_index address / scratch
53 * r1 - task_list address / next task SP
54 * r2 - scratch (index calculations)
55 * r3 - current TCB pointer
56 * r4 - current PSP / task state
57 * r5 - num_created_tasks
58 * r6 - scheduler_enabled / scratch
59 * r7 - os_tick_count / scratch
60 * r8 - scratch (tick calculations)
61 * r9 - next_task_index
62 * r10 - scratch (index * 4)
63 * r11 - next TCB pointer
66.global os_yield_pendsv
67.type os_yield_pendsv, %function
70 /* Save current task context */
71 mrs r0, psp /* Read Process Stack Pointer */
72 stmdb r0!, {r4-r11} /* Push R4-R11 to task stack */
73 mov r4, r0 /* Save updated PSP */
75 /* Load kernel state pointers */
76 ldr r0, =current_task_index
78 ldr r5, =num_created_tasks
79 ldrb r5, [r5] /* r5 = task count */
80 ldrb r9, [r0] /* r9 = current index (will become next) */
82 /* Save current task's stack pointer to TCB */
83 ldrb r2, [r0] /* r2 = current_task_index */
84 lsl r2, r2, #2 /* r2 = index * 4 (pointer size) */
85 ldr r3, [r1, r2] /* r3 = task_list[current_task_index] */
86 str r4, [r3, #TCB_STACK_PTR] /* Save PSP to TCB */
88 /* Check if current task should transition to READY */
89 ldrb r4, [r3, #TCB_TASK_STATE]
90 cmp r4, #STATE_RUNNING
94 /* Round-robin: next = (current + 1) % num_tasks */
97 udiv r7, r6, r5 /* r7 = (current+1) / num_tasks */
98 mul r8, r7, r5 /* r8 = quotient * num_tasks */
99 sub r9, r6, r8 /* r9 = next_task_index (remainder) */
101 /* Load next task's TCB */
102 lsl r10, r9, #2 /* r10 = next_index * 4 */
103 ldr r11, [r1, r10] /* r11 = task_list[next_index] */
104 ldrb r4, [r11, #TCB_TASK_STATE] /* r4 = next task state */
106 /* State machine: determine if task is runnable */
108 beq yield_postprocess /* COLD tasks can start */
111 beq yield_postprocess /* READY tasks can run */
113 cmp r4, #STATE_KILLED
114 beq find_next_task /* Skip KILLED tasks */
116 cmp r4, #STATE_FINISHED
117 beq find_next_task /* Skip FINISHED tasks */
119 cmp r4, #STATE_BLOCKED
120 beq check_sleep_ticks /* Check if sleep expired */
122 b find_next_task /* Unknown state, skip */
125 /* Switch to next task */
126 strb r9, [r0] /* Update current_task_index */
128 /* Set up Memory Protection Unit for task data section */
129 push {r0-r3, r12, lr}
130 ldr r0, [r11, #TCB_DATA_PTR]
131 bl MPU_ConfigureTaskData
134 ldr r1, [r11, #TCB_STACK_PTR] /* Load next task's SP */
135 mov r5, #STATE_RUNNING
136 strb r5, [r11, #TCB_TASK_STATE] /* Set state to RUNNING */
138 /* Check if task is cold (first run) */
140 beq increment_running_count
142 /* Warm task: restore software-saved registers */
143 ldmia r1!, {r4-r11} /* Pop R4-R11 from task stack */
146 msr psp, r1 /* Switch to next task's stack */
147 bx lr /* Return from exception */
150 /* Check if blocked task's sleep has expired */
151 ldr r7, =os_tick_count
152 ldr r7, [r7] /* r7 = current tick */
153 ldr r6, [r11, #TCB_TICK_PAUSED] /* r6 = tick when blocked */
154 ldr r8, [r11, #TCB_TICKS_PAUSE] /* r8 = ticks to sleep */
155 sub r10, r7, r6 /* r10 = elapsed ticks */
156 cmp r8, r10 /* ticks_to_pause vs elapsed */
157 bhs find_next_task /* Still sleeping, try next */
158 b yield_postprocess /* Sleep done, run this task */
161 /* Check if scheduler is enabled before transitioning */
162 ldr r6, =scheduler_enabled
165 beq defer_ctx_switch /* Scheduler disabled, defer */
168 strb r4, [r3, #TCB_TASK_STATE] /* Set current to READY */
171increment_running_count:
172 /* First time running this task */
173 ldr r7, =running_task_count
177 b branch_to_next_task
180 /* Scheduler disabled: re-run current task */
181 mov r11, r3 /* Next = current */
184.size os_yield_pendsv, .-os_yield_pendsv
186/* ============================================================================
187 * start_cold_task - Initialize and Start First Task
188 * ============================================================================
190 * Called once from os_start() to begin task execution.
191 * Sets up PSP, enables interrupts, and jumps to first task.
193 * @param r0 Pointer to task control block (TCB)
195 * Stack frame expected (set up by os_create_task):
197 * [SP+20] PC (entry point)
198 * [SP+16] LR (os_exit_task)
203 * [SP-4] R0 <- Initial SP points here
206.global start_cold_task
207.type start_cold_task, %function
210 /* Load task's initial stack pointer */
211 ldr r1, [r0, #TCB_STACK_PTR]
212 msr psp, r1 /* Set Process Stack Pointer */
214 /* Mark task as RUNNING */
215 mov r1, #STATE_RUNNING
216 strb r1, [r0, #TCB_TASK_STATE]
218 /* Set OS running flags BEFORE switching to unprivileged mode */
223 ldr r5, =running_task_count
226 /* Switch to PSP for thread mode */
227 mov r1, #0x03 /* SPSEL = 1 (use PSP), nPRIV = 1 (unprivileged) */
229 isb /* Instruction barrier */
231 /* Pop exception frame from stack */
232 pop {r0-r3, r12, lr} /* R0-R3, R12, LR */
233 pop {r4} /* PC -> r4 */
234 pop {r5} /* xPSR -> r5 */
235 msr apsr_nzcvq, r5 /* Restore flags */
237 /* Enable interrupts and jump to task */
238 cpsie i /* Enable IRQ */
239 bx r4 /* Branch to task entry point */
241.size start_cold_task, .-start_cold_task
243/* End of context_switch.s */