Alternate Raw32 scheduler.
Works for main thread + one other thread. Currently fails with 2 other threads. STM32M4 code tested and working.ugfx_release_2.6
parent
2f6aaa9586
commit
f2ee56661c
|
@ -430,50 +430,62 @@ void gfxSleepMicroseconds(delaytime_t ms) {
|
||||||
* Threading functions
|
* Threading functions
|
||||||
*********************************************************/
|
*********************************************************/
|
||||||
|
|
||||||
/**
|
#if GOS_RAW_SCHEDULER == SCHED_USE_SETJMP
|
||||||
* There are some compilers we know how they store the jmpbuf. For those
|
|
||||||
* we can use the constant macro definitions. For others we have to "auto-detect".
|
/**
|
||||||
* Auto-detection is hairy and there is no guarantee it will work on all architectures.
|
* There are some compilers we know how they store the jmpbuf. For those
|
||||||
* For those it doesn't - read the compiler manuals and the library source code to
|
* we can use the constant macro definitions. For others we have to "auto-detect".
|
||||||
* work out the correct macro values.
|
* Auto-detection is hairy and there is no guarantee it will work on all architectures.
|
||||||
* You can use the debugger to work out the values for your compiler and put them here.
|
* For those it doesn't - read the compiler manuals and the library source code to
|
||||||
* Defining these macros as constant values makes the system behavior guaranteed but also
|
* work out the correct macro values.
|
||||||
* makes your code compiler and cpu architecture dependent.
|
* You can use the debugger to work out the values for your compiler and put them here.
|
||||||
*/
|
* Defining these macros as constant values makes the system behavior guaranteed but also
|
||||||
#if 0
|
* makes your code compiler and cpu architecture dependent.
|
||||||
// Define your compiler constant values here.
|
*/
|
||||||
// These example values are for mingw32 compiler (x86).
|
#if 0
|
||||||
#define AUTO_DETECT_MASK FALSE
|
// Define your compiler constant values here.
|
||||||
#define STACK_DIR_UP FALSE
|
// These example values are for mingw32 compiler (x86).
|
||||||
#define MASK1 0x00000011
|
#define AUTO_DETECT_MASK FALSE
|
||||||
#define MASK2 0x00000000
|
#define STACK_DIR_UP FALSE
|
||||||
#define STACK_BASE 9
|
#define MASK1 0x00000011
|
||||||
|
#define MASK2 0x00000000
|
||||||
|
#define STACK_BASE 9
|
||||||
|
#else
|
||||||
|
#define AUTO_DETECT_MASK TRUE
|
||||||
|
#define STACK_DIR_UP stackdirup // TRUE if the stack grow up instead of down
|
||||||
|
#define MASK1 jmpmask1 // The 1st mask of jmp_buf elements that need relocation
|
||||||
|
#define MASK2 jmpmask2 // The 2nd mask of jmp_buf elements that need relocation
|
||||||
|
#define STACK_BASE stackbase // The base of the stack frame relative to the local variables
|
||||||
|
static bool_t stackdirup;
|
||||||
|
static uint32_t jmpmask1;
|
||||||
|
static uint32_t jmpmask2;
|
||||||
|
static size_t stackbase;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <setjmp.h> /* jmp_buf, setjmp(), longjmp() */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Some compilers define a _setjmp() and a setjmp().
|
||||||
|
* The difference between them is that setjmp() saves the signal masks.
|
||||||
|
* That is of no use to us so prefer to use the _setjmp() methods.
|
||||||
|
* If they don't exist compile them to be the standard setjmp() function.
|
||||||
|
* Similarly for longjmp().
|
||||||
|
*/
|
||||||
|
#if (!defined(setjmp) && !defined(_setjmp)) || defined(__KEIL__) || defined(__C51__)
|
||||||
|
#define _setjmp setjmp
|
||||||
|
#endif
|
||||||
|
#if (!defined(longjmp) && !defined(_longjmp)) || defined(__KEIL__) || defined(__C51__)
|
||||||
|
#define _longjmp longjmp
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef jmp_buf threadcxt;
|
||||||
|
|
||||||
|
#elif GOS_RAW_SCHEDULER == SCHED_USE_CORTEX_M0 || GOS_RAW_SCHEDULER == SCHED_USE_CORTEX_M1
|
||||||
|
typedef void * threadcxt;
|
||||||
|
#elif GOS_RAW_SCHEDULER == SCHED_USE_CORTEX_M3 || GOS_RAW_SCHEDULER == SCHED_USE_CORTEX_M4
|
||||||
|
typedef void * threadcxt;
|
||||||
#else
|
#else
|
||||||
#define AUTO_DETECT_MASK TRUE
|
#error "GOS RAW32: Unsupported Scheduler. Try setting GOS_RAW_SCHEDULER = SCHED_USE_SETJMP"
|
||||||
#define STACK_DIR_UP stackdirup // TRUE if the stack grow up instead of down
|
|
||||||
#define MASK1 jmpmask1 // The 1st mask of jmp_buf elements that need relocation
|
|
||||||
#define MASK2 jmpmask2 // The 2nd mask of jmp_buf elements that need relocation
|
|
||||||
#define STACK_BASE stackbase // The base of the stack frame relative to the local variables
|
|
||||||
static bool_t stackdirup;
|
|
||||||
static uint32_t jmpmask1;
|
|
||||||
static uint32_t jmpmask2;
|
|
||||||
static size_t stackbase;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <setjmp.h> /* jmp_buf, setjmp(), longjmp() */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Some compilers define a _setjmp() and a setjmp().
|
|
||||||
* The difference between them is that setjmp() saves the signal masks.
|
|
||||||
* That is of no use to us so prefer to use the _setjmp() methods.
|
|
||||||
* If they don't exist compile them to be the standard setjmp() function.
|
|
||||||
* Similarly for longjmp().
|
|
||||||
*/
|
|
||||||
#if (!defined(setjmp) && !defined(_setjmp)) || defined(__KEIL__) || defined(__C51__)
|
|
||||||
#define _setjmp setjmp
|
|
||||||
#endif
|
|
||||||
#if (!defined(longjmp) && !defined(_longjmp)) || defined(__KEIL__) || defined(__C51__)
|
|
||||||
#define _longjmp longjmp
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef struct thread {
|
typedef struct thread {
|
||||||
|
@ -486,7 +498,7 @@ typedef struct thread {
|
||||||
size_t size; // Size of the thread stack (including this structure)
|
size_t size; // Size of the thread stack (including this structure)
|
||||||
threadreturn_t (*fn)(void *param); // Thread function
|
threadreturn_t (*fn)(void *param); // Thread function
|
||||||
void * param; // Parameter for the thread function
|
void * param; // Parameter for the thread function
|
||||||
jmp_buf cxt; // The current thread context.
|
threadcxt cxt; // The current thread context.
|
||||||
} thread;
|
} thread;
|
||||||
|
|
||||||
typedef struct threadQ {
|
typedef struct threadQ {
|
||||||
|
@ -522,7 +534,8 @@ static thread *Qpop(threadQ * q) {
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if AUTO_DETECT_MASK
|
#if GOS_RAW_SCHEDULER == SCHED_USE_SETJMP && AUTO_DETECT_MASK
|
||||||
|
|
||||||
// The structure for the saved stack frame information
|
// The structure for the saved stack frame information
|
||||||
typedef struct saveloc {
|
typedef struct saveloc {
|
||||||
char * localptr;
|
char * localptr;
|
||||||
|
@ -535,7 +548,7 @@ static thread *Qpop(threadQ * q) {
|
||||||
/* These functions are not static to prevent the compiler removing them as functions */
|
/* These functions are not static to prevent the compiler removing them as functions */
|
||||||
|
|
||||||
void get_stack_state(void) {
|
void get_stack_state(void) {
|
||||||
char* c;
|
char *c;
|
||||||
pframeinfo->localptr = (char *)&c;
|
pframeinfo->localptr = (char *)&c;
|
||||||
_setjmp(pframeinfo->cxt);
|
_setjmp(pframeinfo->cxt);
|
||||||
}
|
}
|
||||||
|
@ -556,7 +569,7 @@ static void _gosThreadsInit(void) {
|
||||||
current->fn = 0;
|
current->fn = 0;
|
||||||
current->param = 0;
|
current->param = 0;
|
||||||
|
|
||||||
#if AUTO_DETECT_MASK
|
#if GOS_RAW_SCHEDULER == SCHED_USE_SETJMP && AUTO_DETECT_MASK
|
||||||
{
|
{
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
char ** pout;
|
char ** pout;
|
||||||
|
@ -608,24 +621,165 @@ gfxThreadHandle gfxThreadMe(void) {
|
||||||
return (gfxThreadHandle)current;
|
return (gfxThreadHandle)current;
|
||||||
}
|
}
|
||||||
|
|
||||||
void gfxYield(void) {
|
// Check if there are dead processes to deallocate
|
||||||
if (!_setjmp(current->cxt)) {
|
static void cleanUpDeadThreads(void) {
|
||||||
// Add us back to the Queue
|
thread *p;
|
||||||
Qadd(&readyQ, current);
|
|
||||||
|
|
||||||
// Check if there are dead processes to deallocate
|
while ((p = Qpop(&deadQ)))
|
||||||
while ((current = Qpop(&deadQ)))
|
gfxFree(p);
|
||||||
gfxFree(current);
|
}
|
||||||
|
|
||||||
// Run the next process
|
#if GOS_RAW_SCHEDULER == SCHED_USE_SETJMP
|
||||||
current = Qpop(&readyQ);
|
// Move the stack frame and relocate the context data
|
||||||
_longjmp(current->cxt, 1);
|
void _gfxAdjustCxt(thread *t) {
|
||||||
|
char ** s;
|
||||||
|
char * nf;
|
||||||
|
int diff;
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
// Copy the stack frame
|
||||||
|
#if AUTO_DETECT_MASK
|
||||||
|
if (STACK_DIR_UP) { // Stack grows up
|
||||||
|
nf = (char *)(t) + sizeof(thread) + stackbase;
|
||||||
|
memcpy(t+1, (char *)&s - stackbase, stackbase+sizeof(char *));
|
||||||
|
} else { // Stack grows down
|
||||||
|
nf = (char *)(t) + t->size - (stackbase + sizeof(char *));
|
||||||
|
memcpy(nf, &s, stackbase+sizeof(char *));
|
||||||
|
}
|
||||||
|
#elif STACK_DIR_UP
|
||||||
|
// Stack grows up
|
||||||
|
nf = (char *)(t) + sizeof(thread) + stackbase;
|
||||||
|
memcpy(t+1, (char *)&s - stackbase, stackbase+sizeof(char *));
|
||||||
|
#else
|
||||||
|
// Stack grows down
|
||||||
|
nf = (char *)(t) + t->size - (stackbase + sizeof(char *));
|
||||||
|
memcpy(nf, &s, stackbase+sizeof(char *));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Relocate the context data
|
||||||
|
s = (char **)(t->cxt);
|
||||||
|
diff = nf - (char *)&s;
|
||||||
|
|
||||||
|
// Relocate the elements we know need to be relocated
|
||||||
|
for (i = 1; i && i < MASK1; i <<= 1, s++) {
|
||||||
|
if ((MASK1 & i))
|
||||||
|
*s += diff;
|
||||||
|
}
|
||||||
|
#ifdef MASK2
|
||||||
|
for (i = 1; i && i < MASK2; i <<= 1, s++) {
|
||||||
|
if ((MASK1 & i))
|
||||||
|
*s += diff;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
#define CXT_SET(t) { \
|
||||||
|
if (!_setjmp(t->cxt)) { \
|
||||||
|
_gfxAdjustCxt(t); \
|
||||||
|
current = t; \
|
||||||
|
_longjmp(current->cxt, 1); \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
#define CXT_SAVE() if (_setjmp(current->cxt)) return
|
||||||
|
#define CXT_RESTORE() _longjmp(current->cxt, 1)
|
||||||
|
|
||||||
|
#elif GOS_RAW_SCHEDULER == SCHED_USE_CORTEX_M0 || GOS_RAW_SCHEDULER == SCHED_USE_CORTEX_M1
|
||||||
|
|
||||||
|
// Use the EABI calling standard (ARM's AAPCS) - Save r4 - r11
|
||||||
|
|
||||||
|
#define CXT_SET(t) { \
|
||||||
|
register void *r13 asm ("r13"); \
|
||||||
|
current = t; \
|
||||||
|
r13 = (char *)current + current->size; \
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save the current thread context.
|
||||||
|
*
|
||||||
|
* Automatically returns the calling function when this thread gets restarted
|
||||||
|
* with the thread handle as the return value
|
||||||
|
*/
|
||||||
|
//
|
||||||
|
#define CXT_SAVE() { \
|
||||||
|
register void *r13 asm ("r13"); \
|
||||||
|
asm volatile ( "push {r4, r5, r6, r7, lr} \n\t" \
|
||||||
|
"mov r4, r8 \n\t" \
|
||||||
|
"mov r5, r9 \n\t" \
|
||||||
|
"mov r6, r10 \n\t" \
|
||||||
|
"mov r7, r11 \n\t" \
|
||||||
|
"push {r4, r5, r6, r7}" : : : "memory"); \
|
||||||
|
current->ctx = r13; \
|
||||||
|
}
|
||||||
|
#define CXT_RESTORE() { \
|
||||||
|
register void * r13 asm ("r13"); \
|
||||||
|
r13 = current->ctx; \
|
||||||
|
asm volatile ( "pop {r4, r5, r6, r7} \n\t" \
|
||||||
|
"mov r8, r4 \n\t" \
|
||||||
|
"mov r9, r5 \n\t" \
|
||||||
|
"mov r10, r6 \n\t" \
|
||||||
|
"mov r11, r7 \n\t" \
|
||||||
|
"pop {r4, r5, r6, r7, pc}" : : "r" (r13) : "memory"); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif GOS_RAW_SCHEDULER == SCHED_USE_CORTEX_M3 || GOS_RAW_SCHEDULER == SCHED_USE_CORTEX_M4
|
||||||
|
|
||||||
|
// Use the EABI calling standard (ARM's AAPCS) - Save r4 - r11 and floating point if needed
|
||||||
|
|
||||||
|
#define CXT_SET(t) { \
|
||||||
|
register void *r13 asm ("r13"); \
|
||||||
|
current = t; \
|
||||||
|
r13 = (char *)current + current->size; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#if CORTEX_USE_FPU
|
||||||
|
#define CXT_SAVE() { \
|
||||||
|
register void *r13 asm ("r13"); \
|
||||||
|
asm volatile ("push {r4, r5, r6, r7, r8, r9, r10, r11, lr}" : : : "memory");\
|
||||||
|
asm volatile ("vpush {s16-s31}" : : : "memory"); \
|
||||||
|
current->ctx = r13; \
|
||||||
|
}
|
||||||
|
#define CXT_RESTORE() { \
|
||||||
|
register void * r13 asm ("r13"); \
|
||||||
|
r13 = current->ctx; \
|
||||||
|
asm volatile ("vpop {s16-s31}" : : : "memory"); \
|
||||||
|
asm volatile ("pop {r4, r5, r6, r7, r8, r9, r10, r11, pc}" : : : "memory"); \
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#define CXT_SAVE() { \
|
||||||
|
register void *r13 asm ("r13"); \
|
||||||
|
asm volatile ("push {r4, r5, r6, r7, r8, r9, r10, r11, lr}" : : : "memory");\
|
||||||
|
current->ctx = r13; \
|
||||||
|
}
|
||||||
|
#define CXT_RESTORE() { \
|
||||||
|
register void * r13 asm ("r13"); \
|
||||||
|
r13 = current->ctx; \
|
||||||
|
asm volatile ("pop {r4, r5, r6, r7, r8, r9, r10, r11, pc}" : : : "memory"); \
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void gfxYield(void) {
|
||||||
|
|
||||||
|
// Clean up zombies
|
||||||
|
cleanUpDeadThreads();
|
||||||
|
|
||||||
|
// Is there another thread to run?
|
||||||
|
if (!readyQ.head)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Save the current thread (automatically returns when this thread gets re-executed)
|
||||||
|
CXT_SAVE();
|
||||||
|
|
||||||
|
// Add us back to the Queue
|
||||||
|
Qadd(&readyQ, current);
|
||||||
|
|
||||||
|
// Run the next process
|
||||||
|
current = Qpop(&readyQ);
|
||||||
|
CXT_RESTORE();
|
||||||
}
|
}
|
||||||
|
|
||||||
// This routine is not currently public - but it could be.
|
// This routine is not currently public - but it could be.
|
||||||
void gfxThreadExit(threadreturn_t ret) {
|
void gfxThreadExit(threadreturn_t ret) {
|
||||||
// Save the results
|
// Save the results in case someone is waiting
|
||||||
current->param = (void *)ret;
|
current->param = (void *)ret;
|
||||||
current->flags |= FLG_THD_DEAD;
|
current->flags |= FLG_THD_DEAD;
|
||||||
|
|
||||||
|
@ -634,11 +788,32 @@ void gfxThreadExit(threadreturn_t ret) {
|
||||||
if ((current->flags & (FLG_THD_ALLOC|FLG_THD_WAIT)) == FLG_THD_ALLOC)
|
if ((current->flags & (FLG_THD_ALLOC|FLG_THD_WAIT)) == FLG_THD_ALLOC)
|
||||||
Qadd(&deadQ, current);
|
Qadd(&deadQ, current);
|
||||||
|
|
||||||
// Switch to the next task
|
// Set the next thread
|
||||||
current = Qpop(&readyQ);
|
current = Qpop(&readyQ);
|
||||||
|
|
||||||
|
// Was that the last thread? If so exit
|
||||||
if (!current)
|
if (!current)
|
||||||
gfxExit(); // Oops - this should never happen!
|
gfxExit();
|
||||||
_longjmp(current->cxt, 1);
|
|
||||||
|
// Switch to the new thread
|
||||||
|
CXT_RESTORE();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _gfxStartThread(thread *t) {
|
||||||
|
|
||||||
|
// Save the current thread (automatically returns when this thread gets re-executed)
|
||||||
|
CXT_SAVE();
|
||||||
|
|
||||||
|
// Add the current thread to the queue because we are starting a new thread.
|
||||||
|
Qadd(&readyQ, current);
|
||||||
|
|
||||||
|
// Change to the new thread and the new stack
|
||||||
|
CXT_SET(t);
|
||||||
|
|
||||||
|
// Run the users function
|
||||||
|
gfxThreadExit(current->fn(current->param));
|
||||||
|
|
||||||
|
// We never get here!
|
||||||
}
|
}
|
||||||
|
|
||||||
gfxThreadHandle gfxThreadCreate(void *stackarea, size_t stacksz, threadpriority_t prio, DECLARE_THREAD_FUNCTION((*fn),p), void *param) {
|
gfxThreadHandle gfxThreadCreate(void *stackarea, size_t stacksz, threadpriority_t prio, DECLARE_THREAD_FUNCTION((*fn),p), void *param) {
|
||||||
|
@ -663,59 +838,10 @@ gfxThreadHandle gfxThreadCreate(void *stackarea, size_t stacksz, threadpriority_
|
||||||
t->size = stacksz;
|
t->size = stacksz;
|
||||||
t->fn = fn;
|
t->fn = fn;
|
||||||
t->param = param;
|
t->param = param;
|
||||||
if (_setjmp(t->cxt)) {
|
|
||||||
// This is the new thread - call the function!
|
|
||||||
gfxThreadExit(current->fn(current->param));
|
|
||||||
|
|
||||||
// We never get here
|
_gfxStartThread(t);
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move the stack frame and relocate the context data
|
// Return the new thread handle
|
||||||
{
|
|
||||||
char ** s;
|
|
||||||
char * nf;
|
|
||||||
int diff;
|
|
||||||
uint32_t i;
|
|
||||||
|
|
||||||
// Copy the stack frame
|
|
||||||
#if AUTO_DETECT_MASK
|
|
||||||
if (STACK_DIR_UP) { // Stack grows up
|
|
||||||
nf = (char *)(t) + sizeof(thread) + stackbase;
|
|
||||||
memcpy(t+1, (char *)&t - stackbase, stackbase+sizeof(char *));
|
|
||||||
} else { // Stack grows down
|
|
||||||
nf = (char *)(t) + stacksz - (stackbase + sizeof(char *));
|
|
||||||
memcpy(nf, &t, stackbase+sizeof(char *));
|
|
||||||
}
|
|
||||||
#elif STACK_DIR_UP
|
|
||||||
// Stack grows up
|
|
||||||
nf = (char *)(t) + sizeof(thread) + stackbase;
|
|
||||||
memcpy(t+1, (char *)&t - stackbase, stackbase+sizeof(char *));
|
|
||||||
#else
|
|
||||||
// Stack grows down
|
|
||||||
nf = (char *)(t) + size - (stackbase + sizeof(char *));
|
|
||||||
memcpy(nf, &t, stackbase+sizeof(char *));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Relocate the context data
|
|
||||||
s = (char **)(t->cxt);
|
|
||||||
diff = nf - (char *)&t;
|
|
||||||
|
|
||||||
// Relocate the elements we know need to be relocated
|
|
||||||
for (i = 1; i && i < MASK1; i <<= 1, s++) {
|
|
||||||
if ((MASK1 & i))
|
|
||||||
*s += diff;
|
|
||||||
}
|
|
||||||
#ifdef MASK2
|
|
||||||
for (i = 1; i && i < MASK2; i <<= 1, s++) {
|
|
||||||
if ((MASK1 & i))
|
|
||||||
*s += diff;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add this thread to the ready queue
|
|
||||||
Qadd(&readyQ, t);
|
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,25 @@
|
||||||
#define GOS_RAW_HEAP_SIZE 0
|
#define GOS_RAW_HEAP_SIZE 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Scheduler cpu support
|
||||||
|
*/
|
||||||
|
#define SCHED_USE_SETJMP 0
|
||||||
|
#define SCHED_USE_CORTEX_M0 1
|
||||||
|
#define SCHED_USE_CORTEX_M1 2
|
||||||
|
#define SCHED_USE_CORTEX_M2 3
|
||||||
|
#define SCHED_USE_CORTEX_M3 4
|
||||||
|
#define SCHED_USE_CORTEX_M4 5
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the preferred scheduler method
|
||||||
|
* @note If not defined the SCHED_USE_SETJMP is used which should work for most platforms.
|
||||||
|
*/
|
||||||
|
#ifndef GOS_RAW_SCHEDULER
|
||||||
|
#define GOS_RAW_SCHEDULER SCHED_USE_SETJMP
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/*===========================================================================*/
|
/*===========================================================================*/
|
||||||
/* Type definitions */
|
/* Type definitions */
|
||||||
/*===========================================================================*/
|
/*===========================================================================*/
|
||||||
|
|
Loading…
Reference in New Issue