1 /* Author: Domen Puncer <domen@cba.si>. License: WTFPL, see file LICENSE */
7 #include <arch/sched.h>
8 #include <arch/cm0_regs.h>
11 static u32 current_counter
;
15 /* so, here's the deal, SYSTICK->VALUE == 0 means we counted down,
16 * but then current_counter is already incremented, so just treat
17 * it as 0xffffff, which is the best approximation */
18 /* VALUE of 0 will add 0 to current_counter here: */
19 /* or so I thought, but following DOES NOT work ok at all */
20 //return current_counter + 0xffffff - ((SYSTICK->VALUE-1) & 0xffffff);
22 return current_counter
+ 0xffffff - (SYSTICK
->VALUE
& 0xffffff);
25 /* cortex-m3 pushes following on stack when entering exception: xPSR, PC, LR, r12, r3, r2, r1, r0
26 * what's remaining/needs to be saved is: PSP, r4-r11
28 void arch_task_new(struct task
*task
, void (*func
)(u32 arg
), u32 arg
)
30 /* saved by exception */
31 task
->stack
[task
->stack_len
-1] = 1<<24 /* xPSR, thumb bit */;
32 task
->stack
[task
->stack_len
-2] = (u32
)func
;
33 task
->stack
[task
->stack_len
-3] = 0x01010101*14 /* LR - this could be something nice, so tasks can actually exit */;
34 task
->stack
[task
->stack_len
-4] = 0x01010101*12 /* r12 */;
35 task
->stack
[task
->stack_len
-5] = 0x01010101*3 /* r3 */;
36 task
->stack
[task
->stack_len
-6] = 0x01010101*2 /* r2 */;
37 task
->stack
[task
->stack_len
-7] = 0x01010101*1 /* r1 */;
38 task
->stack
[task
->stack_len
-8] = arg
/* r0 */;
41 task
->stack
[task
->stack_len
-9] = 0x01010101*7 /* r7 */;
42 task
->stack
[task
->stack_len
-10] = 0x01010101*6 /* r6 */;
43 task
->stack
[task
->stack_len
-11] = 0x01010101*5 /* r5 */;
44 task
->stack
[task
->stack_len
-12] = 0x01010101*4 /* r4 */;
45 task
->stack
[task
->stack_len
-13] = 0x01010101*11 /* r11 */;
46 task
->stack
[task
->stack_len
-14] = 0x01010101*10 /* r10 */;
47 task
->stack
[task
->stack_len
-15] = 0x01010101*9 /* r9 */;
48 task
->stack
[task
->stack_len
-16] = 0x01010101*8 /* r8 */;
50 task
->context
.psp
= (u32
)&task
->stack
[task
->stack_len
-16];
53 void __naked
arch_task_first(struct task
*task
)
57 /* restore created context */
72 /* r2, r3 are scrubbed, that means arguments 2, 3 */
74 //"add sp, sp, #8\n\t" /* pc and xPSR space, just ignore it */
76 // "ldr pc, [sp, #-8]\n\t" /* and jump to the task entry, sp here is top of stack, so -8 is entry -2 = func */
77 //"ldr r3, [sp, #-8]\n\t" /* and jump to the task entry, sp here is top of stack, so -8 is entry -2 = func */
78 "pop {r2, r3}\n" /* r2 (pc), r3 (xPSR) */
80 : : "r" (task
->context
.psp
)
84 /* task to switch to */
85 static struct task
*new_task
;
87 /* cortex-m3 trm 5.11 Setting up multiple stacks
88 * does the task switching from current to new_task */
89 void __naked
pendsv_handler()
92 // XXX idea, also for m3, could just switch stacks, and use push/pop
96 /* first push r4-r7, then r8-r11 */
97 /* emulate stmdb, an alternative would be to abuse MSP */
99 "stmia r3!, {r4-r7}\n"
105 "stmia r3!, {r4-r7}\n"
109 "ldr r0, =current\n\t"
111 "str r3, [r1, %0]\n\t" /* current->context.psp = PSP */
114 "ldr r2, =new_task\n\t"
116 "ldr r3, [r2, %0]\n\t" /* PSP = new_task->context.psp */
119 "str r2, [r0]\n\t" /* current = new_task */
121 /* restore saved regs */
122 "ldmia r3!, {r4-r7}\n"
127 "ldmia r3!, {r4-r7}\n"
132 : : "i" (offsetof(struct task
, context
))
136 void arch_task_switch(struct task
*newt
)
139 /* request PendSV exception */
140 ICSR
= ICSR_PENDSVSET
;
143 void arch_sched_start_timer()
145 /* core clock, enable interrupt, enable systick */
148 SYSTICK
->CTRL
|= 1<<2 | 1<<1 | 1<<0;
150 /* set SysTick pending to get scheduled asap */
151 ICSR
= ICSR_PENDSTSET
;
154 void arch_wait_for_interrupt()
156 asm volatile ("wfi");
159 static int systick_whole
;
160 static int systick_remainder
;
162 /* writing anything to SYSTICK->VALUE clears it (and doesn't cause the interrupt), but the reloads it with RELOAD on next timer tick */
163 void arch_sched_next_interrupt(int offset
)
165 systick_whole
= offset
>> 24;
166 systick_remainder
= offset
& 0xffffff;
167 if (systick_remainder
== 0)
168 systick_remainder
= 1;
171 SYSTICK
->RELOAD
= 0xffffff;
174 SYSTICK
->RELOAD
= systick_remainder
;
175 SYSTICK
->VALUE
= 0; /* clear */
179 void systick_handler()
181 current_counter
+= SYSTICK
->RELOAD
;
184 /* last part? then load the remainder */
185 if (--systick_whole
== 0) {
186 SYSTICK
->RELOAD
= systick_remainder
;