Annotation of nono/vm/syncer.cpp, revision 1.1.1.7

1.1       root        1: //
                      2: // nono
                      3: // Copyright (C) 2022 nono project
                      4: // Licensed under nono-license.txt
                      5: //
                      6: 
                      7: //
                      8: // 実時間同期
                      9: //
                     10: 
                     11: #include "syncer.h"
                     12: #include "config.h"
1.1.1.7 ! root       13: #include "event.h"
1.1.1.4   root       14: #include "monitor.h"
1.1.1.3   root       15: #include "mpu680x0.h"
1.1       root       16: #include "scheduler.h"
                     17: 
                     18: //#define DEBUG
                     19: 
                     20: #if defined(DEBUG)
                     21: #define DPRINTF(fmt...)        printf(fmt);
                     22: #else
                     23: #define DPRINTF(fmt...) /**/
                     24: #endif
                     25: 
                     26: // パフォーマンス測定を行う実時間間隔
                     27: static constexpr uint64 PERF_INTERVAL = 1000_msec;
                     28: 
                     29: // コンストラクタ
                     30: Syncer::Syncer()
                     31:        : inherited(OBJ_SYNCER)
                     32: {
                     33:        for (int i = 0; i < perfq.Capacity(); i++) {
                     34:                perfq.Enqueue(1000);
                     35:        }
                     36: }
                     37: 
                     38: // デストラクタ
                     39: Syncer::~Syncer()
                     40: {
                     41: }
                     42: 
                     43: // 初期化
                     44: bool
                     45: Syncer::Init()
                     46: {
1.1.1.3   root       47:        mpu680x0 = dynamic_cast<MPU680x0Device *>(mpu);
                     48: 
1.1.1.5   root       49:        mode = SYNCMODE_POWEROFF;
1.1       root       50:        overslept = 0;
1.1.1.5   root       51:        dmac_idle = 1;
1.1       root       52: 
                     53:        // 高速モード指定
                     54:        bool fast_mode = gConfig->Find("fast-mode").AsInt();
                     55:        RequestFullSpeed(fast_mode);
                     56: 
1.1.1.6   root       57:        // クロックの同期方法
                     58:        const ConfigItem& sync_item = gConfig->Find("clock-sync");
                     59:        auto clock_sync = sync_item.AsString();
                     60:        if (clock_sync == "real") {
                     61:                sync_realtime = true;
                     62:        } else if (clock_sync == "virtual") {
                     63:                sync_realtime = false;
                     64:        } else {
                     65:                sync_item.Err();
                     66:                return false;
                     67:        }
                     68: 
1.1       root       69:        scheduler->ConnectMessage(MessageID::SCHEDULER_SYNCMODE, this,
1.1.1.2   root       70:                ToMessageCallback(&Syncer::ChangeModeMessage));
1.1       root       71: 
1.1.1.7 ! root       72:        auto evman = GetEventManager();
        !            73:        sync_event = evman->Regist(this,
        !            74:                ToEventCallback(&Syncer::SyncCallback),
        !            75:                "Sync");
        !            76:        sync_event->time = 50_msec;
1.1.1.3   root       77: 
1.1       root       78:        return true;
                     79: }
                     80: 
                     81: void
                     82: Syncer::StartRealTime()
                     83: {
                     84:        realtime.Start();
                     85: }
                     86: 
                     87: void
                     88: Syncer::StopRealTime()
                     89: {
                     90:        realtime.Stop();
                     91: }
                     92: 
                     93: // 時間の始まり
                     94: void
                     95: Syncer::StartTime()
                     96: {
                     97:        scheduler->StartEvent(sync_event);
                     98: 
                     99:        // 諸々の初期化が終わった頃にタイマーを開始。
                    100:        // Stopwatch.Restart() は .Net 由来なので
                    101:        // Restart() は経過時間をリセットしてからタイマーを開始の意。
                    102:        realtime.Restart();
                    103: 
                    104:        rtime = realtime.Elapsed();
                    105:        rtime_epoch = rtime;
                    106:        vtime_epoch = scheduler->GetVirtTime();
                    107: 
                    108:        // パフォーマンス表示用。
                    109:        // 最初の集計が行われる起動後1秒までは、この値が表示されるので
                    110:        // インチキして 100% にしておく。
1.1.1.7 ! root      111:        clock_khz = mpu->GetClockSpeed();
1.1       root      112:        perf_counter = 100;
                    113:        last_perf_rtime = rtime_epoch;
                    114:        last_perf_vtime = vtime_epoch;
                    115:        next_perf_rtime = PERF_INTERVAL;
                    116: }
                    117: 
                    118: // CPU 状態変更(通常/STOP/HALT)を指示する。
                    119: // CPU が呼ぶはずなので自スレッドからのみ呼ばれるはず。
                    120: void
                    121: Syncer::RequestCPUMode(uint32 cpumode)
                    122: {
                    123:        uint32 newmode;
                    124: 
1.1.1.5   root      125:        halt     = cpumode & SYNCMODE_CPU_HALT;
                    126:        cpu_idle = cpumode & SYNCMODE_CPU_STOP;
                    127: 
                    128:        newmode = (mode & ~SYNCMODE_CPU_MASK) | halt | (cpu_idle & dmac_idle);
                    129: 
                    130:        ChangeMode(newmode);
                    131: }
                    132: 
                    133: // DMAC のアクティブ状態を指示する。
                    134: void
                    135: Syncer::RequestDMACActive(bool active)
                    136: {
                    137:        uint32 newmode;
                    138: 
                    139:        dmac_idle = active ? 0 : SYNCMODE_IDLE;
                    140: 
                    141:        newmode = (mode & ~SYNCMODE_IDLE) | (cpu_idle & dmac_idle);
1.1       root      142: 
                    143:        ChangeMode(newmode);
                    144: }
                    145: 
                    146: // 高速モード/同期モードの変更を指示する。
                    147: // true なら高速モード、false なら同期モード。
                    148: // 他スレッドから呼んでもよい。
                    149: void
                    150: Syncer::RequestFullSpeed(bool enable)
                    151: {
                    152:        scheduler->SendMessage(MessageID::SCHEDULER_SYNCMODE, enable);
                    153: }
                    154: 
                    155: // キー入力状態の変更を指示する。
                    156: void
                    157: Syncer::RequestKeyPressed(bool pressed)
                    158: {
                    159:        uint32 newmode;
                    160: 
                    161:        if (pressed) {
1.1.1.5   root      162:                newmode = mode | SYNCMODE_KEY;
1.1       root      163:        } else {
1.1.1.5   root      164:                newmode = mode & ~SYNCMODE_KEY;
1.1       root      165:        }
                    166: 
                    167:        ChangeMode(newmode);
                    168: }
                    169: 
                    170: // 電源状態の変更を指示する。
                    171: void
                    172: Syncer::RequestPowerOffMode(bool poweroff)
                    173: {
                    174:        uint32 newmode;
                    175: 
                    176:        if (poweroff) {
1.1.1.5   root      177:                newmode = mode | SYNCMODE_POWEROFF;
1.1       root      178:        } else {
1.1.1.5   root      179:                newmode = mode & ~SYNCMODE_POWEROFF;
1.1       root      180:        }
                    181: 
                    182:        ChangeMode(newmode);
                    183: }
                    184: 
                    185: // 動作状態変更のメッセージコールバック
                    186: void
1.1.1.2   root      187: Syncer::ChangeModeMessage(MessageID msgid, uint32 arg)
1.1       root      188: {
                    189:        uint32 newmode;
                    190: 
                    191:        if (msgid == MessageID::SCHEDULER_SYNCMODE) {
                    192:                if (arg) {
1.1.1.5   root      193:                        newmode = mode & ~SYNCMODE_SYNC;
1.1       root      194:                } else {
1.1.1.5   root      195:                        newmode = mode | SYNCMODE_SYNC;
1.1       root      196:                }
                    197: 
                    198:                ChangeMode(newmode);
                    199:        }
                    200: }
                    201: 
1.1.1.5   root      202: // newmode に従ってスケジューラの動作状態を変更。
1.1       root      203: // 自スレッドから呼ぶこと。
                    204: void
                    205: Syncer::ChangeMode(uint32 newmode)
                    206: {
                    207:        // いずれかでも変化していれば..
                    208:        if (mode != newmode) {
                    209:                // 動作モード変更要因を全部調べた結果、変更が必要か。
                    210:                // mode、newmode どちらもゼロ(高速モード)か非ゼロ(通常モード)か
                    211:                // なので、等値比較ではなく二値にしてから比較。
                    212:                if ((bool)mode != (bool)newmode) {
                    213:                        // モードが変わるのでここで一旦同期をとる
                    214:                        DoSync();
                    215: 
                    216:                        if (newmode == 0) {
                    217:                                // 高速モードに
                    218:                                putlog(2, "Fast mode");
                    219:                        } else {
                    220:                                // 同期モードに
                    221:                                putlog(2, "Sync mode");
                    222: 
                    223:                                // 同期用の基準点を設置。
                    224:                                // ゲストが STOP 命令による定常状態にいる時は
                    225:                                // 割り込みなど短期間だけ高速モードになるのを繰り返す。
                    226:                                // スリープ粒度の粗い NetBSD ホストでは同期モードに
                    227:                                // 切り替えるたびに基準点を移動していると、スリープ
                    228:                                // しすぎてしまうので、実時間と仮想時間のズレが小さい場合は
                    229:                                // 基準点を移動せず無視する。
                    230:                                uint64 vtime = scheduler->GetVirtTime();
                    231:                                uint64 rspan = rtime - rtime_epoch;
                    232:                                uint64 vspan = vtime - vtime_epoch;
                    233:                                if (vspan > rspan + overslept) {
                    234:                                        rtime_epoch = rtime;
                    235:                                        vtime_epoch = vtime;
                    236:                                }
                    237:                        }
                    238:                }
                    239: 
                    240:                // ブール比較の後で、全ビットを維持したまま代入。
                    241:                mode = newmode;
                    242:        }
                    243:        DPRINTF("ChangeMode end mode=$%x\n", mode);
                    244: }
                    245: 
                    246: // 同期イベント
                    247: void
1.1.1.7 ! root      248: Syncer::SyncCallback(Event *ev)
1.1       root      249: {
                    250:        DPRINTF("SyncCallback\n");
                    251:        DoSync();
                    252: 
                    253:        // 次回のイベントを再登録
                    254:        scheduler->StartEvent(ev);
                    255: }
                    256: 
                    257: // 同期処理
                    258: // ここで仮想時間を実時間と同期させる。
                    259: void
                    260: Syncer::DoSync()
                    261: {
                    262:        // 実時間をここで更新
                    263:        rtime = realtime.Elapsed();
                    264:        DPRINTF("Sync begin rtime=%s\n", SecToStr(rtime).c_str());
                    265: 
                    266:        // 統計情報
                    267:        sync_count++;
                    268:        // XXX どこでやる?
                    269:        if (__predict_false(rtime >= next_perf_rtime)) {
                    270:                CalcPerf();
                    271:        }
                    272: 
                    273:        // XXX 次回 Start する時のイベント間隔をここ(DoSync())で事前に決めている
1.1.1.7 ! root      274:        sync_event->time = 50_msec;
1.1       root      275: 
                    276:        // 同期モードなら..
                    277:        if (mode != 0) {
                    278:                uint64 vtime = scheduler->GetVirtTime();
                    279:                uint64 rspan = rtime - rtime_epoch;
                    280:                uint64 vspan = vtime - vtime_epoch;
                    281:                if (vspan > rspan) {
                    282:                        // 仮想時間のほうが進んでいれば、スリープして待つ
                    283:                        uint64 diff = vspan - rspan;
                    284:                        uint64 prev = rtime;
                    285: 
                    286:                        DPRINTF("Sync wait_for=%s\n", SecToStr(diff).c_str());
                    287:                        scheduler->Sleep(diff);
                    288: 
                    289:                        // スリープしすぎた時間を計測。
                    290:                        // この時間にはスリープ時間を計算するための時間も含む。
                    291:                        rtime = realtime.Elapsed();
                    292:                        DPRINTF("Sync wakeup rtime=%s\n", SecToStr(rtime).c_str());
                    293:                        uint64 realslept = rtime - prev;
                    294:                        if (realslept > diff) {
                    295:                                uint64 over = realslept - diff;
                    296:                                if (over > 1_sec) {
                    297:                                        // スリープ中にサスペンドされたと推定する
                    298:                                        putlog(1, "時刻再同期(スリープ中の実時間跳躍)");
                    299:                                        rtime_epoch = rtime;
                    300:                                        vtime_epoch = vtime;
                    301:                                } else if (over > overslept) {
                    302:                                        overslept = over;
                    303:                                }
                    304:                        } else if (realslept < diff) {
                    305:                                uint64 under = diff - realslept;
                    306:                                if (under > overslept) {
1.1.1.7 ! root      307:                                        sync_event->time = 0;
1.1       root      308:                                }
                    309:                        }
                    310:                } else if (rspan > vspan + overslept) {
                    311:                        // 実時間のほうが時間精度より進んでいれば、サスペンドとかから
                    312:                        // 復帰してきた状態だと推定して、基準時刻を再同期する。
                    313:                        putlog(1, "時刻再同期(実行中の実時間跳躍)");
                    314:                        rtime_epoch = rtime;
                    315:                        vtime_epoch = vtime;
                    316:                } else {
                    317:                        // 同期の範囲内にいるので何もしなくていい
                    318:                }
                    319:        }
                    320: }
                    321: 
                    322: void
                    323: Syncer::CalcPerf()
                    324: {
                    325:        uint64 vtime = scheduler->GetVirtTime();
                    326:        uint64 rperf = rtime - last_perf_rtime;
                    327:        uint64 vperf = vtime - last_perf_vtime;
                    328: 
                    329:        // 百分率で小数以下1桁分までとなるよう整数で計算する
                    330:        perfq.EnqueueForce((uint32)(vperf * 1000 / rperf));
                    331:        // 移動平均を求める (perfq は常に要素数分埋めてある)
1.1.1.7 ! root      332:        uint ma = perfq.GetMovingAverage();
1.1       root      333:        // 最下位を四捨五入して捨てる (整数%になる)
                    334:        ma = (ma + 5) / 10;
                    335:        perf_counter = ma;
                    336: 
                    337:        // 直近1秒の Sync 回数
                    338:        last_sync_count = sync_count;
                    339:        sync_count = 0;
                    340: 
1.1.1.3   root      341:        // 関係ないけど、ついでにここで MPU 内の各種移動平均をとる。
                    342:        if (mpu680x0) {
                    343:                mpu680x0->CalcStat();
                    344:        }
                    345: 
1.1       root      346:        last_perf_rtime = rtime;
                    347:        last_perf_vtime = vtime;
                    348:        next_perf_rtime = rtime + PERF_INTERVAL;
                    349: }
                    350: 
                    351: // パフォーマンス測定用に実時間をログ出力
                    352: void
                    353: Syncer::PutlogRealtime()
                    354: {
                    355:        uint64 vtime = scheduler->GetVirtTime();
                    356: 
                    357:        // 電源オン時からここまでの時間を表示するだけでリセット等はしない。
1.1.1.3   root      358:        putlog(0, "Elapsed since power-on: %u.%03u sec, %6.2f%%",
                    359:                (uint)(rtime / (1000 * 1000 * 1000)),
                    360:                (uint)((rtime / (1000 * 1000)) % 1000),
1.1       root      361:                (double)vtime / rtime * 100);
                    362: 
                    363: #if 0
                    364:        // イベントキューの性能評価用
                    365:        uint64 sum = 0;
                    366:        for (const auto e : events) {
                    367:                sum += e->count;
                    368:        }
                    369:        putlog(0, "%lu events done, %6.3fM/sec", sum, (double)sum / rtime * 1000);
                    370: #endif
                    371: }
                    372: 
                    373: // モニタ更新の下請け (Scheduler から呼ばれる)
                    374: int
1.1.1.6   root      375: Syncer::MonitorUpdateSub(TextScreen& screen, uint64 tmp_vtime)
1.1       root      376: {
                    377:        int y = 0;
                    378: 
1.1.1.6   root      379:        // 一応ローカルコピー。
                    380:        uint32 tmp_mode = mode;
                    381:        uint64 tmp_rtime = rtime;
                    382:        uint64 sync_rt_delta = tmp_rtime - rtime_epoch;
                    383:        uint64 sync_vt_delta = tmp_vtime - vtime_epoch;
                    384: 
                    385:        screen.Print(0, y, "Mode: 0x%04x (", tmp_mode);
                    386:        switch (tmp_mode & SYNCMODE_CPU_MASK) {
1.1.1.5   root      387:         case SYNCMODE_CPU_NORMAL:
1.1       root      388:                screen.Puts(14, y, TA::Off, "Run");
                    389:                break;
1.1.1.5   root      390:         case SYNCMODE_CPU_STOP:
1.1       root      391:                screen.Puts(14, y, TA::On, "STOP");
                    392:                break;
1.1.1.5   root      393:         case SYNCMODE_CPU_HALT:
1.1       root      394:                screen.Puts(14, y, TA::On, "HALT");
                    395:                break;
                    396:        }
1.1.1.6   root      397:        if ((tmp_mode & SYNCMODE_SYNC)) {
1.1       root      398:                screen.Puts(19, y, TA::On, "Sync");
                    399:        } else {
                    400:                screen.Puts(19, y, TA::Off, "Full");
                    401:        }
1.1.1.7 ! root      402:        screen.Puts(24, y, TA::OnOff((tmp_mode & SYNCMODE_KEY)), "Key");
        !           403:        screen.Puts(29, y, TA::OnOff((tmp_mode & SYNCMODE_POWEROFF)), "POff");
        !           404:        screen.Puts(33, y, ")");
1.1       root      405:        y++;
                    406: 
                    407:        // 日まで表示しようとするときは桁位置を動かす
1.1.1.6   root      408:        int width = std::max(tmp_rtime, tmp_vtime) < 86400_sec ? 20 : 25;
1.1       root      409: 
1.1.1.6   root      410:        std::string trt = TimeToStr(tmp_rtime);
                    411:        std::string tvt = TimeToStr(tmp_vtime);
                    412:        //                     012345678901234567
                    413:        screen.Print(0, y++,  "Total  Real Time: %*s", width, trt.c_str());
                    414:        screen.Print(4, y++,      "Virtual Time: %*s", width, tvt.c_str());
                    415:        screen.Puts(0, y + 0, "Sync   Real Time:");
                    416:        screen.Puts(4, y + 1,     "Virtual Time:");
                    417:        if (tmp_mode != 0) {
                    418:                std::string srt = TimeToStr(sync_rt_delta);
                    419:                std::string svt = TimeToStr(sync_vt_delta);
                    420:                screen.Print(18, y + 0, "%*s", width, srt.c_str());
                    421:                screen.Print(18, y + 1, "%*s", width, svt.c_str());
                    422:        }
                    423:        y += 2;
1.1       root      424: 
1.1.1.3   root      425:        screen.Puts(0, y, "Rating MPU Speed   :");
                    426:        if (__predict_true((clock_khz % 1000) == 0)) {
                    427:                screen.Print(21, y++, "%4u.%1uMHz",
                    428:                        clock_khz / 1000, (clock_khz / 100) % 10);
                    429:        } else {
                    430:                screen.Print(21, y++, "%4u.%03uMHz",
                    431:                        clock_khz / 1000, clock_khz % 1000);
                    432:        }
                    433:        uint eff_khz = clock_khz * perf_counter / 100;
                    434:        screen.Print(0, y++, "Effective MPU Speed: %4u.%1uMHz (%4u%%)",
                    435:                (eff_khz / 1000), (eff_khz / 100) % 10, perf_counter);
1.1       root      436: 
                    437:        int x = 52;
                    438:        int y1 = 0;
                    439:        screen.Print(x, y1++, "Max OverSlept: %13s", SecToStr(overslept).c_str());
                    440:        screen.Print(x, y1++, "Sync per sec :          %4u", last_sync_count);
                    441: 
                    442:        return y;
                    443: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.