Team for GR-PEACH Producer Meeting

delay_ms()のバラツキ

19 Jan 2015

ARMコンパイラの動作については了解しました。ありがとうございます。
↓こうする必要があるという事ですね。危ないので覚えておきます。

        val = (uint32_t)(uint64_t)(val64 / count_clock);

確かに、暗黙的なdoubleから整数型への変換は整数部がオーバーフローする時の動作は未定義なので実装依存でした。
組み込み系だと最大値に丸める実装が定番なんでしょうか。
(uint32_tへのキャストを指定しなければそうなっても仕方が無いと思いますが、指定しているので違う気も...)

UNIX系だと、整数化後に代入先のサイズに切り詰める実装が多いのでこちらを期待してしまいした。元のコードを書いた方も同じ認識だったのではないかと。
※Linux(x86, x86_64)+GCC, MacOS X(x86_64)+LLVM, HP-UX(IA64)+HP aCCのいずれも後者でした。

19 Jan 2015

Hamanakaさん、Akagawaさん、
お二人の議論についていけない素人ですが、計算精度はどの程度になっているのですか?
33.333で除算してから丸めているのですか?
33で除算しているのではないのですか?
すみません、一般的なキャストに関しての質問ですが実装依存に関係すると聞くと、納得できるところまでお聞きしたいのですが?

20 Jan 2015

Akagawaさん
コードのご提示ありがとうございます。
ご提示いただいたコードでも、正常に動作します。

>組み込み系だと最大値に丸める実装が定番なんでしょうか。
修正前のコードの場合、最終的にはCortex-A9のハード処理により、最大値に丸められています。
この動作については、Cソース、コンパイラ、Cortex-A9のハード仕様が絡んでいますので、
詳細について、ARMさんに確認をしてみます。

>UNIX系だと、整数化後に代入先のサイズに切り詰める実装が多いのでこちらを期待してしまいした。元のコードを書いた方も同じ認識だったのではないかと。
おっしゃる通り、上記を期待してコードを書いておりました。


Araiさん
該当箇所の計算精度は倍精度計算です。
ですので、除算は33.333333で除算しています。
除算結果は、浮動小数点から整数への変換命令(キャスト)にて丸め込まれます。

この時、修正前のコードですと、
double型からunsigned long型への変換命令となります。
除算結果がunsigned long型の最大値、0xFFFFFFFFより大きい場合、
整数への変換命令で最大値に丸め込まれます。

修正後のコードですと、
double型からunsigned long long型への変換命令となります。
除算結果が0xFFFFFFFFより大きい場合でも、unsigned long long型に格納できるため、
整数への変換命令で0xFFFFFFFFへの丸め込みは発生しません。
この変換結果の下位32bitをunsigned long型の戻り値として採用します。

21 Jan 2015

1/19の修正後、再度テストしましたがEthernetと同時に動作させるとおかしくなるようです。
修正前は1時間以内の不定時間で停止していたものが連続動作するようになりしたが、およそ1時間11分30秒後からwait_ms()が指定した時間の前に返ってくるようになります。
wait_ms(5000)ではなく、Thread::wait(5000)だと問題無く動作します。 Ethernetを使わないプログラム(SNIC版の方)ではこの現象は発生しないため、RTOSというよりはEthernetの問題なのかもしれません。

また、これは制約事項になるのでしょうが、uint64_t→doubleなので、wrap_arroundが1048576を超えた辺りから怪しくなると思います。
→ doubleの仮数部は52bitなので、52-32=20, 2^20=1048576。4年3ヶ月ちょいですかね。

2015.1.21 20:25更新
動作不良は1/19のRTOSに関しての議論と同件でした。
Revision 444:219e1437f2ffで解消していることを確認しました。

22 Jan 2015

EthernetInterfaceのソースを読んでいると、us_ticker_read()を呼び出している様なので、1/19のRTOSに関しての議論と同件かもしれません。
正式な修正版のリリース後に再度テストしたいと思います。

21 Jan 2015

修正版のライブラリでは問題なく動作する様なので、昨晩の問題については撤回します。

21 Jan 2015

Akagawaさん
現象が最新版のmbed-srcで発生しなくなったとのことですが、ちょっと気になりませんか?
蒸し返す意図はありませんが、us_ticker_read()を割り込み禁止にしてrtosの問題は解決しましたが、割り込み禁止する以前のプログラムでは現象はいつでも起こる事で1時間11分31秒前に起こって何の不思議もありません。
何故、1時間以上逆に正常に動作してから突然起きるのでしょうか?
最新の「Known issues」でTickerを二つ定義して動作させるとタイミングが狂う現象が確認できている模様です。
us_tickerのコードを覗く時間が多くなり、私が感じるのは読み出しで問題があったと同様に、次の未来最短イベントのためにキューを生成していますが、キューへの追加部分などにも何かまだ問題があるように思えます(たぶんまた割り込み関係)。
いずれにしても1時間以上何もなくそれから問題が現れるのは納得できません。
感情だけで申し訳ありませんが・・・・
しかし素人でprintfでのDebugでは限界であり、中の人の活動にお任せするしかありません。よろしくお願いします。

22 Jan 2015

独り言
1)殆どのmbedは、1μSを32ビットタイマーで計測して4294秒ちょっとまで計測
2)GR-PEACHは、30nSちょっとを32ビットタイマーで計測して128秒ちょっとまで計測して、上位をソフトウェアカウンターで計測
3)wait()やTicker等の時間管理は、全てこのタイマーへの時系列順のキューが生成され、順番に入れ出し(キューへの登録、削除)が行われる
4)タイマーのアウトプットコンペア割り込みを利用してイベントを管理。時系列の先頭を次の割り込みのアウトプットコンペア値としてイベントを待つ。無くなれば割り込み禁止
5)キューは64ビットで時間管理されている(timestamp_t)が、タイマーが32ビットなので上位は無視された値でアウトプットコンペアにセットされる
6)まあ一時間ちょっと計測できるタイマーなので、まさかwaitで2時間待つユーザーはいないだろう? という発想か?
キュー自体は64ビットなので現実は問題無し?
7)それに対して、GR-PEACHは120秒ちょっとで毎回割り込みが発生するが、空振りで何度も割り込み設定しながら本番を待つ想定
8)上位の共通ルーチンであるus_ticker_api.cpp内us_ticker_irq_handler()が呼ばれるが、
if ((int)(head->timestamp - us_ticker_read()) <= 0)
で判断ではなく、120秒ちょっと毎に割り込みが必要か否かで判断する必要はないのかな?

22 Jan 2015

Akagawaさん
mbed-src-v442-pre(v444)でのご確認、ありがとうございます。
Araiさんからコメントありましたように、
「known issues」「defects No.6」にTickerの新たな問題をアップしました。
原因を解析中です。
進捗あり次第、情報を展開します。

Araiさん
ご見解ありがとうございます。
Tickerについて解析を進め、あらためてコメントさせていただきます。

22 Jan 2015

Araiさん
修正版で何度かテストしても発生しませんし、24時間以上動作させても発生しなくなりました。不具合の証拠が提出できないので、引っ込めました。
解消した理由について究明できてませんが、RZのEthernetInterfaceの実装において、複数の場所でus_ticker_read()を利用しているということから、us_ticker_read()の戻り値がラップアラウンドするタイミングでなんらかの問題があったのであろうと判断しています。

mbed-src-v442-pre(v444)においても、割り込み禁止に関する部分をコメントアウトするだけで再現できます。
実はTCPのコネクションを確立する必要すらなく、以下のプログラムでも再現可能です。接続先のサーバ用意する必要はありません。LANケーブルを繋げなくてもOKです。

main.cpp

#include "mbed.h"
#include "EthernetInterface.h"

EthernetInterface eth;
Serial pc(USBTX, USBRX);

int main() 
{
    eth.init(); //Use DHCP
    eth.connect();
    while(1) {
       uint32_t t1 = us_ticker_read();
        wait_ms(5000);
//       Thread::wait(5000);
       uint32_t t2 = us_ticker_read();
       pc.printf("t1:%u t2:%u t2-t1:%u\r\n", t1, t2, (t2-t1));
    }
    eth.disconnect();  
}

※実際には毎度1:11:31も待てないので、wrap_arround=32;としてテストしています。162秒ほどで発生させることが可能です。

RZはメモリもCPUも有り余っているので、Tickerやwaitではなくて、RTOSのRtosTimerやThread::waitを使うべきなんでしょうね。

22 Jan 2015

Akagawaさん
追加情報、ありがとうございます。
皆さんと違って、私はGR-PEACHで何もまだ作れていないので、ユーザーが専念すべき方向に舵を切りたいと思います。
事務局から今後の計画も発表があるようですし、rtos、I2Cを含めmbed-srcが安定してきたので、言い訳が出来なくなってきました。
しかし、まだすっきりしませんが・・・・