Finished RTOS similar to FreeRTOS

This is an RTOS developed by myself, with some code taken from FreeRTOS. This is some of the most difficult code I have ever written, although it really doesn’t do anything useful. Man, context switching in C can be a bear! It’s like a programatic buffer overflow! (seriously, you do a function call and just pop off enough stuff to overwrite the instruction pointer).

Framework

The purpose of this assignment is to begin implementing our RTOS.

You are to implement the first several functions and data items listed in the UIKAPI documentation: UIKInitialize, UIKAddTask, UIKRun, UIKDelay, and the internals UIKDispatcher, UIKIdle, UIKTickHandler, UIKTicknum, UIKTickLen, and UIKIntDepth. This will also involve the implementation of the context-switching mechanism you will use inside the process of switching tasks.

In order to demonstrate your scheduler and the use of the API, you can modify some of your earlier assignments to use the API, or write new tasks. You should be able to demonstrate the current execution of at least three tasks at once, all being scheduled by your system.

The code will follow at the end, but first, a video demonstration.

Semaphores

The purpose of this assignment is to add counting semaphores to theUIKAPI. This assignment
can be done in groups of up to two people.

You are to implement the following counting ssemaphore functions:

+ UIKSem* UIKSemCreate(); – will create and initialize a new counting semaphore. The
new semaphore should be initialized to 1. The function should return a pointer to the
new semaphore, or a value of -1 if there was problem creating the semaphore.
+ void UIKSemPend(UIKSem* sem); – will perform the P(s) (wait or test or acquire) operation
on the semaphore. This function should decrement the semaphore value, and if
the result is negative, should block the requesting task.
+ void UIKSemPost(UIKSem* sem); – will perfrmthe V (s) (signal or release) operation on
the semophore. This function is the one that should be called when a task is leaving a
critical section. It should increment the semaphore value, and if the result is less than or
equal to zero, should unblock the highest priority task that is waiting on the semaphore
by changing its state to ready.
+ INT8 UIKSemValue(UIKSem* sem); – will return the current value of the semaphore.

In addition to implementing the semaphore functions, design a set of tasks that demonstrates
the proper operation of your semaphore functions.

The critical sections are implemented by disabling interrupts.  In this assignment, there is a task that delays in the middle of a semaphore lock to demonstrate the trace when two tasks are fighting for the same resource.

Alrighty, so here is a video demonstrating this:

Event Queues

The purpose of this assignment is to add event queues and the associated support routines to
the UIKAPI. This is the last piece of the UIK we will implement – yeah!

A common requirement is for a task to wait until an event occurs. An event is usually some
sort of externally-triggered operation, such as an I/O device requiring service or a timer going
off. One possibility is for an interrupt service routine to set an event flag, then allow tasks to
wait until that flag is set. Once the flag is set, the task can become ready.

An event flag group is an O/S global variable (byte) that represents events by bits. For the
purpose of this exercise, only one event flag will be defined: Bit 0 represents a timer event.
This flag should be set whenever an auxilliary timer goes off. For our implementation, this
timer can be implemented using one of the timers built into the Atmel AVR, spearate from the
timer you are using for the tick. You should write in ISR that handles an interrupt from this
timer by setting bit 0 in the event flags variable.

You are to implement the following event functions:

+ void UIKAssocEvent(int Event); – will cause the task to become ¿associated¿ with
the flag(s) specified by Event. Event is a value that specifies the bits representing one or
more events that the task should be associated with. The task should then block until the
event occurs.
+ void UIKDisassocEvent(int Event); – causes the task to no longer be associated with
the event. In other words, the task will not be scheduled when the event occurs.

In addition to implementing the semaphore functions, design an application that demonstrates
the proper operation of your event functions.

Events are fairly straightforward. project6 adds a timer0 event. taskblinky3 (in tasks.h/tasks.c) is associated with a timer0 overflow via a call to UIKAssocEvent (which sets the task to a wait state and associates the function with the flag). It uses a global flag, EVENT, to keep track of when an interupt occurs. Then, during the tick, if this EVENT flag is set then the function associated with this event will be set to ready.

Here is a video of this:

Code

lundeen_rtos.tar.gz

I guess I’ll post most of it inline here too, because why not?

//main.c
/*
 * This is a simple demonstration of the uik api.
 * It schedules 3 simple tasks and lets the scheduler take over execution
 */

#include "uik.h"
#include "tasks.h"

/* As per the specs, TICKLEN is the number of 10^-9 seconds on a 1MHz chip */
/* as set below it 1 TICKLEN == 1/100 seconds */
const int TICKLEN = 10000;

/*declare vars in tasks.h */
extern uint8_t taskblinky_stack[64];
extern void taskblinky();
extern uint8_t taskblinky2_stack[64];
extern void taskblinky2();
extern uint8_t taskblinky3_stack[64];
extern void taskblinky3();
extern uint8_t taskcount_stack[80];
extern void taskcount();

extern UIKSem* portbsem;

void init_timer0() {
  /*prescaler/64*/
  TCCR0 |= ((1 << CS01) | (1 << CS00));
  /*overflow mode - about every 1/61 seconds */
  TIMSK |= (1 << TOIE0);
}

void main(void) {
  int id;

  /*Initialize the ports */
  DDRB = 0xff;
  DDRD = 0x00;

  /*semaphore to protect portb */
  portbsem = UIKSemCreate();
  PORTB = 0xff;

  /*Initialize the RTOS - this sets up the timer */
  UIKInitialize(TICKLEN);

  /*Tell the RTOS about our tasks */
  id = UIKADDTask(&taskblinky, 42, taskblinky_stack, 64);
  UIKRun(id);
  id = UIKADDTask(&taskblinky2, 35, taskblinky2_stack, 64);
  UIKRun(id);
  id = UIKADDTask(&taskblinky3, 99, taskblinky3_stack, 64);
  UIKRun(id);
  id = UIKADDTask(&taskcount, 30, taskcount_stack, 80);
  UIKRun(id);

  /*this is an external event we make use of */
  init_timer0();

  /*this enables interupts, so doesn't need to be done in main */
  startrtos();
}

//tasks.h
/*
 * This is a simple demonstration of the uik api.
 * It schedules 3 simple tasks and lets the scheduler take over execution
 */

#include "uik.h"
#include "tasks.h"

/* As per the specs, TICKLEN is the number of 10^-9 seconds on a 1MHz chip */
/* as set below it 1 TICKLEN == 1/100 seconds */
const int TICKLEN = 10000;

/*declare vars in tasks.h */
extern uint8_t taskblinky_stack[64];
extern void taskblinky();
extern uint8_t taskblinky2_stack[64];
extern void taskblinky2();
extern uint8_t taskblinky3_stack[64];
extern void taskblinky3();
extern uint8_t taskcount_stack[80];
extern void taskcount();

extern UIKSem* portbsem;

void init_timer0() {
  /*prescaler/64*/
  TCCR0 |= ((1 << CS01) | (1 << CS00));
  /*overflow mode - about every 1/61 seconds */
  TIMSK |= (1 << TOIE0);
}

void main(void) {
  int id;

  /*Initialize the ports */
  DDRB = 0xff;
  DDRD = 0x00;

  /*semaphore to protect portb */
  portbsem = UIKSemCreate();
  PORTB = 0xff;

  /*Initialize the RTOS - this sets up the timer */
  UIKInitialize(TICKLEN);

  /*Tell the RTOS about our tasks */
  id = UIKADDTask(&taskblinky, 42, taskblinky_stack, 64);
  UIKRun(id);
  id = UIKADDTask(&taskblinky2, 35, taskblinky2_stack, 64);
  UIKRun(id);
  id = UIKADDTask(&taskblinky3, 99, taskblinky3_stack, 64);
  UIKRun(id);
  id = UIKADDTask(&taskcount, 30, taskcount_stack, 80);
  UIKRun(id);

  /*this is an external event we make use of */
  init_timer0();

  /*this enables interupts, so doesn't need to be done in main */
  startrtos();
}

lundeen@AnnieWilkes:~/classes/rtos/project6/project6$ ls
default  main.c  project6.aps  project6.avi  project6.aws  README  sourceLineMap.txt  tasks.c  tasks.h  uik.c  uik.h
lundeen@AnnieWilkes:~/classes/rtos/project6/project6$ cat tasks.h
/*This file contains prototypes of the tasks that will be added */

#ifndef TASKS_H
#define TASKS_H

#include "uik.h"
#include

/*semaphore to protect portb */
UIKSem* portbsem;

/*declare places to save contexts */
uint8_t taskblinky_stack[64] __attribute__((weak));
/*flash PORTB (tested with LEDs) */
void taskblinky();

/*declare places to save contexts - this has a forced conflict with blinky*/
uint8_t taskblinky2_stack[64] __attribute__((weak));
/*flash PORTB (tested with LEDs) */
void taskblinky2();

/*declare places to save contexts */
uint8_t taskblinky3_stack[64] __attribute__((weak));
/*flash PORTB (tested with LEDs) */
void taskblinky3();

/*count the lower bytes of PORTB up from 0 to 8 */
uint8_t taskcount_stack[80] __attribute__((weak));
void taskcount();

#endif

//tasks.h
/*This file contains prototypes of the tasks that will be added */

#ifndef TASKS_H
#define TASKS_H

#include "uik.h"
#include

/*semaphore to protect portb */
UIKSem* portbsem;

/*declare places to save contexts */
uint8_t taskblinky_stack[64] __attribute__((weak));
/*flash PORTB (tested with LEDs) */
void taskblinky();

/*declare places to save contexts - this has a forced conflict with blinky*/
uint8_t taskblinky2_stack[64] __attribute__((weak));
/*flash PORTB (tested with LEDs) */
void taskblinky2();

/*declare places to save contexts */
uint8_t taskblinky3_stack[64] __attribute__((weak));
/*flash PORTB (tested with LEDs) */
void taskblinky3();

/*count the lower bytes of PORTB up from 0 to 8 */
uint8_t taskcount_stack[80] __attribute__((weak));
void taskcount();

#endif

//uik.h
/*
 * uik.h contains the header for the uik kernel
 */

#ifndef UIK_H
#define UIK_H

#include
#include

#include
#include

#define max_numtasks 16

typedef enum { wait, ready, run, initialized } task_state;

typedef struct task_context {
  volatile task_state state;
  uint8_t priority;
  //stack_ptr includes registers, sreg, ip, - eg context
  uint8_t* stack_ptr;
  //delay is the amount of time the process must wait
  uint32_t delay;
} task_context;

/* for now, just make a static array for tcb
   TODO: make tcb into something better, like a linked list */
volatile task_context tcb[max_numtasks];

/*saves the current context */
void savecontext( ) __attribute__((naked));
/*restores the context of the taskid */
void restorecontext(uint8_t id) __attribute__((naked));

void UIKInitialize(uint16_t ticklen);
uint8_t UIKADDTask(void* task_ptr, uint8_t priority, uint8_t* buffer_ptr, uint16_t stack_size);
void UIKRun(uint8_t taskid);
void SIG_OUTPUT_COMPARE1A(void) __attribute__ ((signal, naked));
void tick(void);
void ticknodelay(void);
void startrtos(void) __attribute__ ((naked));
void UIKDelay(uint32_t ticks) __attribute__ ((naked));

/*the idle task should always be part of the tasklist */
uint8_t idletask_stack[64] __attribute__((weak));
void idletask();

/*semaphores */

typedef struct _UIKSem {
  uint8_t value;
} UIKSem;

UIKSem* UIKSemCreate();
unsigned char semtest(UIKSem* sem);
void UIKSemPend(UIKSem* sem);
void UIKSemPost(UIKSem* sem);
uint8_t UIKSemValue(UIKSem* sem);

/*Events */

/*MAX_EVENTS cannot be larger than the size of EVENT_FLAG*/
#define MAX_EVENTS 8
#define PORT0_OVERFLOW 0
/*bit 0 of event_flag is associated with a timer0 interrupt */
unsigned char EVENT_FLAG;

uint8_t ASS_BLOCK[MAX_EVENTS];
void UIKAssocEvent(uint8_t Event);
void UIKDisassocEvent(uint8_t Event);

#endif
/*
 * uik.c contains the source for the uik kernel
 */

#include "uik.h"

int numtasks = 0;

/*0 is always the idle task, which is the default curr_task */
int curr_task = 0;

/*pointer to current task */
volatile uint8_t* currTCB;

/*
 * macro for saving the context: first saves sreg, disables interrupts
 * pushes all general purpose registers on the stack and saves where the
 * stack is at
 */

#define savecontext()
  asm volatile ( "push  r0               nt"
                 "in    r0, __SREG__     nt"
                 "cli                    nt"
                 "push  r0               nt"
                 "push  r1               nt"
                 "clr   r1               nt"
                 "push  r2               nt"
                 "push  r3               nt"
                 "push  r4               nt"
                 "push  r5               nt"
                 "push  r6               nt"
                 "push  r7               nt"
                 "push  r8               nt"
                 "push  r9               nt"
                 "push  r10              nt"
                 "push  r11              nt"
                 "push  r12              nt"
                 "push  r13              nt"
                 "push  r14              nt"
                 "push  r15              nt"
                 "push  r16              nt"
                 "push  r17              nt"
                 "push  r18              nt"
                 "push  r19              nt"
                 "push  r20              nt"
                 "push  r21              nt"
                 "push  r22              nt"
                 "push  r23              nt"
                 "push  r24              nt"
                 "push  r25              nt"
                 "push  r26              nt"
                 "push  r27              nt"
                 "push  r28              nt"
                 "push  r29              nt"
                 "push  r30              nt"
                 "push  r31              nt"
                 "lds  r26, currTCB      nt"
                 "lds  r27, currTCB+1    nt"
                 "in    r0, 0x3d         nt"
                 "st    x+, r0           nt"
                 "in    r0, 0x3e         nt"
                 "st    x+, r0           nt"
        );

/*
 * Opposite to savecontext().  Interrupts will have been disabled during
 * the context save so we can write to the stack pointer.
 */

#define restorecontext()
  asm volatile (    "lds    r26, currTCB               nt"
                    "lds    r27, currTCB+1             nt"
                    "ld     r28, x+                    nt"
                    "out    __SP_L__, r28              nt"
                    "ld        r29, x+                 nt"
                    "out    __SP_H__, r29              nt"
                    "pop    r31                        nt"
                    "pop    r30                        nt"
                    "pop    r29                        nt"
                    "pop    r28                        nt"
                    "pop    r27                        nt"
                    "pop    r26                        nt"
                    "pop    r25                        nt"
                    "pop    r24                        nt"
                    "pop    r23                        nt"
                    "pop    r22                        nt"
                    "pop    r21                        nt"
                    "pop    r20                        nt"
                    "pop    r19                        nt"
                    "pop    r18                        nt"
                    "pop    r17                        nt"
                    "pop    r16                        nt"
                    "pop    r15                        nt"
                    "pop    r14                        nt"
                    "pop    r13                        nt"
                    "pop    r12                        nt"
                    "pop    r11                        nt"
                    "pop    r10                        nt"
                    "pop    r9                         nt"
                    "pop    r8                         nt"
                    "pop    r7                         nt"
                    "pop    r6                         nt"
                    "pop    r5                         nt"
                    "pop    r4                         nt"
                    "pop    r3                         nt"
                    "pop    r2                         nt"
                    "pop    r1                         nt"
                    "pop    r0                         nt"
                    "out    __SREG__, r0               nt"
                    "pop    r0                         nt"
                );
/*
 * UIKInitialize sets up the timer, and adds the idle task to tcb
 */

void UIKInitialize(uint16_t ticklen) {
  int i;

  // setup timer - timer1 is used so we can use 16 bits
  TCCR1B |= (1 << WGM12);
  //enable CTC interrupt
  TIMSK |= (1 << OCIE1A);
  OCR1A = ticklen;

  //not default:timer runs with /64 resolution
  //TCCR1B |= ((1 << CS10) | (1 << CS11));
  //default: timer1 runs at 1MHz on the atmega16 so ticklen can be specified in microsecs
  TCCR1B |= (1 << CS10);

  // add the idle task as the task with lowest priority */
  UIKADDTask(&idletask, 255, idletask_stack, 64);

  //initialize the ASS_BLOCK so that there are no events associated
  //with anything - note interupts are not enabled yet
  for(i=0; i max_numtasks) {
    //error, tcb is not big enough, need to increase max_numtasks in uik.h
    return (-1);
  }
  //setup parameters
  //"initialized" could be changed to ready without much effect, but UIKRun would
  //not have to be called
  tcb[numtasks].state = initialized;
  tcb[numtasks].priority = priority;
  tcb[numtasks].delay = 0;

  // place a few known bytes on the bottom - useful for debugging
  // tcb[numtasks].stack_lptr = stack_ptr;
  tcb[numtasks].stack_ptr = stack_ptr - 1;

  // will be location of most significant part of stack address
  *stack_ptr = 0x11;
  stack_ptr--;
  // least significant byte of stack address
  *stack_ptr = 0x22;
  stack_ptr--;
  *stack_ptr = 0x33;
  stack_ptr--;

  // address of the executing function
  this_address = task_ptr;
  *stack_ptr   = this_address & 0x00ff;
  stack_ptr--;
  this_address >>= 8;
  *stack_ptr = this_address & 0x00ff;
  stack_ptr--;

  //simulate stack after a call to savecontext
  *stack_ptr = 0x00;  //necessary for reti to line up
  stack_ptr--;
  *stack_ptr = 0x00;  //r0
  stack_ptr--;
  *stack_ptr = 0x00;  //r1 wants to always be 0
  stack_ptr--;
  *stack_ptr = 0x02;  //r2
  stack_ptr--;
  *stack_ptr = 0x03;  //r3
  stack_ptr--;
  *stack_ptr = 0x04;  //r4
  stack_ptr--;
  *stack_ptr = 0x05;  //r5
  stack_ptr--;
  *stack_ptr = 0x06;  //r6
  stack_ptr--;
  *stack_ptr = 0x07;  //r7
  stack_ptr--;
  *stack_ptr = 0x08;  //r8
  stack_ptr--;
  *stack_ptr = 0x09;  //r9
  stack_ptr--;
  *stack_ptr = 0x10;  //r10
  stack_ptr--;
  *stack_ptr = 0x11;  //r11
  stack_ptr--;
  *stack_ptr = 0x12;  //r12
  stack_ptr--;
  *stack_ptr = 0x13;  //r13
  stack_ptr--;
  *stack_ptr = 0x14;  //r14
  stack_ptr--;
  *stack_ptr = 0x15;  //r15
  stack_ptr--;
  *stack_ptr = 0x16;  //r16
  stack_ptr--;
  *stack_ptr = 0x17;  //r17
  stack_ptr--;
  *stack_ptr = 0x18;  //r18
  stack_ptr--;
  *stack_ptr = 0x19;  //r19
  stack_ptr--;
  *stack_ptr = 0x20;  //r20
  stack_ptr--;
  *stack_ptr = 0x21;  //r21
  stack_ptr--;
  *stack_ptr = 0x22;  //r22
  stack_ptr--;
  *stack_ptr = 0x23;  //r23
  stack_ptr--;
  *stack_ptr = 0x24;  //r24
  stack_ptr--;
  *stack_ptr = 0x25;  //r25
  stack_ptr--;
  *stack_ptr = 0x26;  //r26
  stack_ptr--;
  *stack_ptr = 0x27;  //r27
  stack_ptr--;
  *stack_ptr = 0x28;  //r28
  stack_ptr--;
  *stack_ptr = 0x29;  //r29
  stack_ptr--;
  *stack_ptr = 0x30;  //r30
  stack_ptr--;
  *stack_ptr = 0x31;  //r31
  stack_ptr--;

  //store the address of the stack
  this_address = stack_ptr;
  *(tcb[numtasks].stack_ptr) = (this_address & 0xff);
  this_address >>= 8;
  *(tcb[numtasks].stack_ptr + 1) = (this_address & 0xff);

  numtasks++;

  //return the task id
  return(numtasks - 1);
}

/*
 * UIKRun is sort of unnecessary, but since it's in the specification API we will
 * force the call here, inventing a distinction between the "initialized" and
 * "ready" states.
 */

void UIKRun(uint8_t taskid) {
  //the scheduler will handle if/when it actually runs based on priority
  tcb[taskid].state = ready;
}

/*this is the scheduler - handled in an interrupt (other interupts are disabled) */
void SIG_OUTPUT_COMPARE1A(void) {
  //save the execution context - this disables interupts
  savecontext();
  tick();
  restorecontext();
  //reti re-enables interrupts
  asm volatile ("reti");
}

/*
 * tick increments through tcb and does 2 tasks: it increments the delays and
 * finds the maximum ready task. One constraint is the tick should be long enough
 * that this has a chance to complete.
 */

void tick(void) {
  int i;
  //start with the high being the idle task, which is always ready
  //and has lowest priority
  int high = 0;

  for(i=0; i 0) {
	  tcb[i].delay--;
	  if(tcb[i].delay == 0) {
	    //the state should have been waiting
	    tcb[i].state = ready;
	  }
    }
    //if an event needs to be dealt with by marking the task as ready
	//and updating "Event"
	if(EVENT_FLAG != 0) {
	  //if tcb[i] in ASS_BLOCK, mark tcb[i] as ready
      //if xxxxxxx1
	  if((EVENT_FLAG & (unsigned char)0x01) != 0) {
        tcb[ASS_BLOCK[0]].state = ready;
		//reset the bit to 0
		EVENT_FLAG &= 0xFE;
      }
	  /*TODO, additional comparisons as events are added
	    see UIKAssocEvent */
	}

	//find highest priority task that's ready
    if(tcb[i].state == ready && tcb[i].priority < tcb[high].priority) {
	  high = i;
	}
  }
  //EVENT_FLAGS should all be dealt with now and their processes marked as ready
  curr_task = high;
  currTCB = tcb[high].stack_ptr;
}

/*
 * ticknodelay is the tick without decrementing delays
 * this is kind of a band-aid
 */

void ticknodelay(void) {
  int i;
  //start with the high being the idle task, which is always ready
  //and has lowest priority
  int high = 0;

  for(i=0; i<numtasks; i++) {
    //if an event needs to be dealt with by marking the task as ready
	//and updating "Event"
	if(EVENT_FLAG != 0) {
	  //if tcb[i] in ASS_BLOCK, mark tcb[i] as ready
      //if xxxxxxx1
	  if((EVENT_FLAG & (unsigned char)0x01) != 0) {
        tcb[ASS_BLOCK[0]].state = ready;
		//reset the bit to 0
		EVENT_FLAG &= 0xFE;
      }
	  /*TODO, additional comparisons as events are added
	    see UIKAssocEvent */
	}

	//find highest priority task that's ready
    if(tcb[i].state == ready && tcb[i].priority < tcb[high].priority) {
	  high = i;
	}
  }
  //EVENT_FLAGS should all be dealt with now and their processes marked as ready
  curr_task = high;
  currTCB = tcb[high].stack_ptr;
}

/*
 * startrtos sets up the idletask, enables interrupts, and begins the idletask executing
 * it will be interupted if any other tasks exist on the first tick
 */

void startrtos(void) {
  //start with the idle process
  tcb[0].state = ready;
  currTCB = tcb[0].stack_ptr;

  //this is the idle process, it will be swapped out soon enough
  restorecontext();
  //enable interupts with the return
  asm volatile ("reti");
}

/*
 * This is the interupt that is in charge of scheduling
 */

void UIKDelay(uint32_t ticks) {
  //save the execution context - this disables interupts
  savecontext();
  //if a -1 is passed it implies no time passes tick-wise
  if (ticks value = 1;
  return thissem;
}

/*
 * This is for UIKSemPend logic
 */

unsigned char semtest(UIKSem* sem) {
  if(sem->value > 0) {
    //critical secion - must retest
	cli();
	if(sem->value > 0) {
	  sem->value--;
	  sei(); //end critical section
	  return 1;
	}
  }
  return 0;
}

/*
 * This is the semaphore acquire
 */

void UIKSemPend(UIKSem* sem) {
  //wait until sem > 0
  while (semtest(sem) == 0) {
    //delay current task, which is the one trying to access semaphore
	//implies highest priority task gets semaphore first
	UIKDelay(1);
  }
}

/*
 * This is the semaphore release
 */

void UIKSemPost(UIKSem* sem) {
  //critical section
  cli();
  sem->value++;
  sei(); //end critical section
  //theoretically shouldn't have to do anything
  //but just as to not have to wait for the next tick
  UIKDelay(0);
}

/*
 * This returns the current value of a semaphore
 */

uint8_t UIKSemValue(UIKSem* sem) {
  return sem->value;
}

/*
 * UIKAssocEvent associates an external event with the process that calls it
 * the events are defined in uik.h
 */

void UIKAssocEvent(uint8_t Event) {
  //one function can be associated with multiple events
  //but an event should only be associated with one function at a time
  //error checking does not exist to save space, but be wary

  //if you would like to error check, just verify if(ASS_BLOCK[i] != 255) for every bit
  //before setting it. also you would need to reenable interupts before returning

  if(Event == 0) {
    //succesfuly give them the nothing they ask for
	return;
  }
  //ASS_BLOCK and curr_task must be protected
  //unless we want a semaphore for curr_task, the best way seems to just be to disable
  //interupts
  cli();
  //set the state for waiting, since it's waiting for the event to occur
  tcb[curr_task].state = wait;

  //if xxxxxxx1
  if((Event & 0x01) != 0) {
    ASS_BLOCK[0] = curr_task;
  }
  //if xxxxxx1x
  if((Event & 0x02) != 0) {
    ASS_BLOCK[1] = curr_task;
  }
  //if xxxxx1xx
  if((Event & 0x04) != 0) {
    ASS_BLOCK[2] = curr_task;
  }
  //if xxxx1xxx
  if((Event & 0x08) != 0) {
    ASS_BLOCK[3] = curr_task;
  }
  //if xxx1xxxx
  if((Event & 0x10) != 0) {
    ASS_BLOCK[4] = curr_task;
  }
  //if xx1xxxxx
  if((Event & 0x20) != 0) {
    ASS_BLOCK[5] = curr_task;
  }
  //if x1xxxxxx
  if((Event & 0x40) != 0) {
    ASS_BLOCK[6] = curr_task;
  }
  //if 1xxxxxxx
  if((Event & 0x80) != 0) {
    ASS_BLOCK[7] = curr_task;
  }
  sei();
  //give up execution - a -1 allows the other processes not to tick
  UIKDelay(-1);
}

/*
 * UIKDissasocEvent disassociates a task with an external event
 */

void UIKDisassocEvent(uint8_t Event) {
  //a semaphore could easily be used in place of disabling interupts
  cli();
    //if xxxxxxx1
  if((Event & 0x01) != 0) {
    ASS_BLOCK[0] = 255;
  }
  //if xxxxxx1x
  if((Event & 0x02) != 0) {
    ASS_BLOCK[1] = 255;
  }
  //if xxxxx1xx
  if((Event & 0x04) != 0) {
    ASS_BLOCK[2] = 255;
  }
  //if xxxx1xxx
  if((Event & 0x08) != 0) {
    ASS_BLOCK[3] = 255;
  }
  //if xxx1xxxx
  if((Event & 0x10) != 0) {
    ASS_BLOCK[4] = 255;
  }
  //if xx1xxxxx
  if((Event & 0x20) != 0) {
    ASS_BLOCK[5] = 255;
  }
  //if x1xxxxxx
  if((Event & 0x40) != 0) {
    ASS_BLOCK[6] = 255;
  }
  //if 1xxxxxxx
  if((Event & 0x80) != 0) {
    ASS_BLOCK[7] = 255;
  }
  sei();
}

/*
 * timer0 overflowing is a hardware event that our event functionality handles
 */

ISR(TIMER0_OVF_vect) {
  //interupts are disabled since this is an interrupt so locking event_flag not necessary
  //the 0th bit is associated with timer0
  EVENT_FLAG |= (1 << PORT0_OVERFLOW);
}

Installation/Compilation

Due to the complex nature of this program, it was compiled with AVR Studio on Windows XP. Although I certainly miss my usual development tools on Linux, the AVR studio debugger was much better in my opinion.

To compile, open project6.aps, select Build->Rebuild All. To copy to the Atmega16, click on the “AVR” sprite. Under Program ->Flash, click the “Program” button and select the homework4_*.hex file.

Of course, everything should work under linux as well.

2 Responses to Finished RTOS similar to FreeRTOS

  1. mopey says:

    This reminds me of that time I made a compiler (https://webstersprodigy.net/2007/06/25/ramel/) for a class. Totally useless, like this, but I guess I can say that I have build both a functional Operating System and a compiler now.

  2. mopey says:

    Also, the STK500 I was using for this, I had to give back – so development is done for now. But hey, hopefully this is still useful to someone.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s