Annotation of nono/vm/scheduler.cpp, revision 1.1.1.8

1.1       root        1: //
                      2: // nono
1.1.1.4   root        3: // Copyright (C) 2020 nono project
                      4: // Licensed under nono-license.txt
1.1       root        5: //
                      6: 
1.1.1.4   root        7: #include "scheduler.h"
                      8: #include "config.h"
1.1.1.3   root        9: #include "mainapp.h"
1.1       root       10: #include "mfp.h"
1.1.1.4   root       11: #include "mpu680x0.h"
                     12: #include "mpu88xx0.h"
                     13: #include "mythread.h"
1.1       root       14: #include "rtc.h"
                     15: #include "vm.h"
                     16: 
                     17: // VM スケジューラは、仮想時間とイベントを管理する。
                     18: //
1.1.1.7   root       19: // 仮想時間は起動時からの nsec で表し、MPU デバイス (実際はその向こうの
                     20: // CPU コア) が管理している。VM 界からは gMPU->GetVirtTime() で取得する。
1.1       root       21: 
                     22: static void *scheduler_run(void *arg);
                     23: 
1.1.1.3   root       24: std::unique_ptr<Scheduler> gScheduler;
1.1       root       25: 
                     26: // コンストラクタ
                     27: Scheduler::Scheduler()
                     28: {
                     29:        logname = "scheduler";
                     30:        devname = "Scheduler";
                     31: 
                     32:        eventlist.clear();
                     33: }
                     34: 
                     35: // デストラクタ
                     36: Scheduler::~Scheduler()
                     37: {
1.1.1.3   root       38:        Terminate();
1.1       root       39: }
                     40: 
                     41: // 初期化
                     42: bool
                     43: Scheduler::Init()
                     44: {
1.1.1.3   root       45:        // 起動時引数指定の高速モード
                     46:        SetFullSpeed(gMainApp.fast_mode);
1.1       root       47: 
                     48:        // スケジューラ(VM)スレッド起動
1.1.1.3   root       49:        pthread_create(&thread, NULL, scheduler_run, NULL);
                     50:        thread_created = true;
1.1       root       51: 
                     52:        return true;
                     53: }
                     54: 
                     55: // スケジューラを開始する
                     56: void
                     57: Scheduler::Run()
                     58: {
                     59:        std::unique_lock<std::mutex> lock(cvmtx);
                     60:        atomic_reqflag |= REQ_POWER_ON;
                     61:        cv.notify_one();
                     62: }
                     63: 
                     64: // スレッドエントリ関数
                     65: void *
                     66: scheduler_run(void *arg)
                     67: {
1.1.1.4   root       68:        PTHREAD_SETNAME("VM");
1.1       root       69: 
                     70:        gScheduler->ThreadRun();
                     71:        return NULL;
                     72: }
                     73: 
                     74: // スレッドエントリ関数(の実体)
                     75: void
                     76: Scheduler::ThreadRun()
                     77: {
1.1.1.3   root       78:        uint32 req;
1.1       root       79: 
                     80:        // 最初の電源オンを待つ
1.1.1.3   root       81:        for (;;) {
1.1       root       82:                std::unique_lock<std::mutex> lock(cvmtx);
1.1.1.3   root       83:                cv.wait(lock, [&] { return (atomic_reqflag != 0); });
                     84:                req = atomic_reqflag.load();
                     85:                // 終了
                     86:                if ((req & REQ_EXIT)) {
                     87:                        return;
                     88:                }
1.1.1.8 ! root       89:                // REQ_POWER_ON ならこれだけ下げて抜ける。
        !            90:                // 他のフラグはメインループで処理するため維持。
1.1.1.3   root       91:                if ((req & REQ_POWER_ON)) {
                     92:                        req &= ~REQ_POWER_ON;
                     93:                        atomic_reqflag.store(req);
                     94:                        break;
                     95:                }
1.1       root       96:        }
                     97:        // ここからは電源オン
                     98: 
                     99:        // RTC 時刻だけは最初から必要。
                    100:        rtimestart = GetRealTime();
                    101:        rtc_last_clock = rtimestart;
                    102: 
                    103:        mode = 0;
                    104:        for (;;) {
                    105:                req = atomic_reqflag.exchange(0);
1.1.1.8 ! root      106:                if (__predict_false(req != 0)) {
        !           107:                        uint32 newmode = mode;
        !           108: 
        !           109:                        // 動作モード変更要因を一つずつ調べる
        !           110:                        if ((req & REQ_CPU_MODE)) {
        !           111:                                // CPU 状態が new_cpumode に変更になった
        !           112:                                newmode &= ~SCHED_CPU_MASK;
        !           113:                                newmode |= new_cpumode;
        !           114:                                req &= ~REQ_CPU_MODE;
        !           115:                        }
        !           116:                        if ((req & REQ_USER_MODE)) {
        !           117:                                // ユーザからの高速/同期モード指示が変更になった
        !           118:                                newmode &= ~SCHED_SYNC;
        !           119:                                newmode |= new_usermode;
        !           120:                                req &= ~REQ_USER_MODE;
        !           121:                        }
        !           122:                        // 動作モード変更要因を全部調べた結果、変更が必要か。
        !           123:                        // mode、newmode どちらもゼロ(高速モード)か非ゼロ(通常モード)か
        !           124:                        // なので、等値比較ではなく二値にしてから比較。
        !           125:                        if ((bool)newmode != (bool)mode) {
        !           126:                                // 動作モード変更 (ここは全ビットを維持して代入)
        !           127:                                mode = newmode;
        !           128: 
        !           129:                                if (mode == 0) {
        !           130:                                        // 高速モードに
        !           131:                                        putlog(1, "高速モード");
        !           132: 
        !           133:                                        // 高速モードでは RAM は直接アクセス
        !           134:                                        ::direct_ram_size = gRAM->GetSize();
        !           135:                                } else {
        !           136:                                        // 同期モードに
        !           137:                                        putlog(1, "同期モード");
        !           138: 
        !           139:                                        // 同期モードでは RAM アクセスはデバイス経由
        !           140:                                        ::direct_ram_size = 0;
        !           141:                                }
1.1       root      142: 
                    143:                                // 基準時刻をリセット
                    144:                                rtimebase = ::GetRealTime();
1.1.1.7   root      145:                                vtimebase = gMPU->GetVirtTime();
1.1       root      146:                        }
                    147: 
                    148:                        if ((req & REQ_POWER_OFF)) {
                    149:                                // 電源オフ要求の場合
                    150: 
                    151:                                // 全デバイス電源オフ
                    152:                                gVM->DevicePowerOff();
                    153: 
                    154:                                req &= ~REQ_POWER_OFF;
                    155:                        }
                    156: 
1.1.1.6   root      157:                        if ((req & REQ_RESET)) {
                    158:                                // リセット要求の場合
                    159: 
                    160:                                // 全デバイスをリセット
                    161:                                gVM->ResetHard();
                    162: 
                    163:                                req &= ~REQ_RESET;
                    164:                        }
                    165: 
1.1       root      166:                        if ((req & REQ_EXIT)) {
                    167:                                // 終了要求
                    168:                                // この for ブロックをいきなり抜けるだけなので
                    169:                                // もうフラグの値も関係ないのだが一応。
                    170:                                req &= ~REQ_EXIT;
                    171:                                break;
                    172:                        }
                    173: 
                    174:                        // 電源オン要求は来ないはず
1.1.1.3   root      175:                        assertmsg(req == 0, "req=$%x", req);
1.1       root      176:                }
                    177: 
                    178:                // RTC は常にホスト時刻で動いており
                    179:                // 32Hz = 31.25msec ごとにパルスを入れる。
                    180:                while (::GetRealTime() > rtc_last_clock + 31.25_msec) {
                    181:                        gRTC->ClockIn();
                    182:                        rtc_last_clock += 31.25_msec;
                    183:                }
                    184: 
1.1.1.6   root      185:                // イベントリストを調べ現時点から最も早く発生するイベントまでの必要
1.1.1.7   root      186:                // 時間を求めて、この時間分だけ MPU を駆動する。
1.1.1.6   root      187:                // MPU 実行中にデバイスアクセスなどによってスケジューラにイベントが
                    188:                // 登録されると、その命令の終了とともに処理を打ち切って戻ってくる。
1.1.1.7   root      189:                // また、電源オン(リセット)時には MPU デバイスが 0nsec で MPU の
                    190:                // リセット例外イベントを起こすようになっている。
                    191: 
                    192:                // delta_vtime が今回進める仮想時間。
                    193:                // とりあえず初期値を仮置きしとくけど、イベントは通常 20msec 以内には
                    194:                // 必ず起きるので (例えば LUNA の sysclk は 10 or 16 msec 周期)、
                    195:                // この初期値は実質上限値。20msec 程度より大きければいくらでもよくて
                    196:                // 100msec とかでいいんだけど、二進数で置数しやすいよう
                    197:                // 0x200'0000 (約33.5msec) にしておく。
                    198:                uint64 delta_vtime = 0x200'0000;
1.1       root      199: 
                    200:                // イベント
1.1.1.7   root      201:                uint64 vtime = gMPU->GetVirtTime();
1.1       root      202:                for (;;) {
1.1.1.6   root      203:                        Event *e;
                    204:                        // ロック区間
                    205:                        {
                    206:                                std::lock_guard<std::mutex> lock(evcs);
                    207: 
                    208:                                if (eventlist.empty()) {
                    209:                                        break;
                    210:                                }
                    211:                                e = eventlist.front();
1.1.1.7   root      212:                                if (e->vtime > vtime) {
1.1.1.6   root      213:                                        // まだ時刻に到達していない
1.1.1.7   root      214:                                        delta_vtime = e->vtime - vtime;
1.1.1.6   root      215:                                        break;
                    216:                                }
                    217: 
                    218:                                // 到達したのでこのイベントをリストから削除
                    219:                                e->active = false;
                    220:                                eventlist.pop_front();
1.1       root      221:                        }
                    222: 
                    223:                        // コールバック
1.1.1.8 ! root      224:                        e->dev->putlogf(3, lstr("イベント '%s' 時刻到達",
        !           225:                                e->GetName().c_str()));
1.1.1.6   root      226:                        ((e->dev)->*(e->func))(*e);
1.1       root      227: 
                    228:                        // 同時刻のイベントがあるかも知れないのでなくなるまで調べる
                    229:                }
1.1.1.6   root      230: 
1.1.1.7   root      231:                // CPU を駆動
1.1.1.8 ! root      232:                new_cpumode = gMPU->Run(delta_vtime);
1.1.1.7   root      233: 
                    234:                // CPU 駆動によって進んだ仮想時刻を再取得
                    235:                vtime = gMPU->GetVirtTime();
1.1       root      236: 
                    237:                // 同期モードなら実時間調整。
                    238:                // あるいは高速モードであってもストップ状態なら実時間駆動する。
                    239:                if (mode != 0) {
                    240:                        // 通常モード
1.1.1.7   root      241:                        uint64 vspan;
                    242:                        uint64 rspan;
1.1       root      243: 
                    244:                        // 基準時からの経過時間
1.1.1.7   root      245:                        vspan = vtime         - vtimebase;
                    246:                        rspan = GetRealTime() - rtimebase;
1.1       root      247: 
1.1.1.7   root      248:                        if (vspan > rspan) {
1.1       root      249:                                // 仮想時間のほうが進んでいれば、スリープして待つ
1.1.1.7   root      250:                                uint64 diff = vspan - rspan;
1.1       root      251:                                struct timespec ts;
                    252:                                ts.tv_sec  = diff / (1000 * 1000 * 1000);
                    253:                                ts.tv_nsec = diff % (1000 * 1000 * 1000);
                    254:                                nanosleep(&ts, NULL);
                    255:                        } else {
                    256:                                // 実時間のほうが進んでいれば、間に合ってない
                    257: 
                    258:                                // XXX ここで間引き運転とか
                    259:                        }
                    260:                }
                    261: 
1.1.1.8 ! root      262:                // CPU 側の走行状態が変わったら、このループに通知
        !           263:                if (__predict_false(((mode ^ new_cpumode) & SCHED_CPU_MASK))) {
        !           264:                        atomic_reqflag |= REQ_CPU_MODE;
1.1       root      265:                }
                    266:        }
                    267: }
                    268: 
1.1.1.3   root      269: // 必要ならスレッドの終了を指示して完了まで待つ。
1.1       root      270: void
                    271: Scheduler::Terminate()
                    272: {
1.1.1.3   root      273:        if (thread_created) {
1.1.1.8 ! root      274:                RequestExit();
1.1.1.3   root      275:                pthread_join(thread, NULL);
                    276:                thread_created = false;
                    277:        }
1.1       root      278: }
                    279: 
1.1.1.8 ! root      280: // スレッド終了を指示する。
        !           281: void
        !           282: Scheduler::RequestExit()
        !           283: {
        !           284:        std::unique_lock<std::mutex> lock(cvmtx);
        !           285:        atomic_reqflag |= REQ_EXIT;
        !           286:        cv.notify_one();
        !           287: }
        !           288: 
1.1       root      289: // 動作モードを設定する。
                    290: // true なら高速モード、false なら同期モード。
                    291: void
                    292: Scheduler::SetFullSpeed(bool enable)
                    293: {
                    294:        if (enable) {
1.1.1.8 ! root      295:                new_usermode = 0;
1.1       root      296:        } else {
1.1.1.8 ! root      297:                new_usermode = SCHED_SYNC;
1.1       root      298:        }
1.1.1.8 ! root      299:        atomic_reqflag |= REQ_USER_MODE;
1.1       root      300: }
                    301: 
1.1.1.3   root      302: // 指定のイベントを開始する。
1.1       root      303: // すでに同イベントが登録されている場合は古いイベントを削除してから
                    304: // 新しいイベントを再登録となる。
                    305: // イベントはワンショットのみ。
                    306: void
1.1.1.3   root      307: Scheduler::StartEvent(Event *newev)
1.1       root      308: {
                    309:        bool inserted = false;
                    310:        bool updated = false;
                    311: 
1.1.1.7   root      312:        // time は仮想相対時間 [nsec]、vtime は仮想絶対時刻 [nsec]
                    313:        newev->vtime = gMPU->GetVirtTime() + newev->time;
1.1       root      314: 
                    315:        evcs.lock();
                    316:        // すでにあれば削除
                    317:        if (newev->active) {
                    318:                for (auto it = eventlist.begin(); it != eventlist.end(); ++it) {
                    319:                        Event *e = *it;
                    320:                        if (e == newev) {
                    321:                                eventlist.erase(it);
                    322:                                updated = true;
                    323:                                break;
                    324:                        }
                    325:                }
                    326:        }
                    327:        // ソートされているところに自身を挿入
                    328:        for (auto it = eventlist.begin(); it != eventlist.end(); ++it) {
                    329:                Event *e = *it;
1.1.1.7   root      330:                if (newev->vtime <= e->vtime) {
1.1       root      331:                        // このイベントの前に入れる
                    332:                        eventlist.insert(it, newev);
                    333:                        inserted = true;
                    334:                        break;
                    335:                }
                    336:        }
                    337:        if (inserted == false) {
                    338:                // 最後に追加
                    339:                eventlist.push_back(newev);
                    340:        }
                    341:        newev->active = true;
                    342:        evcs.unlock();
                    343: 
                    344:        gMPU->Release();
                    345: 
1.1.1.8 ! root      346:        newev->dev->putlogf(3, lstr("イベント '%s' %s %d.%03d usec 後",
1.1.1.2   root      347:                newev->GetName().c_str(),
1.1.1.3   root      348:                updated ? "更新" : "開始",
1.1       root      349:                (int)(newev->time / 1000),
1.1.1.8 ! root      350:                (int)(newev->time % 1000)));
1.1       root      351: }
                    352: 
1.1.1.3   root      353: // 指定のイベントを停止する。
1.1       root      354: // 指定されたイベントが登録されていなければ何もしない。
                    355: void
1.1.1.3   root      356: Scheduler::StopEvent(Event *event)
1.1       root      357: {
                    358:        bool found = false;
                    359: 
                    360:        evcs.lock();
                    361:        for (auto it = eventlist.begin(); it != eventlist.end(); ++it) {
                    362:                Event *e = *it;
                    363:                if (e == event) {
                    364:                        e->active = false;
                    365:                        eventlist.erase(it);
                    366:                        found = true;
                    367:                        break;
                    368:                }
                    369:        }
                    370:        evcs.unlock();
                    371: 
1.1.1.3   root      372:        // イベントを停止した場合は MPU の実行中断はしなくてよい。
1.1       root      373:        // 1msec 後にイベントを追加した後、やっぱりそのイベントを取り消した
                    374:        // 場合 (LUNA の電源オフとか) はここで MPU 処理を中断するよりも
                    375:        // そのまま 1msec 走って問題ない。
                    376: 
                    377:        if (found) {
1.1.1.8 ! root      378:                event->dev->putlogf(3, lstr("イベント  '%s' 停止",
        !           379:                        event->GetName().c_str()));
1.1       root      380:        }
                    381: }
                    382: 
                    383: // 経過時間 t を文字列にして返す。
                    384: static const std::string
                    385: TimeToStr(uint64 t)
                    386: {
                    387:        char buf[32];
                    388:        char *p;
                    389:        size_t len;
                    390:        int n;
                    391: 
                    392:        uint ns = t % 1000;
                    393:        t /= 1000;
                    394:        uint us = t % 1000;
                    395:        t /= 1000;
                    396:        uint ms = t % 1000;
                    397:        t /= 1000;
                    398:        uint s = t % 60;
                    399:        t /= 60;
                    400:        uint m = t % 60;
                    401:        t /= 60;
                    402:        uint h = t;
                    403: 
                    404:        p = buf;
                    405:        len = sizeof(buf);
                    406:        if (h) {
                    407:                n = snprintf(p, len, "%d:%02d:%02d", h, m, s);
                    408:                p += n;
                    409:                len -= n;
                    410:        } else if (m) {
                    411:                n = snprintf(p, len, "%d:%02d", m, s);
                    412:                p += n;
                    413:                len -= n;
                    414:        } else {
                    415:                n = snprintf(p, len, "%d", s);
                    416:                p += n;
                    417:                len -= n;
                    418:        }
                    419:        n = snprintf(p, len, ".%03d'%03d'%03d", ms, us, ns);
                    420:        p += n;
                    421:        len -= n;
                    422: 
                    423:        return std::string(buf, p - buf);
                    424: }
                    425: 
1.1.1.5   root      426: // モニターサイズ取得
                    427: nnSize
                    428: Scheduler::GetMonitorSize()
                    429: {
                    430:        // 本当は Init() 完了時点でイベント個数は確定しているはずだけど、
                    431:        // そのタイミングで一度だけ呼ばれるフックが今の所ない。
                    432:        return nnSize(68, 11 + gEvents.size());
                    433: }
                    434: 
                    435: void
                    436: Scheduler::MonitorUpdate(TextScreen& monitor)
1.1       root      437: {
                    438:        int x, y;
                    439: 
                    440:        monitor.Clear();
                    441:        x = 0;
                    442:        y = 0;
                    443: 
1.1.1.7   root      444:        int clock_khz = gMPU->GetClockSpeed();
                    445: 
1.1.1.8 ! root      446:        monitor.Print(0, y, "Mode: 0x%04x (", mode);
        !           447:        if ((mode & SCHED_SYNC)) {
        !           448:                monitor.Puts(14, y, TA::On, "Sync");
        !           449:        } else {
        !           450:                monitor.Puts(14, y, "Full");
        !           451:        }
        !           452:        switch (mode & SCHED_CPU_MASK) {
        !           453:         case SCHED_CPU_NORMAL:
        !           454:                monitor.Puts(19, y, "Run");
        !           455:                break;
        !           456:         case SCHED_CPU_STOP:
        !           457:                monitor.Puts(19, y, TA::On, "STOP");
        !           458:                break;
        !           459:         case SCHED_CPU_HALT:
        !           460:                monitor.Puts(19, y, TA::On, "HALT");
        !           461:                break;
        !           462:        }
        !           463:        monitor.Puts(23, y, ")");
        !           464:        y++;
1.1       root      465:        monitor.Print(0, y++, "Req: %08x", (uint32)atomic_reqflag);
                    466:        monitor.Print(0, y++, "MPU Speed: %d.%03dMHz",
1.1.1.7   root      467:                (clock_khz / 1000), (clock_khz % 1000));
                    468:        uint64 vtime = gMPU->GetVirtTime();
                    469:        uint64 rtime = GetRealTime();
                    470:        uint64 vspan = vtime - vtimebase;
                    471:        uint64 rspan = rtime - rtimebase;
1.1       root      472:        // RealTime は実時間なのでスケジューラ開始からの時間に変換
1.1.1.7   root      473:        rtime -= rtimestart;
                    474:        monitor.Print(0, y++, "Total  Real Time: %18s", TimeToStr(rtime).c_str());
                    475:        monitor.Print(0, y++, "    Virtual Time: %18s", TimeToStr(vtime).c_str());
                    476:        double ratio = (double)vtime / rtime * 100;
1.1       root      477:        monitor.Print(0, y++, "    Ratio       : %3d.%01d%%",
                    478:                (int)ratio, ((int)(ratio * 10) % 10));
1.1.1.7   root      479:        monitor.Print(0, y++, "Moment Real Time: %18s", TimeToStr(rspan).c_str());
                    480:        monitor.Print(0, y++, "    Virtual Time: %18s", TimeToStr(vspan).c_str());
1.1       root      481:        // XXX 移動平均をとる
1.1.1.7   root      482:        double relratio = (double)vspan / rspan * 100;
1.1       root      483:        monitor.Print(0, y++, "    Ratio       : %3d.%01d%%",
                    484:                (int)relratio, ((int)(relratio * 10) % 10));
                    485: 
                    486:        // 0         1         2         3         4         5         6
1.1.1.2   root      487:        // 0123456789012345678901234567890123456789012345678901234567890123456
                    488:        // Set Time        Remain Time     Description               Code
                    489:        //   3.123'456'789   3.123'456'789 0123456789012345678901234 $01234567
1.1       root      490:        x = 0;
                    491:        y++;
1.1.1.4   root      492:        monitor.Puts(x, y, "Set Time");
                    493:        monitor.Puts(x + 16, y, "Remain Time");
                    494:        monitor.Puts(x + 32, y, "Description");
                    495:        monitor.Puts(x + 58, y, "Code");
1.1       root      496:        y++;
1.1.1.6   root      497: 
1.1       root      498:        for (const auto& e : gEvents) {
                    499:                uint64 rem;
1.1.1.4   root      500:                TA attr;
1.1       root      501:                if (e->active) {
1.1.1.4   root      502:                        attr = TA::Normal;
1.1.1.7   root      503:                        if (e->vtime > vtime) {
                    504:                                rem = e->vtime - vtime;
1.1.1.6   root      505:                        } else {
                    506:                                // XXX 実際には起きないけど、この表示をするにあたって
                    507:                                // スケジューラスレッドと一切調停していないので、仮想時刻が
                    508:                                // e->cycle (イベント発生時刻) をすぎていることがある。
                    509:                                // すぎてるので残り時間 0 と表示しておく。
                    510:                                rem = 0;
                    511:                        }
1.1       root      512:                } else {
                    513:                        attr = TA::Disable;
                    514:                        rem = 0;
                    515:                }
                    516:                monitor.Print(x, y++, attr,
1.1.1.2   root      517:                        "%3u.%03u'%03u'%03u %3u.%03u'%03u'%03u %-25s $%08x",
1.1       root      518:                        (uint)(e->time / (1000 * 1000 * 1000)),
                    519:                        (uint)((e->time / 1000 / 1000) % 1000),
                    520:                        (uint)((e->time / 1000) % 1000),
                    521:                        (uint)(e->time % 1000),
                    522:                        (uint)(rem / (1000 * 1000 * 1000)),
                    523:                        (uint)((rem / 1000 / 1000) % 1000),
                    524:                        (uint)((rem / 1000) % 1000),
                    525:                        (uint)(rem % 1000),
1.1.1.2   root      526:                        e->GetName().c_str(),
1.1       root      527:                        e->code);
                    528:        }
                    529: }

unix.superglobalmegacorp.com

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