Uexec Embedded OS



WHAT IS UEXEC?

    A multitasking mini operating system. You can create tasks that
    have thier own stacks and message queues. When a task is created
    you assign the number of milliseconds it can run before another
    task is selected to run. This provides information about what
    percentage of the total processor it can have. Of course if the
    task blocks on receiving a message or sleeping it gives up its
    time slice immediately. All blocking UEXEC calls can be timed.

WHO WROTE UEXEC?

    Initially Richard F. Man (www.imagecraft.com) wrote UEXEC
    for the HC11. David Gates (davidgates@fourway.net) ported it to the
   ATMEL 103 and added message queues, signals, blocking, and sleep.

WHAT LANGUAGE IS UEXEC WRITTEN IN?

    Most of the code is in C. There is a small amount of assembly
    that is processor specific.

WHAT COMPILERS WERE USED TO BUILD UEXEC?

    GCCAVR was used.

WHAT MACHINES HAVE RUN UEXEC SUCCESSFULLY?

    The ATMEL 103L which has 128k of flash and 4k of ram. The flash
    holds up to 64k of code and 64k of data.

    A PowerPC version is available on request. It requires CodeWarrior.

WHAT ARE THE ADDITIONAL FEATURES?

    Tasks or drivers can send messages to any task created under
    UEXEC. Messages are queued and you specify the size of the
    message queue. You also indicate the size of each tasks stack.

    Tasks or drivers can send any task a signal to wake it up. Any
    task can sleep waiting on a signal.

    Tasks can attach themselves to the UEXEC clock. These tasks are
    scheduled periodically, for example every 100 milliseconds.

    Below is an internal include file used by UEXEC. It describes
    the states a task can have. Also the task control block is
    defined. The user of UEXEC does not have to be concerned with
    this, its for information only.

    Preemptive multitasking is used. The task scheduler looks for
    the highest priority task thats ready to run. Tasks of the
    same priority are run round robin.

WHAT ARE EOC MESSAGES?

    This was added to send messages out of the processor for
    an HDSL2 project i worked on. To bad its not TCP/IP!




/* Task State:
*/
enum {
    T_STOPPED, // task is not running at all
    T_CREATED, // task ready to run for 1st time
    T_READY, // task ready to run again
    T_WF_MSG, // task waiting for msg
    T_WF_EOC_MSG, // task waiting for EOC msg
    T_WF_SIGNAL, // task waiting for a signal
    T_SLEEPING, // task sleeping for a while
};



typedef struct TaskControlBlock
{
    struct TaskControlBlock* next; // pointer to next TCB
    unsigned char tid; /* task id */
    unsigned char prior; // priority 0 (highest)
    unsigned char state; /* task state */
    unsigned char ticksTReady; /* how many ticks does the task execute */
    unsigned int func; /* current PC for task */
    unsigned int stack_start; /* stack low value */
    unsigned int stack_end; /* stack high value */
    unsigned int sp; /* current stack pointer */
    unsigned char signal; // last signal number received
    unsigned char sigTid; // task id of task sending last signal
    unsigned int tmoTicks; // ticks left before timeout of task state
    unsigned char numMsgOnQue; // number of msgs waiting on que
    unsigned char* msgQueGetPtr; // msg queue get pointer for reading msgs
    unsigned char* msgQuePutPtr; // msg queue put pointer for saving msg
    unsigned char* msgQue1stPtr; // msg queue start pointer
    unsigned char* msgQueEndPtr; // msg queue end pointer + 1

} TaskControlBlock;

// list of tasks attached to clock
typedef struct AttachList_t
{
    unsigned int timer; // initial timer value in mills
    unsigned int ticks; // current timer value in ticks

} AttachList_t;



//Below is the user interface.


// create a new task
int UEXC_CreateTask(void (*func) (void), // initial start address
    unsigned char* stack_start,// address of stack buffer
    unsigned int stack_size, // size of stack buffer (200 recommended)
    unsigned char* msgQ_start, // address of msg que (optional)
    unsigned int msgQ_size, // size of msg que (optional)
    unsigned int mills, // min mills to run if not blocked
    unsigned char prior); // task's priority 0-255 (0 is highest)

// startup task scheduler
void UEXC_StartScheduler(void);

// give up current time slice
void UEXC_Defer(void); // task runs next time slice

// increase current time slice length
void UEXC_HogProcessor(void);


// take task off task schedule list
void UEXC_KillTask(int tid);

// get msg from task in this processor
char UEXC_ReadMsg( unsigned char* msgBuf, // user buffer to recv msg
    unsigned char bufSize, // byte size of buffer
    unsigned char* xmtTid, // task id that sent msg (if any)
    unsigned char* msgSize, // byte size of msg rcvd
    unsigned long tmoMills); // max millisec to wait

// send msg to task in this processor
char UEXC_SendMsg( unsigned char* msgBuf, // user buffer holding msg
    unsigned char bufSize, // byte length of msg to send
    unsigned char fromTid, // task id sending msg (if any)
    unsigned char destTid); // task id to sent to 0 to N

// check to see if msgs available
char UEXC_ifMsgAvail(); // from task in this processor)

// get msg from task in another processor
char UEXC_ReadEocMsg( unsigned char* msgBuf, // user buff to recv msg
    unsigned char bufSize, // bytes size of user buffer
    unsigned char lineNum, // hdsl2 line number 1 to N
    unsigned char* msgSize, // byte size of msg rcvd
    unsigned long tmoMills);// max millisec to wait

// wait for a signal to arrive
// from task in this processor
char UEXC_WaitSignal( unsigned long tmoMills, // time to wait, forever (0)
    unsigned char *sigVal, // signal number received
    unsigned char* fromTid); // task id that sent signal

// send signal to task in this procssor
char UEXC_SendSignal( unsigned char sigVal, // signal number to send
    unsigned char destTid); // task id to recv signal

// suspend task for time specified
char UEXC_Sleep(unsigned int seconds, // number of seconds to sleep
    unsigned int mills); // number if milliseconds to sleep

// task gets run periodically
char UEXC_Attach(unsigned int seconds, // number of seconds to sleep
    unsigned int mills); // number of milliseconds to sleep

// get task scheduler's clock ticks
unsigned int UEXC_GetClockTicks(); // current ticks, UEXC_CLOCK_TICK_MILLS each





//Below is an example usage of UEXEC. Two tasks are created and one
//sends messages to the other.

#include <stdio.h>
#include "uexec.h"
#include "uart.h"

unsigned char task_one_stack[200];
unsigned char task_one_msgQ [100];
int taskOneTid;

unsigned char task_two_stack[200];
unsigned char task_two_msgQ [100];
int taskTwoTid;

unsigned char task_3_stack[200];
int task3Tid;

void One(void);
void Two(void);
void RunsOffClock(void);
unsigned int msec(unsigned int time);
//
// main
//

int main(void)
{

    UART_Init();

    EOL();
    UPRINT("\r\n-------------------- main is alive--------------------");
    EOL();

    *LEDcmd = (U8)~0xff;

    taskOneTid = UEXC_CreateTask(
        One, // entry point
        task_one_stack, // stack ptr
        sizeof(task_one_stack), // stack size
        task_one_msgQ, // msgQ ptr
        sizeof(task_one_msgQ), // msqQ size
        (U16)UEXC_DEFAULT_TIME_SLICE, // time slice mills
        100); // task's priority

    taskTwoTid = UEXC_CreateTask(
    Two, // entry point
        task_two_stack, // stack ptr
        sizeof(task_two_stack), // stack size
        task_two_msgQ, // msgQ ptr
        sizeof(task_two_msgQ), // msqQ size
        (U16)UEXC_DEFAULT_TIME_SLICE, // time slice mills
        (U8)100); // task's priority (higher)

    task3Tid = UEXC_CreateTask(
        RunsOffClock, // entry point
        task_3_stack, // stack ptr
        sizeof(task_3_stack), // stack size
        0, // msgQ ptr (none)
        0, // msqQ size
        (U16)UEXC_DEFAULT_TIME_SLICE, // time slice mills
        100); // task's priority (higher)

    UEXC_StartScheduler(); // won't return

    UPRINT("\r\nmain still alive, can't happen");
    UPRINT("\r\nmain is finished");

    return 0;
}


//
// task One
//
extern void One(void)
{
    unsigned char msgBuf[20];
    U8 xmtTid, msgSize, i;
    U16 tmoMills=3000;

    while (1)
    {
        UPRINT("\r\n\r\ntask One entering 1 sec spin loop");
        msec(1000);

        if (UEXC_ReadMsg( msgBuf, sizeof(msgBuf), &xmtTid, &msgSize, tmoMills))
        {
            UPRINT("\r\ntask One got msg of 0x"); UPRINTU8(msgSize);
            UPRINT(" bytes from task id 0x"); UPRINTU8(xmtTid);
            UPRINT("\r\n");
            for (i=0; i<msgSize; ++i)
            {
                UPRINT(" "); UPRINTU8(msgBuf[i]);
            }
        }
    else
    UPRINT("\r\ntask One UEXC_ReadMsg failed");
    }
}

//
// task Two
//
extern void Two(void)
{
    U8 msgBuf[]={0,1,2,3,4,5,6,7,8,9};


    while (1)
    {
        UPRINT("\r\n\r\ntask Two 1 sec spin loop");
        msec(1000);


        UPRINT("\r\ntask Two calling UEXC_SendMsg");
        if (UEXC_SendMsg(msgBuf, sizeof(msgBuf), taskTwoTid, taskOneTid))
        {
            UPRINT("\r\ntask Two sent msg of 0x");
            UPRINTU8(sizeof(msgBuf)); UPRINT(" bytes\r\n");
            //for (i=0; i<sizeof(msgBuf); ++i)
            //{
            // UPRINT(" "); UPRINTU8(msgBuf[i]);
            //}
         }
        else
            UPRINT("\r\ntask Two UEXC_SendMsg failed");

        UPRINT("\r\ntask Two calling UEXC_Sleep 1 sec");
        UEXC_Sleep(1,0);

        ++msgBuf[0];
    }
}


extern void RunsOffClock(void)
{

    unsigned int ticks;

    UEXC_Attach(2,0); // attach to clock

    while (1)
    {
        //UPRINT("\r\ntask RunsOffClock calling UEXC_Sleep UEXC_SLEEP_ATTACHED");

        UEXC_Sleep( UEXC_SLEEP_ATTACHED ,0); // sleep until run off clock

        ticks = UEXC_GetClockTicks(); // get current clock ticks

        UPRINT("\r\nRunsOffClock ticks 0x"); UPRINTU16(ticks);

    }

}


//The uart.c and uart.h files describe the PRINT macro. Basically
//it is uses polled I/O to print or read from the serial port (uart).





//below is the make file for GCCAVR



# Simple Makefile
# Volker Oth (c) 1999

include $(AVR)/include/make1

########### change this lines according to your project ##################

#put the name of the target mcu here (-2313, -8515, -8535, -mega103, -mega106)
MCU = -mega103

#put the name of the target file here (without extension)
TRG = test

#put your C sourcefiles here
SRC = uexec.c uart.c $(TRG).c

#put additional assembler source file here
ASRC =

#needed libraries and object files to link
LIB =

#needed includes to compile
INC = uexec.h uart.h

#compiler flags
CPFLAGS = -O2 -g -save-temps -D GCCAVR

#assembler flags
ASFLAGS = -L

#linker flags
LDFLAGS = -v -l$(TRG).lnk

########### you should not need to change the following line #############
include $(AVR)/include/make2

###### dependecies, add any dependencies you need here ###################

uart.o : uart.c uart.h
uexec.o : uexec.c uexec.h
$(TRG).o : $(TRG).c



the modules used are:

test.c - test program for UEXEC
uexec.c - UEXEC multitasking operation system
uexec.h - header file for interface to user
_uexec.h - header defining task states and task control block
uart.c - uart read and write routines. includes printing text and hex
uart.h - uart user interface header file

Download uexec7.zip

Download avrgcc.zip


enjoy!

Home