FTFデザインチャレンジ向け共同開発者募集 [ColdFire V1]
http://www.freescale.co.jp/event/FTFJ/designchallenge/index.html
案件は幾つか有るのですが、一人での開発は無理そうなので共同で開発してくれる方が居られましたら、そりゃありがたい!。という事なんです。
詳細は、FTFデザインチャレンジに申し込み後、
http://micon.arrow.jp/modules/newbb/
の「秘密の相談室」で読めます。
※なおso-net blogが今日の15時から2日間アクセスできなくなりますので、リンク先はお早めに記録して置いてください。
ラウンドロビンプログラムの修正 [ColdFire V1]
FTFデザインチャレンジにチャレンジしよう! [ColdFire V1]
さて皆さん、FTFデザインチャレンジの募集が日本語で開始されました。
しかし使用するマイコンが予め決められており、それが障害となって参加を見合わせる方もいらっしゃるだろうとは思います。
そこで中国とかアメリカに負けない様に是非多数の方に参加してもらいたいので、ダイアモンドエースの無償基板頒布を始めようかと思います。(※基板のみです。チップはフリスクのアメリカサイトからサンプル要求できます。)
応募資格は、FTFデザインチャレンジにチャレンジする、またはしてみようと言う方のみ、一人一枚で5名様に頒布します。
搭載可能なマイコンは、MCF51QEシリーズに、9S08QEシリーズですね。
応募はここのコメント欄にエントリー(※決して個人情報を書かない様に!)です。
では、早い者勝ちと言う事で。
※ちびーっと設計失敗しております。大概の用途では問題にならないと思いますが。
※基板の両サイドに40pinコネクタで全pinを出しておりますので、DEMOQE128ボードの基板より使い易くなっています。
※開発にはマルチリンクBDMをお使いください。(多分、、、)
※このマイコン、開発は本当に楽です。
ああこれで日本語のデータシートが有って、DIGI-KEYでチップが購入できればなぁ!、みんな使うのに、、、。
※FTFデザインチャレンジのページを開くと、CPU使用率が100パーになるのは勘弁して欲しいぞ!。
DiamondAceでもグラフィック液晶 [ColdFire V1]
パスカルさんの所の16階調、160×128ピクセルLCDに表示してみました。オリジナルは一番下の写真です。
新型戦車のパラパラマンガなのですが、CodeWarriorのサイズ制限が64Kbyteな為、6枚の写真しか登録できていません。
なんか別の方法を考えねばネ。
tslp_tskらしき物の作成 [ColdFire V1]
とは言え、このカーネル(らしき物)はITRONの様に優先度ベースのスケジューリングを行ってもいないし、単にサービスコール名を合わせたに過ぎない物です。
本当はwup_tskやrel_waiと言ったところまで行きたかったのですが、スキルが足りなくて、全然進みませんでした。
tslp_tskは、引数にTMO型の引数を取り、単位をmsとした時間分だけタスクの実行を遅延します。
と、同様のサービスコールにdly_tskが有りますが、こちらの引数はRELTIM型で、サービスコールの動作も異なります。
tslp_tskの動作は、自タスクをRUNNINGからWAITINGに遷移させ、現在時刻からの相対時間分の時間待ちを行いますが、引数には相対時間(正の整数型)以外にTMO_POL(0に定義されている)、
TMO_FEVR(負の整数型 -1に定義されている)を取りえる事ができ、それぞれ動作が異なってきます。
また、他のタスクや非タスクコンテキスト部からの起床要求を受ける事ができ、必要ならば起床要求のキューイングを行いますが、まだそこまで作成していません。
この改造に伴い、前回のラウンドロビンからはスケジューラの構造が随分変わる事となりました。
このカーネルでは一部のサービスコールを除いて(除かれたサービスコールはマクロで実現するとか)trap命令で実現しています。
その為、サービスコールの前処理として機能コードをレジスタに代入する必要が有ります。
ちなみに機能コードはμITRON4.0仕様の機能コードを当てはめています。
#include "mytron.h"
void __slp_tsk(
UB fc, /*機能コード*/
TMO tmout /*タイムアウト時間*/
)
{
__asm {
trap #0;
};
}
"mytron.h"内では以下の様に定義されています。
void __slp_tsk( UB fc, TMO tmout );
#define tslp_tsk( tmout ) __slp_tsk( (UB)FC_TSLP_TSK, (TMO)tmout )
#define slp_tsk() __slp_tsk( (UB)FC_SLP_TSK, TMO_FEVR )
つまりtslp_tskもslp_tskもどちらも同じ関数”__slp_tsk”を呼びますが、関数コールなので自動的に引数はレジスタに代入される事となります。(※但しd0、d1、a0、a1を使ってしまうとスタック渡しに切り替わる)
trap命令の行き先の”exceptions.c”の中の割込みサービスルーチンは、以下の様に改造しました。アセンブラの割込みサービスルーチンには変更が無く、C言語の割込みサービスルーチンのみです。
void *C_isr_handler( UB fc, VP_INT exinf, void *framepointer, void *usp )
{
BOOL DelayedDispatch = FALSE;
volatile unsigned short stackFrameWord = (*(unsigned short *)(framepointer));
volatile unsigned long stackFrameVector = (unsigned long)MCF5XXX_RD_SF_VECTOR(&stackFrameWord);
int loop;
/*割り込みサービスルーチンの開始*/
switch ( stackFrameVector )
{
case VectorNumber_Vtrap0 : /*trap #0 ソフトウエア割り込み*/
DelayedDispatch = ( svc_list[ fc ^ 0xff ].func )( exinf );
break;
case VectorNumber_Vtpm1ovf : /*TPM1のオーバーフロー割り込み*/
DelayedDispatch = TPM1_OVF_ISR(); /*遅延ディスパッチを実行する*/
break;
default :
break;
}
if( DelayedDispatch == TRUE ) /*遅延ディスパッチが必要な時*/
{
/*コンテキストの保存*/
tcb[ RunningTaskID ].sp = usp; /*スタックポインタをタスクコントロールブロックに保存*/
/*次のタスクIDを取得*/
for( loop = 0; loop < MAX_TID_NUMBER; loop++ )
{
if( ++RunningTaskID >= MAX_TID_NUMBER ) RunningTaskID = 0;
if( tcb[ RunningTaskID ].tskstat == TTS_RDY ) break;
}
if( loop == MAX_TID_NUMBER ) /*全てのタスクが待ち状態の時はIDLE LOOPを呼び出す。*/
{
RunningTaskID = MAX_TID_NUMBER;
}
tcb[ RunningTaskID ].tskstat = TTS_RUN; /*該当タスクをRUNNINGに遷移させる*/
/*次のコンテキストの取り出し*/
usp = tcb[ RunningTaskID ].sp; /*タスクコントロールブロックからスタックポインタの取り出し*/
}
return usp;
}
割込みベクター番号に相当する処理を先のswitch-case文で処理していますが、そのcaseの中ではテーブルジャンプを使って該当処理をコールします。
(※μITRONの機能コードは負の値となっています。これをテーブルのインデックスとするには、全bitを反転するだけで済みます。)
ジャンプテーブル
#include "mytron.h"
BOOL illegal( VP_INT exinf ){ return TRUE;}
BOOL exec_tslp_tsk( TMO tmout );
BOOL exec_rot_rdq( PRI tskpri );
const SERVICE_CALL_LIST svc_list[] =
{
{ illegal }, /*(-0x01) 予約*/
{ illegal }, /*(-0x02) 予約*/
{ illegal }, /*(-0x03) 予約*/
{ illegal }, /*(-0x04) 予約*/
{ illegal }, /*FC_CRE_TSK (-0x05)*/ /* cre_tskの機能コード */
{ illegal }, /*FC_DEL_TSK (-0x06)*/ /* del_tskの機能コード */
{ illegal }, /*FC_ACT_TSK (-0x07)*/ /* act_tskの機能コード */
{ illegal }, /*FC_CAN_ACT (-0x08)*/ /* can_actの機能コード */
{ illegal }, /*FC_STA_TSK (-0x09)*/ /* sta_tskの機能コード */
{ illegal }, /*FC_EXT_TSK (-0x0A)*/ /* ext_tskの機能コード */
{ illegal }, /*FC_EXD_TSK (-0x0B)*/ /* exd_tskの機能コード */
{ illegal }, /*FC_TER_TSK (-0x0C)*/ /* ter_tskの機能コード */
{ illegal }, /*FC_CHG_PRI (-0x0D)*/ /* chg_priの機能コード */
{ illegal }, /*FC_GET_PRI (-0x0E)*/ /* get_priの機能コード */
{ illegal }, /*FC_REF_TSK (-0x0F)*/ /* ref_tskの機能コード */
{ illegal }, /*FC_REF_TST (-0x10)*/ /* ref_tstの機能コード */
{ (BOOL (*)( VP_INT ))exec_tslp_tsk }, /*FC_SLP_TSK (-0x11)*/ /* slp_tskの機能コード */
{ (BOOL (*)( VP_INT ))exec_tslp_tsk }, /*FC_TSLP_TSK (-0x12)*/ /* tslp_tskの機能コード */
以下、凄く続く
}
該当処理、つまりtslp_tskの中身は以下の様になっています。
BOOL exec_tslp_tsk(
TMO tmout /*タイムアウト時間*/
)
{
ID tid = ref_tst() - 1;
tcb[ tid ].dwn_cnt = (RELTIM)tmout; /*待ち時間の設定*/
if( tmout == TMO_POL )
{
tcb[ tid ].tskstat = TTS_RDY; /*ポーリングの時はディスパッチを発生させるのみ*/
}
else
{
tcb[ tid ].tskstat = TTS_WAI; /*待ち状態に入る*/
tcb[ tid ].tskwait = TTW_SLP; /*TMO_FEVRが指定されている時は起床待ちとする*/
}
return TRUE;
}
つまり引数の待ち時間を、変更/追加されたタスクコントロールブロックのダウンカウンターに代入し、またその待ち時間のタイプによってREADYに遷移させるのか、WAITINGに遷移させるのかを決めます。
サービスコール”tslp_tsk”は、引数の時間が経過する事を待つ処理なので、その時間経過を検出するのはシステムタイマーを更新している処理で行う事となります。つまりシステムタイマーとして設定しているTPM1の割込みの中です。
/****************************************************************************/
/* TPM1 オーバーフロー割り込み */
/* 一番目の引数はA0に入っている(アドレスだから?)。 */
/* 二番目の引数はA1に入っている。 */
/****************************************************************************/
BOOL TPM1_OVF_ISR( void )
{
int loop;
static int count;
RELTIM old_ltime = systime.ltime;
TPM1SC_TOF = 0; /*clear status*/
/*システムタイマーの更新*/
systime.ltime += kernel_tic_cnt;
if( systime.ltime < old_ltime ) systime.utime++;
/*時間待ちタスクのタイマーカウンターのカウントダウン*/
for( loop = 0; loop < MAX_TID_NUMBER; loop++ )
{
if( (tcb[ loop ].tskstat & TTS_WAI) && tcb[ loop ].dwn_cnt > 0 ) /*待ち状態でカウントダウン実行中のタスクを探す*/
{
if( --tcb[ loop ].dwn_cnt == 0 ) /*カウントダウンの最後を検出*/
{
if( !(tcb[ loop ].tskstat & TTS_SUS) ) /*SUSPENDは設定されていない事*/
{
tcb[ loop ].tskstat = TTS_RDY; /*タイムアウトとなったので、READYに遷移させる*/
}
else
{
tcb[ loop ].tskstat &= ~TTS_WAI; /*単純な待ち状態の解除*/
}
tcb[ loop ].tskwait &= ~(TTW_SLP | TTW_DLY); /*SLEEPまたはDELAYステータスの解除*/
}
}
}
/*LEDチカチカ*/
if( ++count >= 197 )
{
PTAD_PTAD0 ^= 1;
count = 0;
}
return TRUE;
}
新しいタスクコントロールブロック
typedef struct
{
VP sp; /*タスクスタックポインタ*/
FP fn; /*起動関数*/
VP_INT exinf; /*起動時パラメータ(D0に入れられてタスクが起動する)*/
UB tskstat; /* タスクの状態 */
UB actcnt; /* 起動要求キューイング数 */
STAT tskwait; /* 待ち要因 */
RELTIM dwn_cnt; /* 待ち要因のダウンカウンター */
} TASK_CONTROL_BLOCK;
「システムタイマーの更新処理」とか「LEDチカチカ」は良いとして、「時間待ちタスクのタイマーカウンターのカウントダウン」で、タスクコントロールブロックのダウンカウンターを片っ端からカウントダウンを行っています。
その中で指定時間が来たタスクに関しては、現在の待ち状態から、必要に応じて別の状態への遷移を行います。(別タスクや非タスクコンテキスト部からのサスペンドを受ける可能性も有るので、通常の待ち状態とサスペンドの両方が解除されないとREADYには遷移できない。)
従来のラウンドロビンではタスクの状態がREADYかRUNNINGしか取りえなかったのですが、今度はWAITINGまでとなったので、場合によっちゃあ、全てのタスクがWAITINGとなる事も有り得ます。
その為、idl_loopと言う何もしないタスクの代わりの処理を用意します。このIDLE LOOPへの入り方は、タスクの入り方と何ら変わりません。
/****************************************************************************/
/* IDLEタスク */
/* 他に実行すべきタスクが無い時、このタスクが呼ばれる */
/****************************************************************************/
void idl_loop( void )
{
for( ; ; )
{
_Wait; /*derivative.hにマクロが有るので、それを利用*/
}
}
何もする事が無くてここに来たので、WAITモードに入っています。場合によってはもっと深い省電力モードにも入れるでしょう。
tslp_tskが実現できたので、タスクは以下の様に変更しました。
/****************************************************************************/
/* タスク1 */
/* レジスタD0に起動パラメータが入っている。 */
/****************************************************************************/
void task1( VP_INT exinf )
{
int tid = ref_tst();
exinf *= tid;
for( ; ; )
{
tslp_tsk( (RELTIM)exinf );
switch ( tid )
{
case 1 :
PTAD_PTAD1 ^= 1;
break;
case 2 :
PTAD_PTAD2 ^= 1;
break;
case 3 :
PTAD_PTAD3 ^= 1;
break;
case 4 :
PTBD_PTBD0 ^= 1;
break;
case 5 :
PTBD_PTBD1 ^= 1;
break;
case 6 :
PTBD_PTBD2 ^= 1;
break;
default :
PTBD_PTBD3 ^= 1;
break;
}
}
}
※前回とはLEDチカチカするポートが異なっていますが、これはダイアモンドエースで実行させたので、割り付けが異なっているからです。
※相変わらず一つの関数で、こんどは7つのタスクを実行させています。
※定義の一部には、Project HOSのHOSの定義ファイルの一部を利用させてもらっています。
※何故かrot_rdqが上手く行かないです。原因を調べねば。
※タスクコントロールブロックの管理やタイムアウトしたタスクの検索には、このカーネルでは配列を使用しています。これは構造が単純で動作がイメージし易いからです。しかしスケーラビリティは最低です。
このカーネルは、あくまでも両手くらいのタスク数を最大とした前提での話です。
もしスケーラビリティを重視するなら、HOSの様にリンクリストを使う必要が有ります。
ITRONプログラミング入門―H8マイコンとHOSで始める組み込み開発
- 作者: 濱原 和明
- 出版社/メーカー: オーム社
- 発売日: 2005/04/25
- メディア: 単行本
μITRON準拠TOPPERSの実践活用―製品開発にも学習教材にも使えるフリーのOSプラットホーム (TECH I Embedded Software)
- 作者:
- 出版社/メーカー: CQ出版
- 発売日: 2007/03
- メディア: 単行本
以下のファイルをクリックせず、右クリックメニューからローカルに落として、拡張子をzipに修正してお使い下さい。
プロジェクトファイル一式
※Pac and Goで生成したzipファイルはLhacaでは正常に解凍できませんでした。
※_Waitマクロを使用した場合、デバッカーを外して単独では正常に動作しませんでしたので、このプロジェクトファイルではフルパワーで走っています。タイマーが動いていないのか、割込みが入らないのか判りませんが、まだまだ研究が必要な様です。
ダイアモンドエース再開 [ColdFire V1]
取り敢えず例のラウンドロビンプログラムを突っ込んで、沢山のLEDチカチカまでは来ました。
もうしばらくラウンドロビンプログラムを改造して遊ぼうと思っています。
ITRONプログラミング入門―H8マイコンとHOSで始める組み込み開発
- 作者: 濱原 和明
- 出版社/メーカー: オーム社
- 発売日: 2005/04/25
- メディア: 単行本
リアルタイムオペレーティングシステム HI68K―ITRONアーキテクチャの実現
- 作者:
- 出版社/メーカー: パーソナルメディア
- 発売日: 1987/08
- メディア: 単行本
ダイアモンドエース組み立て完了 [ColdFire V1]
シャア専用!
組み立てるだけで精一杯でした。動作確認は週末に。
Cold Fire V1の最大の欠点は、入手が難しいところですね。ダイレクトからは購入できますが送料高過ぎ!。
せめてDigi-Keyから購入できる様にしてください。フリスクの人。
ColdFire V1がやって来た スーパーバイザーでラウンドロビン [ColdFire V1]
全然終わっていないじゃん。
さて、某企みの為にタスクスイッチ周りを研究している訳です。
前回まで行ったラウンドロビンでは、タスクをユーザーモードで動かしていました。なんとなく、ただなんとなくそれが正当なやり方に思えたからですけれど。
しかし某企みでは、タスクコンテキスト部の中からも、一時的に割り込み禁止にする必要が有ります。これがSRレジスタをいじる必要性からユーザーモードでは出来ないんです。
そこで今度はスーパーバイザーモードでラウンドロビンを行ってみます。
まずmainの中の初期化ですが、
/*タスクの初期化*/
for( i = 1; i < MAX_TID_NUMBER; i++ )
{
/*2番目のタスクから、既にスタック上にコンテキストが保存されている様に見せかける*/
c_text = (TASK_CONTEXT *)((char *)tcb[ i ].sp - sizeof(TASK_CONTEXT));
c_text->pc = tcb[ i ].fn; /*起動関数のアドレス*/
c_text->ccr = 0x40002000; /*SRレジスタの初期値*/
c_text->d[0] = tcb[ i ].exinf; /*起動時パラメータ*/
c_text->a[5] = _A5; /*?*/
tcb[ i ].sp = c_text; /*これらが保存された後のスタックアドレス*/
}
また、最初のタスクの起動部分は、
__asm {
move.l sp,INT_SSP //割り込みスタックポインタを保存する領域にコピー
movea.l tcb[0].sp,sp //タスクコントロールブロックからタスクスタックを取得する
movea.l tcb[0].fn,a0 //タスクコントロールブロックから起動関数のアドレスを取得する
move.l tcb[0].exinf,d0 //起動時パラメータ
move.w SR,d1 //
andi.l #0xF8FF,d1 //割り込み許可
move.w d1,SR
jmp (a0) //最初のタスクへジャンプ
};
と、微妙に変更されているのが判ります。また、ユーザーモードに入らないので、SRレジスタのSbitのクリアは行っていません。
”exceptios.c”ではアセンブラのハンドラを以下の様に変更しています。
asm void asm_isr_handler( void )
{
move.w #0x2700,SR /*一時的に多重割り込みを禁止している*/
lea -60(sp),sp /*スタックに、汎用レジスタを保存する領域の確保*/
movem.l d0-d7/a0-a6,(sp) /*汎用レジスタの保護*/
lea 60(sp),a0 /*A0レジスタにフレームポインタを代入*/
move.l sp,a1 /*カレントユーザースタックを取得*/
move.l INT_SSP,sp /*割り込みスタックに切り換え*/
jsr C_isr_handler /*C言語での処理ルーチン*/
movea.l a0,sp /*戻り値のユーザースタックをSSPに代入*/
movem.l (sp), d0-d7/a0-a6 /*汎用レジスタの復帰*/
lea 60(sp), sp /*使用スタックの破棄*/
rte /*・・・ヽ (´ー`)┌ */
}
大体同じなのですが、タスクスタックにレジスタ類を保存後、割り込み専用スタックに切り換えを行っています。
C言語のハンドラですが、ここが最も大きく変更された所です。変更の有ったタスクスイッチのところだけ掻い摘んで紹介すると、
if( DelayedDispatch == TRUE ) /*遅延ディスパッチが必要な時*/
{
/*コンテキストの保存*/
tcb[ RunningTaskID ].sp = usp; /*スタックポインタをタスクコントロールブロックに保存*/
/*次のタスクIDを取得*/
if( ++RunningTaskID >= MAX_TID_NUMBER ) RunningTaskID = 0;
/*次のコンテキストの取り出し*/
usp = tcb[ RunningTaskID ].sp; /*タスクコントロールブロックからスタックポインタの取り出し*/
}
とやけに短くなっています。要するにユーザーモードで行っていたメモリ/メモリ間の転送が無くなったのです。
マルチタスクシステムではどうしてもタスクスイッチの切り換え時間が性能として出てしまいますので、今度の方式は期待できるでしょう。
と言う訳で、参考のプロジェクトはここから。