
                    ＝ JSPカーネル 設計メモ ＝

             （Release 1.4対応，最終更新: 15-Mar-2005）

------------------------------------------------------------------------ 
 TOPPERS/JSP Kernel
     Toyohashi Open Platform for Embedded Real-Time Systems/
     Just Standard Profile Kernel

 Copyright (C) 2000-2003 by Embedded and Real-Time Systems Laboratory
                             Toyohashi Univ. of Technology, JAPAN

 上記著作権者は，以下の (1)〜(4) の条件か，Free Software Foundation 
 によって公表されている GNU General Public License の Version 2 に記
 述されている条件を満たす場合に限り，本ソフトウェア（本ソフトウェア
 を改変したものを含む．以下同じ）を使用・複製・改変・再配布（以下，
 利用と呼ぶ）することを無償で許諾する．
 (1) 本ソフトウェアをソースコードの形で利用する場合には，上記の著作
     権表示，この利用条件および下記の無保証規定が，そのままの形でソー
     スコード中に含まれていること．
 (2) 本ソフトウェアを，ライブラリ形式など，他のソフトウェア開発に使
     用できる形で再配布する場合には，再配布に伴うドキュメント（利用
     者マニュアルなど）に，上記の著作権表示，この利用条件および下記
     の無保証規定を掲載すること．
 (3) 本ソフトウェアを，機器に組み込むなど，他のソフトウェア開発に使
     用できない形で再配布する場合には，次のいずれかの条件を満たすこ
     と．
   (a) 再配布に伴うドキュメント（利用者マニュアルなど）に，上記の著
       作権表示，この利用条件および下記の無保証規定を掲載すること．
   (b) 再配布の形態を，別に定める方法によって，TOPPERSプロジェクトに
       報告すること．
 (4) 本ソフトウェアの利用により直接的または間接的に生じるいかなる損
     害からも，上記著作権者およびTOPPERSプロジェクトを免責すること．

 本ソフトウェアは，無保証で提供されているものである．上記著作権者お
 よびTOPPERSプロジェクトは，本ソフトウェアに関して，その適用可能性も
 含めて，いかなる保証も行わない．また，本ソフトウェアの利用により直
 接的または間接的に生じたいかなる損害に関しても，その責任を負わない．

 @(#) $Id: design.txt,v 1.11 2005/08/08 11:20:06 hiro Exp $
------------------------------------------------------------------------


１．JSPカーネルにおける制限事項

(1) サポートする機能

JSPカーネルは，名前の通りスタンダードプロファイルに含まれる機能のみを
サポートする．スタンダードプロファイル外の機能は，原則としてサポートし
ない．

内部構造の設計にあたっては，スタンダードプロファイル外の機能をサポート
するための拡張性は考慮せず，スタンダードプロファイルに含まれる機能を効
率良く実現することを目指す．

(2) サービスコール内での割込みの扱い

JSPカーネルでは，サービスコールはすべて割込み禁止で実行する．スタンダー
ドプロファイルの機能の範囲内では，この方法で十分と考えられるが，拡張機
能を追加する時には設計を見直すべきである．具体的には，非タスクコンテキ
ストから呼び出されたサービスコールを遅延実行する方法が有力である．

(3) CPU例外ハンドラの実行コンテキスト

JSPカーネルでは，CPU例外ハンドラは非タスクコンテキストで実行する．

参考: μITRON4.0仕様では，タスクコンテキストで発生したCPU例外に対する
CPU例外ハンドラを，タスクコンテキストで実行するか非タスクコンテキスト
で実行するかは，実装定義としている．


２．実行コンテキストとシステム状態の仮想化

(1) 実行コンテキスト

実行コンテキストは，タスクコンテキストと非タスクコンテキストのいずれか
に分類される．

実行コンテキストの管理はターゲット依存部で行う．具体的には，タスクコン
テキストと非タスクコンテキストを判別する関数（sense_context）を，ター
ゲット依存部で用意する．

また，タスクコンテキストと非タスクコンテキストの間の遷移も，ターゲット
依存部の責任である．具体的には，割込みハンドラ／CPU例外ハンドラが起動
されると非タスクコンテキストに切り換わり，割込みハンドラ／CPU例外ハン
ドラからリターンすると元の実行コンテキストに戻るよう，ターゲット依存部
を実装する．

(2) CPUロック状態

システムは，CPUロック状態か，CPUロック解除状態のいずれかにある．CPUロッ
ク状態では，すべての割込み（カーネルの管理外のものを除く．以下同じ）が
禁止されている．

CPUロック状態の管理はターゲット依存部で行う．具体的には，CPUロック状態／
CPUロック解除状態のいずれの状態であるかを判別する関数（sense_lock，
t_sense_lock，i_sense_lock）と，CPUロック状態とCPUロック解除状態の間を
遷移させる関数（t_lock_cpu，t_unlock_cpu，i_lock_cpu，i_unlock_cpu）を，
ターゲット依存部で用意する．これらの関数を呼び出す以外の方法で，CPUロッ
ク状態とCPUロック解除状態の間を遷移することはない．


３．ディスパッチとタスク例外処理ルーチン起動の方法

(1) ディスパッチが必要なタイミング

ディスパッチを行う必要があるのは，以下のタイミングである．

(a) サービスコール内での明示的なタスクディスパッチ要求

タスクコンテキストから呼び出されたサービスコール処理において，明示的に
タスクディスパッチが要求された場合．具体的には，実行状態のタスクよりも
優先順位の高く実行できる状態のタスクが生じる場合と，実行状態のタスク
（自タスク）を待ち状態にする場合がある．いずれの場合にも，サービスコー
ル処理内でディスパッチャを明示的に呼び出すことで，ディスパッチを行う．

(b) 割込みハンドラ／CPU例外ハンドラの出口

割込みハンドラ／CPU例外ハンドラ（以下，ハンドラと総称する）から呼び出
されたサービスコールでタスクディスパッチが要求された場合，ハンドラの出
口でタスクディスパッチが必要かチェックし，必要であればタスクディスパッ
チを行う．ハンドラが多重に起動されている場合，最も外側のハンドラの出口
でのみ行う（※）．

※ プロセッサによっては，CPU例外ハンドラの入口処理の先頭では，非タスク
コンテキストであると認識させられない場合がある．このような場合，入口処
理の途中でハンドラが多重に起動されても，内側のハンドラでさらに外側のハ
ンドラがあることを認識できず，内側のハンドラでタスクディスパッチを行う
ことになる．この場合でも，入口処理の非タスクコンテキストであると認識さ
れない部分は，ハンドラの一部ではないと解釈すれば，上記の原則は守られて
いることになる．この注記は，以下の記述にも同様にあてはまる．

(2) タスク例外処理ルーチンの起動が必要なタイミング

タスク例外処理ルーチンを起動するのは，仕様書によると，「タスク例外処理
許可状態」「保留例外要因が 0 でない」「タスクが実行状態である」「非タ
スクコンテキストまたはCPU例外ハンドラが実行されていない」の四つの条件
が揃った場合である．このことから，タスク例外処理ルーチンの起動を行う必
要があるのは，以下のタイミングである．

(a) タスクディスパッチの直後

タスクディスパッチにより，実行状態のタスク（runtsk）が変化する．新しく
実行状態になったタスクが起動条件を満たしていれば，タスク例外処理ルーチ
ンを起動する．

(b) 割込みハンドラ／CPU例外ハンドラの出口

割込みハンドラ／CPU例外ハンドラ（以下，ハンドラと総称する）の出口では，
タスクディスパッチを行う場合がある．その場合には，(a) の理由で必要なら，
タスク例外処理ルーチンを起動する．

ハンドラの出口でタスクディスパッチを行わない場合でも，ハンドラ内で実行
状態のタスクに対してタスク例外処理が要求された場合には，ハンドラの出口
で起動条件をチェックし，起動条件を満たしていればタスク例外処理ルーチン
を起動する．ハンドラが多重に起動されている場合，最も外側のハンドラの出
口でのみ行う（※）．

(c) 自タスクに対するタスク例外処理の要求

自タスクに対してタスク例外処理を要求した結果，自タスクが起動条件を満た
すようになれば，タスク例外処理ルーチンを起動する．

(d) タスク例外処理の許可

タスク例外処理許可状態に移行した結果，自タスクが起動条件を満たすように
なれば，タスク例外処理ルーチンを起動する．タスク例外処理許可状態への移
行は，ena_tex の呼出しによって起こる．

(3) サービスコール処理内での明示的なディスパッチャの呼出し

タスクコンテキストから呼び出されたサービスコール処理においてディスパッ
チが必要になった場合，サービスコールの処理を中断すべきタイミングで 
dispatch を呼び出す．dispatch を呼び出すと，その時点で最も優先順位の高
いタスクが実行される．dispatch は，呼び出したタスクが次に実行状態にな
るとタスク例外処理ルーチンの起動条件をチェックし，起動条件を満たしてい
ればタスク例外処理ルーチンを起動する．

そのため，dispatch を呼び出したサービスコールへ処理が戻ってくるのは，
呼び出したタスクが次に実行状態となり，起動すべきタスク例外処理ルーチン
がなくなった時点である．

なお，dispatch の処理は，ターゲット依存部で提供される．

(4) 割込みハンドラ／CPU例外ハンドラの出口での処理

割込みハンドラ／CPU例外ハンドラ（以下，ハンドラと総称する）の出口処理
では，ディスパッチが必要か，タスク例外処理ルーチンの起動が必要かをチェッ
クし，必要な処理を行う．ハンドラが多重に起動されている場合，最も外側の
ハンドラの出口でのみこの処理を行う．

具体的には，ディスパッチとタスク例外処理ルーチンの起動のいずれかの処理
が必要な場合には reqflg を TRUE にすることとし，最も外側のハンドラの出
口で reqflg が TRUE になっていれば，まずディスパッチが必要かチェックし，
必要ならディスパッチを行う．ディスパッチにより新たに実行状態になったタ
スクがタスク例外処理ルーチンの起動条件を満たしていれば，タスク例外処理
ルーチンを起動する．ディスパッチが必要なかった場合には，実行中のタスク
がタスク例外処理ルーチンの起動条件を満たしているかチェックし，満たして
いればタスク例外処理ルーチンを起動する．二つのタスク例外処理ルーチンの
起動処理は，意味的には異なるものだが，ルーチン的には共通化できる可能性
がある．

なお，ハンドラの出口処理は，ターゲット依存部で提供される．

(5) タスクの起動と終了時の処理

タスク起動時のレジスタ復帰と，タスク終了時のレジスタ保存は，ターゲット
依存部の実装によっては省略できる．

タスクの起動は，create_context と activate_context によって，次にその
タスクへのディスパッチが起こった時に，タスクの起動番地から実行するよう
にタスクコンテキストを設定することで行う．

タスク終了時には，unlock_cpu に代えて，exit_and_dispatch を呼び出す．
exit_and_dispatch は，現在実行中のコンテキストを保存せずに，ディスパッ
チを行う関数である．

なお，create_context，activate_context，exit_and_dispatch の処理は，ター
ゲット依存部で提供される．

(6) 実行できる状態のタスクがない場合の扱い

実行できる状態のタスクがない場合の対策として，アイドルタスクを導入する
方法がある．アイドルタスクを導入すると，ディスパッチャの途中で割込み待
ちをする必要がないので，ソフトウェアの構造的にはエレガントになる．また，
ディスパッチャ中での条件分岐も減る．逆に，RAM の使用量がどうしても増え
てしまうという問題がある．このことから，アイドルタスクは使わない方針と
する．

(7) ディスパッチ保留状態の保持方法

ディスパッチが保留されるのは，次のいずれかの場合である．

 ・ディスパッチ禁止状態
 ・非タスクコンテキスト実行中

CPU例外ハンドラは非タスクコンテキストで実行するとしたため，CPU例外ハン
ドラ実行中は，非タスクコンテキスト実行中に含めて考えることができる．ま
た，サービスコールはすべて割込み禁止で実行するため，サービスコール実行
中も考慮する必要がない．

この中で，非タスクコンテキスト実行中は，sense_context() で判定できる．
そこで，ディスパッチ禁止状態を表すBOOL型の変数を用意すれば，ディスパッ
チ保留状態が保持できることになる．実際には，ディスパッチ許可状態を表す
BOOL型の変数 enatex を導入した．

※ サービスコール実行中に割込みを許可する（ディスパッチは許可しない）
場合には，サービスコール処理の途中で起動された割込みハンドラの出口では，
ディスパッチもタスク例外処理ルーチンの起動も行ってはならない．参照する
場所が違うことから，両者の禁止を別々の変数にする方法も考えられるが，設
定する方の効率を考えると，一つの変数で両方禁止できる方が良いと思われる．
すなわち，ディスパッチ禁止状態とサービスコール実行中状態を，同じ変数を
ビットフィールドに分けて記憶するのが妥当と考えられる．さらに，CPU例外
ハンドラをタスクコンテキストで実行する実装では，同じ変数にCPU例外ハン
ドラのネスト回数も記憶したくなる．例えば，次のような変数 pndflg を導入
する方法が考えられる．

 ・最下位ビット … サービスコール実行中
                                → 1 なら両者保留
 ・下から2ビットめ … ディスパッチ禁止状態
                                → 1 ならタスク切替え保留
 ・残りのビット … CPU例外ハンドラのネスト回数
                                → 0 以外ならタスク切替え保留


４．サービスコールの記述方法

(1) sns_xxx

sns_ctx，sns_loc，sns_dsp，sns_tex は，サービスコール内部でクリティカ
ルセクションを作らなくても実装できる．

(2) タスクコンテキスト専用のサービスコール

※ CPUロック状態で呼ばれるとエラーになることに注意．

(2-1) タスク切換えを起こさないもの（例: get_pri）

        エラーチェック
        t_lock_cpu();
        /*
         *  クリティカルセクション実行
         */
        t_unlock_cpu();

(2-2) タスク切換えを起こす可能性のあるもの（例: act_tsk）

        エラーチェック
        t_lock_cpu();
        /*
         *  クリティカルセクション実行
         */
        if (タスクディスパッチが必要 && enadsp) {
                /* 自タスクを待ち状態にするサービスコールでは，
                        上で enadsp をチェックする必要はない．*/
                dispatch();
        }
        t_unlock_cpu();

(2-3) 自タスクを終了するもの（ext_tsk）

        エラーチェック
        t_lock_cpu();
        /*
         *  クリティカルセクション実行
         */
        exit_and_dispatch();

(2-4) ras_tex

        エラーチェック
        tcb = get_tcb_self(tskid);
        t_lock_cpu();

        E_OBJエラーのチェック

        tcb->texptn |= rasptn;
        if (tcb == runtsk && runtsk->enatex) {
                texptn = runtsk->texptn;
                runtsk->enatex = FALSE;
                runtsk->texptn = 0;

                t_unlock_cpu();
                (*runtsk->tinib->texrtn)(texptn, runtsk->tinib->exinf);

                /* CPUロック状態のままリターンした場合を対策 */
                if (!t_sense_lock()) {
                        t_lock_cpu();
                }

                タスク例外処理ルーチンの起動条件を再度チェック＆起動
                （runtsk->texptn が 0 になるまで上の処理を繰り返す）

                runtsk->enatex = TRUE;
        }
        t_unlock_cpu();

(2-5) ena_tex

        エラーチェック
        t_lock_cpu();

        E_OBJエラーのチェック

        runtsk->enatex = TRUE;
        if (runtsk->texptn != 0) {
                texptn = runtsk->texptn;
                runtsk->enatex = FALSE;
                runtsk->texptn = 0;

                t_unlock_cpu();
                (*runtsk->tinib->texrtn)(texptn, runtsk->tinib->exinf);

                /* CPUロック状態のままリターンした場合を対策 */
                if (!t_sense_lock()) {
                        t_lock_cpu();
                }

                タスク例外処理ルーチンの起動条件を再度チェック＆起動
                （runtsk->texptn が 0 になるまで上の処理を繰り返す）

                runtsk->enatex = TRUE;
        }
        t_unlock_cpu();

(3) 非タスクコンテキスト専用のサービスコール

※ CPUロック状態で呼ばれるとエラーになることに注意．

(3-1) タスク切替えを起こさないもの

        i_lock_cpu();
        /*
         *  クリティカルセクション実行
         */
        i_unlock_cpu();

(3-2) タスク切替えを起こす可能性のあるもの（例: iact_tsk）

        i_lock_cpu();
        /*
         *  クリティカルセクション実行
         *
         *  タスクディスパッチが必要な場合には，reqflg を TRUE にする．
         *  ただし，enadsp が FALSE の時は，reqflg を TRUE にしない．
         */
        i_unlock_cpu();

(3-3) iras_tex

        エラーチェック
        tcb = get_tcb(tskid);
        i_lock_cpu();

        E_OBJエラーのチェック

        tcb->texptn |= rasptn;
        if (tcb == runtsk && runtsk->enatex) {
                reqflg = TRUE;
        }
        i_unlock_cpu();

以上


