KOZOSのシミュレータ対応を進めていますが、シリアル通信、シリアルでのOSのダウンロード、ネットワーク通信が動作するようになりました。これでPC上でTCP/IPの開発が進められます。


明日からのOSC東京では、シミュレータ環境も持っていきます。興味のある方はどうぞ。


シミュレータ対応については、近日中にホームページのほうにまとめます。


シミュレータで動作するとGDBがあっさり使えるためすっごく便利。いざとなったらシミュレータの内部にprintf()入れて情報を出させることもできるので、実機でデバッグするよりはるかに効率がよさそうです。


これでinsightとか使って動作させたら、なかなか見栄えがしますね~


AD
出先で実機が無い状態でもKOZOSの開発ができるように,シミュレータ対応を考えている.

で,いろいろ試しているのだが,とりあえずブートローダーが起動して文字の入出力ができるまでをまとめた.

(シミュレータ編第1回)とりあえず動かしてみた

まだまだ動き始めたばかりですが,徐々に進めていくので興味のある人は待っててください.
とりあえず手元では,cuでシリアル接続してXMODEMでOSのファームをダウンロードして起動をかけるところまでは成功しています.

AD
(注意)このブログは本家のほうの文章部分のみの転載です.ソースコードの配布,画像などについては本家のほうを参照してください.文章中のリンク先は面倒なのですべて本家のほうに変換してしまっているのでご注意ください.

組込みOS自作の書籍執筆中です.

前回,KOZOS/H8のコードを書籍用に整理しなおしたのだけど,もうちょっと整理して,さらに12回のステップに分けて自作していくという内容で,12回ぶんに分けてみた.参考までに公開します.

これからKOZOS/H8のソースを読むというのならば,こっちがいいと思います.読み方としては,以下ソースの第1回のコードから順に,前の回に対する差分を見るかたちで読んでいくといいでしょう.興味のあるかたはどうぞ.ちなみにライセンスはKL-01です.すべて秋月のLANコントローラ付きH8/3069Fボードで動作確認しています.(2010/01/17時点で,すべてのコードについて動作確認済み)12回の具体的な内容は,以下です.まずは「Hello World」を作るところから始めて,ブートローダー,OSというように,12ステップで徐々に作り上げていくという内容になっています.
  • 第1部 ブートローダーの作成
    • 第1回 ... フラッシュROMに書き込んで動作する「Hello World」を作成
    • 第2回 ... 各種ライブラリ関数を実装,数値出力ライブラリを実装
    • 第3回 ... 静的変数の書き換え対応
    • 第4回 ... XMODEMによるファイル転送
    • 第5回 ... ELF形式の解析を実装
    • 第6回 ... ブートローダーからRAM上にロードし実行する「Hello World」の作成
  • 第2部 OSの作成
    • 第7回 ... 割込み処理の実装
    • 第8回 ... スレッド動作を実装
    • 第9回 ... 優先度スケジューリング実装,スレッド制御システムコール追加
    • 第10回 ... メモリ管理を実装
    • 第11回 ... スレッド間通信を実装
    • 第12回 ... 割込みハンドラ実装,割込みベースで動作するコマンド応答プログラム実装
ちなみに全体のコード量は以下.ライセンス関連の文書を除いた,Makefileやリンカスクリプトやアセンブラ,動作確認用サンプルプログラムも含めた全体のコード量です.
ブートローダーOS合計追加量
第1回269---269269
第2回374---374105
第3回433---43359
第4回616---616183
第5回723---723107
第6回7314181149426
第7回8885651453304
第8回9059761881428
第9回90511942099218
第10回90513222227128
第11回90515322437210
第12回90518002705268
初回が269行から始まって,だんだん拡張していって最終的にブートローダーが905行,OSが1800行です.両方合わせても2705行だから,まあいいかんじなんではないかな.だいたい毎回平均して200行ていどを追加していってる計算になるね.
AD
(注意)このブログは本家のほうの文章部分のみの転載です.ソースコードの配布,画像などについては本家のほうを参照してください.文章中のリンク先は面倒なのですべて本家のほうに変換してしまっているのでご注意ください.

えー,ブログでも書いているけれど,組込みOS自作本を執筆中です.

で,書籍用にKOZOS/H8のソースコードを整理しなおしたので,参考までに公開してみます.これからKOZOS/H8のソースを読むというのならば,こっちのほうがいいかもしれません.書籍用ということで,かなりばっさりと機能限定して,あと無駄を省いてシンプル・コンパクトにしています.

また,全体的に見直して,きれいに整理しなおしました.かなりごっそりと書き換えています.単語の意味とかも整理・統一化しています.

あと,従来はシリアルの送信割込みを見ていなかったのですが(文字列送信時は,1文字ずつ文字出力がはけるのをポーリングで監視していた),きちんと割込みを見て1文字ずつ送信するように書き換えました.これでよーやくまともに割込み処理をやっているということができるでしょう.

現状で,リンカスクリプトやアセンブラやMakefileもすべてぜんぶ含めて,ブートローダーが945行,アプリケーション含めたOS全体が1828行です.うーん,いい感じにまとまった.

ただ以下のような部分があり,削ろうと思えばまだまだ削れそうです.たぶん本気で削れば,ブートローダーは800行程度,OSはアプリ含めて1500行を切るくらいにはできるでしょう.
  • 初心者用ということで,やたらとふんだんにコメントを入れてある.(普段なら「そーいうことはコメントでなくソースコードで伝えるべき」として,絶対に入れないような内容のコメントも敢えてガシガシ入れてある)
  • 初心者用ということで,わかりやすくするためにわざと冗長に書いている部分がいくつかある.
  • ブートローダーとOSで,ライブラリ関数などの共通コードは極力そのままにしてある(変更あると,書籍で説明しなければならなくなるので,OS側では使わない関数もそのまま残してある).このため,OS側では使わないライブラリ関数とかがいくつか残っている.
全体的に見直して整理しなおしたことで,組込みOSのエッセンスというか,要点がぎっちりといい感じでつまって濃縮されたコードになっています.実際,内容の密度は濃いと思います.(そのかわりエラー処理とかチェックとか移植性とか抽象化と階層化とかは,あまり本題でないということで,かなり省かれていますが)

うーん,自分で言うのもなんだけど,これ,結構いいかも.自分がOS勉強し始めたころにこーいうのがあったらホント良かったのにとしみじみ思う.
(注意)このブログは本家のほうの文章部分のみの転載です.ソースコードの配布,画像などについては本家のほうを参照してください.文章中のリンク先は面倒なのですべて本家のほうに変換してしまっているのでご注意ください.

今まで内蔵RAM上で動いていたのだけど,LANコントローラ対応でいよいよ内蔵RAMではサイズ的にきつくなってきて,これ以上の機能追加は厳しいので,OSをDRAM上で動かすように修正しよう.

で,DRAMへの以降なのだけど,OSで行っているDRAMコントローラの初期化をブートローダーで行うようにして,あとOSのリンカスクリプト書き換えてDRAM上に持っていくだけで超簡単にできると思っていたのだけど,バグがあってちょっとひと苦労.

まずDRAMの初期化なのだけど,OSのデバッグ中に気がついたことなのだけど,従来のままだとしばらく値を保持させるとビット化けしてしまうアドレスが存在することが判明.というのは,ブートローダー対応してからOSをDRAMに移したのだけど,DRAM上で動かすようにすると固まってしまう(内蔵RAM上だとなぜか正常に動作する)という問題が出て,まあ実際の原因は後述するint型の問題だったのだけど,どうもDRAMがちゃんと動いていないように思えたのでチェックプログラムを作って試してみたらやはりビット化けするアドレスが存在する.

従来のメモリチェックは値を書き込んだ直後に読む,ということをやっていたので値をしばらくのあいだ保持できているかということはチェックできていなかった.なので最初に全領域に値を書き込んで,その後で全領域を読んでチェック,さらに特定のビットが0に化けても1に化けても検出できるように,ビットを立てたパターンとビットを落としたパターンの両方でチェックする,というようなチェックをしたらビット化けが検出された.

ということでdram_init()での設定パラメータがあやしく思い,ウエイトとリフレッシュレートをいろいろ変えてみたのだけど,リフレッシュレートを上げたらビット化けしないようになった.どうもリフレッシュレートが低すぎたらしい.まあほんとはデータシート見たりして適切な値を調べるべきなのだけど,とりあえず全メモリ全パターンチェックでエラーにならなくなったのでよしとする.(なんかDRAM搭載のボートで買うと,DRAMコントローラの設定例が載ったドキュメントがついてくるみたいね.今回利用しているボードだとDRAMはおまけ扱いなのでドキュメントがついてないってことかもしれん)

ただそれだけ直しただけではまだダメ(っていうか,ビット化けするのは使っていないアドレスだったので,これが固まる原因ではなかった)で,OSのテキストとデータ領域はDRAMに移しても正常動作したのだけど,BSS領域を移すとやはりうまく動かず固まってしまう.

いろいろ見直したのだけど原因がよくわからなくて,しかたがないのでいろんなところに puts() を入れてシリアル出力させてどこまで処理が進んだか見るという原始的ないわゆる printf デバッグで追いかけたら,kz_free()でのメモリ解放でおかしな領域を解放しようとして kz_sysdown() が呼ばれて while(1) の無限ループに入っていることが判明.この時点ではうわーメモリ破壊かなーめんどくさそーと思ったのだけど,もっと追いかけたらkz_send()でのメッセージ送信でなんか送信先のスレッドIDがおかしくなっていて,そのせいでおかしなポインタの先を参照して kz_free() におかしなアドレスを渡していることが判明.

ということで原因はおかしなスレッドIDでkz_send()が呼ばれていることなのだけど,printfデバッグで調べたら,送信元は command スレッドになっている.ということは送り先は extintr なのだけど,extintr_id は uint32 としてきちんと定義されている.

で,extintr_id はどこで得られているかというと,kozos.c のスレッド起動部分でkz_run()の戻り値を与えている.このへんでようやく気がついたのだけど,実は kz_run() の戻り値が int 型として定義されていたので,16ビット整数になってしまい,スレッドIDの上位ビットが削られた状態で extintr_id に格納されていたのだ.このため kz_send()でのメッセージ送信時に,おかしなスレッドID宛に送信していたというのが結局の原因.

この原因を考えると,うーんいままでよく動いていたなーと思うのだけど,実は従来は内蔵RAM上にBSSがあったのだけど,内蔵RAMのアドレスが0xffbf20 ~ 0xffff00 なので,上位ビットが削られて16ビット値となっても,その後に extintr_id に格納されるときに32ビットに拡張される際に,上位ビットが符号拡張されて,0xffffXXXX のようなアドレスになって格納されると思うのだな.で,実際にメモリアクセスされる際には上位8ビットが無視されて0x00ffXXXX のアドレスがアクセスされることで,正常動作していたようだ.なのでBSSをDRAMに移したことでアドレスが 0x400000 ~ 0x5fffff になってしまい,正常動作しなくなったということのようだ.

いやー,実は今までもBSSを動かすとなんか固まるときがあるなーとは思っていたのだけど,これが原因だったわけだ.ちょっと調べるのたいへんだったけど,まあ原因判明してよかったよかった.なおしたらあっさりと動いた.

で,今回の修正なのだけど,いい機会なのでリンカスクリプトを修正して,メモリ上のアドレスは基本的にリンカスクリプトで指定して,そこを見るように変更した.というのは,たとえばスレッドのスタックのアドレスを指定するのに今まではconfigure.h で

#define THREAD_STACK_START 0xffe820

のようなことをやっていたわけなのだけど,こーいうアドレス情報は基本的にはリンカスクリプトで指定して,その値を見るようにすべきだと思うのだな.というのは,これだとまずいろんなアドレス設定がいろんなヘッダファイルに点在してしまい,メモリ調整が面倒になったり調整し忘れでバグの原因になったりする可能性が高い.さらに,たとえばある領域の直後の領域を使いたい場合などに,#define でのベタ書きではなく,リンカスクリプトのリンク情報に応じて自動調整されるようにしたほうが効率的,といった理由がある.

ということで,今回の大きな修正は以下.
  • ブートローダーにDRAMコントローラを持っていって,ブートローダーでDRAMの初期化を行う.
  • DRAM初期化時の設定値で,リフレッシュレートを高くする.
  • DRAMチェック用関数を追加.(データ保持をチェックできるように,全領域ライトの後で全領域リードしてチェックをいくつかのメモリパターンで行う)
  • kz_run()の戻り値を32ビットに変更.
  • リンカスクリプトを修正して,メモリアドレスは基本としてリンカスクリプトですべて指定(or調整)されるようにする.
  • intが16ビットで問題ありそうな箇所を部分的に修正.
以下,修正したソース.まずブートローダーの修正点について.

main.cなのだけど,DRAMコントローラの初期化用に dram_init() の呼び出しを追加した.実際の DRAM 初期化処理は dram.c をOS側から持ってきて,リフレッシュレートの対応とメモリチェックの追加を行った.具体的には dram.c に以下のような対応が入っている.

--- os/ether/dram.c Sat Oct 3 13:22:25 2009
+++ bootload/dram/dram.c Sun Oct 4 23:35:28 2009
@@ -27,10 +27,29 @@

int dram_init()
{
+ /*
+ * dram_check2()でチェックしたら,値をうまく保持できないビットが存在した
+ * ので,リフレッシュレートを上げたら解消された.(ウエイトを多めにしても
+ * 効果は無かった)
+ * とりあえず,リフレッシュレート高めの設定にしておく.
+ */
+
*H8_3069F_ABWCR = 0xff;
+#if 0
*H8_3069F_RTCOR = 0x07;
+#else
+ *H8_3069F_RTCOR = 0x03; /* リフレッシュ周期を短めに設定 */
+#endif
+#if 1
*H8_3069F_RTMCSR = 0x37;
+#else
+ *H8_3069F_RTMCSR = 0x2f; /* リフレッシュ周波数UP */
+#endif
+#if 1
*H8_3069F_DRCRB = 0x98;
+#else
+ *H8_3069F_DRCRB = 0x9f; /* ウエイト挿入 */
+#endif
*H8_3069F_DRCRA = 0x30;

*H8_3069F_P1DDR = 0xff;
@@ -39,9 +58,17 @@
/* *H8_3069F_PBDDR = ...; */

/* H8_3069F_WCRH = ...; */
+#if 1
*H8_3069F_WCRL = 0xcf;
-
- *H8_3069F_ASTCR = 0xfb;
+#else
+ *H8_3069F_WCRL = 0xff; /* ウエイト挿入 */
+#endif
+
+#if 1
+ *H8_3069F_ASTCR = 0xfb; /* 2ステートアクセス */
+#else
+ *H8_3069F_ASTCR = 0xff; /* 3ステートアクセス */
+#endif

return 0;
}

dram.c には他にもメモリチェックとメモリクリアの関数が追加されている.これは以下.

@@ -107,4 +134,82 @@
putxval((unsigned long)*p, 8);
puts("\n");
return -1;
+}
+
+static uint32 dram_check2_val0(uint32 *addr) { return 0x55555555UL; }
+static uint32 dram_check2_val1(uint32 *addr) { return 0xaaaaaaaaUL; }
+static uint32 dram_check2_val2(uint32 *addr) { return 0x00000000UL; }
+static uint32 dram_check2_val3(uint32 *addr) { return 0xffffffffUL; }
+static uint32 dram_check2_val4(uint32 *addr) { return (uint32)addr; }
+static uint32 dram_check2_val5(uint32 *addr) { return ~(uint32)addr; }
+
+int dram_check2()
+{
+ uint32 *p;
+ int ret = 0, i;
+ uint32 (*getval[])(uint32 *) = {
+ dram_check2_val0,
+ dram_check2_val1,
+ dram_check2_val2,
+ dram_check2_val3,
+ dram_check2_val4,
+ dram_check2_val5,
+ NULL
+ };
+
+ for (i = 0; getval[i]; i++) {
+
+ puts("DRAM check pattern: ");
+ putxval(i, 0);
+
+ puts("\nDRAM setting...\n");
+
+ for (p = (uint32 *)DRAM_START; p < (uint32 *)DRAM_END; p++) {
+ *p = getval[i](p);
+ if (!((uint32)p & 0xfff)) {
+ putxval((unsigned long)p, 8);
+ puts("\x08\x08\x08\x08\x08\x08\x08\x08");
+ }
+ }
+
+ puts("\nDRAM checking...\n");
+
+ for (p = (uint32 *)DRAM_START; p < (uint32 *)DRAM_END; p++) {
+ if (*p != getval[i](p)) {
+ puts("\nERROR! :");
+ putxval((unsigned long)p, 8);
+ puts(" ");
+ putxval((unsigned long)*p, 8);
+ puts("\n");
+ ret = -1;
+ }
+ if (!((uint32)p & 0xfff)) {
+ putxval((unsigned long)p, 8);
+ puts("\x08\x08\x08\x08\x08\x08\x08\x08");
+ }
+ }
+
+ puts("\n");
+
+ }
+
+ if (ret == 0) {
+ puts("\nall check OK.\n");
+ }
+
+ return ret;
+}
+
+int dram_clear()
+{
+ uint32 *p;
+
+ puts("DRAM clearing...\n");
+
+ for (p = (uint32 *)DRAM_START; p < (uint32 *)DRAM_END; p++)
+ *p = 0;
+
+ puts("DRAM cleared.\n");
+
+ return 0;
}

あとmemset()とかのライブラリ関数を集めた lib.c と,あとシリアルドライバのserial.c が,OS側のやつとちょっとなんか食い違っていたので,OSのものに実装を合わせた.

さらに main.c の init() に dram_init() の呼び出しを加えて,コマンド解釈部分には メモリチェック用に ramchk, ramchk2 と,あとメモリクリア用に ramclr を追加した.以下のような感じ.

@@ -159,6 +76,12 @@
f = (void (*)())entry_point;
f();
}
+ } else if (!strcmp(buf, "ramchk")) {
+ dram_check();
+ } else if (!strcmp(buf, "ramchk2")) {
+ dram_check2();
+ } else if (!strcmp(buf, "ramclr")) {
+ dram_clear();
} else {
puts("unknown command.\n");
}

あと serial.c をOS側に合わせて修正したことでSCIの番号指定が必要になったので,シリアルを使っている部分全般(main.cとlib.cとxmodem.c)にインデックス指定の対応が各所に入っている.

次に,OS側の修正.

まず,kz_run()の戻り値がint型になっていて,スレッドIDの上位ビットを渡せていないという大問題の修正.

diff -u os/ether/syscall.h os/ondram/syscall.h
--- os/ether/syscall.h Sat Oct 3 11:01:06 2009
+++ os/ondram/syscall.h Mon Oct 5 01:02:01 2009
@@ -31,7 +31,7 @@
int stacksize;
int argc;
char **argv;
- int ret;
+ uint32 ret;
} run;
struct {
int dummy;

修正はこれだけ.kz_run()の処理の本体である thread.c:thread_run() では戻り値はきちんとuint32になっているので,ホントに修正忘れ.これでずいぶんハマッてしまった.

次に,リンカスクリプトの修正.

diff -u os/ether/ld.scr os/ondram/ld.scr
--- os/ether/ld.scr Sat Oct 3 21:11:47 2009
+++ os/ondram/ld.scr Sun Oct 4 23:38:45 2009
@@ -5,11 +5,19 @@
MEMORY
{
/*
- * reserve 256bytes space for software interrupt vector
- * and 256bytes space for ELF header.
+ * DRAM (2MB)
+ * reserve 256bytes space for ELF header.
*/
+ dram(rwx) : o = 0x400000 + 0x100, l = 0x200000 - 0x100
+
+ /*
+ * internal RAM (16KB)
+ * reserve 256bytes space for software interrupt vector.
+ */
+ uservec(rw) : o = 0xffbf20, l = 0x000100 /* start of RAM */
ram(rwx) : o = 0xffbf20 + 0x200, l = 0x004000 - 0x200 /* 16kb */
- bootstack(rw) : o = 0xffff00, l = 0x000010 /* end of RAM */
+ userstack(rw) : o = 0xffc120, l = 0x000010
+ bootstack(rw) : o = 0xffff00, l = 0x000010
intrstack(rw) : o = 0xffff00, l = 0x000010 /* end of RAM */
}

@@ -19,29 +27,43 @@
_text_start = . ;
*(.text)
_etext = . ;
- } > ram
+ } > dram

.rodata : {
_rodata_start = . ;
*(.strings)
*(.rodata)
+ *(.rodata.*)
_erodata = . ;
- } > ram
+ } > dram

.data : {
_data_start = . ;
*(.data)
_edata = . ;
- } > ram
+ } > dram

.bss : {
_bss_start = . ;
*(.bss)
*(COMMON)
_ebss = . ;
- } > ram
+ } > dram /* DRAMにするとなぜか固まる.要調査 */

+ . = ALIGN(4);
_end = . ;
+
+ .freearea : {
+ _freearea = .;
+ } > dram
+
+ .uservec : {
+ _uservec = .;
+ } > uservec
+
+ .userstack : {
+ _userstack = .;
+ } > userstack

.bootstack : {
_bootstack = .;

dram空間を定義して,テキスト,データ,BSSをdram空間に持っていく.dram空間を+256バイトして定義しているのは,以前にELF形式をロードできるようにしたときと同じ理由で,ELFヘッダがうまく入る領域を空けているため(ELFヘッダがはみ出してしまうので).

さらにソフトウエア割り込みベクタの登録域として uservec 領域,スレッドのスタック領域として userstack 領域というのを新設した.これらは従来は configure.h で THREAD_STACK_START とか,interrupt.h で VECTOR_ADDR とかで直の値で定義してあったのだけど,リンカスクリプトで集中定義することでメモリ調整時の修正ミスや修正洩れを無くすため.

さらにさらに,従来は kz_alloc() で獲得できる動的メモリ領域の2048バイトの領域をDRAMの先頭からとしてこれも直接指定していたのだけど,DRAM先頭付近はOSの本体がロードされるので,それに合わせて後ろの空いた部分を使えるように,freeareaという領域をBSSの直後に作成している.(実際にはその先頭アドレスがわかればいいだけなので,空領域として定義している)

で,リンカスクリプトの修正に合わせて,configure.h と interrupt.h の修正.

diff -u os/ether/configure.h os/ondram/configure.h
--- os/ether/configure.h Sat Oct 3 16:11:48 2009
+++ os/ondram/configure.h Mon Oct 5 01:11:41 2009
@@ -4,13 +4,10 @@
#include "types.h"
#include "lib.h"

-extern uint32 intrstack;
+extern char intrstack;
+extern char userstack;
#define INTR_STACK_START (&intrstack)
-#if 0
-#define THREAD_STACK_START 0xffe820
-#else
-#define THREAD_STACK_START 0x5f0000
-#endif
+#define THREAD_STACK_START (&userstack)

#define SIGHUP 1
#define SIGILL 4

アドレス直指定でなく,リンカスクリプトで定義した userstack シンボルの位置を見るようにしている.userstack はリンカスクリプト内で作成したシンボルなので変数としての実体は無いが,C言語から参照するために extern 宣言して参照している.

次に,interrupt.h の修正.

diff -u os/ether/interrupt.h os/ondram/interrupt.h
--- os/ether/interrupt.h Sat Oct 3 11:01:06 2009
+++ os/ondram/interrupt.h Sun Oct 4 17:32:21 2009
@@ -2,7 +2,10 @@
#define _INTERRUPT_H_INCLUDED_

#define VECTOR_NUM 64
-#define VECTOR_ADDR ((void *)0xffbf20)
+
+extern char uservec;
+#define VECTOR_ADDR (&uservec)
+
typedef void (*interrupt_handler_t)(int vec);

#define VECTORS ((interrupt_handler_t *)VECTOR_ADDR)

これも configure.h の修正と同じで,アドレス直指定でなくリンカスクリプトで定義した uservec シンボルの位置を見ることで,ソフトウエア割り込みベクタの位置を求めるようにしている.どう?スマートになったでしょ?

次に,main.c の修正.

diff -u os/ether/main.c os/ondram/main.c
--- os/ether/main.c Sat Oct 3 19:45:04 2009
+++ os/ondram/main.c Mon Oct 5 01:08:59 2009
@@ -14,11 +14,25 @@
extern int ebss;

/* clear BSS */
+#if 0
memset(&bss_start, 0, (uint32)&ebss - (uint32)&bss_start);
+#else
+ /*
+ * memset()はサイズをintで渡すので,BSSが32KBを越えたときに
+ * 正常にクリアできないので,memset()を利用せずに自前でクリアする.
+ */
+ {
+ char *p;
+ for (p = (char *)&bss_start; p < (char *)&ebss; p++)
+ *p = 0;
+ }
+#endif

serial_initialize(0, 0);

+#if 0 /* ブートローダーで初期化しているので,初期化不要 */
dram_init();
+#endif

return 0;
}

まあコメントの通りなのだけど,BSSのクリアに従来は memset() を呼んでいたが,memset() のサイズは16ビットint型として渡しているので,BSSが大きくなったときにクリアしきれないことが考えられる.これについてはmemset()側を直すべきかとも思ったのだけど,BSSの初期化みたいな基本的な部分がライブラリ関数に左右されるのもよくないかと思ったので,自前で(そしてサイズに依存しないように)クリアするように修正した.

dram_init()によるDRAMの初期化はブートローダー側で行うので呼ばないように修正.ていうかこの段階ではすでにOSがDRAM上で動いているので,下手に設定値をいじったらいきなりDRAMの内容が消えたりしてOSが動かなくなる可能性があるので,dram_init()を呼んではいけない.

次に memory.c での動的メモリ管理の修正.

diff -u os/ether/memory.c os/ondram/memory.c
--- os/ether/memory.c Sat Oct 3 21:10:10 2009
+++ os/ondram/memory.c Mon Oct 5 01:15:24 2009
@@ -4,11 +4,11 @@

#include "lib.h"

-#define MEMORY_AREA1_SIZE 32
+#define MEMORY_AREA1_SIZE (uint32)32
#define MEMORY_AREA1_NUM 16
-#define MEMORY_AREA2_SIZE 64
-#define MEMORY_AREA2_NUM 8
-#define MEMORY_AREA3_SIZE 2048
+#define MEMORY_AREA2_SIZE (uint32)256
+#define MEMORY_AREA2_NUM 16
+#define MEMORY_AREA3_SIZE (uint32)2048
#define MEMORY_AREA3_NUM 64

#if 0
@@ -16,7 +16,8 @@
static char memory_area_2[MEMORY_AREA2_SIZE * MEMORY_AREA2_NUM];
static char memory_area_3[MEMORY_AREA3_SIZE * MEMORY_AREA3_NUM];
#else
-#define memory_area_1 ((char *)DRAM_START)
+extern char freearea;
+#define memory_area_1 (&freearea)
#define memory_area_2 (memory_area_1 + MEMORY_AREA1_SIZE * MEMORY_AREA1_NUM)
#define memory_area_3 (memory_area_2 + MEMORY_AREA2_SIZE * MEMORY_AREA2_NUM)
#endif

まずメモリサイズを uint32 として定義するように修正.というのは,サイズ×領域数の掛け算を行う際に,(int)×(int)になるとintの上限あふれしたときにまたおかしなことになってしまうかな~と思ったので.実際にどうなるかは未確認なのだけど.

あと,DRAM_STARTでなくリンカスクリプトで定義した freearea を見ることで,OSのBSSの直後から無駄無くメモリを利用するように修正.こーいうのができるのが,リンカスクリプトにメモリ定義をすべて移した利点ではある.(自動調整してくれる)

最後に,これはおまけだけど,extintr の修正.

diff -u os/ether/extintr.c os/ondram/extintr.c
--- os/ether/extintr.c Sat Oct 3 16:13:04 2009
+++ os/ondram/extintr.c Sun Oct 4 23:55:35 2009
@@ -8,7 +8,8 @@

#define USE_MESSAGE

-#define BUFFER_SIZE 1800
+#define CONS_BUFFER_SIZE 16
+#define ETH_BUFFER_SIZE 1800

uint32 extintr_id;

@@ -125,9 +126,9 @@
char *buffer;

#ifdef USE_MESSAGE
- buffer = kz_kmalloc(BUFFER_SIZE);
+ buffer = kz_kmalloc(CONS_BUFFER_SIZE);
#else
- buffer = kx_kmalloc(BUFFER_SIZE);
+ buffer = kx_kmalloc(CONS_BUFFER_SIZE);
#endif
size = 1;

@@ -220,9 +221,9 @@
char *buffer;

#ifdef USE_MESSAGE
- buffer = kz_kmalloc(BUFFER_SIZE);
+ buffer = kz_kmalloc(ETH_BUFFER_SIZE);
#else
- buffer = kx_kmalloc(BUFFER_SIZE);
+ buffer = kx_kmalloc(ETH_BUFFER_SIZE);
#endif

size = ether_recv(intp - interrupts, buffer);

従来はシリアル入力もLANからのパケットも1800バイトの領域を獲得することでmemory.c の2048バイト領域を利用していたのだけど,シリアル入力は1文字入力であり,さすがにこれは無駄なので,シリアル入力では少ない領域が利用されるように修正.

あと dram.c は dram_init() を呼ばなくなったのでOS側では必要は無くなったのだけど,いちおうブートローダー側の修正に合わせた修正をしておいた.

修正は以上.で,これでようやくDRAM上で実際に動作するようになった.面倒なので今回はお試し結果は省略.各自で試してみてね.シリアルの速度はちょっと前に 19200bps に変更されているので,接続時には注意してちょうだい.