Skip to content

Commit 3087d46

Browse files
committed
SchedulerARMAVR
Proposal for a new version of the Scheduler.h, now including asm code for AVR and 3 macro to facilitate atomic and concurent situation handling
1 parent a0a51fc commit 3087d46

File tree

4 files changed

+439
-0
lines changed

4 files changed

+439
-0
lines changed
Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
/*
2+
* Copyright (C) 2012 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
18+
/* modified by Fabrice Oudert 8 Jan 2013
19+
* including spcific code for compatibility with AVR platform (not 2560)
20+
* Scheduler library package available on google Code here:
21+
* https://code.google.com/p/arduino-scoop-cooperative-scheduler-arm-avr/
22+
*/
23+
24+
#include "SchedulerARMAVR.h"
25+
26+
extern "C" {
27+
28+
#if defined(__arm__)
29+
#define NUM_REGS (8+1+1) // r4-r11, sp, pc
30+
#elif defined(__AVR_3_BYTE_PC__)
31+
#error "not compatible with atmega256x, sorry ! please provide ideas on the arduino.cc forum"
32+
#else
33+
#define NUM_REGS (8+1+1+1) // r2-r17, sp, pc, r28-r29
34+
#endif
35+
36+
typedef struct CoopTask {
37+
#ifdef __arm__
38+
uint32_t regs[NUM_REGS];
39+
#else
40+
uint16_t regs[NUM_REGS];
41+
#endif
42+
void* stackPtr;
43+
struct CoopTask* next;
44+
struct CoopTask* prev;
45+
} CoopTask;
46+
47+
static CoopTask *cur = 0;
48+
49+
static CoopTask* __attribute__((noinline)) coopSchedule(char taskDied) {
50+
CoopTask* next = cur->next;
51+
52+
if (taskDied) {
53+
// Halt if last task died.
54+
55+
if (next == cur)
56+
while (1)
57+
;
58+
59+
// Delete task
60+
if (cur->stackPtr)
61+
free(cur->stackPtr);
62+
cur->next->prev = cur->prev;
63+
cur->prev->next = cur->next;
64+
free(cur);
65+
}
66+
cur = next;
67+
#ifdef __AVR__
68+
asm volatile ("movw r30, %[reg]" : : [reg] "r" (next));
69+
asm volatile ("ldd r5, Z+3");
70+
asm volatile ("ldd r6, Z+4");
71+
asm volatile ("ldd r7, Z+5");
72+
asm volatile ("ldd r8, Z+6");
73+
asm volatile ("ldd r9, Z+7");
74+
asm volatile ("ldd r10, Z+8");
75+
asm volatile ("ldd r11, Z+9");
76+
asm volatile ("ldd r12, Z+10");
77+
asm volatile ("ldd r13, Z+11");
78+
asm volatile ("ldd r14, Z+12");
79+
asm volatile ("ldd r15, Z+13");
80+
asm volatile ("ldd r16, Z+14");
81+
asm volatile ("ldd r17, Z+15");
82+
83+
asm volatile ("ldd r2, Z+16"); // restore SP
84+
asm volatile ("ldd r3, Z+17");
85+
asm volatile ("in r4, 0x3f"); // save SREG
86+
asm volatile ("cli "); // just to be safe on playing with stack ptr :) (useless with xmega)
87+
asm volatile ("out 0x3e, r3"); // SPH
88+
asm volatile ("out 0x3d, r2"); // SPL
89+
asm volatile ("out 0x3f, r4"); // restore SREG asap (same approach as in setjmp.S credit to Marek Michalkiewicz)
90+
91+
asm volatile ("ldd r2, Z+0");
92+
asm volatile ("ldd r3, Z+1");
93+
asm volatile ("ldd r4, Z+2");
94+
asm volatile ("ldd r28, Z+20"); // get previous R28,R29
95+
asm volatile ("ldd r29, Z+21");
96+
asm volatile ("ldd __tmp_reg__, Z+18"); // return low
97+
asm volatile ("ldd r31, Z+19"); // return hi
98+
asm volatile ("mov r30,__tmp_reg__");
99+
asm volatile ("ijmp"); // jump back to task return adress
100+
#endif
101+
102+
return cur; // just to avoid warning !
103+
}
104+
105+
static void __attribute__((naked)) __attribute__((noinline)) coopTaskStart(void) {
106+
#ifdef __arm__
107+
asm (
108+
"mov r0, r5;"
109+
"blx r4;"
110+
"mov r0, #1;"
111+
"bl coopSchedule;"
112+
"ldmia r0, {r4-r12, lr};"
113+
"mov sp, r12;"
114+
"bx lr;"
115+
);
116+
#else
117+
asm volatile ("movw r30,r2"); // get original taskF parameter
118+
asm volatile ("movw r24,r4"); // get original taskData parameter
119+
asm volatile ("icall"); // call taskF
120+
asm volatile ("ldi r24,1"); // task died
121+
asm volatile ("rjmp coopSchedule"); // get next task pointer
122+
#endif
123+
}
124+
125+
static void __attribute__((naked)) __attribute__((noinline)) coopDoYield(CoopTask* curTask) {
126+
#ifdef __arm__
127+
asm (
128+
"mov r12, sp;"
129+
"stmia r0, {r4-r12, lr};"
130+
"mov r0, #0;"
131+
"bl coopSchedule;"
132+
"ldmia r0, {r4-r12, lr};"
133+
"mov sp, r12;"
134+
"bx lr;"
135+
);
136+
#else
137+
asm volatile ("movw r30, %[cur]" : : [cur] "r" (curTask));
138+
asm volatile ("std Z+0, r2"); // save register in current cooptask structure
139+
asm volatile ("std Z+1, r3");
140+
asm volatile ("std Z+2, r4");
141+
asm volatile ("std Z+3, r5");
142+
asm volatile ("std Z+4, r6");
143+
asm volatile ("std Z+5, r7");
144+
asm volatile ("std Z+6, r8");
145+
asm volatile ("std Z+7, r9");
146+
asm volatile ("std Z+8, r10");
147+
asm volatile ("std Z+9, r11");
148+
asm volatile ("std Z+10, r12");
149+
asm volatile ("std Z+11, r13");
150+
asm volatile ("std Z+12, r14");
151+
asm volatile ("std Z+13, r15");
152+
asm volatile ("std Z+14, r16");
153+
asm volatile ("std Z+15, r17");
154+
asm volatile ("pop r3"); // return adresse = yield caller /// NOT COMPATIBLE 2560
155+
asm volatile ("pop r2");
156+
asm volatile ("std Z+18, r2"); // store return adress
157+
asm volatile ("std Z+19, r3");
158+
asm volatile ("in r2, 0x3d"); // SPL
159+
asm volatile ("in r3, 0x3e"); // SPH
160+
asm volatile ("std Z+16, r2");
161+
asm volatile ("std Z+17, r3");
162+
asm volatile ("std Z+20, r28");
163+
asm volatile ("std Z+21, r29");
164+
165+
asm volatile ("ldi r24, 0");
166+
asm volatile ("rjmp coopSchedule"); // get next task pointer
167+
#endif
168+
}
169+
170+
static int coopInit(void) {
171+
CoopTask* task;
172+
173+
task = reinterpret_cast<CoopTask *>(malloc(sizeof(CoopTask)));
174+
if (!task)
175+
return 0;
176+
task->next = task;
177+
task->prev = task;
178+
task->stackPtr = 0;
179+
cur = task;
180+
181+
return 1;
182+
}
183+
184+
static int coopSpawn(SchedulerParametricTask taskF, void* taskData, uint32_t stackSz) {
185+
uint8_t *stack = (uint8_t*)malloc(stackSz);
186+
if (!stack)
187+
return 0;
188+
189+
CoopTask *task = reinterpret_cast<CoopTask *>(malloc(sizeof(CoopTask)));
190+
if (!task) {
191+
free(stack);
192+
return 0;
193+
}
194+
task->stackPtr = stack;
195+
task->regs[0] = (uint32_t) taskF;
196+
task->regs[1] = (uint32_t) taskData;
197+
task->regs[8] = ((uint32_t)(stack + stackSz))
198+
#ifdef __arm__
199+
& ~7
200+
#endif
201+
;
202+
task->regs[9] = (uint32_t) & coopTaskStart;
203+
204+
task->prev = cur;
205+
task->next = cur->next;
206+
cur->next->prev = task;
207+
cur->next = task;
208+
209+
// These are here so compiler is sure that function is
210+
// referenced in both variants (cancels a warning)
211+
if (stackSz == 0xFFFFFFFF)
212+
coopSchedule(0);
213+
if (stackSz == 0xFFFFFFFE)
214+
coopSchedule(1);
215+
216+
return 1;
217+
}
218+
219+
void yield(void) {
220+
coopDoYield(cur);
221+
}
222+
223+
}; // extern "C"
224+
225+
SchedulerClass::SchedulerClass() {
226+
coopInit();
227+
}
228+
229+
static void startLoopHelper(void *taskData) {
230+
SchedulerTask task = reinterpret_cast<SchedulerTask>(taskData);
231+
while (true)
232+
task();
233+
}
234+
235+
void SchedulerClass::startLoop(SchedulerTask task, uint32_t stackSize) {
236+
coopSpawn(startLoopHelper, reinterpret_cast<void *>(task), stackSize);
237+
}
238+
239+
static void startTaskHelper(void *taskData) {
240+
SchedulerTask task = reinterpret_cast<SchedulerTask>(taskData);
241+
task();
242+
}
243+
244+
void SchedulerClass::start(SchedulerTask task, uint32_t stackSize) {
245+
coopSpawn(startTaskHelper, reinterpret_cast<void *>(task), stackSize);
246+
}
247+
248+
void SchedulerClass::start(SchedulerParametricTask task, void *taskData, uint32_t stackSize) {
249+
coopSpawn(task, taskData, stackSize);
250+
}
251+
// added for some level of compatibility with Arduino < 150
252+
void SchedulerClass::delay(uint32_t ms) {
253+
uint32_t end = millis() + ms;
254+
while (millis() < end) yield();
255+
}
256+
257+
SchedulerClass Scheduler;
258+
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Copyright (C) 2012 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
18+
/* modified by Fabrice Oudert 8 Jan 2013
19+
* including spcific code for compatibility with AVR platform (not atmega2560)
20+
* Scheduler library package available on google Code here:
21+
* https://code.google.com/p/arduino-scoop-cooperative-scheduler-arm-avr/
22+
*/
23+
24+
#ifndef _SCHEDULDER_H_
25+
#define _SCHEDULDER_H_
26+
27+
#include <Arduino.h>
28+
29+
extern "C" {
30+
typedef void (*SchedulerTask)(void);
31+
typedef void (*SchedulerParametricTask)(void *);
32+
33+
#if ARDUINO < 150
34+
void yield(void); // define a global yield function
35+
#warning "library calls to Arduino delay doesnt contain yield() and might block scheduler during this delay"
36+
#endif
37+
38+
}
39+
40+
#ifndef yieldPROTECT
41+
#define yieldPROTECT() static uint8_t __yieldProtect = 0; \
42+
uint8_t* __temp __attribute__((__cleanup__(__yieldUnprotect))) = & __yieldProtect; \
43+
while (__yieldProtect) yield(); __yieldProtect = 1;
44+
void inline __yieldUnprotect(uint8_t* *__s) { uint8_t* staticFlag = *__s; *staticFlag = 0; };
45+
#endif
46+
47+
#ifndef yieldATOMIC
48+
#define yieldATOMIC for ( uint8_t __temp __attribute__((__cleanup__(__decYieldAtomic))) = __incYieldAtomic(); __temp ; __temp = 0 )
49+
static volatile uint8_t __yieldAtomic = 0;
50+
void inline __decYieldAtomic(const uint8_t *__s) { --__yieldAtomic; }
51+
uint8_t inline __incYieldAtomic(void) { ++__yieldAtomic; return 1; }
52+
#endif
53+
54+
#ifdef __AVR__
55+
#define SchedulerDefaultStack 256 // proposed value, might be too low fo complex application or too high if not needed ... to be experimented by user
56+
#else
57+
#define SchedulerDefaultStack 1024 // original value , probably too much for most usage, but lot of memory on SAM
58+
#endif
59+
60+
61+
class SchedulerClass {
62+
public:
63+
SchedulerClass();
64+
static void startLoop(SchedulerTask task, uint32_t stackSize = SchedulerDefaultStack);
65+
static void start(SchedulerTask task, uint32_t stackSize = SchedulerDefaultStack);
66+
static void start(SchedulerParametricTask task, void *data, uint32_t stackSize = SchedulerDefaultStack);
67+
static void delay(uint32_t ms);
68+
69+
static void yield() { ::yield(); };
70+
};
71+
72+
extern SchedulerClass Scheduler;
73+
74+
75+
#endif
76+

0 commit comments

Comments
 (0)