Senshu LAB
サイト内検索: AND検索 OR検索  


Counter: 7847, today: 5, yesterday: 4

SDCC情報2

PICでコンパイルすると依存情報ファイル*.dが消えてしまう

JS3SUH? (2010-01-11 (月) 10:20:53)

一般に?Cでの依存情報ファイルとして*.dファイルが使われる事が多いと思うのですが、
SDCCでコンパイルすると折角作った*.dファイルが消えてしまいます。不審に思っていたら
原因はgputilsにあるようです。

gputilsのtrackerに2009-12-10 11:09付けで以下がありました。
gpasm removes *.d files while compiling - ID: 2911936

既にバグとしてあがっているのですがgputilsは9ヶ月以上メンテされていませんので
officialな対応は期待できないようです。

gpasmのソース、 deps.c Line 32 をみると

 if (state.depfile != named) {
   strncpy(state.depfilename, state.basefilename, sizeof(state.depfilename));
   strncat(state.depfilename, ".d", sizeof(state.depfilename));  
 }

とソース名.dをアセンブラの内部で使う依存情報ファイル名として使っており
折角Cソースの為に作った*.dを上書きしてしまいます。
処理終了後削除してしまうため*.dファイルが消失してしまうようです。

対応としては、
・Cで使用する依存情報ファイルの名前を変える。たとえば、*.depや、*.c.d等
・deps.cを修正しgpasmが使う依存ファイルの名前を変える。たとえば以下のように
 ソース名.asm.dにしてみる等

   // strncpy(state.depfilename, state.basefilename, sizeof(state.depfilename));
   strncpy(state.depfilename, state.srcfilename, sizeof(state.depfilename));


それにしても、一時ファイルなら一時ファイルっぽい名前にしてくれればいいのに、
なぜポピュラーな*.d何でしょうかね…


  • senshu 2010-01-11 (月) 10:44:53
    JS3SUHさん、今年もよろしくお願いいたします。

    以前は、私もgputilsを使っていましたが、マクロの制限や codepackが機能しない
    など、一時間ほど使っただけなのに多数のバグを確認し、完成度に疑問を持ちました。
    それ以降、利用を中断しています。

    さらにバグ修正が遅いので、安心して使えないという印象があります。

    そのため、こうした情報は貴重です。今後もよろしくお願いいたします。

インラインアセンブラで#ifディレクティブを使うとエラーになる

JS3SUH? (2009-12-20 (日) 21:28:57)

Cソースにインラインアセンブラでソースを記述している際に、
__asm 〜 __endasm間で#ifや#ifdefを使いたいことが多々あります。
数行程度のものであれば問題ないのですが、数画面もの

#ifブロックを書くと

405        ; 開放時処理
406_scan_keys_hold_key2_up:
407        BTFSS (_m_key_regs + ((((0 + (1)) + (1)) + (1)) + (1))), (7 -4);
408        B _scan_keys_hold_key2_end;
409        MOVFW ((_m_key_regs + (((((((((0 + (1)) + (1)) + (1)) + (1)) + (1)) \
   + (1)) + (1)) + (1)) + (1)) + (2 * (1))) + 0);
410        IORWF ((_m_key_regs + (((((((((0 + (1)) + (1)) + (1)) + (1)) + (1)) \
   + (1)) + (1)) + (1)) + (1)) + (2 * (1))) + 1), 0x0000;
411        SKPNZ;
412        BCF (_m_key_regs + ((((0 + (1)) + (1)) + (1)) + (1))), (7 -4);
413_scan_keys_hold_key2_end:
414        # 838 "key.c"
415_scan_keys_unstable:
416        MOVFW (_m_key_regs + (0 + (1)));
417        MOVWF (_m_key_regs + ((0 + (1)) + (1)));
418
419        RETURN

414行の様な#linenoという行がCPPにより生成されます。
行の直前のコードからCソースのlineno行まで省略したよというサインみたいで、数行
程度では(見通しが利くから?)生成されません。
asmファイルを見直すにはあると有益なのですが、残念ながらこのままではアセンブル
できません。

sdcc -mpic14 -p16f886 --std-c99 --stack-size 10 -I. -Wl-c -Wl-m -Wl-w -Wl"-s \
nns-as01.lkr"   -c -o state.o state.c
sdcc -mpic14 -p16f886 --std-c99 --stack-size 10 -I. -Wl-c -Wl-m -Wl-w -Wl"-s \
nns-as01.lkr"   -c -o key.o key.c
key.asm:414:Error [103] syntax error
make: *** [key.o] エラー 1
sdcc -mpic14 -p16f886 --std-c99 --stack-size 10 -I. -Wl-c -Wl-m -Wl-w -Wl"-s \
nns-as01.lkr"   -c -o lcd.o lcd.c

この#lineno行が生成されたことで上記のようにgpasmはこれをsyntax errorとしてしまい
困ってしまいます。

そこで、コンパイルオプションに -Wp-P を加えます。
このオプションは #linenoの生成を抑制するものです。
このオプションをつけると以下のように無事アセンブルまで通ります。

sdcc -mpic14 -p16f886 --std-c99 --stack-size 10 -Wp-P -I. -Wl-c -Wl-m -Wl-w -Wl\
"-s nns-as01.lkr"   -c -o state.o state.c
sdcc -mpic14 -p16f886 --std-c99 --stack-size 10 -Wp-P -I. -Wl-c -Wl-m -Wl-w -Wl\
"-s nns-as01.lkr"   -c -o key.o key.c
sdcc -mpic14 -p16f886 --std-c99 --stack-size 10 -Wp-P -I. -Wl-c -Wl-m -Wl-w -Wl\
"-s nns-as01.lkr"   -c -o lcd.o lcd.c

この問題は、__asm〜__endasmの中で使用した際のみ起きます。
__asm〜__endasmの外で#ifを使う分には問題ないため、-Wp-Pでは無く、一度__endasmと
閉じてしまって記述する手もありますが、面倒ですので-Wp-Pを書くほうが楽チンです
よね。
※ アセンブリソースが汚いのは、アセンブリからアクセスする変数は
オブジェクト毎に纏めて必要数のbyte配列として確保してMACROで
名前付けしているからです。こうすると少なくともインラインを
書いている際にBANKSELを多様しなくて済みますから。
…asmファイルの可読性としてはとても悪いのですが(--;。


  • JS3SUH? 2009-12-21 (月) 20:43:08
    暫らく-Wp-Pで使っていたのですが副作用が出てしまいました。
    エラー表示でファイル名や行番号が正常に出ないのです。
    こちらの方が致命的でしたorz
    そこで、SDCCに簡単なパッチを当ててみました。
    *** sdcc-20091219-5600/src/pic/gen.c Fri Nov  6 08:11:10 2009
    --- sdcc/src/pic/gen.c       Mon Dec 21 18:26:39 2009
    ***************
    *** 4395,4400 ****
    --- 4395,4401 ----
      {
        char *buffer, *bp, *bp1;
        bool inComment = FALSE;
    +   bool inHash = FALSE;
    
        FENTRY;
        DEBUGpic14_emitcode ("; ***","%s  %d",__FUNCTION__,__LINE__);
    ***************
    *** 4412,4423 ****
                ++bp;
                break;
    
              case '\n':
    !           inComment = FALSE;
    !           *bp++ = '\0';
    !           if (*bp1)
    !             addpCode2pBlock(pb, newpCodeAsmDir(bp1, NULL)); // inline directly, no process
    !           bp1 = bp;
                break;
    
              default:
    --- 4413,4443 ----
                ++bp;
                break;
    
    +         /* Check # xxx CPP generate linenum sign */
    +       case '#':
    +         if (!inComment)
    +           {
    +             inHash = TRUE;
    +           }
    +         ++bp;
    +         break;
    +
              case '\n':
    !         /* Skip # linenum line */
    !         if (inHash)
    !           { /* # linenum */
    !             inHash = FALSE;
    !             bp++;
    !             bp1 = bp;
    !           }
    !         else
    !           { /* other */
    !             inComment = FALSE;
    !             *bp++ = '\0';
    !             if (*bp1)
    !               addpCode2pBlock(pb, newpCodeAsmDir(bp1, NULL)); // inline directly, no process
    !             bp1 = bp;
    !           }
                break;
    
              default:
    インラインアセンブラ展開中にコメント以外で#が現れたらその行そのものを
    出力しないというものです。
    とりあえず、インラインアセンブラブロックから# linenumを取り除くという
    目的そのものは果たせています。
    ちゃんとエラー時のファイル名や行番号も出るのが確認できています。

    PIC14しか当てていませんが、恐らくPIC16でも同様の処理が必要ではないかと
    思います。今のところPIC18Fを触る機会が無いのでやはり未確認です。

たとえ定数でも少数は使わない

JS3SUH? (2009-12-20 (日) 09:54:29)

たとえば、こういうとき。

#define TICKS_1SEC      (128)
#define SEC_BLINK_TIMEUP        (TICKS_1SEC/2)


#define TICKS_1SEC      (128)
#define SEC_BLINK_TIMEUP        (TICKS_1SEC*0.5)


どちらもSEC_BLINK_TIMEUPの最終的な結果は64です。ちゃんと割り切れます。
ちゃんと割り切れている定数をchar形に入れるというケースなのですが、
全く違う結果になります。

元になるソースコード

  /* 秒を更新してから約0.5秒後に:を消去(:のブリンク) */
  if (cnt++ == SEC_BLINK_TIMEUP)
    {
      lcd_locate(CLOCK_DOT_X,y);                                                

前者のコンパイル結果結果

_00189_DS_~
;       .line   563; "utils.c"  if (cnt++ == SEC_BLINK_TIMEUP)
        BANKSEL _clock_blink_cnt_1_1
        MOVF    _clock_blink_cnt_1_1,W
        BANKSEL r0x1032
        MOVWF   r0x1032
        BANKSEL _clock_blink_cnt_1_1
        INCF    _clock_blink_cnt_1_1,F
        BANKSEL r0x1032
        MOVF    r0x1032,W
        XORLW   0x40
        BTFSS   STATUS,2
        GOTO    _00192_DS_
;       .line   565; "utils.c"  lcd_locate(CLOCK_DOT_X,y);
        BTFSS   r0x1031,0
        GOTO    _00196_DS_
        MOVLW   0x40
        MOVWF   r0x1032
        GOTO    _00197_DS_
_00196_DS_~
        BANKSEL r0x1032
        CLRF    r0x1032

後者のコンパイル結果

_00189_DS_~
;       .line   563; "utils.c"  if (cnt++ == SEC_BLINK_TIMEUP)
        BANKSEL _clock_blink_cnt_1_1
        MOVF    _clock_blink_cnt_1_1,W
        BANKSEL r0x1032
        MOVWF   r0x1032
        BANKSEL _clock_blink_cnt_1_1
        INCF    _clock_blink_cnt_1_1,F
        BANKSEL r0x1032
        MOVF    r0x1032,W
        PAGESEL ___uchar2fs
        CALL    ___uchar2fs
        PAGESEL $
        BANKSEL r0x1035
        MOVWF   r0x1035
        MOVF    STK00,W
        MOVWF   r0x1034
        MOVF    STK01,W
        MOVWF   r0x1033
        MOVF    STK02,W
        MOVWF   r0x1032
        MOVLW   0x00
        MOVWF   STK06
        MOVLW   0x00
        MOVWF   STK05
        MOVLW   0x80
        MOVWF   STK04
        MOVLW   0x42
        MOVWF   STK03
        MOVF    r0x1032,W
        MOVWF   STK02
        MOVF    r0x1033,W
        MOVWF   STK01
        MOVF    r0x1034,W
        MOVWF   STK00
        MOVF    r0x1035,W
        PAGESEL ___fseq
        CALL    ___fseq
        PAGESEL $
        BANKSEL r0x1032
        MOVWF   r0x1032
        MOVF    r0x1032,W
        BTFSC   STATUS,2
        GOTO    _00192_DS_
;       .line   565; "utils.c"  lcd_locate(CLOCK_DOT_X,y);
        BTFSS   r0x1031,0
        GOTO    _00196_DS_
        MOVLW   0x40
        MOVWF   r0x1032
        GOTO    _00197_DS_
_00196_DS_~
        BANKSEL r0x1032
        CLRF    r0x1032

衝撃の事実・・・としか言いようがありません。
定数計算の最中に小数が使われた場合(恐らく割り切れなかったときも?)
SDCCは渡された定数をあくまで浮動小数点数としてfloat型で扱ってしまっています。
その為、charをわざわざfloatに変換して比較して結果を確認しているので、後者は
とても長いコードになっています。

正直バグなのか正しい解釈なのかはわかりませんが、
「定数計算であっても少数は使わない。」
これは厳守ですね。もしくは意図して(char)とキャストを入れる。

↑では書かなかったのですが、上の例で(TICKS_1SEC/3)という
割り切れないものも試してみました。結果は前者の方になりました。

SDCCというかCのTips的要素が強い内容ですが、こういう解釈されるという例で
あげておきました。


SDCC PIC14の割り込みとSTKxx

JS3SUH? (2009-12-18 (金) 22:54:15)

こんばんは、お初にお目にかかります。

こちらにSDCC PIC関連のTipsが蓄積されているようなので、気がついたことを投稿
しておきます。既に皆さんご存知だとおもいますけど…
以下、全てPIC14portsに関してです(PIC16は未確認)。

・割り込み処理で1byteを超える引数及び戻り値を持つ関数を呼び出すと容易にクラッシュします。

<原因>
SDCCはSTKxxを割り込みで退避しない為。
SDCCでは1byteの引数又は戻り値はWREGで受け渡しします。1byteを超える場合
STK00〜の変数を使用して、引数及び戻り値を受け渡しします。
たとえば void hoge(int fuga); という関数の引数に0x1234という2バイトを渡す場合、

  MOVLW 0x34
  MOVWF STK00
; ココ
  MOVLW 0x12
  PAGESEL _hoge
  CALL _hoge

のようなコードになるのですが、ココと書いた以降に割り込みが発生し、
その割り込み処理中に他の関数呼び出しでSTK00を使用した場合、STK00の値が
不定となってしまいます。

不思議な事にコンパイラは自身でSTK00等の変数を割り当てておきながら、割り込み
処理でこれを全く退避しないのです。その為、割り込みで1byteを超える引数、又は
戻り値を持つ関数を呼び出してはいけないようです。

・実はSTKxxは自由につかえる?
 たくさんのCソースの中間アセンブリソースを見ていますが、引数と
 戻り値以外でSTKxxを使っているケースは無いように思います。
 そして引数は全てrxxxxという変数へ代入してしまい、それ以降はSTKxxは
 別の関数を呼び出す時以外は使用されていません。
 12個も確保されているSTK変数は引数や戻り値で予約されている物を除い
 て全く使われないわけです。使われないのが判っているならNOBANKで
 アクセス出来るこれらの変数は内部ワークとしておいしく使えるはずです。
 もちろん、今後のコンパイラの改良で変わるかもしれません。
 が、今のところ、2.9.4 #5573時点では、大丈夫なようです。
 

  • senshu 2009-12-19 (土) 12:13:10
    JS3SUHさん、有益な情報をありがとうございます。

    実は、PIC16Fシリーズにはsdccを利用することは諦めています。意図した動作を
    しない場合が多いので、sdccを使うメリットが無いと判断しました。

    PIC18F用ではややマシですが、それでも不完全な部分があり、コードも大きめです。

    sdccを利用している方に、利用のポイントを紹介していただければ、この印象は
    変わるかもしれません。

    どうぞよろしくお願いします。

ポインタ配列の初期化に関して

JS3SUH? (2009-12-21 (月) 04:22:39)

以下、PIC14portsについてのみ確認しています。

メニュー等に文字列の配列を使いたいとき以下のような初期化をします。
よくある初期化のパターンです。

__code char *menu_strs[MAX_MENUS] =
 {
   "Time",
   "Menu2",
   "Menu3",
   "Menu4",
   "Menu5",
   "Menu6",
   "Menu7",
   "Menu8",
   "Menu9",
   "Exit"
};

コンパイルすると以下のような結果になりました。

ID_state_setup_0        idata
_menu_strs
       db      0x00, 0x00 ; ここに__str_1のポインタがセットされる筈だがNULL
       db      0x00, 0x00 ; ここに__str_2のポインタがセットされる筈だがNULL
       db      0x00, 0x00
       db      0x00, 0x00
       db      0x00, 0x00
       db      0x00, 0x00
       db      0x00, 0x00
       db      0x00, 0x00
       db      0x00, 0x00
       db      0x00, 0x00

ID_state_setup_2        code
__str_1
       retlw 0x54 ; 'T'
       retlw 0x69 ; 'i'
       retlw 0x6d ; 'm'
       retlw 0x65 ; 'e'
       retlw 0x00 ; '.'

ID_state_setup_3        code
__str_2
       retlw 0x4d ; 'M'
       retlw 0x65 ; 'e'
       retlw 0x6e ; 'n'
       retlw 0x75 ; 'u'
       retlw 0x32 ; '2'
       retlw 0x00 ; '.'

残念ながら各ポインタが設定されていません。NULLポインタになってしまっています。
もしかしたら、リンク時に書き換えられるのか???
残念ながらそれも否でした。

なぜなのか?
PICのメモリアーキテクチャを考えたとき、渡された文字列"Time"がRAMか
ROMかでアクセス手段を変えなければなりません。

単純に"Time"とかかれた場合文字列Timeは一体どこに格納されているのかが
判りません。その為コンパイラは配列を初期化する際にポインタを設定せず
NULLを格納するようです。これは正しいようで正しくない動作ですね。
特定できないならエラーではじくべきだと思うのですが…。

ちなみにこの動作は__code char *menu_strsの__codeを外しても同様です。
ポインタに纏わる希望した動作が得られない理由の一つがこの件だと思います。

何処にあるのかが判ればよいということで、ためしに文字列がcode領域にあると
明示してみると…

__code char *menu_strs[MAX_MENUS] =
 {
   (__code *)"Time", // 明示的にcode領域のポインタである事を明記
   (__code *)"Menu2",
   (__code *)"Menu3",
   (__code *)"Menu4",
   (__code *)"Menu5",
   (__code *)"Menu6",
   (__code *)"Menu7",
   (__code *)"Menu8",
   (__code *)"Menu9",
   (__code *)"Exit"
 };

これは、以下のコードを生成します。

ID_state_setup_0        idata
_menu_strs ; ちゃんとポインタが設定されている
       db      low (__str_1+0), high (__str_1+0)
       db      low (__str_2+0), high (__str_2+0)
       db      low (__str_3+0), high (__str_3+0)
       db      low (__str_4+0), high (__str_4+0)
       db      low (__str_5+0), high (__str_5+0)
       db      low (__str_6+0), high (__str_6+0)
       db      low (__str_7+0), high (__str_7+0)
       db      low (__str_8+0), high (__str_8+0)
       db      low (__str_9+0), high (__str_9+0)
       db      low (__str_10+0), high (__str_10+0)

思惑通りちゃんと反映されています。
普段PC用のCでは意識しないこともきっちり意識して書かなくては
ならないところがマイコンや組み込みでのCの難しいところです。

ところで、SDCCのPIC14portsのポインタは大きく3種類あります。
汎用ポインタ、コードポインタ、データポインタの3つです。

汎用ポインタ  3byte  Low,High,Mark
コードポインタ 2byte  Low,High
データポインタ 2byte  Low,High

汎用ポインタはコードでもデータでも格納できる便利ポインタです。
その為、コードかデータかを判定するためにMarkが入ります。
Markは00hがデータ、80hがコードとなっています。
コードポインタは下位はPCL、上位はPCLATHの値です。
データポインタは下位はFSRに入れて使う値そのものです。
上位は0bit目がIRPの値となっています。
実際に扱う時には、

コードポインタの例

 MOVFW highptr;
 MOVWF PCLATH;
 MOVFW lowptr;
 MOVWF PCL;


データポインタの例

 MOVFW lowptr;
 MOVWF FSR;
 BCF STATUS, IRP;
 BTFSC highptr, 0;
 BSF STATUS, IRP;


といった感じになります。

自作関数でポインタを扱う場合、明らかにROM上かRAM上と判っていれば
__code/__dataで種別を明記することでポインタサイズを1byte小さくする事が出来ます。
また、無用なポインタ種別の判定処理を省く事にもなり少し時間とメモリが節約できます。
逆にlcd_puts等、ROM上でもRAM上でもどちらのデータでも扱いたいというときは
明記しない事で汎用ポインタが使われることになります。
自作関数を作るときにちょっとでも役に立てばと思います。


  • senshu 2009-12-21 (月) 18:42:15
    __codeキーワードは知っていましたが、これほど酷使する必要があるとは知りませんでした。
    大変勉強になりますが、他のCコンパイラではもう少し柔軟(普通)に記述できるようです。


  • JS3SUH? 2009-12-21 (月) 20:18:16
    実は、私も思いっきり嵌った口です(^^;。

    LCDに簡単なメニューを出そうと上のように書いたら表示が出ない!!
    あわてて調べた結果が上のとおりでした。
    コンパイラがもう少し賢いといいんですけどねー。

    この辺りはPIC16でもあまり変わらないと思いますのでSDCCで書く上では
    要注意ということでしょうか。

複数行のインラインアセンブリマクロを使いたい

JS3SUH? (2009-12-22 (火) 21:16:51)

1行だけのインラインアセンブリマクロなら以下のように書けます。

#define _di()   __asm BCF _INTCON,GIE __endasm

2行以上のインラインアセンブリマクロを使いたい場合どうかきますか?
最も単純に考え付くのは以下のような記述

#define _saved_di()  __asm		\
	MOVFW	     _INTCON		\
	MOVWF	     _g_isave		\
	BCF	     _INTCON, GIE	\
	__endasm

残念ながらこの記述はダメです。プリプロセスの結果を見ると原因がわかります。

 __asm MOVFW _INTCON MOVWF _g_isave BCF _INTCON, GIE __endasm;

全てが1行に展開されてしまいました。
これではアセンブラがおかしいぞ!とsyntax errorを吐いてしまいます。
こういう時の為、SDCCにはasmブロックをプリプロセスしないというpragmaがあります。

#pragma preproc_asm -
#define _saved_di()  _asm
	MOVFW	     _INTCON
	MOVWF	     _g_isave
	BCF	     _INTCON, GIE
	_endasm
#pragma preproc_asm +

プリプロセスを通してみると、

 _asm
	MOVFW	     _INTCON
	MOVWF	     _g_isave
	BCF	     _INTCON, GIE
	_endasm;


確かに改行まで出ています。これで目的は達成されました。めでたしめでたし…と言い
たいのですが二つ問題点があります。
asmキーワードをよく見てください。_asmと'_'が一つです。_asmはsdccの拡張キーワード
です。その為、--std-c89や--std-c99を指定していた場合コンパイルが通りませ
ん。--std-c89や--std-c99を指定している場合には、__asmと'_'を二つつけること
でインラインアセンブラと認識させることが出来るのですが、今度はpreproc_asmと
いうpragmaが__asmをインラインアセンブラ開始と認識しません。結局両方を両立させ
るためには、--std-sdcc89、--std-sdcc99とsdcc拡張を有効にしなければなりません。
もう一つは、上をみて気づいた方も居ると思うのですが、先の例のように'\'をつけずに
改行しています。preproc_asmは_asm〜_endasm間を一切のプリプロセスをせずに出力し
ます。なので改行もちゃんと改行されています。特に問題が無いように思いますがプリ
プロセスをしないというのが一つ問題です。
たとえば、以下のようなマクロ展開を伴う場合

#define BUSY	(7)

#pragma preproc_asm
#define WaitBusy() _asm
	BANKSEL	   _hoge_stat
	BTFSS	   _hoge_stat, BUSY
	GOTO	   $-1
	__endasm
#pragma preproc_asm +


プリプロセスを通してみると、

 _asm
	BANKSEL	   _hoge_stat
	BTFSS	   _hoge_stat, BUSY
	GOTO	   $-1
	_endasm;


一見期待通りなのですが、BUSYマクロが展開されていません。このままでは
やはりアセンブルできません。プリプロセスをしないということはこういうことです…。
これではちょっと使い方が制限されてしまいます。

そこで、いろいろ考えた結果、少々トリッキーですが以下のように書くと期待通り
出せるようになりました。

#define ASM(...) _Pragma("save") __VA_ARGS__ _Pragma("restore")

#define WaitBusy() \
  __asm;					\
  ASM(BANKSEL   _hoge_stat) 			\ 
  ASM(BTFSS     _hoge_stat,BUSY)		\
  ASM(GOTO      $-1)				\
  __endasm;


以下がプリプロセスの結果です。

  __asm;
# 153 "encoder.c"
#pragma save
# 153 "encoder.c"
  BANKSEL _hoge_stat
# 153 "encoder.c"
#pragma restore
# 153 "encoder.c"
 
# 153 "encoder.c"
#pragma save
# 153 "encoder.c"
  BTFSS _hoge_stat,(7)
# 153 "encoder.c"
#pragma restore
# 153 "encoder.c"

# 153 "encoder.c"
#pragma save
# 153 "encoder.c"
  GOTO $-1
# 153 "encoder.c"
#pragma restore
# 153 "encoder.c"
  __endasm;;


ちょっと判りにくいですが_Pragmaで生成されたsave/restoreのpragmaに挟まれて確かに
期待通りマクロ展開されたコードが生成されています。save/restoreというpragmaは
現在のpragmaの設定を退避/復帰するというもので、間でpragmaを使うわけでもないので
何の意味も有りません。pragmaを生成させることで強制的に改行させつつ、他に影響の
無いpragmaを選んだ結果です。
生成されたアセンブリソースをみてもちゃんと期待通りなのが判ります。

	BANKSEL _hoge_stat
	BTFSS _hoge_stat,(7)
	GOTO $-1
	;

この記述は可変引数マクロ__VA_ARGS__を使用していますので--std-c99又は--std-sdcc99
でのみ可能です。あまり使うことは無いと思いますがこういう書き方もあるよというこ
とで…。

#どうすれば、記事が乱れない状態で投稿できるんでしょうか?(^^;


  • senshu 2009-12-22 (火) 22:05:53
    いつも有益な内容をありがとうございます。

    書き込みを意図したように行うには、pukiwikiのルールに沿って書く必要があります。
    特にお願いしたいのは、ソースコードは、先頭に一文字の空白を挿入することです。
    これだけでも、かなり改善できます。

    また、>,-.*,#,+のような文字は、行の先頭に書くと、特別な意味を持ちます。
    どうしても書きたい場合には、とりあえず全角文字で書いてください。

    今までの書き込みは、全て私が、先のルールに沿って、書き直していました。

    無駄や誤った修正を避けるためにも、ご協力をお願いたします。

  • JS3SUH? 2009-12-23 (水) 01:08:41
    いつもお世話になっております。
    毎回修正いただきお手数ばかりかけてしまい申し訳ないです。
    上記、了解しました。次回から気をつけます。
  • JS3SUH? 2010-01-10 (日) 19:39:40
    こんにちは。
    上記、preproc_asmが--std-c99で使えない不具合ですが、
    まだ動作確認していませんが、1/4付けでBugFixされているようです。

興味深い考察(2題)

senshu (2008-04-14 (月) 18:32:37)

(1)「不定期更新日記」さんからの引用

http://www3.cnet.ne.jp/yokomizu/_exc/dmsgbd2/dmsgbd.asp

【2007年12月30日】
◆SDCCについて

    電子メトロノームのファーム開発では、SDCC(Small Device C Compiler)を
    使ったのだが、SDCCが吐き出すコードはバグっぽくて、おかしな動作をする
    ことが多かった。但し、これにはMakefileの不出来も一部絡んでいた模様。

    以前に公開したXCODE用プロジェクトテンプレートに含まれている、Makefile
    (20行目付近)を以下のように修正し、ライブラリパスよりも先にオブジェクト
    パスを指定することで、若干安定した。

    -----------------------------------------------------------
    $(TARGET).hex: $(OBJS)
    $(LD) -I$(LIBDIR) $(LDFLAGS) -o $(TARGET) $^ $(LIBS)
    -----------------------------------------------------------

    他にも山ほどおかしな動作はあるが、以下のようなオペレーションをしなければ
    安定して使える模様。

     ・掛け算
     ・割り算
     ・LONG値の四則演算
     ・SHORT値のグローバル変数
     ・LONG値のグローバル変数

    汎用レジスタの再利用に絡んでバグっぽいコードを吐き出すので、最適化を無効に
    するなど、SDCCをいじってみたが余り効果無し。まだ原因が特定できていない動作が
    多いので、やんなって回避する使い方になった。

    つまるところ、8ビットMCUでSDCCを使うのは厳しいっちゅう事だな。

おぉ。SDCCのバージョンが不明ですが、やはり苦労されているようです。上の書き込みは、
「難しいことをさせない」ということでしょうか。C言語は、アセンブラでは面倒なことが
簡単に書ける点にメリットがあると思うのですが、、、。 [sad]

(2)「sdcc for PIC HowTo」⇒ http://www.freenet.org.nz/sdcc/

2005/07/16に書かれた文章ですが、現在では改善されている点も多数あることに 注意が必要です。

  • 「18Fのアーキテクチャはひどい(SUCK)、でも16Fはこれよりも悪い」
  • 「18Fを使うこと。価格も僅かしか違わず、生成コードの効率もよい」

18Fの利用を推薦している点は私も同じです。sdccは、pic18F用なら実用レベルなのです。
しかし、16Fのシンプルさにも魅力があるのは事実です。(小規模な応用なら、評価版でも代用可能?)
また、PICマイコン用のライブラリの説明が欠けている点も指摘しています。
やはり、制限事項と基本的使い方をまとめた解説が必要なことを痛感します。


  • 以前サイプレスのUSBチップ8051互換CPUで、SDCCを使おうと検討しましたが、
    そのときもバグが多いので注意しないとならないとどこかで見ました。
    結局サイズ限定のメーカー品コンパイラを使用しました。 -- すん? 2008-04-14 (月) 19:07:03
  • 8051用なら退行テストの結果は良好なのですが、それでも不具合が多いなら
    困りますね。実績を積み、細かな不具合の解決が重要なのだと思います。 -- senshu 2008-04-14 (月) 19:10:14
  • 結局使う人が多くなればコンパイラ開発もやる人が出てくるのでしょうね。
    AVRや上位PICのようにGNU-Cにならないのは、なぜでしょうね。 -- すん? 2008-04-15 (火) 17:07:29
  • 8051のバグは4年くらい前の話です。 -- すん? 2008-04-15 (火) 17:08:56
  • PIC16FのアーキテクチャではGCCの移植は困難です。PIC18FでもGCCは移植は出来ません。
    PIC24FやdsPICクラスの能力があればこそ、GCCが実現できるのだと思います。 -- senshu 2008-04-15 (火) 18:13:22

PIC14用に利用する時の留意点

senshu (2008-04-15 (火) 18:33:38)

arms22さん・不定期更新日記さんの情報やsdccのマニュアルを元に、sdccをPIC14用に
使う場合の注意点をまとめてみます。

PIC14用に使う際の注意点備考(要望)
引数や戻り値にはUnsigned charを使う了解(PIC14は8ビットMCU)
ローカル変数は、static で確保するauto変数は効率が悪いため
SHORT, LONGのグローバル変数は使わない〃 static型で利用する
グローバル関数は非効率〃 static宣言する
配列、ポインタ、switch文は使わないせめて配列は使いたい
乗算、除算は使わない整数(8/16/32bit)の乗除算もNG?
LONGの四則演算は使わないせめて加減算くらいは使いたい
浮動小数点演算は使わない利用厳禁
CONFIGワードは項目を漏れなく指定16進数での指定も検討する


このことを考慮すると、

  • ライブラリはstatic関数で書く
  • main.cでソースをインクルード

ライブラリ関数はモジュール化せずに、ファイルに取り込むことになります。 商用のCCS-Cコンパイラ(PIC14用)も、分割コンパイルはサポートしておらず、
似た実装になっています。

小型のPIC14マイコンの用途では、この制約があっても適用可能な場合も多いと思います。
実際、sdccのマニュアルにも類似の事が記載されています。

現行のsdccをPIC14用に使う場合、かなりの制限を理解して使う必要がありますが、
小規模な応用ではそれほど気にならないのかもしれません。

上記の表に書いたことのいくつかは、今のSDCCでは解決されているものあると思います。
そうした情報をお持ちの方は、ぜひコメントをお願いいたします。


  • static関数を使うのは引数やローカル変数がグローバル変数に割り当てられて、
    再利用されない為です。それらを許容できるなら分割コンパイルしてもOKです。 -- arms22? 2008-04-16 (水) 09:56:05
  • PIC14では、スタックは戻りアドレスしか保存できないので、ローカル変数は通常の
    RAM領域に割り当てるしか方法がありません。こうして割り当てられたエリアは、場合
    によっては再利用も可能ですが、ネストした呼び出しを考えると、単純ではないですね。
    具体的な例を示す必要を感じます。 -- senshu 2008-04-16 (水) 10:49:42

snapshot 2.8.1 #5135

senshu (2008-04-15 (火) 19:23:42)

しばらく改訂の無かったsdccですが、#5134+1の変化は、mcs51用の修正でした。
(取りあえず、静観します)

最近の改定履歴を見ると、nl等の北欧勢が頑張っていますね。ATMELなども同様です。
日本からの貢献が少ないように感じるのは気のせいでもないと思います。
日本では日本固有のチップがシェアを占めているのが影響しているのでしょうか。

2008-04-14 Maarten Brock <sourceforge.brock AT dse.nl>
 * device/include/mcs51/cc2510fx.h: added _XPAGE
 * device/include/mcs51/compiler.h: cosmetic changes

  • #5137が公開されました。mcs51用の改訂が中心です。mcs51の開発者の精力的な改良には脱帽です。 -- senshu 2008-04-18 (金) 22:34:32

USBフレームワークをsdccで実現する

senshu (2008-04-16 (水) 01:12:31)

sdcc用のPIC18F用のUSBフレームワーク移植に関する情報です。

「Porting Microchip MCHPFSUSB v1.3 CDC to use sdcc on Linux」

http://www.efn.org/~rick/work/MCHPFSUSB/

コンパクトにまとまった紹介記事です。
かなり複雑なプログラムですが、この程度の作業で移植できるならsdccもかなり使えると
思うのですが、18F用と16F用のコード生成部分は別のコードとなっています。


  • この記事を試してみましたが、sdccのリビルドが必要です。ソースがpic18f87j50 用に なっていますが、sdcc公開版はこれをサポートしていません。このチップが一般的なら、 sdccに含めたいと思います。 -- senshu 2008-04-17 (木) 07:43:10
  • 上記は、sdccだけでなくgputilsのrebuildも必要でした。 [sad]
    gputilsのSVN版には、pic18f87j50 用のヘッダファイルが含まれているのですが、なぜか
    このMCUはサポート対象外でした。パッチを作成し、gputils, sdccの両方をpic18f87j50に
    対応させました。さらに、USBフレームワークのソースを修正したところ、ようやくコンパ
    イルが完了しました。-- senshu 2008-04-17 (木) 10:16:10

PIC14の制約を考える

senshu (2008-04-20 (日) 20:23:36)

char *str1="str1";                      ..... (1)
char str2[]={'s','t','r','2', 0};       ..... (2)
const char str3[]={'s','t','r','3',0};  ..... (3)
const char *str4[]={"str4"};            ..... (4)

これをsdcc -mpc14 p16f88 でコンパイルすると

;--------------------------------------------------------
; File Created by SDCC : free open source ANSI-C Compiler
; Version 2.8.1 #5134 (Apr 20 2008) (UNIX)
; This file was generated Sun Apr 20 21:09:23 2008
;--------------------------------------------------------
; PIC port for the 14-bit core
;--------------------------------------------------------
:
;--------------------------------------------------------
; global declarations
;--------------------------------------------------------
	global	_str1
	global	_str2
	global	_str4
	global	_str3

;--------------------------------------------------------
; global definitions
;--------------------------------------------------------
;--------------------------------------------------------
; absolute symbol definitions
;--------------------------------------------------------
;--------------------------------------------------------
; compiler-defined variables
;--------------------------------------------------------
;--------------------------------------------------------
; initialized data
;--------------------------------------------------------

ID_a_0	idata
_str1
	db	0x00, 0x00, 0x00


ID_a_1	idata
_str2
	db	0x73
	db	0x74
	db	0x72
	db	0x32
	db	0x00


ID_a_2	idata
_str4
	db	0x00, 0x00, 0x00


ID_a_3	code
_str3
	retlw 0x73
	retlw 0x74
	retlw 0x72
	retlw 0x33
	retlw 0x00


ID_a_4	code
__str_0
	retlw 0x73 ; 's'
	retlw 0x74 ; 't'
	retlw 0x72 ; 'r'
	retlw 0x31 ; '1'
	retlw 0x00 ; '.'

ID_a_5	code
__str_1
	retlw 0x73 ; 's'
	retlw 0x74 ; 't'
	retlw 0x72 ; 'r'
	retlw 0x34 ; '4'
	retlw 0x00 ; '.'
;--------------------------------------------------------
; overlayable items in internal ram 
;--------------------------------------------------------
;	udata_ovr
;--------------------------------------------------------
; code
;--------------------------------------------------------
code_a	code

;	code size estimation:
;	    0+    0 =     0 instructions (    0 byte)

	end

constの有無で異なる扱いになります。これは組込み用では十分な配慮が必要です。
通常のCプログラミングではあまり意識することがないのですが、(2)のように書くと、
データと初期化用のコードの両方を占有する点に注意してください。
PICマイコンはRAM領域が少ないので、こうした文字列が数十文字あるだけでRAMが溢れる
ことになります。こうした理由から、文字列初期化は「意図的に」できにくいよう
に制限しているのも知れません。
一方、(4)の書き方では、RAM上には文字列のアドレスデータが確保されるだけですから、
より多くの文字列を格納できます。

UBW上でも動作可能?なmKernelの紹介

senshu (2008-04-25 (金) 07:54:00)

UBWのような環境が入手可能になり、ハード面の問題が解決すると、ソフト面の充実
が課題となります。本格的なものは無理ですが、簡易版のOSが欲しいと思っていました。
PIC18F2550/4550では、30kB程度のメモリが使えますから、簡単なOSなら十分に乗る性能
を有しています。

そんなことを考えていたら、sourceforgeにて、「sdccを使って実現したPICマイコン用の
小型のマルチタスク実行環境」に出会いました。
2007年12月16日が初版リリースが始まり、2008-4-12に現行の0.5.0が公開されました。
おおよそ、月に一度のペースで更新されているようです。

http://sourceforge.net/forum/forum.php?forum_id=810407

以下は、紹介文の引用です。

Posted By: merdmann
Date: 2008-04-12 01:31
Summary: Mkernel 0.5.0 available

Mkernel, a small kernel for the pic18fxxxx processor family implemented
using SDCC, is well on it's way from alfa to beta release.  
 
The current release 0.5.0 supports:  
- Preemptive and cooperative scheduling  
- Task Priorities  
- Simple memory management  
- Process Comminication based on simple messages  
- Process/Interrupt handler communication based on so called resource locks  
- High Priority Interrupt driven Timer  
- Simple device model  
- Drivers for most of the build in devices of the PIC18f4550 (ADC,PWM,CVREF
 I2C, COMP.
- Drivers for the IOR5E board

内部構成を見てみるとfsusbを含んでおり、洋の東西を問わず、類似のものに取り組ん
でいる人の存在を実感できます。

mkernelは、SBC44UCというボードをターゲットにしています。どんなボードかを
調べてみました。UKでは、結構面白いものが手に入るようです(うらやましい)。
http://www.skpang.co.uk/catalog/product_info.php?cPath=33_34&products_id=219

mKernelは、0.5.0というβの段階ですが、ソースコードは読み易く、発展が期待できます。
多くの方々の改良が加われば、より実用的なものになると考えます。なお、18f4550と2550は
内蔵するI/O(ピン数)の違いが主なものであり、ソフト的にはほぼ同じものです。

現在は、ソースコードからHEXファイルの生成までを確認しました。今後は、UBW上での動作
と改良を計画しています。

snapshot#5148の改良点

senshu (2008-05-02 (金) 16:02:41)

連日、sdccは小刻みな改訂が行われていますが、多くはPICマイコン以外のものでした。
しかし、#5148でPIC16用の大きな改良が追加されました。

PIC16(18Fシリーズ)には多くのタイプがありますが、これらの対応させるには
コンパイラのソースを修正して再コンパイルする必要がありました。
この作業は誰にでもできるものではなく、未対応のPICマイコンを使いたい場合には、
非常に多くの手間と時間が必要でした。

#5148の改訂により、テキストファイルにPICマイコンの仕様を追加すれば、その
マイコンに対応ができます。ライブラリなどは自前で作成する必要がありますが、
sdccをリビルドしなくともよいのは大きな進化です。

なお、UBWもsdccでコンパイル可能ですから、非常に手軽にPIC16(18Fシリーズ)用の
柔軟な開発環境を用意できることになります。

特に、Mac OSやLinuxを使っている方には朗報といえます。今後の発展に注目したいと思います。

#5151でも、PIC16用の不具合修正が行われています。しかしこれらの変更に
よって、Windows用のsdcc#5184以降では、-mpic16オプションでのコンパイルで動作しない
バグが発生しています。pic16用の動作テストは、Windows版では行われていないのかも
しれません。

修正すべき箇所はわかりましたので、報告したいと思います。

現時点では、今しばらく静観し、熟成を待つのが賢明かもしれません。

予約語の不思議

senshu (2008-05-13 (火) 11:42:36)

sdccで以下のプログラムを書いたのですが、意外な事にエラーになります。
1行目がエラーとの事ですが、エラーが見当たりません。

    1	static char data[10];
    2	
    3	void main()
    4	{
    5		data[0] = 0;
    6		data[1] = 1;
    7		data[2] = 2;
    8		data[3] = 3;
    9	
   10	}

>sdcc -mpic16 -p18f2550 -S s.c
s.c:1: syntax error: token -> '[' ; column 17

不思議に思って、sdccのマニュアルを読んでみました。見る場所は reserved word
です。P96を見ると!、ありました。通常のコンパイラよりも多くの予約語があるようです。

Certain words that are valid identifiers in the standard may be reserved 
words in SDCC unless the --stdc89 or --std-c99 command line options are used.

These may include (depending on the selected processor):'at', 'banked', 'bit',
'code', 'critical', 'data', 'eeprom', 'far', 'flash', 'idata', 'interrupt', 
'near', 'nonbanked','pdata', 'reentrant', 'sbit', 'sfr', 'shadowregs', 
'sram', 'using', 'wparam', 'xdata', '_overlay', '_asm', '_endasm',and '_naked'. 

Compliant equivalents of these keywords are always available in a form 
that begin with two underscores, f.e. '__data' instead of 'data'.

Integer promotion of variable arguments is not performed if the argument is
explicitly taypecasted unless the --std-c89 or --std-c99 command line options
are used.
  • at, banked, bit,
  • code, critical
  • data, eeprom, far, flash, idata, interrupt,
  • near, nonbanked,pdata, reentrant,
  • sbit, sfr, shadowregs, sram, using,
  • wparam, xdata, _overlay,
  • _asm, _endasm, _naked.

上記の予約語のいくつかは、よく使う変数です。予約語は変数宣言には使えません。
dataやeeprom, bitなどが予約語なのは、誰のセンスなのでしょうか。 [sad]

しかし、--std-c89や--std-c99をオプション指定すれば、この予約語は無効に
できます。__dataのように__を予約語に追加すれば、その機能は利用できる
ようですから、妙なエラーが起きずにプログラムを書くことができます。
可能なら、これをデフォルトにすべきだと思います>sdccの開発者様

sdcc -mpic16 -p18f2550 --std-c89 -S s.c

このように指定すれば、↑のコードは正常にコンパイル可能です。
--std-c89オプションは必須といえそうです。

[SDCC] 不具合と解決法

コンパイラの不具合(gpasmに起因するBUGと解決策)

senshu (2008-04-03 (木) 17:33:32)

sdccを使って、過去に書いてきたコードをsdcc向けにリライトしてみましたが、不具合を
発見しました。
sdccでは、以下のようにPICマイコン固有の設定情報を記述します。ANSI-Cから外れた
書き方ですが、PICマイコンの特性を考慮すれば十分に納得できる記述法です。

しかしP16F88のように、複数ワードのCONFIG情報を持つPICマイコンでは、最初の項目のみが
出力されるようです。通常は、重要項目は最初に集中している為にそれほど致命的ではありま
せんが、ブラウンリセットの値などを指定したい場合(16F887)には、手動で設定を行う必要が
あります。

typedef unsigned int word;
word at _CONFIG1 CONFIG1 = _WDT_OFF & _PWRTE_ON & _XT_OSC & \
	_MCLR_ON & _BODEN_ON & _LVP_OFF & _CPD_OFF & _WRT_PROTECT_OFF &  \
	_DEBUG_OFF & _CCP1_RB0 & _CP_OFF;
word at _CONFIG2 CONFIG2 = _FCMEN_OFF & _IESO_ON;

なお、PIC18Fなどの最初から複数バイトが標準のものについては、この不具合はありません。


sdcc/gputilsを切り分ける為、以下のようなコードを書いてみましたが、

	LIST   P=PIC16F88
 	include <p16f88.inc>
 	__CONFIG    _CONFIG1, 0x3210
	__CONFIG    _CONFIG2, 0x0123
 	end


gputilsは、意図したHEXファイルを生成してくれました。しかし、これは絶対番地方式の
アセンブルの場合です。

:020000040000FA
:02400E0010326E
:0240100023018A
:00000001FF

リロケータブルオブジェクトのCOFF側の生成に問題がありました。まだ原因はハッキリしませんが、
現状では、sdccをPIC16F用の開発に使う場合、複数のCONFIGのうち、最初のCONFIG1のみ正しく
設定されます。

原因判明!、問題はgpasm.exeにありました。
これでは安心して使えないのでソースを調べたところ、gpasmのソース(coff.c)に以下の
記述を見つけました。「不具合があるのはわかっているけど、まだ直していない」という
感じです。14bit coreの場合、2バイトだけ書き出すのが仕様となっています。(ふう、
ここまで4時間かかりました。。。)

    if(_16bit_core) {
      config_section->size = 0;    
      start = config_section->address >> 1;
      stop = CONFIG7H >> 1;
      for (i = start; i <= stop; i++) {
        word = i_memory_get(state.c_memory, i);
        if (word & MEM_USED_MASK) {
          i_memory_put(config_section->data, i, word);
        } else {
          /* fill undefined configuration registers with 0xff */
          i_memory_put(config_section->data, i, 0xffff | MEM_USED_MASK);
        }
        config_section->size += 2;    
      }
      
    } else {
      /* FIXME: Some 16xx devices have two config words (16f88). */
      word = i_memory_get(state.c_memory, state.device.config_address);
      assert(word & MEM_USED_MASK);
      i_memory_put(config_section->data, state.device.config_address, word);
      config_section->size = 2;    
    }  

ここまでわかれば直すだけですが、根性がありません。どなたかチャレンジしてみませんか?
弱音を吐くのはやめました。というのは冗談で、gputilsのBUGレポートを調べたところ、
なんと!2008-4-1に修正パッチが見つかりました。
2007-11-13の報告に対する修正が行われたのが日本時間で比較すると1日前です。あまりの
タイミング良さに驚かされます。
早速パッチを適用して動作確認を行いましたが、結果は良好です。 [smile]
もう少しテストを行ってから、実行イメージの公開(Windows/Linux)を考えます。

diff -ur gputils/gpasm/coff.c gputils-0.13.5/gpasm/coff.c
--- gputils/gpasm/coff.c	2008-03-27 22:19:49.000000000 +0900
+++ gputils-0.13.5/gpasm/coff.c	2008-04-03 22:12:05.354755851 +0900
@@ -161,11 +161,26 @@
       }
       
     } else {
+#if 0
       /* FIXME: Some 16xx devices have two config words (16f88). */
       word = i_memory_get(state.c_memory, state.device.config_address);
       assert(word & MEM_USED_MASK);
       i_memory_put(config_section->data, state.device.config_address, word);
       config_section->size = 2;    
+#else
+      config_section->size = 0;
+      start = state.device.config_address;
+      stop = start+1;
+      for (i = start; i <= stop; i++) {
+        word = i_memory_get(state.c_memory, i);
+        if (word & MEM_USED_MASK) {
+          i_memory_put(config_section->data, i, word);
+        } else {
+	  break;
+	}
+        config_section->size += 2;
+      }
+#endif
     }  
 
     _update_section_symbol(config_section);