🆓

FreeRTOS(Cortex-M)のコンテキストスイッチ周りを調べてみた

2024/04/28に公開

はじめに

  • 仕事関係で調べる必要があったのと元々、OSの深い部分に関して興味があったのでCortex-MのFreeRTOSのコンテキストスイッチ周りの部分について調べてみた。
  • 自分用のメモとしてここに残しておく。

前提となる事項

  • この記事の読者は以下の内容を知っていることを前提とする。
    • ARM Cortex-Mの概要
      • 特にSVC、Memory Protection Unitなど
    • FreeRTOSの概要
  • FreeRTOSのサンプルは以下とする。
    • FreeRTOS/FreeRTOS/Demo/CORTEX_MPU_M3_MPS2_QEMU_GCC
  • 上記のサンプルに付随してFreeRTOSのport layerに関しては以下を対象とする。
    • /portable/GCC/ARM_CM3_MPU/

全体概要

  • コンテキストスイッチ前後の概要は以下のようになる。
    • 処理フローは大きく分けると①~③となっている。
      • ①SVCハンドラー(vPortSVCHandler)またはSystickハンドラー(xPortSysTickHandler)内でPendSVを発行する
      • ②PendSVハンドラー(xPortPendSVHandler)内でコンテキストの退避と遷移先のタスクの選定(vTaskSwitchContext)、MPUの設定、コンテキストの復元を行う
      • ③遷移先のタスクコンテキストでのLR(リンクレジスタ)が保持するアドレスへジャンプする
  • 主に②の詳細とその前段となるPendSVとはどういうものかを以下で説明する。

PendSVとは?

  • PendSVとはどういうものかというとソフトウェア割り込みの一種でSVC(SuperVisor Call)の遅延版である。
    • 以下資料(※資料自体はv8-MのものであるがCortex-M全体で共通する内容である、https://developer.arm.com/documentation/107706/0100/System-exceptions/Pended-SVC---PendSV)に記載の通りPendSVはICSR(https://developer.arm.com/documentation/ddi0403/d/System-Level-Architecture/System-Address-Map/System-Control-Space--SCS-/Interrupt-Control-and-State-Register--ICSR)のPENDSVSETを1にセットすることで発行されるソフトウェア割り込みで、自分より優先度の高い割り込みが終わるまで処理が保留される割り込みである。
    • これを最低優先度に設定し、OSにとって比較的緊急でないコンテキストスイッチなどに使用することで、ほかの割り込みの処理中にコンテキストスイッチが生じなくなり、すべての割り込みの処理が終わった後にコンテキストスイッチが生じるという流れにできる。またこれによりPendSVの優先度を最低優先度に設定しておけば他のハンドラーからPendSVを発行してもそのハンドラー自体は最後まで処理をしてくれる。
    • ARMとしてはOS開発の際にはこれをコンテキストスイッチに使用しその他をSVCなどを利用することを推奨している模様である。以下の図がSVCとPendSVの比較表である。
    • PendSV自体はすべてのCortex-Mのプロセッサーに存在し共通化されているためCortex-Mのプロセッサーを使用している限りはカスタマイズが必要ない。
  • Cortex-MのFreeRTOSではPendSVを利用してコンテキストスイッチを実行する。


出典:https://developer.arm.com/documentation/107706/0100/System-exceptions/Pended-SVC---PendSV

PendSVハンドラー(xPortPendSVHandler)内の処理

概要

  • PendSVハンドラー(xPortPendSVHandler)内の処理は大きく分けると以下のようになる。
    • 遷移元のタスクのコンテキストの退避
    • 遷移先のタスクの選定(vTaskSwitchContext)
    • MPUの設定
    • 遷移先のタスクのコンテキストの復元
  • 以下内容のうち重要度の高い内容を以下で詳細に説明する。

遷移先のタスクの選定(vTaskSwitchContext)

  • vTaskSwitchContextの中で次に遷移するタスクを検索している。
  • 処理の実体はtaskSELECT_HIGHEST_PRIORITY_TASK(シングルコア時)またはprvSelectHighestPriorityTask(シングルコア以外の時)である。
    • この中でpxReadyTasksListsというReady状態のタスクのリストを優先度順に検索し見つかったものを次のタスクに設定する。

https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/8e07366994f81354a2d4556ca1da9f73dab781e6/tasks.c#L175-L200

MPU(Memory Protection Unit)の設定

  • MPUに対応したターゲット用のFreeRTOSではxTaskCreateRestricted(https://www.freertos.org/xTaskCreateRestricted.html)でタスク生成することで、生成されたタスクのアクセス権限を設定できる。
  • 内部的にはxTaskCreateRestrictedでのタスク生成時にAPIに渡したMPUの設定をTCB(Task Control Block)に詰めてコンテキストスイッチ時に設定する流れとなっている。

https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/8e07366994f81354a2d4556ca1da9f73dab781e6/tasks.c#L358-L436

  • 以下がコンテキストスイッチ時にMPUをリプログラミングする処理である。

https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/portable/GCC/ARM_CM3_MPU/port.c#L1014-L1034

参考資料

Discussion