/*! * システムコールの処理(exd_tsk():自タスクの終了と排除) * TCBが排除されるので返却値はなしにする */ void exd_tsk_isr(void) { TCB *tmpcurrent; /* * カーネルオブジェクトを取得しているかチェック. * release_objectの後のコールスタックでg_currentが書き換えられる場合があるので, * 呼び出したタスク(自タスク)を一時退避させておく */ if (g_current->get_info.flags != TASK_NOT_GET_OBJECT) { tmpcurrent = g_current; g_current = tmpcurrent; } /* 本来ならスタックも解放して再利用できるようにすべきだが省略. */ KERNEL_OUTMSG(g_current->init.name); KERNEL_OUTMSG(" EXIT.\n"); memset(g_current, -1, sizeof(*g_current)); /* ノードの初期化 */ /* タスクalocリストから抜き取りfreeリストへ挿入 */ get_aloclist(g_current); /* alocリストから抜き取り */ /* freeリスト先頭へ挿入 */ g_current->free_next = g_tsk_info.freehead; g_current->free_prev = g_tsk_info.freehead->free_prev; /* NULLが入る */ g_tsk_info.freehead = g_current->free_next->free_prev = g_current->free_prev->free_next = g_current; g_tsk_info.id_table[g_current->init.tskid] = NULL; /* ID変換テーブルのクリア */ }
/*! * システムコールの処理(ext_tsk():自タスクの終了) * タスクの状態としては実行状態から休止状態へ移行 * 自タスクの終了は資源を取得しているものは解放する処理をする(ただし,取得したオブジェクト1つの時のみ) * リターンパラメータはあってはならない */ void ext_tsk_isr(void) { TCB *tmpcurrent; /* * カーネルオブジェクトを取得しているかチェック. * release_objectの後のコールスタックでg_currentが書き換えられる場合があるので, * 呼び出したタスク(自タスク)を一時退避させておく */ if (g_current->get_info.flags != TASK_NOT_GET_OBJECT) { tmpcurrent = g_current; g_current = tmpcurrent; } /* システムコール発行時タスクはカレントタスクから抜かれてくるのでstateは0になっている */ g_current->state |= TASK_DORMANT; /* タスクを休止状態へ */ KERNEL_OUTMSG(g_current->init.name); KERNEL_OUTMSG(" DORMANT.\n"); g_current->priority = g_current->init.priority; /* タスクの優先度を起動状態へ戻す */ tsk_stack_init(g_current); /* ユーザスタックを起動時に戻す */ }
/*! * システムコール処理(ter_tsk():タスクの強制終了) * 自タスク以外のタスクを強制終了する(実行可能状態と待ち状態にあるもの) * 実行可能状態にあるものはレディーキューから抜き取る処理, * 待ち状態にあるものは待ち要件を調べ該当の待ちキューから抜き取る * 強制終了なので,待ち状態に入っているシステムコールの返却値は書き換えない * tskid : 強制終了するタスクコントロールブロックへのポインタ * (返却値)E_ILUSE : エラー終了(タスクが実行状態.つまり自タスク) * (返却値)E_OBJ : エラー終了(タスクが休止状態) * (返却値)E_OK : 正常終了(タスクが実行可能状態または待ち状態) */ ER ter_tsk_isr(TCB *tcb) { /* 実行状態 */ if (g_current == tcb) { DEBUG_LEVEL1_OUTMSG(" not temination activ : ter_tsk_isr().\n"); return E_ILUSE; } /* 休止状態の場合 */ else if (tcb->state == TASK_DORMANT) { DEBUG_LEVEL1_OUTMSG(" not termination dormant : ter_tsk_isr().\n"); return E_OBJ; } /* ここから強制終了処理 */ else { /* 実行可能状態 */ if (tcb->state == TASK_READY) { get_tsk_readyque(tcb); /* レディーキューから抜き取る(呼び出した後はg_currentに設定されている) */ } /* 待ち状態(何らかの待ち行列につながれている時は対象タスクを待ち行列からはずす) */ else { /* タイマブロックを持っているものは対象タイマブロックを排除する */ if (tcb->wait_info.tobjp != 0) { tcb->wait_info.tobjp = 0; /* クリアにしておく */ } } tcb->state |= TASK_DORMANT; /* タスクを休止状態へ */ KERNEL_OUTMSG(tcb->init.name); KERNEL_OUTMSG(" DORMANT.\n"); tcb->priority = tcb->init.priority; /* タスクの優先度を起動状態へ戻す */ tsk_stack_init(tcb); /* ユーザスタックを起動時に戻す */ return E_OK; } }
/*! * @brief OSメイン関数 * @attention CPSRの外部割込み無効モードとして起動 * @param[in] なし * @param[out] なし * @return 終了値 * @retval 0:OS実効終了 * @note 正常retvalは返却されない */ int main(void) { unsigned long *p; /* BSSセクションの初期化(BSSセクションの初期化はここでOK) */ for (p = &_bss_start; p < &_end; p++) { *p = 0; } uart3_init(); /* シリアルの初期化 */ KERNEL_OUTMSG("kernel boot OK!\n"); /* OSの動作開始 */ kernel_init(start_threads, "init tsk", 0, 0x100, 0, NULL); /* initタスク起動 */ /* 正常ならばここには戻ってこない */ return 0; }
/*! * タスクのスタック領域の確保 * -タスクスタックはheapメモリを使用しない(スタックトレースをしやすくするため) * -リンカスクリプトで定義しているタスクスタック領域は,再利用を考えて,リスト管理をした方が良い * *tcb : スタック領域を確保するTCB * stacksize : 確保するスタックサイズ * (返却値)E_NOMEM : スタック領域が確保できない * (返却値)E_OK : スタック領域が確保完了 */ static ER get_tsk_stack(TCB *tcb, int stacksize) { extern char _tskstack; /* リンカスクリプトで定義されるスタック領域 */ static char *p_stack = &_tskstack; tcb->stacksize = stacksize; /* タスクスタック領域を獲得 */ memset(p_stack, 0, stacksize); p_stack += stacksize; /* タスクスタック領域を切り出す */ /* タスクスタック空間検査 */ if (p_stack > (char *)(0x90000000 + 0x00002000)) { KERNEL_OUTMSG("task stack over flow.\n"); return E_NOMEM; } tcb->stack = p_stack; /* タスクスタックの上方アドレスの設定 */ return E_OK; /* ローカルstatic変数は返却できないので,エラーコードを返却 */ }
/*! * 動的優先度を調節する関数(優先度ごとの2分木) * 優先度枯渇状態の場合は整列リストの先頭から動的優先度を調節する */ static void adjust_priority_pribtree(void) { TCB *tmp; int priority; TCB **s_prev = ¤t->ready_info.un.btree_ready.sort_prev; BTREERCB *p_rdy = &mg_ready_info.entry->un.pri_btree.ready[current->priority]; /* 動的優先度枯渇状態の場合 */ if (check_priority_exhaustion(p_rdy->min_priority, p_rdy->tsk_num)) { tmp = mg_ready_info.entry->un.pri_btree.ready[current->priority].slist_head; priority = 1; } /* 動的優先度枯渇状態でない場合 */ else { tmp = current; /* * 整列リストの先頭以外に挿入した時(挿入した箇所から, * 整列リストの後のTCBの動的優先度を調整するために,挿入したTCBの動的優先度を決定) */ if (*s_prev != NULL) { priority = (*s_prev)->ready_info.un.btree_ready.dynamic_prio + 1; } /* 整列リスト先頭に挿入した時(整列リストのすべてのTCBの優先度を調整するために,優先度を1へ設定している) */ else { priority = 1; } } /* 整列リストを進めて,動的優先度を調整 */ for (; tmp != NULL; tmp = tmp->ready_info.un.btree_ready.sort_next, priority++) { if (PRIORITY_NUM <= priority) { KERNEL_OUTMSG("priority starvation"); /* kernel sleep */ down_system(); } tmp->ready_info.un.btree_ready.dynamic_prio = priority; } mg_ready_info.entry->un.pri_btree.ready[current->priority].min_priority = priority; /* 最低動的優先度を記録 */ }
/*! * Least Laxity Firstスケジューリングミスハンドラ * タイマのセットはEDFと同様. * 余裕時刻をオーバーすれば,デッドラインもミスするので(domino effectする事もある), * ハンドラを用意した. * *argv : つねにNULL(タイマ割込みが発生したならば,現在実行中のタスクがcurrentに設定してあるため) */ void llfschedule_miss_handler(void *argv) { KERNEL_OUTMSG(" Least Laxity First Float time Miss task set\n"); KERNEL_OUTMSG(" OS freeze... Please push reset button\n"); freeze_kernel(); }
/*! * Earliest Deadline Firstスケジューリングデッドラインミスハンドラ * EDFの場合デッドライン時刻でタイマをかけて,タイムアウトした場合はこのハンドラでOSをスリープさせる * (一つのタスクがデッドラインをミスした時点でスリープ.domino effectを防ぐ) * 一応タイマコールバックルーチンとなるので,scheduler.cではなくtimer_callrte.cに配置した * *argv : つねにNULL(タイマ割込みが発生したならば,現在実行中のタスクがcurrentに設定してあるため) */ void edfschedule_miss_handler(void *argv) { KERNEL_OUTMSG(" Earliest Deadline First Deadline Miss task set\n"); KERNEL_OUTMSG(" OS freeze... Please push reset button\n"); freeze_kernel(); }