あなたの天然記念物
ホーム更新雑談Perl鉄ゲタランドナーコースガイド自転車Linuxリンク経歴連絡先

スタックの取り扱い (2014.08.20)

リセット直後

ROMの状況は次の通りです。 LPC1769のメモリマップは、RAMが0x10000000~0x10008000、ROMが0x00000000~0x00080000。 リンカスクリプトが_vStackTopをRAMの最終アドレス(0x10008000)で定義。 Cソース上でベクタテーブルの第1エントリに_vStackTopを設定。 リンカがベクタテーブルを0x00000000に配置。 そしてリセットされるとLPC1769はメインスタックポインタにROM先頭の4バイトを入れ、メインスタックポインタが0x10008000になります。

タスクスケジューラー起動

アイドルタスクを生成します。 SVC 0を実行、SVC例外が発生、ハンドラモードで処理を開始します。 2014.08.20 タスクスケジューラーを起動した所には制御が戻りません。 起動時点のメインスタックに積んだ内容は不要なので、メインスタックポインタをRAMの最終アドレスに戻します。 この補足を書いて気付いたけど、スタックのデータをタスクが参照するような処理があれば、 例外がメインスタックを使うので最初に積んだ内容が破壊されてしまいます。

タスク起動

vTaskCreateで指定したスタック確保量(4バイトを1とする単位)をヒープから確保してタスクに割り当てます。 ヒープの確保処理はFreeRTOSに添付のheap1/heap2/heap3から1つ選択してビルドに追加します。 私のプロジェクトではheap2を使っています。

例外(割り込み含む)

例外はハンドラモードでメインスタックを使います。 LPC1769は例外処理の前に、タスク走行中であればプロセススタックに、例外走行中であればメインスタックに、r0~r3、r12、r14、r15、xPSRの8レジスタをプッシュします。 私のプロジェクトではFreeRTOSが使うSysTickとPendSVは優先度31、アプリケーションの例外は優先度30です。 SysTickとPendSVに対してアプリケーションが横取り(例外処理中に別の例外を処理)します。 でもアプリケーション側同士は優先度が30で統一されていて横取りしません。 例外処理は普通のC関数です。 r4~r11を破壊する予定があれば入口でメインスタックにプッシュして走行します。

タスクスイッチ

契機はSysTick例外、またはvTaskDelayといったAPI呼び出しです。 SysTick例外の場合、確かデフォルトは10ms間隔でヘッダファイルで変更できたハズです。 API呼び出しで固有の操作はありません。 両者とも最後はプログラムからPendSV例外を発行し保留されます。PendSVの優先度はSysTickと同じ31なので横取りしません。 SysTick例外終了後、テールチェインでPendSV例外が始まります。 テールチェインは、例外終了処理後に例外開始処理をするのが事前に判明した際は両者を相殺して省略する動作です。 ここではSysTick例外の終了処理~PendSV例外開始処理が相殺されるテールチェインが発生します。 そしてPendSV例外処理が始まります。 FreeRTOSは、直前まで走行中だったタスクのプロセススタックにr4~r11の8レジスタをプッシュ、走行中のTCBにpsp(プロセススタックポインタ)を保存します。 メインスタックにr3、r14をプッシュ、basepriをconfigMAX_SYSCALL_INTERRUPT_PRIORITYに設定します。 これはヘッダファイルで5と定義してあります。 この設定により例外優先度が5以上の低優先度な例外は禁止されます。 私のプロジェクトは自分用の例外は30、FreeRTOSが使うPendSVとSysTieckは31で、例外全面禁止になります。 vTaskSwitchContextが次に走行するタスクを選択して切り替えます。 この後は、これまでやって来た事を復旧するように処理が進みます。 でもタスクに関しては、切替後のタスクを復旧しますから別のタスクが走行する訳です。

図解

文章ではサッパリ分からないと思い、図解してみましたがやっぱり分かりません(泣)。 スタックの取り扱い

今回の鉄ゲタは…

いやあ、LPCXPresso(LPC1769版)を始めてから、スタックを調べようと思い、幾年月、ようやくその全貌を把握できました、多分(汗)。
LPC1769、FreeRTOS含めて、全部鉄ゲタですよ、もう。<本人の理解力不足とも言う。