Tiny H8でHOSを!

[IMAGE]AKI-3664

H8もHOSも、ITRONもRTOSも組み込みシステムの開発も知らない私ですが、WEB 上に公開されている先達の成果のお蔭で、GNUツールで開発環境を構築し、 Tiny H8(AKI-3664)でHOSのサンプルを稼働させるところまで出来ました。

結局のところほとんど全てが2次情報に過ぎないわけですが、折角なので経緯 をまとめてみました。H8やHOS(RTOS,μITRON)に興味はあるが、簡単には手を 出すことが出来ないと思っている方にはお役に立てるかもしれません。

2001.01.23記 新井
2001.02.14更新


  1. はじめに
  2. FreeBSD上でGNUのツールによる開発環境を構築する
  3. HOSを載せる
  4. とにかくサンプルを動かす
  5. 今後の課題
  6. 謝辞

はじめに

私は最近、秋葉原の秋月電子通商 で「AKI-3664 タイニーマイコンキット」な るものを購入してしまいました。

これは日立のH8ファミリの一つ、 H8/3664Fを搭載したマイコンキット(というか、外付けされているのはRS232C ドライバ、発振子、コンデンサだけのそのまんま)です。H8は自動車その他の 製品に広く採用されている組み込み用プロセッサで、高性能と使い勝手の良さ、 そして入手の容易さから趣味の電子工作の世界では、かつてZilog Z80が占め ていたような地位にあるようです。

まぁH8についてはこの程度で収めておきましょう。何故?はっきり言って私は 2ヶ月前にはH8の事なんかトラ技の広告位しか知らなかったし、今もそう大し て知っている訳ではないからです。日立のWEBやサーチエンジンのお世話にな ればいくらでもH8の紹介は発見できると思うので、詳細な情報をお求めの方は そちらを参照されると良いでしょう。

とにかく私はそのマイコンキットを買ったのです。常々H8というものに興味は ありましたし、最低限のソフト開発環境(アセンブラ/リンカ)が附属し、 RS232Cを備えたPCさえあれば即開発可能という手軽さ、そして何より2,800円 (ソフト無しなら2,000円)という安さに釣られてしまったのでした。

早速半田付けをして完成、サンプルプログラムを書き込んでLEDを点滅させて みたりしました。しかし!その先が無い。

そもそも何をやらせるか、目的が無いのだから仕方ありません。困りました。 そう、こんな時こそサーチエンジンです。何かH8のアプリケーションとして魅 力的なものはないでしょうか?

そこで私の目に留まったのがHOS-H8です。HOSとはRyuz氏により開発/公開され たμITRON 3.0準拠のRTOSで、Z80用とH8用があります。…これについてもそれ 以上の説明はやめておきましょう。H8云々よりも更に知らなかった、そして今 もそう知っているとは言えない状態だからです。こちらのRyuz氏のページ、Ryuz Laboratoryを見て戴きた い。RTOSにしてもITRONにしても、ホンの少し前まで、「そういうものがある」 という程度の認識しかなかったのですから。

まとめるとこういう事です。

「マイコンがある→入出力のある何かの装置を作ってみるか→ハードは今すぐ 手軽に始められない→ソフト先行で行こう→まず開発環境の整備と、ソフト作 りの基盤となるべき土台探し→GNUツールで環境を構築し、HOS-H8を載せるこ とでH8プログラミングや開発環境そのものに習熟しよう」


FreeBSD上でGNUのツールによる開発環境を構築する

開発環境を載せるのはFreeBSD 4.1-RELEASEです。今やPC-UNIXと言えばLinux ですが、私はかたくなにFreeBSDです。今更Linuxは使えません(笑)。今回の作 業では殆んどどちらでも同じ事なので、Linuxでも全く構わないのですが。

手近でGNUのプロダクトを手に入れる最も容易な手段は、やはりUNIX USER誌の 付録CDでしょう。いくらなんでもnet経由でgetするのはキツ過ぎます。アナロ グ33.6kな私には。

そこで借りてきた(←その上人から借りるのか!)CDからコピーしたのが、

gcc-2.95.2.tar.gz
binutils-2.9.1.tar.gz
の二つ。

インストール作業はエラく簡単です。GNU関係のmakeにはGNU makeが必要らし いので、packagesからgmake-3.79.1を入れておきます。後はこれだけ。

frodo% tar xzf binutils-2.9.1.tar.gz
frodo% cd binutils-2.9.1
frodo% ./configure --target=h8300-hms --prefix=/usr/local
       (ガンガン作業が進む)
frodo% gmake all
       (ガンガン作業が進む)
frodo% su
frodo# gmake install
       (ガンガン作業が進む)
frodo# exit
frodo% cd ..
frodo% tar xzf gcc-2.95.2.tar.gz
frodo% cd gcc-2.95.2/
frodo% ./configure --target=h8300-hms --prefix=/usr/local
       (ガンガン作業が進む)
「これだけ」と言った割に、ここで手作業が必要です。

gcc/Makefileの355行目に -Dinhibit_libc と書き加えます。

355: LIBGCC2_CFLAGS = -O2 $(LIBGCC2_INCLUDES) $(GCC_CFLAGS)
$(TARGET_LIBGCC2_CFLAGS) $(LIBGCC2_DEBUG_CFLAGS) $(GTHREAD_FLAGS)
-DIN_LIBGCC2 -D__GCC_FLOAT_NOT_NEEDED
↑これを↓こうします。(注: 表示上複数行に渡っている様に見えるが、実際は 一行のもの。また、"355:"は便宜的に書いた行番号。)
355: LIBGCC2_CFLAGS = -O2 -Dinhibit_libc $(LIBGCC2_INCLUDES) $(GCC_CFLAGS)
$(TARGET_LIBGCC2_CFLAGS) $(LIBGCC2_DEBUG_CFLAGS) $(GTHREAD_FLAGS)
-DIN_LIBGCC2 -D__GCC_FLOAT_NOT_NEEDED  
これをやらないと、makeに失敗します。このMakefileは./configureの度に 書き換えられるので注意しましょう。

frodo% gmake LANGUAGES=C all
       (ガンガン作業が進む)
frodo% su
frodo# gmake LANGUAGES=C install
       (ガンガン作業が進む)
frodo# exit
これで/usr/local/bin以下に、

h8300-hms-addr2line  h8300-hms-ar
h8300-hms-as         h8300-hms-c++filt
h8300-hms-coffdump   h8300-hms-gasp
h8300-hms-gcc        h8300-hms-gcj
h8300-hms-ld         h8300-hms-nm
h8300-hms-objcopy    h8300-hms-objdump
h8300-hms-ranlib     h8300-hms-size
h8300-hms-srconv     h8300-hms-strings
h8300-hms-strip      h8300-hms-sysdump
が出来ました。また、/usr/local/h8300-hms/以下にbin、includeやlibが入っ ています。

開発環境ができたからには何かコンパイルして動かしてみたいのが人情。それ では秋月のサンプルプログラム、TEST.MARをCで書き直してみます。

/* test.c */
/* rewrite TEST.MAR for test of h8300-hms-gcc */
#define IENR1 ((volatile unsigned char *)0xfff4)
#define IENR1_TA 0x40 /* bit 6 */

#define IRR1 ((volatile unsigned char *)0xfff6)
#define IRR1_TA 0x40 /* bit 6 */

#define TMA ((volatile unsigned char *)0xffa6)

#define PMR1 ((volatile unsigned char *)0xffe0)
#define PCR1 ((volatile unsigned char *)0xffe4)
#define PDR1 ((volatile unsigned char *)0xffd4)

void timer_int( void);

int main( void)
{
  unsigned char *p;

  *(PMR1) = (unsigned char)0x00; /* ポート1を汎用入出力ポートに設定 */
  *(PCR1) = (unsigned char)0xff; /* ポート1を出力ポートに設定 */

  *(TMA)  = (unsigned char)0x98; /* タイマーAを1Hzに設定 */

  *(IENR1) |= IENR1_TA; /* タイマーA割り込みを許可する。*/
        
  __asm("andc.b #127,ccr"); /* 割り込みマスクをクリアする */


  while (1);
  
  return 0;
}

#pragma interrupt
void int_timera( void)
{

  *(IENR1) &= (!IENR1_TA); /* 割り込み停止 */
  *(IRR1) &= (!IRR1_TA); /* 割り込みフラグをクリアー */

  *(PDR1) = ~(*(PDR1)); /* 値を反転し,反転値を出力する。*/

  *(IENR1) |= IENR1_TA; /* 割り込み再開 */
}
こんな風になりました。

このプログラムに関しては説明の必要は無いでしょう。port1の端子をテスタ で観察するなり、LEDと適当な抵抗をつけてVccに繋ぐなりすれば楽しめる筈で す。

それでは早速試しましょう。と言いたいところですが、まだCのプログラムを アセンブルするところまでしか出来ていません。リンクするにはメモリの割り 付けを行なうルールをldに教えてやらなければならないのです。

そのためのファイルがリンカスクリプトというものです。前述のGNUツールの 導入を行なっていれば、/usr/local/h8300-hms/lib/ldscripts/以下にあるの がそれです。しかし、そのまま一発では使えません。

更にリンクにはもう一つ、スタートアップルーティンが必要です。通常のC開 発環境を使っていると特に意識することは無いかもしれませんが、main関数を 実行する前の処理、スタックポインタとか静的変数領域の初期化なんかをやる 部分です。

また、リンクが済んでもまだ終りではありません。AKI-3664(やっ と本体が出てきた)に出来たものを転送する手段も必要です。ここでも先達の 成果を利用させて戴きましょう。

Linuxの活用について非常に有用な情報を公開しておられる、みつい わゆきお氏のLINUX研究所 から、3664dev.tar.gzをgetしてきます。

詳しい事は先ほどのLINUX研究所でも解説されていますし、ハードウェアマニュ アルにも詳述されていますが、いわゆるZTATマイコンのフラッシュメモリへの 書き込みは、

  1. ブートモードで起動
  2. ホストからフラッシュメモリ書き込みプログラムをSCI経由でRAMに読み込み
  3. フラッシュメモリ書き込みプログラムに制御を移す
  4. ホストから目的のプログラムを転送、フラッシュメモリ書き込みプログラム が受け取ったプログラムを書き込む
という手順で実行されます。

まずはそのフラッシュメモリ書き込みプログラムのバイナリを作ります。

frodo% tar xzf 3664dev.tar.gz
frodo% cd 3664dev
frodo% make
出来ました。h8_3664.binがそれです。

次はホスト側プログラムをコンパイルします。 これはLinuxを標準としているようなので、#define FreeBSDを有効にし、デフォ ルトのシリアルデヴァイス名をFreeBSD用に書換えます。また、h8_3664.binを カレントから探すようにもしました。ソースを見れば簡単にカスタマイズが出 来ると思いますが、一応私のやった改変を載せておきます。

--- 3664tool.c        Thu Nov 16 10:50:51 2000
+++ 3664tool.freebsd.c        Sun Jan 14 10:16:55 2001
@@ -5,21 +5,23 @@
  *                                        *
  ****************************************/
 
-#define        LINUX
-#undef        FreeBSD
+/*#define        LINUX*/
+#define        FreeBSD
 
 #include        <stdio.h>
 #include        <sys/stat.h>
 #include        <unistd.h>
 #include        <fcntl.h>
-#define        RSLINE        "/dev/ttyS0"
+
 #ifdef LINUX
  #include <termios.h>
  #define sgttyb termios
  #define stty(fd,term) tcsetattr(fd,TCSANOW,term)
+ #define RSLINE        "/dev/ttyS0"
 #endif
 #ifdef FreeBSD
  #include <sgtty.h>
+ #define RSLINE        "/dev/cuaa1"
 #endif
 
 unsigned char sPort[256], fileName[256], rsbuf[0x601];
@@ -129,7 +131,7 @@
         fprintf(stderr,"H8/3664F is ready!  2000/11/15 Yukio Mituiwa.\n");
         putbyte(0x06); getbyte();
         putbyte(0x00); getbyte();
-        if((fp = fopen("/usr/bin/h8_3664.bin","r")) == NULL) {
+        if((fp = fopen("h8_3664.bin","r")) == NULL) {
                 if((fp = fopen("/usr/local/bin/h8_3664.bin","r")) == NULL) {
                         fprintf(stderr, "h8_3664.bin not installed!\n" );
                         exit(-1);
これで、

frodo% gcc -O2 -o 3664tool 3664tool.freebsd.c -lcompat
frodo% strip 3664tool
frodo% su
frodo# chown root.network 3664tool h8_3664.bin
frodo# chmod u+s 3664tool
frodo# mv 3664tool h8_3664.bin /usr/local/bin/
とやれば書き込みプログラムの導入は完了です。

リンカスクリプトは3664dev/sample/h8rom.xを使います。h8rom.xの中を見れ ばtest.cの割り込み処理の関数名がint_timeraである理由がわかります。この ようにリンカスクリプト内で.vectors sectionが定義されていれば、ベクタテー ブルを一々プログラム中で定義しなくても、関数名と#pragma interruptだけ で済むのでスマートな感じがします。

取り敢えずのスタートアップルーティンとして3664dev/ramcrt0.sも作業ディ レクトリにコピーしておきます。スタックポインタを設定してmainに飛ぶだけ なので、そのままでは将来的には不足ですが今は十分です。

現在、作業ディレクトリには以下のファイルがあります。

h8rom.x ramcrt0.s test.c
手で作業をするのは面倒なので、Makefileを作ります。 3664dev/sample/Makefileが参考になるでしょう。

CFLAGS= -O -mh -nostdlib

test.mot : test
	h8300-hms-objcopy -O srec test test.mot
	chmod -x test.mot

test : Makefile h8rom.x test.c ramcrt0.s
	h8300-hms-gcc $(CFLAGS) -T h8rom.x test.c -o test  ramcrt0.s

clean :
	rm -f test.mot test

write:
	3664tool test.mot
AKI-3664をCOM2に接続し、ジャンパをBOOT modeに設定、電源ON。

以下にホスト上での作業結果を示します。

frodo% make write
h8300-hms-gcc -O -nostdlib -mh -mrelax -T h8rom.x -o test test.c ramcrt0.s
/usr/local/h8300-hms/bin/ld: warning: h8300 architecture of input file `/var/tmp/ccEGa688.o'
 is incompatible with h8300h output
h8300-hms-objcopy -O srec test test.mot
rm test
3664tool test.mot
H8/3664F is ready!  2000/11/15 Yukio Mituiwa.
EEPROM writing program transfer successed.
writing

0000 : 00 be 00 be 00 be 00 be 00 be 00 be 00 be 00 be 
0010 : 00 be 00 be 00 be 00 be 00 be 00 be 00 be 00 be 
0020 : 00 be 00 be 00 be 00 64 00 be 00 be 00 be 00 be 
0030 : 00 be 00 be 01 00 6d f6 0f f6 18 aa 6a aa 00 00 
0040 : ff e0 8a ff 6a aa 00 00 ff e4 fa 98 6a aa 00 00 
0050 : ff a6 6a 2a 00 00 ff f4 ca 40 6a aa 00 00 ff f4 
0060 : 06 7f 40 fe 01 00 6d f6 0f f6 01 00 6d f0 01 00 
0070 : 6d f1 01 00 6d f2 01 00 6d f3 01 00 6d f4 01 00 
0080 : 6d f5 18 bb 7a 02 00 00 ff f6 68 ab 7a 03 00 00 
0090 : ff d4 68 3a 17 0a 68 ba fa 40 6a aa 00 00 ff f4 
00a0 : 01 00 6d 75 01 00 6d 74 01 00 6d 73 01 00 6d 72 
00b0 : 01 00 6d 71 01 00 6d 70 01 00 6d 76 56 70 79 07 
00c0 : ff 7e 5e 00 00 34 40 f6 ff ff ff ff ff ff ff ff 
00d0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 
00e0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 
00f0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 
EEPROM Writing is successed.
frodo%
ジャンパを外して電源をONしてみます。テスタの針なりLEDがピコピコすれば OKです。

リンカ/アセンブラから文句が出てますが気にしません。どうしても気持ちが 悪いなら、ramcrt0.sの先頭に

        .h8300h
と書き、9行目の

        mov.w #_stack,sp

        mov.l #_stack,sp
と書換えれば警告は消えます。

これで開発環境は出来ました。


HOSを載せる

開発環境は出来ました。それではHOS-H8に取り掛かりましょう。

HOSのGNU開発環境へのportingは こちらで細淵氏により既に公開されています。v0.02がベースなので現在 のv0.07より少し古い版ですし、オリジナルの開発環境はイエローソフ トのものから秋月コンパイラに替わってしまっていますが、基本的にはそのままです。 大変参考になりました。

作業の結果をまとめたものが、

hos-h8-v0.07-gnu.tar.gz (67237 bytes)
です。極力オリジナルのhh8h_007.lzhを保存するようにしたつもりですが、ひょっ とすると洩れもあるかもしれないので気をつけて下さい。また、sampleの3664 用のmakeには日立の3664用ヘッダファイル header3664_j.hが必要です。日立 のWEBサイトから別途手に入れ、hos-h8-v0.07-gnu/sample/に置いて下さい。 3048用にもmake出来るようにはしてありますが、持っていないので検証はして いません。まぁ、3664だってsample程度でしか検証出来ていませんが。

以下に少し改変部分についての解説を。

細淵氏の情報によりGNU開発環境対応はほぼOKです。しかし、それだけでは淋 しいので更に独自に手を加える事にします。それは、「アドレス情報について はリンカスクリプトのみで全て決定する」ようにする事です。

オリジナルでは、スタックポインタの初期値をコンフィグレータに渡して設定 する事になっていますが、RAMエリアやら何やらの配置が変わるなら、どうせ リンカスクリプトの方も変更の必要が出てくる筈です。ならば、同種の情報は 一元化するに越した事はないでしょう。

そうなると、ベクタテーブルをコンフィグレータに出力させるのも一元化とい う方針にそぐわないような気がしてきます。ベクタテーブルもリンカスクリプ トに任せるようにします。

実はこれにはアドレス情報一元化以外にも意味があります。それはコンフィグ レータの、ターゲットCPU(この場合、厳密には動作モード)に対する依存性を 減らす事です。こんな風に書くと何やら大層に読めますが、つまりはオリジナ ルのコンフィグレータはベクタテーブルの構造を3048(アドバンスドモード)の ものに決め打っているので3664(ノーマルモード)ではそのまま使えないという 理由から必要になった事です。

hos.cfg内の割り込みハンドラ定義、DEF_INT( 24,TimerTrap);に対してオリジ ナルのコンフィグレータの吐くcfg_asm.srcのベクタテーブル部はこんな風に なっています。

; コンフィギュレーター アセンブラ部

                .CPU     300HA

                .IMPORT        _hos_start
                .IMPORT        _int_default
                .IMPORT        _int_trap
                .IMPORT        _TimerTrap

                (中略)

                .SECTION  VECT,DATA,ALIGN=2

; -----------------------------------------------
;          割り込みベクタテーブル
; -----------------------------------------------
                .DATA.L        _hos_start
                .DATA.L        _int_default

                (中略)

                .DATA.L        _int_default
                .DATA.L        INT18
                .DATA.L        _int_default

                (中略)

                .DATA.L        _int_default

                (中略)
                .SECTION  P,CODE,ALIGN=2

; -----------------------------------------------
;          割り込みハンドラ
; -----------------------------------------------

INT18:                PUSH.L        ER1
                MOV.L        #_TimerTrap,ER1
                jmp        @_int_trap

                (中略)

                .EXPORT        _fmcbtbl
                .EXPORT        _fmcbstbl


                .SECTION  C,DATA,ALIGN=2

_fmcbtbl:
_fmcbstbl:


                .END
そうです。hos.cfgで指定したDEF_INT( ベクタ番号, ベクタハンドラ);から、 VECTセクションの先頭+ベクタ番号*4の番地にベクタハンドラのアドレス (4bytes)を置く事になるのです。

問題は3664(ノーマルモード)の割り込みベクタが2bytesであるという事です。

勿論、出力するベクタを2bytesにするようにhcfgh8hを改造するという手もあ ります。しかし、そのような改造では3664用と3048用でコンフィグレータの実 行形式を共有出来ない(オプションで動作切替えという方式もアリだが)事にな りイマイチです。

そこで改造を加えたコンフィグレータの吐くcfg_asm.sはどうなるかというと、 DEF_INT(19, TimerTrap)に対して、

; コンフィギュレーター アセンブラ部

                .h8300h


                .section        .text

; -----------------------------------------------
;          割り込みハンドラ
; -----------------------------------------------

                .global        int13
int13:                push.l        er1
                mov.l        #_TimerTrap,er1
                jmp        @_int_trap

                .end
となります。

これに加えリンカスクリプトで、

SECTIONS
{
.vectors : {
        /* Use something like this to place a specific function's address
           into the vector table.  */
        SHORT(ABSOLUTE(_hos_start))        /*  Reset vector */
        SHORT(ABSOLUTE(_int_default))        /*  Reserved */
        (中略)
        SHORT(ABSOLUTE(_int_default))        /*  Reserved */
        SHORT(DEFINED(int07)?ABSOLUTE(int07):ABSOLUTE(_int_default))
        SHORT(DEFINED(int08)?ABSOLUTE(int08):ABSOLUTE(_int_default))
        SHORT(DEFINED(int09)?ABSOLUTE(int09):ABSOLUTE(_int_default))
        SHORT(DEFINED(int0a)?ABSOLUTE(int0a):ABSOLUTE(_int_default))
        SHORT(DEFINED(int0b)?ABSOLUTE(int0b):ABSOLUTE(_int_default))
        SHORT(DEFINED(int0c)?ABSOLUTE(int0c):ABSOLUTE(_int_default))
        SHORT(DEFINED(int0d)?ABSOLUTE(int0d):ABSOLUTE(_int_default))
        SHORT(DEFINED(int0e)?ABSOLUTE(int0e):ABSOLUTE(_int_default))
        SHORT(DEFINED(int0f)?ABSOLUTE(int0f):ABSOLUTE(_int_default))
        SHORT(DEFINED(int10)?ABSOLUTE(int10):ABSOLUTE(_int_default))
        SHORT(DEFINED(int11)?ABSOLUTE(int11):ABSOLUTE(_int_default))
        SHORT(DEFINED(int12)?ABSOLUTE(int12):ABSOLUTE(_int_default))
        SHORT(DEFINED(int13)?ABSOLUTE(int13):ABSOLUTE(_int_default))
        (中略)
        SHORT(DEFINED(int19)?ABSOLUTE(int19):ABSOLUTE(_int_default))
        }  > vectors
と指定する事で、ベクタテーブルの出力を行ないます。

3048用のリンカスクリプトでは、

        LONG(DEFINED(intXX)?ABSOLUTE(intXX):ABSOLUTE(_int_default))
        ----
と指定します。こうしてリンカスクリプトを切替えるだけでOKとなり、コンフィ グレータはベクタ長に依存しなくなりました。また、DEF_INT()で与えるベク タ番号と実際のベクタ番号を一致させる必要もなくなりました。リン カスクリプトの書き方次第です。これはあまり意味が無いかもしれませんが。

ついでに、startupルーティンがHOS本体に含まれているのもやや考えものだと 思われました。crt0.oとして/usr/local/h8300-hms/libにたたき込むのも一案 ですが、場合によってはstartupを切替えて使いたい事もあるかと思い(この時 は3048用と3664専用を作ればと考えていた)、作業ディレクトリにstartup.sを 持ち、明示的にリンクする方式を採りました。


アドレス=16bit(ノーマルモード)では他にもハメられた

3664はアドレス空間が16bit(64KB)しかありません。いわゆるアドバンスドモード はないのです。ノーマルモードで32(24)bitアドレスでアクセスしても上位ワー ド(上位8bit)は無視されるだけです。従って(:8、:16なアドレッシングモード 等もあるらしいですが)32(24)bitを指定しなければならない3048用(のアドバ ンスドモード)よりもコードサイズ/実行ステートの節約が可能です。

startup.sにおいても僅かではありますが、コードサイズを小さくする事が出 来ます。その為、3048(アドバンスドモード)用とは別になります。ただ、いく つか.lが.wになっているだけですが。 当然、HOS本体やアプリケーションについても節約の余地がある訳ですが、そ れは今後の課題です。gccに手を入れるのは少しやっかい(手に余る)と思われ るので、いかがわしいソースレベルオプチマイザなど作れれば愉しいかと。実 際に作(れ)るかどうかは別にして、名前だけは既に決まっています。 「USO3664」。"Usokusai Sourcelevel Optimizer for 3664"の略(^^)。オプティ マイズレベルを上げていくと、必要な32bit演算すら丸めてくれる危険な奴 (^^;。

このPCの有効が下位16bitのみというのが実は今回の作業の最大のハマリポイ ントでした。どうアセンブラ部分を見直してもsampleがうまく動かない。しか も必ずしも症状が一定しない。まれにTask1だけ動いているように見える事も ある。そんな状態が長く続きました。

細淵氏のGNU port版をベースにしていた時は、偶然Task1のみ動いているかの ような挙動を示していたので「なんだ、そのままで簡単だったな」等と思って しまいました。そしてHOSのサポート掲示版でHOS-H8hを3664上で動作させる事 に「成功」した事を報告した後、v0.07に取り掛かって事は発覚。動いている と思っていたものも、Taskや割り込みハンドラに少しコードを加えるだけで動 かなくなる事を知るに至りました。

3048での動作は確認されている筈のものです。いくら見てもおかしなところが あるとは思えません。しまいにはgasの吐くコードを疑って日立モニタ(3664用) 上の逆アセンブラで見てみたりしました。

他にしようもないので、hos_startから追いかけてもみましたが、そもそも μITRONの構造とかも全然分かってないので少し無理がある。どこで暴走して いるのかも特定できません。

若干煮詰まり気味だったその頃、HOSをうまく動かせないことはさて置いて、 前述のUSO3664について考えている時にふと気付きました。 「アドレス演算において32bitやるのは無駄だなぁ…ん?jsr/bsr/rtsってまさ か32bit分をPUSH/POPしてんのかなぁ?…64Kしか無い空間、RAMも2KBしかない のにそんな無駄な…そもそもPCだって16でえーじゃないか…そういえば実際、 PCって16bitなんじゃないのか?ハードウェアマニュアルを見ればわかるな… もしそうなら3048と挙動が変わってくる。HOSのCプログラムの中でスタックを 操作している部分があったような…」

タスクの処理でスタックを作っている部分がありました。そこでは当然 jsr/bsrでスタックに積まれる戻り番地は32bit(アドバンスドモードではダミー 8bit+PC 24bit)であるとして記述されているのです。ここを書換え、長いトン ネルから出る事が出来ました。

         /* スタックの初期設定 */
+#ifdef __TINY__
+        tcb->sp -= 1;
+        *(tcb->sp) = (INT)(UW)(FP)__tskst_entry; /* リターンアドレス */
+#else
         tcb->sp -= 2;
         *(FP *)tcb->sp = (FP)__tskst_entry;      /* リターンアドレス */
+#endif
         tcb->sp -= 10;
         *(FP *)tcb->sp = tcbs->task;             /* タスク開始アドレス */
         *(--tcb->sp) = (VH)stacd;                /* 初期化コード */

task.cの↑の部分がそのポイントです。

300Hコアと称するだけあって、流石にPCは24bitあることになっていましたが、 3664はノーマルモードでしか走らず、そのノーマルモードのjsr/bsrはPCの 下位16bit分しかスタックに積まないのでした。

これらの事から、3664はH8/300H互換とは銘打たれていますが、H8/300として 考えた方が良いのかもしれません。即ち-mno-h。しかし、そうすると32bit拡 張レジスタの旨味が生かせない上、他にも影響が出るかもしれないので、今は このまま-mhで行きます。

-mhnなんてのがあればな…やはりUSO3664…。

これで終ったと思うなよ!(追記 2001/2/11)

なんということだろう…。これで終りではありませんでした。というか、前段 のことに気付いた時点で当然注意して然るべき事だったのですが。

gccは、関数の引数渡しにおいて3個迄はレジスタ、er0〜er2を使い、4個以降 はスタックで渡します。そして関数では、4個目以降のスタックに積まれて渡 された変数に対するアクセスは、全く自然で当然なことに、関数の頭でスタッ クポインタを保存しているスタックフレームであるところのer6を基準として 読みます。

う〜む。全くまっとうなお話ですね。で、何が問題かと言うと…またもや jsr/bsrで積まれるPCのサイズなのです…。もうお分りですね。ノーマルモー ドとアドバンスドモードでは、スタック渡しの場合に引数の格納アドレスが 「2」ズレてしまうのでした。

なかなか気付きませんでした。4個の引数がある関数が少なかったから…とい うか、2個だけだったから。wai_flg(pol_flg)です。

どうにか動き始めた時から、何か変だとは思っていたのです。TWF_CLRしてい るのだからフラグをクリアする筈なのに、なぜか直後にclr_flgした場合と比べ て挙動が違う感じがして。-S出力を見てやっと気付きました。

で、対策です。アプリケーションレベルにおいては『引数が4個以上の関数 を作ってはならない』で済む訳です(-_-)が、μITRONなHOSにそれは通じませ ん。取り敢えずC言語API上に限って規格を守るという方向で許して戴きましょ う。

eventflg.hについては、

+#ifdef __TINY__
+#define wai_flg(a,b,c,d) wai_flg_t((a),(b),((c)<<8)|(d))
+#define pol_flg(a,b,c,d) pol_flg_t((a),(b),((c)<<8)|(d))
+ER   wai_flg_t(UB *p_flgptn, ID flgid, UH w_ptn_mode); /* イベントフラグ待ち */
+ER   pol_flg_t(UB *p_flgptn, ID flgid, UH w_ptn_mode); /* イベントフラグ待ち(ポーリング) */
+#else
ER   wai_flg(UB *p_flgptn, ID flgid, UB waiptn, UB wfmode); /* イベントフラグ待ち */
ER   pol_flg(UB *p_flgptn, ID flgid, UB waiptn, UB wfmode); /* イベントフラグ待ち(ポーリング) */
+#endif
こう。

これに加えeventflg.cで、

+#ifdef __TINY__
+/* イベントフラグ待ち */
+ER   wai_flg_t(UB *p_flgptn, ID flgid, UH w_ptn_mode)
+{
+	T_WFLG_STAT wflg_stat;
+	T_FCB  *fcb;
+	INT    ercd;
+
+	
+	wflg_stat.waiptn = w_ptn_mode >> 8;
+	wflg_stat.wfmode = w_ptn_mode & 0xff;
+
+#if __ERR_CHECK_LEVEL >= 4
+	/* IDチェック */
+	if ( flgid <= 0 )
+		return E_ID;
+	if ( flgid > fcbcnt )
+		return E_NOEXS;
+	if ( wflg_stat.wfmode > 3 || wflg_stat.waiptn == 0 )
+		return E_PAR;
+#endif
+	
+	fcb = &fcbtbl[flgid - 1];
+	
+	__set_imsk();
+	
+#if __ERR_CHECK_LEVEL >= 3
+	/* 状態チェック */
+	if ( sysstat != 0 ) {
+		__res_imsk();
+		return E_CTX;
+	}
+#endif
+	
+	if ( p_flgptn != NADR )
+		*p_flgptn = fcb->flgptn;
+	
+	/* フラグチェック */
+	if ( __chk_flg(fcb, &wflg_stat) ) {
+		__res_imsk();
+		return E_OK;
+	}
+	
+	/* 待ち状態に移行 */
+	__del_que(curtcb);
+	curtcb->tskstat = TTS_WAI;
+	curtcb->tskwait = TTW_FLG;
+	curtcb->data    = (VP)&wflg_stat;
+	__adt_que(&fcb->que, curtcb);
+	
+	/* ディスパッチ */
+	ercd = __tsk_dsp();
+	
+	__res_imsk();
+	
+	return ercd;
+}
+
 
+/* イベントフラグ待ち(ポーリング) */
+ER   pol_flg_t(UB *p_flgptn, ID flgid, UH w_ptn_mode)
+{
+	T_WFLG_STAT wflg_stat;
+	T_FCB  *fcb;
+	
+	wflg_stat.waiptn = w_ptn_mode >> 8;
+	wflg_stat.wfmode = w_ptn_mode & 0xff;
+
+#if __ERR_CHECK_LEVEL >= 4
+	/* IDチェック */
+	if ( flgid <= 0 )
+		return E_ID;
+	if ( flgid > fcbcnt )
+		return E_NOEXS;
+	if ( wflg_stat.wfmode > 3 || wflg_stat.waiptn == 0 )
+		return E_PAR;
+#endif
+	
+	fcb = &fcbtbl[flgid - 1];
+	
+	__set_imsk();
+	
+	/* フラグチェック */
+	if ( __chk_flg(fcb, &wflg_stat) ) {
+		if ( p_flgptn != NADR )
+			*p_flgptn = fcb->flgptn;
+		__res_imsk();
+		return E_OK;
+	}
+	
+	__res_imsk();
+	
+	return E_TMOUT;
+}
+#else
 /* イベントフラグ待ち */
 ER   wai_flg(UB *p_flgptn, ID flgid, UB waiptn, UB wfmode)
 {
 	T_WFLG_STAT wflg_stat;
 	T_FCB  *fcb;
 	INT    ercd;

@@ -201,7 +295,7 @@
 	
 	return E_TMOUT;
 }
+#endif /* __TINY__ */
とします。

かなり邪悪な感じがしないでもないですが、まぁこんなところでしょう。ああ、 -mhnがあれば…。


とにかくサンプルを動かす

sampleについても少しばかり手を入れる必要がありました。

  1. 3664のSCI3関係の割り込みベクタは一つだけで、SSRを読んで送信/受信/エ ラー処理に振り分ける必要がある。
  2. SSRのTDRE/RDRFはTDR/RDRの書き込み/読み込みで自動的にクリアされる。
  3. なんとなく19200bpsにしてみた。
  4. システムタイマにはTimer Aを使う。VやWは他に美味しい使い道がありそ うなので。
  5. AKI-3664はφ=16MHzで、外付けサブクロックの水晶は32.768kHz。これで はTimer AをNmsec(Nは整数)のインターバルタイマに設定するのは不可能。そ こで、8.192msecのインターバルタイマに設定し、TimerTrapの頭で101/125は タイマを8進め、24/125は9進めることにする。
この程度。sample.cでも#ifdef __TINY__ 〜 #endif で3664用改変部分を括っ ていますが、h8_sci.cの方は結構手を入れてしまったのでh8tiny_sci.cと改名 しておきました。こんなところでsampleの改変は完了です。

とうとうこの時が来ました。AKI-3664でHOSのサンプルプログラムを動かして みましょう。

アーカイブを適当なディレクトリに展開し、makeあるのみです。

frodo% tar xzf hos-h8-v0.07-gnu.tar.gz
おっと、日立のWEBからheader3664_j.hを入手したでしょうか? hos-h8-v0.07-gnu/sample/にコピーしておきましょう。

frodo% cd hos-h8-v0.07-gnu/
frodo% foreach f ( config  src sample)
foreach? cd $f && make && cd ..
foreach? end
        (ガンガン作業が進む)
frodo%
後は書き込むだけです。AKI-3664をCOM2に接続し、ジャンパをブートモードに 設定して電源をON。
frodo% cd sample
frodo% make write
        (ガンガン作業が進む)
EEPROM Writing is successed.
frodo%
/etc/remoteに次の行を加えます。

cuaa1d|cua1d:dv=/dev/cuaa1:br#19200:pa=none:
これでsampleの実行を確認する準備はできました。

frodo% tip cuaa1d
ここでAKI-3664のジャンパを外し、電源をONにします。HOS-H8hの世界にやっ とたどり着きました(、と願っています)。


今後の課題

やはりなんといっても、これがしっかりとHOSとして、RTOSとして動いている事 を証明しなければなりません。そのためには、

  1. μITRON プログラミングを学ぶ
  2. I/O port、Timer V/WやADC、IIC等の周辺デバイスのドライバを作る
  3. 以上を満たした上で、それらを実際に使うハード/ソフトを設計/製作/運 用する
というような事が必要になるでしょう。一体いつになることやら。

あ、USO3664も忘れてはいけません。また、src以下のファイル分割も避けては 通れないでしょう。そうでないとライブラリ化されている事が生かされないこ とになります。

果して行く先は…


謝辞

それでは最後を素晴らしい成果物を世に公開された方々への謝辞で締めくくり たいと思います。

皆様のお蔭で楽しむことができました。ここに感謝と敬意を表します。