🆓

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