ColdFire V1でコンテキストスイッチ [ColdFire (ColdeFire) V1]
ColdFire V1を使って、コンテキストスイッチを行うプログラムを作成してみました。モデルは、OS-9/68000です。
//************************************************************** // $Id: main.c,v 1.3 2008/01/10 15:04:18 noritan Exp $ // // Multi tasking Practice //************************************************************** #include <hidef.h> /* for EnableInterrupts macro */ #include "derivative.h" /* include peripheral declarations */
定数の定義です。タスクの数、スタックのサイズ、ステートマシンの状態コードを定義しています。スタックサイズは、4Byte(Long Word)単位の数です。
#define N_TASK (10) #define N_SSTACK (30) #define N_USTACK (30) #define ST_NULL (0) #define ST_SLEEP (1) #define ST_ACTIVE (2) #define ST_RUN (3)
TaskDescriptor は、OS-9では、Process Descriptor と呼ばれていました。
他のオペレーティング・システムでは、 Task Control Block と呼ばれることもあります。
OS-9/68000 の Process Descriptor は、2KByteもありましたが、ここでは必要最小限としてあります。
count は、状況によって使い分ける万能カウンタです。
struct TaskDescriptor { const struct ModuleDescriptor *module; dword *workarea; dword *ssp; dword *usp; dword status; dword count; dword sStack[N_SSTACK]; };
ModuleDescriptor は、プログラム単位を定義しています。
OS-9/68000 では、ModuleDescriptor に指定された大きさの作業領域などを動的に割り当てることが出来るのですが、今回は、初期化と実行部のエントリだけ定義しています。
struct ModuleDescriptor { void (*init)( struct TaskDescriptor *task, int argc, char **argv ); void (*proc)(void); }; typedef struct TaskDescriptor TaskDescriptor; typedef struct ModuleDescriptor ModuleDescriptor;
TaskDescriptor は、同時実行可能なタスクの数だけ必要です。
thisTaskは、現在実行中のタスクを指します。
TaskDescriptor taskTable[N_TASK]; TaskDescriptor *thisTask = 0;
各タスクに必要な作業領域は、初期化ルーチンで確保します。
ユーザモードでのスタックは、ここに確保します。
作業領域は、割り当てるけど開放しないいいかげん方式です。
typedef struct WorkArea { dword toggle; dword period; dword uStack[N_USTACK]; } WorkArea; dword workareaUsed = 0; WorkArea workareaTable[N_TASK];
ModuleDescriptor を元に新しいタスクを作成し、実行中タスクリストに登録します。
もっとも、現在の実装では、リストを作成しているわけではなく、
タスクの状態を "ACTIVE" にするだけで、
あとはスケジューラが解決してくれています。
//---------------------------------------------- // Create a TASK with a MODULE and PARAMETER // and add to ACTIVE list. //---------------------------------------------- TaskDescriptor *tsk_start( const ModuleDescriptor * module, int argc, char * * argv ) { int i; struct TaskDescriptor *task; for (i = 0, task = &taskTable[0]; i < N_TASK; i++, task++) { if (task->status == ST_NULL) { task->module = module; task->ssp = &(task->sStack[N_SSTACK]); task->status = ST_ACTIVE; task->count = 0; (task->module->init)(task, argc, argv); return task; } } return 0; }「何もしないプログラム」の初期化部分です。 作業領域を確保し、スタックフレームを構築しています。 スタックフレームは、例外が発生した時のイメージを再現しています。
void idle_task_init( TaskDescriptor * task, int argc, char **argv ) { #pragma unused (argc, argv) WorkArea *workarea = &workareaTable[workareaUsed++]; register dword *usp = &(workarea->uStack[N_USTACK]); register dword *ssp; dword *_A5; dword i; void idle_task_proc(); void tsk_exit(void); #pragma warn_any_ptr_int_conv off // Establish DUMMY user stack *(--usp) = (word)tsk_exit; // Establish a stack frame ssp = task->ssp; *(--ssp) = (dword)idle_task_proc; // PC *(--ssp) = 0x40000000; // Vector & SR *(--ssp) = (dword)usp; // A6 __asm { move a5,_A5 } *(--ssp) = (dword)_A5; // copy of current A5 for (i = 0; i < 13; i++) { *(--ssp) = 0; // D0-D7/A0-A4 } #pragma warn_any_ptr_int_conv reset task->workarea = (dword *)workarea; task->usp = usp; task->ssp = ssp; }「何もしないプログラム」の実行部です。 犬にえさを与え続けます。
void idle_task_proc(void) { for (;;) { // Do nothing __RESET_WATCHDOG(); /* feeds the dog */ } }「何もしないプログラム」モジュールを定義します。 モジュールは、ROMに配置されます。
const ModuleDescriptor idle_module = { idle_task_init, idle_task_proc };メインルーチンです。 マイコンの初期化を行い、 「何もしないプログラム」モジュールを使って二つの「何もしないタスク」を作成し、 "trap #0" でコンテキストを切り替えます。
void main(void) { TaskDescriptor *idle_task; // Configure one-time-write SOPT1 SOPT1 = 0 | SOPT1_WAITE_MASK // Enable WAIT instruction | SOPT1_RSTOPE_MASK // Enable RSTO* port | SOPT1_BKGDPE_MASK // Enable BKGD port | SOPT1_RSTPE_MASK ; // Enable RESET* port // Configure RTC module RTCSC_RTCLKS = 0; // 1kHz OSC RTCSC_RTCPS = 11; // 10msec RTCMOD = 50-1; // 50counts (500msec) RTCSC_RTIE = 1; // Enable interrupts. // Invoke two tasks idle_task = tsk_start(&idle_module, 0, 0); idle_task = tsk_start(&idle_module, 0, 0); __asm { // yeild to context switch trap #0 } EnableInterrupts; /* enable interrupts */ for(;;) { __RESET_WATCHDOG(); /* feeds the dog */ } /* loop forever */ /* please make sure that you never leave main */ }ユーザモードの実行部プログラムが終了したら、"tsk_exit" にやってきます。 今回は、使われません。 あれ? "trap #0" が必要なはずだぞ。
void tsk_exit(void) { thisTask->status = ST_NULL; }次に実行すべきタスクを選択します。 OS-9/68000 がそうであるように、 タスクリストの中から、もっとも待ち時間が長い実行状態タスクを選びます。 待ち時間は、 "count" で数えていて、 "schedule" で選択されないと インクリメントされます。
void schedule(void) { int i; struct TaskDescriptor *task; dword maxCount = 0; struct TaskDescriptor *taskSelected = 0; for (i = 0, task = &taskTable[0]; i < N_TASK; i++, task++) { if (task->status == ST_ACTIVE) { task->count++; if (task->count > maxCount) { taskSelected = task; maxCount = task->count; } } } thisTask = taskSelected; if (thisTask) { thisTask->count = 0; } }"trap #0" 例外は、現在実行中のタスクを中断して、 別のタスクに切り替える機能があります。 この部分は、 タスク選択の部分だけは"schedule"関数を呼び出していますが、 スタックの深さが重要になってくるので、 残りはフルにアセンブラで記述しています。
asm interrupt VectorNumber_Vtrap0 trap0_isr(void) { // Save the stack frame == task context lea -60(a7),a7 movem d0-d7/a0-a6,(a7) // Save SSP/USP to the current TaskDescriptor move.l thisTask,a0 move.l a7,struct(TaskDescriptor.ssp)(a0) move.l usp,a1 move.l a1,struct(TaskDescriptor.usp)(a0) jsr schedule // Restore SSP/USP from the selected TaskDescriptor move.l thisTask,a0 move.l struct(TaskDescriptor.usp)(a0),a1 move.l a1,usp move.l struct(TaskDescriptor.ssp)(a0),a7 // Restore the stack frame movem (a7),d0-d7/a0-a6 lea 60(a7),a7 rte }タイマ割り込みのISRです。 基本的に"trap #0"と同じですが、 タイマフラグをクリアするシーケンス"rtc_isr_content"を 呼び出しています。
void rtc_isr_content(void) { RTCSC_RTIF = 1; // Clear the flag. } asm interrupt VectorNumber_Vrtc rtc_isr(void) { // Save the stack frame == task context lea -60(a7),a7 movem d0-d7/a0-a6,(a7) // Save SSP/USP to the current TaskDescriptor move.l thisTask,a0 move.l a7,struct(TaskDescriptor.ssp)(a0) move.l usp,a1 move.l a1,struct(TaskDescriptor.usp)(a0) // Do ISR specific task jsr rtc_isr_content // Do task scheduling jsr schedule // Restore SSP/USP from the selected TaskDescriptor move.l thisTask,a0 move.l struct(TaskDescriptor.usp)(a0),a1 move.l a1,usp move.l struct(TaskDescriptor.ssp)(a0),a7 // Restore the stack frame movem (a7),d0-d7/a0-a6 lea 60(a7),a7 rte } //************************************************************** // $Log: main.c,v $ // Revision 1.3 2008/01/10 15:04:18 noritan // Completed... maybe // //**************************************************************
参考文献
コメント 0