|
|
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.