|
|
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.9 ! root 19: // 仮想時間は電源オン時からの nsec で表し、MPU デバイス (実際はその向こうの
1.1.1.7 root 20: // CPU コア) が管理している。VM 界からは gMPU->GetVirtTime() で取得する。
1.1.1.9 ! root 21: //
! 22: // 実時間はストップウォッチ gRealtime を基準とし、こちらも nsec で表す。
! 23: // このストップウォッチ自体は Scheduler 初期化時から常に単調増加する。
! 24: // 電源オフ中でも RTC へのパルス供給のために単調増加し続けることに注意。
! 25: //
! 26: // 「実経過時間」は、GetRealTime() (中身は gRealtime.Elapsed()) で取得でき
! 27: // o (たぶん)アプリケーション起動時から、常に増加。
! 28: // o (たぶん)ホストの時刻変更やサスペンドの影響を受けず、常に増加。
! 29: // o VM 電源オフ時でも進行する。
! 30: // o デバッガプロンプトで停止中は進行が停止する。
! 31: // という性質を持つ。
! 32: // 「仮想経過時間」は、gMPU->GetVirtTime() で取得でき
! 33: // o 電源オン時のみ有効。
! 34: // o (今のところ) MPU の命令境界で増加する
! 35: // という性質を持つ。
! 36: //
! 37: // 同期走行モードにおいては rtime_epoch, vtime_epoch をそれぞれの基準点とし
! 38: // これらと現在の実経過時間、仮想経過時間の差が同期するよう調整する。
! 39: // 高速走行モードにおいてはこれらは必要ないので特に参照しない。
! 40: //
! 41: // 一定間隔で行うパフォーマンス測定は、前回計測時の実および仮想経過時間を
! 42: // last_perf_rtime, last_perf_vtime として持っているので、これと現在の
! 43: // 経過時間との比で求める。同期走行/高速走行の影響は受けない。
1.1 root 44:
45: static void *scheduler_run(void *arg);
46:
1.1.1.3 root 47: std::unique_ptr<Scheduler> gScheduler;
1.1 root 48:
1.1.1.9 ! root 49: // パフォーマンス測定を行う実時間間隔
! 50: static constexpr uint64 PERF_INTERVAL = 1000_msec;
! 51:
! 52: // 実時間の基準となるストップウォッチ。
! 53: Stopwatch gRealtime;
! 54:
! 55: // 実経過時間 [nsec] を返す
! 56: static inline uint64 GetRealTime()
! 57: {
! 58: return gRealtime.Elapsed();
! 59: }
! 60:
! 61: #define FORMAT_FULL 0
! 62: #define FORMAT_SEC 1
! 63:
! 64: // 経過時間 t を文字列にして返す。
! 65: static const std::string
! 66: TimeToStrF(uint64 t, int format)
! 67: {
! 68: char buf[32];
! 69: char *p;
! 70: size_t len;
! 71: int n;
! 72:
! 73: uint ns = t % 1000;
! 74: t /= 1000;
! 75: uint us = t % 1000;
! 76: t /= 1000;
! 77: uint ms = t % 1000;
! 78: t /= 1000;
! 79:
! 80: uint s, m, h, d;
! 81: if (format == FORMAT_FULL) {
! 82: s = t % 60;
! 83: t /= 60;
! 84: m = t % 60;
! 85: t /= 60;
! 86: h = t % 24;
! 87: t /= 24;
! 88: d = t;
! 89: } else {
! 90: s = t;
! 91: m = 0;
! 92: h = 0;
! 93: d = 0;
! 94: }
! 95:
! 96: p = buf;
! 97: len = sizeof(buf);
! 98: if (d) {
! 99: n = snprintf(p, len, "%3dd %02u:%02u:%02u", d, h, m, s);
! 100: p += n;
! 101: len -= n;
! 102: } else if (h) {
! 103: n = snprintf(p, len, "%2u:%02u:%02u", h, m, s);
! 104: p += n;
! 105: len -= n;
! 106: } else if (m) {
! 107: n = snprintf(p, len, "%2u:%02u", m, s);
! 108: p += n;
! 109: len -= n;
! 110: } else {
! 111: n = snprintf(p, len, "%u", s);
! 112: p += n;
! 113: len -= n;
! 114: }
! 115: n = snprintf(p, len, ".%03u'%03u'%03u", ms, us, ns);
! 116: p += n;
! 117: len -= n;
! 118:
! 119: return std::string(buf, p - buf);
! 120: }
! 121:
! 122: static const std::string
! 123: TimeToStr(uint64 t)
! 124: {
! 125: return TimeToStrF(t, FORMAT_FULL);
! 126: }
! 127:
! 128: static const std::string
! 129: SecToStr(uint64 t)
! 130: {
! 131: return TimeToStrF(t, FORMAT_SEC);
! 132: }
! 133:
! 134: //
! 135: // スケジューラ
! 136: //
! 137:
1.1 root 138: // コンストラクタ
139: Scheduler::Scheduler()
140: {
141: logname = "scheduler";
142: devname = "Scheduler";
143:
1.1.1.9 ! root 144: for (int i = 0; i < perfq.Capacity(); i++) {
! 145: perfq.Enqueue(1000);
! 146: }
! 147:
! 148: sync_event.dev = this;
! 149: sync_event.func = (DeviceCallback_t)&Scheduler::SyncCallback;
! 150: sync_event.time = 50_msec;
! 151: sync_event.SetName("Scheduler Sync");
1.1 root 152: }
153:
154: // デストラクタ
155: Scheduler::~Scheduler()
156: {
1.1.1.3 root 157: Terminate();
1.1 root 158: }
159:
160: // 初期化
161: bool
162: Scheduler::Init()
163: {
1.1.1.3 root 164: // 起動時引数指定の高速モード
165: SetFullSpeed(gMainApp.fast_mode);
1.1 root 166:
167: // スケジューラ(VM)スレッド起動
1.1.1.3 root 168: pthread_create(&thread, NULL, scheduler_run, NULL);
169: thread_created = true;
1.1 root 170:
171: return true;
172: }
173:
174: // スレッドエントリ関数
175: void *
176: scheduler_run(void *arg)
177: {
1.1.1.4 root 178: PTHREAD_SETNAME("VM");
1.1 root 179:
180: gScheduler->ThreadRun();
181: return NULL;
182: }
183:
184: // スレッドエントリ関数(の実体)
185: void
186: Scheduler::ThreadRun()
187: {
1.1.1.9 ! root 188: // ここが rtime の基準点。
! 189: // Stopwatch.Restart() は .Net 由来なので
! 190: // Restart() は経過時間をリセットしてからタイマーを開始の意。
! 191: gRealtime.Restart();
! 192: rtime = 0;
! 193: next_rtc_rtime = 31.25_msec;
1.1 root 194:
1.1.1.3 root 195: for (;;) {
1.1.1.9 ! root 196: if (RunOff() == false)
! 197: return;
! 198: if (RunOn() == false)
1.1.1.3 root 199: return;
1.1.1.9 ! root 200: }
! 201: }
! 202:
! 203: // 電源オフ時のスケジューラメインループ。
! 204: // false ならこのスレッドの終了指示。
! 205: bool
! 206: Scheduler::RunOff()
! 207: {
! 208: uint32 oldreq;
! 209:
! 210: ispower = false;
! 211:
! 212: oldreq = -1;
! 213: for (;;) {
! 214: // RunOn() と違ってこっちは処理したフラグだけを下ろす。
! 215:
! 216: // デバッグ表示も前回から変化した時だけ。
! 217: uint32 req = atomic_reqflag.load();
! 218: if (__predict_false(req != oldreq)) {
! 219: if (loglevel >= 2) {
! 220: PutlogReqFlag(req);
! 221: }
! 222: oldreq = req;
1.1.1.3 root 223: }
1.1.1.9 ! root 224:
! 225: // 終了
! 226: if ((atomic_reqflag & REQ_EXIT)) {
! 227: return false;
1.1.1.3 root 228: }
1.1.1.9 ! root 229:
! 230: if ((atomic_reqflag & REQ_RESTART)) {
! 231: // 電源オフ中に RESTART は通常来るはずはないが、来てしまったら
! 232: // メニューから RESTART 操作中に VM が電源オフしてしまったのかも
! 233: // 知れないので、単に電源を入れる。
! 234: atomic_reqflag &= ~REQ_RESTART;
! 235: atomic_reqflag |= REQ_POWER_ON;
! 236: }
! 237:
! 238: // 電源オン指示
! 239: if ((atomic_reqflag & REQ_POWER_ON)) {
! 240: // 電源オン要求の場合
! 241:
! 242: // 全デバイス電源オン
! 243: for (const auto& d : gDevices) {
! 244: if (!d->PowerOn()) {
! 245: return false;
! 246: }
! 247: }
! 248:
! 249: // 続いて全デバイスをリセット
! 250: for (const auto& d : gDevices) {
! 251: d->ResetHard();
! 252: }
! 253:
! 254: // POWER_ON は処理したので下ろす。他のフラグは触らない。
! 255: atomic_reqflag &= ~REQ_POWER_ON;
! 256: // 電源オンは RESET も兼ねる (RESET は RunOn() 内で行う)
! 257: atomic_reqflag |= REQ_RESET;
! 258:
! 259: return true;
! 260: }
! 261:
! 262: // RTC はホスト時間で 32Hz (31.25msec) ごとにカウントアップするが、
! 263: // usleep() はそこまでの精度がないことと、RunRTC() は前回カウント
! 264: // アップ時刻からの差で処理しており入力が多少ぶれても構わないので、
! 265: // 適当にその周期の半分強で sleep してみる。
! 266: usleep(16_msec / 1_usec);
! 267:
! 268: // RTC をホスト時間で進める。
! 269: // (電源オンするまでは gRealTime は進み続けている)
! 270: rtime = GetRealTime();
! 271: RunRTC();
1.1 root 272: }
1.1.1.9 ! root 273: __unreachable();
! 274: }
1.1 root 275:
1.1.1.9 ! root 276: // 電源オン時のスケジューラメインループ。
! 277: // false ならこのスレッドの終了指示。
! 278: bool
! 279: Scheduler::RunOn()
! 280: {
! 281: uint64 last_event_vtime; // 直近のイベント発生時刻 (統計用)
! 282: Statistics<uint64> event_stat;
! 283: uint64 last_sync_rtime;
! 284:
! 285: ispower = true;
! 286:
! 287: // パフォーマンス計測は電源オン時のみなので、ここで初期化
! 288: last_perf_rtime = rtime;
! 289: last_perf_vtime = gMPU->GetVirtTime();
! 290: next_perf_rtime = rtime + PERF_INTERVAL;
! 291: // 最初の集計が行われる起動後1秒までは、この値が表示されるので
! 292: // インチキして 100% にしておく。
! 293: perf_counter = 100;
! 294:
! 295: last_event_vtime = last_perf_vtime;
! 296:
! 297: // 実時間系の初期化
! 298: rtime = GetRealTime();
! 299: last_sync_rtime = 0;
! 300: overslept = 0;
! 301: sync_event.Start();
1.1 root 302:
303: for (;;) {
1.1.1.9 ! root 304: vtime = gMPU->GetVirtTime();
! 305:
! 306: uint32 req = atomic_reqflag.exchange(0);
1.1.1.8 root 307: if (__predict_false(req != 0)) {
1.1.1.9 ! root 308:
! 309: // デバッグ表示
! 310: if (loglevel >= 2) {
! 311: PutlogReqFlag(req);
! 312: }
! 313:
1.1.1.8 root 314: uint32 newmode = mode;
315:
316: // 動作モード変更要因を一つずつ調べる
317: if ((req & REQ_CPU_MODE)) {
318: // CPU 状態が new_cpumode に変更になった
319: newmode &= ~SCHED_CPU_MASK;
320: newmode |= new_cpumode;
321: req &= ~REQ_CPU_MODE;
322: }
323: if ((req & REQ_USER_MODE)) {
324: // ユーザからの高速/同期モード指示が変更になった
325: newmode &= ~SCHED_SYNC;
326: newmode |= new_usermode;
327: req &= ~REQ_USER_MODE;
328: }
1.1.1.9 ! root 329: if ((req & REQ_BOOT_MODE)) {
! 330: // ブートページが変更になった
! 331: newmode &= ~SCHED_BOOT;
! 332: newmode |= new_bootmode;
! 333: req &= ~REQ_BOOT_MODE;
! 334: }
! 335: if ((req & REQ_KEY_MODE)) {
! 336: // キー入力中かどうか
! 337: newmode &= ~SCHED_KEY;
! 338: newmode |= new_keymode;
! 339: req &= ~REQ_KEY_MODE;
! 340: }
! 341:
! 342: // いずれかでも変化していれば..
! 343: if (mode != newmode) {
! 344: // 動作モード変更要因を全部調べた結果、変更が必要か。
! 345: // mode、newmode どちらもゼロ(高速モード)か非ゼロ(通常モード)か
! 346: // なので、等値比較ではなく二値にしてから比較。
! 347: if ((bool)mode != (bool)newmode) {
! 348: // モードが変わるのでここで一旦同期をとる
! 349: auto over = Sync();
! 350: if (overslept < over) {
! 351: overslept = over;
! 352: }
! 353:
! 354: if (newmode == 0) {
! 355: // 高速モードに
! 356: putlog(1, "高速モード");
! 357:
! 358: // 同期モードを抜けた時刻
! 359: last_sync_rtime = rtime;
! 360:
! 361: // 高速モードでは RAM は直接アクセス
! 362: ::direct_ram_size = gRAM->GetSize();
! 363:
! 364: } else {
! 365: // 同期モードに
! 366: putlog(1, "同期モード");
! 367:
! 368: // 同期モードでは RAM アクセスはデバイス経由
! 369: ::direct_ram_size = 0;
! 370:
! 371: // 同期用の基準点を設置。
! 372: // ゲストが STOP 命令による定常状態にいる時は
! 373: // 割り込みなど短期間だけ高速モードになるのを繰り返す。
! 374: // スリープ粒度の粗い NetBSD ホストでは同期モードに
! 375: // 切り替えるたびに基準点を移動していると、スリープ
! 376: // しすぎてしまうので、短期間だけ高速モードを出入り
! 377: // した分は基準点を移動せず無視する。
! 378: if (rtime - last_sync_rtime > overslept) {
! 379: rtime_epoch = rtime;
! 380: vtime_epoch = vtime;
! 381: }
! 382: }
! 383: }
! 384:
! 385: // ブール比較の後で、全ビットを維持したまま代入。
1.1.1.8 root 386: mode = newmode;
1.1.1.9 ! root 387: }
1.1.1.8 root 388:
1.1.1.9 ! root 389: if ((req & REQ_RESTART)) {
! 390: // 電源オン中に RESTART が来たら電源オンを予約して電源オフ。
! 391: // poweroff_callback は GUI にアプリケーションを終了させる
! 392: // コールバックなのでここでは呼ばない。
! 393: atomic_reqflag |= REQ_POWER_ON;
1.1.1.8 root 394:
1.1.1.9 ! root 395: // 全デバイス電源オフ
! 396: for (const auto& d : gDevices) {
! 397: if (!d->PowerOff()) {
! 398: return false;
! 399: }
1.1.1.8 root 400: }
1.1 root 401:
1.1.1.9 ! root 402: return true;
1.1 root 403: }
404:
405: if ((req & REQ_POWER_OFF)) {
406: // 電源オフ要求の場合
407:
408: // 全デバイス電源オフ
1.1.1.9 ! root 409: for (const auto& d : gDevices) {
! 410: if (!d->PowerOff()) {
! 411: return false;
! 412: }
! 413: }
! 414:
! 415: // GUI に通知
! 416: if (poweroff_callback) {
! 417: poweroff_callback();
! 418: }
1.1 root 419:
1.1.1.9 ! root 420: return true;
1.1 root 421: }
422:
1.1.1.6 root 423: if ((req & REQ_RESET)) {
424: // リセット要求の場合
425:
426: // 全デバイスをリセット
1.1.1.9 ! root 427: for (const auto& d : gDevices) {
! 428: d->ResetHard();
! 429: }
1.1.1.6 root 430:
431: req &= ~REQ_RESET;
432: }
433:
1.1 root 434: if ((req & REQ_EXIT)) {
435: // 終了要求
1.1.1.9 ! root 436: return false;
1.1 root 437: }
438:
1.1.1.9 ! root 439: // これいるかな?
1.1.1.3 root 440: assertmsg(req == 0, "req=$%x", req);
1.1 root 441: }
442:
1.1.1.9 ! root 443: // パフォーマンス表示
! 444: if (__predict_false(rtime >= next_perf_rtime)) {
! 445: uint64 rperf = rtime - last_perf_rtime;
! 446: uint64 vperf = vtime - last_perf_vtime;
! 447:
! 448: // 百分率で小数以下1桁分までとなるよう整数で計算する
! 449: perfq.EnqueueForce((uint32)(vperf * 1000 / rperf));
! 450: // 移動平均を求める (perfq は常に要素数分埋めてある)
! 451: int ma = 0;
! 452: int qlen = perfq.Length();
! 453: for (int i = 0; i < qlen; i++) {
! 454: auto v = perfq.Peek(i);
! 455: ma += v;
! 456: }
! 457: ma /= qlen;
! 458: // 最下位を四捨五入して捨てる (整数%になる)
! 459: ma = (ma + 5) / 10;
! 460: perf_counter = ma;
! 461:
! 462: // 直近1秒の Sync 回数
! 463: last_sync_count = sync_count;
! 464: sync_count = 0;
! 465:
! 466: // 直近1秒のイベント統計
! 467: last_event_stat = event_stat;
! 468: event_stat.Clear();
! 469:
! 470: last_perf_rtime = rtime;
! 471: last_perf_vtime = vtime;
! 472: next_perf_rtime = rtime + PERF_INTERVAL;
1.1 root 473: }
474:
1.1.1.6 root 475: // イベントリストを調べ現時点から最も早く発生するイベントまでの必要
1.1.1.7 root 476: // 時間を求めて、この時間分だけ MPU を駆動する。
1.1.1.6 root 477: // MPU 実行中にデバイスアクセスなどによってスケジューラにイベントが
478: // 登録されると、その命令の終了とともに処理を打ち切って戻ってくる。
1.1.1.7 root 479:
480: // delta_vtime が今回進める仮想時間。
481: // とりあえず初期値を仮置きしとくけど、イベントは通常 20msec 以内には
482: // 必ず起きるので (例えば LUNA の sysclk は 10 or 16 msec 周期)、
483: // この初期値は実質上限値。20msec 程度より大きければいくらでもよくて
484: // 100msec とかでいいんだけど、二進数で置数しやすいよう
485: // 0x200'0000 (約33.5msec) にしておく。
486: uint64 delta_vtime = 0x200'0000;
1.1 root 487:
488: // イベント
489: for (;;) {
1.1.1.6 root 490: Event *e;
491: // ロック区間
492: {
493: std::lock_guard<std::mutex> lock(evcs);
494:
1.1.1.9 ! root 495: if (eventq.empty()) {
1.1.1.6 root 496: break;
497: }
1.1.1.9 ! root 498: e = eventq.top();
1.1.1.7 root 499: if (e->vtime > vtime) {
1.1.1.6 root 500: // まだ時刻に到達していない
1.1.1.7 root 501: delta_vtime = e->vtime - vtime;
1.1.1.6 root 502: break;
503: }
504:
505: // 到達したのでこのイベントをリストから削除
506: e->active = false;
1.1.1.9 ! root 507: eventq.pop();
1.1 root 508: }
509:
1.1.1.9 ! root 510: // イベント発生間隔 (統計情報)
! 511: // t は前回と今回のイベント予定時刻の間隔。
! 512: uint64 t = e->vtime - last_event_vtime;
! 513: event_stat.Add(t);
! 514: last_event_vtime = e->vtime;
! 515:
1.1 root 516: // コールバック
1.1.1.8 root 517: e->dev->putlogf(3, lstr("イベント '%s' 時刻到達",
518: e->GetName().c_str()));
1.1.1.6 root 519: ((e->dev)->*(e->func))(*e);
1.1 root 520:
521: // 同時刻のイベントがあるかも知れないのでなくなるまで調べる
522: }
1.1.1.6 root 523:
1.1.1.9 ! root 524: // RTC をホスト時間で進める
! 525: // (同期イベントによって sleep したかもしれないので、その後がよかろう)
! 526: RunRTC();
! 527:
1.1.1.7 root 528: // CPU を駆動
1.1.1.8 root 529: new_cpumode = gMPU->Run(delta_vtime);
1.1.1.7 root 530:
1.1.1.9 ! root 531: // CPU 側の走行状態が変わったら、このループに通知
! 532: if (__predict_false(((mode ^ new_cpumode) & SCHED_CPU_MASK))) {
! 533: atomic_reqflag |= REQ_CPU_MODE;
! 534: }
! 535: }
! 536: __unreachable();
! 537: }
1.1 root 538:
1.1.1.9 ! root 539: // 同期イベント
! 540: void
! 541: Scheduler::SyncCallback(Event& ev)
! 542: {
! 543: Sync();
! 544:
! 545: // 次回のイベントを再登録
! 546: ev.Start();
! 547: }
1.1 root 548:
1.1.1.9 ! root 549: // 同期処理
! 550: // ここで仮想時間を実時間と同期させる。
! 551: // スリープしすぎた実時間を返す。
! 552: uint64
! 553: Scheduler::Sync()
! 554: {
! 555: uint64 over = 0;
! 556:
! 557: // 実時間をここで更新
! 558: rtime = GetRealTime();
! 559:
! 560: // 統計情報
! 561: sync_count++;
! 562:
! 563: // 同期モードなら..
! 564: if (mode != 0) {
! 565: uint64 rspan = rtime - rtime_epoch;
! 566: uint64 vspan = vtime - vtime_epoch;
! 567: if (vspan > rspan) {
! 568: // 仮想時間のほうが進んでいれば、スリープして待つ
! 569: uint64 diff = vspan - rspan;
! 570: struct timespec ts;
! 571: ts.tv_sec = diff / (1000 * 1000 * 1000);
! 572: ts.tv_nsec = diff % (1000 * 1000 * 1000);
! 573: nanosleep(&ts, NULL);
! 574:
! 575: // スリープしすぎた時間を計測。
! 576: // この時間にはスリープ時間を計算するための時間も含む。
! 577: uint64 prev = rtime;
! 578: rtime = GetRealTime();
! 579: uint64 realslept = rtime - prev;
! 580: if (realslept > diff) {
! 581: over = realslept - diff;
1.1 root 582: }
1.1.1.9 ! root 583: } else {
! 584: // 実時間のほうが進んでいれば、間に合ってない
1.1 root 585:
1.1.1.9 ! root 586: // XXX ここで間引き運転とか
1.1 root 587: }
588: }
1.1.1.9 ! root 589: return over;
! 590: }
! 591:
! 592: // RTC は常にホスト時刻で動いており
! 593: // 32Hz = 31.25msec ごとにパルスを入れる。
! 594: // VM の電源がオフでも実行する。
! 595: void
! 596: Scheduler::RunRTC()
! 597: {
! 598: while (rtime >= next_rtc_rtime) {
! 599: gRTC->ClockIn();
! 600: next_rtc_rtime += 31.25_msec;
! 601: }
1.1 root 602: }
603:
1.1.1.3 root 604: // 必要ならスレッドの終了を指示して完了まで待つ。
1.1 root 605: void
606: Scheduler::Terminate()
607: {
1.1.1.3 root 608: if (thread_created) {
1.1.1.8 root 609: RequestExit();
1.1.1.3 root 610: pthread_join(thread, NULL);
611: thread_created = false;
612: }
1.1 root 613: }
614:
1.1.1.8 root 615: // スレッド終了を指示する。
616: void
617: Scheduler::RequestExit()
618: {
619: atomic_reqflag |= REQ_EXIT;
620: }
621:
1.1 root 622: // 動作モードを設定する。
623: // true なら高速モード、false なら同期モード。
624: void
625: Scheduler::SetFullSpeed(bool enable)
626: {
627: if (enable) {
1.1.1.8 root 628: new_usermode = 0;
1.1 root 629: } else {
1.1.1.8 root 630: new_usermode = SCHED_SYNC;
1.1 root 631: }
1.1.1.8 root 632: atomic_reqflag |= REQ_USER_MODE;
1.1 root 633: }
634:
1.1.1.9 ! root 635: // ブートページモードを設定する。
! 636: // 戻り値は現在の動作モードがブートモード(ROM)なら true。
! 637: // (VM::SwitchBootPage から呼ぶので個別には呼ばないこと)
! 638: void
! 639: Scheduler::RequestBootPageMode(bool isrom)
! 640: {
! 641: if (isrom) {
! 642: new_bootmode = SCHED_BOOT;
! 643: } else {
! 644: new_bootmode = 0;
! 645: }
! 646: atomic_reqflag |= REQ_BOOT_MODE;
! 647: }
! 648:
! 649: // キー入力中かどうかを設定する。
! 650: // true ならキー入力中、false なら入力中でない。
! 651: void
! 652: Scheduler::RequestKeyPressed(bool pressed)
! 653: {
! 654: if (pressed) {
! 655: new_keymode = SCHED_KEY;
! 656: } else {
! 657: new_keymode = 0;
! 658: }
! 659: atomic_reqflag |= REQ_KEY_MODE;
! 660: }
! 661:
! 662: // 電源オフ時のコールバックを設定する
! 663: void
! 664: Scheduler::SetPowerOffCallback(void (*callback)())
! 665: {
! 666: poweroff_callback = callback;
! 667: }
! 668:
! 669: // REQ_* フラグのデバッグ表示
! 670: void
! 671: Scheduler::PutlogReqFlag(uint32 req) const
! 672: {
! 673: static const char * const cpumode_str[] = {
! 674: "Normal",
! 675: "STOP",
! 676: "HALT",
! 677: "HALTXXX",
! 678: };
! 679: char buf[128];
! 680: int len = 0;
! 681:
! 682: buf[0] = '\0';
! 683:
! 684: #define SET(FL, MSG) \
! 685: if ((req & (FL))) len += snprintf(buf + len, sizeof(buf) - len, ",%s", MSG)
! 686:
! 687: SET(REQ_EXIT, "EXIT");
! 688: SET(REQ_RESTART, "RESTART");
! 689: SET(REQ_RESET, "RESET");
! 690: SET(REQ_POWER_OFF, "POWER_OFF");
! 691: SET(REQ_POWER_ON, "POWER_ON");
! 692: if ((req & REQ_USER_MODE)) {
! 693: len += snprintf(buf + len, sizeof(buf) - len, ",USER:%s",
! 694: (new_usermode == 0) ? "Fast" : "Sync");
! 695: }
! 696: if ((req & REQ_CPU_MODE)) {
! 697: len += snprintf(buf + len, sizeof(buf) - len, ",CPU:%s",
! 698: cpumode_str[new_cpumode & 3]);
! 699: }
! 700: if ((req & REQ_BOOT_MODE)) {
! 701: len += snprintf(buf + len, sizeof(buf) - len, ",BOOT:%s",
! 702: (new_bootmode == 0) ? "Normal" : "Boot");
! 703: }
! 704: if ((req & REQ_KEY_MODE)) {
! 705: len += snprintf(buf + len, sizeof(buf) - len, ",KEY:%s",
! 706: (new_keymode == 0) ? "-" : "Pressed");
! 707: }
! 708:
! 709: if (buf[0] != '\0') {
! 710: buf[0] = '<';
! 711: strlcat(buf, ">", sizeof(buf));
! 712: }
! 713: putlogn("%sreq=$%04x%s", (ispower ? "" : "Off "), req, buf);
! 714: }
! 715:
1.1.1.3 root 716: // 指定のイベントを開始する。
1.1 root 717: // すでに同イベントが登録されている場合は古いイベントを削除してから
718: // 新しいイベントを再登録となる。
719: // イベントはワンショットのみ。
720: void
1.1.1.3 root 721: Scheduler::StartEvent(Event *newev)
1.1 root 722: {
723: bool updated = false;
724:
1.1.1.7 root 725: // time は仮想相対時間 [nsec]、vtime は仮想絶対時刻 [nsec]
726: newev->vtime = gMPU->GetVirtTime() + newev->time;
1.1 root 727:
728: evcs.lock();
1.1.1.9 ! root 729: // すでにあれば削除して...
1.1 root 730: if (newev->active) {
1.1.1.9 ! root 731: updated = eventq.Erase(newev);
1.1 root 732: }
1.1.1.9 ! root 733: // 自身を追加
! 734: eventq.push(newev);
1.1 root 735: newev->active = true;
736:
1.1.1.9 ! root 737: // 先頭なら再スケジュールのため CPU 実行をここで打ち切り
! 738: if (eventq.top() == newev) {
! 739: gMPU->Release();
! 740: }
! 741: evcs.unlock();
1.1 root 742:
1.1.1.8 root 743: newev->dev->putlogf(3, lstr("イベント '%s' %s %d.%03d usec 後",
1.1.1.2 root 744: newev->GetName().c_str(),
1.1.1.3 root 745: updated ? "更新" : "開始",
1.1 root 746: (int)(newev->time / 1000),
1.1.1.8 root 747: (int)(newev->time % 1000)));
1.1 root 748: }
749:
1.1.1.3 root 750: // 指定のイベントを停止する。
1.1 root 751: // 指定されたイベントが登録されていなければ何もしない。
752: void
1.1.1.3 root 753: Scheduler::StopEvent(Event *event)
1.1 root 754: {
755: bool found = false;
756:
757: evcs.lock();
1.1.1.9 ! root 758: found = eventq.Erase(event);
1.1 root 759: evcs.unlock();
1.1.1.9 ! root 760: event->active = false;
1.1 root 761:
1.1.1.3 root 762: // イベントを停止した場合は MPU の実行中断はしなくてよい。
1.1 root 763: // 1msec 後にイベントを追加した後、やっぱりそのイベントを取り消した
764: // 場合 (LUNA の電源オフとか) はここで MPU 処理を中断するよりも
765: // そのまま 1msec 走って問題ない。
766:
767: if (found) {
1.1.1.8 root 768: event->dev->putlogf(3, lstr("イベント '%s' 停止",
769: event->GetName().c_str()));
1.1 root 770: }
771: }
772:
773:
1.1.1.5 root 774: // モニターサイズ取得
775: nnSize
776: Scheduler::GetMonitorSize()
777: {
778: // 本当は Init() 完了時点でイベント個数は確定しているはずだけど、
779: // そのタイミングで一度だけ呼ばれるフックが今の所ない。
1.1.1.9 ! root 780: return nnSize(72, 10 + gEvents.size());
1.1.1.5 root 781: }
782:
783: void
784: Scheduler::MonitorUpdate(TextScreen& monitor)
1.1 root 785: {
786: int x, y;
787:
788: monitor.Clear();
789: x = 0;
790: y = 0;
791:
1.1.1.7 root 792: int clock_khz = gMPU->GetClockSpeed();
793:
1.1.1.8 root 794: monitor.Print(0, y, "Mode: 0x%04x (", mode);
795: if ((mode & SCHED_SYNC)) {
796: monitor.Puts(14, y, TA::On, "Sync");
797: } else {
798: monitor.Puts(14, y, "Full");
799: }
800: switch (mode & SCHED_CPU_MASK) {
801: case SCHED_CPU_NORMAL:
802: monitor.Puts(19, y, "Run");
803: break;
804: case SCHED_CPU_STOP:
805: monitor.Puts(19, y, TA::On, "STOP");
806: break;
807: case SCHED_CPU_HALT:
808: monitor.Puts(19, y, TA::On, "HALT");
809: break;
810: }
1.1.1.9 ! root 811: monitor.Puts(24, y, TA::OnOff((mode & SCHED_BOOT)), "Boot");
! 812: monitor.Puts(29, y, TA::OnOff((mode & SCHED_KEY)), "Key");
! 813: monitor.Puts(33, y, ")");
1.1.1.8 root 814: y++;
1.1 root 815: monitor.Print(0, y++, "Req: %08x", (uint32)atomic_reqflag);
816: monitor.Print(0, y++, "MPU Speed: %d.%03dMHz",
1.1.1.7 root 817: (clock_khz / 1000), (clock_khz % 1000));
1.1.1.9 ! root 818: uint64 rt = rtime;
! 819: uint64 vt = vtime;
! 820:
! 821: auto s_rt = TimeToStr(rt);
! 822: auto s_vt = TimeToStr(vt);
! 823: std::string s_sync_rt {};
! 824: std::string s_sync_vt {};
! 825: if (mode != 0) {
! 826: uint64 sync_rt = rtime - rtime_epoch;
! 827: uint64 sync_vt = vtime - vtime_epoch;
! 828: s_sync_rt = TimeToStr(sync_rt);
! 829: s_sync_vt = TimeToStr(sync_vt);
! 830: }
! 831: // 日 まで表示しようとするときは桁位置を動かす
! 832: int width = std::max(rt, vt) > 86400_sec ? 25 : 20;
! 833:
! 834: monitor.Print(0, y++, "Total Real Time: %*s", width, s_rt.c_str());
! 835: monitor.Print(4, y++, "Virtual Time: %*s", width, s_vt.c_str());
! 836: monitor.Print(0, y++, "Sync Real Time: %*s", width, s_sync_rt.c_str());
! 837: monitor.Print(4, y++, "Virtual Time: %*s", width, s_sync_vt.c_str());
! 838:
! 839: monitor.Print(0, y++, "Performance : %4d%%", perf_counter);
! 840:
! 841: x = 44;
! 842: y = 0;
! 843: monitor.Print(x, y++, "Max OverSlept: %13s", SecToStr(overslept).c_str());
! 844: monitor.Print(x, y++, "Sync per sec : %4u", last_sync_count);
! 845: monitor.Print(x, y++, "Event per sec: %8u",
! 846: (uint)last_event_stat.Count());
! 847: monitor.Print(x, y++, "Evt Intvl Min: %13s",
! 848: SecToStr(last_event_stat.Min()).c_str());
! 849: monitor.Print(x, y++, "Evt Intvl Avg: %13s",
! 850: SecToStr(last_event_stat.Average()).c_str());
! 851: monitor.Print(x, y++, "Evt Intvl Max: %13s",
! 852: SecToStr(last_event_stat.Max()).c_str());
1.1 root 853:
854: // 0 1 2 3 4 5 6
1.1.1.9 ! root 855: // 01234567890123456789012345678901234567890123456789012345678901234568
! 856: // Set Time Remain Time Description
! 857: // 3.123'456'789 3.123'456'789 012345678901234567890123456789
1.1 root 858: x = 0;
1.1.1.9 ! root 859: y = 9;
! 860: monitor.Puts(x + 0, y, "Event Time");
! 861: monitor.Puts(x + 15, y, "Remain Time");
! 862: monitor.Puts(x + 30, y, "Description");
1.1 root 863: y++;
1.1.1.6 root 864:
1.1 root 865: for (const auto& e : gEvents) {
866: uint64 rem;
1.1.1.4 root 867: TA attr;
1.1 root 868: if (e->active) {
1.1.1.4 root 869: attr = TA::Normal;
1.1.1.7 root 870: if (e->vtime > vtime) {
871: rem = e->vtime - vtime;
1.1.1.6 root 872: } else {
873: // XXX 実際には起きないけど、この表示をするにあたって
874: // スケジューラスレッドと一切調停していないので、仮想時刻が
875: // e->cycle (イベント発生時刻) をすぎていることがある。
876: // すぎてるので残り時間 0 と表示しておく。
877: rem = 0;
878: }
1.1 root 879: } else {
880: attr = TA::Disable;
881: rem = 0;
882: }
883: monitor.Print(x, y++, attr,
1.1.1.9 ! root 884: "%13s %13s %-30s",
! 885: SecToStr(e->time).c_str(),
! 886: SecToStr(rem).c_str(),
! 887: e->GetName().c_str());
1.1 root 888: }
889: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.