TOPPERS/SSPカーネル コンフィギュレータ仕様 対応バージョン: Release 1.3.0 最終更新: 2014年 5月 19日 このドキュメントは,TOPPERS/SSPカーネルのコンフィギュレータが標準的に生 成すべきファイルの内容について解説したものである.ターゲット依存に生成 する内容については,このドキュメントの範囲外である. ---------------------------------------------------------------------- TOPPERS/SSP Kernel Toyohashi Open Platform for Embedded Real-Time Systems/ Smallet Set Profile Kernel Copyright (C) 2005-2010 by Embedded and Real-Time Systems Laboratory Graduate School of Information Science, Nagoya Univ., JAPAN Copyright (C) 2012-2015 by Naoki Saito Nagoya Municipal Industrial Research Institute, JAPAN 上記著作権者は,以下の(1)〜(4)の条件を満たす場合に限り,本ソフトウェ ア(本ソフトウェアを改変したものを含む.以下同じ)を使用・複製・改 変・再配布(以下,利用と呼ぶ)することを無償で許諾する. (1) 本ソフトウェアをソースコードの形で利用する場合には,上記の著作 権表示,この利用条件および下記の無保証規定が,そのままの形でソー スコード中に含まれていること. (2) 本ソフトウェアを,ライブラリ形式など,他のソフトウェア開発に使 用できる形で再配布する場合には,再配布に伴うドキュメント(利用 者マニュアルなど)に,上記の著作権表示,この利用条件および下記 の無保証規定を掲載すること. (3) 本ソフトウェアを,機器に組み込むなど,他のソフトウェア開発に使 用できない形で再配布する場合には,次のいずれかの条件を満たすこ と. (a) 再配布に伴うドキュメント(利用者マニュアルなど)に,上記の著 作権表示,この利用条件および下記の無保証規定を掲載すること. (b) 再配布の形態を,別に定める方法によって,TOPPERSプロジェクトに 報告すること. (4) 本ソフトウェアの利用により直接的または間接的に生じるいかなる損 害からも,上記著作権者およびTOPPERSプロジェクトを免責すること. また,本ソフトウェアのユーザまたはエンドユーザからのいかなる理 由に基づく請求からも,上記著作権者およびTOPPERSプロジェクトを 免責すること. 本ソフトウェアは,無保証で提供されているものである.上記著作権者お よびTOPPERSプロジェクトは,本ソフトウェアに関して,特定の使用目的 に対する適合性も含めて,いかなる保証も行わない.また,本ソフトウェ アの利用により直接的または間接的に生じたいかなる損害に関しても,そ の責任を負わない. $Id: configurator.txt 659 2015-05-19 09:45:26Z nmir-saito $ ---------------------------------------------------------------------- ○目次 ・生成するファイルの種類 ・静的API一覧 ・カーネル構成・初期化ヘッダファイル(kernel_cfg.h) (1) 固定生成部分 (2) オブジェクト数の定義 (3) オブジェクトのID番号の定義 ・カーネル構成・初期化ファイル(kernel_cfg.c) (1) 固定生成部分 (2) インクルードディレクティブ(#include)の処理 (3) オブジェクトのID番号を保持する変数の定義 (4) トレースログマクロのデフォルト定義 (5) 各カーネルオブジェクトに関する定義 (5-1) タスクに関する定義 (5-2) イベントフラグに関する定義 (5-3) データキューに関する定義 (5-4) 周期ハンドラに関する定義 (5-5) アラームハンドラに関する定義 (6) 割込みに関する定義 (6-1) 割込み要求ラインに関する定義 (6-2) 割込みサービスルーチンに関する定義 (6-3) 割込みハンドラに関する定義 (7) CPU例外に関する定義 (8) 共有スタック領域に関する定義 (9) タイムイベント管理に関する定義 (10)各モジュールの初期化関数の定義 (11)初期化ルーチンの実行関数の定義 (12)終了処理ルーチンの実行関数の定義 ○生成するファイルの種類 SSPカーネルのコンフィギュレータは,システムコンフィギュレーションファイ ルを処理して,カーネル構成・初期化ファイル(kernel_cfg.c)と構成・初期 化ヘッダファイル(kernel_cfg.h)を生成する.また,コンフィギュレータの 処理の途中に,必要な中間ファイルを生成する. ○静的API一覧 SSPカーネルのコンフィギュレータが処理する静的APIは次の通り. (1) タスク管理機能 CRE_TSK(ID tskid, { ATR tskatr, intptr_t exinf, TASK task, PRI itskpri, SIZE stksz, STK_T *stk }) DEF_EPR (ID tskid, { PRI exepri }) (2) 同期・通信機能 CRE_FLG(ID flgid, { ATR flgatr, FLGPTN iflgptn }) CRE_DTQ(ID dtqid, { ATR dtqatr, uint_t dtqcnt, void *dtqmb }) ※ dtqmb が NULL でない場合はサポートしない. (3) 時間管理機能 CRE_CYC(ID cycid, { ATR cycatr, intptr_t exinf, CYCHDR cychdr, RELTIM cyctim, RELTIM cycphs }) CRE_ALM(ID almid, { ATR almatr, intptr_t exinf, ALMHDR almhdr }) (4) 割込み管理機能 CFG_INT(INTNO intno, { ATR intatr, PRI intpri }) ATT_ISR({ ATR isratr, intptr_t exinf, INTNO intno, ISR isr, PRI isrpri }) DEF_INH(INHNO inhno, { ATR inhatr, INTHDR inthdr }) (5) CPU例外管理機能 DEF_EXC(EXCNO excno, { ATR excatr, EXCHDR exchdr }) (6) システム構成管理機能 DEF_ICS({ SIZE istksz, STK_T *istk }) DEF_STK({ SIZE stksz, STK_T *stk }) ATT_INI({ ATR iniatr, intptr_t exinf, INIRTN inirtn }) ATT_TER({ ATR teratr, intptr_t exinf, TERRTN terrtn }) これらの静的APIのパラメータの内,ID型のパラメータはオブジェクト識別名, ポインタ型(TASK,CYCHDR,ALMHDR,ISR,INTHDR,EXCHDR,INIRTN,TERRTN, STK_T *)および intptr_t 型のパラメータは一般定数式パラメータとする. その他のパラメータは,整数定数式パラメータとする. ○カーネル構成・初期化ヘッダファイル(kernel_cfg.h) カーネル構成・初期化ヘッダファイル(kernel_cfg.h)には,次の定義を生成する. (1) 固定生成部分 kernel_cfg.hが複数回インクルードされるのを防ぐための記述を生成する.具 体的には,ファイルの先頭に次の行を生成する. #ifndef TOPPERS_KERNEL_CFG_H #define TOPPERS_KERNEL_CFG_H また,ファイルの末尾に次の行を生成する. #endif /* TOPPERS_KERNEL_CFG_H */ (2) オブジェクト数の定義 カーネルがサポートするオブジェクトの数をマクロ定義するプリプロセッサディ レクティブ(#define)を生成する.具体的には,次のような記述を生成する. #define TNUM_TSKID <タスクの数> #define TNUM_FLGID <イベントフラグの数> #define TNUM_DTQID <データキューの数> #define TNUM_CYCID <周期ハンドラの数> #define TNUM_ALMID <アラームハンドラの数> (3) オブジェクトのID番号の定義 コンフィギュレータがID番号を割り付けたオブジェクトの名前を,割り付けた ID番号にマクロ定義するプリプロセッサディレクティブ(#define)を生成する. 例えば,次のような記述を生成する. #define TASK1 1 #define TASK2 2 #define ALM1 1 ************************************************************************** SSPカーネルの場合,CRE_TSK における起動時優先度の高い(値としては小さい) 順番に,1から連続した値がID番号としてそれぞれのタスクに割り当てられる. ************************************************************************** ○カーネル構成・初期化ファイル(kernel_cfg.c) (1) 固定生成部分 kernel_cfg.c用のヘッダファイルとID自動割付け結果ファイルをインクルード するプリプロセッサディレクティブ(#include)を生成する.具体的には,次 の行を生成する. #include "kernel/kernel_int.h" #include "kernel_cfg.h" (2) インクルードディレクティブ(#include)の処理 システムコンフィギュレーションファイルに含まれるC言語プリプロセッサのイ ンクルードディレクティブ(#include)と同一のディレクティブ(#include) を生成する.例えば, #include "sample1.h" というインクルードディレクティブに対して, #include "sample1.h" というディレクティブを生成する.生成するディレクティブの順序は,システ ムコンフィギュレーションファイル中でのインクルードディレクティブの順序 に一致させる. (3) オブジェクトのID番号を保持する変数の定義 コンフィギュレータに対するオプション指定(--external-id)により,コンフィ ギュレータは変数を生成する.変数名は ID 番号を割り付けたオブジェクト名 の末尾に "_id" を付加した名称とする.変数の型は,const属性を付加したID型 とし,変数の値は割り付けたID番号とする.例えば,次のような記述を生成する. const ID TASK1_id = 1; const ID TASK2_id = 2; const ID ALM1_id = 1; (4) トレースログマクロのデフォルト定義 kernel_cfg.cの中で使用するトレースログマクロのデフォルト定義を生成する. 具体的には,次の行を生成する. #ifndef LOG_ISR_ENTER #define LOG_ISR_ENTER(intno) #endif /* LOG_ISR_ENTER */ #ifndef LOG_ISR_LEAVE #define LOG_ISR_LEAVE(intno) #endif /* LOG_ISR_LEAVE */ (5) 各カーネルオブジェクトに関する定義 システムコンフィギュレーションファイル中に,オブジェクトを生成する静的 API「CRE_XXX」が含まれる各カーネルオブジェクトに関して,オブジェクト生 成のための定義を生成する. コンフィギュレータは,同じ種類のオブジェクトを生成する静的APIを集め,各 オブジェクトにID番号を割り付ける.ID番号は,他のオブジェクトのID番号と 重複がなく,ID番号が連続するように割り付ける. コンフィギュレータは,オブジェクトに割り付けるID番号を指定するための オプション(--id-input-file)並びに ID番号を割り付けたオブジェクトの名前 および割り付けたID番号の組をファイルに出力するためのオプション (--id-output-file)を持つ.しかし,SSPカーネルではこれらのオプションの 使用を原則として ***禁止*** する(理由は後述). ************************************************************************** 【理由】 SSPカーネルの場合,「タスクIDの場合に限り」コンフィギュレータは --id-input-file の指定内容を無視する.それは,CRE_TSKで指定した起動時優先 度の設定値が優先されるためである.また,--id-output-file で指定したファイ ルへの出力内容も --id-input-file で指定したタスクIDの値がそのまま出力され る.その出力内容は kernel_cfg.h へ出力されるタスクIDの値とは無関係である. 従って,混乱が生じる恐れがあることから,SSPカーネルでは --id-input-file および --id-output-file オプションを指定してはならない. 本来ならば,--kernel=ssp および --id-input-file を同時に指定した場合に コンフィギュレーションエラーとすべきところである.しかし,これらのオプショ ンに対する処理は全てコンフィギュレータ内部で実施され,テンプレートファイル (kernel.tf)では処理することができない.現時点ではコンフィギュレータの対応 について未定であるため暫定的にこのような扱いとしている. ************************************************************************** 各カーネルオブジェクトに関する定義の標準的な構成は,次の通りである.オ ブジェクトによって例外がある場合には,オブジェクト毎の項で説明する. (a) 最大のオブジェクトIDの変数の定義 最大のオブジェクトIDを持つ変数の定義を生成する.具体的には,オブジェク トの省略記号を「XXX/xxx」とすると,次のような行を生成する. const ID _kernel_tmax_xxxid = (TMIN_XXXID + TNUM_XXXID - 1); (b) オブジェクトに必要なメモリ領域の定義 オブジェクトによっては,オブジェクトに必要なメモリ領域の定義を生成する. 具体的には,オブジェクト毎の項で説明する. (c) オブジェクトの初期化ブロックの定義 オブジェクトの初期化ブロックの定義を生成する.具体的には, ・オブジェクトの省略記号を「XXX/xxx」 ・属性項目のデータ型を「YYY」 ・属性項目の名称を「zzz」 とすると,オブジェクトの属性項目毎に次のような行を生成する. const YYY _kernel_xxxinib_zzz[TNUM_XXXID] = { <オブジェクトIDが1のオブジェクトに対する zzz の初期化情報>, <オブジェクトIDが2のオブジェクトに対する zzz の初期化情報>, …… <オブジェクトIDがTNUM_XXXIDのオブジェクトに対する zzz の初期化情報>, }; オブジェクトの初期化情報の形式は,オブジェクトおよび属性項目毎に異なる. 具体的にはオブジェクト毎の項で説明する. (d) オブジェクトのコントロールブロックの定義 オブジェクトのコントロールブロックの定義を生成する.具体的には, ・オブジェクトの省略記号を「XXX/xxx」 ・属性項目のデータ型を「YYY」 ・属性項目の名称を「zzz」 とすると,オブジェクトの属性項目毎に次のような行を生成する. YYY _kernel_xxxcb_zzz[TNUM_XXXID]; オブジェクトのコントロールブロックの形式は,オブジェクトおよび属性項目毎に異なる. 具体的にはオブジェクト毎の項で説明する. (5-1) タスクに関する定義 SSPカーネルは,タスクが一つもないケースに対応していないため,タスクに関する 定義は必ず生成しなければならない. タスクの省略記号は「TSK」「tsk」である. ただし,タスク初期化ブロックの変数名は _kernel_tinib_<属性項目名> である. (「TSK/tsk」に代えて「T/t」を用いている). タスクコントロールブロックは存在しない. タスク初期化ブロックには,「CRE_TSK」静的APIで指定される情報に加えて, 「DEF_EPR」静的APIで指定される情報を含める. 以下では,システムコンフィギュレーションファイルに次の静的APIが含まれて いる時に生成すべき情報について述べる. CRE_TSK(tskid, { tskatr, exinf, task, itskpri, stksz, stk }); DEF_EPR(tskid, { exepri }); ※ 以降の初期化情報の配列に格納される順序は,起動時優先度の高い順となる. ※ なぜなら,タスクID が起動時優先度の高い順番で割り当てられるからである. (5-1-1) タスクの初期化情報(タスク属性) タスク属性の初期化情報は,次の形式とする. const ATR _kernel_tinib_tskatr[TNUM_TSKID] = {(tskatr)}; (5-1-2) タスクの初期化情報(拡張情報) タスクの拡張情報の初期化情報は,次の形式とする. const intptr_t _kernel_tinib_exinf[TNUM_TSKID] = {(intptr_t)(exinf)}; (5-1-3) タスクの初期化情報(起動番地) タスクの起動番地の初期化情報は,次の形式とする. const TASK _kernel_tinib_task[TNUM_TSKID] = {(task)}; (5-1-4) タスクの初期化情報(実行時優先度) タスクの実行時優先度の初期化情報は,次の形式とする. (実際には改行されず,1行で出力される) const TASK _kernel_tinib_epriority[TNUM_TSKID] = { INT_PRIORITY(実行時優先度)}; 実行時優先度の値は CRE_TSK の起動時優先度(itskpri) および DEF_EPR の実行時優先度(exepri) の設定値を元に算出される. 算出方法については(5-1-6)に後述する. (5-1-5) ready_primap の初期値の定義 タスクに対しては,ビットマップ ready_primap の初期値情報を生成する. タスク属性の設定値から一意に定めることができるからである. 具体的には以下のような定義を生成する. const uint_t _kernel_init_rdypri = ; ここで ready_primap の初期値は,タスク属性(tskatr)の値から算出される. 変数 _kernel_init_rdypri の各ビットはそれぞれのタスクに対応する. タスクIDが1のタスクは最下位ビット(LSB)に対応づけられており, タスクIDの値が1つ増える毎に1つ高位のビットがそれぞれ対応づけられる. 例えば,タスクIDが4のタスクは最下位から数えて4番目のビットが対応する. タスク属性として TA_ACT を持つタスクは,そのタスクに対応するビットが 1になる.TA_ACT を持たない場合は対応するビットが0になる. (5-1-6) 優先度の表現 優先度の値は,コンフィギュレーションファイルの CRE_TSK 及び DEF_EPR にて 指定した値を元に,コンフィギュレータが自動的に割り当てる. 値の範囲は TMIN_TPRI(=1) から TMAX_TPRI の範囲の値をとる. また,コンフィギュレータにより割り当てられる優先度の値とは別に,処理効率の 観点からプログラム中で使用される優先度の内部表現がある. 内部表現の値は 0 から (TMAX_TPRI-TMIN_TPRI) の範囲の値をとる. 優先度とその内部表現との間には (内部表現 = 優先度 - TMIN_TPRI)の関係がある. タスクの起動時優先度は,CRE_TSK で指定した起動時優先度の高い順に(値としては 小さい順に),TMIN_TPRI(=1) からはじまる連続した値が割り当てられる. つまり,タスクの起動時優先度の値はタスクID に等しい. タスクの実行時優先度の値は,以下の方法で算出される. (1)当該タスクに対し DEF_EPR で実行時優先度を定義しない場合 当該タスクに対し DEF_EPR で実行時優先度を定義しない場合, 起動時優先度の値と同じ値を実行時優先度の値とする. (2)当該タスクに対し DEF_EPR で実行時優先度を定義する場合 当該タスクに対し DEF_EPR で実行時優先度を定義した場合, DEF_EPR で指定した実行時優先度の設定値,並びに,全てのタスクに対する 起動時優先度の設定値およびコンフィギュレーション結果として得られる起動時優先度 の値をもとに,実行時優先度の値を算出する. 実行時優先度の値を算出する方法は,「実行中のタスクがプリエンプトされる/されない」 という関係が保存される範囲で適切な値が算出されることを意図したもので, 具体的な手順は次のようになる. 【実行時優先度の算出手順 ※これを全てのタスクに対して行う】 (1) あるタスクを選ぶ(T1とする) (2) T1 の実行時優先度の設定値と同じ,または,それよりも低い(値としては 大きい)起動時優先度の設定値を持つタスクの集合の中で,起動時優先度の 設定値が最も高い(値としては最も小さい)タスクを探す(T2) (3) T2 の起動時優先度の値を T1 の実行時優先度の値とする. 例) CRE_TSK(TASK1, { TA_NULL, 1, task1, 2, 10, NULL }); CRE_TSK(TASK2, { TA_NULL, 3, task2, 6, 30, NULL }); CRE_TSK(TASK3, { TA_NULL, 5, task3, 4, 50, NULL }); CRE_TSK(TASK4, { TA_ACT, 7, task4, 8, 100, NULL }); DEF_IPR(TASK1, { 1 } ); DEF_IPR(TASK2, { 5 } ); DEF_IPR(TASK3, { 3 } ); DEF_IPR(TASK4, { 4 } ); 結果は次のようになる: 【起動時優先度】 TASK1:設定値=2 → (割り当てられる)起動時優先度=1 TASK2:設定値=6 → (割り当てられる)起動時優先度=3 TASK3:設定値=4 → (割り当てられる)起動時優先度=2 TASK4:設定値=8 → (割り当てられる)起動時優先度=4 起動時優先度の高い順に並び替えると TASK1:設定値=2 → 起動時優先度=1 TASK3:設定値=4 → 起動時優先度=2 TASK2:設定値=6 → 起動時優先度=3 TASK4:設定値=8 → 起動時優先度=4 【実行時優先度】 TASK1:設定値=1 TASK3:設定値=3 TASK2:設定値=5 TASK4:設定値=4 ・ためしに TASK4 の場合の実行時優先度の値を算出してみる. ・TASK4 の実行時優先度の設定値(4)と同じまたはそれよりも低い起動時  優先度の設定値をもつタスクの集合 = {TASK2,TASK3,TASK4} ・上記タスクの集合の中で最も起動時優先度の設定値が高いもの = TASK3 ・上記タスクの起動時優先度の値 = 2 ・従ってTASK4 の実行時優先度の値は 2 ・同様に,TASK3 の場合は. ・TASK3 の実行時優先度の設定値(3)と同じまたはそれよりも低い起動時  優先度の設定値をもつタスクの集合 = {TASK2,TASK3,TASK4} ・上記タスクの集合の中で最も起動時優先度の設定値が高いもの = TASK3 ・上記タスクの起動時優先度の値 = 2 ・従ってTASK3 の実行時優先度の値は 2 実行時優先度に対する初期化情報の kernel_cfg.c への結果は次のようになる. 実際は1行で表現される.また,配列への格納順序は TASK1, TASK3, TASK2, TASK4 の順序で格納されることに注意する. ちなみに,INT_PRIORITY マクロは優先度の値をその内部表現に変換するマクロで あり,task.h で定義されている. const uint_t _kernel_tinib_epriority[TNUM_TSKID] = {INT_PRIORITY(1),INT_PRIORITY(2),INT_PRIORITY(3),INT_PRIORITY(2)}; (5-1-7) スタック領域設定 SSPカーネルでは,タスクを含む全ての処理単位が同じスタック領域を共用する ように設計されている. 共用されるスタック領域のことを「共有スタック領域」と呼ぶ. 共有スタック領域の定義は DEF_STK を用いて行う.詳しくは(9)を参照のこと. コンフィギュレータは共有スタック領域のみ確保し,タスクコンテキスト専用の スタック領域は確保しない. このため,CRE_TSK では先頭番地 stk として NULL のみ受け付ける. NULL 以外の値を指定した場合,エラーとなる. また,CRE_TSK で指定するタスクのスタックサイズ stksz の値は, DEF_STK を用いない場合には共有スタック領域のサイズを決定するために用いられ, DEF_STK を用いる場合には DEF_STK で指定した共有スタック領域のサイズを チェックするために用いられる.※ (9-4) も参照のこと. 全タスクによるスタック使用量(の推定値)は,CRE_TSK で指定する スタックサイズ stksz, および起動時優先度 itskpri,ならびに DEF_EPR で 指定する実行時優先度 exepri から算出される.算出手順については次節で説明する. (5-1-8) 全タスクによるスタック使用量の算出方法 全タスクのスタック使用量に対する計算の基本的な考え方は,それぞれの優先度ごとに, 同じ優先度をもつタスクの中で最もスタック使用量が大きいものを選び,それらの スタック使用量をすべて加算したものと考えることである.実際にはプログラムにより それより少なくなるケースも存在し得るが,現在の実装では考え方を単純化している. SSPカーネルの仕様では,ひとつの起動時優先度につき登録可能なタスクは 高々一つである.そのため,DEF_EPR により実行時優先度を設定しない場合, すなわち,全てのタスクの実行時優先度がそれぞれの起動時優先度に等しい 場合,各タスクのスタック使用量を単純に加算したものが,全タスクによる スタック使用量となる. それに対し,DEF_EPR を用いて特定のタスクに対する実行時優先度を指定し, 当該タスク実行中の優先度を引き上げるように設定した場合,本来ならばプリ エンプトされるはずのものがされなくなるケースが生じ,結果として全タスクの スタック使用量を少なくできる場合がある. 実行時優先度を考慮した「全タスクによるスタック使用量」の算出手順は 次のようになる(※) (a) あるタスクから実行開始し,他のどのようなタスクにプリエンプトされるか, そして結果的にどういうプログラムの実行経路が存在し得るかを全て列挙する. (b) それぞれの場合の「全タスクによるスタック使用量」を求める. (c) 求めた値の中の最大値を選ぶ. ※ 実装では,不要な処理の省略等も行っているが,ここでは基本的な考え方 のみを示す. 算出手順を具体的な例を用いて説明する. 例として先の (5-1-6) の例を用いる. 下の手順 (a), (b), (c) は上に挙げた各項目に対応している. CRE_TSK(TASK1, { TA_NULL, 1, task1, 2, 10, NULL }); CRE_TSK(TASK2, { TA_NULL, 3, task2, 6, 30, NULL }); CRE_TSK(TASK3, { TA_NULL, 5, task3, 4, 50, NULL }); CRE_TSK(TASK4, { TA_ACT, 7, task4, 8, 100, NULL }); DEF_IPR(TASK1, { 1 } ); DEF_IPR(TASK2, { 5 } ); DEF_IPR(TASK3, { 3 } ); DEF_IPR(TASK4, { 4 } ); (a) あるタスク(TASKn, n = 1,2,3,4)を選択する. TASKn の実行時優先度よりも高い起動時優先度(値としては小さい)をもつ タスクの集合を探す. そして「プリエンプトされるタスク→プリエンプトするタスク」の組を列挙する. ・TASK1 に着目すると,これをプリエンプト可能なタスクは存在しない. ・TASK2 に着目すると,実行中は優先度 5 になり,これをプリエンプト可能な  タスクは TASK1 および TASK3. ・TASK3 に着目すると,実行中は優先度 3 になり,これをプリエンプト可能な  タスクは TASK1 だけ. ・TASK4 に着目すると,実行中は優先度 4 になり,これをプリエンプト可能な  タスクは TASK1 だけ. したがってここでは,次の組が得られる. TASK2 → TASK1 TASK2 → TASK3 TASK3 → TASK1 TASK4 → TASK1 全くプリエンプトされないケース,および,プリエンプトしたタスクがさらに 別のタスクにプリエンプトされるケースまで考慮すると,結果的にプログラムの 実行経路は次のようになると考えられる. ・TASK1 から始まるケース: TASK1 (プリエンプトされずに終了) ・TASK2 から始まるケース: TASK2 TASK2 → TASK1 (TASK2から実行開始して,TASK1にプリエンプトされた) TASK2 → TASK3 TASK2 → TASK3 → TASK1 ・TASK3 から始まるケース: TASK3 TASK3 → TASK1 ・TASK4 から始まるケース: TASK4 TASK4 → TASK1 (b) それぞれのケースでスタック使用量を計算する. ・TASK1 から始まるケース: TASK1:10 ・TASK2 から始まるケース: TASK2:30 TASK2 → TASK1:30+10 TASK2 → TASK3:30+50 TASK2 → TASK3 → TASK1:30+50+10 ・TASK3 から始まるケース: TASK3:50 TASK3 → TASK1:50+10 ・TASK4 から始まるケース: TASK4:100 TASK4 → TASK1:100+10 (c) 最も値が大きくなる経路を選ぶ. ここでは TASK4 → TASK1 の場合が最大で,見積もり値は 110 (5-1-9) 全タスクによるスタック使用量の定義 コンフィギュレータは,全タスクによるスタック使用量の見積もり過程を kernel_cfg.c にコメントとして出力する. /* * Task Stack Size Estimation: * (ここに算出の過程が出力される) */ そして,次のような行を生成する. #define TOPPERS_TSTKSZ <全タスクによるスタック使用量の計算値> (5-1-10) エラー条件 タスクに関するエラー条件は次の通りである. *コンフィギュレータ本体が検出するもの ・タスクが一つも存在しない場合. ・同じtskidに対するCRE_TSKが複数ある場合(E_OBJ) ・DEF_EPRに対応するCRE_TSKがない場合(E_NOEXS) ・同じtskidに対するDEF_EPRが複数ある場合(E_OBJ) *パス2で検出するもの ・tskatrが([[TA_ACT]|[TA_RSTR])でない場合(E_RSATR) ※ ターゲット依存部でタスク属性を追加可(TARGET_TSKATR) ・(TMIN_TPRI <= itskpri && itskpri <= TMAX_TPRI)でない場合(E_PAR) ・同じ itskpri に対する CRE_TSK が複数ある場合 (E_PAR) ・stkszが0か,ターゲット定義の最小値(TARGET_MIN_STKSZ)よりも小さい場  合(E_PAR) ・stk が NULL でない場合(E_PAR) ・(TMIN_TPRI <= exepri && exepri <= TMAX_TPRI) でない場合:E_PAR エラー ・(itskpri < exepri && exepri <= TMAX_TPRI) である場合:E_ILUSEエラー *パス3で検出するもの ・task がプログラムの開始番地として正しくない場合(E_PAR) - ターゲット依存の値(CHECK_FUNC_ALIGN)の倍数でない場合 - NULLの場合(ターゲット依存,CHECK_FUNC_NONNULL) (5-2) イベントフラグに関する定義 イベントフラグの省略記号は「FLG/flg」である.以下では,システムコンフィ ギュレーションファイルに次の静的APIが含まれている時に生成すべき情報につ いて述べる. CRE_FLG(flgid, { flgatr, iflgptn }); (5-2-1) イベントフラグの初期化情報(イベントフラグ属性) イベントフラグの属性情報の初期化情報は,次の形式とする. const ATR _kernel_flginib_atr[TNUM_FLGID] = {(flgatr)}; (5-2-2) イベントフラグの初期化情報(フラグパターン初期値) イベントフラグのフラグパターンの初期化情報は,次の形式とする. const FLGPTN _kernel_flginib_iflgptn[TNUM_FLGID] = {(iflgptn)}; (5-2-3) イベントフラグのフラグパターン イベントフラグのフラグパターンを格納する配列は,以下の形式とする. FLGPTN _kernel_flgcb_flgptn[TNUM_FLGID]; (5-2-4) エラー条件 イベントフラグに関するエラー条件は次の通りである. *コンフィギュレータ本体が検出するもの ・同じflgidに対するCRE_FLGが複数ある場合(E_OBJ) *パス2で検出するもの ・tskatrが([[TA_CLR]|[TA_NULL])でない場合(E_RSATR) ※ TA_CLR と TA_NULL を同時に指定した場合,TA_CLR のみを指定した場合と同様に振る舞う ・iflgptn がFLGPTN に格納できない(iflgptnの値がFLGPTN型の最大値よりも大きい)場合(E_PAR) (5-3) データキューに関する定義 データキューの省略記号は「DTQ/dtq」である.以下では,システムコンフィギュ レーションファイルに次の静的APIが含まれている時に生成すべき情報について 述べる. CRE_DTQ(dtqid, { dtqatr, dtqcnt, dtqmb }); (5-3-1) データキューに必要なメモリ領域の定義 データキューに必要なメモリ領域として,データキュー管理領域がある.生成 するデータキュー毎に,必要なサイズのデータキュー管理領域を定義する.具 体的には,上記の静的APIに対して,次の定義を生成する. *dtqcntが0でない場合のみ生成 static intptr_t _kernel_dtqmb_<データキュー名>[dtqcnt]; (5-3-2) データキューの初期化情報(データキュー属性) データキューの属性情報の初期化情報は,次の形式とする. const ATR _kernel_dtqinib_atr[TNUM_DTQID] = {(dtqatr)}; (5-3-3) データキューの初期化情報(データキュー管理領域) データキューのデータキュー管理領域の初期化情報は,次の形式とする. intptr_t * const _kernel_dtqinib_data[TNUM_DTQID] = {(_kernel_dtqmb_<データキュー名>)}; ※ SSPカーネルでは待ち状態をサポートしないため,同期呼出しが実現できない.  そのため dtqcnt が 0 の場合,コンフィギュレーションエラー(E_PAR)となる. (5-3-4) データキューの初期化情報(データキューのサイズ) データキューのサイズの初期化情報は,次の形式とする. const uint8_t _kernel_dtqinib_size[TNUM_DTQID] = {(dtqcnt)}; (5-3-5) データキューに格納されている要素数 データキューに格納されている要素数の配列は,次の形式とする. uint8_t _kernel_dtqcb_count[TNUM_DTQID]; (5-3-6) データキューの先頭・末尾要素 データキューに格納されている先頭要素・末尾要素を格納する配列は,次の形式とする. uint8_t _kernel_dtqcb_head[TNUM_DTQID]; uint8_t _kernel_dtqcb_tail[TNUM_DTQID]; (5-3-7) エラー条件 データキューに関するエラー条件は次の通りである. *コンフィギュレータ本体が検出するもの ・同じdtqidに対するCRE_DTQが複数ある場合(E_OBJ) *パス2で検出するもの ・dtqatrが([TA_NULL])でない場合(E_RSATR) ・dtqcntが 0 の場合 (E_PAR) ・dtqmb が NULL でない場合(E_NOSPT) (5-4) 周期ハンドラに関する定義 周期ハンドラの省略記号は「CYC/cyc」である.以下では,システムコンフィ ギュレーションファイルに次の静的APIが含まれている時に生成すべき情報につ いて述べる. CRE_CYC(cycid, { cycatr, exinf, cychdr, cyctim, cycphs }); (5-4-1) 周期ハンドラの初期化情報(拡張情報) 周期ハンドラの拡張情報の初期化情報は,次の形式とする. const intptr_t _kernel_cycinib_exinf[TNUM_CYCID] = {(intptr_t)(exinf)}; (5-4-2) 周期ハンドラの初期化情報(起動番地) 周期ハンドラのアドレスの初期化情報は,次の形式とする. const CYCHDR _kernel_cycinib_cychdr[TNUM_CYCID] = {(cycdhr)}; (5-4-3) 周期ハンドラの初期化情報(起動周期) 周期ハンドラの起動周期の初期化情報は,次の形式とする. const RELTIM _kernel_cycinib_cyctim[TNUM_CYCID] = {(cyctim)}; (5-4-4) 周期ハンドラの初期化情報(位相) 周期ハンドラの位相の初期化情報は,次の形式とする. const RELTIM _kernel_cycinib_cycphs[TNUM_CYCID] = {(cycphs)}; (5-4-5) 周期ハンドラの動作状態 周期ハンドラの動作状態を格納する変数は,次の形式とする. uint_t _kernel_cyccb_cycact; 変数 _kernel_cyccb_cycact の各ビットはそれぞれの周期ハンドラに対応する. 周期ハンドラIDが1のタスクは最下位ビット(LSB)に対応づけられており, IDの値が1つ増える毎に1つ高位のビットがそれぞれ対応づけられる. 例えば,ハンドラIDが4の周期ハンドラは最下位から数えて4番目のビットが 対応する. 動作している状態の周期ハンドラは,その周期ハンドラに対応するビットが 1となる.動作していない状態の周期ハンドラは対応するビットが0になる. (5-4-6) 周期ハンドラの次回起動時刻の定義 周期ハンドラの次回起動時刻を格納する変数は,次の形式とする. EVTTIM _kernel_cyccb_evttim[TNUM_CYCID]; (5-4-7) タイムイベントキューの中のオフセット情報の定義 タイムイベントキューの中で周期ハンドラ用に割り当てられた領域の先頭位置を 示す情報を変数に保持する.タイムイベントキューは配列で表現されており, 先頭位置は配列の先頭からのオフセット(つまり配列のインデックス)として 表現される.周期ハンドラに対してはタイムイベントキュー配列の先頭から割り 当てられるため,この値は常に 0 となる.従って変数定義は,次の形式となる. const uint_t _kernel_cycevtid_offset = 0; タイムイベントキューについては(9-3)を参照のこと. (5-4-8) エラー条件 周期ハンドラに関するエラー条件は次の通りである. *コンフィギュレータ本体が検出するもの ・同じcycidに対するCRE_CYCが複数ある場合(E_OBJ) *パス2で検出するもの ・cycatrが([TA_STA])でない場合(E_RSATR) ・(0 < cyctim && cyctim <= TMAX_RELTIM)でない場合(E_PAR) ・(0 <= cycphs && cycphs <= TMAX_RELTIM)でない場合(E_PAR) ・警告:cycatrにTA_STAが設定されていて,(cycphs == 0)の場合 ・CRE_CYC のエントリが16個より多い ※SSP実装定義事項 *パス3で検出するもの ・cychdrがプログラムの開始番地として正しくない場合(E_PAR) - ターゲット依存の値(CHECK_FUNC_ALIGN)の倍数でない場合 - NULLの場合(ターゲット依存,CHECK_FUNC_NONNULL) (5-5) アラームハンドラに関する定義 アラームハンドラの省略記号は「ALM/alm」である.以下では,システムコン フィギュレーションファイルに次の静的APIが含まれている時に生成すべき情報 について述べる. CRE_ALM(ID almid, { almatr, (intptr_t)(exinf), almhdr }); (5-5-1) アラームハンドラの初期化情報(拡張情報) アラームハンドラの拡張情報の初期化情報は,次の形式とする. const intptr_t _kernel_alminib_exinf[TNUM_ALMID] = {(intptr_t)(exinf)}; (5-5-2) アラームハンドラの初期化情報(起動番地) アラームハンドラの起動番地の初期化情報は,次の形式とする. const ALMHDR _kernel_alminib_almhdr[TNUM_ALMID] = {(almhdr)}; (5-5-3) アラームハンドラの動作状態 アラームハンドラの動作状態を格納する変数は,次の形式とする. uint_t _kernel_almcb_almact; 変数 _kernel_almcb_almact の各ビットは個々のアラームハンドラに対応する. アラームハンドラIDが1のタスクは最下位ビット(LSB)に対応づけられており, IDの値が1つ増える毎に1つ高位のビットがそれぞれ対応づけられる. 例えば,ハンドラIDが4のアラームハンドラは最下位から数えて4番目のビットが 対応する. 動作している状態のアラームハンドラは,その周期ハンドラに対応するビットが 1となる.動作していない状態の周期ハンドラは対応するビットが0になる. (5-5-4) タイムイベントキューの中のオフセット情報の定義 タイムイベントキューの中でアラームハンドラ用に割り当てられた領域の先頭 位置を示す情報を変数に保持する.タイムイベントキューは配列で表現されており, 先頭位置は配列の先頭からのオフセット(つまり配列のインデックス)として 表現される.アラームハンドラに対しては周期ハンドラ用領域の次の要素から 割り当てられるため,この値は常に周期ハンドラの個数に等しい. 変数定義は,次の形式となる. const uint_t _kernel_almevtid_offset = <周期ハンドラの個数>; タイムイベントキューについては(9-3)を参照のこと. (5-5-5) エラー条件 アラームハンドラに関するエラー条件は次の通りである. *コンフィギュレータ本体が検出するもの ・同じalmidに対するCRE_ALMが複数ある場合(E_OBJ) *パス2で検出するもの ・almatrが(TA_NULL)でない場合(E_RSATR) ・CRE_ALM のエントリが16個より多い ※SSP実装定義事項 *パス3で検出するもの ・almhdrがプログラムの開始番地として正しくない場合(E_PAR) - ターゲット依存の値(CHECK_FUNC_ALIGN)の倍数でない場合 - NULLの場合(ターゲット依存,CHECK_FUNC_NONNULL) (6) 割込みに関する定義 割込みに関して生成する情報は,ターゲット毎に定めることができる. ターゲット毎に定めない場合には,以下で述べるターゲットに依存しない標準的な 情報を生成する.ターゲット毎に定める場合には,(6-1)と(6-3)に述べる情報は生 成しない((6-2)に述べる情報は生成する). (6-1) 割込み要求ラインに関する定義 割込み要求ラインの属性を設定する静的API「CFG_INT」で設定した割込み要求 ラインに関する定義を生成する.具体的には次の通り. 以下では,システムコンフィギュレーションファイルに次の静的APIが含まれ ている時に生成すべき情報について述べる. CFG_INT(INTNO intno, { intatr, intpri }); (6-1-1) 設定する割込み要求ラインの数 設定する割込み要求ラインの数をマクロ定義するプリプロセッサディレクティ ブ(#define)を生成する.また,その値を持つ変数の定義を生成する.具体的 には,次のような行を生成する. #define TNUM_INTNO <設定する割込み要求ラインの数> const uint_t _kernel_tnum_intno = TNUM_INTNO; (6-1-2) 割込み要求ラインの初期化情報(割込み番号) 割込み番号の初期化情報は,次の形式とする. const INTNO _kernel_intinib_intno[TNUM_INTNO] = {(intno)}; (6-1-3) 割込み要求ラインの初期化情報(割込み要求ライン属性) 割込み要求ライン属性の初期化情報は,次の形式とする. const ATR _kernel_intinib_intatr[TNUM_INTNO] = {(intatr)}; (6-1-4) 割込み要求ラインの初期化情報(割込み要求ライン属性) 割込み優先度の初期化情報は,次の形式とする. const PRI _kernel_intinib_intpri[TNUM_INTNO] = {(intpri)}; (6-1-5) エラー条件 割込み要求ラインに関するエラー条件は次の通りである. *パス2で検出するもの ・intno が CFG_INTに対する割込み番号として正しくない場合(E_PAR) ・intno が CFG_INTによって設定済みの場合(E_OBJ) ・intatrが([TA_ENAINT]|[TA_EDGE])でない場合(E_RSATR) ※ ターゲット依存部で割込み属性を追加可(TARGET_INTATR) ・カーネル管理に固定されている intno に対して,intpriにTMIN_INTPRI  よりも小さい値(つまり,優先度としては高い値)が指定された場合(E_OBJ) ・カーネル管理外に固定されている intno に対して,intpri に TMIN_INTPRI  と同じかまたはそれよりも大きい値(つまり,優先度としては同じかまたは  それより低い値)が指定された場合(E_OBJ) ・intpri が CFG_INT に対する割込み優先度として正しくない場合(E_PAR) *必要に応じてターゲット依存部で検出するもの ・intatr が割込み属性として設定できない値の場合(E_RSATR) ・intpri が割込み優先度として設定できない値の場合(E_PAR) ・同一の割込み優先度しか設定できない割込み要求ラインに対して,異なる割  込み優先度を設定した場合(E_PAR) (6-2) 割込みサービスルーチンに関する定義 (6-2-1) 割込みハンドラの生成 システムコンフィギュレーションファイル中に含まれる割込みサービスルーチ ンを追加する静的API「ATT_ISR」に基づき,同一の割込み番号に対して追加さ れた割込みサービスルーチンを順に呼び出す関数を生成する. 具体的には,同一の割込み番号に対して割込みサービスルーチンを追加する ATT_ISR({ isratr_1, exinf_1, intno, isr_1, isrpri_1 }); ATT_ISR({ isratr_2, exinf_2, intno, isr_2, isrpri_2 }); …… ATT_ISR({ isratr_n, exinf_n, intno, isr_n, isrpri_n }); という静的APIに対して,次のような関数を生成する.ここで,isrpri_1, isrpri_2,……,isrpri_nは,値の小さい順に並べ替えられているものとする. 値が同じものの間では,システムコンフィギュレーションファイル中での静的 APIの順序の通りに並んでいるものとする. static void _kernel_inthdr_(void) { PRI saved_ipm; i_begin_int(intno); saved_ipm = i_get_ipm(); LOG_ISR_ENTER(); /* ISR1の呼出し */ isr_1((intptr_t)(exinf_1)); LOG_ISR_LEAVE(intno); if (i_sense_lock()) { /* ISRの呼出し前の状態に戻す */ i_unlock_cpu(); } i_set_ipm(saved_ipm); LOG_ISR_ENTER(); /* ISR2の呼出し */ isr_2((intptr_t)(exinf_2)); LOG_ISR_LEAVE(intno); if (i_sense_lock()) { /* ISRの呼出し前の状態に戻す */ i_unlock_cpu(); } i_set_ipm(saved_ipm); …… LOG_ISR_ENTER(); /* ISRnの呼出し */ isr_n((intptr_t)(exinf_n)); LOG_ISR_LEAVE(intno); i_end_int(intno); } ここで,ISRnの呼出しの後に呼出し前の状態に戻さないのは,割込みハンドラ からのリターンにより,カーネルが元の状態に戻すためである. 同一の割込み番号に対して追加された割込みサービスルーチンが1つのみの場合 には,次のような関数を生成する. static void _kernel_inthdr_(void) { i_begin_int(intno); LOG_ISR_ENTER(intno); isr_1((intptr_t)(exinf_1)); LOG_ISR_LEAVE(intno); i_end_int(intno); } 【課題】ここで,LOG_ISR_ENTER,LOG_ISR_LEAVEの引数をどうするかが課題と して残っている.ATT_ISRで登録されたISRに対してはISR IDが付与されないた め,IDでISRを区別することができない.やむなく割込み番号を渡しているが, 拡張情報(exinf)も渡すべきかもしれない. (6-2-2) 割込みハンドラの定義に相当する処理 上のように割込みハンドラを生成した場合には,次に説明する割込みハンドラ に関する定義において,システムコンフィギュレーションファイル中に次の静 的APIが含まれているのと同様に処理する. DEF_INH(inhno, { TA_NULL, _kernel_inthdr_ }); ここでinhnoは,intnoに対応する割込みハンドラ番号である. (6-2-3) エラー条件 割込みサービスルーチンに関するエラー条件は次の通りである. *パス2で検出するもの ・isratr が(TA_NULL)でない場合(E_RSATR) ※ ターゲット依存部で割込みサービスルーチン属性を追加可(TARGET_ISRATR) ・intno が ATT_ISR に対する割込み番号として正しくない場合(E_PAR) ※ intno に対応する inhno がない場合を含む ・(TMIN_ISRPRI <= isrpri && isrpri <= TMAX_ISRPRI)でない場合(E_PAR) ・intno に対応する inhno に対して DEF_INH がある場合(E_OBJ) ・intno に対する CFG_INT がない場合(E_OBJ) ・intno に対して CFG_INT が存在し,かつ,CFG_INT で設定された割込み優先度が  TMIN_INTPRI よりも小さい(つまり,優先度としては高い)場合(E_OBJ) ※ カーネル管理外のISRはサポートしないため *必要に応じてターゲット依存部で検出するもの ・isrがプログラムの開始番地として正しくない場合(E_PAR) (6-3) 割込みハンドラに関する定義 割込みハンドラを定義する静的API「DEF_INH」で定義した割込みハンドラ (上述の割込みサービスルーチンの追加によりコンフィギュレータが生成した 割込みハンドラを含む)に関する定義を生成する.具体的には次の通り. 以下では,システムコンフィギュレーションファイルに次の静的APIが含まれ ている時に生成すべき情報について述べる. DEF_INH(inhno, { inhatr, inthdr }); (6-3-1) 定義する割込みハンドラの数 定義する割込みハンドラの数をマクロ定義するプリプロセッサディレクティブ (#define)を生成する.また,その値を持つ変数の定義を生成する.具体的 には,次のような行を生成する. #define TNUM_INHNO <定義する割込みハンドラの数> const uint_t _kernel_tnum_inhno = TNUM_INHNO; (6-3-2) 割込みハンドラの出入口処理 定義する割込みハンドラ毎に,割込みハンドラの出入口処理ルーチンを生成す る.具体的には,次のような行を生成する. INTHDR_ENTRY(inhno, inhno_num, inthdr) ここでinhno_numは,inhno(割込みハンドラ番号)を数値で表現したもので, アセンブリ言語記述に使用するためのものである. (6-3-3) 割込みハンドラの初期化情報(割込みハンドラ番号) 割込みハンドラ番号の初期化情報は,次の形式とする. const INHNO _kernel_inhinib_inhno[TNUM_INHNO] = {(inhno)}; (6-3-4) 割込みハンドラの初期化情報(割込みハンドラ属性) 割込みハンドラ属性の初期化情報は,次の形式とする. const ATR _kernel_inhinib_inhatr[TNUM_INHNO] = {(inhatr)}; (6-3-5) 割込みハンドラの初期化情報(割込みハンドラアドレス) 割込みハンドラのアドレスの初期化情報は,次の形式とする. const FP _kernel_inhinib_entry[TNUM_INHNO] = {(inthdr)}; (6-3-6) エラー条件 割込みハンドラに関するエラー条件は次の通りである. *パス2で検出するもの ・inhno が DEF_INH に対する割込みハンドラ番号として正しくない場合(E_PAR) ・inhno が DEF_INH によって設定済みの場合(E_OBJ) #・inhno に対応する intno に対して ATT_ISR がある場合(E_OBJ) # ※ inhno に対応する intno がない場合には,このチェックを行わない # → このチェックは,割込みサービスルーチン側で行う ・inhatr が(TA_NULL)でない場合(E_RSATR) ※ ターゲット依存部で割込みハンドラ属性を追加可(TARGET_INHATR) ※ TA_NONKERNEL を使う場合には,TARGET_INHATR に設定する ・カーネル管理に固定されている inhno に対して,inhatr に TA_NONKERNEL が  指定されている場合(E_RSATR) ・カーネル管理外に固定されている inhno に対して,inhatr に TA_NONKERNEL が  指定されていない場合(E_RSATR) ・inhno に対応する intno に対する CFG_INT がない場合(E_OBJ) ※ inhno に対応する intno がない場合には,このチェックを行わない ・inhatr に TA_NONKERNEL が指定されておらず,inhno に対応する intno に対して  CFG_INT で設定された割込み優先度が TMIN_INTPRI よりも小さい場合(E_OBJ) ※ inhno に対応する intno がない場合には,このチェックを行わない ・inhatr に TA_NONKERNEL が指定されており,inhno に対応する intno に対して  CFG_INT で設定された割込み優先度が TMIN_INTPRI 以上である場合(E_OBJ) ※ inhno に対応する intno がない場合には,このチェックを行わない *必要に応じてターゲット依存部で検出するもの ・inthdrがプログラムの開始番地として正しくない場合(E_PAR) (7) CPU例外に関する定義 CPU例外に関して生成する情報は,ターゲット毎に定めることができる. ターゲット毎に定めない場合には,以下で述べるターゲットに依存しない標準的な 情報を生成する.ターゲット毎に定める場合には,これらの情報は生成されない. システムコンフィギュレーションファイル中に,CPU例外ハンドラを定義する 静的API「DEF_EXC」が含まれている場合に,CPU例外ハンドラに関する定義を 生成する. 以下では,システムコンフィギュレーションファイルに次の静的APIが含まれ ている時に生成すべき情報について述べる. DEF_EXC(excno, { excatr, exchdr }); (7-1) 定義するCPU例外ハンドラの数 定義するCPU例外ハンドラの数をマクロ定義するプリプロセッサディレクティ ブ(#define)を生成する.また,その値を持つ変数の定義を生成する.具体 的には,次のような行を生成する. #define TNUM_EXCNO <定義するCPU例外ハンドラの数> const uint_t _kernel_tnum_excno = TNUM_EXCNO; (7-2) CPU例外ハンドラの出入口処理 定義するCPU例外ハンドラ毎に,CPU例外ハンドラの出入口処理ルーチンを生成 する.具体的には,次のような行を生成する. EXCHDR_ENTRY(excno, excno_num, exchdr) ここでexcno_numは,excno(CPU例外ハンドラ番号)を数値で表現したもので, アセンブリ言語記述に使用するためのものである. (7-3) CPU例外ハンドラ番号の初期化情報(CPU例外番号) CPU例外ハンドラ番号の初期化情報は,次の形式とする. const EXCNO _kernel_excinib_excno[TNUM_EXCNO] = {(excno)}; (7-4) CPU例外ハンドラ番号の初期化情報(CPU例外ハンドラ属性) CPU例外ハンドラ属性の初期化情報は,次の形式とする. const ATR _kernel_excinib_excatr[TNUM_EXCNO] = {(excatr)}; (7-5) CPU例外ハンドラ番号の初期化情報(CPU例外ハンドラの先頭番地) CPU例外ハンドラの先頭番地の初期化情報は,次の形式とする. const FP _kernel_excinib_entry[TNUM_EXCNO] = {(FP)(EXC_ENTRY(excno, exchdr))}; ここで EXC_ENTRY はCPU例外ハンドラ番号とCPU例外ハンドラの先頭番地を元に CPU例外ハンドラの入り口処理の名称を生成するマクロで,ターゲット依存部で 定義されている. (7-6) エラー条件 CPU例外ハンドラに関するエラー条件は次の通りである. *パス2で検出するもの ・excno が DEF_EXC に対する CPU 例外ハンドラ番号として正しくない場合(E_PAR) ・excno が DEF_EXC によって設定済みの場合(E_OBJ) ・excatr が(TA_NULL)でない場合(E_RSATR) ※ ターゲット依存部でCPU例外ハンドラ属性を追加可(TARGET_EXCATR) *必要に応じてターゲット依存部で検出するもの ・exchdrがプログラムの開始番地として正しくない場合(E_PAR) (8) 非タスクコンテキスト用のスタック領域に関する定義 先の (5-1-7) でも説明したように,SSPカーネルでは,コンフィギュレータは 共有スタック領域のみ確保する.したがって,非タスクコンテキスト専用のスタック領域は 確保しない.このため,DEF_ICS の役割は,非タスクコンテキストが用いるスタック領域の サイズを指定することのみとなる.また,DEF_ICS では先頭番地 istk として NULL のみ 受け付ける.NULL 以外の値を指定した場合はエラーとなる. ※ 共有スタック領域に関する定義は (9) を参照すること. なお,非タスクコンテキスト用のスタックサイズに関する定義は,以下のように DEF_ICS の有無にかかわらず常に生成される. (8-1) DEF_ICS がない場合 システムコンフィギュレーションファイルに,静的API「DEF_ICS」が含まれて いない場合には,次の行を生成する. #define TOPPERS_ISTKSZ DEFAULT_ISTKSZ (8-2) DEF_ICS がある場合 以下では,システムコンフィギュレーションファイルに次の静的APIが含まれて いる時に生成すべき情報について述べる. DEF_ICS({ istksz, istk }); DEF_ICS では istk としてNULLのみ指定可能であり,出力には影響しない. 非タスクコンテキスト用スタック領域のサイズ istksz により次の行を生成する. #define TOPPERS_ISTKSZ (istksz) ※共有スタック領域の定義については (9) を参照のこと. (8-3) エラー条件 非タスクコンテキスト用のスタック領域に関するエラー条件は次の通りである. *パス2で検出するもの ・静的API「DEF_ICS」が複数ある(E_OBJ) ・istkszがスタック領域のサイズとして正しくない場合(E_PAR) - istksz が 0 の場合 ・istkがスタック領域の先頭番地として正しくない場合(E_PAR) - NULL以外の値の場合 *必要に応じてターゲット依存部で検出するもの ・istkszが小さすぎる場合(E_PAR) (9) 共有スタック領域に関する定義 先の (5-1-7) にて説明したように,SSPカーネルではタスクを含む全ての処理単位が 同じスタック領域を共用するように設計されている.そして共用されるスタック領域の ことを「共有スタック領域」と呼ぶ. 共有スタック領域の定義は DEF_STK を用いて行う.そして,コンフィギュレーションでは 共有スタック領域に関する定義は,DEF_STK の有無に関わらず常に生成される. 以下では,DEF_STK の有無および与えられる引数の内容により, どのように共有スタック領域の定義が生成されるかについて説明する. (9-1) DEF_STK がない場合に生成される定義 システムコンフィギュレーションファイルに,静的API「DEF_STK」が含まれて いない場合には,次の行を生成する. static STK_T _kernel_stack[COUNT_STK_T(TOPPERS_TSTKSZ+TOPPERS_ISTKSZ)]; #define TOPPERS_STK _kernel_stack #define TOPPERS_STKSZ ROUND_STK_T(TOPPERS_TSTKSZ+TOPPERS_ISTKSZ) (9-2) DEF_STKがある場合にのみ生成される定義 以下では,システムコンフィギュレーションファイルに次の静的APIが含まれて いる時に生成すべき情報について述べる. DEF_STK({ stksz, stk }); (9-2-1) stk が NULL の場合 stk が NULL の場合には,指定されたサイズの共有スタック領域を確保し, 共有スタック領域の初期化情報を生成する. 具体的には,上記の静的APIに対して,次の行を生成する. static STK_T _kernel_stack[COUNT_STK_T(stksz)]; #define TOPPERS_STK _kernel_stack #define TOPPERS_STKSZ ROUND_STK_T(stksz) (9-2-2) stk が NULL 以外の場合 stk が NULL でない場合には,共有スタック領域の初期化情報を,次の形式で生成する. #define TOPPERS_STK (stk) #define TOPPERS_STKSZ (stksz) (9-3) DEF_STK の有無に関係なく,常に生成される定義 DEF_STKの有無によらず,次の定義を生成する. const SIZE _kernel_stksz = TOPPERS_STKSZ; STK_T *const _kernel_stk = TOPPERS_STK; #ifdef TOPPERS_ISTKPT STK_T *const _kernel_istkpt = TOPPERS_ISTKPT(TOPPERS_STK, TOPPERS_STKSZ); #endif /* TOPPERS_ISTKPT */ (9-4) エラー条件 共有スタック領域に関するエラー条件は次の通りである. *パス2で検出するもの ・静的API「DEF_STK」が複数ある(E_OBJ) ・stkszがスタック領域のサイズとして正しくない場合(E_PAR) - stksz として 0 を指定した場合 - ターゲット依存の値(CHECK_STKSZ_ALIGN)の倍数でない場合 ※ このエラーチェックは,stk が NULL でない場合にのみ行う ・警告:stksz が,(TOPPERS_TSTKSZ+TOPPERS_ISTKSZ) よりも小さい場合 ※ DEF_STK が存在し,stk が非NULLの場合. ※ マクロの値については (5-1-9), (8-1), (8-2) を参照のこと *パス3で検出するもの ・stkがスタック領域の先頭番地として正しくない場合(E_PAR) - ターゲット依存の値(CHECK_STACK_ALIGN)の倍数でない場合 - NULLの場合(ターゲット依存,CHECK_STACK_NONNULL) (10) タイムイベント管理に関する定義 タイムイベント管理に関連して,次の定義を生成する. (10-1) タイムイベントハンドラの数 生成されたタイムイベントハンドラの数をマクロ定義するプリプロセッサ ディレクテイブ(#define) を生成する. SSPカーネルではタイムイベントハンドラとして周期ハンドラおよびアラーム ハンドラのみをサポートする.そのため,CRE_CYC および CRE_ALM の合計が タイムイベントハンドラ数となる. 具体的には,次のような記述を生成する. #define TNUM_TMEVT <タイムイベントハンドラの個数> (10-2) タイムイベントブロックのサイズの定義 タイムイベントブロックの要素数を定義する変数を生成する. タイムイベントブロックの要素数はタイムイベントハンドラの数に等しい. 具体的には,次のような記述を生成する. const uint_t _kernel_tmevt_queue = <タイムイベントハンドラの個数>; (10-3) タイムイベントキューの定義 タイムイベント管理のためのキューを生成する.キューは配列として実装されて いる.キューの先頭を表現するための配列要素を確保するため,配列の要素数は 「タイムイベントハンドラの個数+1」となる. 具体的には,次のような記述を生成する. extern QUEUE tmevt_queue[TNUM_TMEVT+1]; (10-4) タイムイベント時間の定義 タイムイベントハンドラの起動時刻を格納する変数は,次の形式とする. extern EVTTIM tmevt_time[TNUM_TMEVT]; (10-5) タイムイベントのコールバックの定義 タイムイベントハンドラのコールバック関数を格納する変数は, 次の形式とする. extern CBACK tmevt_callback[TNUM_TMEVT]; (10-6) タイムイベントコールバックの引数の定義 タイムイベントハンドラのコールバック関数の引数を格納する変数は, 次の形式とする. extern uintptr_t tmevt_arg[TNUM_TMEVT]; (11) 各モジュールの初期化関数の定義 各カーネルオブジェクトの管理,割込み管理,CPU例外ハンドラ管理の各機能を 初期化関数を順に呼び出す関数(_kernel_initialize_object)を生成する.次 の3つの初期化関数を除いては,使用しない機能の初期化関数は呼び出さない. _kernel_initialize_task(); _kernel_initialize_interrupt _kernel_initialize_exception また次の初期化関数については,周期ハンドラまたはアラームハンドラを使用する 場合にのみ呼び出される. _kernel_initialize_time_event(); すべての機能を使った場合に生成される関数は次の通りである. void _kernel_initialize_object(void) { _kernel_initialize_time_event(); _kernel_initialize_task(); _kernel_initialize_interrupt(); _kernel_initialize_exception(); _kernel_initialize_cyclic(); _kernel_initialize_alarm(); _kernel_initialize_eventflag(); } (12) 初期化ルーチンの実行関数の定義 システムコンフィギュレーションファイル中に含まれる初期化ルーチンを追加 する静的API「ATT_INI」に対して,追加した初期化ルーチンを順に呼び出す関 数を生成する.具体的には, ATT_INI({ iniatr, exinf, inirtn }); という静的APIに対して, (inirtn)((intptr_t)(exinf)); を呼び出す関数を,_kernel_call_inirtnという名前で生成する.初期化ルーチ ンを呼び出す順序は,システムコンフィギュレーションファイル中での静的 APIの順序に一致させる. 例えば, ATT_INI({ TA_NULL, 0, timer_initialize }); ATT_INI({ TA_NULL, CONSOLE_PORTID, serial_initialize }); という2つの静的APIがこの順序で記述された時に生成する関数は次の通りであ る. void _kernel_call_inirtn(void) { ((INIRTN)(timer_initialize))((intptr_t)(0)); ((INIRTN)(serial_initialize))((intptr_t)(CONSOLE_PORTID)); } (12-1) エラー条件 初期化ルーチンに関するエラー条件は次の通りである. *パス2で検出するもの ・iniatrが(TA_NULL)でない場合(E_RSATR) *必要に応じてターゲット依存部で検出するもの ・inirtnがプログラムの開始番地として正しくない場合(E_PAR) (13) 終了処理ルーチンの実行関数の定義 システムコンフィギュレーションファイル中に含まれる終了処理ルーチンを追 加する静的API「ATT_TER」に対して,追加した終了処理ルーチンを順に呼び出 す関数を生成する.具体的には, ATT_TER({ teratr, exinf, terrtn }); という静的APIに対して, (terrtn)((intptr_t)(exinf)); を呼び出す関数を,_kernel_call_terrtnという名前で生成する.終了処理ルー チンを呼び出す順序は,システムコンフィギュレーションファイル中での静的 APIの逆順に一致させる. 例えば, ATT_TER({ TA_NULL, 0, timer_terminate }); ATT_TER({ TA_NULL, CONSOLE_PORTID, serial_terminate }); という2つの静的APIがこの順序で記述された時に生成する関数は次の通りであ る. void _kernel_call_terrtn(void) { ((TERRTN)(serial_terminate))((intptr_t)(CONSOLE_PORTID)); ((TERRTN)(timer_terminate))((intptr_t)(0)); } (13-1) エラー条件 終了処理ルーチンに関するエラー条件は次の通りである. *パス2で検出するもの ・teratrが(TA_NULL)でない場合(E_RSATR) *必要に応じてターゲット依存部で検出するもの ・terrtnがプログラムの開始番地として正しくない場合(E_PAR) 以上