ハードウェアを意識したプログラミングの基礎(前編)

荘司靖

tag: 組み込み

技術解説 2008年4月 8日

● コンパイラが生成するコード

 SIMDのように高機能な命令セット以外にも,使える命令が異なることがあります.リスト6のコードを見てください.

1: #include <stdio.h>
2: #include <linux/types.h>
3:
4: int main()
5: {
6:   char fifo[] = {0x12, 0x34, 0xAB, 0xCD};
7:   __u16 val = 0;
8:
9:   val = *(__u16 *)fifo;
10:
11:  printf("0x%Xエn", val);
12:  return 0;
13: }


リスト6 使える命令が異なる例

 筆者は以前,ARMのCPUボード「Armadillo」を使ってあるデバイスを制御する必要がありました.ArmadilloにはPC104と呼ばれるISAに似た拡張バスがあります.このバスは,16ビットと8ビットのアクセスが行えるようになっています.この拡張バスにデバイスを接続して制御しなければなりません.そこで,以前のコードを16ビット・アクセスに変更したと仮定してください.

 一見正しそうに見えるコードですし,アプリケーションとしては正しく動くコードになっています.しかし,デバイスは期待通りに動いてくれませんでした.どうしても分からなかったので,オシロスコープを持ってきてバスの電気信号をのぞいてみることにしました.そのときの波形が図10です.

zu10_01.gif
図10 オシロスコープの波形(8ビット・アクセスが2回)

 波形を確認すると,I/O Read(IOR)信号が2回出ていることが分かりました.その後,何度コードを読み直しても,9行目で一度しかFIFOにアクセスしていません.にもかかわらず,謎のIORは2回出ているままです.なぜでしょう?

 こんな時,昔の筆者はよく「ありえない!」と騒いでいました.そこに筆者の同僚で,尊敬する先輩でもあるMさんがやってきて,「ありえないことは,ありえない」とどこかの標語のようなことを教えてくれました.アナログや高速ディジタルの世界は分かりませんが,普通のディジタルの世界では突き詰めればすべて論理的に解決できるはずです.それ以来,「ありえない!」と叫ぶ前に「ありえないことは,ありえない」と自分に言い聞かせるようになりました.

 また,H先輩は「バイナリを読む」ことを教えてくれました.アプリケーション開発を行っていると,高級言語がコンピュータに命令しているような錯覚に陥ります.しかし実際にCPUに命令として伝わっているのは,マシン語なのです.さすがに01のマシン語をみるのは辛いので,生成したバイナリを逆アセンブルしてみました.リスト7が逆アセンブルしたコードです.

1: 0000850c <main>:
2:     850c:    e52de004    str lr, [sp, #-4]!
3:     8510:    e24dd004    sub sp, sp, #4 ; 0x4
4:     8514:    e3a02004    mov r2, #4 ; 0x4
5:     8518:    e59f1024    ldr r1, [pc, #36] ; 8544 <.te
xt+0x184>
6:     851c:    e1a0000d    mov r0, sp
7:     8520:    ebffffa0    bl 83a8 <.text-0x18>
8:    8524:    e5dd3000    ldrb    r3, [sp]
9:     8528:    e5dd1001    ldrb r1, [sp, #1]
10:    852c:    e59f0014    ldr r0, [pc, #20] ; 8548 <.te
xt+0x188>
11:    8530:    e1831401    orr r1, r3, r1, lsl #8
12:    8534:    ebffff98    bl 839c <.text-0x24>
13:    8538:    e3a00000    mov r0, #0 ; 0x0
14:    853c:    e28dd004    add sp, sp, #4 ; 0x4
15:    8540:    e8bd8000    ldmia sp!, {pc}
16:    8544:    00008648    andeq r8, r0, r8, asr #12
17:    8548:    00008640    andeq r8, r0, r0, asr #12


リスト7 逆アセンブルしたコード

 問題は8行目と9行目にありました.なんとldrbが2回,つまりバイト・アクセスを2回行っていました.16ビットでアクセスするように変更したつもりなのに,なぜか2回のバイト・アクセスになっているのでした.

 なぜ,バイト・アクセスなのでしょうか? 16ビット・アクセスの命令があったはずと思い,ARMのリファレンス・マニュアルを見ていてハッと気が付きました.ARMが16ビット・アクセスの命令に対応したのは,ARMv4からだったのです.コンパイラがどのARMアーキテクチャ・バージョンをターゲットにしたのかを調べるために,以下のようなコマンドを実行してみました.

  $ arm-linux-gnu-gcc -E -x c /dev/null
  -dM|grep __ARM_ARCH
  #define __ARM_ARCH_3__ 1

 デフォルトではARMv3がターゲットのようです.そこでARMv4をターゲットにするために「-march=armv4」というオプションを付けてみました.

  $ arm-linux-gnu-gcc -march=armv4 -E -x c
  /dev/null -dM|grep __ARM_ARCH
  #define __ARM_ARCH_4__ 1

 ようやくARMv4対応になったようです.そこでこのオプションを付けてもう一度コンパイルしたところ,リスト8のようなコードになりました.

1: 0000850c <main>:
2:    850c:    e52de004 str lr, [sp, #-4]!
3:    8510:    e24dd004    sub sp, sp, #4 ; 0x4
4:    8514:    e3a02004    mov r2, #4 ; 0x4
5:    8518:    e59f101c    ldr r1, [pc, #28] ; 853c <.te
xt+0x17c>
6:    851c:    e1a0000d     mov r0, sp
7:    8520:    ebffffa0    bl  83a8 <.text-0x18>
8:    8524:    e1dd10b0    ldrh  r1, [sp]
9:    8528:    e59f0010    ldr r0, [pc, #16] ; 8540 <.te
xt+0x180>
10:    852c:    ebffff9a    bl 839c <.text-0x24>
11:    8530:    e3a00000    mov r0, #0 ; 0x0
12:    8534:    e28dd004    add sp, sp, #4 ; 0x4
13:    8538:    e8bd8000    ldmia sp!, {pc}
14:    853c:    00008640    andeq  r8, r0, r0, asr #12
15:    8540:    00008638    andeq  r8, r0, r8, lsr r6


リスト8 ARMv4用にコンパイルしたコード

 8行目で2回のldrbの代わりに16ビット・アクセス命令のldrhが使われていることが確認できます.オシロスコープによる計測でもIOR信号は一度になっていました(図11).やはり,「ありえないことは,ありえない」のです.

zu11_01.gif
図11 オシロスコープの波形(16ビット・アクセスが1回)

組み込みキャッチアップ

お知らせ 一覧を見る

電子書籍の最新刊! FPGAマガジン No.12『ARMコアFPGA×Linux初体験』好評発売中

FPGAマガジン No.11『性能UP! アルゴリズム×手仕上げHDL』好評発売中! PDF版もあります

PICK UP用語

EV(電気自動車)

関連記事

EnOcean

関連記事

Android

関連記事

ニュース 一覧を見る
Tech Villageブログ

渡辺のぼるのロボコン・プロモータ日記

2年ぶりのブログ更新w

2016年10月 9日

Hamana Project

Hamana-8最終打ち上げ報告(その2)

2012年6月26日