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