Team for GR-PEACH Producer Meeting

delay_ms()のバラツキ

07 Dec 2014

(タイトル間違い。delay_ms()ではなく、wait_ms()でした。m()m)

wait_ms(1)を繰り返し実行した結果をロジアナで測定したところ、一定のパターンで0.5msec.と1msec.よりも大幅に短い動作が見受けられました。

#include "mbed.h"

DigitalOut myled(P1_2);

int main() {
    while(1) {
        myled = 1;
        wait_ms(1);
        myled = 0;
        wait_ms(1);
    }
}

/media/uploads/matsujirushi/20141207c.png

Lチカのバラツキと関係あるかも? ご確認願います。

06 Dec 2014

TickTimer割り込みを使って1/20秒間隔でLチカを行うとある時間が過ぎると1/10程度にLチカが遅くなります。 どうもタイマー関係が怪しい気がするのですが・・・

07 Dec 2014

僕も同じくです。 wait_ms(1000)を使って1秒ごとにLED点滅をするプログラムを作ったのですが、明らかに等間隔ではなく光っているように見えました。

07 Dec 2014

僕も同じくです。 wait_ms(1000)を使って1秒ごとにLED点滅をするプログラムを作ったのですが、明らかに等間隔ではなく光っているように見えました。

07 Dec 2014

Lチカだけの簡単なプログラムを走らせると小一時間程度でハングします。 どのプログラムでもある程度時間が経つとハングするんですが皆さんも同じでしょうか。

07 Dec 2014

たぶん、私が以前指摘したこの件との関係です。
http://developer.mbed.org/users/kenjiArai/notebook/lpc1114fn28---suggestion-for-improvement/#
32bitのTickerタイマーオーバーフローが1時間11分過ぎに起きます。
mbed-srcでなくmbedのライブラリーは、現在治っていますが、GR-PEACHが古い修正前不具合を引きずっているのではないでしょうか?
関係するソフトのバージョンを最新版にすると、停止する問題は解決するでしょう。
但し、バラツキは別問題のような気がしますが・・・

07 Dec 2014

大変失礼いたしました。ご指摘通りmbed-srcのライブラリを使用しておりました。 現在mbedライブラリを使用して動作試験中です。

07 Dec 2014

ちょっと覗いただけですが、たぶん今のように16bit+16bitのタイマー構成で、

us_ticker_read()内

   val = MTU2.TCNT_1<<16 | MTU2.TCNT_2;  // concat cascaded Counters

のように二つのタイマーが走っている時にデータを個別に読んで足しては同期が保たれないでしょう。
データ解析しないと何とも言えないですが、下位の16bitタイマーのオバーフロー前後の読み出しタイミングで連続性が保たれていないように思われます。
インプットキャプチャー機能で上位、下位のそれぞれのタイマー値をソフトウェアトリガーで一緒に補足できれば使えるでしょうが、そんなことできますか?
一番簡単なのは32bitタイマーを使用することだと思います。
ほかのmbedは間違いなく32bitタイマーを使用していますが、RZ/A1Hは32bitタイマーで使えるのがないのですか?

07 Dec 2014

マニュアルを拝見すると、32Bitタイマーが二つあり、アウトプットコンペアも32bitで割り込み発生が出来そうです。
片方は使っていると思いますが、もう一方が使えませんか?
先のコメントで、16+16Bitで2回読み出しの問題を指摘しましたが、もっと問題なのは上位16Bitだけでアウトプットコンペア割り込みをかけている点です。
これでは正確な時間計測は無理でしょう。
Floatで係数を掛け算して割り出していますが、これも賛成できかねます。
クロックベースを極力1μSecにすべきでしょう。
先ずは暫定対策を提案してみたいですが・・・

08 Dec 2014

本件、ご報告ありがとうございました。
現在解析中です。

08 Dec 2014

TickTimer割り込みを使用して50ms間隔でLEDを点滅させているのですが1分程すると100ms間隔に落ちます。 何やらOSの重い処理が発生するのかフラグチェックからLEDの反転処理が50ms内で終わらず次のTickTime割り込みが発生してる模様。

#include "mbed.h"

Ticker flipper;
DigitalOut led(LED_USER);

unsigned int ledcounter;                            // カウンタ
unsigned char ledflg;                               // LEDフラグ

void flip() {
    ledcounter++;
    if(ledcounter > 50) {
        ledcounter = 0;
        ledflg = 1;
    }
}

int main() {
    ledcounter = 0;                                 // カウンタ初期化
    ledflg = 0;                                     // フラグ初期化

    flipper.attach(&flip, 0.001);                   // 1msでTichTimer割り込み

    led = 1;                                        // LED消灯

    while(1) {
        if(ledflg) {                                // フラグチェック
            ledflg = 0;                             // フラグクリア
            led = !led;                             // LED反転
        }

    }
}

バラつきに関係してるのかな?

ちなみにmbedのライブラリを使用したプログラムは1時間以上は動くようになったのですが寝ている間にハングしたようです。

08 Dec 2014

とりあえず下記の対応で、暫定対策としては効果があったようです
興味のある方は試してみてください。
Matsuokaさんのような良い計測手段を持っていないので確信は持てませんが、少なくとも誤計測の確率は少なくなりました。

us_ticker.c

uint32_t us_ticker_read() {
    static uint32_t max_val = 0x8551eb85; //*F_us2CLK(0xffffffff)+1;
    uint32_t val;
    if (!us_ticker_inited)
        us_ticker_init();
#if 0  // Original
    val = MTU2.TCNT_1<<16 | MTU2.TCNT_2;  // concat cascaded Counters
#else  // check data and read again if wrong
    volatile uint16_t tl0, tl1, th0, th1;
    tl0 = MTU2.TCNT_2;
    th0 = MTU2.TCNT_1;
    tl1 = MTU2.TCNT_2;
    th1 = MTU2.TCNT_1;
    if ((tl1 > tl0) && (th0 == th1)){
        val = th1 << 16 | tl1; 
    } else {
        tl1 = MTU2.TCNT_2;
        th1 = MTU2.TCNT_1;
        val = th1 << 16 | tl1;
    }            
#endif
    if (val > max_val) {  // if overflow (in us-timer)
        val -= max_val;   // correct value
        MTU2.TCNT_1 = 0;  // reset counter
        MTU2.TCNT_2 = val;
    }
    val = F_CLK2us(val);
    return val;
}

やはり、2つのタイマー値を足す部分に問題があったようです。
但し、あくまでも暫定対策です。
タイマークロックとして、33.33MHzのクロックしか選べないのも痛いですね!
32bitタイマーがほしかったですね!

09 Dec 2014

Araiさんのコードを反映して、1st postと同じ方法で測定しました。

大幅な異常値は無くなりましたー!

ありがとうございます。

/media/uploads/matsujirushi/20141209a.png

(ちなみに、測定はSaleae Logic Pro 16を使っています。測定結果をCSVエクスポートして、Excelでグラフ化しています。以前はZEROPLUSを使っていましたが、使い勝手が悪くて...。)

09 Dec 2014

Matsuokaさん、 お手数かけました。10マイクロセック程度の誤差が生じていますね!float演算や再読みが原因でしょう。やはり1マイクロセックの入力信号32ビットカウンターが正当な解決手段のはずです。33.33MHzのクロックを32MHzにしてもらうとスッキリするのですが弊害がでるでしょうか?mbedに参加すると決めた時点で1マイクロセックは基本のポイントだと思います。再考願いたい部分です。

10 Dec 2014

tickerに32bitタイマを使用するよう変更いたしました。
ご自身のプロジェクトの mbed-src を右クリックして update を選択すると、変更が反映されます。
/media/uploads/RyoheiHagimoto/mbed-src_update.png

なお、Kenji Arai さんご指摘のオーバーフローの件ですが、今回の変更で130秒ごとにタイマがオーバーフローします。
mbed共通APIのwait_usがタイマの回り込みを考慮されていないため、
回りこみの直前でwait関連関数がコールされると関数から戻ってこなくなることが予想されます。
ARM様と協議の上早急に修正します。
お手数ですが、対応されるまでの間、暫定的に、mbed-src\common\wait_api.cのwait_us関数を以下のように変更していただけますでしょうか。

void wait_us(int us) {
    uint32_t start = us_ticker_read();
    uint32_t expire_time = 0;
    uint32_t now;
    uint32_t last  = start;

    do {
        now = us_ticker_read();
        if ( last > now ) expire_time += 130150524;
        last = now;
    } while ((now + expire_time - start) < (uint32_t)us);
}
10 Dec 2014

祝32ビットタイマー採用!!!

私も32ビットタイマーをマニュアルで調べたのですが、インプットクロックが33.33MHzしか選べないので落胆したところでした。
1μSecのカウンターであれば、オバーフローが4294秒ごとですが、Hagimotoさんの説明のようにPEACHでは180秒ごとに発生してしまうことが弊害となって上位関数にも影響が出る結果なんですね。
しかし、これで精度もかなり向上したのではないでしょうか?

Matsuokaさん
また測定して結果を皆さんに紹介してしていただくと、この問題提起も(表向き)一件落着となる気がします。

尚、PEACH以外のmbedで同じプログラムを走らせる方も多いと思いますので、下記をリコメンドします。 これで何も気にせずボード変更できます。

mbed-src\common\wait_api.c

#if defined(TARGET_RZ_A1H)
void wait_us(int us) {
    uint32_t start = us_ticker_read();
    uint32_t expire_time = 0;
    uint32_t now;
    uint32_t last  = start;
 
    do {
        now = us_ticker_read();
        if ( last > now ) expire_time += 130150524;
        last = now;
    } while ((now + expire_time - start) < (uint32_t)us);
}
#else
void wait_us(int us) {
    uint32_t start = us_ticker_read();
    while ((us_ticker_read() - start) < (uint32_t)us);
}
#endif

10 Dec 2014

mbed-src Rev.434を測定しました。大幅な異常値はありませんでした!

/media/uploads/matsujirushi/20141210a.png /media/uploads/matsujirushi/20141210b.png

11 Dec 2014

Matsuokaさん
お手数かけましたが、これではっきりしましたね!
私の16+16bit時の精度が、幅で12μSec、上記32bitで、幅が0.8μSecと読み取れると思いますので、精度は十分と思います。

実は、NucleoF411REを使用した周波数カウンターを製作中なのですが、内部クロック100MHzを32bitカウンターでGPSの1PPSで補正をかけて使用したところ、使えないと結論を出しました。
理由は100MHzがPLLで作られているのですが、制御幅として70Hz程度を30秒くらいかけてのこぎり刃上にのこぎり波状に変化します。
PLL制御上、この変動幅と周期はどうにもなりません。
PEACHも内部PLLの変動幅と周期の兼ね合いで上記のような変動が起きていると推察します。
32bitのカウンター取り込み誤差(量子化)は、33.33MHz入力では30nSec程度のはずですから、測定値と一桁違います。
周波数カウンターは、32bitを持つほかのARMでは実現できますが、ユーザーが使用できる32bitタイマーがなくてPEACHではできません。
これを2月の作品にと思ったのですが残念です。

11 Dec 2014

もう一つ、doubleでの力任せの演算で丸め誤差もこの付近になると大きい要因と思います。
32bitタイマーに自由度の大きいプリスケーラーがあると完璧だったのですが・・・
ここまでで議論は終了でしょうか?
あとはWishにしかならず反映が難しい話になってしまいます。

16 Dec 2014

Kenji Arai さんご指摘のオーバーフローの件を修正致しました。
本修正により、wait_api.cの変更が不要となりました。
ご自身のプロジェクトの mbed-src を右クリックして update を選択すると、変更が反映されます。

Araiさんご指摘の通り、
RTOSおよびフリーランニングで32bitタイマを使用しているため、ユーザが使用できる32bitタイマはありません。
ユーザの使用できるタイマはMTU2となりますが、
こちらはプリスケーラを有するものの、16bitとなります。

>ここまでで議論は終了でしょうか?
>あとはWishにしかならず反映が難しい話になってしまいます。
ご要望にお応えできず、すいません。
本議論の主題であるdelay_ms()のばらつきに関しては、
多数のご指摘を頂けたことにより、解決することができました。

皆様、本件に関するご指摘、性能測定等、誠にありがとうございます。

16 Dec 2014

あらためてロジアナのサンプリング周期を上げて、測定しました。

とても良好です!

/media/uploads/matsujirushi/20141216a.png /media/uploads/matsujirushi/20141216b.png /media/uploads/matsujirushi/20141216c.png

ご対応、どうもありがとうございました。

21 Dec 2014

一週間、多忙のため反応できずに申し訳ありません。

Hamanakaさん
非常にスマートな解決になったと思います。
ご苦労様でした。
32bitタイマーは、ぜひ今後のCPU開発で入れ込んでいただきたい周辺機能と思います。
タイマーのオーバーフロー、アンダーフローは永遠のテーマです。
インプットキャプチャーは16+16Bitでも何とかなりますが、アウトプットコンペアは16+16bitではどうにもならない(?)と思われます。

Matsuokaさん
いつも検証、ご苦労様です。
時系列の推移は、面白いほど規則性がありそうですね。
1000.1μS付近は幅があるのですか?
大きく幅を持ったところは一回だけですか?
時間が有るときで結構ですから、拡大して100サンプリングくらいを見せていただけないでしょうか?
良い測定器を持っていないもので・・・申し訳ありません。

21 Dec 2014

拡大を添付します。

/media/uploads/matsujirushi/20141221f.png

というか、データも添付したほうが良かったですね。^^

添付します。

/media/uploads/matsujirushi/wait_ms_rev440.zip

21 Dec 2014

Matsuokaさん
早速の対応、ありがとうございました。
通常はほとんど量子化誤差だけになったということですね。
70mS毎に処理による誤差が増えるのでしょうか?
お手数かけました。

15 Jan 2015

us_ticker.cのus_ticker_read()に不具合を見つけました。(1時間11分30秒の件です)

us_ticker.c(80)

    val = (uint32_t)(val64 / count_clock);

とありますが、2^32-1を越える(work_arround>32)と、ずっと4294967295を返します。

擬似コードを作成して実行してみましたが、Cortex-Mでも同じ動作なので、ARMコンパイラの仕様でしょうか。
※x86のgcc(gcc version 4.1.2 20080704 (Red Hat 4.1.2-54))だと、ラップします。

検証用擬似コードはこちら

#include "mbed.h"
#define US_TICKER_CLOCK_US_DEV (1000000)
#define CM0_RENESAS_RZ_A1_P0_CLK ( 33333333u)

Serial pc(USBTX, USBRX);

int main()
{
    uint32_t val = 0;
    uint64_t val64;
    double   count_clock = 0;
    uint32_t wrap_arround = 0;

    count_clock = ((double)CM0_RENESAS_RZ_A1_P0_CLK / (double)US_TICKER_CLOCK_US_DEV);
    while(wrap_arround < 40){
        wrap_arround++;
        val64 = ((uint64_t)wrap_arround << 32) + val;

        /* clock to us */
        val = (uint32_t)(val64 / count_clock);
        pc.printf("%u %u\n\r", wrap_arround, val);
    }
}

work_arroundがオーバーフローしたときの動作までは検証していないので、そちらの動作は不明です。

※2015.1.12 21:39追記
以下だと期待通りの動作をするようです。

us_ticker.c(77)

    val64 = (((uint64_t)wrap_arround << 32) + val) / count_clock;

    /* clock to us */
    val = (uint32_t)val64;
12 Jan 2015

Akagawaさん
よくたどり着きましたね!すごいです!
私もこの付近のコードを追いかけたのですが、さすがに値のチェックまでしていませんでした。
私もAkagawaさんの上記コードで、おかしなふるまいを確認しました。
*追記を読む前に、作業完了してPCから離れていたので、私は下記コードに置き換えて動作確認してみました。

//    val = (uint32_t)(val64 / count_clock);
    val = val64 >> 5; // divided by 32 (actual value is 33.333333f) 

5%程度の誤差にはなりますが、停止の有無確認を目的としました。クロックソースを、32MHzにしてもらうとdoubleでの計算は不要なのですが・・・・(せめて割り切れる数のクロックにしてほしかったがHW要望は締切ですね)

http://developer.mbed.org/users/kenjiArai/code/GR-PEACH_test_wo_rtos/
で、I2Cデバイスなど一切接続しないで、裸のGR-PEACHで上記修正プログラムを実行したところ、一晩8時間以上動きました。
1時間11分30秒問題は、Akagawaさんの指摘した部分を修正すればうまく動くようになるでしょう。
ご苦労様でした。
確認していませんが、追記した部分のcount_clockは、doubleをuint64_tにキャストしてから計算されるのでしょうか?
最終的に中の人が(といってもコンパイラのふるまいだとすると、中の人にもつらい!?)どんな対策をしてもらえるか?
対策方針と時期をお聞きしましょう。
後はrtos関連の停止条件の解析ですが・・・・

13 Jan 2015

Araiさん
やってみたら期待通りの動作をしたというだけなので、よくわかりません。
FPUの動作にも依存するので、コンパイラの問題ではないのかもしれません。
本件については、「修正しました」だけではなくて、今ダメなのはどういう動作をしていて、修正後はどういう動作になるので大丈夫ですという報告が欲しいところです。たまたま動いているだけというのでは困りますので。
※そして「ちゃんとテストしてからcommitしてね」って感じです。

13 Jan 2015

Akagawaさん
tickerに関し、解析ありがとうございます。
現在原因を解析中です。判明次第、情報展開します。

19 Jan 2015

Akagawaさん
報告が遅くなってしまい、すいません。

こちらで解析を行い、修正コードにて動作確認した結果、
1h30m11sの件が改善されていることを確認しました。

社内にてレビュー後、詳細についてご連絡致します。

19 Jan 2015

Akagawaさん

大変お待たせしました。
us_ticker_read()に関して、解析結果をコメント致します。

【不具合内容】
プログラムを実行し、1時間11分30秒を経過すると、プログラムが止まる。

【原因】
下記コードのアセンブラコードを確認しました。
double型で除算した結果を、直接unsigned long型へ格納しようとした場合、
除算結果が0x100000000以上の値となった時に、
unsigned long型のMAX値である0xFFFFFFFFに丸められていました。

従来のコード

uint32_t us_ticker_read() {
    uint32_t val;
    uint64_t val64;
    
    ・・・中略・・・
    
    val64 = ((uint64_t)wrap_arround << 32) + val;

    /* clock to us */
    val = (uint32_t)(val64 / count_clock);

    ・・・中略・・・
}


【対策】
コードを下記のように書き換え、アセンブラコードを確認しました。
下記コードでは、double型で除算した結果を、一度、unsigned long long型に格納しますので、
上で述べた除算結果の丸めこみは発生しません。
これをunsigned long型へキャストするので、正常な除算結果がunsigned long型に格納されます。
(やっていることは、Akagawaさんからお知らせいただいたコードと同じ内容です。)

修正後のコード

uint32_t us_ticker_read() {
    uint32_t val;
    volatile uint64_t val64;    //★ volatileを追加
    
    ・・・中略・・・
    
    val64 = ((uint64_t)wrap_arround << 32) + val;

    /* clock to us */
    val64 = val64 / count_clock;    //★ val64に除算結果を格納
    val = (uint32_t)val64;
    
    ・・・中略・・・
    
}


【対策コードでの確認結果】
本コードにて、1時間11分30秒以上経過しても、処理が止まらない事を確認しました。

【対策版コードの展開時期】
RTOSに関しての議論と修正箇所が関連するため、
そちらの動作確認が終了次第、mbed-src-v442-preにて展開させていただきます。
問題がなければ、明日中に展開する予定です。