Cerevo tech blog
DM355のインストールディスクを作る 後編
- 2009-03-12 (木)
- ハードウェア | 組み込みソフトウェア
こんにちは、Cerevoの稲垣です。
前回は、DM355のブート処理を概観し、SDカード用のブートローダ (SD-UBL) を試してエラーを起こすところまで扱いました。今回はSD-UBLを分析・修正して、実際にインストールディスクを作ってみたいと思います。
メッセージを分析
SD-UBLが出すメッセージはけっこう冗長なので保存して比較してみると、ブートモードとインストールモードではカーネル (Linux) とRAMディスクのロード先が入れ替わっていることが分かります。関係するメッセージだけ引用します:
ブート時のメッセージ: * Loading kernel sdcard_read sdc_src_addr=0x00081000 dst=0x82000000 len=0x00200000 * Loading ramdisk sdcard_read sdc_src_addr=0x00281000 dst=0x80700000 len=0x00400000 インストール時のメッセージ: * Flashing kernel sdcard_read sdc_src_addr=0x00081000 dst=0x80700000 len=0x00200000 * Flashing Root FS sdcard_read sdc_src_addr=0x00281000 dst=0x82000000 len=0x00400000
SDカードにアクセスできないU-Bootを使っていますから、ロード先が入れ替わっては起動するはずがありません。しかし、ひとまずU-Bootのパラメータをデフォルトから変更すれば起動しそうです。実際にU-Bootの自動起動を中止して起動スクリプトを書き直してやると起動しました。
ただし、このままのメモリ配置では9MiBより大きなRAMディスクをロードできません。やはりSD-UBLの修正が必要です。それでハックの方針は以下のようになります:
- Linuxとramdiskのロード先アドレスを変更する
- ロードサイズを変更する
なお、インストールモードのフラッシュ書き換え機能は、ブートメッセージによれば、TIがリリースしているユーティリティをベースにしているらしいので使いません。そのTIのユーティリティはNANDフラッシュの書き込みエラー処理とかしてないっぽいので、信用できないからです。
SD-UBLを解析する
まずはディスクイメージからSD-UBLを探します。RBLの仕様はTIが公開しているDM355のARM subsystemのデータシートに書かれていますから、RBLになったつもりでディスクイメージを見ていきます……すぐに見つかりますね。ダンプしてみると、第1セクタにUBLディスクリプタが書かれています:
00000200: 00 ed ac a1 00 01 00 00 2e 00 00 00 09 00 00 00
左から順に、マジックナンバー0xa1aced00、エントリポイントが0×100、サイズが0×2eセクタ、先頭は第9セクタ、という意味です。なおUBLはメモリ空間の0×0020にロードされるので解析には注意が必要です。
位置が分かったのでSD-UBLを切り出します:
dd if=dm355_boot.sdcard of=sd-ubl.bin bs=512 skip=9 count=$((0x2e))
逆アセンブルします:
arm_v5t_le-objdump -b binary -m arm -D sd-ubl.bin > sd-ubl.s
逆アセンブルしたソースを検索してみると、ロード元のアドレス0×41000とか0×81000を引数にして同じサブルーチンが3回ほど続けて呼ばれていることが分かります。関係する部分を引用します:
2fd8: e59f3064 ldr r3, [pc, #100] ; 0x3044
2fdc: e59f4064 ldr r4, [pc, #100] ; 0x3048
2fe0: e5932000 ldr r2, [r3] ; 0x15aa4
2fe4: e1a01004 mov r1, r4
2fe8: e1a02482 mov r2, r2, lsl #9
2fec: e3a00a41 mov r0, #266240 ; 0x41000
2ff0: ebfffe76 bl 0x29d0
3044: 00015aa4 andeq r5, r1, r4, lsr #21
3048: 81080000 tsthi r8, r0
304c: 00015594 muleq r1, r4, r5
5a84: 0000012c andeq r0, r0, ip, lsr #2
ARMのgccの関数呼び出し規約ではr0からr3が引数ですので、そっちのレジスタも見てみると、ロード先とサイズも引数として渡されているらしいことが分かります (簡単に書いてますが劇的な場面ですよ)。実にExcellentなコードですね。
0×15aa4という変なアドレスにアクセスしているので解説しておきましょう。TCMは0×00000と0×10000の両方からアクセスできるようになっているので、アドレス0×15aa4は0×5aa4と同じです。さらに、UBLのロードされるアドレスは0×00020ですから、0×15aa4へのアクセスは 0×15aa4 = 0×10000 + 0×20 + 0×5a84 と分解することができ、ソースの5a84:の部分へのアクセスになるわけです。
同様に解析するとU-Boot、Linux、ramdiskのロードがほぼ同じように書かれているらしいことが分かります。ロードする長さだけは変数としてメモリ上に置いてあります (グローバル変数なのでしょう……なぜだろう)。
バイナリパッチ
結局、具体的には以下のようにバイナリエディタでハックします (Emacsでは M-x hexl-find-file):
- Linuxとramdiskのロード先はハードコードされているので入れ替える:
- 3000: e3a01482 を e59f1054 に変更
- 301c: e59f1038 を e3a01482 に変更
- グローバル(?)変数になっているそれぞれのイメージサイズを変更する:
- 5a84: U-Bootのセクタ数
- 5a88 ramdiskのバイト数
- 5a8c Linuxのバイト数
淡々と結果だけ書いてしまいましたが、ARM命令は32bit固定長なのでバイナリパッチが簡単なのです。今回は値を入れ替えたりそのままメモリ上に変数として置いてある値を書き換えるだけなのでコードも増えませんし、PC相対のディスプレースメントだけちょっと計算すればお終いです。
あとはU-Bootの環境変数を書き換えておいて、SDカードに書き込めばインストールディスクのできあがりです。好きなインストーラが起動するように仕込みましょう:
dd of=/dev/sdc bs=512 seek=9 if=sd-ubl.bin dd of=/dev/sdc bs=512 seek=520 if=uboot.bin dd of=/dev/sdc bs=512 seek=1032 if=uImage #linux dd of=/dev/sdc bs=512 seek=5128 if=fs.bin #ramdisk
おしまい
SD-UBLがバグっているので多少バイナリパッチをしましたが、ARMのバイナリは割とハックしやすいと思います。ブートローダ程度のものなら皆さんもハックしてみてはいかがでしょうか?
- Comments: 0
- Trackbacks: 0
DM355のインストールディスクを作る 前編
- 2009-02-04 (水)
- 組み込みソフトウェア
こんにちは、Cerevoの稲垣です。今回も割と低レイヤーな話です。
今どきのPCは、買ってくるとHDDが内蔵されていてOS (Windowsとか) がインストールされているのが普通です。組み込みの機器も、やはり工場でファームウェアをインストールされて出荷されます。例えばNOR型のフラッシュROMを使う場合、最初からファームウェアの書かれたチップをハンダ付けするそうです。対してNAND型のフラッシュROMだと、生ROMを載せておいて基板が完成してから書き込むことになります。これはNANDフラッシュには不良ブロックがあるので、各自で対策を講じつつ書き込まないといけないからです。ちょうどPCのインストールで使うようなインストールディスクを作って量産工場に渡しておかないといけません。つまり今回はブートローダとかインストーラとかそういう話です。
DM355のブート処理
CPUに電源が入ると、PCの場合はROMに入っているBIOSが最初に走りだすわけですが、私が今相手にしているTI (Texas Instruments) のDM355でもやはりROMに入っているBIOSのようなものが走ります。TIの用語ではこれをRBL (ROM BootLoader) と呼んでいます。なおRBLによってロードされるプログラムのことは、TIの用語でUBL (User BootLoader) と言います。RBLはCPUに直結したスイッチによって四通りの場所からUBLをロードすることができます:
- NANDフラッシュROM
- NORフラッシュROM
- SDメモリカード
- シリアルポート
したがってインストール“ディスク”はSDカードだったりします。もちろんシリアルポートにPCを繋げてインストールとかいう方法も使えなくはありませんが、遅いしPCが必要だし面倒なのでやらないと思います。
RBLはロード作業以外の、DDRメモリの初期化とかは (恐らくは) してくれません。この辺はややこしいこと満載なのですが、DM355のCPUコアはARMなので、TCM (Tightly Coupled Memory――密結合メモリ) とかAIM (ARM Internal Memory――ミサイルじゃないよ) とか呼ばれるメモリを *CPUに内蔵* することができるのです。キャッシュとは別物です。DM355の場合、TCM領域は64KiBあり、32KiBのRAMと、8KiBのROMと、24KiBの予約領域が配置されています。ともかく、RBLはCPU内のROMに書かれていて、CPU内のRAMにプログラムをロードしてくれるわけです。RBLではCPU内部のメモリしか使わないので、DDRメモリの初期化なんかは段階的にロードされたプログラムがやる必要があるわけですね。その後、ロードされたUBLは、DDRメモリを初期化してNANDフラッシュなりSDカードなりからU-BootなどのLinux用ブートローダを読み込むわけですが、この辺はフツーなので割愛します。
DM355の起動に関する仕様はだいたいこんな感じです。身も蓋もない言い方をすれば、インストールSDカードにはSDカード用のUBLを入れればいいのです。ところが、TIはSDカード用のUBLをリリースしていません。TIの掲示板を見ると……
https://community.ti.com/forums/p/1970/7286.aspx
さすがはTIだ。SDカード対応のU-Bootを先にリリースする予定とは……順序が逆なんじゃないのか。
有志のSDカード用UBLを試す
というわけで、TIよりも先にSDカード対応のUBL (勝手にSD-UBLと呼びます) を開発して、インストールディスクのデモを作った人がいます:
http://community.ti.com/forums/t/2299.aspx
U-BootとLinuxとramdiskイメージをロードして、単に起動したりNANDフラッシュに書き込んだりしてくれるようです (一応、ディスクイメージを見なくてもなんとなく分かるように記事を書いたつもりですが、気になる人はダウンロードして見てください)。これを適当に解析して改造すればインストールディスクが作れそうです。
とりあえずSDカードに書き込んで動かしてみると、シリアルコンソールにバナーとメニューが出ます。ブートとインストール、二つの機能があります:
SD Card DM35x boot loader by Constantine Shulyupin http://www.LinuxDriver.co.il/, sponsored by Applitec based on TI DM35x FlashAndBootUtils 1.10 SFT and SpectrumDigital evmdm355 v1 Compiled on Dec 18 2008 at 13:16:08 scd_nand_copy sd_init MMCSD_initCard 1 - boot; 2 - install; 3 - global flash erase and install
まずは単純にブートさせてみると……正常に起動しません。U-BootがLinuxを起動してすぐリセットがかかります。一方、インストール機能は正常に動作します。まずはデバッグが必要なようです。
つづく
次回の後編では、SD-UBLのバグを探して、バイナリを直接書き直し、実際にインストールディスクを作ります。
- Comments: 0
- Trackbacks: 0
Beagle Board用 ツールチェインとAndroidの起動のおまけ
- 2009-01-22 (木)
- ハードウェア | 組み込みソフトウェア
Cerevo まつけんです。
すこし間があいてしまいましたが、Beagle Board用第2弾をお送りしたいと思います。
今回は、BeagleBoard上で実行することが可能なバイナリが作成できるようになるための準備をしたいと思います。
その後、ツールチェインをつくるだけではおもしろくないので、Androidをコンパイルして起動してみましょう。といっても、Androidの場合、ツールチェインが必要になるのは、カーネルコンパイル時だけだったりしますが。
ツールチェインとは
まずは、ツールチェインって何?というところですが、私の理解では、コンパイラ、リンカなどのBeagleBoard上で動くプログラムを作成するためのツール集です。linuxの場合は、大抵、binutils+gcc+glibc or uclibcなどの組み合わせになると思います。(newlibとかもあるのかな)
そして、こういった組込機器向けの場合、これらツールチェインは、ストレージ容量やコンパイル速度の都合上、ビルドはPC上で行うことが多いかと思います。その場合、クロスコンパイラとして、ツールチェインを作成し、PCでビルドして、BeagleBoard上で動作させるという流れになります。
ツールチェイン作成のための環境
まず、今回のビルド作業を2パターンの環境で試しました。どちらでも、作業する内容は同じで問題なく動作しました。
- KVM(qemu)上のi386なマシン(ホストは、Phenom 9950BE,メモリ8GBで、ゲストには2GB割り当てています。)のGentoo
- Amazon EC2 c1.xlarge上のGentoo
今回は、EC2を使って作業というのを体験してみたかったので、同時に試してみました。
EC2 c1.xlargeは確かに速いんですが、高いです。今回の作業で20hくらいつかったのですが、結局、カスタムAMIをS3においたり、ビルド作業用ディレクトリをEBSに置いたりすると、30$近くかかりました。興味半分で、c1.xlargeにしたのですが、c1.mediumで十分だと思います。
メリットとしては、自宅などに簡単にlinuxの環境なんか用意できねーよ、みたいな人にはかなりよいかも。ツールチェイン作りって基本的に、CPUパワー命なので、あまり非力なマシンだと時間かかりまくりで悲しくなってしまいますし。
ツールチェイン作成
さて、本題のツールチェイン作成です。作成には、結構いろんなパターンがあります。
一番、漢な手段は全部、手動でコンパイルとかなのでしょうが、当然、そんなのはやりたくありません。逆に、一番、お手軽なパターンは、BeagleBoard向けの場合、もっともメジャーなのは、バイナリな形で配布されているCode Sourcery ARM Sourcery G++ 2007q3.をダウンロードしてきてインストールするというパターンです。
今回は、その中間くらいのパターンとして、ツールを利用して、コンパイルする手段をとりたいと思います。どういう流れでツールチェインが作成されるのかを知りたかったのでこれでいきました。そこで、Gentooには、すばらしいツールがあります。crossdevです。これは、portageで提供されているツールチェイン作成ツールです。
# PORTAGE_OVERLAY=/opt/crossdev crossdev -t arm-gentoo-linux-gnueabi
と実行するだけで、ツールチェインが作成できます。
ただ、”-S”オプションやバージョンを指定しないと、最新バージョンが選択され、結構、Floating Exceptionなどでコンパイルに失敗したりします。その場合は、バージョンをオプションで指定しましょう。
今回は、かなり新しめのでもいけるかなーということで、そのまま実行したら、とりあえず、うまくいったので、それを利用しています。
- binutils: 2.19
- gcc: 4.3.2
- glibc: 2.9
コマンドが完了すると、arm-gentoo-linux-gccなどのコマンドが入ります。
簡単にためすなら、
$ echo 'int main(){return 0;}' > crossdev-test.c $ arm-gentoo-linux-gnueabi-gcc -Wall crossdev-test.c -o crossdev_test $ file crossdev_test crossdev_test: ELF 32-bit LSB executable, ARM, version 1 (SYSV), for GNU/Linux 2.6.16, dynamically linked (uses shared libs), not stripped
と実行して、ARM向けのバイナリであることが確認できます。
Androidのビルド
さて、最後にAndroidのビルドを試してみます。
Android on BeagleBoardに関しては、先人がすでにポーティングしてくれています。それに従いましょう。
Android Porting guide for Beagle Board
最初のカーネルコンパイル時に、
$make ARCH=arm omap3_beagle_android_defconfig
$make ARCH=arm CROSS_COMPILE=PATH_TO_CODE_SOURCERY_TOOL_CHAIN uImage
となっていますが、このPATH_TO_CODE_SOURCERY_TOOL_CHAINに先ほど作成したarm-gentoo-linux-gnueabi-を渡します。そうすると、コンパイルされるかと思います。
あとは、ほぼ書かれている手順どおりでいけました。パッチがうまくあたらない部分などは手動であててみました。
実際には、はじめは、linux-omap3のツリーに手動でAndroid用の拡張を自力でパッチを当てていたのですが、そのあとパッチを発見してちょっと悲しい思いをしたりしました。
そして、起動したときのムービーが以下です。
最後に
というわけで、ツールチェインの作成はおわりました。これで、BeagleBoard上で様々な自作プログラムを動作させられるところまでは来ました。あとは、このツールチェインをつかってrootfsを構築すれば、自分でBeagleBoard上のすべてソフトウェアをコンパイルできるようになります。あとは、お好みの構成を考えて、なんでも好きなものがつくれるようになる。。。はずです。
今回の内容は、Gentoo Embedded Handbookをかなり参考にしています。ユーザランドの構築もこちらには書かれています。是非、参考にしてみてください。
- Comments: 0
- Trackbacks: 1
電源が入らなくなったchumbyを復活させてみる ~その1~
- 2008-12-25 (木)
- ハードウェア
はじめまして。Cerevoの鈴木です。
私も稲垣と同じく組み込みソフトウェアの担当なのですが、より低レイヤの方が守備範囲となっています。
最近デザインの参考に色々なガジェットを集めているのですが、先日ある方から「壊れているchumbyならあるけど」という連絡があり、頂けることになりました。
折角なのでchumbyの内部を勉強しつつ、いじくり回して復活させるまでの体験記みたいなものを今後紹介していこうと思います。
初見
このブログをご覧になっている方達に「chumbyとは…」という話は不要でしょう。
最近はビックカメラやソフマップでも入手できるようになりました。
ところで、頂いたchumbyは外側のカバーが剥がされて、中の臓物がむき出しのちょっと可哀想な状態でした。

さてさて、どこから手をつけましょうか…
まずは電源投入
見つめていても仕方ないので、何はともあれまずは電源を投入してみます。
フロントのLCDに何か表示されるかな、と思っていたのですが、何も映りません。。。
一瞬でも変化しないか、と数回電源のON/OFFをしてみましたが、何の変化もしません。。。
本当に電源が投入されているのか確認したくなってきました。
一度、電源電圧をテスタで測定してみたほうが良さそうです。
テスタでどこに当たればよいのか?それを知るためには回路図を入手する必要があります。
回路図を入手
Chumbyはほんの一部のソフトを除いて、ハードウェアを含めたほぼ全ての情報がオープンになっています。
回路図ももちろん入手可能です。
ただし、一度 http://www.chumby.com/developers にアクセスしてアカウントを登録する必要があります。
アカウント登録後は、Hardwareのページから “Complete schematics and layout for the chumby core unit with cross-references” のリンクを辿ることで Rev37_release.pdf がダウンロードできるはずです。
大元の電源電圧を確認してみる
電源回路は4ページ目になります。
(大きな画像で見る)
今回はバッテリを使わずACアダプタで動かします。ACアダプタ出力電圧はACアダプタを良く見ると書いてあって +12V です。
回路図を見てみると、内部ではこの+12Vから+5V, +3.3V, +1.8Vが生成されているようです。
信号名 DCIN_PROTECTED は元々 RAW_PWR の入力に対してフィルタやヒューズ、逆流防止用のダイオードを経由して生成されていて、(ほぼ)ACアダプタ出力なので、R300の片側が測定ポイントに出来そうです。
測定がしにくいので、測定に関係のないLCDは外すことにします。
外す前の状態は↓のような感じです。

LCDがメインボードと薄い銅板のようなモノでハンダ付けされているので、これを外さないといけません。
外した後の状態は↓のような感じになります。あとはLCDから伸びているフレキシブル基盤を外すことで、メインボードがむき出しにできます。

メインボードのプリント基板外周の金色のパターンはGNDなので、まずはR300の片側とGND間の電圧を測定してみます。
測定結果は…何だか良く分かりません。+12Vではなく値がフラフラして安定していません。
どうも、どこかで電源関係がおかしくなっているようです。
電源とGNDの抵抗値を測定してみる
こんなときは電源とGNDがどこかでショートしていることを疑ったほうがいいです。
ちょっとしたコツとしては、末端の方から調べてみるということでしょうか。
回路図を見ると、chumbyの場合はACアダプタ入力(+12V)→+5V→+3.3V, +1.8Vという系統になっていることが分かります。
そのため、一度chumbyの電源をOFF(意外と忘れやすい)にしてから、まずは+3.3V, +1.8VとGND間の抵抗値を確認してみます。
+3.3VはJ302で確認できるので、GND間との抵抗値を測定します。
測定結果は…801Ω、大丈夫そうです。
次に+1.8VはJ304で確認できますので、同じ調子で測定してみます。
測定結果は…3Ω、これはちょっと低すぎです。
モノにもよりますが、通常電源ラインとGND間の抵抗値は数100Ω~数kΩ程度の値になります。3Ωはあまりにも低い値です。
抵抗値が低いということは+1.8Vには多くの電流が流れるということを意味しています。
ここで先程説明した系統を思い出してほしいのですが、+1.8Vに多くの電流が流れるということは、遡るように+5Vにも多くの電流が流れて、最終的に+12Vにも多くの電流が流れることになります。
電源ICには流すことが出来る電流が仕様で決まっていて、それ以上になると電圧が下がります。
もしかして+1.8Vの過負荷が原因で、+12Vの電圧が不安定になっているのでしょうか…
負荷を減らしてみる
+1.8Vがおかしいのは何となくわかりましたが、何が原因なのかは良く分かりません。
こういうときは負荷となっているモノをばっさりと切り離してしまうとはっきりします。
+1.8Vが回路に供給されないようにすればいい訳です。
今回の場合は、+1.8Vを生成する電源ICを外してみるのが手っ取り早い感じです。
そこで+1.8Vを生成しているIC、U302を外します。
この程度の大きさのICであれば、ハンダゴテ2本を使うと以外と簡単に外せます。
U302を外したあと、+1.8V~GND間の抵抗値を測定してみます。
測定結果は…2.6kΩなので、負荷側には問題なさそうです。どうやらこのU302が壊れているようです。
ここまできたので改めて電源を投入してみます。+12Vの値が正常になりました!
次回の予告
さて+12Vはマトモになりましたが、じゃあ次の系統の+5Vはというと…まだフラフラして安定していません。
そして回路図を見てみると、+5Vを生成している電源チップに CHUMBY_ON という制御信号が接続されています。あやしいです。
(大きな画像で見る)
この辺りを少し追いかけてみようと思います。
- Comments: 1
- Trackbacks: 0
MSP430のコードを小さくするテクニック
- 2008-12-16 (火)
- 組み込みソフトウェア
Cerevoの稲垣です。
私は組み込みソフトウェア開発の担当で、主にLinuxを扱っていますが、ボードに載っているマイクロコントローラのプログラミングもします。最近はMSP430というTIのマイコンを相手にしているので、MSP430のアセンブリ言語プログラミングについて、x86アセンブリの経験者を対象にして書きたいと思います。
MSP430のレジスタとアドレッシング
MSP430には16本のレジスタがありますが、そのうちのr0はPC (プログラムカウンタ)、r1はSP (スタックポインタ)、r2はSR (ステータスレジスタ) および定数ジェネレータ、r3は定数ジェネレータとなっていて、汎用レジスタとして使えるのはr4からr15の12本のレジスタです。スタックの扱いはx86と同じです (ARMにあるようなリンクレジスタはない)。
MSP430の2オペランド命令はソースとデスティネーションが直交していて、それぞれ以下のモードが使えます:
ソース:
- r4 ; レジスタモード
- foo(r4) ; インデックスモード
- @r4 ; 間接レジスタモード
- @r4+ ; 間接自動インクリメント
デスティネーション:
- r4 ; レジスタモード
- foo(r4) ; インデックスモード
ソース・デスティネーションの両方にメモリオペランドを使うこともできる点がx86とは大きく異なる点と言えます (x86だとそういう命令はpush、pop、movsくらいしかありません)。インデックスモードを使うとディスプレースメントが付くので命令は可変長です。
オペランドのエンコーディングは上記の4種類だけですが、PCおよび定数ジェネレータとの組み合わせによって以下のアドレッシングモード
が実現されています:
- foo ; シンボリックモード (PCを使ったインデックスモード、いわゆるPC相対)
- &foo ; 絶対モード (定数生成レジスタをインデックスにしたインデックスモード)
- #foo ; 即時モード (PC間接自動インクリメントモード……これは面白い実装だと思います)
MSP430命令の注意点
演算命令はx86とほぼ共通なものが揃っていますが細かいところが違います:
- オペランドはソース, デスティネーション の順に書きます。
- バイト演算でレジスタをデスティネーションにすると上位バイトがクリアされます。それで、and #0xff, r4 は mov.b r4, r4 で代用すると1ワード節約できます。
- シフト・ローテートは1ビットづつしかできません。上位バイトと下位バイトを入れ替えるswpb命令が用意されているので組み合わせてシフトすることになります。
フラグ関連は微妙でありながらけっこう重要な違いがあります:
- bic (ビットクリア)、bis (ビットセット) ではフラグは変化しません。
- andとxorでは、演算結果が0でなければキャリーフラグがセットされます。
- subとcmpで生じるキャリーフラグはx86とは逆です。けっこう混乱します。
- 補助キャリーとパリティはありません。x86でも滅多に使わないフラグですが。
callは、jmpのようなオフセット値によるエンコーディングではなく、通常のソースオペランドで実現されています。したがって、callを使ったコードをROMからRAMにコピーするときはリロケート処理が必要です。あるいはオペランドをレジスタやメモリ(PC相対)にしておく方が楽かも知れません。
MSP430の定数ジェネレータを利用する
定数ジェネレータは -1, 0, 1, 2, 4, 8 を生成することができ、これを使うと即値の分の1ワードを節約することができます。例えば7回ループしたいとき、普通は以下のようにします:
mov #7, r4 ; カウンタ
foo:
dec r4 ; 減算
jnz foo
しかしこれを以下のように置き換えると1ワード節約できます:
mov #2, r4 ; カウンタ
foo:
add.b r4, r4 ; シフト
jnz foo
定数ジェネレータのお蔭で、インクリメントに1以外の値を使ってもコードは短いままです。それで、例えば32回のループは以下のようにすれば短くなります:
mov #0, r4
foo:
add.b #8, r4 ; 32回ループ
jnz foo
他に以下の回数のループは1ワード短く書くことができますので、暇があれば考えてみると面白いかも知れません:
3 5 6 7 9 13 14 15 16 31 63 127 255 8191 16383 32767 65535
MSP430の自動インクリメントも利用する
x86ではinc・decが1バイトでできるため、ループの終了条件はほぼカウンタ一択です。しかし、MSP430ではアドレスの自動インクリメントがあるため、メモリアクセスをループさせる場合は、終了アドレスとポインタを比較した方が短いコードになることがあります。例えば、通常のメモリ間コピーは以下のようにします:
mov #src, r4
mov #count, r5
foo:
mov @r4+, dest-src-2(r4) ; 自動インクリメント、メモリ間コピー
dec r5
jnz foo
しかしcountが定数ジェネレータで生成できない場合、mov #count, r5とdec r5で3ワードも消費してしまいます。これをアドレス比較に書き換えると1ワード節約できます:
mov #src, r4
foo:
mov @r4+, dest-src-2(r4) ; 自動インクリメント、メモリ間コピー
cmp #src+count*2, r4
jnz foo
自動インクリメントと定数ジェネレータがあるMSP430ならではの最適化です。
最適化できない場合
x86だと即値とレジスタの間のmovには専用形が用意されていますが、MSP430にはありません。したがって、例えば r4 = r5 ^ 0×1234; を実行する場合、以下のどちらのコードでも結果は全く同じです:
; x86ではこっちの方が1バイト短い
mov #0x1234, r4
xor r5, r4 ; x86では1バイト長くなる
mov r5, r4
xor #0x1234, r4
MSP430ではデスティネーションのエンコーディングが2種類しかないので、メモリデスティネーションをアクセスするとディスプレースメントが必ずついてしまいます。これを最適化する方法はありません。メモリマップされたペリフェラルを操作することが多いので多分これでいいのですが、なんだかすっきりしない気分になります。
終わりに
MSP430のROMは何KBとある上に、新しいチップは安くてRAMもたくさん載っているので、ここで書いたような1ワードを争う最適化は滅多に必要ありません。でも100クロックかかる処理が90クロックで済むようになったら、消費電力は10%減るわけです。そう考えると、21世紀もまだまだアセンブリ言語の出番はあるのかも知れません。あるといいな……
- Comments: 0
- Trackbacks: 0