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

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:                }
                     89:                // REQ_POWER_ON なら他のフラグを書き戻して抜ける。
                     90:                // REQ_{SYNC|FAST} は以降のメインループで処理するため。
                     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);
                    106:                if (req) {
                    107:                        if ((req & REQ_SYNC)) {
                    108:                                // スケジューラを同期モードに
                    109:                                mode |= SCHED_SYNC;
1.1.1.6   root      110:                                // 同期モードでは RAM アクセスはデバイス経由
                    111:                                ::direct_ram_size = 0;
1.1       root      112:                        }
                    113:                        if ((req & REQ_FAST)) {
                    114:                                // スケジューラを高速モードに
                    115:                                mode &= ~SCHED_SYNC;
1.1.1.6   root      116:                                // 高速モードでは RAM は直接アクセス
                    117:                                ::direct_ram_size = ::ram_size;
1.1       root      118:                        }
                    119:                        if ((req & REQ_RUN)) {
                    120:                                // MPU が通常状態になった
                    121:                                mode &= ~SCHED_STOP;
                    122:                        }
                    123:                        if ((req & REQ_STOP)) {
                    124:                                // MPU が STOP 状態になった
                    125:                                mode |= SCHED_STOP;
                    126:                        }
                    127:                        if ((req & REQ_MODEMASK)) {
                    128:                                // mode がどれかにでも変わったら
                    129:                                static const char *modestr[] = {
                    130:                                        "高速",
                    131:                                        "高速 & STOP状態",
                    132:                                        "通常",
                    133:                                        "通常 & STOP状態",
                    134:                                };
                    135:                                putlog(1, "モード変更 mode=%d (%s)",  mode, modestr[mode]);
                    136: 
                    137:                                // 基準時刻をリセット
                    138:                                rtimebase = ::GetRealTime();
1.1.1.7 ! root      139:                                vtimebase = gMPU->GetVirtTime();
1.1       root      140: 
                    141:                                // 該当ビットを落としておく
                    142:                                req &= ~REQ_MODEMASK;
                    143:                        }
                    144: 
                    145:                        if ((req & REQ_POWER_OFF)) {
                    146:                                // 電源オフ要求の場合
                    147: 
                    148:                                // 全デバイス電源オフ
                    149:                                gVM->DevicePowerOff();
                    150: 
                    151:                                req &= ~REQ_POWER_OFF;
                    152:                        }
                    153: 
1.1.1.6   root      154:                        if ((req & REQ_RESET)) {
                    155:                                // リセット要求の場合
                    156: 
                    157:                                // 全デバイスをリセット
                    158:                                gVM->ResetHard();
                    159: 
                    160:                                req &= ~REQ_RESET;
                    161:                        }
                    162: 
1.1       root      163:                        if ((req & REQ_EXIT)) {
                    164:                                // 終了要求
                    165:                                // この for ブロックをいきなり抜けるだけなので
                    166:                                // もうフラグの値も関係ないのだが一応。
                    167:                                req &= ~REQ_EXIT;
                    168:                                break;
                    169:                        }
                    170: 
                    171:                        // 電源オン要求は来ないはず
1.1.1.3   root      172:                        assertmsg(req == 0, "req=$%x", req);
1.1       root      173:                }
                    174: 
                    175:                // RTC は常にホスト時刻で動いており
                    176:                // 32Hz = 31.25msec ごとにパルスを入れる。
                    177:                while (::GetRealTime() > rtc_last_clock + 31.25_msec) {
                    178:                        gRTC->ClockIn();
                    179:                        rtc_last_clock += 31.25_msec;
                    180:                }
                    181: 
1.1.1.6   root      182:                // イベントリストを調べ現時点から最も早く発生するイベントまでの必要
1.1.1.7 ! root      183:                // 時間を求めて、この時間分だけ MPU を駆動する。
1.1.1.6   root      184:                // MPU 実行中にデバイスアクセスなどによってスケジューラにイベントが
                    185:                // 登録されると、その命令の終了とともに処理を打ち切って戻ってくる。
1.1.1.7 ! root      186:                // また、電源オン(リセット)時には MPU デバイスが 0nsec で MPU の
        !           187:                // リセット例外イベントを起こすようになっている。
        !           188: 
        !           189:                // delta_vtime が今回進める仮想時間。
        !           190:                // とりあえず初期値を仮置きしとくけど、イベントは通常 20msec 以内には
        !           191:                // 必ず起きるので (例えば LUNA の sysclk は 10 or 16 msec 周期)、
        !           192:                // この初期値は実質上限値。20msec 程度より大きければいくらでもよくて
        !           193:                // 100msec とかでいいんだけど、二進数で置数しやすいよう
        !           194:                // 0x200'0000 (約33.5msec) にしておく。
        !           195:                uint64 delta_vtime = 0x200'0000;
1.1       root      196: 
                    197:                // イベント
1.1.1.7 ! root      198:                uint64 vtime = gMPU->GetVirtTime();
1.1       root      199:                for (;;) {
1.1.1.6   root      200:                        Event *e;
                    201:                        // ロック区間
                    202:                        {
                    203:                                std::lock_guard<std::mutex> lock(evcs);
                    204: 
                    205:                                if (eventlist.empty()) {
                    206:                                        break;
                    207:                                }
                    208:                                e = eventlist.front();
1.1.1.7 ! root      209:                                if (e->vtime > vtime) {
1.1.1.6   root      210:                                        // まだ時刻に到達していない
1.1.1.7 ! root      211:                                        delta_vtime = e->vtime - vtime;
1.1.1.6   root      212:                                        break;
                    213:                                }
                    214: 
                    215:                                // 到達したのでこのイベントをリストから削除
                    216:                                e->active = false;
                    217:                                eventlist.pop_front();
1.1       root      218:                        }
                    219: 
                    220:                        // コールバック
1.1.1.3   root      221:                        e->dev->putlog(3, "イベント '%s' 時刻到達", e->GetName().c_str());
1.1.1.6   root      222:                        ((e->dev)->*(e->func))(*e);
1.1       root      223: 
                    224:                        // 同時刻のイベントがあるかも知れないのでなくなるまで調べる
                    225:                }
1.1.1.6   root      226: 
1.1.1.7 ! root      227:                // CPU を駆動
1.1.1.6   root      228:                uint32 outer;
1.1.1.7 ! root      229:                outer = gMPU->Run(delta_vtime);
        !           230: 
        !           231:                // CPU 駆動によって進んだ仮想時刻を再取得
        !           232:                vtime = gMPU->GetVirtTime();
1.1       root      233: 
                    234:                // 同期モードなら実時間調整。
                    235:                // あるいは高速モードであってもストップ状態なら実時間駆動する。
                    236:                if (mode != 0) {
                    237:                        // 通常モード
1.1.1.7 ! root      238:                        uint64 vspan;
        !           239:                        uint64 rspan;
1.1       root      240: 
                    241:                        // 基準時からの経過時間
1.1.1.7 ! root      242:                        vspan = vtime         - vtimebase;
        !           243:                        rspan = GetRealTime() - rtimebase;
1.1       root      244: 
1.1.1.7 ! root      245:                        if (vspan > rspan) {
1.1       root      246:                                // 仮想時間のほうが進んでいれば、スリープして待つ
1.1.1.7 ! root      247:                                uint64 diff = vspan - rspan;
1.1       root      248:                                struct timespec ts;
                    249:                                ts.tv_sec  = diff / (1000 * 1000 * 1000);
                    250:                                ts.tv_nsec = diff % (1000 * 1000 * 1000);
                    251:                                nanosleep(&ts, NULL);
                    252:                        } else {
                    253:                                // 実時間のほうが進んでいれば、間に合ってない
                    254: 
                    255:                                // XXX ここで間引き運転とか
                    256:                        }
                    257:                }
                    258: 
                    259:                // CPU の STOP 状態をこっちのモードに反映。
                    260:                // このために SCHED_STOP と CPU_REQ_STOP は同じビット位置にしてある。
1.1.1.4   root      261:                // CPU の HALT はスケジューラ的には STOP と同じ扱いでいい。
1.1       root      262:                if (((mode ^ outer) & SCHED_STOP)) {
                    263:                        // 状態変更要求
1.1.1.4   root      264:                        if ((outer & (CPU_REQ_STOP | CPU_REQ_HALT)) != 0) {
1.1       root      265:                                atomic_reqflag |= REQ_STOP;
                    266:                        } else {
                    267:                                atomic_reqflag |= REQ_RUN;
                    268:                        }
                    269:                }
                    270:        }
                    271: }
                    272: 
1.1.1.3   root      273: // 必要ならスレッドの終了を指示して完了まで待つ。
1.1       root      274: void
                    275: Scheduler::Terminate()
                    276: {
1.1.1.3   root      277:        if (thread_created) {
                    278:                atomic_reqflag |= REQ_EXIT;
                    279:                pthread_join(thread, NULL);
                    280:                thread_created = false;
                    281:        }
1.1       root      282: }
                    283: 
                    284: // 動作モードを設定する。
                    285: // true なら高速モード、false なら同期モード。
                    286: void
                    287: Scheduler::SetFullSpeed(bool enable)
                    288: {
                    289:        if (enable) {
                    290:                atomic_reqflag |= REQ_FAST;
                    291:        } else {
                    292:                atomic_reqflag |= REQ_SYNC;
                    293:        }
                    294: }
                    295: 
1.1.1.3   root      296: // 指定のイベントを開始する。
1.1       root      297: // すでに同イベントが登録されている場合は古いイベントを削除してから
                    298: // 新しいイベントを再登録となる。
                    299: // イベントはワンショットのみ。
                    300: void
1.1.1.3   root      301: Scheduler::StartEvent(Event *newev)
1.1       root      302: {
                    303:        bool inserted = false;
                    304:        bool updated = false;
                    305: 
1.1.1.7 ! root      306:        // time は仮想相対時間 [nsec]、vtime は仮想絶対時刻 [nsec]
        !           307:        newev->vtime = gMPU->GetVirtTime() + newev->time;
1.1       root      308: 
                    309:        evcs.lock();
                    310:        // すでにあれば削除
                    311:        if (newev->active) {
                    312:                for (auto it = eventlist.begin(); it != eventlist.end(); ++it) {
                    313:                        Event *e = *it;
                    314:                        if (e == newev) {
                    315:                                eventlist.erase(it);
                    316:                                updated = true;
                    317:                                break;
                    318:                        }
                    319:                }
                    320:        }
                    321:        // ソートされているところに自身を挿入
                    322:        for (auto it = eventlist.begin(); it != eventlist.end(); ++it) {
                    323:                Event *e = *it;
1.1.1.7 ! root      324:                if (newev->vtime <= e->vtime) {
1.1       root      325:                        // このイベントの前に入れる
                    326:                        eventlist.insert(it, newev);
                    327:                        inserted = true;
                    328:                        break;
                    329:                }
                    330:        }
                    331:        if (inserted == false) {
                    332:                // 最後に追加
                    333:                eventlist.push_back(newev);
                    334:        }
                    335:        newev->active = true;
                    336:        evcs.unlock();
                    337: 
                    338:        gMPU->Release();
                    339: 
1.1.1.3   root      340:        newev->dev->putlog(3, "イベント '%s' %s %d.%03d usec 後",
1.1.1.2   root      341:                newev->GetName().c_str(),
1.1.1.3   root      342:                updated ? "更新" : "開始",
1.1       root      343:                (int)(newev->time / 1000),
                    344:                (int)(newev->time % 1000));
                    345: }
                    346: 
1.1.1.3   root      347: // 指定のイベントを停止する。
1.1       root      348: // 指定されたイベントが登録されていなければ何もしない。
                    349: void
1.1.1.3   root      350: Scheduler::StopEvent(Event *event)
1.1       root      351: {
                    352:        bool found = false;
                    353: 
                    354:        evcs.lock();
                    355:        for (auto it = eventlist.begin(); it != eventlist.end(); ++it) {
                    356:                Event *e = *it;
                    357:                if (e == event) {
                    358:                        e->active = false;
                    359:                        eventlist.erase(it);
                    360:                        found = true;
                    361:                        break;
                    362:                }
                    363:        }
                    364:        evcs.unlock();
                    365: 
1.1.1.3   root      366:        // イベントを停止した場合は MPU の実行中断はしなくてよい。
1.1       root      367:        // 1msec 後にイベントを追加した後、やっぱりそのイベントを取り消した
                    368:        // 場合 (LUNA の電源オフとか) はここで MPU 処理を中断するよりも
                    369:        // そのまま 1msec 走って問題ない。
                    370: 
                    371:        if (found) {
1.1.1.3   root      372:                event->dev->putlog(3, "イベント  '%s' 停止", event->GetName().c_str());
1.1       root      373:        }
                    374: }
                    375: 
                    376: // 経過時間 t を文字列にして返す。
                    377: static const std::string
                    378: TimeToStr(uint64 t)
                    379: {
                    380:        char buf[32];
                    381:        char *p;
                    382:        size_t len;
                    383:        int n;
                    384: 
                    385:        uint ns = t % 1000;
                    386:        t /= 1000;
                    387:        uint us = t % 1000;
                    388:        t /= 1000;
                    389:        uint ms = t % 1000;
                    390:        t /= 1000;
                    391:        uint s = t % 60;
                    392:        t /= 60;
                    393:        uint m = t % 60;
                    394:        t /= 60;
                    395:        uint h = t;
                    396: 
                    397:        p = buf;
                    398:        len = sizeof(buf);
                    399:        if (h) {
                    400:                n = snprintf(p, len, "%d:%02d:%02d", h, m, s);
                    401:                p += n;
                    402:                len -= n;
                    403:        } else if (m) {
                    404:                n = snprintf(p, len, "%d:%02d", m, s);
                    405:                p += n;
                    406:                len -= n;
                    407:        } else {
                    408:                n = snprintf(p, len, "%d", s);
                    409:                p += n;
                    410:                len -= n;
                    411:        }
                    412:        n = snprintf(p, len, ".%03d'%03d'%03d", ms, us, ns);
                    413:        p += n;
                    414:        len -= n;
                    415: 
                    416:        return std::string(buf, p - buf);
                    417: }
                    418: 
1.1.1.5   root      419: // モニターサイズ取得
                    420: nnSize
                    421: Scheduler::GetMonitorSize()
                    422: {
                    423:        // 本当は Init() 完了時点でイベント個数は確定しているはずだけど、
                    424:        // そのタイミングで一度だけ呼ばれるフックが今の所ない。
                    425:        return nnSize(68, 11 + gEvents.size());
                    426: }
                    427: 
                    428: void
                    429: Scheduler::MonitorUpdate(TextScreen& monitor)
1.1       root      430: {
                    431:        int x, y;
                    432: 
                    433:        monitor.Clear();
                    434:        x = 0;
                    435:        y = 0;
                    436: 
1.1.1.7 ! root      437:        int clock_khz = gMPU->GetClockSpeed();
        !           438: 
1.1       root      439:        monitor.Print(0, y++, "Mode: %s %s",
                    440:                (mode & SCHED_SYNC) ? "Sync" : "Full",
                    441:                (mode & SCHED_STOP) ? "STOP" : "Run ");
                    442:        monitor.Print(0, y++, "Req: %08x", (uint32)atomic_reqflag);
                    443:        monitor.Print(0, y++, "MPU Speed: %d.%03dMHz",
1.1.1.7 ! root      444:                (clock_khz / 1000), (clock_khz % 1000));
        !           445:        uint64 vtime = gMPU->GetVirtTime();
        !           446:        uint64 rtime = GetRealTime();
        !           447:        uint64 vspan = vtime - vtimebase;
        !           448:        uint64 rspan = rtime - rtimebase;
1.1       root      449:        // RealTime は実時間なのでスケジューラ開始からの時間に変換
1.1.1.7 ! root      450:        rtime -= rtimestart;
        !           451:        monitor.Print(0, y++, "Total  Real Time: %18s", TimeToStr(rtime).c_str());
        !           452:        monitor.Print(0, y++, "    Virtual Time: %18s", TimeToStr(vtime).c_str());
        !           453:        double ratio = (double)vtime / rtime * 100;
1.1       root      454:        monitor.Print(0, y++, "    Ratio       : %3d.%01d%%",
                    455:                (int)ratio, ((int)(ratio * 10) % 10));
1.1.1.7 ! root      456:        monitor.Print(0, y++, "Moment Real Time: %18s", TimeToStr(rspan).c_str());
        !           457:        monitor.Print(0, y++, "    Virtual Time: %18s", TimeToStr(vspan).c_str());
1.1       root      458:        // XXX 移動平均をとる
1.1.1.7 ! root      459:        double relratio = (double)vspan / rspan * 100;
1.1       root      460:        monitor.Print(0, y++, "    Ratio       : %3d.%01d%%",
                    461:                (int)relratio, ((int)(relratio * 10) % 10));
                    462: 
                    463:        // 0         1         2         3         4         5         6
1.1.1.2   root      464:        // 0123456789012345678901234567890123456789012345678901234567890123456
                    465:        // Set Time        Remain Time     Description               Code
                    466:        //   3.123'456'789   3.123'456'789 0123456789012345678901234 $01234567
1.1       root      467:        x = 0;
                    468:        y++;
1.1.1.4   root      469:        monitor.Puts(x, y, "Set Time");
                    470:        monitor.Puts(x + 16, y, "Remain Time");
                    471:        monitor.Puts(x + 32, y, "Description");
                    472:        monitor.Puts(x + 58, y, "Code");
1.1       root      473:        y++;
1.1.1.6   root      474: 
1.1       root      475:        for (const auto& e : gEvents) {
                    476:                uint64 rem;
1.1.1.4   root      477:                TA attr;
1.1       root      478:                if (e->active) {
1.1.1.4   root      479:                        attr = TA::Normal;
1.1.1.7 ! root      480:                        if (e->vtime > vtime) {
        !           481:                                rem = e->vtime - vtime;
1.1.1.6   root      482:                        } else {
                    483:                                // XXX 実際には起きないけど、この表示をするにあたって
                    484:                                // スケジューラスレッドと一切調停していないので、仮想時刻が
                    485:                                // e->cycle (イベント発生時刻) をすぎていることがある。
                    486:                                // すぎてるので残り時間 0 と表示しておく。
                    487:                                rem = 0;
                    488:                        }
1.1       root      489:                } else {
                    490:                        attr = TA::Disable;
                    491:                        rem = 0;
                    492:                }
                    493:                monitor.Print(x, y++, attr,
1.1.1.2   root      494:                        "%3u.%03u'%03u'%03u %3u.%03u'%03u'%03u %-25s $%08x",
1.1       root      495:                        (uint)(e->time / (1000 * 1000 * 1000)),
                    496:                        (uint)((e->time / 1000 / 1000) % 1000),
                    497:                        (uint)((e->time / 1000) % 1000),
                    498:                        (uint)(e->time % 1000),
                    499:                        (uint)(rem / (1000 * 1000 * 1000)),
                    500:                        (uint)((rem / 1000 / 1000) % 1000),
                    501:                        (uint)((rem / 1000) % 1000),
                    502:                        (uint)(rem % 1000),
1.1.1.2   root      503:                        e->GetName().c_str(),
1.1       root      504:                        e->code);
                    505:        }
                    506: }

unix.superglobalmegacorp.com

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