ICARUS OS (Intelligent Cooperative Architecture for Real-time Unified Systems) 0.1.0
Preemptive Real-Time Operating System for ARM Cortex-M7
Loading...
Searching...
No Matches
context_switch.s
Go to the documentation of this file.
1/**
2 * @file context_switch.s
3 * @brief ICARUS OS Context Switch Implementation (ARM Cortex-M7)
4 * @version 0.1.0
5 *
6 * @details Low-level assembly routines for task context switching.
7 * Implements PendSV-based preemptive context switch.
8 *
9 * @note This code runs in ITCM for zero wait-state execution.
10 *
11 * @see ARMv7-M Architecture Reference Manual
12 * @see docs/do178c/design/SDD.md Section 5.2 - Context Switch
13 *
14 * @author Souham Biswas
15 * @date 2026
16 *
17 * @copyright Copyright (c) 2026 ICARUS Project
18 */
19
20.syntax unified
21.cpu cortex-m7
22.thumb
23
24/* Place in ITCM for zero wait-state execution */
25.section .itcm,"ax",%progbits
26
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) */
35
36/* Task state constants */
37.equ STATE_COLD, 0
38.equ STATE_RUNNING, 1
39.equ STATE_READY, 2
40.equ STATE_BLOCKED, 3
41.equ STATE_KILLED, 4
42.equ STATE_FINISHED, 5
43
44/* ============================================================================
45 * os_yield_pendsv - PendSV Context Switch Handler
46 * ============================================================================
47 *
48 * Called from PendSV_Handler to perform context switch.
49 * Saves current task context, selects next ready task, restores its context.
50 *
51 * Register usage:
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
64 */
65
66.global os_yield_pendsv
67.type os_yield_pendsv, %function
68
69os_yield_pendsv:
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 */
74
75 /* Load kernel state pointers */
76 ldr r0, =current_task_index
77 ldr r1, =task_list
78 ldr r5, =num_created_tasks
79 ldrb r5, [r5] /* r5 = task count */
80 ldrb r9, [r0] /* r9 = current index (will become next) */
81
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 */
87
88 /* Check if current task should transition to READY */
89 ldrb r4, [r3, #TCB_TASK_STATE]
90 cmp r4, #STATE_RUNNING
91 beq set_task_to_ready
92
93find_next_task:
94 /* Round-robin: next = (current + 1) % num_tasks */
95 mov r6, r9
96 add r6, r6, #1
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) */
100
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 */
105
106 /* State machine: determine if task is runnable */
107 cmp r4, #STATE_COLD
108 beq yield_postprocess /* COLD tasks can start */
109
110 cmp r4, #STATE_READY
111 beq yield_postprocess /* READY tasks can run */
112
113 cmp r4, #STATE_KILLED
114 beq find_next_task /* Skip KILLED tasks */
115
116 cmp r4, #STATE_FINISHED
117 beq find_next_task /* Skip FINISHED tasks */
118
119 cmp r4, #STATE_BLOCKED
120 beq check_sleep_ticks /* Check if sleep expired */
121
122 b find_next_task /* Unknown state, skip */
123
124yield_postprocess:
125 /* Switch to next task */
126 strb r9, [r0] /* Update current_task_index */
127
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
132 pop {r0-r3, r12, lr}
133
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 */
137
138 /* Check if task is cold (first run) */
139 cmp r4, #STATE_COLD
140 beq increment_running_count
141
142 /* Warm task: restore software-saved registers */
143 ldmia r1!, {r4-r11} /* Pop R4-R11 from task stack */
144
145branch_to_next_task:
146 msr psp, r1 /* Switch to next task's stack */
147 bx lr /* Return from exception */
148
149check_sleep_ticks:
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 */
159
160set_task_to_ready:
161 /* Check if scheduler is enabled before transitioning */
162 ldr r6, =scheduler_enabled
163 ldrb r6, [r6]
164 cmp r6, #0
165 beq defer_ctx_switch /* Scheduler disabled, defer */
166
167 mov r4, #STATE_READY
168 strb r4, [r3, #TCB_TASK_STATE] /* Set current to READY */
169 b find_next_task
170
171increment_running_count:
172 /* First time running this task */
173 ldr r7, =running_task_count
174 ldrb r6, [r7]
175 add r6, r6, #1
176 strb r6, [r7]
177 b branch_to_next_task
178
179defer_ctx_switch:
180 /* Scheduler disabled: re-run current task */
181 mov r11, r3 /* Next = current */
182 b yield_postprocess
183
184.size os_yield_pendsv, .-os_yield_pendsv
185
186/* ============================================================================
187 * start_cold_task - Initialize and Start First Task
188 * ============================================================================
189 *
190 * Called once from os_start() to begin task execution.
191 * Sets up PSP, enables interrupts, and jumps to first task.
192 *
193 * @param r0 Pointer to task control block (TCB)
194 *
195 * Stack frame expected (set up by os_create_task):
196 * [SP+24] xPSR
197 * [SP+20] PC (entry point)
198 * [SP+16] LR (os_exit_task)
199 * [SP+12] R12
200 * [SP+8] R3
201 * [SP+4] R2
202 * [SP+0] R1
203 * [SP-4] R0 <- Initial SP points here
204 */
205
206.global start_cold_task
207.type start_cold_task, %function
208
209start_cold_task:
210 /* Load task's initial stack pointer */
211 ldr r1, [r0, #TCB_STACK_PTR]
212 msr psp, r1 /* Set Process Stack Pointer */
213
214 /* Mark task as RUNNING */
215 mov r1, #STATE_RUNNING
216 strb r1, [r0, #TCB_TASK_STATE]
217
218 /* Set OS running flags BEFORE switching to unprivileged mode */
219 ldr r5, =os_running
220 mov r6, #1
221 strb r6, [r5]
222
223 ldr r5, =running_task_count
224 strb r6, [r5]
225
226 /* Switch to PSP for thread mode */
227 mov r1, #0x03 /* SPSEL = 1 (use PSP), nPRIV = 1 (unprivileged) */
228 msr control, r1
229 isb /* Instruction barrier */
230
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 */
236
237 /* Enable interrupts and jump to task */
238 cpsie i /* Enable IRQ */
239 bx r4 /* Branch to task entry point */
240
241.size start_cold_task, .-start_cold_task
242
243/* End of context_switch.s */