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

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

unix.superglobalmegacorp.com

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