newlibと自家製FAT-FS Driver [趣味]
自家製FAT FileSystem Driverがだいぶいい感じになってきた。
趣味でのSHマイコン上の開発を、binutils+gcc+newlibでやっている。
マニアルを読むとわかるのだが、newlibは、低水準I/Oを自分で実装することもできる。C言語の低水準I/Oは、POSIX準拠環境(わかりやすく言えば、UNIX)のSystemCallである。
デフォルトのハンドラは、弱いシンボルなので、強いシンボルで再定義&リンクすれば、置き換えが可能だ。
自家製FATドライバを用いて、低水準I/Oを書いてみた。
そして、高水準I/Oからアクセスできるかどうかを見ようとすると、暴走。
PCが00000000番地になっているので、リセットでもかかったか?
気になるのは、デフォルトルーチンでは、trapを発行しているということだ。
おそらくLinux等を意識したものだろう。
私のOSはデバッグ用にTrapを使っているが、システムコールはTrap経由で呼び出すものではない。
これも原因かもしれない。
そういえば、高水準I/Oを使おうとすると、sbrk()が無いなどと言っていた。
これは、動的なメモリの確保を行うためのルーチンで、私自身は利用していない。
おそらく、高水準I/O内部で利用しているのだろう。
もしそうだとすると、困ったことになる。
このマイコンは、自家製リアルタイムマルチタスクOSが動作している。
マルチスレッド環境下で、メモリプールの保護をせずに複数のタスクが動的メモリ確保を行えば、どうなるか?メモリ管理用のLink-Listはずたずたに破壊され暴走する。
しらべるなら、その前に最新の環境を試してみようと考え、
binutils-2.16.1
gcc-4.1.0-20060223
newlib-1.14.0
を落としてきて、コンパイルを始めた。(gccは新しすぎるかな…)
gcc & newlibのコンパイルには、時間がかかる。
以前使用していたマシンでは約3日かかった。
爆速マシンではどのくらいでコンパイルできるだろうか…。
自家製FS-Driver [趣味]
仕事が暇になると、時々自家製FS-Driverのソースの整理をしている。
まとまった時間が取れない状況で作成したソフトウエアなので、各ルーチンの独立性は高い(一部を除く)。
しかしながら、それゆえにかぶっている部分も多い。
また、変数名のつけ方にも問題がある。
時々見ては、修正する。Bugも大量に見つかった。
だいぶBugを潰したが、発見頻度を考えると、頂点に達し、減少傾向に移行するには、あと数日かかるだろう。
機能も強化された。
ファイル削除機能
ファイルシーク機能
ディレクトリ作成機能
ディレクトリ削除機能
空き容量取得機能
以前つくった作った機能(File Open/Create/Close/Read/Write, Dir Open/Close, Read)とあわせて、ほぼFileSystemドライバとしての機能は、実現できたと考えている。
そういえば、空き容量の取得は、現在クラスタ全体をなめている。
(将来、クラスタ確保&開放でカウントUp/Down形式に変更予定)
したがって遅い。しかしながら、特にバリバリチューンしたわけではないが、総クラスタ数13万程度のSDカード(SPI接続)の未使用クラスタのカウントが3秒で終わる。
今使用しているマイコンと同じマイコンで、クロックが1/4程度の機械で、市販されている某社の某組み込みFATファイルシステムドライバの場合1分以上かかる内容だ。
(このファイルシステムドライバがひどすぎるのだ。)
逆に言うならば、一部の機能は市販品レベルに達した。
後は、地道に完成度を上げていこう(ここが辛いところだ)。
自家製FATDriver 内部Unicode化 [趣味]
自家製FAT FileSystem-Driverの内部処理をUnicode化した。
先々週から、仕事前の毎朝20分程度ずつ使ってコーディングしていた。
先週は旧社長に、同FS-Driverが動作するマイコンを作成していたので、改造作業はできなかった。したがって、実作業は10日×20分。
京浜東北線の人身事故などで、遅刻したときもあったので、約3時間前後である。
3時間にしては、まあまあなものができた。
FAT FileSystemは、ファイル名の扱いが非常に複雑だ。
基本的に長い名前が本物のファイル名だ。
しかし、長いファイル名は、ファイル名以外の情報をほとんど持っていない。
短いファイル名がそのファイルの情報を持っている。
したがって、長い名前と短い名前をあわせて、初めて一人前になる。
本物の名前から擬似的に短い名前を作り、そこにファイルの情報を持たせるのだ。
そして、長いファイル名と短いファイル名では、文字コードが違う。
長いファイル名は16bitのUnicode(UTF-16LE)※、短い名前はShift_JIS※でできている。
ここに、日本特有の問題が絡まる。
WindowsのShift_JISは歴史的経緯により、1つの文字に対して、複数のコードが割り当てられているものが含まれる。
Windows95がでる前は、NEC社のPC98シリーズが日本の市場を支配していた。この状況は世界から見て極めて特殊だったのだ。
世界ではIBM互換機(というよりもCOMPAQ互換機かな?)が支配しており、無視できない存在だった。
NECもIBMも独自にShift_JISを拡張し、Shift_JISと言いながら、Shift_JISではないコード体系を作ってしまっていた。
(ハード的な問題により、共通のコードをつけられなかったらしい)
それぞれが独立していたのなら、それでも問題はなかった。
しかし、Windowsの登場によりややこしくなった。
Microsoftは、それら拡張された両方の文字を表示させるため、なんと単純に重ねたのだ。
その結果、一部の文字がShift_JIS上に複数存在するという奇妙なコード体系となった。
同じ絵の別の文字というわけではない。あくまで同じ文字なのだ。
なぜならば、Unicodeでは1つなのだから。
これでファイル名の衝突を検出することを考えるとき、その面倒臭さは、人間の致死量の2倍を軽く越えているだろう。
Unicodeでは、とりあえず1つだ。
ゆえにUnicodeで処理すれば、別のコードを同じ文字とみなす処理が不要になる。
ファイル名生成処理は、ややこしい。
実質2週間かかったが、すでに2年越しの実装作業である。
十分に短い。
今日、FileSystemのコードサイズを見てみた。
コードが15KB、テーブルが16KB程度ある。全部で32KBもある。
最初はReadOnly、階層ディレクトリ未サポート、LongName未サポートだったときには、1KB程度のコードたっだのに、いつのまにか32倍になっていた。
実行速度での最適化もバリバリ使っているので、Inline展開される部分が多いのだろう。
- Unicode(UTF-16LE)。確信は無いが16bit固定長のコードで、正確にはUTF-16LEではないと思われる。
つまり、サロゲートペアには対応していないと考えている。
WindowsNTの内部文字コードがUnicodeだった。それの影響でLong名もUnicodeになったと思われる。WindowsNTが開発されたのは、今から10年以上前で、その当時Unicode自体が非常に流動的だった。16bitで十分という意見もあり、それに固定されているかもしれない。
ちなみに日本人が日常で利用する文字数は2,000文字程度である。中国人は6,000文字程度。ファイル名に使用できる文字が65,000文字に制限されていたとしても十分ではないだろうか?- 本文でも多少触れているが、実際には独自拡張が行われており、厳密にはShift-JISではない。
CP932(CodePage932)と呼ぶのが正解だろう。
しかしながら、一般的にはCP932と呼ぶよりもShift_JISと呼んだほうが通じやすいので、この表現をとっている。
自家製FileSystemDriver書き込み機能追加 [趣味]
自家製FileSystemDriverに書き込み機能を追加した。
もちろんLong名+日本語対応である。
現時点では、とりあえず書き込みができると言う程度。
サブディレクトリの作成はできないし、ファイルの削除もできない。
Long名のエントリを作成するのは、予想通り困難の連続であった。
C言語で実装したのだが、コードはどうしても汚くなってしまう。
実装してわかったことがある。
(実装するまえからうすうす気が付いていたが、とにかくやってみた)
この点については、まだ修正をしていない。
書き込み機能を実装している途中だった。両方とも中途半端になるのが嫌だったので、見送った。
サブディレクトリの作成やファイルの削除の実装は、技術的困難さがほとんどない。それゆえに作る楽しみもない。本当に暇になったときにでも実装しよう。
さてと。ひとつの山場を越えた。整理しておくかな。
FAT32対応 [趣味]
どんなことであれ、のめり込みすぎは良くない。
ここ何週間かプロトコルスタックの実装をしていたが、一応コーディングが終わり、コンパイルが通るようになった。
ただし、ARPとICMPだけ動作する状態だ。UDPは、エラーにならないようにしただけで、動作しない。
あせってやると失敗するので、気分転換にFileSystemの改造をし始めた。
相変わらずReadOnlyであるが、FAT32対応作業をした。
FAT32と、FAT12/16の違いは、Rootディレクトリのアクセス方法だ。
(もちろんFATエントリのサイズも異なる)
FAT12/16の場合、Rootディレクトリ領域は、FAT領域の直後から固定長(フォーマット時に決定)で確保されている。
FAT32の場合、Rootディレクトリも他のディレクトリ同様、可変長である。開始クラスタ番号は、BPB内のBPB_RootClus(offset=44)で示されている。
(つまり、FAT32の場合、特別なRootディレクトリ領域は無い)
実際には、Rootディレクトリは、クラスタ#2(2は一番最初のクラスタを示す)であることが圧倒的に多い。つまり、結局FAT領域の直後がRootディレクトリの場所となることが非常に多いため、FATエントリのアクセスさえ作れば、往々にしてRootディレクトリもアクセスできてしまう(Rootディレクトリがクラスタ以上の大きさになると問題だ)。
しかし、これでは偶然見えているだけなので、きちんとFAT32対応した。
意外に思ったのは、".."ディレクトリだ。
".."ディレクトリは、親ディレクトリを指す。FAT12/16の場合、親ディレクトリがRootディレクトリの場合、".."エントリ内の開始クラスタ番号には0を入れることになっている。FAT(FileAllocationTable)内に無いのだから、当然だ。
FAT32の場合、RootディレクトリもFAT内にあるのだから、そのクラスタ番号を入れるのだろうと思っていたが、実際には"0"であった。
互換性のためなのかもしれない。
これに対処するため、サブディレクトリで、クラスタ番号0から始まるものは、Rootディレクトリとみなすことにした。
ここまできたら、後は書き込み機能を実装してみたい。
書き込みを実装するには、以下に示すような厄介な問題がある。
ファイル名の衝突判定
- CP932(Shift_JIS)無いには、Unicode上で重複している文字がある。 たとえば"﨑"のCP932上のコードは、0xED95と0xFAB1である。これらが単一Unicodeに割り当てられている。他にも400文字程度ある。 ちなみに、ここでCP932->Unicode変換の情報が入手できる
longファイル名を格納する十分なディレクトリエントリの確保
- long名のエントリは連続していなければならない。たった1つの空きエントリを探すだけなら簡単だが、複数の連続したエントリを探さなくてはいけない。もちろんクラスタ境界をまたぐ可能性もある。
longファイル名からshortファイル名を自動生成
- shortファイルは、longファイル名から生成される。これも文字コード関連でややこしい。
数字付きShortファイル名(新規テキ~1.txt)の自動生成
- shortファイル名が重複する場合、~数字ファイル名を自動生成しなければならない。この数字が重複してはいけないので、ディレクトリをスキャンしなければならない。
プロトコルスタックの修正 [趣味]
プロトコルスタックの問題点を修正した。
実装が進むにつれ、新たな疑問がでてくる。
実際にやってみると、理解が深まる。見えなかったものも見えるようになる。
めんどくさかろうが、辛かろうが、どんなことであれ、やってみることが大切だ。
疑問は、平日の朝&昼にじっくり考えるために残しておこう。
進まないプロトコルスタック製作 [趣味]
今月は何度か停滞しているプロトコルスタック作成の作業をしている。
正月休みになったら、作業をする予定だった。
しかしながら、先月末23日あたりから体の調子が悪く、頭がうまく働かない。
注意力が下がっているため、間違いをしやすい。
このとき、一生懸命やれば間違えないと思う。しかし、現実に間違いが増えることを、私は経験上知っている。
このような場合でも間違いを最小に抑えるために、一歩引いて全体の分割方法を考えて、各部分を1つづつ考える。
1つづつ考えた後、また一歩引いて分割方法に間違いがなかったか、考え直す。
要するに、考える量を減らして、間違える量を減らすのだ。
しかし、この方法は、時間がかかる。
したがって、少ない注意力を、時間で補うといえる。
しかし、やはり間違えた。
与えられている時間は短い。その焦りが招いたミスだ。
事前の設計よりも良さそうな実装を思いつき、その実装に変えた。だがそれは間違いで、設計段階ですでに気がついていた内容だった。
それを、忘れていた。働かない頭を気持ちで支えて、乗り切ろうとして先走った結果だ。
31日の作業が無駄だったことに昨日、気がついた。
不健康大好き星人である私は、普段は足かせをしているようなものである。しかし、危機的状況下では、不健康行為を自粛する。
そうすることで、一時的にさまざまな能力が活性化する。
そのため、昨日はのんびり過ごした。そして、早ね早起きをした。酒もやめた。
タバコの本数も減らしている(これはやめられない)。
頭のボケ具合は下がった(ように思う)。おなかの調子もだいぶ良い。
これで右目も直ればよいが…。
SD Card [趣味]
先週に続いて、SD Cardをいじった。
数年前に作ったFATアクセスルーチンをマージする。
このルーチン自体はPC上で開発をしていた。自家製OSのC言語I/Fを模倣するルーチンをPC上に構築し、Borland C++ Builderのコマンドラインアプリとして、デバッグをしていた。
PCはLittleEndianだが、SHマイコンはBigEndianなので、問題が発生するのではないかと思っていた。
マージしてMakefileを書き換えて、コンパイルする。あっさり通る。
そして、実行。操作は、デバッグ用のコマンドラインから行う。
MMC/SDインタフェースおよび、自家製FAT FileSystemの初期化を行い、恐る恐る。ディレクトリエントリ表示コマンドをたたく。
だだだっと、ディレクトリ一覧が表示される。
>dir DOC\ 2005/11/26 06:27:26 <DIR> . 2005/11/26 06:27:26 <DIR> .. 2005/10/08 06:26:12 559 LinuxPing.txt 2005/10/31 06:26:31 16384 README.TXT 2005/10/16 06:26:12 3672 TupleMemo.txt 2005/10/16 06:26:14 108544 受信バッファメモ.vsd 2005/10/16 06:26:12 18221 送信バッファのメモ.pdf 2005/10/16 06:26:13 87552 送信バッファのメモ.vsd ercd=FFFE >
サブディレクトリもアクセスできている。長いファイル名もアクセスできている。日本語ファイル名も大丈夫だ。
しかし、なぜかファイルの時間がおかしい。日付は大丈夫そうだが…。
朝の6時はあまりにも変だ。
31秒や13秒もおかしいかもしれない。古いDOS形式では、奇数秒はありえない。
VFATになってから、100msec単位の秒情報が追加され、奇数秒も表現可能になった。2年前に、そこまでちゃんと実装してたのかな?
そういえば、BCBで作っているときにも、こんな時間解釈Bugがあった気がしてきた。記憶があやふやだ。
まぁ、ほとんど問題も無くあっさりうごいたんだから、とりあえず、いい気分。
そして、いい気分転換にもなった。
SH-2マイコンでMMC(SD)Card [趣味]
先月時々時間を見つけては、MMC(SD)カードスロットをSH-2マイコンボードにつなぐ作業をしていた。
何かを作るのはいい。無心になれる。
SH7144は3.3V電源なので、レベル変換はいらない。
10月の時点で、SHマイコンのSPIのエッジ極性が、MMC/SDのそれと同じであることを確認していたので、その他の回路も要らない。
結線数も少ないので、半田付けも簡単だった。回路は以下のような感じだ。
(回路図は急いでVisioで書いたので、間違っているかも知れない)
電源の操作を行う部分が多少ややこしいが、単純にシリアルポートと、SDカードスロットをつないだだけだ。
電源操作回路は、不要かとも思ったが、SPI←→MMCモードの切り替えには電源ON/OFFが必要なので、つけた。
その代わり、CardDetectやWriteProtectは未接続だ。
また、電源操作ポートだけPortEからとっている。私が使用しているマイコンボードのPE15に最初からLEDがついていたため、そのままそれをSDカードの電源インヂケータにするため、このようにした。
半田付けは、11月頭に終わっていたのだが、会社解散がらみでドタバタしていたので、先月は13日に一度触っただけだ。
今日CQ出版の「TECI Vol14 PCカード/メモリカード徹底研究」付属のコード(ARM用)を改造した。この付属コードはよくできており、ハードウエア依存部が1つのファイルにまとめられている。改造はしやすい。
元のコードがgccで書かれていたため、コンパイルエラーもでることは無かった。
自家製デバッグshell経由で、APIを呼び出せるように改造して動作させてみる。
初期化、Open後に、readしてみると、あっさりと動作した。拍子抜けするくらいだ。
同マイコンにはCFカードスロットも自分で実装している。CFをつなぐのはかなり大変だったが、MMC/SDは簡単だ。
しかし、残念なのは仕様書を入手できないことだ。ソースコードがあったからあっさり行ったが、上記TECIにも詳しいことは書かれていない。
とても便利で安価なストレージなのだが。
ファイルシステムは、2003年ごろに自作したFAT互換ファイルシステムを使う予定だ。たぶん来週以降になるだろう。
このFAT互換ルーチンは、FAT32に未対応だが、LongFileName、日本語ファイル名にも対応している。
プロトコルスタックばかり作っていたが、たまには別のものを作るのもいい。
※12/18 図を見やすくした。
自家製TCP/IPプロトコルスタック2 [趣味]
自家製TCP/IPプロトコルスタックで、一応UDP通信ができるようになった。
UDPを実装する前に、IP周りの実装を見直し、結構いじった。内容としては、IPフラグメンテーションに対応したのが大きい。
一応動作したが、まだまだこれからいろいろ見直す必要がある。
通信端点(私のプロトコルスタックはITRON-TCP/IPに似ている)ごとに受信バッファを独立させているので、通信端点の独立性が高く、ある特定の通信端点のデータを優先的に処理させやすくなっている。
しかしながら、NIC内臓バッファからの取り出しは到着した順番なので、完全に独立しているわけではない。さらに突き詰めれば、同じ物理メディア上に仮想の回線を構築しているので、完全に通信端点を独立させることは魔法の通信媒体でも使わない限り、不可能だ。
さて、一応動作したUDPのパフォーマンスはというと、あまり芳しくない。
データ部12ByteのUDPパケットをPCから送信し、それをマイコンがUDPでエコーバックするということで行った。ずいぶん小さなパケットである。
最初のパケットを、エコーバックするまでの時間は2msec程度だ。これは良い。
しかし、次のパケットを出すまでに10msec程度かかり、結果的に10kbps程度しか出ない。
処理の負荷は以下のようになっている