Toppers/ASP Cortex-M3に挑戦メモ
出典: Wikimura
Toppersは商用の教材はあるようだが、インターネット上で見つかるのは結構敷居が高い気がする。 幸いにも、Cortex-M3はToppers/ASP(Advanced Standard Profile)に移植されていた。片手間になるが取り組んでみる。
目次 |
ビルドする
下調べ
こういったものは、サンプルプログラムが動くか以前に、ツールが使えないとどうにもならない。 まずは下調べとして、チュートリアルや事例探しを行った。
ところが、公式ページにチュートリアルらしきものが極めて少ないことに気づいた。
既に理解している人が頷ける程度の難易度なら多く存在するが、RTOSの中身を知らないと少しきつい内容の方が多い(これはこれで良いことだが)。また、どちらかといえば商用の教材が多いような印象も受けた。商用の教材は確かに貴重な情報源ではあるものの、オープンな情報源は個人的なものに限られているのが少し辛い。
そんな中、「Toppers チュートリアル」で検索してヒットした2chのスレッド「【TOPPERS】ITRON総合スレ2【NORTi】【HOS】」で、
チュートリアルの少なさについて議論がなされていた。この中で紹介されていたのが、JSPカーネルのビルド方法について取り扱った以下のページだった。
このページから、JSPカーネルのユーザーズマニュアルに辿り着いた。更新が止まっているようだが、コンフィギュレーションについて取り扱っているので参考になった。またFAQでは「ダウンロードしたもののその後の作業が分からない」などの疑問に答えているのも参考になる。
このサイトのドキュメントページから、PDF化されたASPカーネルのユーザーズマニュアルにたどり着けた。 内容はソースファイルに添付されたものと同じだが、しおりが付いているので使い勝手が良い。
これらの情報を参考にして、ビルドに挑戦してみる。
最初の一歩
ユーザーズマニュアル3章「クイックスタート」に、すでに用意されたターゲット用にサンプルプログラムを構築する手順が示されている。
この章の初めには、開発環境の構築について示されている。クロス開発環境の構築と、コンフィギュレータと呼ばれるツールの構築について取り扱われている。しかし、CygwinやMake等は既に用意してあった。ARM用コンパイラはこれまで使用していたCodeSourcery G++ Liteをそのまま使うことにした。また、コンフィギュレーションツールは、以下のリンクから入手できる。Windowsであればバイナリが入手できる。結局、新規の環境構築は特に必要なかった。
続いて、実際にサンプルプログラムをビルドする作業に移った。
まず生成物を格納するディレクトリ(例ではOBJ)を用意し、コンフィギュレーションを行う。
Cygwin上で以下のコマンドを実行することで、サンプルプログラム「sample1」に必要なファイルが生成される。
$ mkdir OBJ $ cd OBJ $ perl ../configure -T <ターゲット略称>
幸いにも、ターゲットとしてSTM32マイコンを使った「cq_starm_gcc」が定義されていた。 マイコンは全く同じなので、少し設定をいじればそのまま使えそうだ。
生成されたmakefileを使って、残りの処理を行う。
$ make depend $ make
実際にやってみる
まずコンフィギュレーションを行った。(これはコンフィギュレータではない)
$ ../configure -T cq_starm_gcc configure: Generating Makefile from ../sample/Makefile.. configure: Generating sample1.c from ../sample/sample1.c.. configure: Generating sample1.h from ../sample/sample1.h.. configure: Generating sample1.cfg from ../sample/sample1.cfg..
続いてコンフィギュレータを実行した。 ここで問題が発生。../cfg/cfg/cfgが無いと言われてしまう。
$ make depend rm -f Makefile.depend arm-none-eabi-gcc -S -mcpu=cortex-m3 -mthumb -Wall -g -O2 -DTOPPERS_LABEL_ASM -I. -I../include -I../arch -I.. -I../target/cq_starm_gcc -I../arch/arm_m_gcc/ -DALLFUNC -fno-strict-aliasing -mcpu=cortex-m3 -I../kernel ../arch/arm_m_gcc/ma keoffset.c /usr/bin/perl ../utils/genoffset makeoffset.s > offset.h ../cfg/cfg/cfg --pass 1 --kernel asp -I. -I../include -I../arch -I.. -I../targe t/cq_starm_gcc -I../arch/arm_m_gcc/ --api-table ../kernel/kernel_api.csv --cfg1- def-table ../kernel/kernel_def.csv --cfg1-def-table ../target/cq_starm_gcc/targ et_def.csv --cfg1-def-table ../arch/arm_m_gcc/prc_def.csv sample1.cfg make: ../cfg/cfg/cfg: Command not found make: *** [cfg1_out.c] Error 127
ダウンロードしたコンフィギュレータのバイナリを、ソースディレクトリのcfg以下のcfgディレクトリにコピーし直すことで解決した。 これについては、makefileを書き換えて、コンフィギュレータの場所を指定し直しても解決できる。 ちなみに、間違ってcfg/cfg以下にcfgディレクトリを作ってしまったのだが、「../cfg/cfg/cfg」が実行形式でなくディレクトリを指定するものと思われてしまい、「Permission Denied」になった。
コンフィギュレータを移動して、make depend実行したところ、以下のような作業が行われた。(arm-none-eabi-gccのある場所へはパスを通していること)
$ make depend
rm -f Makefile.depend
arm-none-eabi-gcc -S -mcpu=cortex-m3 -mthumb -Wall -g -O2 -DTOPPERS_LABEL_ASM
-I. -I../include -I../arch -I.. -I../target/cq_starm_gcc -I../arch/arm_m_gcc/
-DALLFUNC -fno-strict-aliasing -mcpu=cortex-m3 -I../kernel ../arch/arm_m_gcc/ma
keoffset.c
/usr/bin/perl ../utils/genoffset makeoffset.s > offset.h
../cfg/cfg/cfg.exe --pass 1 --kernel asp -I. -I../include -I../arch -I.. -I../t
arget/cq_starm_gcc -I../arch/arm_m_gcc/ --api-table ../kernel/kernel_api.csv --c
fg1-def-table ../kernel/kernel_def.csv --cfg1-def-table ../target/cq_starm_gcc/
target_def.csv --cfg1-def-table ../arch/arm_m_gcc/prc_def.csv sample1.cfg
arm-none-eabi-gcc -c -mcpu=cortex-m3 -mthumb -Wall -g -O2 -DTOPPERS_LABEL_ASM
-I. -I../include -I../arch -I.. -I../target/cq_starm_gcc -I../arch/arm_m_gcc/
-DALLFUNC -fno-strict-aliasing -mcpu=cortex-m3 -I../kernel cfg1_out.c
arm-none-eabi-gcc -c -mcpu=cortex-m3 -mthumb -Wall -g -O2 -DTOPPERS_LABEL_ASM
-I. -I../include -I../arch -I.. -I../target/cq_starm_gcc -I../arch/arm_m_gcc/
-DALLFUNC -fno-strict-aliasing -mcpu=cortex-m3 -I../kernel ../arch/arm_m_gcc/st
art.S
arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -Wall -g -O2 -DTOPPERS_LABEL_ASM -I
. -I../include -I../arch -I.. -I../target/cq_starm_gcc -I../arch/arm_m_gcc/ -no
stdlib -nostdlib -Wl,-Ttext,0x08000000 -Wl,-Tdata,0x20000000 -T ../target/cq_s
tarm_gcc/cq_starm.ld -o cfg1_out.exe \
cfg1_out.o
arm-none-eabi-nm -C cfg1_out.exe > cfg1_out.syms
arm-none-eabi-objcopy -O srec -S cfg1_out.exe cfg1_out.srec
../cfg/cfg/cfg.exe --pass 2 --kernel asp -I. -I../include -I../arch -I.. -I../t
arget/cq_starm_gcc -I../arch/arm_m_gcc/ \
-T ../target/cq_starm_gcc/target.tf --api-table
../kernel/kernel_api.csv --cfg1-def-table ../kernel/kernel_def.csv --cfg1-def-t
able ../target/cq_starm_gcc/target_def.csv --cfg1-def-table ../arch/arm_m_gcc/pr
c_def.csv sample1.cfg
touch kernel_cfg.timestamp
Generating Makefile.depend.
最後にmakeを実行した。
$ make
arm-none-eabi-gcc -c -mcpu=cortex-m3 -mthumb -Wall -g -O2 -DTOPPERS_LABEL_ASM
-I. -I../include -I../arch -I.. -I../target/cq_starm_gcc -I../arch/arm_m_gcc/
sample1.c
arm-none-eabi-gcc -c -mcpu=cortex-m3 -mthumb -Wall -g -O2 -DTOPPERS_LABEL_ASM
-I. -I../include -I../arch -I.. -I../target/cq_starm_gcc -I../arch/arm_m_gcc/
../target/cq_starm_gcc/target_serial.c
arm-none-eabi-gcc -c -mcpu=cortex-m3 -mthumb -Wall -g -O2 -DTOPPERS_LABEL_ASM
-I. -I../include -I../arch -I.. -I../target/cq_starm_gcc -I../arch/arm_m_gcc/
../syssvc/syslog.c
中略
arm-none-eabi-nm -C asp.exe > asp.syms
arm-none-eabi-objcopy -O srec -S asp.exe asp.srec
../cfg/cfg/cfg.exe --pass 3 --kernel asp -I. -I../include -I../arch -I.. -I../t
arget/cq_starm_gcc -I../arch/arm_m_gcc/ \
--rom-image asp.srec --symbol-table asp.syms \
-T ../target/cq_starm_gcc/target_check.tf --api-
table ../kernel/kernel_api.csv --cfg1-def-table ../kernel/kernel_def.csv --cfg1
-def-table ../target/cq_starm_gcc/target_def.csv --cfg1-def-table ../arch/arm_m_
gcc/prc_def.csv sample1.cfg
check complete
STM32-P103用にする
ビルド手順は確認できたので、今度は実機でテストする。 STM32-P103ボードではUSART2がRS-232コネクタにつながっているのだが、cq_starm_gccはUSART1を使っていることがわかった。 まずはこの部分の変更を行う。
何をすればよいのか
target/cq_starm_gcc に添付された target_user.txtに以下の記述があった。
○他のターゲットへの移植
CQ-STARMターゲット依存部で使用するシリアルは,STM32F10xの内蔵機能の
みを使用するため,STM32F10x系のCotex-M3を用いた環境には容易にポーティ
ングが可能である.ポーティングに関しては,以下の初期化ルーチンにターゲ
ット毎の初期化を追加すればよい.
・target_initialize() : target_config.c
ターゲット依存部の初期化(C言語)
・_hardware_init_hook : target_support.S
低レベルのターゲット依存の初期化
スタートアップモジュールの中で,メモリの初期化の前に呼び出される
CQ-STARM基板はSTM32F103VBT6を搭載している。STM32F103VBT6はMedium-densityの100ピンである。 これに対し、STM32-P103基板はSTM32F103RBT6を搭載している。STM32F103RBT6はMedium-densityの64ピンである。 これらはピン配置は異なるものの、メモリマップは同一である。
そのため、シリアル通信のチャネルとGPIOの設定を変えるだけでSTM32-P103上でのテストが行えるはずである。
比較
cq_starm_gccをコピーし、stm32_p103というターゲットフォルダを作り、変更を加えた。 そんなに変更したつもりがないのに動かなくなってしまった。比較のために以下のコマンドを用いた。
for i in ../target/cq_starm_gcc/* ; do export j=`echo $i | sed -e s/cq_starm_gcc/stm32_p103/` echo $j diff $i $j done
どうやら、ディレクトリ名の接尾辞にはツールチェイン名をつけなくてはならなかったらしい。
stm32_p103_gccに改名した。for i in ../target/cq_starm_gcc/* ; do export j=`echo $i | sed -e s/cq_starm_gcc/stm32_p103_gcc/` echo $j diff $i $j done
USART2に切り替える
元々USART2も使用できるような作りになっていた。まずtarget_config.cに書かれた初期化部分でGPIOの設定を書き換えた。
#if (TNUM_PORT >= 2) sil_orw((void*)RCC_APB2ENR, APB2ENR_IOPA_EN); sil_orw((void*)RCC_APB1ENR, APB1ENR_USART2_EN); #endif 中略 #if (TNUM_PORT >= 2) /* USART2(RX) プルアップ */ set_cr_mode(GPIOA_BASE, 3, MODE_INPUT); set_cr_cnf(GPIOA_BASE, 3, CNF_IN_FLOATING); /* USART2(TX) */ set_cr_mode(GPIOA_BASE, 2, MODE_OUTPUT_50MHZ); set_cr_cnf(GPIOA_BASE, 2, CNF_OUT_AF_PP); #endif
続いてtarget_config.hにあった、ポート選択のマクロを変更した。
/* * 使用するシリアルポートID */ #define SIO_PORTID (2)
しかし、make dependで失敗してしまった。
arm-none-eabi-gcc -c -mcpu=cortex-m3 -march=armv7-m -mthumb -mfix-cortex-m3-ldr d -mthumb -Wall -g -O2 -DTOPPERS_LABEL_ASM -I. -I../include -I../arch -I.. -I ../target/stm32_p103_gcc -I../arch/arm_m_gcc/ -DALLFUNC -fno-strict-aliasing -I../kernel cfg1_out.c ../target/stm32_p103_gcc/target_serial.cfg:8: error: 'INTNO_SIO' undeclared here (not in a function) make: *** [cfg1_out.o] Error 1
シリアル通信の割り込み番号が定義されていないと言われているらしい。ポート番号SIO_PORTIDによって、マクロ定義が切り替えられる作りになっているように見えたが...なぜだめなのだろう? SIO_PORTIDをgrepで検索してみた。
for i in ../target/stm32_p103_gcc/* ; do echo $i grep SIO_PORTID $i done 実行結果で気になった箇所 ../target/stm32_p103_gcc/target_serial.h #if (SIO_PORTID == 1)
マクロで分岐する箇所がおかしい気がする。SIO_PORTIDが1と2で切り替わるようになっているなら、SIO_PORTID==2もあるはずなのに... target_serial.hを見直してみた。ところ原因がわかった。タイプミスでSIO_PORIDになっていた。オリジナルにミスがあることがわかった。
#if (SIO_PORTID == 1) #define INHNO_SIO IRQ_VECTOR_USART1 #define INTNO_SIO IRQ_VECTOR_USART1 #elif (SIO_PORID == 2) <---- オリジナルにミスがあったところ #define INHNO_SIO IRQ_VECTOR_USART2 #define INTNO_SIO IRQ_VECTOR_USART2 #endif
これを修正したら、make dependを通すことができた。さらにmakeもできた。
デバッグに挑戦
デバッグするために、シンボル情報を追加するよう設定を書き換えた。 Makefile.dependのコンパイラオプションに、コンパイラのバグフィックスとデバッグ情報追加オプションを付け加えた。
# # コンパイルオプション # INCLUDES := $(INCLUDES) -I$(TARGETDIR) CDEFS := $(CDEFS) COPTS := $(COPTS) -mcpu=cortex-m3 -march=armv7-m -mthumb -mfix-cortex-m3-ldrd -g3
そしてリセット時にジャンプする_startにハードウェアブレイクポイントを設定し、OpenOCDでデバッグを試みた。
●フラッシュ書き込み target remote localhost:3333 monitor halt load monitor reset init thbreak _start continue ●デバッグのみ target remote localhost:3333 monitor halt monitor reset init thbreak _start continue
その結果、ちゃんとソースを見ながらデバッグできることが確認できた。画像は_startで停止したところ。
シリアル通信がうまくいかない?
プログラムは動いていることが確認できたが、シリアル通信が動いていないようだった。 クロック有効化とGPIO設定の変更を行い、マクロの設定で割り込みハンドラがUSART2ようになるようにも設定した。 しかし動かない...
そこで、target_serial.cを読みなおしたところ、気になる記述があった。
static const uint32_t sioreg_table[TNUM_PORT] = {
USART1_BASE,
#if (TNUM_PORT >= 2)
USART2_BASE
#endif
TNUM_PORTというマクロが定義されているらしい。そこでgrepで調べてみた。
for i in ../target/stm32_p103_gcc/* ; do echo $i grep TNUM_PORT $i done 検索結果の1つ: ../target/stm32_p103_gcc/target_syssvc.h #define TNUM_PORT (1) /* サポートするシリアルポートの数 */
ここを2に書き換えてみると同時に、TNUM_SIOPも2にしてみた。(1だとうまくいかない気がするため)
/* * シリアルポート数の定義 */ #define TNUM_PORT (2) /* サポートするシリアルポートの数 */ #define TNUM_SIOP (2)
OS起動確認
上記の変更を行ったところ、ついに動作する様子が確認できた。
TOPPERS/ASP Kernel Release 1.3.2 for CQ-STARM(STM32F103) (Aug 17 2009, 22:09:14)
Copyright (C) 2000-2003 by Embedded and Real-Time Systems Laboratory
Toyohashi Univ. of Technology, JAPAN
Copyright (C) 2004-2008 by Embedded and Real-Time Systems Laboratory
Graduate School of Information Science, Nagoya Univ., JAPAN
System logging task is started on port 2.
Sample program starts (exinf = 0).
task1 is running (001). |
task1 is running (002). |
task1 is running (003). |
task1 is running (004). |
task1 is running (005). |
task1 is running (006). |
task1 is running (007). |
実はこれは正しい動作をしていないのだが...一応動くことはわかった。
正しく動作するまで
文字を受信しない?
文字を送ることでタスクの状態を切り替えられるのだというが、文字を送っても一向にタスクが切り替わらない。 そこで、USART2の割り込みハンドラで、受信部分にブレイクポイントをセットして実行した。
文字を打つとブレイクポイントで止まることが確認できた。また、文字も受信できていることが確認できた。更に追いかけると、受信バッファに蓄えられていることがわかった。しかしタスクはそれを受け取らない。
試しに、文字入力後のスイッチ文で、文字の入った変数を上書きしてやると分岐が生じる。
task1 is running (170). | task1 is running (171). | task1 is running (172). | task1 is running (173). | task1 is running (174). | #1#slp_tsk() task2 is running (001). + task2 is running (002). + task2 is running (003). + task2 is running (004). + task2 is running (005). + 中略 task2 is running (240). + task2 is running (241). + task2 is running (242). + task2 is running (243). + task2 is running (244). + task2 is running (245). + #2#tslp_tsk(10000) task3 is running (001). * task3 is running (002). * task3 is running (003). * task3 is running (004). * task3 is running (005). *
分かっていること
- USART2の割り込みハンドラが実行されている。
- PCから文字を送信すると、USART2の割り込みハンドラから、1文字受信関数へ飛んでいる。
- 受信バッファに1文字ずつデータが格納されている。
- フルになったら新しいデータが破棄されている。
- 確認できていないが、受信バッファは全く読まれていないように見える。
- デバッガのMemory表示で観測すると、書き込むたびにバッファの内容が変わるのが分る。
- ある程度書き込むと戻るかと思ったが、データは溜まったまま。
- タスク(task関数)で入力文字に対して分岐するところでブレイクし、文字の入った変数を見ると、常に0だった。
- 分岐時に文字の入った変数を変更し、強制的に分岐させることで、タスク切り替えを発生させることができた。
- タスクをスリープさせ、10秒後に起きるようなコマンド(文字'S'で分岐する)を実行したが、何秒たっても切り替わらない。
test_task1を動かしてみる
どうにもサンプルプログラムが動かないので、別のを試した。
test_task1というタスク切り替えを確かめるプログラムをビルドし、テストした。 ビルドは少し手間がかかった。設定が分からず、最後まで依存関係が解決しなかった。 test_lib.cを無理やりインクルードして解決した。
TOPPERS/ASP Kernel Release 1.3.2 for STM32-P103(STM32F103) (Aug 18 2009, 12:05:21)
Copyright (C) 2000-2003 by Embedded and Real-Time Systems Laboratory
Toyohashi Univ. of Technology, JAPAN
Copyright (C) 2004-2008 by Embedded and Real-Time Systems Laboratory
Graduate School of Information Science, Nagoya Univ., JAPAN
System logging task is started on port 2.
7 messages are lost.
Check pint 8 passed.
Check point 9 passed.
Check point 10 passed.
Check point 11 passed.
Check point 12 passed.
Check point 13 passed.
Check point 14 passed.
Check point 15 passed.
Check point 16 passed.
Check point 17 passed.
Check point 18 passed.
Ch-- buffered messages --
Check point 20 passed.
Check point 21 passed.
Check point 22 passed.
Check point 23 passed.
Check point 24 passed.
Check point 25 passed.
Check point 26 passed.
Check point 27 passed.
Check point 28 passed.
Check point 29 passed.
Check point 30 passed.
Check point 31 passed.
Check point 32 passed.
Check point 33 passed.
Check point 34 passed.
Check point 35 passed.
Check point 36 passed.
Check point 37 passed.
Check point 38 passed.
Check point 39 passed.
Check point 40 passed.
All check points passed.
最初の"7 messages are lost."は気になるが、OSの機能そのものは正しく動いているよう無きがする。 Cortex-M3...というよりSTM32用のシリアル通信がおかしいのだろうか?
test_sem1を動かしてみる
TOPPERS/ASP Kernel Release 1.3.2 for STM32-P103(STM32F103) (Aug 18 2009, 13:16:11)
Copyright (C) 2000-2003 by Embedded and Real-Time Systems Laboratory
Toyohashi Univ. of Technology, JAPAN
Copyright (C) 2004-2008 by Embedded and Real-Time Systems Laboratory
Graduate School of Information Science, Nagoya Univ., JAPAN
System logging task is started on port 2.
Check point 1 passed.
Check point 2 passed.
Check point 3 passed.
Check point 4 passed.
Check point 5 passed.
Check point 6 passed.
Check point 7 passed.
Check point 8 passed.
Check point 9 passed.
Check point 10 passed.
Check point 11 passed.
Check point 12 passed.
Check point 13 passed.
Check point 14 passed.
Check point 15 passed.
Check point 16 passed.
Check point 17 passed.
Check point 18 passed.
Check point 19 passed.
Check point 20 passed.
Check point 21 passed.
Check point 22 passed.
Check point 23 passed.
heck point 24 passed.
Check point 25 passed.
Check point 26 passed.
Check point 27 passed.
Check point 28 passed.
Check point 29 passed.
Check point 30 passed.
Check point 31 passed.
Check point 32 passed.
Check point 33 passed.
Check point 34 pa-- buffered messages --
Check point 35 passed.
Check point 36 passed.
Check point 37 passed.
Check point 38 passed.
Check point 39 passed.
Check point 40 passed.
Check point 41 passed.
Check point 42 passed.
Check point 43 passed.
Check point 44 passed.
Check point 45 passed.
Check point 46 passed.
Check point 47 passed.
Check point 48 passed.
Check point 49 passed.
Check point 50 passed.
Check point 51 passed.
Check point 52 passed.
Check point 53 passed.
Check point 54 passed.
Check point 55 passed.
Check point 56 passed.
Check point 57 passed.
Check point 58 passed.
Check point 59 passed.
Check point 60 passed.
Check point 61 passed.
Check point 62 passed.
All check points passed.
sample1がおかしかった原因?
振り返ってみると、sample1がおかしかったのは、main_taskのせいかもしれない。 main_taskは、文字読み込みのサービスコールを使用することで、文字をバッファから取り出す役割を担っている。
以下の部分が、起動直後たった1度しか実行されない事が分かった。唯一文字を読み込むところなので、これが実行されないために、バッファから全く読みだされないという事態が起きたと思われる。
sample1.cの一部
/*
* メインループ
*/
do {
SVC_PERROR(serial_rea_dat(TASK_PORTID, &c, 1));
switch (c) {
そこで、セマフォのテストtest_sem1のテスト終了直前に以下の記述を加えた。
unsigned char c = 0;
syslog( LOG_NOTICE, "Enter 'q' to quit:");
while( c!='q'){
serial_rea_dat(2, &c, 1); // ポート2から文字を1文字読み、cへ格納する
}
これを実行すると、小文字のqを入力した時点でループを抜けていることがわかる。 入力を正しく行うことができることがわかった。
Check point 59 passed. Check point 60 passed. Check point 61 passed. Enter 'q' to quit: abcdefghijklmnopq-- buffered messages -- Check point 62 passed. All check points passed.
sample1が文字入力で動作を切り替えることができなかった原因は、main_taskが正しく実行されていないからではないかと疑っている。 文字をバッファから読まずにいるために、何を入力しても無視されてしまうと考えている
ただ、なぜmain_taskが実行されないのかは、今のところ理解できない。そもそもITRONのタスクを作ることすらできていないので、現時点ではとても厳しい。

