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