AM335x: Privileged Modeにならない問題

| コメント(0)

記事日付: 2015/11/04

ここのところ、TIのAM335xの新規採用のため立ち上げ作業をやっているのだが、
解決した問題についてメモを残しておこうと思う。

概要・結論

問題: CPUSwitchToPrivilegedMode() を実行しても、特権モードにならない
解決: Debug設定の「Enable Semihosting」のチェックを外す

Problem: CPUSwitchToPrivilegedMode() doesn't work.
Solution: Uncheck "Enable Semihosting" option in Debug Configuration dialog.

前提

筆者はAM3352をOSレスで動かすことを目標に、StarterWareを流用してブートローダ・ハードウェア設定のコードを書こうとしている。
開発環境は以下による。

IDE: TI Code Composer Studio (CCS) Ver 7.3.0
コンパイラ: TI C/C++ Compiler 16.9.4 LTS
JTAGデバッガ: TI XDS100v2
評価ボード: BeagleBone Black (BBB) ※プロセッサはAM3358

プロジェクトファイルはStarterWareからのインポートではなく、新規CCSプロジェクトからBasic Examples -> Hello Worldで作成した。

Privileged Mode

AM335xのピンマルチマルチプレクサ設定を行うためには、
プロセッサの動作モードをPrivileged Mode (System Mode) にしなくてはならない。
そのためには:

① StarterWareのcpu.cにあるCPUSwitchToPrivilegedMode() を用いる。

当該関数の中にあるSWI (またはSVC)命令によりスーパーバイザコールの例外(ソフト発生例外)が発生する。

③ このとき、プロセッサの動作モードがスーパーバイザモードに変化し、例外処理が実行される。
 ※例外処理は exceptionhandler.asm の SVC_Handler である。
 ※事前に例外ベクタのセットがなされている必要がある。

④ 例外処理の中でSPSR_SVCレジスタのモード設定(M)ビットを操作してシステムモードにする。

⑤ 例外処理から復帰した時に、SPSR_SVCレジスタの内容がCPSRに反映され、以降システムモードとなる。

というような手順を踏む。

とりあえず動かない

以上の手順をやればよいだけなのだが、うまくいかない。

問題(1) 例外処理コードが実行されない?

SWI (またはSVC)命令の実行後、プロセッサのプログラムカウンタは例外ベクタに飛ぶものの、
なぜかその直後に呼び出し元(SWI命令の次の行)に戻ってきてしまう。

TIのコミュニティでも以下のような報告を何回か見かけたので、
筆者のほかにも困ってる人がいたのではないか。

https://e2e.ti.com/support/embedded/starterware/f/790/p/399811/1420993

The function CPUSwitchToPrivilegedMode() raises an exception with the SWI instruction.
Then I observe a very strange behavior. The CPU jumps to the SVC_Handler function,

defined in exceptionhandler.asm, but the code is not executed.
Instead the CPU jumps right back to the caller.

試行錯誤の結果、「SVC_Handler」のラベル名を何か別の名前(「SVC_Handler2」とか)に変えると、
例外処理が実行されることが判明した。

なお、以下のようなコードにして、ベクタテーブルのSVC例外ハンドラとして「Hoge」を設定したとき、
「Hoge」自体は実行されるが、コード実行が「SVC_Handler」まで来た瞬間、コード実行が例外発生元に戻ってしまう事も判明した。
これも「SVC_Handler」の名前を別の名前にするだけで問題なくなってしまう。

Hoge:       
NOP      ←このあたりは特に問題なく実行される。
NOP
NOP
NOP
SVC_Handler:   ←ここまでくると勝手に例外発生元の場所にジャンプしてしまう。

STMFD r13!, {r0-r1, r14} ; Save context in SVC stack
(以下略)

SVC例外処理の中身

cpu.c の CPUSwitchToPrivilegedMode(void)は以下のようになっている。

void CPUSwitchToPrivilegedMode(void)
{
 asm(" SWI #458752");
}

exceptionhandler.asm の SVC_Handler は以下のようになっている。

SVC_Handler:
STMFD r13!, {r0-r1, r14} ; Save context in SVC stack
SUB r13, r13, #0x4 ; Adjust the stack pointer
LDR r0, [r14, #-4] ; R0 points to SWI instruction
BIC r0, r0, #MASK_SVC_NUM ; Get the SWI number
CMP r0, #458752
MRSEQ r1, spsr ; Copy SPSR
ORREQ r1, r1, #0x1F ; Change the mode to System
MSREQ spsr_cf, r1 ; Restore SPSR
ADD r13, r13, #0x4 ; Adjust the stack pointer
LDMFD r13!, {r0-r1, pc}^ ; Restore registers from IRQ stack

「458752」とは意味不明だが、要は「0x070000」である。
例外処理側では、戻り先のポインタ-4 (=例外発生元のSWI命令)を読み込んで、
命令の2桁をマスクすると、命令の後ろにあるコード(#458752の部分)を取り出すことができる。
取り出したコードが「458752」であれば、システムモードに切り替えを行う。

問題(2) 呼び出し元のSWI命令が読めない

さて、ここでもさらに問題があり、なぜか LDR r0, [r14, #-4] で例外発生元の
SWI命令が正しく読み込みされない。
r0には例外発生元のSWI命令とは関係ない値が読み込まれてしまう。

例外処理の中にテストコードを書いて試験したところ:
LDR r0, [r14, #-8] ⇒OK
LDR r0, [r14, #-4] ⇒NG
LDR r0, [r14, #-0]  ⇒OK
LDR r0, [r14, #4]  ⇒OK

STR r0, [r14, #-8]  ⇒OK
STR r0, [r14, #-4]  ⇒NG
STR r0, [r14, #0]  ⇒OK
STR r0, [r14, #4]  ⇒OK

であり、一番欲しいところ以外に対してはLDRもSTRも正しく動作している。

原因と解決

以上のとおり、挙動が不自然である。
特に、「SVC_Handler」のラベル名が影響するあたり、プロセッサやコンパイラの問題とも考えられない。
そうするとデバッガ周りが怪しいと考え設定項目を確かめてみると、GUIの中に以下の設定があった

[Debug Configurations] → [Target]→[Program/Memory Load Options]
"Enable Semihosting (requires setting a breakpoint at SVC_Handler)"

Semihostingとは、printf()などを用いた入出力を、IDEのコンソール対してデバッガ経由で行うための仕組みであるらしい。
これにかかわる処理を、SVC例外ハンドラ周辺で実施しているようである。

このチェックボックスを外したところ、前述の二つの問題は両方とも解消した。

コメントする

本ページの管理人

Likipon

埼玉在住の一応エンジニア。最近はシステムエンジニア気味で回路が本業でなくなってしまった。

うなりくんのファン。

メニュー

広告