Line 10... |
Line 10... |
* Plasma Real Time Operating System
|
* Plasma Real Time Operating System
|
* Fully pre-emptive RTOS with support for:
|
* Fully pre-emptive RTOS with support for:
|
* Heaps, Threads, Semaphores, Mutexes, Message Queues, and Timers.
|
* Heaps, Threads, Semaphores, Mutexes, Message Queues, and Timers.
|
* This file tries to be hardware independent except for calls to:
|
* This file tries to be hardware independent except for calls to:
|
* MemoryRead() and MemoryWrite() for interrupts.
|
* MemoryRead() and MemoryWrite() for interrupts.
|
* Partial support for multiple CPUs using symmetric multiprocessing.
|
* Support for multiple CPUs using symmetric multiprocessing.
|
*--------------------------------------------------------------------*/
|
*--------------------------------------------------------------------*/
|
#include "plasma.h"
|
#include "plasma.h"
|
#include "rtos.h"
|
#include "rtos.h"
|
|
|
#define HEAP_MAGIC 0x1234abcd
|
#define HEAP_MAGIC 0x1234abcd
|
Line 132... |
Line 132... |
static int ThreadSwapEnabled;
|
static int ThreadSwapEnabled;
|
static uint32 ThreadTime; //Number of ~10ms ticks since reboot
|
static uint32 ThreadTime; //Number of ~10ms ticks since reboot
|
static void *NeedToFree; //Closed but not yet freed thread
|
static void *NeedToFree; //Closed but not yet freed thread
|
static OS_Semaphore_t SemaphoreReserved[SEM_RESERVED_COUNT];
|
static OS_Semaphore_t SemaphoreReserved[SEM_RESERVED_COUNT];
|
static OS_Semaphore_t *SemaphoreSleep;
|
static OS_Semaphore_t *SemaphoreSleep;
|
static OS_Semaphore_t *SemaphoreRelease;
|
static OS_Semaphore_t *SemaphoreRelease; //Protects NeedToFree
|
static OS_Semaphore_t *SemaphoreLock;
|
static OS_Semaphore_t *SemaphoreLock;
|
static OS_Semaphore_t *SemaphoreTimer;
|
static OS_Semaphore_t *SemaphoreTimer;
|
static OS_Timer_t *TimerHead; //Linked list of timers sorted by timeout
|
static OS_Timer_t *TimerHead; //Linked list of timers sorted by timeout
|
static OS_FuncPtr_t Isr[32];
|
static OS_FuncPtr_t Isr[32]; //Interrupt service routines
|
|
#if defined(WIN32) && OS_CPU_COUNT > 1
|
|
static unsigned int Registration[OS_CPU_COUNT];
|
|
#endif
|
|
|
/***************** Heap *******************/
|
/***************** Heap *******************/
|
/******************************************/
|
/******************************************/
|
OS_Heap_t *OS_HeapCreate(const char *name, void *memory, uint32 size)
|
OS_Heap_t *OS_HeapCreate(const char *name, void *memory, uint32 size)
|
{
|
{
|
Line 418... |
Line 420... |
if(threadCurrent)
|
if(threadCurrent)
|
{
|
{
|
assert(threadCurrent->magic[0] == THREAD_MAGIC); //check stack overflow
|
assert(threadCurrent->magic[0] == THREAD_MAGIC); //check stack overflow
|
if(threadCurrent->state == THREAD_RUNNING)
|
if(threadCurrent->state == THREAD_RUNNING)
|
OS_ThreadPriorityInsert(&ThreadHead, threadCurrent);
|
OS_ThreadPriorityInsert(&ThreadHead, threadCurrent);
|
|
//printf("Pause(%d,%s) ", OS_CpuIndex(), threadCurrent->name);
|
rc = setjmp(threadCurrent->env); //ANSI C call to save registers
|
rc = setjmp(threadCurrent->env); //ANSI C call to save registers
|
if(rc)
|
if(rc)
|
|
{
|
|
//threadCurrent = ThreadCurrent[OS_CpuIndex()];
|
|
//printf("Resume(%d,%s) ", OS_CpuIndex(), threadCurrent->name);
|
return; //Returned from longjmp()
|
return; //Returned from longjmp()
|
}
|
}
|
|
}
|
|
|
//Remove the new running thread from the ThreadHead linked list
|
//Remove the new running thread from the ThreadHead linked list
|
threadNext = ThreadCurrent[OS_CpuIndex()]; //removed warning
|
threadNext = ThreadCurrent[OS_CpuIndex()]; //removed warning
|
assert(threadNext->state == THREAD_READY);
|
assert(threadNext->state == THREAD_READY);
|
OS_ThreadPriorityRemove(&ThreadHead, threadNext);
|
OS_ThreadPriorityRemove(&ThreadHead, threadNext);
|
threadNext->state = THREAD_RUNNING;
|
threadNext->state = THREAD_RUNNING;
|
threadNext->cpuIndex = OS_CpuIndex();
|
threadNext->cpuIndex = OS_CpuIndex();
|
|
#if defined(WIN32) && OS_CPU_COUNT > 1
|
|
//Set the Windows thread that the Plasma thread is running on
|
|
((jmp_buf2*)threadNext->env)->extra[0] = Registration[threadNext->cpuIndex];
|
|
#endif
|
longjmp(threadNext->env, 1); //ANSI C call to restore registers
|
longjmp(threadNext->env, 1); //ANSI C call to restore registers
|
}
|
}
|
}
|
}
|
|
|
|
|
Line 474... |
Line 485... |
{
|
{
|
OS_Thread_t *thread;
|
OS_Thread_t *thread;
|
uint8 *stack;
|
uint8 *stack;
|
jmp_buf2 *env;
|
jmp_buf2 *env;
|
uint32 state;
|
uint32 state;
|
|
#ifdef WIN32
|
|
int stackFrameSize;
|
|
#endif
|
|
|
OS_SemaphorePend(SemaphoreRelease, OS_WAIT_FOREVER);
|
OS_SemaphorePend(SemaphoreRelease, OS_WAIT_FOREVER);
|
if(NeedToFree)
|
if(NeedToFree)
|
OS_HeapFree(NeedToFree);
|
OS_HeapFree(NeedToFree);
|
NeedToFree = NULL;
|
NeedToFree = NULL;
|
Line 519... |
Line 533... |
thread->prevTimeout = NULL;
|
thread->prevTimeout = NULL;
|
thread->magic[0] = THREAD_MAGIC;
|
thread->magic[0] = THREAD_MAGIC;
|
|
|
OS_ThreadRegsInit(thread->env);
|
OS_ThreadRegsInit(thread->env);
|
env = (jmp_buf2*)thread->env;
|
env = (jmp_buf2*)thread->env;
|
env->sp = (uint32)stack + stackSize - 24; //minimum stack frame size
|
|
env->pc = (uint32)OS_ThreadInit;
|
env->pc = (uint32)OS_ThreadInit;
|
|
#ifndef WIN32
|
|
env->sp = (uint32)stack + stackSize - 24; //minimum stack frame size
|
|
#else
|
|
stackFrameSize = env->Ebp - env->sp;
|
|
env->Ebp = (uint32)stack + stackSize - 24;//stack frame base pointer
|
|
env->sp = env->Ebp - stackFrameSize;
|
|
#endif
|
|
|
//Add thread to linked list of ready to run threads
|
//Add thread to linked list of ready to run threads
|
state = OS_CriticalBegin();
|
state = OS_CriticalBegin();
|
OS_ThreadPriorityInsert(&ThreadHead, thread);
|
OS_ThreadPriorityInsert(&ThreadHead, thread);
|
OS_ThreadReschedule(0); //run highest priority thread
|
OS_ThreadReschedule(0); //run highest priority thread
|
Line 635... |
Line 655... |
|
|
|
|
/******************************************/
|
/******************************************/
|
//Must be called with interrupts disabled every ~10 msecs
|
//Must be called with interrupts disabled every ~10 msecs
|
//Will wake up threads that have timed out waiting on a semaphore
|
//Will wake up threads that have timed out waiting on a semaphore
|
void OS_ThreadTick(void *Arg)
|
static void OS_ThreadTick(void *Arg)
|
{
|
{
|
OS_Thread_t *thread;
|
OS_Thread_t *thread;
|
OS_Semaphore_t *semaphore;
|
OS_Semaphore_t *semaphore;
|
int diff;
|
int diff;
|
(void)Arg;
|
(void)Arg;
|
Line 1178... |
Line 1198... |
|
|
/******************************************/
|
/******************************************/
|
//Plasma hardware dependent
|
//Plasma hardware dependent
|
uint32 OS_InterruptStatus(void)
|
uint32 OS_InterruptStatus(void)
|
{
|
{
|
return MemoryRead(IRQ_STATUS);
|
return MemoryRead(IRQ_STATUS) & MemoryRead(IRQ_MASK);
|
}
|
}
|
|
|
|
|
/******************************************/
|
/******************************************/
|
//Plasma hardware dependent
|
//Plasma hardware dependent
|
Line 1212... |
Line 1232... |
|
|
/**************** Init ********************/
|
/**************** Init ********************/
|
/******************************************/
|
/******************************************/
|
//If there aren't any other ready to run threads then spin here
|
//If there aren't any other ready to run threads then spin here
|
static volatile uint32 IdleCount;
|
static volatile uint32 IdleCount;
|
|
static int SimulateIsr;
|
static void OS_IdleThread(void *arg)
|
static void OS_IdleThread(void *arg)
|
{
|
{
|
(void)arg;
|
(void)arg;
|
|
|
//Don't block in the idle thread!
|
//Don't block in the idle thread!
|
for(;;)
|
for(;;)
|
{
|
{
|
++IdleCount;
|
++IdleCount;
|
}
|
|
}
|
|
|
|
|
|
/******************************************/
|
|
#ifndef DISABLE_IRQ_SIM
|
#ifndef DISABLE_IRQ_SIM
|
//Simulate the hardware interrupts
|
MemoryRead(IRQ_MASK + 4); //will call Sleep
|
static void OS_IdleSimulateIsr(void *arg)
|
if(SimulateIsr && (int)arg == OS_CPU_COUNT-1)
|
{
|
{
|
uint32 count=0, value;
|
unsigned int value = IRQ_COUNTER18; //tick interrupt
|
(void)arg;
|
|
|
|
for(;;)
|
for(;;)
|
{
|
{
|
MemoryRead(IRQ_MASK + 4); //calls Sleep(10)
|
value |= OS_InterruptStatus();
|
#if WIN32
|
if(value == 0)
|
while(OS_InterruptMaskSet(0) & IRQ_UART_WRITE_AVAILABLE)
|
break;
|
OS_InterruptServiceRoutine(IRQ_UART_WRITE_AVAILABLE, 0);
|
|
#endif
|
|
value = OS_InterruptMaskSet(0) & 0xf;
|
|
if(value)
|
|
OS_InterruptServiceRoutine(value, 0);
|
OS_InterruptServiceRoutine(value, 0);
|
++count;
|
value = 0;
|
|
}
|
|
}
|
|
#endif
|
|
#if OS_CPU_COUNT > 1
|
|
if((int)arg < OS_CPU_COUNT - 1)
|
|
{
|
|
unsigned int state;
|
|
#ifndef WIN32
|
|
for(IdleCount = 0; IdleCount < 100000; ++IdleCount) ;
|
|
#endif
|
|
state = OS_SpinLock();
|
|
OS_ThreadReschedule(1);
|
|
OS_SpinUnlock(state);
|
|
}
|
|
#endif
|
}
|
}
|
}
|
}
|
#endif //DISABLE_IRQ_SIM
|
|
|
|
|
|
/******************************************/
|
/******************************************/
|
//Plasma hardware dependent
|
//Plasma hardware dependent
|
//ISR called every ~10 msecs when bit 18 of the counter register toggles
|
//ISR called every ~10 msecs when bit 18 of the counter register toggles
|
static void OS_ThreadTickToggle(void *arg)
|
static void OS_InterruptTick(void *arg)
|
{
|
{
|
uint32 status, mask, state;
|
uint32 state, mask;
|
|
|
//Toggle looking for IRQ_COUNTER18 or IRQ_COUNTER18_NOT
|
//Toggle looking for IRQ_COUNTER18 or IRQ_COUNTER18_NOT
|
state = OS_SpinLock();
|
state = OS_SpinLock();
|
status = MemoryRead(IRQ_STATUS) & (IRQ_COUNTER18 | IRQ_COUNTER18_NOT);
|
mask = MemoryRead(IRQ_MASK);
|
mask = MemoryRead(IRQ_MASK) | IRQ_COUNTER18 | IRQ_COUNTER18_NOT;
|
mask ^= IRQ_COUNTER18 | IRQ_COUNTER18_NOT;
|
mask &= ~status;
|
|
MemoryWrite(IRQ_MASK, mask);
|
MemoryWrite(IRQ_MASK, mask);
|
OS_ThreadTick(arg);
|
OS_ThreadTick(arg);
|
OS_SpinUnlock(state);
|
OS_SpinUnlock(state);
|
}
|
}
|
|
|
Line 1271... |
Line 1294... |
/******************************************/
|
/******************************************/
|
//Initialize the OS by setting up the system heap and the tick interrupt
|
//Initialize the OS by setting up the system heap and the tick interrupt
|
void OS_Init(uint32 *heapStorage, uint32 bytes)
|
void OS_Init(uint32 *heapStorage, uint32 bytes)
|
{
|
{
|
int i;
|
int i;
|
|
|
if((int)OS_Init > 0x10000000) //Running from DDR?
|
if((int)OS_Init > 0x10000000) //Running from DDR?
|
OS_AsmInterruptInit(); //Patch interrupt vector
|
OS_AsmInterruptInit(); //Patch interrupt vector
|
OS_InterruptMaskClear(0xffffffff); //Disable interrupts
|
OS_InterruptMaskClear(0xffffffff); //Disable interrupts
|
HeapArray[0] = OS_HeapCreate("Default", heapStorage, bytes);
|
HeapArray[0] = OS_HeapCreate("Default", heapStorage, bytes);
|
HeapArray[1] = HeapArray[0];
|
HeapArray[1] = HeapArray[0];
|
SemaphoreSleep = OS_SemaphoreCreate("Sleep", 0);
|
SemaphoreSleep = OS_SemaphoreCreate("Sleep", 0);
|
SemaphoreRelease = OS_SemaphoreCreate("Release", 1);
|
SemaphoreRelease = OS_SemaphoreCreate("Release", 1);
|
SemaphoreLock = OS_SemaphoreCreate("Lock", 1);
|
SemaphoreLock = OS_SemaphoreCreate("Lock", 1);
|
for(i = 0; i < OS_CPU_COUNT; ++i)
|
if((MemoryRead(IRQ_STATUS) & (IRQ_COUNTER18 | IRQ_COUNTER18_NOT)) == 0)
|
OS_ThreadCreate("Idle", OS_IdleThread, NULL, 0, 256);
|
|
#ifndef DISABLE_IRQ_SIM
|
|
if((OS_InterruptStatus() & (IRQ_COUNTER18 | IRQ_COUNTER18_NOT)) == 0)
|
|
{
|
{
|
//Detected that running in simulator so create SimIsr thread
|
//Detected running in simulator
|
UartPrintfCritical("SimIsr\n");
|
UartPrintfCritical("SimIsr\n");
|
OS_ThreadCreate("SimIsr", OS_IdleSimulateIsr, NULL, 1, 0);
|
SimulateIsr = 1;
|
}
|
}
|
#endif //DISABLE_IRQ_SIM
|
for(i = 0; i < OS_CPU_COUNT; ++i)
|
|
OS_ThreadCreate("Idle", OS_IdleThread, (void*)i, i, 256);
|
//Plasma hardware dependent (register for OS tick interrupt every ~10 msecs)
|
//Plasma hardware dependent (register for OS tick interrupt every ~10 msecs)
|
OS_InterruptRegister(IRQ_COUNTER18 | IRQ_COUNTER18_NOT, OS_ThreadTickToggle);
|
OS_InterruptRegister(IRQ_COUNTER18 | IRQ_COUNTER18_NOT, OS_InterruptTick);
|
OS_InterruptMaskSet(IRQ_COUNTER18 | IRQ_COUNTER18_NOT);
|
OS_InterruptMaskSet(IRQ_COUNTER18);
|
}
|
}
|
|
|
|
|
/******************************************/
|
/******************************************/
|
//Start thread swapping
|
//Start thread swapping
|
void OS_Start(void)
|
void OS_Start(void)
|
{
|
{
|
|
#if defined(WIN32) && OS_CPU_COUNT > 1
|
|
jmp_buf env;
|
|
OS_ThreadRegsInit(env);
|
|
Registration[OS_CpuIndex()] = ((jmp_buf2*)env)->extra[0];
|
|
#endif
|
ThreadSwapEnabled = 1;
|
ThreadSwapEnabled = 1;
|
(void)OS_SpinLock();
|
(void)OS_SpinLock();
|
OS_ThreadReschedule(1);
|
OS_ThreadReschedule(1);
|
}
|
}
|
|
|
Line 1312... |
Line 1339... |
void OS_Assert(void)
|
void OS_Assert(void)
|
{
|
{
|
}
|
}
|
|
|
|
|
#if OS_CPU_COUNT > 1
|
|
static uint8 SpinLockArray[OS_CPU_COUNT];
|
|
/******************************************/
|
|
uint32 OS_CpuIndex(void)
|
|
{
|
|
return 0; //0 to OS_CPU_COUNT-1
|
|
}
|
|
|
|
|
|
/******************************************/
|
|
//Symmetric Multiprocessing Spin Lock Mutex
|
|
uint32 OS_SpinLock(void)
|
|
{
|
|
uint32 state, cpuIndex, i, j, ok, delay;
|
|
|
|
cpuIndex = OS_CpuIndex();
|
|
delay = cpuIndex + 8;
|
|
state = OS_AsmInterruptEnable(0);
|
|
//Spin until only this CPU has the spin lock
|
|
do
|
|
{
|
|
ok = 1;
|
|
if(++SpinLockArray[cpuIndex] == 1)
|
|
{
|
|
for(i = 0; i < OS_CPU_COUNT; ++i)
|
|
{
|
|
if(i != cpuIndex && SpinLockArray[i])
|
|
ok = 0; //Another CPU has the spin lock
|
|
}
|
|
if(ok == 0)
|
|
{
|
|
SpinLockArray[cpuIndex] = 0;
|
|
for(j = 0; j < delay; ++j) //wait a bit
|
|
++i;
|
|
if(delay < 128)
|
|
delay <<= 1;
|
|
}
|
|
}
|
|
} while(ok == 0);
|
|
return state;
|
|
}
|
|
|
|
|
|
/******************************************/
|
|
void OS_SpinUnlock(uint32 state)
|
|
{
|
|
uint32 cpuIndex;
|
|
cpuIndex = OS_CpuIndex();
|
|
if(--SpinLockArray[cpuIndex] == 0)
|
|
OS_AsmInterruptEnable(state);
|
|
|
|
assert(SpinLockArray[cpuIndex] < 10);
|
|
}
|
|
#endif //OS_CPU_COUNT > 1
|
|
|
|
|
|
/************** WIN32/Linux Support *************/
|
|
#ifdef WIN32
|
|
#ifdef LINUX
|
|
#define putch putchar
|
|
#undef _LIBC
|
|
#undef kbhit
|
|
#undef getch
|
|
#define UartPrintf UartPrintf2
|
|
#define UartScanf UartScanf2
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <termios.h>
|
|
#include <unistd.h>
|
|
//Support RTOS inside Linux
|
|
void Sleep(unsigned int value)
|
|
{
|
|
usleep(value * 1000);
|
|
}
|
|
|
|
int kbhit(void)
|
|
{
|
|
struct termios oldt, newt;
|
|
struct timeval tv;
|
|
fd_set read_fd;
|
|
|
|
tcgetattr(STDIN_FILENO, &oldt);
|
|
newt = oldt;
|
|
newt.c_lflag &= ~(ICANON | ECHO);
|
|
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
|
|
tv.tv_sec=0;
|
|
tv.tv_usec=0;
|
|
FD_ZERO(&read_fd);
|
|
FD_SET(0,&read_fd);
|
|
if(select(1, &read_fd, NULL, NULL, &tv) == -1)
|
|
return 0;
|
|
if(FD_ISSET(0,&read_fd))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
int getch(void)
|
|
{
|
|
struct termios oldt, newt;
|
|
int ch;
|
|
|
|
tcgetattr(STDIN_FILENO, &oldt);
|
|
newt = oldt;
|
|
newt.c_lflag &= ~(ICANON | ECHO);
|
|
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
|
|
ch = getchar();
|
|
return ch;
|
|
}
|
|
#else
|
|
//Support RTOS inside Windows
|
|
#undef kbhit
|
|
#undef getch
|
|
#undef putch
|
|
extern int kbhit(void);
|
|
extern int getch(void);
|
|
extern int putch(int);
|
|
extern void __stdcall Sleep(unsigned long value);
|
|
#endif
|
|
|
|
static uint32 Memory[8];
|
|
|
|
//Simulates device register memory reads
|
|
uint32 MemoryRead(uint32 address)
|
|
{
|
|
Memory[2] |= IRQ_UART_WRITE_AVAILABLE; //IRQ_STATUS
|
|
switch(address)
|
|
{
|
|
case UART_READ:
|
|
if(kbhit())
|
|
Memory[0] = getch(); //UART_READ
|
|
Memory[2] &= ~IRQ_UART_READ_AVAILABLE; //clear bit
|
|
return Memory[0];
|
|
case IRQ_MASK:
|
|
return Memory[1]; //IRQ_MASK
|
|
case IRQ_MASK + 4:
|
|
Sleep(10);
|
|
return 0;
|
|
case IRQ_STATUS:
|
|
if(kbhit())
|
|
Memory[2] |= IRQ_UART_READ_AVAILABLE;
|
|
return Memory[2];
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//Simulates device register memory writes
|
|
void MemoryWrite(uint32 address, uint32 value)
|
|
{
|
|
switch(address)
|
|
{
|
|
case UART_WRITE:
|
|
putch(value);
|
|
break;
|
|
case IRQ_MASK:
|
|
Memory[1] = value;
|
|
break;
|
|
case IRQ_STATUS:
|
|
Memory[2] = value;
|
|
break;
|
|
}
|
|
}
|
|
|
|
uint32 OS_AsmInterruptEnable(uint32 enableInterrupt)
|
|
{
|
|
return enableInterrupt;
|
|
}
|
|
|
|
void OS_AsmInterruptInit(void)
|
|
{
|
|
}
|
|
#endif //WIN32
|
|
|
|
|
|
/**************** Example *****************/
|
/**************** Example *****************/
|
#ifndef NO_MAIN
|
#ifndef NO_MAIN
|
#ifdef WIN32
|
#ifdef WIN32
|
static uint8 HeapSpace[1024*512]; //For simulation on a PC
|
static uint8 HeapSpace[1024*512]; //For simulation on a PC
|
#endif
|
#endif
|
Line 1506... |
Line 1360... |
OS_Init((uint32*)programEnd,
|
OS_Init((uint32*)programEnd,
|
RAM_EXTERNAL_BASE + RAM_EXTERNAL_SIZE - programEnd);
|
RAM_EXTERNAL_BASE + RAM_EXTERNAL_SIZE - programEnd);
|
#endif
|
#endif
|
UartInit();
|
UartInit();
|
OS_ThreadCreate("Main", MainThread, NULL, 100, 4000);
|
OS_ThreadCreate("Main", MainThread, NULL, 100, 4000);
|
|
#if defined(WIN32) && OS_CPU_COUNT > 1
|
|
OS_InitSimulation();
|
|
#endif
|
OS_Start();
|
OS_Start();
|
return 0;
|
return 0;
|
}
|
}
|
#endif //NO_MAIN
|
#endif //NO_MAIN
|
|
|