2011年06月29日

FreeRTOS: syscalls.cと_sbrk()_rの話

FreeRTOS: syscalls.cと_sbrk()_rの話
*背景的な話
*CoOSで同じ問題が発生
*結論 (オイ
* 「syscalls.c 改」
  Newlibのprintf()関数
* FreeRTOSの「heap_3.c」の謎
*そもそもな話
*Redlibのprintf()

*背景的な話
Newlibに含まれる「標準のprintf()関数(stdio.hに含まれる)」は
「FreeRTOS」のメモリ・モデル(MemMang)「heap_1.c」では動作しないという状況に遭遇した。(注1)

orz

ググると、
Newlibのprintf()の場合、「heap_3.c」なら動く、と、
ねむいさんのブログにあったので、やってみるとあっさり動いた。(^^)/  感謝。
これに気づくまでに時間がかかって  orz orz だった。

この話は、深く考えずに、「これでおしまい」にした。
めでたし、めでたし。終?了?っ。
(ちなみに、
    普通はChaNさんの超軽量タイプの整数型「xprintf()」を使っていた。)

時は流れて。  (オイ
*CoOSで同じ問題が発生
最近、CooCox CoOSで同様に簡単なデモ動作をさせてみたところ、
Newlibのprintf()がさっぱり動かないのだった。

CoOSにはFreeRTOSのような複数のメモリ・モデルはなく、調べていくと
FreeRTOSの「heap_1.c」とよく似ていることが分かった。

コマイ話は省略して、
*結論 (オイ
原因は、FreeRTOSに付属している「syscalls.c」の_sbrk_r()関数が
RTOSのメモリ・モデルにうまく適合しないことだった。
これ、自分がここで使ったのと同じ「YAGARTO」の「syscalls.c」そのままだ。

_sbrk_r()関数を見てみると以下の様になっている。
/* Register name faking - works in collusion with the linker.  */
register char * stack_ptr asm ("sp");

caddr_t _sbrk_r (struct _reent *r, int incr)
{
  extern char   end asm ("end"); /* Defined by the linker.  */
  static char * heap_end;
  char *        prev_heap_end;

  if (heap_end == NULL)
    heap_end = &end;
  
  prev_heap_end = heap_end;
  
  if (heap_end + incr > stack_ptr)
  {
      /* Some of the libstdc++-v3 tests rely upon detecting
        out of memory errors, so do not abort here.  */
#if 0
      extern void abort (void);
      _write (1, "_sbrk: Heap and stack collision\n", 32);
      abort ();
#else
      errno = ENOMEM;
      return (caddr_t) -1;
#endif
  }
  heap_end += incr;
  return (caddr_t) prev_heap_end;
} 
上で、問題なのは15行目の「if文」。 この「_sbrk_r()関数」は、もともと下左図の様なシングル・タスクのメモリ・モデルを 想定していて、(incrは省略) 「stack_ptr」と「heap_end」の間に空きメモリ(Space)があることが大前提。(注0) ところが、FreeRTOSの「heap_1.c」モデルやCoOSの場合、 上右図のように、 「stack_ptr」の位置が逆転してしまうので、(空き量がマイナス) 上のプログラムの「if文は常に真となり、_sbrk_r()関数は常に失敗する」のだった。 _sbrk_r()関数は「malloc()」から呼ばれるので、printf()だけでなく、 malloc()系の関数は全滅。(マルチ・タスク実行時)(注7) そもそも、この「syscalls.c」はマルチ・タスク(マルチ・スタック?)用ではない のだった。 じゃあ、なぜこういうものがFreeRTOSに添付されているのか。(LPC2378用) 調べてみると、 http://sourceforge.net/projects/freertos/forums/forum/382005/topic/3234440 上のやりとりでコンパイルを通すために「単に追加されただけ」のようだ。 当時、自分が書いたブログのほんの少し前の話題。 これ以降のFreeRTOSの一部のDemoには、この「syscalls.c」が標準で添付されるようになった。 * 「syscalls.c 改」 上のメモリ・モデルで分かるように、正攻法は「kernel stack」との位置を 比較するのが良いが、「kernel stack」の消費量は非常に少なく無視できると考え、(注2) 「kernel stack」の最低位番地 ≒ スタックの開始番地(最高位番地) とする。 以下のように、「ヒープ端(heap_end)とスタック端(stack_ptr)の比較」をして 「あり得ない位置関係」なら「比較用のスタック端」を 「スタックの開始番地(最高位番地)(_vStackTop)」に変更する。(参考文献参照)
/* Register name faking - works in collusion with the linker.  */
register char * stack_ptr asm ("sp");

caddr_t _sbrk_r (struct _reent *r, int incr)
{
    extern char   end __asm ("end");    /* Defined by the linker.  */
    static char * heap_end;
    char        * prev_heap_end;
    extern char   _vStackTop;           /* Defined by the linker.  */
    char        * pStack;
    
    if (heap_end == NULL){
        heap_end = &end;
    }
    prev_heap_end = heap_end;

    pStack = ( stack_ptr > heap_end ) ? stack_ptr : &_vStackTop; 
    
    if (heap_end + incr > pStack)
    {
        /* Some of the libstdc++-v3 tests rely upon detecting
        out of memory errors, so do not abort here.  */
#if 0
        extern void abort (void);
        _write_r( r, 1, "_sbrk: Heap and stack collision\n", 32);
        while(1); /* Terminate here */
        /* abort (); */
#else
        errno = ENOMEM;
        return (caddr_t) -1;
#endif
    }
    heap_end += incr;
    return (caddr_t) prev_heap_end;
} 
これで、FreeRTOSの「heap_1.c」でもNewlibのprintf()が動作するようになった。(^^)/ 同様に「CooCox CoOS」も大丈夫だ。 例えば、 「_vStackTop」はリンカスクリプト内で以下のように定義されている、スタックの開始アドレス。 (定義がないならMEMORYセクションの前か後に以下の定義を追加します。)(注9) LPC1768の場合、 _vStackTop = 0x10000000 + 0x8000 - 32; 「-32」の部分はなくても良いかも。(ホントは-8か-16らしい) LPC2388なら、 _vStackTop = 0x40000000 + (64k-32); という感じ。 Newlibのprintf()関数 ただし、 Newlibのprintf()関数は、ローカルスタックを約4kバイト消費する。 従って、printf()を呼び出す「taskのスタック」は、通常処理用に加えて 最低でも約4kバイト確保する必要があるのだ。(注8) さらに、Newlibのprintf()は「heap領域」にも約3k〜4kバイトのバッファを取るので、 「カーネルのスタック端とheap端」の間に4kバイトを上回る領域が必要。 いずれもprintf()の表示内容や「コンパイラの種類やバージョン」(Newlib)に依存しそうなので、 調整は必要と思われる。 従って「SRAMが8Kバイトのマイコン」でNewlibのフル実装のprintf()を使うのは難ありと思われる。 Newlibのprintf()が「printf()を呼び出すtaskのスタック」を食いつぶして 動作しない現象は、フォーラムでもFAQになっているようだ。(注4) FreeRTOSのフォーラム投稿: 同じところに注目している人の投稿。 http://sourceforge.net/projects/freertos/forums/forum/382005/topic/3447091 以下はARM7について詳しく展開されている。 http://sourceforge.net/projects/freertos/forums/forum/382005/topic/1784755 ARM7の場合、 (1)FreeRTOSカーネルは、スーパ・バイザモードで動作する。 (2)IRQ 割込みは、周辺I/O用の割込みとして使う。 (3)ユーザ・タスクは、システムモードで動作する。 上の3つは、それぞれの「動作モード用」のスタックを使うので、カーネル用のスタックは ほとんど必要ない。(ARM7の場合) * FreeRTOSの「heap_3.c」の謎 上のように、原理的なことが分かったので、新たな疑問が沸いてきた。 rtos-heap_3-model.gif 上図の様に「heap_3.c」モデルは、Newlibの「malloc()関数」をそのまま使って、 「heap領域」に「TCB」や「タスク毎のスタック領域」を 確保する。(領域確保は初期化時のシングル・タスク・モード中に行う) マルチ・タスク移行後に、 「stack_ptr」と「heap_end」の位置関係が逆転してしまうのは、 「heap_1.c」モデルと同じなのだ。(注6) 従って、 printf()からmalloc()経由で呼ばれる「_sbrk_r()関数」で破綻するはずなのだ。 が、破綻しないのだった。(^^; デバッガで追っかけると、なんと printf()実行中に「_sbrk_r()関数」が呼ばれていないのだった !! ぐぐってみると、詳細は不明なものの malloc()は、printf()から「呼ばれる場合もあるし、呼ばれない場合もある」らしい。 状況に依存するようだ。(注5) 従って、FreeRTOSに於いて オリジナルの「syscalls.c」を「heap_3.c」で使った場合、 ある条件のもとで正常に動作し続けるが、 マルチ・タスク処理中に「_sbrk_r()関数」が呼び出されると 処理が破綻する。(メモリの確保に失敗し、エラーを返す) と、予想してみました。なので、 「heap_3.c」の時でも「syscalls.c改」の対応をしておいた方が良い。 という、結論に至ったのだった。 *そもそもな話 そもそも、SRAMやFLASHが非常に少ないシステムを想定している「heap_1.c」モデルで SRAMやFLASHを大量に消費する「Newlibのprintf()」を使おうとするのは、 きっと、アレなのかもしれない。 orz (爆 ちなみに、CoOSは基本的にNewlibではなく、軽量タイプのprintf()を採用しているのだった。 *Redlibのprintf()/_sbrk_r() LPCXpresso付属のRedlibの場合、ソースがないので_sbrk_r()の実装は不明。 以前書いたように、Redlibに含まれるprintf()は、Newlibのそれに対して FLASHサイズが10Kバイト程度小さい。 今回、FreeRTOSで使ってみたところ、ローカルスタックの消費量も少ないことが分かった。 Newlibの場合、printf()を実行する「taskのスタック量」は、「+4Kバイト」程度必要だが、 Redlibのprintf()は余分のスタックは必要なかった。 この差は大きいと思う。(必要ヒープ量は不明) Redlibの場合、メモリ・モデルは関係ない: 「heap_1.c」、「heap_2.c」、「heap_3.c」の全てのメモリモデルで 今のところ、printf()が普通に動作している。 やるなぁ Redlib。 (どういう実装かは不明だけど (オイ Switching the selected C library http://support.code-red-tech.com/CodeRedWiki/SwitchingCLibrary おしまいっ (注0): スタックとヒープの位置関係やサイズの指定方法など、メモリモデルの定義は 複数ある。ここでは、スタックのサイズは固定せずヒープと衝突するまで 自由に使えるモデルを基本とした。(シングル・タスクモデルの場合) (注1): コンパイラは、「CodeSourdery g++ Lite for ARM」等。 「Newlibの標準printf()関数」をそのまま使う場合であって、 「超軽量タイプの整数型printf()」(xprintf,rprintf,printf-stdarg.c等)を 使う場合は関係ない。 malloc()関数から呼び出される「_sbrk_r()」系の話。 「FreeRTOSの一部のDemo」では整数型の「printf-stdarg.c」を使用していて _sbrk_r()と直接関係ないものもある。 (注2): 使用マイコンにより、スタックの構成が違う。例えば、 以下のリンクのマイコンでは「kernel stack」の消費量は64バイトとある。 http://sourceforge.net/projects/freertos/forums/forum/382005/topic/1784755 (注4): LPCXpressoに付属している「Redlibのprintf()」は大量にスタックを消費することは ないようだ。 (注5):Newlibのソースは読みづらいので、未読。 orz 「heap_1.c」と「heap_3.c」の挙動の違いを観察したところ、 「heap_1.c」: _sbrk_r()が初めて呼ばれるのは、「マルチ・タスクへ移行後」の printf()関数の中。 「heap_3.c」: _sbrk_r()が初めて呼ばれるのは、初期化時の「シングル・タスク状態」で malloc()を使う時。 予想としては、最初の何度かのmalloc()呼び出しで、ある程度のメモリを確保したら、 次にメモリ不足が起きるまで_sbrk_r()は呼ばれないような気がする。←ただの予想。 (注6): 結局、「heap_1.c」、「heap_2.c」、「heap_3.c」の全てで 「stack_ptr」と「heap_end」の位置関係は逆転している。 (注7):main()関数でスケジューラが実行される前なら、「シングル・タスク状態」なので。 malloc()/_sbrk_r()を呼び出しても問題ない。 (注8):ARMの場合、タスク生成関数に指定する「スタック量の引数」は、4バイト単位。 引数に200を指定すれば、確保されるスタック量は「800バイト」になる。 (注9):実装に依存する。 参考リンク: FreeRTOSフォーラム http://sourceforge.net/projects/freertos/forums/forum/382005 参考文献 LPC2148 demo code http://jcwren.com/arm/ リターゲット方法: Newlib and Redlib Retargeting printf/scanf to use a UART http://support.code-red-tech.com/CodeRedWiki/UartPrintf?highlight=%28retarget%29 To enable the non-floating-point printf from Redlib, define the symbol "CR_INTEGER_PRINTF". FreeRTOS FAQ - Memory Usage and Boot Times http://www.freertos.org/index.html?http://www.freertos.org/FAQMem.html
posted by Copyright (C) avrin All Rights Reserved. at 06:46| Comment(0) | FreeRTOS | このブログの読者になる | 更新情報をチェックする

2011年06月25日

LPC2388: パワーダウンでクールダウン

LPC2388: パワーダウンでクールダウン
*はじまり
*LPC2388の低消費電力モード
    アイドルモード:
    パワーダウンモード:
    ディープ・パワーダウンモード:
*使わない機器はOFFしよう!モード (ナンダソリャ
*自動パワーダウン
*電源スイッチでパワーオン・オフ
*リモコンでパワーオン・オフ


*はじまり
IF誌付録のLPC2388基板に於いて、
lpc2388-3d.png
FreeRTOSで「LEDチカチカとUART表示」しただけで、
CPUが「暖かい」感じになっているのだった。

「熱い」という感じでは全然ないけれど、
「この程度の処理で、この発熱は、ちょっと...」
と、思い始めればきりがないのだった。

そもそも、72MHzで動かすのヤメれ (爆
つか、
ピッ、PLLでクロックアップしてこそ、「漢(オトコ)」だって話が、(ナイナイ

そこで、
「もう少し発熱を抑える方法があったはずだ」、
て、ゆーのが、はじまりなのだった。

*LPC2388の低消費電力モード
LPC2388には「PCONレジスタ」というのがあって、これを設定するだけで、
簡単に低消費電力モードにできるのだった。

詳細は、LPC2388の日本語マニュアル
は、ないけど、LPC2388にそっくりな、
「LPC2468の日本語マニュアル」
http://ics.nxp.com/support/documents/microcontrollers/pdf/user.manual.lpc24xx.jp.pdf
を参考にするのだ。(79ページから)

アイドルモード:  ( PM2, PM1, PM0 ) = ( 0, 0, 1)
        停止項目 : CPUコア・クロック等。
        再起動要因: 任意の割込み。
    これは、「ちょっとユルメな」低消費電力モードなのだった。
    FreeRTOSの「#define configUSE_IDLE_HOOK」オプションを有効(1)にして、
    vApplicationIdleHook()関数を以下のように定義するだけだ。
void vApplicationIdleHook( void )
{
	PCON |= 0x01; /* Go to idle mode */
}
    これで、実行するタスクが無い時に「アイドルモード」に入り、     カーネルのディスパッチャ割込み等で自動的に再開する。(注1)     ちなみに、     自分が試しているデモの場合、常時動作するのは、LEDブリンクとUART送信だけなので、     「CPUは、ほとんど休止中」なのだった。     効果: バツ (オイ        CPUの表面を触った限り、何の変化も感じられないっ!        触って判断するのヤメれェ 〜〜〜っ! (爆(爆        そもそも、そんな微小な変化を求めている訳ではないのだった。 つぎ、 スリープモード。 飛ばします (オイ じゃァ、つぎ、 パワーダウンモード:  ( PM2, PM1, PM0 ) = ( 0, 1, 0)         停止項目 : CPUの停止、PLL系の停止、「SRAMとRTC以外の大多数を停止」。         再起動要因: EINT0,RTC等の決められた割込み。     これはアイドルモードと違って、電源をガシガシ切りまくった上に、CPUの実行を停止して逃げ (ワキャナイ     効果: ◎         CPUの表面を触った限り、クールなマイコンになった。        冷え冷えって感じ。     このパワーダウンモードが、通常の「電源オフ状態」と思われる。     なので、vApplicationIdleHook()関数から呼ぶのではなく、     それなりの「終了処理」をした後に、このモードに入る必要がある。     と、いうのも、     「PCON |= 0x02;」を実行した途端に、ペリフェラルのクロックが停止するので、     例えば非同期系である「UART通信中」だった場合、送受信エラーが発生する。     このモードは復帰する前に安定化待ち時間が必要だし、復帰後もPLLの     再設定が必要になるのだ。 ディープ・パワーダウンモード:  ( PM2, PM1, PM0 ) = ( 1, 1, 0)    調査中。こっちがほんとに、ホントの電源オフ状態と思われる。 *使わない機器はOFFしよう!モード (ナンダソリャ 題名見てみんなバカにしてるけど、 (クンクン  (ガグナ オイ 今回のチープなデモに限り、効果絶大なのだった。 (爆 これは「PCONレジスタ」じゃなくて「PCONPレジスタ」を使うのだった。 初期化時、PLLの設定が終わった後ぐらいに、
PCONP = 0x02 | 0x08;
の様に、使用するペリフェラルの電源だけONにすればいいのだ。 例えば、上の場合、「タイマ0(0x02)とUART0(0x08)」だけをONする。 これ以外の設定は必要ない。 CPUを触ってみると、 「ほぉ〜〜〜んのり、暖かい」感じで、十分満足できる低い温度だった。 これに、アイドルモードを組み合わせれば良いかも。 *自動パワーダウン 例えば、1分間入力が無ければパワーダウンする風の機能。 パワーダウンモードが分かったので出来る。 *電源スイッチでパワーオン・オフ IF誌付録のLPC2388基板の「JP2」が丁度「EINT0」につながっているので、 パワーダウンモードからの再起動要因にそのまま使用できるのだった。 ここにプッシュボタンを付ければ「電源スイッチ」に早変わり。 *リモコンでパワーオン・オフ 同様に、赤外受信モジュールの出力を「EINT0」か「GPIO」に接続して、 リモコンコードをデコードすれば出来るはず。 と、いうわけで、 「自動パワーダウン機能」と「電源スイッチでパワーオン・オフ機能」が付いた、 「LEDチカチカ+UARTデモ」を作成するのだった。 (注1):      CPUクロック停止により、JTAGも停止してしまってデバッグに支障がでるので、      デバッグ中は未使用。RCLK付きのFT2232Hタイプなら大丈夫かもしれない。
posted by Copyright (C) avrin All Rights Reserved. at 23:25| Comment(0) | FreeRTOS | このブログの読者になる | 更新情報をチェックする

2011年06月13日

FreeRTOS on Maple. / Like duino on XPresso

irukaさんの掲示板にxshigeさんが
Maple関係のファイルを投稿されていた。


FreeRTOS対応とあったので、見てみると、
Maple (STM32) library for FreeRTOS
http://akb77.com/g/mcu/maple-stm32-library-for-freertos/
というのがベースらしい。

これで、FreeRTOSにMapleとlibmapleのパワーが得られる。

これは良い。良すぎる。

その前にSTM32ボードを買わないと。(^^;
つか、STBeeも安くて良いけど、「日本ローカル」なところが微妙。
一方、「世界標準ボード」である、 (爆
Maple純正ボードは結構、高価 。orz

次。
*NyLPC
http://code.google.com/p/nylpc-freertos/
http://code.google.com/p/nylpc-freertos/source/checkout
これは、LPCXpresso(世界標準ボード(爆)でFreeRTOSを使ってduino風の記述ができるみたいだ。
これも、おもしろそう。


以前、「libpine構想」を妄想していたが、必要ない感じだ。(オイ


というわけで、最近のキーワードは

「世界標準ボード」だったりする。   (ナンダソリャ
posted by Copyright (C) avrin All Rights Reserved. at 00:43| Comment(2) | FreeRTOS | このブログの読者になる | 更新情報をチェックする

2010年05月01日

FreeRTOS: SH7262 移植にトライ(1)FRK-SH2A

FreeRTOS: SH7262 移植にトライ(1)FRK-SH2A

Hew用のSH7216/FreeRTOS正式デモをもとにしてSH7262/FRK-SH2Aに
移植を試みた。

当然Hewに不慣れなのでコンパイルが通るFreeRTOS/SH7262プロジェクトを
作るのに結構大変だった。(^^;

SH7216の「プロジェクトをリネームしてCPUを変更」という
ことが全然うまくできない。
そもそもCPU変更はうまくできないようだった。

orz

しかも、HewのバージョンのせいかI/Oヘッダファイルに互換がなくて
orz


Hewに慣れないので以下の手順をとった。
ひとまず「雑誌付録のままのHewモニタ上」で動かすこととする。

1,CPUをSH7262で新規プロジェクトを作る。
2,雑誌付録のLEDチカチカサンプルの諸設定を手作業でコピーし、
  LEDチカチカ(割込み動作)することを確認。
3,このプロジェクトにSH7216/FreeRTOSプロジェクトと同じ構成に
  なるようにソースコードを手作業で登録。
4,コンパイルが通るようにSH7216/SH7262のデータシートや
  コンパイラマニュアルを見ながら修正。
5,メモリマップを適切と思われるように変更。

付録のLEDチカチカプロジェクトは
0x1C001000 プログラム領域開始点
0x1C005000 変数領域開始点

と連続したプログラム領域が16kバイト分しか取れないのでマッピングは
見直しが必要。

FreeRTOSのデモ部分はいつものように全面的に削除(オイ
してまずはLEDチカチカ・タスクのみを実装。

設定したセクション
0x1C000000 DVECTTBL,DINTTBL
0x1C000500 PResetPRG,PIntPRG
0x1C001000 P,C,C$BSEC,C$DSEC,D
0x1C020000 B,R
0x1C0F0600 S
プログラム領域(P)に124kバイト確保。ちょうどHewコンパイラの制限程度。 スタック(S)は離れすぎているのでそのうち修正する。 *Hewモニタと新規プロジェクト Hewで新規プロジェクトを作ると「resetprg.c」は #define SR_Init 0x000000F0 で生成される。 Hewモニタを使う場合は #define SR_Init 0x000000E0 に変更しないとデバッグで「STOP」ができないらしい。 *Hewモニタとレジスタバンク問題 ということで、デバッガでステップ実行できるとやっぱり良いね。 ここまでの経緯としてはFreeRTOS動いてないです。 orz まずは根本的なところで 1,Hewモニタとの競合問題 2,レジスタバンク問題 に遭遇中。(←たぶん) 1,は保留中というか、どう影響するか今のところ不明。   「Hewモニタの利用法」に制限事項があるにはある。 2,はFreeRTOSがレジスタバンクを使わない前提で作られているようなので   対処可能か不可能か調査中。 Hewモニタはレジスタバンクを使うのが前提 FreeRTOSはレジスタバンクを使わないのが前提 と、そもそも orz (オイ デバッガで追っかけると 1,最初のタスクを実行するために「TRAPA #32」命令(ユーザ例外ベクタ)を   発行する。   (「TRAPA」命令はレジスタバンクへの待避動作はしない) 2,トラップ処理(TRAPA #32)中で最初のTCB(LEDチカチカ)を内部レジスタ群に   コピー。 3,その結果スタックには「LEDチカチカ」のアドレスが積まれているので   この状態でRTE命令(例外処理から復帰)を実行すると   「LEDチカチカ」タスクに切り替わる。 はずが、全然違う場所に飛んでいる。(^^; 「3,」でスタックに「LEDチカチカ」のアドレスが積まれているのは 確認したので「RTE命令」を発行した瞬間に何かが起きているようだ。 と、予想してみる。 ↑今ここ。 *TRAPAの動き トラップ処理から戻る時におかしくなる。 このトラップ処理は「TCB」の値をゲットしてタスクを切替える というアセンブラ命令。 1,このTRAPA処理を大域変数をカウントアップするだけの   C言語で書いた単純なコードに置き換えてみた。   結果はOK。   正しくTRAPA処理の次の行に戻ってくる。 2,1,のC言語をアセンブラに変換して本来のトラップ処理と   同じアセンブラファイルに同居させてみた。   これもOKだった。 Hewモニタ上で単純なTRAPA処理を実行すること自体に問題はなさそうだ。 従って問題は処理内容と絡んでいる可能性がある。 処理内容はディスパッチ(タスク切り替え)なのでレジスタのR0からR15まで 全部一気に書き換えてしまう。これがだめなんだと思うけど。 特にスタックポインタ(R15)を書き換える様なことはまずそうだ。 これ以上はやめておきます。 来月号にシリアルROMの32kバイト領域にプログラム書いて Hewモニタを介さず実行できるやり方が載るのでそれ待ちにする。 そうすればレジスタバンクがらみも開放されて一気に動きそうな 予感。 あくまで予感。 その代わりHewモニタ使わないのでステップ実行などのデバッグもできない。 *ヘッダファイルの依存関係不良 上の方でプロジェクトにソースコードを追加したけどヘッダファイルの 依存関係が正しく解決されなくてビルド不全が起こっている。 orz クリーン・ビルドすればいいんだけど。ちょっとなぁ。 これも調査中。 といっても「依存関係を更新する」ってメニューがあってこれやっても だめなので手詰まり状態。 *レジスタバンク(1) レジスタバンク機能をざっくり言うと、 内部レジスタ群を「バンク」という単位にまとめてバンク単位で 「いわゆるpush/pop動作」をハードウエアで高速に実行する機能である。 保存先は16バンク分だけ用意されている。 で、 1,レジスタバンクは許可、禁止設定ができる。 2,レジスタバンク許可、禁止は割込み優先順位に対して設定する。 3,IBCRレジスタで各優先順位に対する許可禁止を設定する。 大枠(おおわく)はバンク番号レジスタ IBNR で設定する。 IBNR::bit15,14:BE 初期値 00   00: 全ての割込みでレジスタバンク使用禁止。IBCRは無視。   01: 全ての割込みでレジスタバンク使用許可。IBCRは無視。   10: reserve   11: レジスタバンクの使用はIBCRの設定に従う。 ざっくり書くと 00: 全面禁止。 01: 全て強制使用。 11: 個別設定に従う。 の、どれかを選択できる。 「Hewモニタ」の制約事項では「01:」の「全て強制使用」になっている。 と、書いてある。 と、読めるわけです。(^^; (IBNRレジスタ見ればいいんだけど) *レジスタバンク(2) (resbank)指令 で、実際は上の「IBNR/IBCRの設定」と「C言語の割込み関数定義で (resbank)指令」の指定を正確に整合させなければならない。 ここに不整合があると確実にシステムが破綻する。 以下の"命令"をコンパイラが生成するかどうかを示す。
(resbank)指令push/pop命令resbank命令(pop動作)レジスタバンクへの待避
あり x 待避(push)済みを前提
なし x -
上で"push/pop命令"は実際にはそれに相当するレジスタ待避命令のこと。 ここまでくると上の方で書いた IBNR 割込み発生 ↓ レジスタバンクに待避 ↓ -----------------------------割込みルーチンの先頭 resbank           レジスタバンクから復帰 レジスタ群をスタックにpush { 割込み処理 } レジスタ群をスタックからpop RTE *レジスタバンク(3) JTAG/ROM化 で、 改定版LEDチカチカというのがあって、 http://www.kumikomi.net/interface/editors/2010/04/led.php この中の「Readme.txt」を読んでみた。 これもやっぱり「レジスタバンク系」の対応だった。 上で書いた「IBNRレジスタ」、 リセット時は「IBNR::bit15,14:BE 初期値 00」。 すなわち レジスタバンク 全面禁止になっているので 「レジスタバンク全て強制使用」を前提とした「Hewモニタ用ソフト」を そのままJTAGでRAMへ,或いはROM化等で書き込んで実行させると プログラムが破綻してしまう。 従ってリセットベクタ中の早い時期に IBNR::bit15,14:BE = 01 に設定する変更が入っている。 具体的には「resetprg.c」に以下のコードが追加されただけだ。
 INTC.IBNR.WORD = 0x4000;
posted by Copyright (C) avrin All Rights Reserved. at 17:52| Comment(0) | FreeRTOS | このブログの読者になる | 更新情報をチェックする

2009年09月27日

ARM: FreeRTOSを試す: LPC2388(3)ここまでのまとめ

ARM: FreeRTOSを試す: LPC2388(3)ここまでのまとめ
* はじまり
* V01
* コンパイラを再度試す
* 動作不良の原因わかる  orz (2011/06)
* .dataセグメントと初期化データの整合性を確認する
* V02 (2011/06)
* V03 (2011/06)

* はじまり

ARM: FreeRTOSを試す: LPC2388(1)/ARM7
ARM: FreeRTOSを試す: LPC2388(2)

と、上のように
なんだかんだとやってきたが
ようやくスモール・デモとしてまとめた。

内容はマルチタスクなので、
1.LEDチカチカさせつつ(基板上にあるLED)
2.PCにUART経由で文字列を表示させつつ(追加工作不要,USB-UART変換)
3.入力された数字に応じてLEDチカチカ速度を変更。
という3つのタスクを動作させます。

一応、FreeRTOSの最初の1歩にはなるんじゃないかと。

* V01
V01は以下に述べるように、リンカ・スクリプトがバグっていたので終了しました。orz
(2011/06)

* コンパイラを再度試す
以前、動作があやしげだったコンパイラがあったと書いたが
当然、こっちのソフトがあやしい可能性もあった。

上にアップしたソースでもう一度挙動を確認してみることにした。

makeの引数にコンパイラ名を渡せばメイクできる。
例えば、
YAGARTOの場合、以下でコンパイルできる。
make TC=arm-elf-
コンパイラは全部Windows用。
コンパイラ デモ動作 toolchain バージョン 備考
Sourcery g++ Lite arm-none-eabi-gcc gcc 4.3.3
(Sourcery G++ Lite 2009q1-161)
商用の無料版
YAGARTO arm-elf-gcc gcc 4.3.3 (GCC)
WinARM arm-eabi-gcc gcc 4.3.0 (WinARM March 2008) この版はfloatライブラリがない
devkitARM X arm-eabi-gcc gcc 4.4.0 (devkitARM release 26)
cygwin gcc arm-elf-gcc gcc 4.3.1 (GCC) 「インターフェース誌」提供の物
で、YAGARTOは大丈夫になった。「になった」が微妙(^^; devkitARMはやっぱりだめだった。 devkitARMだけGCCがVer.4.4.0であとはVer.4.3.x系なんで リンカスクリプトを変えないといけないのかも。 このまま行くと他のコンパイラでもVer.4.4.xにアップしたら 動かなくなる可能性もあるな。 orz (2011/06) 上の話は、以下の話で一挙に解決するのだった。 * 動作不良の原因わかる  orz (2011/06) 時は、流れて。(オイ orz 原因はリンカ・スクリプト(lpc2388.ld)の記述を間違えていたのだった。 、修正したのは以下のような感じ。(progと.dataの間だけ示した) (2011/06)
    prog : 
    {
        *(.text)
        *(.rodata)
        *(.rodata*)
        *(.glue_7)
        *(.glue_7t)
    } >flash
    /***  here ****/

    __exidx_start = .;
    .ARM.exidx   : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) } > flash
    .eh_frame    : ONLY_IF_RO {
         KEEP (*(.eh_frame))
         . = ALIGN(4);
     } > flash
     . = ALIGN(4);
     __end_of_text__ = .;

    .data ALIGN(4): 
    {
        __data_beg__ = .;
        __data_beg_src__ = __end_of_text__;
おかしかったのは、赤文字の「__end_of_text__=.;」が「prog」セクションの直後に ある場合だった。(/*** here ****/の位置)(上は修正済み) 「.data」セクションにコピーする初期化データは、「prog」セクションの直後に あると思い込んでいたのだった。 実は、そうではなくて、上の場合、追加した「.eh_frame」セクションの直後から 初期化データが始まるのだ。 で、実際のところ「.eh_frame」セクションは、ほとんどの場合、サイズゼロ。 問題は、「.ARM.exidx」セクションが以前書いたように、 状況に応じてサイズが8バイトになったり、サイズゼロになったりするのだ。 「__end_of_text__=.;」が「prog」セクションの直後にある場合(バグな場合)、 「.ARM.exidx」セクションがゼロなら動作するし、 「.ARM.exidx」セクションが8バイトなら、 初期化データの先頭8バイトに「.ARM.exidx」セクションが食い込むことになり、 アウト。 orz さらに、上で追加されている「ALIGN(4)」系も、追加したほうが良いかも。 というのも、上の「.ARM.exidx」セクション、「.eh_frame」セクションが 共にサイズ0だった場合、その前のセクションの最後のアラインメントが反映される 可能性もある。(これは予想) アラインメント指定がない場合、1〜8のアラインメントになる可能性も否定できない。 その場合でも、続く「.data」セクションのアラインメントも無指定なら大丈夫そうな気もするが、 無意識に「.data」セクションの先頭だけを「ALIGN(4)」にしてしまうと、 直前のセクションの最後のアラインメントが「ALIGN(4)」以外では、 不整合が起きて、アウト。 表現を変えれば、 ソースコードが増えると、状況によって動く場合と、動かない場合がランダムに現れることになる。 さらに、コンパイルオプションや、gccの種類やバージョンなんかも影響する。 自分は多分、これにハマッタ orz orz。 従って、上記のように明示的にアラインメント指定をしておく。 しかし、この明示の仕方や効果がイマイチよく分かってなかっりする。(^^; 例えば、セクション内の最後の行に「. = ALIGN(4);」を記した場合、 そのセクションのサイズが0でもアライン効果があるのかどうか。 とか。 (後述のマップ・ファイルを見ると効果がありそうな感じ) スタック関係: ちなみに、ALIGN(4)/(8)系は探査中。 もしスタック・セクションがあるなら、その先頭アドレスは、 ARMのEABIでは「ALIGN(8)」でなければならないらしい。 (スタックに設定する値は、8バイト・アラインメントでなければならない) このアラインメント問題も、ひと癖ありそうなので探査中。 FreeRTOSは「各タスクのスタック」を作る時、内部的に自動で マイコンごとのアラインメントを考慮してくれる。 CooCox CoOSは、スタック配列の先頭アドレスを手作業で8アラインメントに揃えた上で、 さらに、スタックに設定する「スタック配列の最後のアドレス」を自分で8アラインメントに 揃える必要があった。 orz 例えば、   
uint32_t mystack[25] __attribute__  ((aligned  (8)));
  と、添え字を「奇数」に定義して、スタックに設定するアドレスは   &mystack[ 25 -1 ]   と偶数に。  メンドクサすぎだった。 orz   上の配列定義時の添え字を「偶数」(26)にした時は、スタックに設定するアドレスは   &mystack[ 26 -2 ]の様に2を引く。   て、いうか、フォーラムじゃなくてドキュメントに書いてほしいぞ。 * .dataセグメントと初期化データの整合性を確認する 上のように、微妙な部分もあるので、マップファイルを使って確認する方法を記す。 マップファイルの「.data」セクションのあたりを確認します。 以下は、バクっているパターン。
 *(.fini)
                0x00009c60                . = ALIGN (0x4)
                0x00009c60                __end_of_text__ = .
                0x00009c60                PROVIDE (etext, .)
                0x00009c60                PROVIDE (_etext, .)
                0x00009c60                __exidx_start = .

.v4_bx          0x00009c60        0x0
 .v4_bx         0x00000000        0x0 linker stubs

.ARM.exidx      0x00009c60        0x8
 *(.ARM.exidx* .gnu.linkonce.armexidx.*)
 .ARM.exidx     0x00009c60        0x8 f:/sourceryxpresso/bin/../lib/gcc/arm-none-eabi/4.3.3\libgcc.a(_divdi3.o)
 .ARM.exidx     0x00009c68        0x0 f:/sourceryxpresso/bin/../lib/gcc/arm-none-eabi/4.3.3\libgcc.a(_udivdi3.o)
                                  0x8 (size before relaxing)
                0x00009c68                __exidx_end = .

.eh_frame
 *(.eh_frame)
                0x00009c68                . = ALIGN (0x4)

.data           0x40000000      0x850 load address 0x00009c68
                0x40000000                __data_beg__ = .
                0x00009c60                __data_beg_src__ = __end_of_text__
                0x40000000                _data = .
 *(.data .data.*)
 .data.ulCriticalNesting
上で、赤字の「__end_of_text__」は「0x00009c60」、 黄色のロードアドレスは「0x00009c68」と、本来これらのアドレスは一致しなければならない。 (ロードアドレスとは、Flash上にある初期化データの先頭アドレス) これでは初期値がメチャメチャで(8バイトずれている)、マイコンが動作しない。 が、しかし、 「.ARM.exidx 」セクションが偶然存在しなかった場合、 「問題なく動いてしまうのが」、最も問題だったりする。(^^; 以下は、正しいパターン
 .v4_bx         0x00000000        0x0 linker stubs

.ARM.exidx      0x00009848        0x8
 *(.ARM.exidx* .gnu.linkonce.armexidx.*)
 .ARM.exidx     0x00009848        0x8 f:/sourceryxpresso/bin/../lib/gcc/arm-non
 .ARM.exidx     0x00009850        0x0 f:/sourceryxpresso/bin/../lib/gcc/arm-non
                                  0x8 (size before relaxing)
                0x00009850                __exidx_end = .

.eh_frame
 *(.eh_frame)
                0x00009850                . = ALIGN (0x4)
                0x00009850                __end_of_text__ = .
                0x00009850                PROVIDE (etext, .)
                0x00009850                PROVIDE (_etext, .)

.data           0x40000000      0x850 load address 0x00009850
                0x40000000                __data_beg__ = .
                0x00009850                __data_beg_src__ = __end_of_text__
                0x40000000                _data = .
 *(.data .data.*)
 .data.ulCriticalNesting
直前のセクション(「.ARM.exidx」、「.eh_frame」)に関係なく load address = __end_of_text__ となることが分かる。 マップファイルはコンパイル時に生成する。 * V02 (2011/07) FreeRTOS-ARM7-LPC2388-small-demo-ver02-201107.zip 「シンプルに徹して、はじめの一歩でつまずかないように」というのが目的。 さらに、追加工作不要なので、「CPU基板の生存確認ソフト」も兼ねている。 変更点: 1, 上のV01版のリンカ・スクリプトのバグ修正。 2, make/Makefileをやめて、最近、大マイブーム中の「SCons」でビルドするようにした。 3, LPCXpressoのプロジェクトとしてもコンパイル可能にした。 4, MAM設定を追加。(実行速度が1/3程度しか出ていなかったのを修正) (汗 5, FreeRTOS v7.0.1をプロジェクトに含めた。 * V03 (2011/07) FreeRTOS-ARM7-LPC2388-small-demo-ver03-201107.zip 今のところ、内容は上のV02版に加えて、 変更点: より進んだ内容は、FreeRTOSに付属のデモやねむいさんのブログ、 「徹底入門本」を参考にしたほうが良い。 このページは書きかけで、 参考文献: LPC23xxに特化した解説書。 珍しい。 http://docweb.khk.be/Patrick%20Colleman/ARM7/lpc2300_book_v2_srn.pdf Codesourcery g++ Lite for arm のダウンロード(gcc) http://www.codesourcery.com/sgpp/lite/arm/portal/subscription3053
posted by Copyright (C) avrin All Rights Reserved. at 17:38| Comment(4) | TrackBack(0) | FreeRTOS | このブログの読者になる | 更新情報をチェックする