最終更新: 2010年2月22日(作成中)

このドキュメントは,従来のTOPPERSカーネルや他のμITRON4.0仕様準拠のカー ネルから,TOPPERS新世代カーネルに移行するための方法(またはヒント)を説 明するものである. なお,μITRON4.0仕様とTOPPERS新世代カーネル仕様の違いについては, TOPPERS新世代カーネル統合仕様書の中に【μITRON4.0仕様との関係】として記 載してある.必要に応じてそちらも参照すること.

  Migration Guide to TOPPERS New Generation Kernels
  Copyright (C) 2005-2010 by Embedded and Real-Time Systems Lab
oratory
              Graduate School of Information Science, Nagoya Un
iv., JAPAN
 上記著作権者は,以下の (1)~(3) の条件を満たす場合に限り,本ドキュメ
 ント(本ドキュメントを改変したものを含む.以下同じ)を使用・複製・改
 変・再配布(以下,利用と呼ぶ)することを無償で許諾する.
 (1) 本ドキュメントを利用する場合には,上記の著作権表示,この利用条件
     および下記の無保証規定が,そのままの形でドキュメント中に含まれて
     いること.
 (2) 本ドキュメントを改変する場合には,ドキュメントを改変した旨の記述
     を,改変後のドキュメント中に含めること.ただし,改変後のドキュメ
     ントが,TOPPERSプロジェクト指定の開発成果物である場合には,この限
     りではない.
 (3) 本ドキュメントの利用により直接的または間接的に生じるいかなる損害
     からも,上記著作権者およびTOPPERSプロジェクトを免責すること.また,
     本ドキュメントのユーザまたはエンドユーザからのいかなる理由に基づ
     く請求からも,上記著作権者およびTOPPERSプロジェクトを免責すること.
 本ドキュメントは,無保証で提供されているものである.上記著作権者およ
 びTOPPERSプロジェクトは,本ドキュメントに関して,特定の使用目的に対す
 る適合性も含めて,いかなる保証も行わない.また,本ドキュメントの利用
 により直接的または間接的に生じたいかなる損害に関しても,その責任を負
 わない.

データ型と定数の変更

TOPPERS新世代カーネル仕様では,ITRON仕様の次のデータ型を廃止している. 対応する新しいデータ型を用意しているので,単純に置き換えればよい.

廃止したデータ型

置き換えるデータ型

B

int8_t

UB

uint8_t

VB

uint8_t

H

int16_t

UH

uint16_t

VH

uint16_t

W

int32_t

UW

uint32_t

VW

uint32_t

D

int64_t

UD

uint64_t

VD

uint64_t

VP

void *

INT

int_t

UINT

uint_t

BOOL

bool_t

VP_INT

intptr_t

また,定数についても,次の通り置き換える必要がある.

廃止した定数

置き換える定数

TRUE

true

FALSE

false

なお,ITRON仕様のデータ型と定数を使いたいアプリケーションのために, ITRON仕様との互換性を保つためのitron.hを用意しているが,使用することは 推奨しない.

オブジェクト属性の変更

TOPPERS新世代カーネル仕様では,次のオブジェクト属性は,デフォルト扱いに して廃止している.

TA_HLNG

高級言語用インタフェース

TA_TFIFO

タスクの待ち行列をFIFO順に

TA_MFIFO

メッセージキューをFIFO順に

TA_WSGL

待ちタスクは1つのみ

これらのオブジェクト属性を指定している場合には,単純に削除すればよい. 削除したことにより,指定するオブジェクト属性がなくなる場合には, TA_NULLを指定する. なお,ITRON仕様のオブジェクト属性を使いたいアプリケーションのために, ITRON仕様との互換性を保つためのitron.hを用意しているが,使用することは 推奨しない.

システムコンフィギュレーションファイルの変更

TOPPERS新世代カーネル仕様では,システムコンフィギュレーションファイルに おけるC言語プリプロセッサのディレクティブの扱いが変更になっているため, システムコンフィギュレーションファイルの変更が必要になる. TOPPERS新世代カーネル仕様では,システムコンフィギュレーションファイル中 に記述できるディレクティブが,インクルードディレクティブ(#include)と 条件ディレクティブ(#if,#ifdefなど)のみとなっている.そのため,システ ムコンフィギュレーションファイルにマクロ定義ディレクティブ(#define)が 含まれている場合には,その記述を削除し,必要であれば,#includeディレク ティブでインクルードするファイル中に移動する必要がある. μITRON4.0仕様に準拠して記述されたシステムコンフィギュレーションファイ ル中の#includeディレクティブは,INCLUDEディレクティブに書き換える必要が ある.INCLUDEディレクティブでインクルードするファイルは,システムコンフィ ギュレーションファイルの一部とみなされるため,上記と同じ制限がある. 逆に,μITRON4.0仕様に準拠して記述されたシステムコンフィギュレーション ファイル中のINCLUDE静的APIは,#includeディレクティブに書き換える必要が ある.

オブジェクトIDの自動割付けへの対応

TOPPERS新世代カーネルでは,オブジェクトのID番号を自動割付けすることが基 本となっている.TOPPERS新世代カーネルを用いる際のID番号の管理方法につい ては,「TOPPERS/ASPカーネル ユーザーズマニュアル」の「11.3 オブジェクト IDの管理」の節に説明があるので,まずはこれを参照すること. 従来,ID番号を手動で割り付けており,手動で割り付けていたID番号を変更し たくない場合には,コンフィギュレータの持つID番号の割付けをファイルから 取り込む機能(--id-input-fileオプション)を用いることで,手動で割り付け たID番号を用いることができる.この場合に,手動で割り付けたID番号は,コ ンフィギュレータが—​id-input-fileオプションで取り込むファイル中にのみ記 述し,アプリケーションのソースプログラムからは,kernel_cfg.hを用いるこ とが望ましい.

ヘッダファイルの変更

μITRON4.0仕様においては,コンフィギュレータが生成する自動割付け結果ヘッ ダファイルの名称がkernel_id.hであったが,TOPPERS新世代カーネル仕様では, kernel_cfg.hに変更になっている(kernel_cfg.hの方が含まれる定義が多い). kernel_id.hをインクルードしていたアプリケーションは,kernel_cfg.hをイン クルードするように変更する. TOPPERS/JSPカーネルで,アプリケーションが用いる標準的なヘッダファイルと して用意していたt_services.hとs_services.hは,TOPPERS新世代カーネルでは 用意されていない. t_services.hをインクルードしていたアプリケーションは,それに代えて, kernel.hをインクルードするように変更する.また,必要に応じて, t_syslog.h,t_stdlib.h,syssvc/syslog.h,syssvc/serial.hをインクルード する.ただし,t_services.hに含まれているsyscallマクロと_syscallマクロは, アプリケーションによって適切なエラー処理方法は異なることから,TOPPERS新 世代カーネルでは用意されていない.使用する場合には,アプリケーションで 用意する必要がある. s_services.hをインクルードしていたアプリケーションは,それに代えて, sil.hをインクルードするように変更する.また,必要に応じて,t_syslog.hと ターゲットのハードウェア資源の定義を含むヘッダファイルをインクルードす る.

sta_tskの置き換え

TOPPERS新世代カーネルでは,タスクを起動するサービスコールとしてact_tsk をサポートしており,sta_tskをサポートしていない.act_tskとsta_tskの機能 を比較した場合,前者はタスク起動のキューイング機能を持つのに対して,後 者はタスクに起動コードを渡す機能を持つ.そのため,sta_tskをact_tskに置 き換える場合に,起動コードを渡す機能をどのように実現するかが問題となる. 起動コードを渡す機能を最も簡単に代用する方法は,起動コードを渡すための データキューを用意する方法である.タスクを起動する処理単位は,データ キューに起動コードを送信した後,act_tskによりタスクを起動する.起動され たタスクは,データキューから起動コードを受信する. タスク起動のキューイングが起こらないことが保証できる,言い換えると,タ スクを起動する時には,対象タスクは休止状態にあることが保証できる場合に は,起動コードをグローバル変数に置いて渡す方法もある.タスクを起動する 処理単位がそのグローバル変数に書くのは,タスクが休止状態の間に限られ, 起動されたタスクがそのグローバル変数を読むのは,タスクが実行できる状態 の間に限られるため,グローバル変数に対する排他制御は必要ない. ○set_timの置き換え TOPPERS新世代カーネルでは,システム時刻を設定するサービスコールである set_timが使用されることは稀であると考え,サポートしないこととした. ITRON仕様と互換のset_timとget_timが必要な場合には,下に示すコードの itron_set_timとitron_get_timで代用することができる(下のコードでは,エ ラー処理は省略している).

static SYSTIM  systim_offset = 0U;
void
itron_set_tim(const SYSTIM *p_systim)
{
    SYSTIM current_time;
    get_tim(&current_time);
    systim_offset = *p_systim - current_time;
}

void
itron_get_tim(SYSTIM *p_systim)
{
    SYSTIM current_time;
    get_tim(&current_time);
    *p_systim = systim_offset + current_time;
}

48ビットのシステム時刻

ITRON仕様準拠のカーネルでは,μITRON3.0仕様でシステム時刻を48ビットとす ることを推奨していたため(μITRON4.0仕様では推奨を定めていない),シス テム時刻が48ビットとなっているものがある. int型,long型ともに32ビットの環境で,システム時刻を48ビットに拡張するに は,カーネルを改造する方法も考えられるが,get_timのみが必要な場合には, 周期ハンドラを使って上位桁を求めておく方法がある. 具体的には,まず,上位桁を求める周期ハンドラを登録するために,システム コンフィギュレーションファイルに次の記述を含める.

CRE_CYC(CYCHDR_SYSTIM48, { TA_STA, 0, cychdr_systim48, 1 << 30, 1 << 30 });

周期ハンドラ本体およびそれを用いた48ビット版のget_timは,次のように実現 することができる.

typedef {
    uint_16   utime;   /* システム時刻の上位16ビット */
    uint_32   ltime;   /* システム時刻の下位32ビット */
} SYSTIM48;

static SYSTIM  systim_upper = 0U;

void
cychdr_systim48(intptr_t exinf)
{
    systim_upper += 1;
}

void
itron_get_tim48(SYSTIM48 *p_systim48)
{
    SYSTIM systim;
    get_tim(&systim);
    if (((systim >> 30) & 0x3U) == (systim_upper & 0x3U) {
        p_systim48->utime = (uint_16)(systim_upper >> 2);
    }
    else {
        p_systim48->utime = (uint_16)((systim_upper >> 2) + 1);
    }
    p_systim48->ltime = (uint_32) systim;
}

int型が16ビットの環境では,周期ハンドラの周期として(1 << 30)を使用する ことができないため,周期ハンドラの登録と周期ハンドラ本体を,次のように 修正する必要がある.

CRE_CYC(CYCHDR_SYSTIM48, { TA_STA, 0, cychdr_systim48, 1 << 15, 1 << 15 });

static SYSTIM  systim_upper = 0U;
static SYSTIM  systim_medium = 0U;

void
cychdr_systim48(intptr_t exinf)
{
    systim_medium += 1;
    if (systim_medium == 0U) {
        systim_upper += 1;
    }
}

周期ハンドラの非互換性

TA_PHS属性でない周期ハンドラにおいて,sta_cycを呼び出した後,最初に周期 ハンドラが起動される時刻が,μITRON4.0仕様では,sta_cycを呼び出してから 周期ハンドラの起動周期(cyctim)で指定した相対時間後となっていたが, TOPPERS新世代カーネルでは,起動位相(cycphs)で指定した相対時間後とした. CRE_CYCにおいて,TA_STA属性を指定しない場合には,cycphsにcyctimと同じ値 を指定することで,μITRON4.0仕様と同じ振舞いとなる(μITRON4.0仕様では, TA_STA属性とTA_PHS属性のいずれも指定しない場合には,cycphsは意味を持た ない). TA_STA属性を指定する場合には,カーネルの起動後,最初に周期ハンドラが起 動されるまでの相対時間と,sta_cycを呼び出してから最初に周期ハンドラが起 動されるまでの相対時間が同一でよければ,その時間をcycphsに指定すればよ い.両者が同一では不都合な場合には,μITRON4.0仕様と同じ振舞いをさせる ことはできない.代替手段としては,以下の2つが考えられる.

  1. TA_STA属性を使用せず,カーネルの起動後適切なタイミングでsta_cycを呼び出すことで,周期ハンドラを動作開始する.

  2. 周期ハンドラを2つ用いる.1つをTA_STA属性とし,sta_cycで動作を制御するのをもう片方の周期ハンドラとする.

可変長メモリプールの置き換え

アプリケーションが動的メモリ管理を用いる場合に,malloc/freeの実現に可変 長メモリプールが用いられることがあり,可変長メモリプールがサポートされ ていないことが問題となる場合がある.TOPPERS新世代カーネル仕様で可変長メ モリプールをサポートしないこととしたのは,動的メモリ管理をカーネル内で 実現するより,ライブラリとして実現する方が適切と考えたためである. そこで,ここでは,アプリケーションが用いるmalloc/freeを,オープンソース のメモリ割付けライブラリであるTLSFを用いて実現する方法について述べる. TLSFは,リアルタイムシステム向けの効率的なメモリ割付けライブラリである. TLSFのライセンス条件は,GPLとLGPLのデュアルライセンスであるが,TLSFを通 常のライブラリとして用いて実装されたプログラムは,TLSFの派生物とは見な されず,GPLが適用されないことが明記されている. TLSFは,以下のウェブサイトからダウンロードすることができる. http://www.gii.upv.es/tlsf/ (動作確認は,Version 2.4.6)のアーカイブの中から,tlsf.hとtlsf.cを, アプリケーションまたはライブラリのソースファイルの置かれたディレクトリ にコピーする. tlsf.hは動的メモリ管理を用いるアプリケーションからインクルードすべきヘッ ダファイル,tlsf.cは動的メモリ管理ライブラリの本体である.tlsf.cには以 下のようなパッチをあてる.

'patch -c tlsf.c < tlsh.patch'
*** tlsf.c.orig	2016-08-05 13:40:24.000000000 +0900
--- tlsf.c	2016-08-05 13:42:00.000000000 +0900
***************
*** 76,82 ****


  #if TLSF_USE_LOCKS
! #include "target.h"
  #else
  #define TLSF_CREATE_LOCK(_unused_)   do{}while(0)
  #define TLSF_DESTROY_LOCK(_unused_)  do{}while(0)
--- 76,88 ----


  #if TLSF_USE_LOCKS
! #include "kernel.h"
! #include "kernel_cfg.h"
! #define TLSF_MLOCK_T ID
! #define TLSF_CREATE_LOCK(lock)   (*lock = TLSF_SEM)
! #define TLSF_DESTROY_LOCK(lock)  ini_sem(*lock)
! #define TLSF_ACQUIRE_LOCK(lock)  wai_sem(*lock)
! #define TLSF_RELEASE_LOCK(lock)  sig_sem(*lock)
  #else
  #define TLSF_CREATE_LOCK(_unused_)   do{}while(0)
  #define TLSF_DESTROY_LOCK(_unused_)  do{}while(0)
***************
*** 169,176 ****

  #ifdef USE_PRINTF
  #include <stdio.h>
! # define PRINT_MSG(fmt, args...) printf(fmt, ## args)
! # define ERROR_MSG(fmt, args...) printf(fmt, ## args)
  #else
  # if !defined(PRINT_MSG)
  #  define PRINT_MSG(fmt, args...)
--- 175,184 ----

  #ifdef USE_PRINTF
  #include <stdio.h>
! #include <t_stddef.h>
! #include <t_syslog.h>
! # define PRINT_MSG(fmt, args...) syslog(LOG_ERROR, fmt, ## args)
! # define ERROR_MSG(fmt, args...) syslog(LOG_ERROR, fmt, ## args)
  #else
  # if !defined(PRINT_MSG)
  #  define PRINT_MSG(fmt, args...)

このパッチは,tlsf.cに以下の修正を加えている. * PRINT_MSGおよびERROR_MSGを,syslogを用いるように変更する. * タスク間の排他を,セマフォを用いて行う. malloc/freeを複数のタスクから呼び出す場合には,tslf.cを-DTLSF_USE_LOCKS オプションをつけてコンパイルし,システムコンフィギュレーションファイル に次の記述を追加する.

CRE_SEM(TLSF_SEM, { TA_TPRI, 1, 1 });

TLSFを使用するプログラムでは,メモリプールの領域を配列として確保する (下の例は,メモリプールのために10KBの領域を確保している).

#define MEMORY_POOL_SIZE  (TOPPERS_ROUND_SZ(10*1024, sizeof(intptr_t))
/* 10*1024の部分は,適切なサイズに変更する */
intptr_t memory_pool[MEMORY_POOL_SIZE / sizeof(intptr_t)];

次にメモリプールの初期化を行う.

init_memory_pool(MEMORY_POOL_SIZE, memory_pool);

以上により,tlsf_malloc/tlsf_freeで,malloc/freeが実現できる. なお,TLSFは,複数のメモリプールを用いる機能や,メモリプールのサイズを 拡張する機能を持つ.詳しくは,TLSFのアーカイブ中のREADMEを参照すること. 以上