SSブログ
Smalight OS ブログトップ
- | 次の10件

Smalight OSを使おう 8タスク目 タスク管理を使う2 [Smalight OS]

この記事は、某SNSで書いているものを転載しています。
今回は、前回やり残したタスク管理機構の「tslp_tsk」です。μITRON仕様にも、同じサービスコールが存在します。

しかしこのサービスコールは既にサンプルプログラムで何度も登場しているので、使い方に付いては既に把握して居らっしゃるかと思います。

このサービスコールは、その名前をご覧頂ければ判る様に、自タスクを待ち状態に遷移する「slp_tsk」に、時間管理機能を付加したもので、先頭の"t"が時間管理機能を表しています。
引数は単位をmsとした相対時間であり、符号付き32bitで表現されますので、最大は0x7fffffffとなります。しかし前から思うのですが、現在のシステムタイマーの値からの相対時間なので、ロールオーバーしちゃったらどうなるんでしょうかね。

動作を説明しますと、このサービスコールを発行することで、自タスクをRUNからWAITに遷移させます。
基本的に「slp_tsk」である事から、別のタスク等から起床要求を受けると、READYに遷移してしまいます。
この辺がμITRON仕様の「dly_tsk」と異なる所で、タスクがこのサービスコールから帰って来た時、その要因をリザルトコードで調べる処理が、場合に拠っては必要となります。
また、他タスク等からの起床要求を受けなかった時は、引数の相対時間が経過した後のシステムタイマーの更新タイミングで起床されます。

※「dly_tsk」は、自タスクの実行を引数の時間だけ遅延させ、時間が来たらREADYに遷移する。

リザルトコードとして取り得る値は、
E_OK:正常終了。起床要求を受けて待ち状態が解除された時、このリザルトコードが返ります。
E_TMOUT:ポーリング失敗(引数に TMO_POL(=0) を設定)、またはタイムアウト(相対時間が経過)した時、このリザルトコードが返ります。

ではサンプルプログラムを作成して、実際に体験してみましょう。
コンパイラの設定は、
  SYSTIMEを有効
config.cの設定は、
  tsk01:優先度1、起動時READY
  tsk02:優先度2、起動時READY
  SYSTIM_CYCLIC_TIMは10
ユーザー初期化関数uinitの設定は、
  システムクロック10MHz
  Raタイマー割り込み周期10ms

/***********************************/
/*** reset process               ***/
/***********************************/
void uinit(void)
{
  /* initialize */
  pd2 = 0xff;
  /* Processor clock init  */
  init_clock();		
  /* systime initialize  */
  systim_init();
  /* cyclic timer initialze  */
  init_timerRA();
}

tsk01、tsk02

void tsk01( void )
{
  W ercd;

  ercd = tslp_tsk( 1000 );

  if( ercd == E_OK )  /*起床要求を受けた時*/
    p2_0 = 0;  /*ここにブレークポイントを設定*/
  else  /*タイムアウトした時*/
    p2_0 = 1;  /*ここにブレークポイントを設定*/

  slp_tsk() ;
}

void tsk02( void )
{
#if 1
#else
  wup_tsk( TID_1 );
#endif
  while( 1 ) ;
}


オンチップデバッカーを起動し、tsk02の#if~#else文の条件を変更して、tsk01のブレークポイントのどちらで停止するか試してみて下さい。

まずtsk01が先に起動し、tslp_tskで時間を設定した上で、WAITに遷移しています。

次にtsk01がWAITに遷移した為に,tsk02がREADYからRUNに遷移し、実行を始めます。
この時#if 1ならば、何もせずに無限ループに入ります。
tsk01は何処からも起床要求を受けなかったので、タイムアウトでtslp_tskから抜けて来ます。
その結果、条件式のelse側の処理が行われる事となります。

#if 0とした時は、tsk02はtsk01の起床要求を発行します。
そうするとtsk01はWAITからREADYに遷移しますが、サービスコールの中で実行中のタスク(tsk02)と優先度が比較され、当然tsk01の方が優先度が高い事から、実行中のタスクが切り替えられるプリエンプトが発生し、その時のリザルトコードからE_OK側の処理が行われます。

やはりこれですね。(ループ中)

ITRONプログラミング入門―H8マイコンとHOSで始める組み込み開発

ITRONプログラミング入門―H8マイコンとHOSで始める組み込み開発

  • 作者: 浜原 和明
  • 出版社/メーカー: オーム社
  • 発売日: 2005/04/25
  • メディア: 単行本


Smalight OSを使おう 7タスク目 タスク管理を使う1 [Smalight OS]

※sus_tskは、引数にタスクID番号を取る事から、他タスクを強制的にSUSPENDさせるのが普通の使い方だと思いますが、ここでは引数に自タスクIDを使って、自分自身をSUSPENDに遷移させて見せています。

※この文章を読んで貰えれば判るかと思いますが、よくsmalight osの特徴として、小さく軽くて、更に簡単と言う文章を見受ける事ができますが、それはどうかと思います。つまり小さく軽く作る事の見返りとしてOS内部のチェックが甘くなり、と言うよりプログラマーのスキルに依存する事が前提となってしまっており、その辺をよく理解して使わないと、とんでもないバグを作り込む事になるかもしれません。 また、タスク間通信機能が一切無いのは、ちょっとやり難いなあ。イベントフラグで待ち解除時のパターンを戻り値として取得可能。 小さくて軽いと言うのは同意しますが。

この記事は、某SNSで書いているものを転載しています。
今回は最も基本的なサービスコールであるタスク管理機構を使ってみます。

前にsmalight osにおけるタスクの状態遷移を示しましたが、このタスク管理機構が提供する機能は、直接的に(自または他)タスクに対して状態の遷移を起させる物です。

サービスコールの動作を解説します。
まずはタスク部(タスクの中)から発行できるサービスコールです。

 自タスクをWAITに遷移させるには「slp_tsk」
 WAITのタスクをREADYに遷移させるには「wup_tsk」
 READYのタスクをSUSPENDに遷移させるには「sus_tsk」
 WAITのタスクをWAIT+SUSPENDに遷移させるには「sus_tsk」
 自タスクをSUSPENDに遷移させるには「sus_tsk」
 SUSPENDのタスクをREADYに遷移させるには「rsm_tsk」
 WAIT+SUSPENDのタスクをWAITに遷移させるには「rsm_tsk」
 WAIT+SUSPENDのタスクをSUSPENDに遷移させるには「wup_tsk」
 ローテーションタスクの順番の入れ替えを行う「rot_rdq」

※「sus_tsk」を実行中のタスクから自タスクIDを引数に使用する時には注意が必要です。
もし実行中のタスクが一時的に割り込みを禁止した状態で「sus_tsk」を発行してしまった時、それ以降割り込み禁止状態が継続する事となり、必要なディスパッチが発生しなくなる可能性が有ります。
本来はSUSPENDに入らず、エラーコードを返すべきかと思いますが、smalight osでは、OS自身でこのチェックを行う事は無く、プログラマーの責任となっています。

次いでOS管理下の割り込みハンドラから発行できるサービスコールです。

 WAITのタスクをREADYに遷移させるには「iwup_tsk」
 READYのタスクをSUSPENDに遷移させるには「isus_tsk」
 WAITのタスクをWAIT+SUSPENDに遷移させるには「isus_tsk」
 SUSPENDのタスクをREADYに遷移させるには「irsm_tsk」
 WAIT+SUSPENDのタスクをWAITに遷移させるには「irsm_tsk」
 WAIT+SUSPENDのタスクをSUSPENDに遷移させるには「iwup_tsk」
 ローテーションタスクの順番の入れ替えを行う「irot_rdq」

まずサービスコール名を見れば判る様に、同じ機能のサービスコールの先頭に"i"を付ける事で、割り込みハンドラで使用できるサービスコールである事を示しています。
タスク部から発行できるサービスコールとの違いは、割り込みの一番最後にディスパッチを行う為に、サービスコール内部ではディスパッチを行っていない点です。

※基本的に、割り込みハンドラの中から利用できるサービスコールには、自ら待ち状態に入るサービスコールを利用する事はできません。それは、割り込みの中で無限ループに入るのと同じだからです。
しかしsmalight osの場合はサービスコール内でチェックしませんので、この点もプログラマが注意する必要が有ります。

では、自タスクをWAITに遷移させる「slp_tsk」と、SUSPENDに遷移させる「sus_tsk」と、他タスクを起床させる「wup_tsk」と、SUSPENDを解除する「rsm_tsk」をやってみます。
今回のサンプルプログラムでは、tsk01とtsk02のみしか使用しませんし、システムタイマー、イベントフラグ、セマフォ等の時間管理機構やカーネルオブジェクトも使用しませんので、コンパイル時の定義や、config.cの要所、要所を適宜合わせて置いてください。

tsk01は優先度1、tsk02は優先度2として、またtsk02はWAITの状態で起動しますので、config.cを以下の様に編集します。

/*------ TCB Status Init -------------------------------------*/
/*
 * Task status Init : CTCB_ST_RDY | CTCB_ST_SLP | ...
 *                   ("slos.h" Refer to for details.)
 */
const UB knl_tcbStInit[KNL_TCB_NUM] = {
  CTCB_ST_RDY, /* TCB1   */
  CTCB_ST_SLP, /* TCB2   */
};


タスクはそれぞれ以下の様に記述します。

#define TID_1 1
#define TID_2 2

void tsk01( void )
{
  int i;

  for( i = 0; i < 2; i++ )
  {
    wup_tsk( TID_2 );
  }
  sus_tsk( TID_1 );  /*ここにブレークポイントを設定*/
  slp_tsk();  /*ここにブレークポイントを設定*/
}

void tsk02( void )
{
  while( 1 )
  {
    rsm_tsk( TID_1 );  /*ここにブレークポイントを設定*/
    slp_tsk();  /*ここにブレークポイントを設定*/
    p2_0 ^= 1;  /*ここにブレークポイントを設定*/
  }
}


さて、今回のサンプルの確認には、オンチップデバッカーを使用します。
コメントで「ここにブレークポイントを設定」と書かれた位置にブレークポイントを設定します。

それでは、「リセット後実行」でプログラムスタートしますと、まず最初に優先度1で起動したtsk01が、2回の「wup_tsk」の発行を行って、「sus_tsk」の位置で停止します。この「sus_tsk」は自らに向けて発行しているので、tsk01はこれ以降SUSPENDに遷移します。

この状態から「実行」を行うと、次はtsk02の「rsm_tsk」の位置で停止します。これは、tsk02はWAITで起動したのですが、tsk01の「wup_tsk」の発行によってREADYに遷移し、またtsk01が自ら待ち状態に入った為に、tsk02がRUNに遷移し、ここまでやって来たのです。

この状態から「実行」を行うと、次はtsk01の「slp_tsk」の位置で停止します。これはtsk02の「rsm_tsk」の発行でtsk01がSUSPENDからREADYに遷移し、さらに優先度が比較された時にtsk01の方が優先度が高かった為に、実行中のタスクが切り替わるプリエンプトが行われた為です。
しかしこれ以降、tsk01は自らWAITに遷移します。

この状態から「実行」を行うと、次はtsk02の「slp_tsk」の位置で停止します。勿論これはtsk01がWAITに遷移した事による、tsk02のREADYからRUNへの遷移が行われた為です。

この状態から「実行」を行うと、tsk02は自らWAITに遷移します。そして次の処理の「p2_0 ^= 1;」には決して到達しません。

μITRONと異なり、smalight osの場合、起床要求はキューイングされません。wup_tskを発行した場合、現在WAIT状態のタスクに対してのみ有効で(rsm_tskの場合はSUSPEND)、READY状態になったタスクには適用されませんので、tsk01で2回「wup_tsk」を発行したとしても、最初の一回しか効かなかったのです。

やっぱりC言語の開発スキルをアップしたいと言う事で、どうぞ!。

H8マイコン+C言語効率アップテクニック

H8マイコン+C言語効率アップテクニック

  • 作者: 鹿取 祐二
  • 出版社/メーカー: オーム社
  • 発売日: 2004/11
  • メディア: 単行本


Smalight OSを使おう 6タスク目 割り込みの使用 [Smalight OS]

※入手したマニュアル見ながら書いていますが、ちょっと解釈に自信が無い。あまり信用しない様に。

この記事は、某SNSで書いているものを転載しています。

R8Cの割り込みは、優先度別の多重割り込みを可能としていますので、この割り込みを活用すれば、実際の所OSなんぞ使用しなくとも、綺麗にシステムを構築できるかもしれませんし、場合によってはそちらの方がレスポンスの早いシステムとなる可能性が有ります。

しかしそれで話が終わってしまってはあれなので、smalight os上での割り込みの使用に付いて書いてみたいと思います。
なおルネサス北セミのサンプルマニュアルを落とされた場合、このマニュアルはH8300Hシリーズ用なので、ハードウエア上の割り込みレベルは2までしかなく、R8Cと話が一部一致しないかもしれません。

さて、smalight os上で取り扱われる割り込みの種類は大別して2種類あります。
一つはOS管理下(ディスパッチを行う)の割り込みで、もう一つはOS管理外の割り込みとなります。
更にOS管理外の割り込みには、NMI等のマスク不可能な割り込みと、マスク可能だが、OSの機能を使用しなかったり、OSのオーバーヘッドが気になって使えない場合の割り込みがあります。

config.cの中には、OSの割り込みマスクレベルを設定する項目が有り、OSの管理下の割り込みの場合は、ここで設定したレベル以内で使用する事となります。

/*------ Kernel Mask -----------------------------------------*/
#define KNL_MSK_LEVEL   6 /* IPL:b6-b4 */


と言いますのは、OSがスケジューリングを行う時、一時的に割り込みを禁止しますが、全てのレベルの割り込みを禁止している訳ではなく、マスクレベル以下の割り込みを禁止しますので、その様な使い方が必要となります。

_intTimerRA:
  INTPUSH _INT_LEVEL1_stack ; 現行コンテキストの保存と、割り込みスタックへの切り替え
  JSR.A   _TimerRA_Int ; 割り込みハンドラの呼び出し
  INTPOP ; 新しいコンテキストへの切り替え
void TimerRA_Int(B mode)
{
  適当な処理;
  disp(mode); /*本来、modeの中身が0かそうでないかを確認する事が推奨されている*/
}

では、OS管理外の割り込みではどうするかと言う話ですが、マスク不可能なNMIの場合は、ディスパッチ無し割り込みの手順で利用して下さい。

_intNMI:
  PUSHM   R0,R1,R2,R3,A0,A1,SB,FB ; レジスタの保存
  STC     SP,R1
  LDC     #_NMI_stack,SP ; 割り込みスタックへの切り替え
  PUSHM   R1
  JSR.A   _intNMIFunc ; NMI処理の呼び出し
  POPM    R1
  LDC     R1,SP
  POPM    R0,R1,R2,R3,A0,A1,SB,FB ; レジスタの回復
  REIT
void intNMIFunc( void )
{
  適当な処理;
}

問題はマスク可能な割り込みの場合です。この割り込みの設定で重要なのは、必ずOSの割り込みマスクレベルより大きく設定する必要があると言う事です。

_intUART0_Rcv:
  PUSHM   R0,R1,R2,R3,A0,A1,SB,FB ; レジスタの保存
  STC     SP,R1
  LDC     #_INT_LEVEL7_stack,SP ; 割り込みスタックへの切り替え
  PUSHM   R1
  FSET    I ; 必要ならば多重割り込みの許可
  JSR.A   _UART0_Rcv_Int ; 割り込みハンドラの呼び出し
  POPM    R1
  LDC     R1,SP
  POPM    R0,R1,R2,R3,A0,A1,SB,FB ; レジスタの回復
  REIT

もしOSの割り込みマスクレベルより小さいレベルに設定したら、その割り込み中によりレベルの高いOS管理下の割り込みが発生する可能性がありますが、OS管理下の割り込みをサービスする時、OSは自分がどの状態で割り込みに入っているのか判らなくなります。
そうした場合、本来ならば割り込みの一番最後にディスパッチを発行する所、OS管理下の割り込みが終了した時点でディスパッチを発行してしまい、OS管理外の割り込みに戻って来ない可能性が有ります。

もし割り込み優先レベルを低く、しかしOSの機能を使う予定が無い時は、OS管理下の割り込み処理手順で入り、最後にディスパッチのサービスコールのみ入れて置きます。

  disp(mode);

ではOSの割り込みマスクレベルと同一の場合はどうなるか?ですが、この場合は特に上記の様な問題は発生しませんが、OS管理下の割り込みが先に行われた場合、その割り込みが完了するのを待つ事となり、元々レスポンスを求めてOS管理外の割り込みに設定したわりに待たされると言う結果になるので、やはりOSの割り込みマスクレベルより大きい方が、本来的な使い方と思います。

組み込みシステムをC言語で開発する時のバイブル

C言語でH8マイコンを使いこなす

C言語でH8マイコンを使いこなす

  • 作者: 鹿取 祐二
  • 出版社/メーカー: オーム社
  • 発売日: 2003/10
  • メディア: 単行本


某4月23日号のプログラムのパクリ [Smalight OS]

某誌の4月23日号では、タスク1でスイッチのスキャンを行い、スイッチが押されたらタスク2とタスク3に通達。
タスク2はタスク1からの通達を切欠に500ms周期でLEDを点滅させる電子サイコロ?プログラムを実行。
タスク3は、30秒間スイッチが押されなければ、二つのLEDを1秒周期で点滅させるデモプログラムを実行させる。
と言うお題目が出されていました。

んで、電子サイコロの所は凝った所でタカが知れているので、適当に作成し、お題目に沿って作成してみたのが以下のコードです(*´ー`)。
面倒だし、表示し切れないので、オリジナルではないプログラムと、コメントはあらかた削除しています。

システムタイマーの更新は10ms周期で行うので、config.cの例の所も修正してね。

そうそう、元のプログラムでは二つのタスク(tsk02、tsk03)との同期には両方共イベントフラグを使用していましたが、このプログラムではtsk02の同期にカウンタセマフォを利用する事で、tsk02が動作中も複数の新たな要求を記憶できるメリットがあります。
まあ両方共イベントフラグでやっても良いんですけれどね、その場合は1イベントしか記憶できないので、tsk02が点滅中に一回のみスイッチの入力が有効になります。
しかし、二つのタスクが一つのイベントフラグ待ちになる可能性が有るのに、クリア属性は如何なものか?とは思うけれど。

あと、折角イベントフラグ使ってタスク間の同期(通信はしていないと思うが)の解説をしているのに、何故か大域変数を使って、やっぱりそっちでも同期を取ろうとしているのも、なんだかなぁ!と思ったりして。

#include "slos.h"
#include "reg.h"
#include "sfr_r825.h"
#define TID_SW_SCAN		1	/**/
#define TID_LED_ON_OFF	2	/**/
#define TID_DEMO		3	/**/
#define FID_DEMO		1	/**/
#define SEMID_LED_ON_OFF	1	/**/
#define FLG_PTN_SW_ON	0x0001	/*待ちフラグパターン*/

void tsk01(void);
void tsk02(void);
void tsk03(void);
void TimerRA_Int(B mode);
void init_timerRA(void);

void uinit(void)
{
	init_clock();		
	init_LED();
	systim_init();
	evtflg_init();
	EVTFLG_ATTR(FID_DEMO, (EVFLG_TA_AND + EVFLG_TA_CLR + EVFLG_TA_TFIFO));
	sem_init();
	SEM_ATTR(SEMID_LED_ON_OFF, 0, SEM_TA_TFIFO);  /*セマフォの初期状態は0とする*/
	init_timerRA();
}

void tsk01( void )
{
	unsigned char old_sw;

	old_sw = p1_7;
	while( 1 )
	{
		tslp_tsk( 50 );
		if( old_sw == 0 && p1_7 == 1 )	/*立ち上がりエッジの検出*/
		{
			sig_sem( SEMID_LED_ON_OFF );
			set_flg( FID_DEMO, FLG_PTN_SW_ON );
		}
		old_sw = p1_7;
	}
}

void tsk02( void )
{
	int i;
	W tim;
	static const int loop_pattern[] =
	{ 1,3,5,2,4,6,3 };

	while( 1 )
	{
		wai_sem( SEMID_LED_ON_OFF );	/*カウンタセマフォとして利用する*/
		tim = get_tim();	/*システムタイマーの値を取得*/
		for( i = loop_pattern[ tim % 7 ]; i > 0; i-- )
		{
			p2_0 = 0;	/*LED点灯*/
			tslp_tsk( 250 );
			p2_0 = 1;	/*LED消灯*/
			tslp_tsk( 250 );
		}
	}
}

void tsk03(void)
{
	W	ercd;
	int count = 0;

	while( 1 )
	{
		ercd = twai_flg( FID_DEMO, FLG_PTN_SW_ON, 500 );
		if( ercd == E_TMOUT )	/*タイムアウトした時*/
		{
			if( ++count >= 60 )	/*その数を数え、60(30秒)を超える時、LEDの点滅を開始する。*/
			{
				count = 60;
				p2_0 ^= 1;	/*LED点滅*/
				p2_1 = p2_0;	/*LED点滅*/
			}
		}
		else
		{
			count = 0;
		}
	}
}

void init_timerRA(void)
{
  /*削除*/
    trapre = 250 - 1;	/* Counts an internal count source */
    tra = 50 - 1;		/* Counts an underflow of the Timer RA prescaler register */ 
  /*削除*/
}

  /*以下、大体同じ*/


Smalight OSを使おう 5タスク目 スタックの設定 [Smalight OS]

久しぶりにオフロードバイクのレースに出て、思いっきり転倒した際に、ハンドルのバーエンドが脇腹に突き刺さって、、、うー脇腹がいてえ。やっぱりアバラいっちゃっているかな。

それはさて置き。

この記事は、某SNSで書いているものを転載しています。

HOS本でも書きましたが、結局の所組み込みマイコンではスタックの設定から逃れられない訳です。
その為、smalight osのマニュアルにもきっちりスタックの使用サイズについて書かれています。

タスクスタックサイズの算出
タスクで必要なスタックサイズは、OSが使用する40byteと、タスクの中で必要とされる最大のスタックサイズを加算した値となります。
※E8エミュレータを使用した場合、このエミュレータ用に更に8byte必要となる。
OSが使用するスタックの内、20byteはRUN状態以外の状態の時のコンテキスト保存用に使用され、20byteは割り込みのエントリ時に必要とされます。
また、タスク自身が使用するサイズは、HEWのビルド時のコンパイルオプションで-finfoを有効にしておくとスタック情報が生成されます。
今最も新しい評価版ソフトウエアのバージョンは、ツールチェインバージョンで言えば5.43.00ですが、このバージョンからはCall Walkerで読み込めるようになりました。
http://japan.renesas.com/fmwk.jsp?cnt=evaluation_software_mid_level_landing.jsp&fp=/products/tools/evaluation_software/

Call Walkerで生成オブジェクトのなんとか.sniファイルをロードすれば、各関数の使用スタックサイズが一発で判りますので、このサイズを使用して下さい。
例えばtsk01では関数のネストを含めて11byte使用していますので、12byteとし、それにOS側で必要とする40byte( + E8エミュレータの8byte)で、計52byte(60byte)となります。

ディスパッチ無し割り込みスタックサイズの算出
ディスパッチ無し割り込みを使用するのは、NMI割り込みと、OS管理外の割り込みを使用する時です。割り込み中にOSの機能を使いたい時は、ディスパッチ有り割り込みを使用してください。
割り込みエントリでは、スタックの切り替えを行うまでは、現行のコンテキストのスタックを使用します。
以下はディスパッチ無し割り込みの前処理、後処理部分のサンプルですが、

; ******* _int1 *******
;                Ex)Maskable Interrupt sample(Dispatch)
_int1:
    PUSHM   R0,R1,R2,R3,A0,A1,SB,FB
    STC     SP,R1
    LDC     #_INT1stack,SP    ; change INTstack
    PUSHM   R1
    FSET    I                 ; Multiplex interrupt Enable
;
    JSR.A   _intProc1         ; call interrupt proc
;
    POPM    R1
    LDC     R1,SP
    POPM    R0,R1,R2,R3,A0,A1,SB,FB
    REIT


最初に現行のコンテキストのレジスタを保存して、その後、ディスパッチ無し割り込み専用スタックに切り替えています。
一旦多重割り込みを許可してから、intProc1は割り込み処理関数本体ですが、この関数をコールします。
終了時は、割り込みスタックから元のスタックに戻し、レジスタを復帰させて割り込み処理を終了しています。

つまりここまでで必要なスタックサイズは、スタック切り替え後の処理で必要となるサイズ+多重割り込みを許可する場合は、その多重割り込みのスタックに切り替わるまでで使用するスタックサイズと言う事が出来ます。

付属のサンプルではディスパッチ無し割り込みは使用されていないので、この割り込みのスタックサイズは0ですが、勿論割り込みを使用する場合は、この例ならintProc1で使用するサイズ+多重割り込みに入るまでの20byte + 5byte( + E8エミュレータの8byte)として下さい。

なお、多重割り込みを使用する時は、割り込みの優先レベル毎に割り込みスタックを用意する必要が有ります。

ディスパッチ有り割り込みスタックサイズの算出
ディスパッチ有り割り込みのエントリ部分も無しの場合とほぼ同じですが、若干異なる部分も有ります。
以下はディスパッチ有り割り込みの前処理、後処理部分のサンプルですが、

; ******* _int2 *******
;                Ex)Maskable Interrupt sample(Dispatch)
_int2:
    INTPUSH _INT2stack        ; register save
    JSR.A   $intProc2         ; call interrupt proc
    INTPOP                    ; register load

; ---------------------------------------
;         MACRO
; ---------------------------------------
INTPUSH     .MACRO  wksp
     PUSHM   R0,R1,R2,R3,A0,A1,SB,FB
     STC     SP,R1
     LDC     #wksp,SP     ; change INTstack
     PUSHM   R1
     JSR.A   $knl_spSave
     MOV.B   R0L,R1L
     FSET    I            ; Multiplex interrupt Enable
     .ENDM

INTPOP      .MACRO
    POPM    R1
    LDC     R1,SP
    POPM    R0,R1,R2,R3,A0,A1,SB,FB
    REIT
    .ENDM


まあ途中がマクロになっていますが、ほぼ同じ様ですね。
ただマクロの中のknl_spSaveが何を行っているのか具体的な説明を見つけられなかったので判らないのですが、基本的には割り込みスタックに切り替え後、多重割り込みの許可を行い、関数intProc2を呼んでいます。
つまり、ディスパッチ無し割り込みと同じ様に計算し、適切なサイズを設定して下さい。と思ったのですが、E8エミュレータのメモリダンプを見てみると、割り込みスタックを35byte程度使った形跡が見られます。
intTimが6byte、スタック切り替え後の処理で5byte、エミュレータが8byte使ったとしても全部で19byteと20byteには達していないので、残りの16byteは一体何処で使用したのか?。取りあえず少し多めに、40byte以上辺りで設定してみてください。

なお付属サンプルではタイマー割り込みのみ使用し、その割り込みへのエントリーを別に用意しています。

;  ********** USER TIMER INT **********
;
_tim:
	INTPUSH _TIMstack		; register save
	JSR.A	$intTim		; call interrupt proc
	INTPOP					; register restore


しかし何故かsect30.iniの中で4byteしかスタックを確保しておらず、

	.blkb 04h
_TIMstack:


これは戴けないですね。まあ上の_INT2stackが未使用な為に助かっている訳ですが。


Smalight OSを使おう 4タスク目 タスクの状態遷移 [Smalight OS]

この記事は、某SNSで書いているものを転載しています。

前回サンプルマニュアルを取得してもらったかと思いますが、その中の「図2-2 タスクの状態遷移図」を見て下さい。
状態は、READY、RUN、WAIT、WAIT-SUSPEND、SUSPENDが有りますが、これは勿論ITRON仕様の
READY、RUNNING、WAITING、WAITING-SUSPENDED、SUSPENDEDに該当します。(状態を表す言葉なので、ITRON仕様の記述の方が用語としては正しい。SUSPENDは、するのではなくされる側なのでSUSPENDED、走っているのでRUNNING等ね。)

更に大きな違いはITRON仕様には存在するDORMANTが無い点です。DORMANTは、タスク生成情報は存在するが、カーネル管理下には無い状態で、それはつまりカーネルにとっては存在しないタスクとする事が出来ます。
但し何時でもact_tskやsta_tskでカーネルの管理下に置かれ、READYやRUNNING状態になる事が出来ます。

DORMANTとWAITを比較しますと、WAITでは必要ならばスケジューラの中で該当タスクの管理情報が読み込まれ、WAITを継続するのか、READYに遷移するのか判断されます。
しかしDORMANTであれば、存在しないタスクの管理情報は検索されず、結果その分だけカーネルの動作が軽くなります。

なぜDORMANTを採用しなかったのか?、勿論実装上の問題だと思いますが、その分だけペナルティにもなる事をご承知ください。まあ実装する時に天秤に掛けて、こちらで良いや!と言う事になったのだと思いますが。

さてsmalight osでは、オブジェクトは全て静的に生成されます。それはconfig.cの中で生成情報を記述していました。
前回の生成情報の記述では、タスクの初期状態として全部のタスクをREADYに設定していました。
以下の記述のところです。

/*------ TCB Status Init -------------------------------------*/
/*
 * Task status Init : CTCB_ST_RDY | CTCB_ST_SLP | ...
 *                   ("slos.h" Refer to for details.)
 */
const UB knl_tcbStInit[KNL_TCB_NUM] = {
  CTCB_ST_RDY,  /* TCB1   */
  CTCB_ST_RDY,  /* TCB2   */
  CTCB_ST_RDY,  /* TCB3   */
};


つまりこの配列の初期化次第で、タスクの起動時の状態を制御できます。但し、起動時に取りえる状態はREADYとWAITだけです。
例えば3番目のタスクのみWAITで起動したい時は以下の様に記述すれば良いのです。

/*------ TCB Status Init -------------------------------------*/
/*
 * Task status Init : CTCB_ST_RDY | CTCB_ST_SLP | ...
 *                   ("slos.h" Refer to for details.)
 */
const UB knl_tcbStInit[KNL_TCB_NUM] = {
  CTCB_ST_RDY,  /* TCB1   */
  CTCB_ST_RDY,  /* TCB2   */
  CTCB_ST_SLP,  /* TCB3   */
};

タスクの終了
ITRON仕様では、何時でも任意のタイミングで自タスクを終了させたり、他タスクを終了させたり出来ます。つまりDORMANTに移行します。しかしDORMANTが存在しないsmalight osでは終了する事ができません。
例えばITRON仕様ならタスク実行関数の記述で以下の様に記述が出来ます。

void tsk01( VP_INT exinf  )
{
  必要な処理;
}


もう少しお行儀良く書くなら、

void tsk01( VP_INT exinf  )
{
  必要な処理;
  ext_tsk();
}


どちらも同じ動作をし、関数から戻ってきた段階でDORMANTとなります。
しかしsmalight osではこの記述は禁物です。必ず無限ループとするか、最終的にはWAITに遷移させます。
無限ループの場合

void tsk01( VP_INT exinf  )
{
  while( 1 )
  {
    必要な処理;
  }
}


WAITに遷移の場合

void tsk01( VP_INT exinf  )
{
  必要な処理;
  slp_tsk();
}

そうそう、勿論今回もお勧め本ね。

リアルタイムOSと組み込み技術の基礎―実践μITRONプログラミング

リアルタイムOSと組み込み技術の基礎―実践μITRONプログラミング

  • 作者: 高田 広章, 宿口 雅弘, 岸田 昌巳, 南角 茂樹
  • 出版社/メーカー: CQ出版
  • 発売日: 2004/02
  • メディア: 単行本


Smalight OSを使おう 3タスク目 タスクの生成 [Smalight OS]

この記事は、某SNSで書いているものを転載しています。

どうにもこうにも優先度の設定方法を、マニュアルの中にその具体的な記述を見つける事が出来なかったので、一部こちらの想像で語っています。あまり信用しないように。
※完全版のマニュアルは、製品版なり、評価版なりのsmalight osを入手しないと手に入れられないかもしれない。
以下のリンク先からゲストユーザーで入って、サンプル版のマニュアルを取得して下さい。
http://www.kitasemi.renesas.com/outside/cgi-bin/esdd/smadl.cgi?user=guest

「図2-3 タスクのスケジューリング」はsmalight osのスケジューリングの解説していますが、これを見る限り優先度タスクはそれぞれの優先度に1つ。優先度タスクの下に同一優先度のローテーションタスクがキューに繋がれて存在している形となっています。

μITRONの場合は、各優先度毎にREADY(実行待ちのタスク)キューが存在し、その中でローテーションが行われます。つまり一つの優先度に複数のタスクが存在しても構わないのです。それと比較して優先度別には1つのタスクしか存在し得ないsmalight osは、随分割り切った設計と言える部分です。

この事を踏まえてsmalight osのタスク優先度の設定なのですが、μITRONの場合は、ユーザーが任意のタスクの優先度を、例えばコンフィギュレーションファイルの中で設定しますが、どうもはっきりとは言えないのですが、このsmalight osはタスク生成情報の記述の仕方で優先度やタスクIDが決まってしまう様な感じです。

例えばtsk01、tsk02、tsk03をそれぞれ優先度1、2、3とするなら、config.cの中のタスク生成情報の記述順序もこの順番で記述します。
また3つのタスクをプライオリティタスクとして登録するには、やはりconfig.cのKNL_TCB_PRI_NUM定義を3以上、KNL_TCB_NUM以下に設定します。
タスクがこの3つしか存在しないなら、例として

/*------ TCB Number ------------------------------------------*/
#define KNL_TCB_NUM 3
#ifdef KNL_BB_PRIORITY
#define KNL_TCB_PRI_NUM 3
#endif

/*------ TCB Stack Size --------------------------------------*/
#define TCB1_SIZE 0x80u
#define TCB2_SIZE 0x80u
#define TCB3_SIZE 0x80u


H tcb_stack1[(TCB1_SIZE/sizeof(H))];
H tcb_stack2[(TCB2_SIZE/sizeof(H))];
H tcb_stack3[(TCB3_SIZE/sizeof(H))];
/*------ TCB Stack addr --------------------------------------*/
const TCBSP knl_tcbSpInit[KNL_TCB_NUM] = {
  { (H *)&tcb_stack1[TCB1_SIZE/sizeof(H)] }, /* TCB1 */
  { (H *)&tcb_stack2[TCB2_SIZE/sizeof(H)] }, /* TCB2 */
  { (H *)&tcb_stack3[TCB3_SIZE/sizeof(H)] }, /* TCB3 */
};

/*------ TCB Start addr Init ---------------------------------*/
/*** Refer to exterior ***/
extern void tsk01(void); /* TCB1 */
extern void tsk02(void); /* TCB2 */
extern void tsk03(void); /* TCB3 */
/*** Table for start addr init ***/

const TCBADDR knl_tcbAddrInit[KNL_TCB_NUM] = {
  { tsk01 }, /* TCB1 */
  { tsk02 }, /* TCB2 */
  { tsk03 }, /* TCB3 */
};

/*------ TCB Status Init -------------------------------------*/
/*
* Task status Init : CTCB_ST_RDY | CTCB_ST_SLP | ...
* ("slos.h" Refer to for details.)
*/
const UB knl_tcbStInit[KNL_TCB_NUM] = {
  CTCB_ST_RDY, /* TCB1 */
  CTCB_ST_RDY, /* TCB2 */
  CTCB_ST_RDY, /* TCB3 */
};


とするようです。

ではローテーションタスクへの登録方法ですが、先に出た定義のKNL_TCB_NUMで定義された全タスク数からKNL_TCB_PRI_NUMを引き算した数がローテーションタスクとして扱われるタスクの数であり、プライオリティタスクの生成情報の次にローテーションタスクの生成情報を記述する事となるかと思います。

例えばtsk01のみプライオリティタスクとし、残りのtsk02、tsk03をローテーションタスクとするなら、

/*------ TCB Number ------------------------------------------*/
#define KNL_TCB_NUM 3
#ifdef KNL_BB_PRIORITY
#define KNL_TCB_PRI_NUM 1
#endif

/*------ TCB Stack Size --------------------------------------*/
#define TCB1_SIZE 0x80u
#define TCB2_SIZE 0x80u
#define TCB3_SIZE 0x80u


H tcb_stack1[(TCB1_SIZE/sizeof(H))];
H tcb_stack2[(TCB2_SIZE/sizeof(H))];
H tcb_stack3[(TCB3_SIZE/sizeof(H))];
/*------ TCB Stack addr --------------------------------------*/
const TCBSP knl_tcbSpInit[KNL_TCB_NUM] = {
  { (H *)&tcb_stack1[TCB1_SIZE/sizeof(H)] }, /* TCB1 */
  { (H *)&tcb_stack2[TCB2_SIZE/sizeof(H)] }, /* TCB2 */
  { (H *)&tcb_stack3[TCB3_SIZE/sizeof(H)] }, /* TCB3 */
};

/*------ TCB Start addr Init ---------------------------------*/
/*** Refer to exterior ***/
extern void tsk01(void); /* TCB1 */
extern void tsk02(void); /* TCB2 */
extern void tsk03(void); /* TCB3 */
/*** Table for start addr init ***/

const TCBADDR knl_tcbAddrInit[KNL_TCB_NUM] = {
  { tsk01 }, /* TCB1 */
  { tsk02 }, /* TCB2 */
  { tsk03 }, /* TCB3 */
};

/*------ TCB Status Init -------------------------------------*/
/*
* Task status Init : CTCB_ST_RDY | CTCB_ST_SLP | ...
* ("slos.h" Refer to for details.)
*/
const UB knl_tcbStInit[KNL_TCB_NUM] = {
  CTCB_ST_RDY, /* TCB1 */
  CTCB_ST_RDY, /* TCB2 */
  CTCB_ST_RDY, /* TCB3 */
};


です。

しかしこの仕様はイマイチですね。ITRONの場合、μITRON4.0仕様からはこう言ったオブジェクトの生成情報はコンフィギュレータが自動的に生成する事を必須としており、ユーザーは決まった書式で必要な情報を入力するだけです。
以下はITRON4.0仕様のタスク生成静的APIの記述例。一見なんのこっちゃと思えるかもしれませんが、仕様書を読めばすぐに理解できます。

CRE_TSK(TSKID_1, {TA_HLNG, 1, Task1, 1, 256, NULL});
CRE_TSK(TSKID_2, {TA_HLNG, 1, Task2, 2, 256, NULL});
CRE_TSK(TSKID_3, {TA_HLNG, 1, Task3, 3, 256, NULL});

静的に生成できるオブジェクトの生成情報みたいな物は、容易に、しかも入力ミスを防ぐ意味でもこのコンフィギュレータの利用は有効だと思います。
何よりもコンフィギュレータの実行は、ターゲット側で行われるのではなく開発側で行われますので、コンフィギュレータが有ろうが、無かろうが、ターゲット側の負担は変わりません。

しかしsmalight osの場合は、config.c内のユーザーの配列の書き方に大きく依存してしまうので、うっかりミスが有ると、もうその時点でバグ決定です。
明らかにμITRON4.0仕様の後に開発したと思われるsmalight osでコンフィギュレータを取り入れないのは、なんとも手抜きに感じます。

smalight osを理解するには、結局のところITRON仕様も知っておく方がより理解も進むので、以下の本もどうぞ!。あのHOS本では上手く解説し切れなかった所も、上手に解説されています。(営業モード)

SuperHで学ぶμITRON仕様OS―リアルタイムOSの動作原理と実装法がわかる!

SuperHで学ぶμITRON仕様OS―リアルタイムOSの動作原理と実装法がわかる!

  • 作者: 鹿取 祐二
  • 出版社/メーカー: 電波新聞社
  • 発売日: 2005/12
  • メディア: 単行本


Smalight OSを使おう 2タスク目 システムタイマーの設定 [Smalight OS]

この記事は、某SNSで書いているものを転載しています。
※書き忘れたけれど、smalight osのシステムタイマーは符号付32bit、つまり実質31bitしかない。 これは約24日間でロールオーバーする計算だけれど、ロールオーバーした後は一体どうなるの?。 ちなみにITRONでは48bitなので9000年はロールオーバーしない。

今回は、時間管理機構を使う為のシステムタイマーの設定の解説と、LEDをボヤっと光らせる蛍アプリケーションの作成まで行います。

システムタイマーを調整して見ます。

smalight osと一緒にインストールしたワークスペースは、デフォルトで約10MHzのCPUクロックを発生します。
これはuser.cの中のinit_clockの中で設定されています。

OSで時間管理機構をサポートする場合には、タイマー割り込みで一定周期のタイミングを発生する必要が有ります。
このワークスペースでは、やはりuser.cの中のinit_timerRAでタイマーの初期化を行い、intTimが割り込みハンドラとなっています。
※ITRON仕様で言う所の「周期ハンドラ」と、smalight osでの「周期ハンドラ」は基本的に異なるので、注意が必要です。

init_timerRAの設定状況を見てみると、源振10MHzを÷8÷250÷100で50Hzに落としているのがわかります。つまり一周期20msです。

smalight-osフォルダーの中にconfig.cが有りますが、これがOSの初期化情報を書いたファイルとなっており、タスクの登録や、システムタイマーのタイムティックの設定なんかを行っています。
このファイルの中のSYSTIM_CYCLIC_TIMと言う定義名を見ると20となっています。そうです、この20と言うのはシステムタイマーの更新時に加算するべき値の事を指しています。

μITRONもそうですが時間管理の基本単位はmsで、タイマー割り込みの周期に合わせてシステムタイマーにタイマー周期を加算する事で、トータルの経過時間を合せ込んでいます。

割り込みハンドラの中でslos_cyclic_timerと言う専用の関数を呼んでいますが、この中でシステムタイマーの更新が行われていると思われます。
※HOSの様にソースコードが提供されている訳ではないので、確認できない。

ここまで読めばもうお判りかと思いますが、システムタイマーを駆動するタイマー割り込みの周期と、タイムティック毎に加算される値をいじる事で、システムタイマーの調整を行う事が出来ます。

しかしなんでタイマーの調整が必要なのでしょうか。
実はデフォルトの設定の場合、実際にタイマー処理が行われるのに最大で20msの誤差が生じます。
500ms周期とか1000ms周期でLEDを点滅させている程度ならその誤差を認識できないのですが、これから行ってみる蛍アプリケーションの場合はこの誤差は致命的となります。

tslp_tskの動きは、引数の時間周期で起床要求が発生する訳ではなく、このサービスコールが発行された時間から引数の時間(相対時間)の後で起床要求が発生します。
つまり現在のシステムタイマーが1000、引数が5だとした場合、1000+5=1005の後のシステムタイマーの更新タイミングで比較が行われます。

この処理はslos_cyclic_timerの中で行われる筈です。しかしタイマー割り込みの周期が20msならば、次の比較タイミングは1020で行われる事となり、15msの誤差が出る事となります。
いくらtslp_tsk( 1 )と書いても、実際に起床してその後の処理を再開するのは、最悪20ms後という事です。

勿論先に書いた様にその誤差が問題にならないアプリケーションでは、特にタイマーの調整を必要としないでしょう。むやみやたらと細かい周期でシステムタイマーの更新を行う事は、OSの動作を重くし、結果、十分なパフォーマンスを発揮できなくなるかもしれません。

ではLEDの明度をPWMで実現する蛍アプリケーションの場合はどうでしょうか。この場合PWMはソフトウエア的に行います。タイマーのPWM機能を利用するのが本来だと思いますが、簡易的に実現する為にソフトウエアで行います。その為、更新周期が長いと、暗い側で点滅が目で見えるようになってしまいます。やっぱり蛍なのでぼやーっと光らせたいですよね。

ではconfig.cの中の加算値であるSYSTIM_CYCLIC_TIMを1とし、
user.cのタイマーの初期化関数の一部を以下の様にしますと、

tra = 5 - 1;


1ms周期でシステムタイマーが更新される事となります。

tsk01を以下の様に改造してみます。

void tsk01(void)
{
  int on_time;

  while( 1 )
  {
    /*明るくなる方向*/
    for( on_time = 2; on_time <= 25; on_time++ )
    {
      p2_0 = 0;
      tslp_tsk( on_time );
      p2_0 = 1;
      tslp_tsk( 25 - on_time );
    }

    /*暗くなる方向*/
    for( on_time = 25; on_time >= 2; on_time-- )
    {
      p2_0 = 0;
      tslp_tsk( on_time );
      p2_0 = 1;
      tslp_tsk( 25 - on_time );
    }
  }
}
ついでにtsk02も同様の改造を、しかし微妙にタイミングをずらして動作させると、結構いい感じです。 smalight osを理解するには、結局のところITRON仕様も知っておく方がより理解も進むので、以下の本もどうぞ!。(営業モード)
μITRON4.0標準ガイドブック

μITRON4.0標準ガイドブック

  • 作者: 坂村 健
  • 出版社/メーカー: パーソナルメディア
  • 発売日: 2001/11
  • メディア: 単行本

Smalight OSを使おう 1タスク目 例題をいきなり動かす [Smalight OS]

この記事は、某SNSで書いているものを転載しています。

アットマークエレ(既に某SNSではなくなっている)内でレポートを書く事を前提に、例の日経エレクトロニクスの「組み込み速修キット」が無償配布されましたので、レポート兼ねがねsmalight osについて書いているのですが、あっちではレポートを読む人がほとんど居ない?ので、こっちにも転載する事としました。

但し、smalight os(ver.2)の評価版を手に入れるには、「組み込み速修キット」とか、前年のインタフェースのSH2基板用に配布された物位しか入らないと思われるので(昔のトラ技のH8用は256倍遅いver.1)、既に読者限定となっていますが、製品版の購入を検討されている方にもちっとは役に立つかな?。

※ちなみに今ならヤフオクで1500円~でこの基板が売りに出されていますね。

では「組み込み速修キット」を入手して既にマニュアル通りにHEWとsmalight osをインストール完了している事を前提として話を進めます。

皆さん既にsmalight osのインストール完了されましたか?。
今smalight osのマニュアルを半分位読み終わったのですが、μITRON仕様OSのサブセット版という感じで使えますね。
μITRON仕様と比較して、機能的にはかなりグレードダウン(と言うか思い切って削った)して、その代わりOSが使用するリソースを最小とした感じです。サポートするサービスコールも少ないですし。

しかし優先度ベースのスケジューリング可能な点は譲っていないので、リアルタイムOSとしての最低限は守っています。

smalight osを使う利点は、勿論OSが提供する機能を利用できる点と、タスクを機能毎に用意する事でプログラム構造を単純化できる点だと思います。

まずOSが提供する機能を利用した場合の例として、「組み込みシステム入門」の第3回の例題なんかは以下の様に記述できます。
ベースは、smalight osのインストールと一緒にワークスペースもインストールしていると思いますが、その中のuser.cのtsk01を改造してみます。

  while( 1 )
  {
    tslp_tsk( 500 );
    p2_0 ^= 1;
  }


while文の中身はたったこれだけです。
tslp_tskは、一旦タスクの実行を眠らせて、外部からの起床要求を受けるか、引数の時間(500ms)経過したら、タスクの実行を再開するサービスコールです。

機能毎に分割する例は、第4回の2個のLEDの点滅でやってみます。
えー、上のtsk01と同様に、tsk02を以下の様に改造します。

  while( 1 )
  {
    tslp_tsk( 1000 );
    p2_1 ^= 1;
  }


一つのLEDの点滅を一つの機能としている訳ですね。

smalight osを理解するには、結局のところITRON仕様も知っておく方がより理解も進むので、以下の本もどうぞ!。(営業モード)

ITRONプログラミング入門―H8マイコンとHOSで始める組み込み開発

ITRONプログラミング入門―H8マイコンとHOSで始める組み込み開発

  • 作者: 浜原 和明
  • 出版社/メーカー: オーム社
  • 発売日: 2005/04/25
  • メディア: 単行本


- | 次の10件 Smalight OS ブログトップ

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。