ブートローダーを書き込んだら、マイコンボードのディップスイッチを左から「ON、OFF、ON、OFF(上、下、上、下)」に設定します。PC側では「Tera Term」などの端末エミュレータ(注4)を「9600bps、データ長8ビット、ストップビット長1、パリティなし、フロー制御なし、XON/XOFFなし」で起動し、マイコンボードの電源を入れ直します。
これでマイコンボードがフラッシュROMから起動し、先ほど書き込んだブートローダーが動作します。リスト3のように「kzload>」というプロンプトが出力され、指示待ちの状態になっていれば成功です。リスト3は「cu」というシンプルなシリアル接続用のソフトウェアで接続しているところです。
kzload (kozos boot loader) started. kzload>
この状態で「load」と入力すると、ブートローダーは実行モジュールのダウンロード待ちに入ります。実行モジュールは「XMODEM」というプロトコルで、シリアル経由で転送できます。端末エミュレータのXMODEMファイル送信機能を利用することで、「Hello World」の実行モジュールである「kozos」をマイコンボード側に転送します。
リスト4は「cu」と、さらに「lrzsz」というXMODEM通信用ソフトウェアを利用した場合の例です。cuでは「load」の実行後にキーボードから「~」「C」と入力し、さらに続けて「lsx <ファイル名>」と入力することで、指定したファイルをXMODEM転送できます。
kzload (kozos boot loader) started. kzload> load ~CLocal command? lsx kozos Sending kozos, 11 blocks: Give your local XMODEM receive command now. Bytes Sent: 1536 BPS:165 Transfer complete XMODEM receive succeeded. kzload>
転送が完了した後、「run」と入力すると、実行モジュールに制御を渡します。リスト5で、「starting from……」と出力されているのはブートローダー(kzload)の出力、その後の「Hello World!」が、ブートローダーから起動された実行モジュール(kozos)の出力です。
kzload> run starting from entry point: ffc020 Hello World! >
ここで、KOZOSのブートローダーの動作について、簡単に説明します。
ブートローダーのファイル構成を表1に示します。
ファイル名 | 行数 | 内容 |
---|---|---|
defines.h | 11 | 各種定義 |
ld.scr | 60 | リンカスクリプト |
lib.c | 135 | ライブラリ |
lib.h | 18 | 「lib.c」のヘッダファイル |
main.c | 104 | メイン関数 |
serial.c | 112 | シリアルドライバ |
serial.h | 10 | 「serial.c」のヘッダファイル |
startup.s | 10 | スタートアップ(アセンブラ) |
vector.c | 18 | 割り込みベクターのテーブル |
elf.c | 94 | ELF形式の解析 |
elf.h | 6 | 「elf.c」のヘッダファイル |
xmodem.c | 94 | XMODEMによるファイル転送 |
xmodem.h | 6 | 「xmodem.c」のヘッダファイル |
合計 | 678 | 全ソースコードの総ステップ数 |
表1 ブートローダーのソースコード一覧 |
「ライブラリ」や「リンカスクリプト」などについては連載第2回で説明しました。今回は、新たに追加されている「xmodem.c」と「elf.c」というファイルについて説明します。
まず、「xmodem.c」について説明しましょう。
PCからマイコンボードへのファイル転送には、「XMODEM」というプロトコルを用います。これは、図2のようにブロック単位でデータを転送するプロトコルで、古くから使われているものです。仕様自体はレガシーで、転送効率が悪い、ファイルのサイズ情報を送れないなどの幾つかの欠点がありますが、とても“シンプル”な仕様のため、簡単に実装することができます。
実際に、ブートローダーのXMODEM処理部分を見てみましょう。リスト6は「xmodem.c」から、ブロック単位での受信処理部分を抜粋したものです。
/* ブロック単位での受信 */ static int xmodem_read_block(unsigned char block_number, char *buf) { unsigned char c, block_num, check_sum; int i; block_num = serial_recv_byte(SERIAL_DEFAULT_DEVICE); if (block_num != block_number) return -1; block_num ^= serial_recv_byte(SERIAL_DEFAULT_DEVICE); if (block_num != 0xff) return -1; check_sum = 0; for (i = 0; i < XMODEM_BLOCK_SIZE; i++) { c = serial_recv_byte(SERIAL_DEFAULT_DEVICE); *(buf++) = c; check_sum += c; } check_sum ^= serial_recv_byte(SERIAL_DEFAULT_DEVICE); if (check_sum) return -1; return i; }
リスト6 ブロック単位での受信部分 |
「xmodem.c」では、SOHを受信するとリスト6の「xmodem_read_block()」が呼ばれます。「xmodem_read_block()」では、まず1バイトのブロック番号を受信し、さらに後続の1バイトを受信・ビット反転させ、ブロック番号に一致していることを確認します。続いて、128バイトのデータを受信し、最後に1バイトのチェックサムと比較してデータが壊れていないことを確認します。
図2とリスト6を見比べてみてください。XMODEMのブロック構造は、非常に簡潔なため、ブロック受信部分は数十行で実装できています。さらに「xmodem.c」は、実はたったの94行です。「ファイル転送」というと難しく聞こえるかもしれませんが、このようにシンプルなプロトコルであるため、受信部分のみであればたった数百行で書けてしまうものなのです(注5)。
Copyright © ITmedia, Inc. All Rights Reserved.