Zephyr化する
main()が動くまで
ビルドの環境が出来たので、Zephyrのkernelが動作するように 必要なコードを作りこんでいく。
Zephyrのアプリケーションはmain()
関数から動作を開始する。
ボードに電源を投入してから、様々な初期化処理や設定など、
立ち上げに関する色々な処理が動作して
ようやくmain()
関数の実行に至るのである。
ボードをZephyrに対応させるにはこの初期化処理を実装して、 OSの共通の処理を実行させるのがメインの仕事と言える。
ここでのタスクとなるのは、サンプルから「とりあえず」で持ってきたソースや設定を Zephyrとして動作可能なものに置き換え、最低限の初期化処理を実装することである。
リンカスクリプトを修正する
arch/riscv/common/linker.ld
にriscv向けの共用のリンカスクリプトがある。
他のRISC-VのSoCと同様、これをincludeするだけのリンカスクリプトに修正する。
エントリーポイントを作る
Zephyrのエントリーポイントは__start
のシンボルから実行を開始する。
soc/riscv/riscv-privilege/common/vector.S
に共用の実装があるが、
GD32VF103ではCSR_MMISC_CTL
, CSR_MTVT2
のようにRISV-Vの仕様に含まれない固有のレジスタがあり、固有の初期化処理を入れる必要がある。
ここではsoc/riscv/riscv-privilege/gigadevice-gd32vf103/entry.S
にファイルを作成し、Kconfig
でエントリーポイントのシンボルを変更して対応する。
初期化処理はサンプルの初期化ルーチンを参照して実装とし、
初期化処理のあとにZephyrとして必要な処理を行う。
共用の実装でも行っているが、mtvec
のレジスタに割り込みハンドラを設定する。
ZephyrではRISC-Vの共用の割り込みハンドラの__irq_wrapper
で割り込み処理を行っているので、
このアドレスを設定すればよいのだが、GD32VF103ではmtvecに設定するアドレスは
64bit alignを要求しているため、ここでは__irq_wrapper
のラッパーを作ってそれを
設定する。
割り込みハンドラの設定が完了したら、__initialize
に制御を移して、
ZephyrのOSの処理を開始する。
Multi Thread処理を無効にする
Zepherはマルチスレッドに対応したOSであるが、立ち上げのときにマルチスレッド動作を行うとスレッドの切り替えなど動作確認が煩雑になる。 一旦、マルチスレッドの処理を無効にしておく。
CONFIG_MULTITHREADING=n
と設定する。
マルチスレッドの処理には割り込みとタイマー機能がかかわるため、 これらの実装が完了したら、再度マルチスレッドを有効にする。
初期化処理
アセンブラレベルのブート処理が終わったら、Zephyrの初期化関数の呼び出しが行われる。
初期化を行う関数の呼び出しをZephyrの仕組みに沿った形で実装する。
システムの初期化はSYS_INIT
のマクロで、定義する。
このマクロでは、初期化を実行するタイミングをレベルとして
PRE_KERNEL1
, PRE_KERNEL2
, POST_KERNEL
, POST_KERNEL_SMP
, APPLICATION
から指定する。PRE_KERNEL1
が最も早い段階で実行されるので、ここでは
SYS_INIT(_init, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
のように設定する。
SYS_INIT
は指定した初期化関数を呼び出すコードを
.devconfig.init
のセクションに配置し、起動時に実行する。
arch_swap
ここまでで出来たこと
ここまでで、Zephyrの起動処理を終えてmain()
関数まで辿り着いた。
これで、実用的ではないものの最低限のレベルでZephyrが動いたといえる。