Kernelの技術説明 - renesas/FreeRTOS-Kernel GitHub Wiki

Renesas/FreeRTOS-Kernel wiki

Now under construction.


コンフィグ

FreeRTOSConfig.h にFreeRTOSの動作を制御する設定マクロが定義されています。

FreeRTOSのコンフィグ

公式HPを参照してください。
FreeRTOS.org: FreeRTOSConfig.h

ルネサス独自のコンフィグ

ルネサスが独自に定義した設定マクロを紹介します。
表を用いて設定可能な値とその内容を説明します。設定値列で斜体フォントである値はデフォルト値を意味します。

FreeRTOSConfig.hを直接編集する場合の注意点

FreeRTOSConfig.h を直接編集して設定マクロの値を変更する場合、SCでコード生成機能を実行またはビルド時に、一部の設定マクロがデフォルト値に戻される場合があります。
これを回避するためには、下記の手順を実施してください。

  1. 当該の設定マクロをFreeRTOSConfig.h から定義ごと削除する。
  2. コンパイル・オプション(※)で当該マクロと値を設定する。
    (IDEのビルド・オプション画面から設定すると便利です。)

※ CC-RX: -define=<macro_name>[=<string>], GCC: -D <macro_name>[=<string>]

configUSE_TASK_DPFPU_SUPPORTマクロ

この設定マクロはDPFPU(※1)関連レジスタをコンテキスト退避/復帰するかを制御します。CC-RXコンパイラを使用、かつ、RXv3コアのデバイスを使用している時(FreeRTOS-Kernel\portable\Renesas\RX700v3_DPFPU\port.c)に利用可能です。

GCCコンパイラ使用時は、DPFPU関連レジスタのコンテキスト退避/復帰に対応していません。

この設定マクロの値を直接変更した場合、デフォルト値に戻される可能性があります。 詳細はFreeRTOSConfig.hを直接編集する場合の注意点を参照してください。

設定値 内容 備考
0 DPFPU関連レジスタをコンテキスト退避/復帰しません。 DPFPUを使用していない場合に設定してください。
DPFPUを使用している場合(※1)、この値を設定してはいけません。
さもないと、浮動小数点計算の結果が異常になる場合があります。
10.4.3-rx-1.0.9以降は、e2 studioで新規生成したDPFPU未搭載デバイス向けプロジェクトのデフォルト値です。
1 特定のタスク(※2)で
DPFPU関連レジスタをコンテキスト退避/復帰します。
DPFPUを使用している場合(※1)に設定してください。
DPFPU関連レジスタの退避/復帰の可否をタスクごとに制御したいユーザ向けの設定です。
2 すべてのタスクで
DPFPU関連レジスタをコンテキスト退避/復帰します。(※3)
DPFPUを使用している場合(※1)に設定してください。
10.4.3-rx-1.0.9以降は、e2 studioで新規生成したDPFPU搭載デバイス向けプロジェクトのデフォルト値です。
  • ※1:

    • DPFPUはRXv3コア以降のデバイスに搭載されている場合があります(例えば、RX72N)。
    • DPFPUはコンパイラ・オプション(CC-RX: -dpfpu, GCC: --mdfpu=64)を指定することで使用可能です。このオプションを指定することで、倍精度浮動小数点処理命令を使用したアセンブラ命令が生成されます。
    • DPFPUに関する詳細は、デバイスのハードウェア・マニュアル、コンパイラのマニュアル、RXv3命令セットアーキテクチャのマニュアルをご覧ください。
  • ※2:

    • 特定のタスクとは、ユーザによってvPortTaskUsesDPFPU()が実行されたタスクです。
      • DPFPU関連レジスタのコンテキスト退避/復帰を行いたいタスクにおいて、浮動小数点計算(倍精度浮動小数点処理命令)を実際に実行するまでにこの関数を1度だけ実行してください。
      • 例えば、タスクエントリ関数(タスクの初回起動時に実行される関数)内の処理の先頭でこの関数を実行してください。
      • この関数を実行していない状態の時、DPFPU関連レジスタのコンテキスト退避/復帰を行いません。
  • ※3:

    • 各タスクで浮動小数点計算(倍精度浮動小数点処理命令)を実行しているかをユーザは気にする必要がないため、FreeRTOSでDPFPUを一番手軽に利用できます。
    • 特定のタスク(設定値:1)に比べて、カーネル占有時間の増大やヒープメモリ使用量増大というデメリットがあります。

マルチタスクの仕組み

1個のCPUは1つのプログラムしか実行できない、という大原則があります。よって、1個のCPUしか搭載していない場合、複数のプログラムを同時に実行するためには、プログラムを次々に切り替えて1つのプログラムだけを実行する必要があります。
プログラムに実行権を割り当てることを「ディスパッチ(dispatch)」と呼びます。
FreeRTOSでは、1個のCPUで実行可能な1つのプログラムのことを「タスク」と呼び、タスクを同時に実行する(ように見せる)ことを「マルチタスク」と表現します。

ディスパッチ処理を実現するためには、任意の時点で特定のタスクへと切り替える仕組みが必要です。
任意の時点を決める仕組みは「スケジューラ」が担っています。
そして、特定のタスクへと切り替える仕組みは「コンテキストスイッチ」と呼ばれます。「コンテキスト」または「タスクコンテキスト」とはタスクにおけるCPU処理の情報(CPUレジスタなど)を総称した表現です。

以上をまとめると、マルチタスクは、スケジューラによってディスパッチが発生しコンテキストスイッチによってタスクが次々と切り替わる一連の流れによって実現されています。FreeRTOSカーネルの主要な使命はこのマルチタスク制御を破綻なく動作させることにあります。

スケジューラ

WIP

コンテキストスイッチ

コンテキストスイッチは、「コンテキスト退避」→狭義のコンテキストスイッチ→「コンテキスト復帰」という流れで実現しています。

  • コンテキスト退避:タスクコンテキストをヒープメモリに一時保存することを指します。FreeRTOSでは、タスクスタック(後述するTCBを参照)にタスクコンテキストが保存されます。
  • 狭義のコンテキストスイッチ:次に動かすべきタスクはReadyキューから判別され、CPUのスタックポインタレジスタを次のタスクのコンテキストへと更新、つまりコンテキストを現在実行中のタスクのものから入れ替えます。
  • コンテキスト復帰:タスクコンテキストをヒープメモリから復元することを指します。

コンテキストスイッチはスケジューラからトリガされるソフトウェア割り込みの割り込みハンドラで実行されます。割り込みハンドラでは次の1-3.の処理が行われます。

  1. 割り込み直前に動作していたタスクのコンテキストをタスクスタックに保存します(コンテキスト退避)。
  2. 次に実行すべきタスクを判別しそのタスクスタックをスタックポインタレジスタに代入します(狭義のコンテキストスイッチ)。つまり、割り込み直前に実行されていたタスクのタスクスタックではなく、次に実行したいタスクスタックへとスタックポインタを入れ替えます。
  3. その後、通常の割り込み復帰のようにタスクスタックからプログラムカウンタなどのCPUレジスタを復元します(コンテキスト復帰)。

1-3.の処理の結果、割り込み処理完了後には次に実行すべきだったタスクへとプログラムジャンプしています。

つまり、コンテキストスイッチ動作は割り込み動作を利用しています。
Bare-metal(RTOSを使っていない)環境では、非割り込み処理のローカル変数などがスタックに保存されます。さらに、割り込みが発生した際、非割り込み処理のCPUレジスタ情報などがスタックに追加で保存されます。そして、割り込みハンドラが終了すると、スタックからCPUレジスタ情報を復元し、割り込み発生直前の非割り込み処理に戻ります。
この動作をFreeRTOS環境に当てはめる時、非割り込み処理がタスク、割り込み起因がソフトウェア割り込み、割り込み処理がコンテキストスイッチに相当します。ただし、タスクごとに個別のスタック領域を持っており、割り込み処理でこれらのスタック領域が入れ替わるイメージです。

コンテキスト退避/復帰されるCPUレジスタ

コンテキストスイッチ時に保存や復元されるCPUレジスタは次のとおりです。

  • 汎用レジスタ:R0-R15
  • プログラムカウンタ (注1):PC
  • プロセッサステータスワード (注1):PSW
  • アキュムレータ:ACC0, ACC1
  • 単精度浮動小数点ステータスワード:FPSW
  • DPFPU関連レジスタ (注2):DR0-DR15, DPSW, DCMR, DECNT, DEPC

注1:厳密にはコンテキスト退避時ではなく、通常の割り込み(ソフトウェア割り込み)処理に基づいて自動で退避されます。
注2:DPFPU関連レジスタはconfigUSE_TASK_DPFPU_SUPPORTマクロを1または2に設定している場合に退避/復帰されます。このマクロはCC-RXを使用、かつ、RXv3コアを搭載したデバイスのみ利用可能です。詳細はconfigUSE_TASK_DPFPU_SUPPORTマクロを参照してください。

タスクコントロールボックス(TCB)

タスクの各種情報を記憶する領域のことを「タスクコントロールボックス(TCB)」と呼びます。FreeRTOSでは、1つのタスクごとに1つのTCBが存在します。TCBもまたヒープメモリに格納されています。
FreeRTOSカーネルは個々のタスクを識別するために各タスクが紐づくTCBの先頭アドレスを利用しています。例えば、現在実行中のタスクやReadyキュー内の各タスクなどを示す変数にはTCBの先頭アドレスが代入されています。
TCBに記憶される情報のうち重要な内容の一つが「タスクスタック」へのポインタです。タスクスタックはその名のとおり一般的なスタックの役割に加えて、コンテキストスイッチにとっても重要な役割を果たします。

割り込み(xxxFromISR、割り込みレベル)

遅延割り込み

ヒープメモリ

クリティカルセクション

FreeRTOSを用いる場合、処理の類別が大きく分けて3つ存在します。

  • カーネル処理
  • タスク処理
  • 割り込み処理

カーネル処理はマルチタスク制御を実現するための核となる処理です。カーネル処理を実行中、ユーザアプリケーション(タスク処理や割り込み処理)にCPU実行権を自由に奪われてしまうと、マルチタスク制御は破綻してしまいます。したがって、カーネル処理の実行中はユーザアプリケーションの実行を阻止する仕組み、つまりクリティカルセクションが必要です。
FreeRTOSでは下表の仕組みでカーネル処理に対するクリティカルセクションを実現しています。
参考:Mastering the FreeRTOS Real Time Kernel - a Hands On Tutorial Guide 7.2章

関数
(クリティカルセクション突入/脱出)
クリティカルセクションの
実現方法
割り込み処理内
実行可否
説明 使用場面
taskENTER_CRITICAL()/
taskEXIT_CRITICAL()
IPL制御 不可 IPLをconfigMAX_SYSCALL_INTERRUPT_PRIORITYに
引き上げることでコンテキストスイッチを抑止(※1)し、
タスク内の保護対象コード領域が他のタスクから
アクセスされることを防ぐ。
ただし、configMAX_SYSCALL_INTERRUPT_PRIORITYよりも
大きい優先度である割り込みからのアクセスの場合は保護されない。
処理が非常に短いコード領域を
保護したい場合
taskENTER_CRITICAL_FROM_ISR()/
taskEXIT_CRITICAL_FROM_ISR()
同上 可能 同上 同上
vTaskSuspendAll()/TaskResumeAll() スケジューラの
一時停止/再開
不可
(※2)
スケジューラを一時停止(ロック)し、タスク内の
保護対象コード領域が他のタスクからアクセスされることを防ぐ。
ただし、割り込みは有効状態であるため、割り込み(※3)からの
アクセスの場合は保護されない。
IPL制御では処理が長すぎるコード領域を
保護したい場合
  • ※1:コンテキストスイッチはIPL == configKERNEL_INTERRUPT_PRIORITYのソフトウェア割り込みからトリガされるため。
  • ※2:TaskResumeAll()の内部ではtaskENTER_CRITICAL() / taskEXIT_CRITICAL()が使用されているため。
  • ※3:通常状態のタスクはIPL == 0 (最低レベル)である。
⚠️ **GitHub.com Fallback** ⚠️