SSブログ
CodeWarrior ブログトップ

HCS08 に64ビットの足し算をさせる [CodeWarrior]このエントリーを含むはてなブックマーク#

昔書いたプログラムの断片が出てきたので,記録にとめておきます.

64ビットの型が存在する

HCS08は,純粋な8ビットマイコンです.そのため,あまりビット長の多い数値の演算は得意としません.一方, CodeWarrior が提供する MC9S08QG8.H ヘッダファイルには, dlong という64ビットの型が宣言されています.あたかも,64ビットの演算が可能であるかのようです.

typedef unsigned char byte;
typedef unsigned int word;
typedef unsigned long dword;
typedef unsigned long dlong[2];

しかしながら,この型は,「長さ2の unsigned long の配列」を再定義したものに過ぎません.従って, C でプログラムを書くときには,32ビットの long 型の演算はできますが,64ビットの dlong 型の演算を使うことは出来ません. dlong 型は,あくまでも64ビットの領域を確保するための型なのです.

64ビットの値の演算がほしい

型が存在するのなら,使ってみたいものです.そこで,64ビットの演算を行う関数を作成してみました.まずは,64ビットの場所に16ビットの値を足し込む関数です.

void add_dl_w(dlong *lhs, word rhs) {
  __asm {
    LDHX  lhs
    LDA   7,X
    ADD   rhs:1
    STA   7,X
    LDA   6,X
    ADC   rhs:0
    STA   6,X
    LDA   5,X
    ADC   #0
    STA   5,X
    LDA   4,X
    ADC   #0
    STA   4,X
    LDA   3,X
    ADC   #0
    STA   3,X
    LDA   2,X
    ADC   #0
    STA   2,X
    LDA   1,X
    ADC   #0
    STA   1,X
    LDA   0,X
    ADC   #0
    STA   0,X
  }
}

ループなどを使っていると,長くなるので,ベタにアセンブラで記述しています.関数の引数である, rhslhs は,アセンブラ記述の中でも使用することが出来ます.

続いて,64ビットの場所に32ビットの値を足し込む関数です.

void add_dl_dw(dlong *lhs, dword rhs) {
  __asm {
    LDHX  lhs
    LDA   7,X
    ADD   rhs:3
    STA   7,X
    LDA   6,X
    ADC   rhs:2
    STA   6,X
    LDA   5,X
    ADC   rhs:1
    STA   5,X
    LDA   4,X
    ADC   rhs:0
    STA   4,X
    LDA   3,X
    ADC   #0
    STA   3,X
    LDA   2,X
    ADC   #0
    STA   2,X
    LDA   1,X
    ADC   #0
    STA   1,X
    LDA   0,X
    ADC   #0
    STA   0,X
  }
}

プログラムの断片にあったのは,足し算だけでした.


Cにおけるヘッダ・ファイルの話 [CodeWarrior]このエントリーを含むはてなブックマーク#

前回、Cにおける外部参照の話では、プロトタイプ宣言に一貫性が無い場合に問題が起こることを示しました。 今回は、一貫性を保証するためにどんな方法があるかを考えます。

プロトタイプ宣言を等しくするヘッダ・ファイル

答えを先に書きます。 「ヘッダ・ファイルの中にプロトタイプ宣言を書いて、宣言が必要な全てのソース・コードは、ヘッダ・ファイルを呼び出す。」 という約束にしてしまうのです。 この場合のヘッダ・ファイルは、このようになります。

/*  sub.h  */

word sub1(byte p1, word p2);  /* prototype */

そして、 "sub.c" ファイルと "main.c" ファイルからは、プロトタイプを宣言する代わりにヘッダ・ファイルを #include します。

/*  sub.c  */

#include <hidef.h> /* for EnableInterrupts macro */
#include "derivative.h" /* include peripheral declarations */
#include "sub.h"

word sub1(byte p1, word p2) {
  MTIMMOD = p1;
  TPMC1V  = p2;
  return MTIMCNT;
}
/*  main.c  */

#include <hidef.h> /* for EnableInterrupts macro */
#include "derivative.h" /* include peripheral declarations */
#include "sub.h"

volatile word value;

void main(void) {
  value = sub1(50, 1200);

  for(;;) {
  } /* loop forever */
  /* please make sure that you never leave main */
}

新たに作成したヘッダ・ファイルは、 "Sources" フォルダに入れます。 しかし、プロジェクト・ペインでは "Sources" には入れずに "Includes" に入れます。

WS000273.png

これは、 "sub.h" を単体でコンパイルさせないための工夫です。 ただ、この工夫が本当に正しく CodeWarrior に理解されているのかは、今のところ不明です。

約束は、破られるためにある?!

この「約束」は、あくまでも紳士協定なので、従わないプログラムを書こうと思えばいくらでも書けます。 以前、USBプロジェクト - サンプル・ソフトへの長い道のり(後編のつもりだった)で紹介した "LPGUID" の定義などは、その一例です。 複数のヘッダ・ファイルに同じ型宣言が散逸していて、その場しのぎの対応をするというのは、バグの温床に他なりません。

バグを早期発見する方法は、バグを早期発見できる「約束」を作り、それを「守る」ことです。

もし、関数定義を間違えていたら

ヘッダ・ファイルを使用する時の効果は、コンパイル時に誤りを見つけやすいということです。 そこで、関数定義部分で間違えた場合に何が起こるか調べてみました。

/*  sub.c  */

#include <hidef.h> /* for EnableInterrupts macro */
#include "derivative.h" /* include peripheral declarations */
#include "sub.h"

word sub1(word p2, byte p1) {
  MTIMMOD = p1;
  TPMC1V  = p2;
  return MTIMCNT;
}

例として、引数の順番を間違えてしまった場合を考えます。 この時には、以下のようなエラーが発生します。

Warning : C1140: This function is already declared and has a different prototype 
sub.c line 7   
Warning : C1020: Incompatible type to previous declaration (found 'unsigned int (*) (unsigned int ,unsigned char )', expected 'unsigned
int (*) (unsigned char ,unsigned int )')
sub.c line 7   
C1440: This is causing previous message 1020
sub.h line 3   

見事に、 sub.h ファイル内のプロトタイプ宣言と実際の関数定義が異なっていると指摘してくれました。 決して、 Warning だからと甘く見てはいけません。

もし、関数の参照のしかたを間違えていたら

次は、関数を参照する部分、具体的には、 main.c ファイルで間違えたらどうなるかを確認してみます。

/*  main.c  */

#include <hidef.h> /* for EnableInterrupts macro */
#include "derivative.h" /* include peripheral declarations */
#include "sub.h"

volatile word value;

void main(void) {
  value = sub1(50, 1200, 10000);

  for(;;) {
  } /* loop forever */
  /* please make sure that you never leave main */
}

最初は、引数の数を間違えた場合です。 この場合には、以下のエラーが発生します。

Error   : C1821: Wrong number of arguments
main.c line 10   
Error   : Compile failed

これは、前回、Cにおける外部参照の話で見た例と同じですね。

次は、引数の順番を間違えた場合のソース・コードと結果です。

/*  main.c  */

#include <hidef.h> /* for EnableInterrupts macro */
#include "derivative.h" /* include peripheral declarations */
#include "sub.h"

volatile word value;

void main(void) {
  value = sub1(1200, 50);

  for(;;) {
  } /* loop forever */
  /* please make sure that you never leave main */
}
Warning : C2705: Possible loss of data
main.c line 10   

これも、 Warning だからといって、見逃してはいけません。 16ビットの値 "1200" を8ビットの引数 "p1" に入れようとしている事をおかしいと判断している、真性の Warning です。

もし、プロトタイプ宣言を間違えていたら

プロトタイプ宣言は、つまり「仕様」なのだから、コーディング後に書き換えられるのは、筋違いだ。 もっともなご意見ですが、開発初期には、「仕様」が途中で変わってしまう場合もあります。 えっ? 出荷直前であっても「仕様」を変えられてしまうのですか? ご愁傷様です。

/*  sub.h  */

word sub1(byte p1, word p2, word p3);  /* prototype */
Error   : C1821: Wrong number of arguments
main.c line 10   
Error   : Compile failed
Warning : C1140: This function is already declared and has a different prototype 
sub.c line 7   
Warning : C1020: Incompatible type to previous declaration (found 'unsigned int (*) (unsigned char ,unsigned int )', expected 'unsigned
int (*) (unsigned char ,unsigned int ,unsigned int )')
sub.c line 7   
C1440: This is causing previous message 1020
sub.h line 3   

引数の数が違っている場合には、 main.c と sub.c の双方のコンパイルで堂々としたエラーが発生します。

/*  sub.h  */

word sub1(word p2, byte p1);  /* prototype */
Warning : C2705: Possible loss of data
main.c line 10   
Warning : C1140: This function is already declared and has a different prototype 
sub.c line 7   
Warning : C1020: Incompatible type to previous declaration (found 'unsigned int (*) (unsigned char ,unsigned int )', expected 'unsigned
int (*) (unsigned int ,unsigned char )')
sub.c line 7   
C1440: This is causing previous message 1020
sub.h line 3   

引数の順番を間違えた場合にも、双方のコンパイルで Warning が発生します。 ただし、この場合には、 Warning なので、コンパイルとリンクそのものは終了して機械語の出力が得られます。 せっかく、コンパイラが Warning を出してくれたのですから、どこに問題があるのか、しっかりと確認しましょう。

/*  sub.h  */

word sub2(byte p1, word p2);  /* prototype */
Warning : C1801: Implicit parameter-declaration for 'sub1'
main.c line 10   

この例は、プロトタイプ宣言で関数の名前を間違えてしまった場合です。 そんな、馬鹿な間違いはしないとお思いでしょうが、意外にソース・コードを見ただけでは問題を見つけられないという場面に出会います。 次回は、「ソース・コードを一見しただけでは発見できない」誤りをご紹介します。


Cにおける外部参照の話 [CodeWarrior]このエントリーを含むはてなブックマーク#

前方参照のお話では、ソースコードを単一のファイルに記述した場合を考えました。 現実のアプリケーション開発では、複数のファイルに関数を記述して、参照する場合が多く有ります。 このとき、どんな点に注意すれば良いのでしょうか。

関数だけくくり出すと

ここでは、 main.c と sub.c という二つのファイルが有ると想定し、 sub.c で宣言された sub1 関数を main.c から呼び出す場合を考えてみます。 まずは、 sub1 関数だけを くくり出して sub.c ファイルに移動します。

#include <hidef.h> /* for EnableInterrupts macro */
#include "derivative.h" /* include peripheral declarations */

word sub1(byte p1, word p2) {
  MTIMMOD = p1;
  TPMC1V  = p2;
  return MTIMCNT;
}

main.c にあったものと同じ #include が追加されていますが、これは sub1 関数で使用されているレジスタ名を宣言させるためのものです。

WS000270.png

このファイルを単独でコンパイルすると正常にコンパイルされます。 sub.c ファイルをコンパイル対象とするためには、このファイルを Sources フォルダに入れる必要がありますのでお忘れなく。


一方の main.c ファイルからは、 sub1 関数の本体部分だけを削除し、プロトタイプは残します。

#include <hidef.h> /* for EnableInterrupts macro */
#include "derivative.h" /* include peripheral declarations */

word sub1(byte p1, word p2);  /* prototype */

volatile word value;

void main(void) {
  value = sub1(50, 1200);

  for(;;) {
  } /* loop forever */
  /* please make sure that you never leave main */
}

これだけで、コンパイルとリンクがエラーなく行われます。 シミュレータで確認しても、 $E09F 番地の sub1 という関数を呼び出していることがわかります。

WS000272.png

extern は、不要なのか?

外部関数を呼び出す際には、多くの場合 extern という修飾子を付けますが、付けなくてもエラーにはなりませんでした。 え~と、これは何故なんでしょうか?

extern というのは、記憶クラスと呼ばれるものの一つです。 記憶クラスには、他に auto static というものがあります。 このうち、 auto は、関数定義の内部でのみ使われる記憶クラスです。 試しに main.c の sub1 関数宣言に auto 記憶クラスをつけて「auto word sub1(byte p1, word p2); /* prototype */」とするとエラーになりました。

Error   : C1005: Illegal storage class
main.c line 4   

ただ、 main 関数の中で auto 記憶クラスの関数を定義してもエラーにはなりませんでした。

#include <hidef.h> /* for EnableInterrupts macro */
#include "derivative.h" /* include peripheral declarations */

volatile word value;

void main(void) {
  auto word sub1(byte p1, word p2);  /* prototype */

  value = sub1(50, 1200);

  for(;;) {
  } /* loop forever */
  /* please make sure that you never leave main */
}

このことから、関数の宣言も変数の宣言と同じように、関数の外部であれば、 extern (外部からの参照が可能)がデフォルトになり、関数の内部であれば、 auto (関数の内部でだけ有効)がデフォルトになると考えられます。 つまり、 extern をわざわざ付けてやる必要なんかなかったということになります。

ちなみに、 static を付けた場合には、「外部からの参照が不可能」という意味になります。 そのため、 main.c ファイルの内部で sub1 関数を定義する必要があります。 この場合には、エラーが発生します。

Warning : C3603: Static 'sub1' was not defined
main.c line 4   

sub1 関数が、 main.c ファイル内部で定義されていないという意味のエラーです。

引数の数を間違えたら、どうなるか

main.c ファイルの中では、プロトタイプ宣言と実際の使い方に矛盾が無かったため、コンパイルエラーは起きません。 それでは、どちらも間違えていた場合には、何が起こるのでしょうか。

#include <hidef.h> /* for EnableInterrupts macro */
#include "derivative.h" /* include peripheral declarations */

word sub1(byte p1, word p2, word p3);  /* prototype */

volatile word value;

void main(void) {
  value = sub1(50, 1200, 10000);

  for(;;) {
  } /* loop forever */
  /* please make sure that you never leave main */
}

コンパイルすると、エラーは発生しません。 コンパイル単位である main.c ファイル内部で矛盾がないのだから当然です。 リンクする際にもエラーは発生しません。 リンクするときに引数のチェックは行われないので、これも当然です。

そこで、 main.c ファイルを Dsiassemble して、どんな機械語に変換されたかを確認してみました。

    9:    value = sub1(50, 1200, 10000);
  0000 a632     [2]             LDA   #50
  0002 87       [2]             PSHA  
  0003 4504b0   [3]             LDHX  #1200
  0006 89       [2]             PSHX  
  0007 8b       [2]             PSHH  
  0008 452710   [3]             LDHX  #10000
  000b cd0000   [6]             JSR   sub1
  000e a703     [2]             AIS   #3
  0010 960000   [5]             STHX  value

アセンブラが読めない人、ごめんなさい。 かいつまんで説明すると、三つの引数は、それぞれ、以下のように割り当てられて sub1 関数に引き渡されています。

引数名格納場所
p150Aレジスタ
p21200スタック内
p310000HXレジスタ

一方、 sub1 関数は、このようにコンパイルされています。

    5:    MTIMMOD = p1;
  0000 b700     [3]             STA   _MTIMMOD
    6:    TPMC1V  = p2;
  0002 3500     [5]             STHX  _TPMC1V
    7:    return MTIMCNT;
  0004 be00     [3]             LDX   _MTIMCNT
  0006 8c       [1]             CLRH  
    8:  }
  0007 81       [6]             RTS   

これも、解説すると、こうなっています。

引数名格納場所
p150Aレジスタ
p210000HXレジスタ

つまり、二番目の引数"1200"は、 p2 には引き渡されず、三番目の引数"10000"が p2 に引き渡されてしまっています。 コンパイラが、プログラムを記述した人の意思と異なったコンパイルを行っていて、それがコンパイル時もリンク時もエラーとして報告されない。 これは、ゆゆしき事態ではありませんか。

この原因は、 sub.c ファイルの関数定義と main.c のプロトタイプ宣言が異なっているからにほかなりません。 どうやって、双方のプロトタイプ宣言が等しくなるように保証してやるか。 これは、次回のテーマとします。


Cにおける前方参照のお話 [CodeWarrior]このエントリーを含むはてなブックマーク#

Cのコンパイラは、ワンパスが前提となっているので、何かを使おうとしたら、その「何か」をあらかじめ宣言しておく必要があります。 でも、なんでもかんでも宣言するのは面倒なので、「宣言しなくても使える」仕組みが残されているのです。

ワンパスって、何?

序文でさらりと書いた「ワンパス」ですが、これを説明するには、歴史の教科書が必要になってきます。 そもそも、コンパイラというのは、ソースコードを機械語の羅列に変換するプログラムです。 そのため、コンパイラには、「ソースコードを読む」機能が備わっていなくてはなりません。

では、ソースコードは、どこに入っていたかというと、昔は高価なハードディスクなどには入っていませんでした。 使われていたのは、パンチカード、紙テープ、磁気テープなどです。 フロッピーディスクが使われ始めたのは、最近の話です。

これらのメディアは、二つの種類に分けられます。 シーケンシャルなアクセスしか許されないものとランダムアクセス可能なものです。 UNIX世界では、シーケンシャルなアクセスしか許されないファイルシステムをキャラクタ・デバイス、ランダムアクセス可能なファイルシステムをブロック・デバイスと呼んでいます。

今では、考えられないことですが、ソースコードのほとんどは、キャラクタ・デバイスに入っていました。 そのため、ソースコードを一度にメモリに読み込んでランダムアクセスを行うと自由にコンパイルが出来るようになります。 ところが、ギッチョン、当時のコンピュータにはソースコードを一度に保存できるほどのメモリ容量がありませんでした。 そのため、キャラクタデバイスからソースコードを読みながら、コンパイルを行い、出てきた機械語プログラムの結果を別のキャラクタデバイスに書き出すという「省メモリに徹したコンパイラ」を作らざるを得ませんでした。

ワンパスという言葉は、この時に生まれました。 キャラクタデバイスからソースコードを一度読んだだけで、コンパイラが機械語を生成できることを「ワンパス」と呼びました。

もちろん、「ツーパス」のコンパイラも存在しました。 このコンパイラの場合には、同じソースコードを二回読み込む必要がありました。 一回目の読み込みで必要な情報を収集し、二回目の読み込みで機械語のプログラムを作成します。 二回目に同じソースコードを読み込むためには、パンチカードをもう一度デッキにセットしなおしたり、紙テープを巻き戻したり、磁気テープを巻き戻したりする必要があります。 これらの作業は、もちろん、人間の手作業によって行われます。

こう考えていくと、「ワンパス」と「ツーパス」のどちらが優れているかは、明白だと思います。 そのため、「ワンパス」である事を売りにしたコンパイラが続々と出てきたのです。

きちんと宣言したプログラムの場合

ワンパスという言葉の意味がわかった所で、「ワンパスのコンパイラに優しい」プログラムの例を挙げます。

#include <hidef.h> /* for EnableInterrupts macro */
#include "derivative.h" /* include peripheral declarations */


word sub1(byte p1, word p2) {
  MTIMMOD = p1;
  TPMC1V  = p2;
  return MTIMCNT;
}


volatile word value;

void main(void) {
  value = sub1(50, 1200);

  for(;;) {
  } /* loop forever */
  /* please make sure that you never leave main */
}

このサンプルプログラムは、 MC9S08QG8 をターゲットとしたプログラムで、 main 関数から sub1 という関数を呼び出しています。 処理の内容に意味は無いので気にしないように。

このプログラムでは、「sub1 という関数が byte 型の p1 と word 型の p2 という二つの引数を受け取り、 word 型の結果を返す。」ことがあらかじめ宣言されています。 そのため、何のためらいも無く main 関数で sub1 関数を使うプログラムが生成されます。

   15:    value = sub1(50, 1200);
  0000 a632     [2]             LDA   #50
  0002 4504b0   [3]             LDHX  #1200
  0005 ad00     [5]             BSR   sub1
  0007 960000   [5]             STHX  value

関数を後で定義したらどうなるか

先の例では、 sub1 関数を main 関数で使用する前に宣言していました。 もし、逆に、 sub1 関数を main 関数の後に配置したらどうなるでしょうか。

#include  /* for EnableInterrupts macro */
#include "derivative.h" /* include peripheral declarations */


volatile word value;

void main(void) {
  value = sub1(50, 1200);

  for(;;) {
  } /* loop forever */
  /* please make sure that you never leave main */
}

word sub1(byte p1, word p2) {
  MTIMMOD = p1;
  TPMC1V  = p2;
  return MTIMCNT;
}

このプログラムをコンパイルすると、以下のようなエラーメッセージが表示されます。

Warning : C1801: Implicit parameter-declaration for 'sub1'
main.c line 8   

Warning : C1140: This function is already declared and has a different prototype 
main.c line 15   

Error   : C1019: Incompatible type to previous declaration (found 'unsigned int (*) (unsigned char ,unsigned int )', expected 'int (*)
(int ,int )')
main.c line 15   

C1440: This is causing previous message 1019
main.c line 8   

Error   : C2801: ';' missing
main.c line 18   

Error   : Compile failed

main.c 8 行目「value = sub1(50, 1200);」では、"C1801"という警告が出ています。 これは、 sub1 関数が前もって宣言されていないために、「暗黙的な引数宣言を行った」という意味です。 つまり、ソースコードに書いていない事を勝手に解釈してコンパイルしちまったぜ、というとんでもない警告なのです。

ただ、このメッセージには続きがあって、 main.c 15 行目で、「前に宣言した型と違うじゃないか」とエラーを発しています。 エラーが発生すると、コンパイルは完了しないので、機械語のプログラムは生成されず、問題も発生しません。

このメッセージの解説によると、 main.c 8 行目でコンパイラが勝手に解釈していた sub1 関数は、「int sub1(void)」と解釈されたことがわかります。

コンパイラの勝手な解釈が現実と一致した場合

それでは、 sub1 関数がコンパイラが勝手に解釈していた「int sub1(void)」と同じだったら、何が起こるでしょうか。

#include <hidef.h> /* for EnableInterrupts macro */
#include "derivative.h" /* include peripheral declarations */


volatile int value;

void main(void) {
  value = sub1();

  for(;;) {
  } /* loop forever */
  /* please make sure that you never leave main */
}

int sub1(void) {
  return MTIMCNT;
}

コンパイルの結果、エラーは無くなりました。

Warning : C1801: Implicit parameter-declaration for 'sub1'
main.c line 8   

でも、相変わらず、警告は出ています。 コンパイルの結果は、こうなりました。

  0000 cd0000   [6]             JSR   sub1
  0003 960000   [5]             STHX  value

確かに間違ったことはしていません。 正常に働くプログラムが出来ることでしょう。

警告を消したい

ですが、警告といえども、メッセージが表示されるのは、気持ちがいいものではありません。 何とか消す方法はないでしょうか。 それが、プロトタイプ宣言です。

#include <hidef.h> /* for EnableInterrupts macro */
#include "derivative.h" /* include peripheral declarations */

int sub1(void);  /* prototype */

volatile int value;

void main(void) {
  value = sub1();

  for(;;) {
  } /* loop forever */
  /* please make sure that you never leave main */
}

int sub1(void) {
  return MTIMCNT;
}

追加した一行「int sub1(void);」がプロトタイプ宣言と呼ばれるものです。 この一行を加えたことで、警告も出なくなりました。 これで、万事解決です。

引数が違っていても大丈夫

上の例は、コンパイラの都合に合わせて、 sub1 関数の宣言を変えてしまったのですが、プロトタイプ宣言を行えば、引数や戻り値の型が異なっていても構いません。

#include <hidef.h> /* for EnableInterrupts macro */
#include "derivative.h" /* include peripheral declarations */

word sub1(byte p1, word p2);  /* prototype */

volatile word value;

void main(void) {
  value = sub1(50, 1200);

  for(;;) {
  } /* loop forever */
  /* please make sure that you never leave main */
}

word sub1(byte p1, word p2) {
  MTIMMOD = p1;
  TPMC1V  = p2;
  return MTIMCNT;
}

別のソースコードに含まれる関数を参照する場合については、別の機会に。


MC9S08JS8のSPIを使おうとしたんだけど、コンパイラではまった [CodeWarrior]このエントリーを含むはてなブックマーク#

MC9S08JS8のSPIを使おうとしましたが、向かうところ問題だらけでした。

SPRFフラグがクリアできない

MC9S08JS8には、16-bitのSPIが搭載されています。 こいつと74HC595シフトレジスタを使って、XXXしようと思いました。 74HC595は、シリアル転送後にLATCHクロックを発行する必要があるので、シリアル転送の終了を検出する仕組みが必要です。

そのためには、SPRFフラグを使うと便利です。 SPRFフラグを検出したら、フラグをクリアして、LATCHクロックをトグルする。 これで、完璧だったはずなのに、なぜかシフト・データがずれます。 原因を探るうちに、SPRFフラグがクリアされていないことがわかりました。 SPRFフラグがクリアされないと、LATCHクロックをトグルするタイミングが早すぎて、出力データがずれてしまいます。

SPRFフラグのクリアシーケンス

SPRFフラグをクリアするためのシーケンスは、このように書かれています。

SPRF is cleared by reading SPRF while it is set, then reading the SPI data register.
SPRFをクリアするには、SPRFフラグがセットされているときに(SPISレジスタを)読みだし、さらにSPIデータレジスタ(SPID)を読み出す。

前半部分は、 while 構文を使ってフラグのアサートを待つだけで十分です。 問題は、後半の SPID レジスタの読み出しでした。

最初のコード

うまくシリアル転送が出来ずに、データがずれてしまった最初のコードがこれです。

    SPID16 = buf[line][3];
    while (!SPIS_SPRF) ;
    // Dummy read with a void casting.
    (void)SPID16;

16-bitのSPIDレジスタは、"SPID16"というシンボルで宣言されています。 このレジスタは、"volatile"宣言されているので、(void)修飾をつけると、単なるレジスタの読み出しを行ってくれるはずです。 サンプルコードとして一般に使用されている技法です。 ところが、生成されたコードは、これでした。

  0088          L88:    
   70:              while (!SPIS_SPRF) ;
  0088 0f00fd   [5]             BRCLR 7,_SPIS,L88 ;abs = 0088
   71:              // Dummy read with a void casting.
   72:              (void)SPID16;
  008b b601     [3]             LDA   _SPID16:1

SPID16 レジスタのLSB側 8-bit だけが読み込まれています。 そのため、データの受け取りが完了したとみなされずに、SPRFフラグがクリアされなかったと考えられます。 なんで、8-bitしか読み出してくれないんだ?

二つ目のコード

読み出した値が使われないのが問題なのだろうと思い、二つ目のコードを書きました。

  word wDummy;
  :
    SPID16 = buf[line][4];
    while (!SPIS_SPRF) ;
    // Dummy read with a word variable.
    wDummy = SPID16;

ダミー変数を用意して、そこに値を代入させるようにしました。 すると、こんなコードが生成されました。

  006c          L6C:    
   63:              while (!SPIS_SPRF) ;
  006c 0f00fd   [5]             BRCLR 7,_SPIS,L6C ;abs = 006c
   64:              // Dummy read with a word variable.
   65:              wDummy = SPID16;
  006f b601     [3]             LDA   _SPID16:1

なんだ、全然変わらないジャン。 これもダメみたいです。

最後のコード

二つ目のコードの問題は、"wDummy"が結局は使われないというところにありました。 使わないけど代入して欲しいときには、"volatile"を付けます。

  volatile word vwDummy;
  :
    SPID16 = buf[line][5];
    while (!SPIS_SPRF) ;
    // Dummy read with a volatile word variable.
    vwDummy = SPID16;

ダミー変数の宣言を変えただけです。 これで生成されたコードがこれです。

  0057          L57:    
   59:              while (!SPIS_SPRF) ;
  0057 0f00fd   [5]             BRCLR 7,_SPIS,L57 ;abs = 0057
   60:              // Dummy read with a volatile word variable.
   61:              vwDummy = SPID16;
  005a 5500     [4]             LDHX  _SPID16
  005c 9eff08   [5]             STHX  8,SP

ようやく、16-bitの読み出しをしてくれるようになりました。 こんな事のために局所変数を割り当てる必要があるなんて、実にもったいない。 現在、 Technical Support に真相を問い合わせています。

こんなコードも試した

"volatile"変数に代入せずに"volatile"扱いにするために、こんなコードも試してみました。

    (volatile void)SPID16;

ところが、この場合には、内部エラーが発生してしまいました。

Error : C5701: Internal Error #604 in 'f:\build\Products\Hiware\cw_hc08_Build_Tools\cw_hc08_Build_Tools_070208\hiware\src\ccpp\ccpp.cpp' while compiling file 'C:\Projects\CW\JS08\Sources\main.c', procedure 'main', please report to Freescale

main.c line 11

Error : Internal Error

Error : Compile failed 

これも Technical Support に問い合わせ中です。

留意事項

以上の問題は、私がCodeWarrior for Microcontrollers V6.2 で遭遇したものです。 この記事をごらんになっている時点では修正されている可能性もありますので、ご留意ください。

2009-05-13 追記

freescaleの"Technical Support"から返答がありました。 "Internal Error"が出るのも、16-bitのダミー読み出しが出来ないのも、どちらもバグと認定していただきました。 いつ、修正されるかはわかりませんが、それまでの16-bitのダミー読み出しの対策は、

  (void)SPIDH;
  (void)SPIDL; 

だそうです。 つまり、8-bitずつ読み出せと。 これなら、ちょっとサイクル数がかさみますが、局所変数を割り当てることはありません。 しばらく、これで逃げるか。


MC9RS08KA8シミュレータでMTIMモジュールの誤った実装を見つけた [CodeWarrior]このエントリーを含むはてなブックマーク#

このレポートは、MC9RS08KA8 の Full Chip Simulation で見つけた MTIM モジュールの誤った実装に関するものです。

法とモジュロ・レジスタの関係

MTIM モジュールは、周期的にイベントや割り込みを発生させる簡単なタイマです。 MTIM モジュロ・レジスタ (MTIMMOD) と名づけられた8ビットのレジスタがあり、イベントの発生周期を決めます。 MTIM モジュールは、8ビットのカウンタ (MTIMCNT) の値と MTIMMOD の値を比較し、 MTIMCNT の値が MTIMMOD の値が一致したときにプリスケーラからのクロックが来たら、オーバーフロー・イベントを発生します。 このため、イベントの発生周期は、 (MTIMMOD + 1) にプリスケーラ・クロックの周期を掛けたものになります。

シミュレーションでイベント発生周期を確認した

パラメータで指定した100µ秒単位の待ち時間をつくる関数を作成しました。

void wait_100us(byte n) {
  MTIM2SC  = 0x30;      // Stop & Reset MTIM
  MTIM2CLK = 0x01;      // Prescale BUSCLK*2
  MTIM2MOD = 249;       // 100usec
  MTIM2SC  = 0x00;      // Start MTIM
  while (n-- > 0) {
    while (!MTIM2SC_TOF) ;  // Wait for the flag
    MTIM2SC_TOF = 0;    // Clear the flag
  }
}

BUSCLK が 5MHz に調節されていれば、 MTIM イベントは、 100µ秒ごとに発生します。 マイコンにプログラムを書き込む前にシミュレーションで MTIM イベント発生周期を確認しました。 "MTIM2SC_TOF = 0;" にブレーク・ポイントをしかけると、以下のサイクルでシミュレーションがとまりました。

CPU CyclesDifference
745-
1243498
1741498
2239498
2737498
3235498
3733498

クロック数の差分は、500になることが期待されたのですが、実際には、498になってしまいました。 変ですね。

MTIMCNT が、 (MTIMMOD - 1) の時に切れる

プリスケーラ・クロックの周期を長くすることで、 MTIMCNT カウンタが (MTIMMOD - 1) から 0 に戻っている事を確認しました。 これは、 MC9RS08KA8 データシートにある MITM モジュールの "Functional Description" に反する誤った実装です。

MC9RS08KA8には、二つの MTIM モジュール MTIM1 と MTIM2 があります。 どちらの MTIM モジュールも誤った実装をされていることを確認しました。

2009-03-25 追記

Service Request からの報告によると、この問題は追跡番号 MTWX34191 としてバグとして認定されたとのことです。

参考文献

HCS08 Unleashed: Designer's Guide to the HCS08 Microcontrollers

HCS08 Unleashed: Designer's Guide to the HCS08 Microcontrollers

  • 作者: Fabio Pereira
  • 出版社/メーカー: Booksurge Llc
  • 発売日: 2007/11/13
  • メディア: ペーパーバック

Found an incorrect implementation of MTIM module on MC9RS08KA8 simulator [CodeWarrior]このエントリーを含むはてなブックマーク#

This is a report regarding an incorrect implementation of MTIM modules found on the MC9RS08KA8 Full Chip Simulation.

Relation between Modulus and Modulo Register

The MTIM module is a simple timer to make a periodic events and/or interrupts. There is an 8-bit register named MTIM Modulo Register (MTIMMOD) to specify the PERIOD of the events. The MTIM compares an 8-bit counter (MTIMCNT) value and the MTIMMOD value, and cause an overflow event if a prescaled clock input comes when the MTIMCNT value matches to the MTIMMOD value. As the result, the event PERIOD equal to (MTIMMOD + 1) multiplied by the prescaled clock period.

Confirmed Event Period by Full Chip Simulation

A function is written to make a delay specified by a parameter in 100usec unit.

void wait_100us(byte n) {
  MTIM2SC  = 0x30;      // Stop & Reset MTIM
  MTIM2CLK = 0x01;      // Prescale BUSCLK*2
  MTIM2MOD = 249;       // 100usec
  MTIM2SC  = 0x00;      // Start MTIM
  while (n-- > 0) {
    while (!MTIM2SC_TOF) ;  // Wait for the flag
    MTIM2SC_TOF = 0;    // Clear the flag
  }
}

The MTIM events occurs in every 100usec when the BUSCLK is adjusted to 5MHz. Prior to a programming to the MCU, the MTIM event period is confirmed by the full chip simulation. When a breakpoint is set to the statement "MTIM2SC_TOF = 0;" the simulation stops at following cycles.

CPU CyclesDifference
745-
1243498
1741498
2239498
2737498
3235498
3733498

It is expected that all difference between two cycle counts is 500. But, all of the difference value are 498. It is very strange.

MTIMCNT expires when (MTIMMOD - 1)

It was also confirmed that the MTIMCNT counter value returns to 0 from (MTIMMOD - 1) by increasing the prescaled clock period. This is an incorrect implementation against the MTIM module's "Functional Description" in the MC9RS08KA8 datasheet.

There are two MTIM modules MTIM1 and MTIM2 in the MC9RS08KA8. It was confirmed that both MTIM modules seems to have incorrect implementation.

2009-03-25 : Appended

It was notified by the Service Request that this problem was recognized as a bug with a bug tracking number MTWX34191.

Reference

HCS08 Unleashed: Designer's Guide to the HCS08 Microcontrollers

HCS08 Unleashed: Designer's Guide to the HCS08 Microcontrollers

  • 作者: Fabio Pereira
  • 出版社/メーカー: Booksurge Llc
  • 発売日: 2007/11/13
  • メディア: ペーパーバック

MC9S08SH4シミュレータでTPM2の妙なふるまいを見つけた [CodeWarrior]このエントリーを含むはてなブックマーク#

このレポートは、MC9S08SH4 の Full Chip Simulation で見つけた TPM2 モジュールの妙なふるまいに関するものです。

ギャング出力でPWMのエミュレーション

ギャング出力をPWM出力として使うには、ソフトウェア・エミュレーションのプログラムを書く必要があることがわかってきました。 そこで、 MC9S08SH4 マイコンに搭載されている二つのタイマモジュールのうちの一つ、 TPM2 モジュールのアウトプット・コンペア(OC)機能とオーバ・フロー(OVF)機能を使って、PWMをエミュレーションするプログラムを開発しました。

発想は、実に簡単です。 OVFイベントが発生したらギャング出力をセットし、OCイベントが発生したらギャング出力をクリアします。 双方のイベントとも割り込みにより受け取り、割り込みサービス・ルーチン(ISR)の中に処理を含めることができます。 その結果、ギャング出力には、PWM波形が出てくると期待されます。

お試しプログラム

プログラムを書いて、 CodeWarrior の "Full Chip Simulation" にかけてみました。

word count_tpm2ch0;     // Timer count at TPM2CH0
word count_tpm2ovf;     // Timer count at TPM2OVF

void __interrupt VectorNumber_Vtpm2ch0 tpm2ch0_isr(void) {
  count_tpm2ch0 = TPM2CNT;
  TPM2C0SC_CH0F = 0;    // clear channel flag
}

void __interrupt VectorNumber_Vtpm2ovf tpm2ovf_isr(void) {
  count_tpm2ovf = TPM2CNT;
  TPM2SC_TOF = 0;       // clear overflow flag
}

void main(void) {
  TPM2SC_TOIE = 1;      // Enable overflow interrupt
  TPM2SC_CPWMS =0;      // Edge aligned
  TPM2C0SC_CH0IE = 1;   // Enable channel 0 interrupt
  TPM2C0SC_MS0x = 1;    // Output compare
  TPM2C0SC_ELS0x = 0;   // software only
  TPM2C0V = 10000;      // 50% duty
  TPM2MOD = 19999;      // 20000 cycle
  TPM2SC_PS = 0;        // 1X
  TPM2SC_CLKSx = 1;     // bus clock

  EnableInterrupts; /* enable interrupts */
  /* include your code here */

  for(;;) {
    __RESET_WATCHDOG(); /* feeds the dog */
  } /* loop forever */
  /* please make sure that you never leave main */
}

PWMの動作を確認するための簡単なプログラムです。 二つの変数、 "count_tpm2ch0" と "count_tpm2ovf" には、それぞれタイマカウンタの 50% と 0% の値が入ることが期待されます。 シミュレーションの結果、期待されない妙な結果が大域変数ペインに表示されました。

count_tpm2ch0    17 unsigned int
count_tpm2ovf    56 unsigned int

"count_tpm2ch0" が 0.0% を "count_tpm2ovf" が 0.3% を示しています。 どこがおかしいの?

妙なふるまい

ステップ動作シミュレーションによって、 "TPM2C0SC_CH0F" OC イベント・フラグの妙なふるまいが確認されました。 タイマ・カウンタの値がモジュロ値 (TPM2MOD - 1) から 0x0000 に戻るとき、 "TPM2SC_TOF" フラグがセットされるのと一緒に "TPM2C0SC_CH0F" フラグもセットされてしまいます。 その結果、割り込みベクタの優先順位に従って、 OVF イベントの ISR に先立って OC イベントの ISR が実行され、タイマ・カウンタから 0.0% の値を取り出します。 それから、 OVF イベントの ISR が 0.3% の値を取り出したのです。

加えて、タイマ・カウンタの値が 50% の値に達しても、 OC イベントは発生しませんでした。 このことから、 OC イベント・フラグをセットするロジックに不具合があるのではないかと思われます。

TPM1は、ちゃんと動く

MC9S08SH4 は、もう一つの TPM モジュール TPM1 を持っています。 TPM1 を使って同じプログラムを実行してみました。

word count_tpm1ch0;     // Timer count at TPM1CH0
word count_tpm1ovf;     // Timer count at TPM1OVF

void __interrupt VectorNumber_Vtpm1ch0 tpm1ch0_isr(void) {
  count_tpm1ch0 = TPM1CNT;
  TPM1C0SC_CH0F = 0;    // clear channel flag
}

void __interrupt VectorNumber_Vtpm1ovf tpm1ovf_isr(void) {
  count_tpm1ovf = TPM1CNT;
  TPM1SC_TOF = 0;       // clear overflow flag
}

void main(void) {
  TPM1SC_TOIE = 1;      // Enable overflow interrupt
  TPM1SC_CPWMS =0;      // Edge aligned
  TPM1C0SC_CH0IE = 1;   // Enable channel 0 interrupt
  TPM1C0SC_MS0x = 1;    // Output compare
  TPM1C0SC_ELS0x = 0;   // software only
  TPM1C0V = 10000;      // 50% duty
  TPM1MOD = 19999;      // 20000 cycle
  TPM1SC_PS = 0;        // 1X
  TPM1SC_CLKSx = 1;     // bus clock

  EnableInterrupts; /* enable interrupts */
  /* include your code here */

  for(;;) {
    __RESET_WATCHDOG(); /* feeds the dog */
  } /* loop forever */
  /* please make sure that you never leave main */
}

この場合、変数は期待通りの 50% と 0% の値を示していました。

count_tpm1ch0  10015 unsigned int
count_tpm1ovf     17 unsigned int

これは、 TPM2 だけの不具合なのだろうと思われます。

プロジェクト・アーカイブ

興味があれば、サンプル・プロジェクトをお試しください。 ZIPファイルを得るには、これを"SH07.zip"という名前で保存します。

参考文献

HCS08 Unleashed: Designer's Guide to the HCS08 Microcontrollers

HCS08 Unleashed: Designer's Guide to the HCS08 Microcontrollers

  • 作者: Fabio Pereira
  • 出版社/メーカー: Booksurge Llc
  • 発売日: 2007/11/13
  • メディア: ペーパーバック

Found a strange behavior of TPM2 on MC9S08SH4 simulator [CodeWarrior]このエントリーを含むはてなブックマーク#

This is a report regarding a strange behavior of the TPM2 module found on the MC9S08SH4 Full Chip Simulation.

PWM Emulation on Ganged Output

It was found that a software emulation must be written to use the ganged output as a PWM output. So, I have develop a PWM emulation program using the Output Compare (OC) feature and the Over Flow (OVF) feature of the TPM2 module which is one of the timer modules contained in the MC9S08SH4 MCU.

My idea is very simple. Set the ganged output when an OVF event occurs, and clear the ganged output when an OC event. Both events can be captured by interrupts and the procedure can be included in the interrupt service routine (ISR). As the result, it is expected to get a PWM waveform at the ganged output.

Attempted Program

I have written a program and tried with the "Full Chip Simulation" on the CodeWarrior.

word count_tpm2ch0;     // Timer count at TPM2CH0
word count_tpm2ovf;     // Timer count at TPM2OVF

void __interrupt VectorNumber_Vtpm2ch0 tpm2ch0_isr(void) {
  count_tpm2ch0 = TPM2CNT;
  TPM2C0SC_CH0F = 0;    // clear channel flag
}

void __interrupt VectorNumber_Vtpm2ovf tpm2ovf_isr(void) {
  count_tpm2ovf = TPM2CNT;
  TPM2SC_TOF = 0;       // clear overflow flag
}

void main(void) {
  TPM2SC_TOIE = 1;      // Enable overflow interrupt
  TPM2SC_CPWMS =0;      // Edge aligned
  TPM2C0SC_CH0IE = 1;   // Enable channel 0 interrupt
  TPM2C0SC_MS0x = 1;    // Output compare
  TPM2C0SC_ELS0x = 0;   // software only
  TPM2C0V = 10000;      // 50% duty
  TPM2MOD = 19999;      // 20000 cycle
  TPM2SC_PS = 0;        // 1X
  TPM2SC_CLKSx = 1;     // bus clock

  EnableInterrupts; /* enable interrupts */
  /* include your code here */

  for(;;) {
    __RESET_WATCHDOG(); /* feeds the dog */
  } /* loop forever */
  /* please make sure that you never leave main */
}

This is a very simple test program to check the PWM behavior. It is expected that the two variables "count_tpm2ch0" and "count_tpm2ovf" become 50% and 0% value of the timer counter respectively. As a simulation result, an unexpected strange result at the Global variable pane found.

count_tpm2ch0    17 unsigned int
count_tpm2ovf    56 unsigned int

"count_tpm2ch0" indicates 0.0% and "count_tpm2ovf" indicates 0.3%. What's wrong ?

Strange Behavior

A strange behavior of the "TPM2C0SC_CH0F" OC event flag was confirmed by step simulation. When the timer counter returns to 0x0000 from the modulo value (TPM2MOD - 1) , the "TPM2C0SC_CH0F" flag is set as well as the "TPM2SC_TOF" OVF event flag set. As the result, the OC event ISR is processed prior to the OVF event ISR according to the interrupt vector priority order to get the 0.0% timer counter value. And then, OVF event ISR gets 0.3% timer count value.

In addition, no OC events occur when the timer counter reaches to the 50% value. So, I think the logic to set the OC event flag is incorrect.

TPM1 Works Well

MC9S08SH4 has an another TPM module TPM1. A simulation is attempted with same program for TPM1.

word count_tpm1ch0;     // Timer count at TPM1CH0
word count_tpm1ovf;     // Timer count at TPM1OVF

void __interrupt VectorNumber_Vtpm1ch0 tpm1ch0_isr(void) {
  count_tpm1ch0 = TPM1CNT;
  TPM1C0SC_CH0F = 0;    // clear channel flag
}

void __interrupt VectorNumber_Vtpm1ovf tpm1ovf_isr(void) {
  count_tpm1ovf = TPM1CNT;
  TPM1SC_TOF = 0;       // clear overflow flag
}

void main(void) {
  TPM1SC_TOIE = 1;      // Enable overflow interrupt
  TPM1SC_CPWMS =0;      // Edge aligned
  TPM1C0SC_CH0IE = 1;   // Enable channel 0 interrupt
  TPM1C0SC_MS0x = 1;    // Output compare
  TPM1C0SC_ELS0x = 0;   // software only
  TPM1C0V = 10000;      // 50% duty
  TPM1MOD = 19999;      // 20000 cycle
  TPM1SC_PS = 0;        // 1X
  TPM1SC_CLKSx = 1;     // bus clock

  EnableInterrupts; /* enable interrupts */
  /* include your code here */

  for(;;) {
    __RESET_WATCHDOG(); /* feeds the dog */
  } /* loop forever */
  /* please make sure that you never leave main */
}

In this case, the variables indicate expected values, 50% and 0%

count_tpm1ch0  10015 unsigned int
count_tpm1ovf     17 unsigned int

I suppose that this is a TPM2's own problem.

Project Archive

Please try a sample project if you interested in. To get a ZIP file, Save this as "SH07.zip"

References

HCS08 Unleashed: Designer's Guide to the HCS08 Microcontrollers

HCS08 Unleashed: Designer's Guide to the HCS08 Microcontrollers

  • 作者: Fabio Pereira
  • 出版社/メーカー: Booksurge Llc
  • 発売日: 2007/11/13
  • メディア: ペーパーバック

MULTILINK にも色々とありまして [CodeWarrior]このエントリーを含むはてなブックマーク#

何だか、「Multilink」を目当てに来ていらっしゃる方が多いので、「Multilink」について解説します。 おとなの都合により、写真はありません。

HC08用MULTILINK

8-bitマイコンのHC08シリーズは、MON08というデバッグ用インターフェースを備えています。 このインターフェース規格を使ってデバッグを行う「Multilink」が存在します。

  • M68MULTILINK08

    この Multilink は、PCのプリンタポートに接続するように設計されています。 そのため、最近のPCでは使用することはないでしょう。 現在は、販売もされていないようです。

  • USBMULTILINK08

    この Multilink は、PCのUSBポートに接続するように設計されています。 価格は、$99.00です。

MON08インターフェイスは、正式には16ピンのコネクタで提供されます。 これらの「Multilink」は、MON08インターフェースしか備えていないため、HC08マイコンにしか使用することが出来ません。 HC08マイコンは、MC68HC908XXという型番がついています。

HCS08 / RS08 / ColdFire V1 用MULTILINK

8-bitマイコンのHCS08シリーズおよびRS08シリーズ、 16-bitマイコンのHCS12シリーズ、 さらに32-bitマイコンのColdFire V1シリーズは、Background Debug Mode (BDM) というインターフェースを備えています。 このインターフェイス規格を使ってデバッグを行う「Multilink」が存在します。

  • M68MULTILINKS08

    この Multilink は、PCのプリンタポートに接続するように設計されています。 そのため、最近のPCでは使用することはないでしょう。 現在は、販売もされていないようです。

  • USBMULTILINKBDM

    この Multilink は、PCのUSBポートに接続するように設計されています。 この製品には、いくつかのリビジョンがあり、対応する電源電圧などに違いがあります。 そのため、それぞれ対応するマイコンに違いがあります。 最新の REV.C だけが、上記のすべてのシリーズに対応していますので、購入時には注意が必要です。 価格は、$99.00です。

  • USBMULTILINK12

    HCS12対応の製品名でしたが、現在はUSBMULTILINKBDMに統一されています。

  • USBMULTILINKS08

    HCS08対応の製品名でしたが、現在はUSBMULTILINKBDMに統一されています。

この「Multilink」で使用されるBDMコネクタは、6ピンのコネクタです。 ColdFire V1 は、MCF51XXという型番で呼ばれます。

ColdFire V2 / ColdFire V3 / ColdFire V4 用MULTILINK

32-bitマイコンのColdFire V2/V3/V4シリーズは、Background Debug Mode (BDM) というインターフェースを備えています。 このインターフェイス規格を使ってデバッグを行う「Multilink」が存在します。 この BDM 規格は、HCS08シリーズの BDM 規格とは異なる規格で、26ピンのコネクタが使用されます。 同じ名前であっても、異なる規格ですので注意が必要です。

  • USBMLCF

    この Multilink は、PCのUSBポートに接続するように設計されています。 26ピンのコネクタが直接装備されており、フラットケーブルを介さずにターゲットボードに接続されます。 価格は、$299.00です。

MCF52XXシリーズのデバッグで使用されるのは、この「Multilink」です。 ColdFire V3 および V4 は、 MCF53XX, MCF54XX という型番で呼ばれます。

PowerPC 用MULTILINK

64-bitマイコンのPowerPCシリーズは、Background Debug Mode (BDM) というインターフェースを備えています。 このインターフェイス規格を使ってデバッグを行う「Multilink」が存在します。 この BDM 規格は、HCS08シリーズやColdFire V2シリーズの BDM 規格とは異なる規格で、10ピンのコネクタが使用されます。 同じ名前であっても、異なる規格ですので注意が必要です。

  • USBMLPPCBDM

    この Multilink は、PCのUSBポートに接続するように設計されています。 10ピンのコネクタがフラットケーブルを介してターゲットに接続されるように設計されています。 価格は、$249.00です。

対応しているのは、MPC5XXおよびMPC8XXだそうですが、私には何の事だか良くわかりません。

PowerPC Nexus 用MULTILINK

ある種の64-bitマイコンのPowerPCシリーズは、 JTAG/NEXUS というインターフェースを備えています。 このインターフェイス規格を使ってデバッグを行う「Multilink」が存在します。

  • USBMLPPCNEXUS

    PCのUSBとNEXUSをつなぐインターフェイスだそうです。 詳細は未調査。 価格は、$249.00です。

MPC55XXで使用されるそうですが、どんなコネクタになるのか、調査しておりません。


以上、ローエンドに厚く、ハイエンドに薄い調査となりましたが、違う「Multilink」を買ったりしないようにご注意ください。

参考文献

単にMultilinkが使えますと書いてはありますが…

Interface (インターフェース) 2008年 09月号 [雑誌]

Interface (インターフェース) 2008年 09月号 [雑誌]

  • 作者:
  • 出版社/メーカー: CQ出版
  • 発売日: 2008/07/25
  • メディア: 雑誌

ローエンドのBDMなら、こちら

HCS08 Unleashed: Designer's Guide to the Hcs08 Microcontrollers

HCS08 Unleashed: Designer's Guide to the Hcs08 Microcontrollers

  • 作者: Fabio Pereira
  • 出版社/メーカー: Booksurge Llc
  • 発売日: 2007/11/13
  • メディア: ペーパーバック

DEMO9S08QG8で、各種MCUと接続してみる [CodeWarrior]このエントリーを含むはてなブックマーク#

1938768

CodeWarrior for Microcontrollers V6.2を使うと、DEMO9S08QG8を「汎用デバッガ」として使うことが出来なくなるらしいことが分かってきました。 それでは、どのMCUに対して使用することが出来なくなっているのでしょうか。 手元にあるMCUを片っ端から試してみます。

可・不可一覧表

いちいち、物語形式にする必要も無いと思いますので、一覧表を作りました。 DEMO9S08QG8にそのまま搭載できるDIPパッケージのものとBDMコネクタ付きアプリケーションを作ったことのあるMCUを選んできました。

DEMO9S08QG8で接続できたMCU一覧
MCUMask#date codeV6.1V6.2
MC9S08QG8CPBE3M77B0607YESYES
MC9S08QG4CPAE-0645YESYES
MC9S08QA4CPAE-0752YESYES
MC9S08SH32CTJ--YESNO
MC9S08LC60CLH1M78B0722YESNO
MC9S08QD4VPC-0632YESNO
MC9S08QE8CPG6M40J0802YESNO

CodeWarriorのバージョンごとにDEMO9S08QG8で接続できたかどうかをYES(接続できた)とNO(接続不可)であらわしています。 ごらんのように、ことごとくブロックされて使えなくなっているようです。 V6.1をお持ちの場合には、大事にお使いください。

参考文献

HCS08 Unleashed: Designer's Guide to the Hcs08 Microcontrollers

HCS08 Unleashed: Designer's Guide to the Hcs08 Microcontrollers

  • 作者: Fabio Pereira
  • 出版社/メーカー: Booksurge Llc
  • 発売日: 2007/11/13
  • メディア: ペーパーバック

DEMO9S08QG8でMC9S08SH32CTJをプログラムしてみる - 解決編 [CodeWarrior]このエントリーを含むはてなブックマーク#

1938768

前回までのあらすじ

マイコン工作の実験室で、MC9S08SH32CTJ のプログラムができないとの声が上がりました。 何でも、CodeWarrior for MicrocontrollersV6.2 では書き込めなくて、 V6.1 では書き込めるとのこと。 DEMO9S08QG8に変換基板を実装して追試したところ、再現してしまいました。

これは、バグなのか、ワナなのか?

Service Request に問い合わせちゃえ

何だかよく分からないまま、Service Requestに問い合わせてしまいました。 すると、「何で QG8 なんて表示されているんだ。」「どんなボードを使っているんだ。自前のボードか?」と痛いところを突かれました。 「DEMO9S08QG8 に変換ボードつきの MC9S08SH32CTJ を装着したんだよ。」と返しましたが、向こうは夜になってしまったので、返答はまだありません。

Multilink(青)で証拠固め

08-07-23_20-24.jpg

U.S. からの返答を待っていられません。 「DEMO9S08QG8MC9S08SH32CTJ のデバッグをしようなんて考えるから、そうなるんだよ。そこまで、面倒みきれないよ。」という返答が来ることを予想して、証拠固めをすることにしました。 Multilink (REV.A) を借りてきて、プログラムとデバッグが出来るかどうか確認します。

MC9S08SH32CTJ を装着したままの DEMO9S08QG8BDM コネクタに Multilink (青) を接続します。 Multilink に電源を供給させる方法が分からないので、 DEMO9S08QG8 の DC コネクタに 5V の AC アダプタを接続して、 3.3V の電源を用意しました。

まずは、 V6.1 から。 プログラムもデバッグも問題なく出来ます。 完璧です。

それでは、問題の V6.2 で試します。 プログラム…できました。 実行…できました。 ステップ動作…できました。 というわけで、問題なくデバッグが出来ました。 げげっ。

結論

どうやら、結論が出ました。

CodeWarrior for Microcontrollers V6.2 を使うと、 MC9S08SH32 のデバッグには DEMO9S08QG8 が使えなくなる。

DEMO9S08QG8 が、どのデバイスで使えるのかについては、一つ一つ検証していくしか無いでしょう。 ただ、MC9S08SH32 だけがブロックされたとは考えにくいので、ほとんどのデバイスで使えなくなっているのではないかと予感しています。

やっぱり、本物の Multilink を買うか。

あっ、 Service Request にゴメンナサイしなきゃ。

2008-07-24 08:49 追記

Service Request から返答が来ました。

DEMOQG8は、SH32をプログラムするために作られていないという事を覚えておいてね。 もしSH32のプログラムをしたいのであれば、USBMULTILINK や Cyclone Pro なんかのプログラマを手に入れて、汎用プログラミングアダプタやターゲットボードと一緒に使う必要があるよ。

評価ボードを無理やり使うためのヒントなど、何ら得られず、完敗であります。 Multilink 買うかぁ。

参考文献

この中のどれだけのデバイスが使用不可になっているのだろう。

HCS08 Unleashed: Designer's Guide to the Hcs08 Microcontrollers

HCS08 Unleashed: Designer's Guide to the Hcs08 Microcontrollers

  • 作者: Fabio Pereira
  • 出版社/メーカー: Booksurge Llc
  • 発売日: 2007/11/13
  • メディア: ペーパーバック

DEMO9S08QG8でMC9S08SH32CTJをプログラムしてみる [CodeWarrior]このエントリーを含むはてなブックマーク#

マイコン工作の実験室で、MC9S08SH32CTJのプログラムができないとの声が上がりました。 何でも、CodeWarrior for Microcontrollers の V6.2 では書き込めなくて、 V6.1 では書き込めるとのこと。 いっちょ、確認してみるか。

機材 - DEMO9S08QG8

確認してみるかと、思いはしましたが、私が持っているBDM機材は DEMO9S08QG8 しかありません。 本物のMultilinkと動作は違うとは思いますが、まあ、いいだろう。

サンプル - MC9S08SH32CTJ

サンプルは、TSSOP20パッケージのMC9S08SH32CTJです。 以前、変換基板にはんだ付けしたままのチップがありましたので、これを使います。

いざ、合体ぢゃ

1938768

幸い、MC9S08QG8CPBEとMC9S08SH32CTJは、1から8ピンまでに互換性があります。 この事実を利用しない手は無い。 DEMO9S08QG8にそのまま搭載しました。


1938769

残りの12本の端子は宙に浮いていますが、接触もしていないからまあいいでしょう。 これで、準備完了。 まずは、 V6.2 で試してみます。

確かに V6.2 では、書き込めない

WS000154.png

Processor Expert を使って、 PTB7 に接続された LED を 2Hz で点滅させる「一行プログラム」を作成しました。 デバッグボタンを押すと、いつもの Connection Manager が出てきました。 "Connect"ボタンを押すと…いつもと様子が違います。

エラーメッセージです。 「デバイスに接続できません。HCS08プロセッサの電源を入れなおしてください。」 評価ボード上にチップを置いたのだから、これ以上の好条件はないはずです。 何度"Connect"ボタンを押しても状況は好転しません。 これが、「書き込めない」という症状なのか。

V6.1 では、書き込めた

V6.1 で Processor Expert を使って MC9S08SH32 のプログラムを作成するときには、パッチが必要です。 適用したパッチは、この二つです。 それぞれfreescale.comでキーワード検索すると出てきます。

  1. MCU_V6-1_SG32-SH32_SP
  2. MCU_V61_PE_3_03

合わせて 180MByte のファイルをダウンロード、インストールして、さあ書き込みです。 同じように Processor Expert で作成したプログラムを書き込むために Connection Manager の"Connect"ボタンを押すと…書き込めました。 プログラムの実行も出来ました。 LED はテカテカしています。 ブレークポイントも設定できました。 まったく、正常です。

またまた、Service Request にお願いしよう

ここから先、なぜ接続できないかをつきとめる機材が無いので、いつものように Service Request に質問してしまいます。 「DEMO9S08QG8で書き込めないんだけど」と質問して取り合ってもらえるかどうかが心配ですけど。

というわけで、続く。

参考文献?

この本には、MC9S08SH8は登場しますが、 MC9S08SH32は比較的新しいチップなので登場しません。

HCS08 Unleashed: Designer's Guide to the Hcs08 Microcontrollers

HCS08 Unleashed: Designer's Guide to the Hcs08 Microcontrollers

  • 作者: Fabio Pereira
  • 出版社/メーカー: Booksurge Llc
  • 発売日: 2007/11/13
  • メディア: ペーパーバック

CodeWarrior for Microcontrollers V6.2 Special Edition が壊れているらしい [CodeWarrior]このエントリーを含むはてなブックマーク#

bird.dip.jp: 平日日記で報告されていたので気が付きました。 先日、当ブログ記事で紹介した、"Special Edition: CodeWarrior for Microcontrollers V6.2"をWEBページからダウンロードすると、壊れたZIPファイルが出てくるらしい。 調査を行ったので、ここにまとめます。

(初出2008年6月12日)

あぁ、勘違い

5月27日に当ブログで紹介したときにすでに気が付いていたのですが、 評価(Evaluation)版をダウンロードしてもSpecial Editionをダウンロードしても、 408,790,650バイトの実行可能ファイル(通称EXEファイル)がダウンロードされてきました。

これらは、同一のファイルだったために、 「そうか、Special Editionと評価版には差が無くなったのね。」と 軽く思い込んでしまいました。 30日間はProfessional版の機能が、それ以降はSpecial Editionの機能が使えるものと勘違いをしたのです。

様子が変わってきた

ところが、bird.dip.jp: 平日日記によると、 Special Editionをダウンロードすると壊れたZIPファイルが出てくるようになったそうなのです。 そこで、改めて、ダウンロードしてみると、確かに、408,686,019バイトの壊れたZIPファイルが出てきました。

ZIPファイルを調査した

ZIPファイルは、WindowsXPの圧縮フォルダとしては認識してくれません。 拡張子をEXEにしましたが、実行可能ファイルとしては認識してくれませんでした。 拡張子LZHも試してみましたが、これも破損扱いです。

そこで、MontaVIsta玄箱に持っていって、fileコマンドで素性を調べたところ、

$ file CW_MCU_V6_2_SE.zip
CW_MCU_V6_2_SE.zip: Zip archive data, at least v2.0 to extract

と、ZIPファイルとは認識されているようです。 ただし、gzipさんは、

$ gzip -l CW_MCU_V6_2_SE.zip
compressed uncompr. ratio uncompressed_name
408686019 -1 0.0% CW_MCU_V6_2_SE.zip

と、訳のわからない事を言ってきます。 どうやら、fileコマンドは、ファイルの先頭だけを見て、 ZIPファイルとして認識したものと思われます。

いちかばちか、gzipで解凍させてみました。すると、

$ gzip -dc CW_MCU_V6_2_SE.zip > CW

gzip: CW_MCU_V6_2_SE.zip: invalid compressed data--format violated

確かに壊れてはいるのですが、出力されたファイルは、

$ file CW
CW: MS-DOS executable (EXE), OS/2 or MS Windows

と、確かに実行可能ファイルらしきものになっています。 この現象から、ZIPファイルの尻尾が化けたのだろうと想像できます。

困ったときのService Request

どうにもならないので、Service Requestに問い合わせを出しました。 返答があったら、またご紹介します。

2008年6月18日、追記

2008年6月13日、返答あり

Service Request から、返答がありました。

ZIPファイルに問題があるのは、認識しています。 この週末に修正するから、月曜日にもう一度試して。

月曜日って、きっとU.S.の月曜日なんだろうな。

2008年6月18日、催促に返答あり

その後、xoさん報告によると6月13日の日本時間10時に Copyという名の壊れたファイルがダウンロードされるようになっていました。 月曜日になっても火曜日になっても状況が変わらないので、 Service Requestに催促を出したところ、返答がありました。

評価版をダウンロードして使って。 Special Edition のライセンスファイルは、メールに添付しておいたから。

いや、そういう問題じゃないだろう。

再度、ダウンロード

2008年6月18日の朝になって、ダメモトで、ダウンロードをしなおしたところ、 CW_MCU_V6_2_SE.exeが出てきました。 サイズは、408,790,650バイトと評価版と同じサイズ。 もしやと思って、以前ダウンロードした評価版EXEファイルと比較したところ、 同じファイルでした。

振り出しに戻る。

2008年6月27日、追記

2008年6月23日、再度、問い合わせと返答

現在まで、状況は変わっていません。 2008年6月23日に、再度問い合わせを行いました。

CW_MCU_V6_2_SE.exeが、ダウンロードできるようになったのは、確認できたんだけど、 これって、評価版をダウンロードしたときに出てくるCW_MCU_V6_2_EVAL.exeと 全くおんなじものだよね。 _EVAL_SEって、等価(equivalent)なの?

これに対する返答は、早々と来ました。

質問1)_EVAL_SEは、等価なのか?

回答1)ソフトウェアは、同じ(same)です。ライセンス・ファイルを除いて。

そうこうしている内に、私がインストールした評価版CodeWarriorは、 Special Editionに変身してしまったのでした。


CodeWarrior for Microcontrollers Special Edition のコードサイズ制限を調べた [CodeWarrior]このエントリーを含むはてなブックマーク#

Simさんの所で 無償版コンパイラのコードサイズ制限を調べるプログラムを作成していただいたので、 CodeWarriorでも調べてみました。

DATAセグメントだけが大きい場合

Simさんによると、TICode Composer Essentials (CCE)では、 問題なくコンパイルできたケースだそうです。

#include <hidef.h> /* for EnableInterrupts macro */
#include "derivative.h" /* include peripheral declarations */

static const char x[0x8000] = {1};

void main(void) {
    int i;

  EnableInterrupts; /* enable interrupts */
  /* include your code here */

  PTAD_PTAD7 = 1;
  PTADD_PTADD7 = 1;
  for (;;){
    for (i = 0; i < sizeof(x); i++) {
      PTAD_PTAD7 ^= x[i];
      __RESET_WATCHDOG(); /* feeds the dog */
    }
  } /* loop forever */
  /* please make sure that you never leave main */
}

実験に使用したマイコンは、MC9S08LC60です。 これをコンパイルすると、リンカがエラーメッセージを出しました。

Link Error   : L4025: This limited version allows only 32768 bytes of C code
Link Error   : Link failed

どうやら、Cで生成したコードのサイズに32Kバイトの制限がかかっているようで、 textセグメントのサイズでは無いようです。 アドレス・マップは、こんな具合になっています。

*********************************************************************************************
SECTION-ALLOCATION SECTION
Section Name                    Size  Type     From       To       Segment
---------------------------------------------------------------------------------------------
.init                            132     R     0x1870     0x18F3   ROM
.startData                        14     R     0x18F4     0x1901   ROM
.rodata                        32768     R     0x1902     0x9901   ROM
.text                             47     R     0x9902     0x9930   ROM
.copy                              2     R     0x9931     0x9932   ROM

という結果が出てしまったので、二つ目の実験は行っておりません。


CCEと言われると、Component Creation Editionを思い出してしまう。


It seems that CodeWarrior for Microcontrollers V6.2 Special Edition is broken. [CodeWarrior]このエントリーを含むはてなブックマーク#

This is a translated version of the Japanese article. This article is provided because many international WEB searches are arrived to the Japanese article.

I have notified by the article bird.dip.jp: 平日日記 that the "Special Edition: CodeWarrior for Microcontrollers V6.2" , which is introduced in my BLOG article, is a broken ZIP file when downloaded from the freescale's WEB page. I have investigated the ZIP file and reports here.

Ooops, I have misunderstood

When I have introduced in this BLOG on 27 MAY 2008, I have found that an identical executable file with length 408,790,650 Bytes has been gotten when downloading both an Evaluation Edition and a Special Edition.

Because these are completely same file, I simply understand that "Huum, there is no difference between the Special Edition and the Evaluation Edition." I have mis understood that functions of Professional Edition are available for 30 days, and then functions of Special Edition are available.

WEB page changed

Some days later, I have notified by the bird.dip.jp: 平日日記 article that a broken ZIP file has been gotten when downloading the Special Edition. So, I have double-checked to download, and gotten a broken ZIP file with length 408,686,019Bytes.

Investigate the ZIP file

The ZIP file is not recognized as a compressed folder by the Windows XP. When changed the file's extension to EXE, it is not recognized as an executable file. When changed the file's extension to LZH, it is recognized as a broken file.

Next, I have copied the ZIP file to the KURO-BOX based on MontaVIsta and attempted the file command to investigate the file's property.

$ file CW_MCU_V6_2_SE.zip
CW_MCU_V6_2_SE.zip: Zip archive data, at least v2.0 to extract

It seems that the file is exactly recognized as a ZIP file. But the gzip command says,

$ gzip -l CW_MCU_V6_2_SE.zip
compressed uncompr. ratio uncompressed_name
408686019 -1 0.0% CW_MCU_V6_2_SE.zip

She doesn't understand what is the file. I assume that the file command investigate the header of the file to recognize the file as a ZIP file.

I have tried to extract the file with gzip command.

$ gzip -dc CW_MCU_V6_2_SE.zip > CW

gzip: CW_MCU_V6_2_SE.zip: invalid compressed data--format violated

Well, it seems that the file is really broken.

$ file CW
CW: MS-DOS executable (EXE), OS/2 or MS Windows

But the extracted file is recognized as an executable file. I assume that the tail part of the ZIP file has bee broken from the above investigation.

Try Service Request when you have a question

Because I have no idea to resolve this problem, I have issue a Service Request to freescale. I will report later when I have received a reply.

Received a Reply from Service Request

I have received a reply regarding the Service Request. It says, freescale recognize the problem on the ZIP file and it will be corrected this weekend. It also says, try again on Monday.

OK, I will try again on Monday. But when is Monday ? Is it a U.S. Monday ?


CodeWarrior for Microcontrollers V6.2 出たらしい。 [CodeWarrior]このエントリーを含むはてなブックマーク#

freescale.comService Requestから久々のお知らせがありました。

新バージョンのCodeWarrior for Microcontrollers v6.2がリリースされ、 WEBページからダウンロードできるようになったことをお知らせします。 このリリースでは、本Service Requestであなたが報告したバグの修正が含まれています。

このService Requestでレポートしたのは、 ColdFire でアセンブラ (3) - ミスアセンブル?ColdFire でアセンブラ (4) - ミスアセンブル解決編で書いたインラインアセンブラに掛け算インストラクションを書いた場合のバグでした。 このバグが修正されたかどうかは未確認です。 また、別にレポートしたバグがフィックスされたかどうかも未確認です。

ここのところ、確認するための時間が無いので、 どなたか、試してみませんか。 容量は、400MByteほどと書いてあります。


CodeWarrior のヘルプが出なくなった [CodeWarrior]このエントリーを含むはてなブックマーク#

「あとで消せるから。」というあまい言葉にのせられて、 つい出来心で"Internet Explorer 7"をインストールしたら、 ヘルプが出なくなってしまいました。

"Internet Explorer 7"をアンインストールしても状況は、変わらず。 きっと、ビル・ゲイツの呪いにちがいない。

この「ヘルプが出なくなる」現象は、リリース・ノートに記述があったのを覚えていました。 でも、それは、"Vista"のリリース・ノートです。 私が使っている"XP"の話ではなかったはずなのに。 リリース・ノートの置き場所は、以下の通りです。

"{INSTALLATION DIRECTORY}\Release_Notes\HC08\Notes_CW_MCUs_V6 1_Vista.txt"

この中に復旧の方法が書いてあります。

"The Windows Help (WinHlp32.exe) program is no longer included with Windows operating systems starting with Windows Vista" available at: http://support.microsoft.com/kb/917607

ところが、WinHlp32ファイルは、"C:\Windows\"に確かにあります。 どうやら、動かないようにするようにレジストリが設定されてしまったようです。 まったく、いらんことをする。

この文章を見た人が、同じ被害に遭わないですめば、さいわいです。

2008-02-23 17:36 追記

回復しました。 "itss.dll" というサービス・プロセス(UNIXだと/etc/servicesか?)が 動作していなかったのが原因のようです。

参考サイト
http://pasofaq.jp/windows/startmenu/itss.htm
「ヘルプとサポート」をクリックしても、何も表示されない

よくよく調べたら、CodeWarriorに限らず、すべてのヘルプが出てこなくなっていました。 そこで、代表的なヘルプである「ヘルプとサポート」をキーワードに 検索した結果がこのサイトです。 ありがたや、ありがたや。

"itss.dll"というのが、何者なのかという深いところは、目下調査中です。


CodeWarrior - ディスアセンブル [CodeWarrior]このエントリーを含むはてなブックマーク#

CodeWarriorのデバッガ(以下、HI-WAVE)は、初期状態でもアセンブラ・ウィンドウがあります。 これを、保存したいと思っても、ただのテキスト表示ではないので、 コピー&ペーストするわけにいきません。 さて、どうしましょうか。

HI-WAVEを起動すると、Commandというウィンドウが出てきて、 コマンド・ラインを受け付けることができます。 ここで、dasmコマンドを使うと、ディスアセンブルすることができます。

in>dasm 0xf000..0xf00f
00F000 PSHH   
00F001 PSHX   
00F002 LDHX   5,SP
00F005 LDA    ,X
00F006 AIX    #1
00F008 STHX   5,SP
00F00B PULX   
00F00C PULH   
00F00D RTS    
00F00E AIS    #-4

in>

dasmコマンドの引数には、ディスアセンブルするアドレス範囲を指定します。 ESCキーを押すと、ディスアセンブルは止まります。 オプションとして、;objを付けると、命令に対応するコードも表示されます。

in>dasm 0xf000..0xf00f;obj
00F000 8B       PSHH   
00F001 89       PSHX   
00F002 9EFE05   LDHX   5,SP
00F005 F6       LDA    ,X
00F006 AF01     AIX    #1
00F008 9EFF05   STHX   5,SP
00F00B 88       PULX   
00F00C 8A       PULH   
00F00D 81       RTS    
00F00E A7FC     AIS    #-4

in>

詳しくは、HI-WAVEのオン・ライン・マニュアルを参照してください。


CodeWarrior - ダンプ・リストをとる [CodeWarrior]このエントリーを含むはてなブックマーク#

CodeWarriorのデバッガ(以下、HI-WAVE)は、 初期状態でもメモリ・ダンプ・ウィンドウがあります。 これを、保存したいと思っても、ただのテキスト表示ではないので、 コピー&ペーストするわけにいきません。 さて、どうしましょうか。

HI-WAVEを起動すると、 Commandというウィンドウが出てきて、 コマンド・ラインを受け付けることができます。 ここで、dbコマンドを使うと、ダンプ・リストを取ることができます。

in>db 0xdc00..0xdc2f

DC00: 8B 89 95 E6 04 87 EE 05 8A F6 AF 01 9E EF 06 8B ................
DC10: 88 9E EF 05 88 8A 81 A7 FC C6 DC 96 4C 95 E7 01 ............L...
DC20: C6 DC 95 4C F7 CE DC 97 89 8A CE DC 98 20 1F 89 ...L......... ..

in>

dbコマンドの引数には、ダンプ・リストのアドレス範囲を指定します。 ESCキーを押すと、ダンプ・リストは止まります。 詳しくは、HI-WAVEのオン・ライン・マニュアルを参照してください。


CodeWarrior ブログトップ