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

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

unix.superglobalmegacorp.com

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