連載
» 2008年12月17日 00時00分 公開

−ザ・組み込み−ソフトウェアのハードウェア化(5):「これって本来マイコンがするべき仕事なの?」に注目 (2/4)

[鳥海佳孝 設計アナリスト,@IT MONOist]

LCD時計プログラムをさらに分析

 それでは、「いかにして“時間稼ぎ”の部分をハードウェア化するのか」を考えていきましょう! といきたいところですが、その前にもう少し“時間稼ぎ”の部分の詳細を見ておきましょう。

 “プログラムの詳細を見る”ときに必要なのは、やはりアセンブリ言語です。なかなかC言語レベルで、ハードウェア化を考えようとしても難しいものがあります。なぜなら、作成したプログラムをアセンブリに落とすことによってのみ、その詳細を知ることができるからです。つまり、よりCPUに“近い”ところで議論することが、CPUの負荷を減らすための近道になるということです。

 それでは、実行されているプログラムが一体どんな命令体系によって処理されているのかを見てみましょう。以下のコマンドを使用して、実行ファイルを逆アセンブルし、その出力をファイル(lcd_clock.objdump)にリダイレクトしてください(注)。

# h8300-linux-elf-objdump -D lcd_clock.elf > lcd_clock.objdump 
※注:ファイルの出力には時間がかかります。


 「lcd_clock.objdump」ファイルの中には、AKI−H8/3069Fのマイコンで実行されるC言語で書かれたプログラムのアセンブリのリストが出力されています。このアセンブリ出力をこれから解析していくわけですが、解析の際にどのアセンブリがどの命令なのか、またどんな命令なのかが分からなくては解析のしようがありません。そこで、ルネサス テクノロジ社のサイトから以下のマニュアルをダウンロードして手元にご用意ください。

 準備はよろしいでしょうか。C言語のmainの中でLCD絡みで最初に呼ばれているサブルーチンが「LCD_Init」です。前述したアセンブリの中では、「00000204 <_LCD_Init>:」と表記されているところからこのサブルーチンの命令が始まっています。

 このサブルーチンの最初の方では、

1.LCD_Initのサブルーチンに飛んできたので、必要な情報(er6、er4)をスタックに退避

mov.l   er6,@-er7
mov.l   er7,er6
mov.l   er4,@-er7 

       ↓

2.er0を初期化して、wait関数の引数である20(0x14)をr0lに設定

sub.l   er0,er0
add.b   #0x14,r0l 

       ↓

3.wait関数のあるメモリ番地(0x13a)にジャンプ

jsr     @0x13a:24 

       ↓

4.waitのサブルーチンに飛んできたので、必要な情報(er6、er4)をスタックに退避

mov.l   er6,@-er7
mov.l   er7,er6
mov.l   er4,@-er7 

       ↓

5.er1を初期化して、forループの中で指定している50(0x32)をr1lに設定

sub.l   er1,er1
add.b   #0x32,r1l 

       ↓

6.forループを実行するために(0x608)にジャンプ

jsr     @0x608:24 

       ↓

7.wait関数内にあるms * 50を求めforループを実行

mov.w   r1,r2
mulxu.w r0,er2
mov.w   e0,r3
beq .+4 (0x614)
mulxu.w r1,er3
add.w   r3,e2
mov.w   e1,r3
beq .+4 (0x61c)
mulxu.w r0,er3
add.w   r3,e2
mov.l   er2,er0
rts 

       ・

       ・

       ・

という具合に命令が流れていきます。これより先の流れまで追っていると、ページ数がかさむ一方なので今回はここまでにしておきますが、ご興味のある方はとことん追いかけてみてください。きっとソフトウェアのハードウェア化に役に立つと思います。

 さて、これらアセンブリの命令は、基本的にそれぞれ、2、4、6、8、10サイクルのいずれかのクロック数がかかります。例えば、「H8/300Hシリーズ プログラミングマニュアル」の181ページ「2.4.命令コード一覧」にある「MOV.L @(d:24,ERs),ERd」命令は、10サイクル(バイト)命令ですので、H8/3069Fマイコンの周波数が20MHz(50ns)であることから、1つの命令が完了するまでに少なくとも500nsかかることになります。さらに、関数の呼び出しの際、スタックなどをするときにはRAMのアクセスを行うため、1回のアクセスに少なくとも2サイクル、または3サイクル必要となります(H8/300Hシリーズ プログラミングマニュアルの213ページ「4.基本動作タイミング」を参照のこと)。

 ここまでの解説で分かるように、よくよく分析してみると“C言語レベルの関数の実行にはいろいろなオーバーヘッドが存在”しています。

 C言語で記述する以上、実際の実行に関してはコンパイラ任せになっているので仕方ないところではありますが、Cのような高級言語のレベルでは、ソフトウェアの肥大化の問題点が見えてこない(見えづらい)といえます。だからこそ、ソフトウェアのハードウェア化を行うにはアセンブリレベルでの解析が必要なのです。

Copyright © ITmedia, Inc. All Rights Reserved.