前回は、PICがサーバからバイナリをダウンロードし、自分自身を書き換えるプログラムを紹介しました。ネットワークアップデートに頼るのはいろいろ考えさせられなくもないですが、PHP版のアセンブラで出力したバイナリをPICに書き込むことができるようになりました。
今回は、このダウンローダの動作を詳しく見てみましょう。プログラムの全体は、前回圧縮して用意したアセンブラプログラム「download.asm」となります。
ダウンローダの仕組み
PICは、EEPROMから命令コードを直接読み出して実行しています。このため、メモリ書き換え中は命令コードが読み出せなくなり、実行も停止してしまうという問題があります。HTTPでは全データが一度に送られてきますので、書き込み中に実行が止まると、データを取り落してしまいます。もちろんLANモジュールとのあいだでフロー制御をおこなう方法はありますが、今回はソフトで逃げることにしました。
具体的には、HTTPリクエストを出して、書き込む必要がある部分だけをRAMに保存します。RAMは容量があまりありませんが、あふれた部分はとりあえず捨ててしまいます。そして、RAMのデータを使ってプログラムメモリに書き込みます。次に、もう一度HTTPリクエストを出します。先ほど書き込んだところは、コンペアによって飛ばされます。これを繰り返すことで、最後まで書き込むことができます。
では、全体の流れを見てみましょう。
最初に、LANモジュールからの信号線をチェックします。Hならスイッチが押されていないと判断して、ダウンロード済みのプログラムを実行します。Lが一定時間続いた場合は、ダウンロードモードに入ります。LANモジュールの準備ができるのを待って、HTTPリクエストを発行します。
HTTPレスポンスを解析し、バイナリ部分を取り出します。プログラム用のEEPROMは16ワード単位で消去しないといけないので、16ワード単位でRAMに取り込むようにします。
受信したバイナリと、すでにEEPROMにあるプログラムを比較します。一致していれば書き込みの必要はないので、飛ばします。ついでに、1行のチェックサムの計算も一緒にやってしまいます。もしチェックサムが合わなければ、エラーとします(ここではledにコードを表示しています)。
今回はHTTPのリクエストでしたが、PICでHTTPリクエストを扱う方法については、『組込みプレスVol.19』に詳しい解説があります。
RAMがいっぱいになったら、書き込みに入ります。完了したら、再度HTTPリクエストを発行して、データの取り込みとコンペアをおこないます。全データのコンペアが成功したら、ダウンロードしたプログラムを実行します。
書き込みの方法は、PICの種類によって異なります。PIC16F88では、最初にイレースが必要で、一度に32ワードが消去されます。16F883では、最初の4ワードを書き込むと、16ワードがイレースされますので、続けて12ワードを書き込みます。16F886では、最初の8ワードを書き込むと、16ワードがイレースされますので、続けて8ワードを書き込みます。
説明はここまでなのですが、もう少し詳しく知りたいという読者の方もいらっしゃると思います。そこで、PICマイコンの内部の動作を、ロジックレベルで推測してみましょう。
CPUの中を読む
マイコンをはじめとして、コンピュータというのは電子回路を組み合わせたものです。極論すれば、たくさんのスイッチです。命令コードを2進数であらわした場合に、それぞれのビットがスイッチに対応します。そして、そのスイッチによって回路が切り替えられます。
PIC16Fシリーズの命令コードは14ビットですので、14個のスイッチがあります。このうち、上位(左側)の2つのスイッチにより、4つのタイプの命令にわかれます。
「10」のグループは、callとgotoです。2つしかありませんので、3番目のビットが0ならcall、1ならgotoです。そして残りの11ビットがジャンプ先のプログラムカウンタになります。
今度は、これをスイッチの視点から見てみましょう。下位11ビットはプログラムカウンタで、上位2ビットが「10」のときにプログラムカウンタに取り込まれます。このとき、3ビット目が0なら、今の命令の次のアドレスがスタックに積まれます。しかし、上位2ビットが「10」でなければ、プログラムカウンタには取り込まれません。たとえば、「sleep」の下位11ビットは0x63ですが、上位2ビットは「10」でないので、0x63のアドレスにジャンプすることはありません。
これだけだと、スイッチの視点でもあまり違いませんが、ではmovwfを見てみましょう。命令コードは0x0080で、下位7ビットがレジスタアドレスです。ところで、上位2ビットが「00」のグループを見ると、0x80のビットが1だとレジスタに、0だとWに書き込みになっています。movwfはこのビットが1なので、W→fになるわけです。では、このビットを0にしたらW→Wになるのでしょうか。0x0000を調べるとnopになっていますから、nopの命令は、実際にはW→Wとして実現されていると想像できます。レジスタは使われないので、下位7ビットは何でもいいわけですが、実際にはsleepやclrwdtやreturnはnopのバリエーションとして作られていることがわかります。ということは、この辺の命令コードに、公開されていない命令があるかも知れない、いうことがわがります。
筆者は基本的に、公開されている範囲でプログラムを作ります。しかし、こういう知識を持っていれば、ドキュメントの間違いを見つけるのに役立ったりします。興味があれば、みなさんもいろいろ調べてみてください。