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

荘司靖

tag: 組み込み

技術解説 2008年4月 8日

● 0x1234ABCDか0xCDAB3412か

 それでは実際の例を示し,どんな時にエンディアンが問題になるかを見ていきましょう.

 仮に,あるデバイスのFIFOから32ビットのデータを読み込まなければならないとします.このFIFOからデータを受け取るために,x86のデバイス・ドライバ・プログラマは以下のようなコードを書きました.

  u32 val = *(u32 *)FIFO_ADDR;

 どうやら,このデバイス・ドライバはx86上では正しく動いたようです.しかし,このコードをそのままビッグ・エンディアンに設定したPowerPCに移植したところ,思ったように動かなくなりました.

 この問題を簡単にアプリケーション(C言語のソース・コード)で表したものがリスト1です.

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


リスト1 どちらのエンディアンか

 2行目のヘッダは,コードの見た目をLinuxのデバイス・ドライバに似せるために使っています注2.デバイス・ドライバ内で使える「u32」という型は,ユーザ・ランドではPOSIXのネーム・スペースを侵害してしまいます.そこで「__」を先頭に付けてネーム・スペースを汚さないように変更したものが,アプリケーションから使えるようになっています.u32はUnsigned(符号なし)の32ビット変数という意味です.

注2;カーネルのヘッダ・ファイルをアプリケーションでインクルードするのはあまり良くない.自分の責任で使用すること.

 6行目では,FIFOの代わりにスタック上にバッファをとっています.初期値は,アドレスの小さい方から0x12 0x34 0xAB 0xCDとなるようにしました.

 9行目で,FIFOから値を読み出して,変数valに代入します.そして読み出した値をprintf()を使って出力しています.

 本当のデバイス・ドライバであれば,受け取ったデータを処理したり,初期化関数内でデバイスIDを読み出して比較したり(この場合,FIFOではなくデバイスのレジスタだが)する場合に似たようなことを行います.さて,上記のコードをx86上でコンパイルして実行すると,

  $ ./a.out
  0xCDAB3412

と表示されます.これはx86がリトル・エンディアンだからです.それではPowerPCで実行するとどうなるのでしょうか?

  $ ./a.out
  0x1234ABCD

となりました.x86と違い,PowerPCはビッグ・エンディアンなので,メモリに並んだように値が読めます.混乱した人はもう一度,図2と見比べてみてください.

● マクロle32_to_cpu

 さて,普通に読み込むと値が異なることは分かりました.しかし,このままではデバイス・ドライバを移植することがとても面倒です.Linuxではこの問題を解決するために,エンディアンによるバイト・オーダの違いを吸収するマクロを用意しています.マクロはエンディアンごとに,

  include/linux/byteorder/little_endian.h
  include/linux/byteorder/big_endian.h

で定義されています.リトル・エンディアンで32ビット書き込みを行う場合はcpu_to_le32()が,読み込みの場合は le32_to_cpu()が使えます.このほかにも,ビッグ・エンディアン用のbe32_to_cpu()とcpu_to_be32(),および16ビット用や64ビット用のマクロも用意されています.8ビット・アクセスについては,エンディアンに関係ないのでマクロは用意されていません.

 マクロを使うことで,コードそのものの移植性が高くなります.しかし,上記のヘッダを直接インクルードしてしまうと,結局デバイス・ドライバをエンディアンに依存させてしまうことになります.そこで,デバイス・ドライバからは,上記のヘッダ・ファイルではなく,

  include/asm/byteorder.h

をインクルードします.asm/byteorder.hはビッグ・エンディアンがターゲットであればlinux/byteorder/big_endian.hを,リトル・エンディアンであればlinux/byteorder/little_endian.hをインクルードするようになっています.これでエンディアンに依存させずに,デバイス・ドライバで必要なマクロが使えるようになります.

組み込みキャッチアップ

お知らせ 一覧を見る

電子書籍の最新刊! 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日