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