/* RLPOS: RLPotter Operating System Version 0.5.2 for the 68HC12D60A microcontroller by Ryan Potter ryan@rlpotter.com v0.6 April 24, 2003: 11042 bytes - made the task block and the task control block dynamic, so that now tasks can create tasks, and can also start/stop them - implemented the sysInit() task for kicking off a user system - implemented a kernel debug command for tracing code execution - implemented small msg box system in kernel for tasks - implemented COP watchdog reset timer - added system resources to semlib - added skeleton ISR_handler() code for the other interrupts - implemented _HC12Setup.c to initialize/harden the system v0.5 April 16, 2003: - added priority preemption - finished kernel task state switcher - it is now officially a legitimate rate monotonic priority preemptive multitasking Real Time Operating System :) - made kernel.c and semlib.c consistent with the rest of the kernel - included the early framework for a msg box system v0.4 April 13, 2003: 8096 bytes - gerneralized the shell command-line input parser: cmd (up to 32 chars) - added kernel and shell functions v0.3 April 12, 2003: 6155 bytes - added a beginning shell user interface. - added a resource control block and basic semaphore functions. - added basic kernel functions v0.2 April 9, 2003: - able to round-robin with RTI interrupt. - bonified/certified multitasking with 3 tasks. :) v0.1 April 3, 2003: - able to round-robin without interrupts. - not multitasking, really. v0.0 started April 1, 2003; 0 bytes - no idea where to start. - don't want to look at anyone else's work. ;0) Architecture/C assumptions: 1) 'D' register is the accumulator 2) 'X' register points to the top of the current stack 3) 'Y' register is for general use in indexed operations 4) the heap grows upward and mem segments allocated by malloc, realloc, and calloc are linear and contiguous. 5) the stack grows downwards, and there is no boundary checking. Potential problem areas: 1) 'running' section of the kernel get's compiled using extra push and pop instructions 2) run out of ram (global + stack + heap) */ #include <912d60.h> #include #include #include #include "kernel.h" // FUNCTION PROTOTYPES void RTI_handler(void); void sysInit(void); void shell(void); void sysTime(void); void (*task_ptr[MAXTASKS])(void); // GLOBAL VARIABLES unsigned char *main_frame_ptr = NULL; // bottom of main() frame unsigned char *main_frame_x_ptr = NULL; // top of main() frame (x-reg ptr) unsigned char *temp_task_frame_ptr = NULL; // temp CCR pointer for RTI extern unsigned int current; // current task id number extern unsigned long int system_tick; extern unsigned int cop_cycle; // task control block extern struct task_block { void (*address)(); // Address of the task unsigned char id; // ID of task char name[9]; // Name enum task_state state; // State unsigned char priority; // Priority unsigned long int period_tick; // for determining if deadline is up unsigned int interrupt_msg_box; // flags for pending interrupts enum message_box message; // misc flags unsigned char message_data[2]; // data for misc_msg_box flags unsigned char *heap_ptr; // heap addr while not current task unsigned int heap_size; // heap size unsigned char *frame_ptr; // CCR pointer }; // resource control block extern struct resource_block { unsigned char id; // ID of resource char name[5]; // Name of resource enum resource_state state; // State (busy, free...) unsigned char owner; // Current resource owner signed char queue[3]; // Tasks waiting on resource unsigned char queue_ptr; // Next free spot in queue }; extern struct task_block *task[MAXTASKS]; extern struct resource_block resource[NUMRESOURCES]; extern char error_msg[9][25]; extern char error_src[5][18]; extern char kdb_trace; main() { // LOCAL VARIABLES int result, id; unsigned int i, addr, temp_heap_size; unsigned char *temp_heap_ptr, temp, priority_check; unsigned int deadline; extern int _bss_end, _textmode; _textmode = 1; // maps '\n' to "CR/LF" for Windows terminals current = 0; // start the shell first cop_cycle = 0; // fresh watchdog // initialize the task pointer array and the tcb array. for (i=0; imessage && STATE_FLAG) { // set task state to what the message says task[i]->state = task[i]->message_data[STATE_BOX]; // clear the STATE_FLAG task[i]->message &= ~(STATE_FLAG); #ifdef KDB_TRACE_LEVEL_2 if (kdb_trace) puts("kDB2.3-2a"); #endif } #ifdef KDB_TRACE_LEVEL_2 if (kdb_trace) puts("kDB2.3-2b"); #endif } #ifdef KDB_TRACE_LEVEL_1 if (kdb_trace) puts("kDB2.3-1"); #endif // RT PRIORITY BLOCK: // set the current task id based on priority and deadline // determine if the deadline is up for idle tasks /* deadline, is equal to period plus an initial time (t0) reference (t0 = system_tick). Period = priority + 1. Changing the state from idle to PENDING occurs here. */ for (i=0; istate == IDLE) { deadline = task[i]->period_tick + (task[i]->priority + 1); if (system_tick >= deadline) { task[i]->state = PENDING; // change state at deadline #ifdef KDB_TRACE_LEVEL_2 if (kdb_trace) puts("kDB2.4-2"); #endif } } // if (task[i]->message) {} ??? } #ifdef KDB_TRACE_LEVEL_1 if (kdb_trace) puts("kDB2.4-1"); #endif // set current = to highest priority pending/running task. priority_check = 255; // lowest possible for (i=0; istate == PENDING) || (task[i]->state == RUNNING)) { if (task[i]->priority <= priority_check) { current = i; priority_check = task[i]->priority; #ifdef KDB_TRACE_LEVEL_2 if (kdb_trace) puts("kDB2.5-2"); #endif } } } } #ifdef KDB_TRACE_LEVEL_1 if (kdb_trace) puts("kDB2.5-1"); #endif // DISPATCH, or otherwise deal with the current task // KDB_TRACE Section 3 #ifdef KDB_TRACE_LEVEL_2 if (kdb_trace) { printf("task[%d]->state = %d\n", current, task[current]->state); printf("task[%d]->prior = %d\n", current, task[current]->priority); } #endif switch (task[current]->state) { case IDLE: // skip task #ifdef KDB_TRACE_LEVEL_1 if (kdb_trace) puts("kDB3.1-1"); #endif break; case PENDING: // ready and waiting to run task[current]->state = RUNNING; task[current]->period_tick = system_tick; #ifdef KDB_TRACE_LEVEL_1 if (kdb_trace) puts("kDB3.2-1"); #endif #ifdef KDB_TRACE_LEVEL_2 if (kdb_trace) { //puts("kDB3.2-2"); putchar('S'); putchar(current+48); putchar('\n'); } #endif (*task_ptr[current])(); // start the task task[current]->state = IDLE; // task finished #ifdef KDB_TRACE_LEVEL_1 if (kdb_trace) puts("kDB3.3-1"); #endif #ifdef KDB_TRACE_LEVEL_2 if (kdb_trace) { //puts("kDB3.3-2"); putchar('S'); putchar(current+48); putchar('\n'); } #endif break; case RUNNING: // interrupted. continue running. // restore the task heap to the stack #ifdef KDB_TRACE_LEVEL_1 if (kdb_trace) puts("kDB3.4-1"); #endif #ifdef KDB_TRACE_LEVEL_2 if (kdb_trace) { //puts("kDB3.4-2"); putchar('R'); putchar(current+48); putchar('\n'); } #endif temp_heap_size = task[current]->heap_size; temp_heap_ptr = task[current]->heap_ptr; for (i=0; iframe_ptr; asm("LDS _temp_task_frame_ptr"); asm("RTI"); break; case WAITING: // waiting on a resource #ifdef KDB_TRACE_LEVEL_1 if (kdb_trace) puts("kDB3.5-1"); #endif break; case STOPPED: // done running until later #ifdef KDB_TRACE_LEVEL_1 if (kdb_trace) puts("kDB3.6-1"); #endif break; default: // shouldn't happen, but, error if so. puts("KERNEL: task state error\n"); exit(1); } // end switch } // end while(1) return 0; } // end main() #pragma interrupt_handler RTI_handler() void RTI_handler(void) { size_t frame_size; unsigned int i; unsigned char *local_thp; //ACKNOWLEDGE THE INTERRUPT INTR_OFF(); // redundant. automatically done by the processor RTIFLG = 0x0080; // acknowledge/clear the interrupt #ifdef KDB_TRACE_LEVEL_1 if (kdb_trace) putchar('.'); #endif // SET THE FRAME POINTER for the interupted task. // should point to the CCR entry on the stack. asm("TFR x,d"); // start of RTI stack asm("ADDD #2"); // adjust to CCR stack entry asm("STD _temp_task_frame_ptr"); // put into task_frame_ptr task[current]->frame_ptr = temp_task_frame_ptr; //PLACE CURRENT TASK CONTEXT ONTO THE HEAP // determine size of heap segment needed frame_size = main_frame_ptr - temp_task_frame_ptr; // always positive task[current]->heap_size = (int)frame_size; //printf("frame size: %d\n", frame_size); // reallocate heap memory //printf("heap_ptr = %d\n", task[current]->heap_ptr); task[current]->heap_ptr = realloc(task[current]->heap_ptr, frame_size); local_thp = task[current]->heap_ptr; if (local_thp == NULL) { #ifdef KERNEL_ERROR_MSGS //printf("local_thp: %d\n", local_thp); //printf("heap: %d bytes\n", get_free_memory()); puts("out of heap space!"); #endif exit(0); } // transfer task frame to the heap one byte at a time. // assumes the heap grows from low to high addr. for(i=0; i