|
|
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:
133: cycle = INT64_MAX;
134: mode = 0;
135: for (;;) {
136: req = atomic_reqflag.exchange(0);
137: if (req) {
138: if ((req & REQ_SYNC)) {
139: // スケジューラを同期モードに
140: mode |= SCHED_SYNC;
141: }
142: if ((req & REQ_FAST)) {
143: // スケジューラを高速モードに
144: mode &= ~SCHED_SYNC;
145: }
146: if ((req & REQ_RUN)) {
147: // MPU が通常状態になった
148: mode &= ~SCHED_STOP;
149: }
150: if ((req & REQ_STOP)) {
151: // MPU が STOP 状態になった
152: mode |= SCHED_STOP;
153: }
154: if ((req & REQ_MODEMASK)) {
155: // mode がどれかにでも変わったら
156: static const char *modestr[] = {
157: "高速",
158: "高速 & STOP状態",
159: "通常",
160: "通常 & STOP状態",
161: };
162: putlog(1, "モード変更 mode=%d (%s)", mode, modestr[mode]);
163:
164: // 基準時刻をリセット
165: rtimebase = ::GetRealTime();
166: vtimebase = GetVirtTime();
167:
168: // 該当ビットを落としておく
169: req &= ~REQ_MODEMASK;
170: }
171:
172: if ((req & REQ_POWER_OFF)) {
173: // 電源オフ要求の場合
174:
175: // 全デバイス電源オフ
176: gVM->DevicePowerOff();
177:
178: req &= ~REQ_POWER_OFF;
179: }
180:
181: if ((req & REQ_EXIT)) {
182: // 終了要求
183: // この for ブロックをいきなり抜けるだけなので
184: // もうフラグの値も関係ないのだが一応。
185: req &= ~REQ_EXIT;
186: break;
187: }
188:
189: // 電源オン要求は来ないはず
1.1.1.3 root 190: assertmsg(req == 0, "req=$%x", req);
1.1 root 191: }
192:
193: // RTC は常にホスト時刻で動いており
194: // 32Hz = 31.25msec ごとにパルスを入れる。
195: while (::GetRealTime() > rtc_last_clock + 31.25_msec) {
196: gRTC->ClockIn();
197: rtc_last_clock += 31.25_msec;
198: }
199:
200: // まず CPU を駆動する。MPU::Run() はデバイスアクセスなどによって
201: // スケジューラにイベントが登録されると、その命令の終了とともに
202: // 処理を打ち切って戻ってくる。
203: // ここでイベントリストを調べ、その時点から最も早く発生するイベント
204: // までの必要サイクル数が、次のループでの駆動サイクル数になる。
205:
206: // CPU 駆動
207: uint32 outer;
208: outer = gMPU->Run(cycle);
209:
210: // イベント
211: for (;;) {
212: evcs.lock();
213: if (eventlist.empty()) {
214: cycle = INT64_MAX;
215: break;
216: }
217: Event *e = eventlist.front();
218: if (gMPU->total_cycle() < e->cycle) {
219: // まだ時刻に到達していない
220: cycle = e->cycle - gMPU->total_cycle();
221: break;
222: }
223:
224: // 到達したのでこのイベントをリストから削除
225: e->active = false;
226: eventlist.pop_front();
227: evcs.unlock();
228:
229: // コールバック
1.1.1.3 root 230: e->dev->putlog(3, "イベント '%s' 時刻到達", e->GetName().c_str());
1.1 root 231: ((e->dev)->*(e->func))(e->code);
232:
233: // 同時刻のイベントがあるかも知れないのでなくなるまで調べる
234: }
235: evcs.unlock();
236:
237: // 同期モードなら実時間調整。
238: // あるいは高速モードであってもストップ状態なら実時間駆動する。
239: if (mode != 0) {
240: // 通常モード
241: uint64 vtime;
242: uint64 rtime;
243:
244: // 基準時からの経過時間
245: vtime = GetVirtTime() - vtimebase;
246: rtime = GetRealTime() - rtimebase;
247:
248: if (vtime > rtime) {
249: // 仮想時間のほうが進んでいれば、スリープして待つ
250: uint64 diff = vtime - rtime;
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:
262: // CPU の STOP 状態をこっちのモードに反映。
263: // このために SCHED_STOP と CPU_REQ_STOP は同じビット位置にしてある。
1.1.1.4 root 264: // CPU の HALT はスケジューラ的には STOP と同じ扱いでいい。
1.1 root 265: if (((mode ^ outer) & SCHED_STOP)) {
266: // 状態変更要求
1.1.1.4 root 267: if ((outer & (CPU_REQ_STOP | CPU_REQ_HALT)) != 0) {
1.1 root 268: atomic_reqflag |= REQ_STOP;
269: } else {
270: atomic_reqflag |= REQ_RUN;
271: }
272: }
273: }
274: }
275:
1.1.1.3 root 276: // 必要ならスレッドの終了を指示して完了まで待つ。
1.1 root 277: void
278: Scheduler::Terminate()
279: {
1.1.1.3 root 280: if (thread_created) {
281: atomic_reqflag |= REQ_EXIT;
282: pthread_join(thread, NULL);
283: thread_created = false;
284: }
1.1 root 285: }
286:
287: // 動作モードを設定する。
288: // true なら高速モード、false なら同期モード。
289: void
290: Scheduler::SetFullSpeed(bool enable)
291: {
292: if (enable) {
293: atomic_reqflag |= REQ_FAST;
294: } else {
295: atomic_reqflag |= REQ_SYNC;
296: }
297: }
298:
1.1.1.3 root 299: // 指定のイベントを開始する。
1.1 root 300: // すでに同イベントが登録されている場合は古いイベントを削除してから
301: // 新しいイベントを再登録となる。
302: // イベントはワンショットのみ。
303: void
1.1.1.3 root 304: Scheduler::StartEvent(Event *newev)
1.1 root 305: {
306: bool inserted = false;
307: bool updated = false;
308:
309: // 相対 time から絶対 cycle を計算
310: newev->cycle = gMPU->total_cycle() + Vtime2Cycle(newev->time);
311:
312: evcs.lock();
313: // すでにあれば削除
314: if (newev->active) {
315: for (auto it = eventlist.begin(); it != eventlist.end(); ++it) {
316: Event *e = *it;
317: if (e == newev) {
318: eventlist.erase(it);
319: updated = true;
320: break;
321: }
322: }
323: }
324: // ソートされているところに自身を挿入
325: for (auto it = eventlist.begin(); it != eventlist.end(); ++it) {
326: Event *e = *it;
327: if (newev->cycle <= e->cycle) {
328: // このイベントの前に入れる
329: eventlist.insert(it, newev);
330: inserted = true;
331: break;
332: }
333: }
334: if (inserted == false) {
335: // 最後に追加
336: eventlist.push_back(newev);
337: }
338: newev->active = true;
339: evcs.unlock();
340:
341: gMPU->Release();
342:
1.1.1.3 root 343: newev->dev->putlog(3, "イベント '%s' %s %d.%03d usec 後",
1.1.1.2 root 344: newev->GetName().c_str(),
1.1.1.3 root 345: updated ? "更新" : "開始",
1.1 root 346: (int)(newev->time / 1000),
347: (int)(newev->time % 1000));
348: }
349:
1.1.1.3 root 350: // 指定のイベントを停止する。
1.1 root 351: // 指定されたイベントが登録されていなければ何もしない。
352: void
1.1.1.3 root 353: Scheduler::StopEvent(Event *event)
1.1 root 354: {
355: bool found = false;
356:
357: evcs.lock();
358: for (auto it = eventlist.begin(); it != eventlist.end(); ++it) {
359: Event *e = *it;
360: if (e == event) {
361: e->active = false;
362: eventlist.erase(it);
363: found = true;
364: break;
365: }
366: }
367: evcs.unlock();
368:
1.1.1.3 root 369: // イベントを停止した場合は MPU の実行中断はしなくてよい。
1.1 root 370: // 1msec 後にイベントを追加した後、やっぱりそのイベントを取り消した
371: // 場合 (LUNA の電源オフとか) はここで MPU 処理を中断するよりも
372: // そのまま 1msec 走って問題ない。
373:
374: if (found) {
1.1.1.3 root 375: event->dev->putlog(3, "イベント '%s' 停止", event->GetName().c_str());
1.1 root 376: }
377: }
378:
379: // 経過時間 t を文字列にして返す。
380: static const std::string
381: TimeToStr(uint64 t)
382: {
383: char buf[32];
384: char *p;
385: size_t len;
386: int n;
387:
388: uint ns = t % 1000;
389: t /= 1000;
390: uint us = t % 1000;
391: t /= 1000;
392: uint ms = t % 1000;
393: t /= 1000;
394: uint s = t % 60;
395: t /= 60;
396: uint m = t % 60;
397: t /= 60;
398: uint h = t;
399:
400: p = buf;
401: len = sizeof(buf);
402: if (h) {
403: n = snprintf(p, len, "%d:%02d:%02d", h, m, s);
404: p += n;
405: len -= n;
406: } else if (m) {
407: n = snprintf(p, len, "%d:%02d", m, s);
408: p += n;
409: len -= n;
410: } else {
411: n = snprintf(p, len, "%d", s);
412: p += n;
413: len -= n;
414: }
415: n = snprintf(p, len, ".%03d'%03d'%03d", ms, us, ns);
416: p += n;
417: len -= n;
418:
419: return std::string(buf, p - buf);
420: }
421:
1.1.1.5 ! root 422: // モニターサイズ取得
! 423: nnSize
! 424: Scheduler::GetMonitorSize()
! 425: {
! 426: // 本当は Init() 完了時点でイベント個数は確定しているはずだけど、
! 427: // そのタイミングで一度だけ呼ばれるフックが今の所ない。
! 428: return nnSize(68, 11 + gEvents.size());
! 429: }
! 430:
! 431: void
! 432: Scheduler::MonitorUpdate(TextScreen& monitor)
1.1 root 433: {
434: int x, y;
435:
436: monitor.Clear();
437: x = 0;
438: y = 0;
439:
440: monitor.Print(0, y++, "Mode: %s %s",
441: (mode & SCHED_SYNC) ? "Sync" : "Full",
442: (mode & SCHED_STOP) ? "STOP" : "Run ");
443: monitor.Print(0, y++, "Req: %08x", (uint32)atomic_reqflag);
444: monitor.Print(0, y++, "MPU Speed: %d.%03dMHz",
445: (mpu_clock / 1000), (mpu_clock % 1000));
446: uint64 cycle = gMPU->total_cycle();
447: uint64 vt = GetVirtTime();
448: uint64 rt = GetRealTime();
449: uint64 relvt = vt - vtimebase;
450: uint64 relrt = rt - rtimebase;
451: // RealTime は実時間なのでスケジューラ開始からの時間に変換
452: rt -= rtimestart;
453: monitor.Print(0, y++, "Total Real Time: %18s", TimeToStr(rt).c_str());
454: monitor.Print(0, y++, " Virtual Time: %18s", TimeToStr(vt).c_str());
455: double ratio = (double)vt / rt * 100;
456: monitor.Print(0, y++, " Ratio : %3d.%01d%%",
457: (int)ratio, ((int)(ratio * 10) % 10));
458: monitor.Print(0, y++, "Moment Real Time: %18s", TimeToStr(relrt).c_str());
459: monitor.Print(0, y++, " Virtual Time: %18s", TimeToStr(relvt).c_str());
460: // XXX 移動平均をとる
461: double relratio = (double)relvt / relrt * 100;
462: monitor.Print(0, y++, " Ratio : %3d.%01d%%",
463: (int)relratio, ((int)(relratio * 10) % 10));
464:
465: // 0 1 2 3 4 5 6
1.1.1.2 root 466: // 0123456789012345678901234567890123456789012345678901234567890123456
467: // Set Time Remain Time Description Code
468: // 3.123'456'789 3.123'456'789 0123456789012345678901234 $01234567
1.1 root 469: x = 0;
470: y++;
1.1.1.4 root 471: monitor.Puts(x, y, "Set Time");
472: monitor.Puts(x + 16, y, "Remain Time");
473: monitor.Puts(x + 32, y, "Description");
474: monitor.Puts(x + 58, y, "Code");
1.1 root 475: y++;
476: for (const auto& e : gEvents) {
477: uint64 rem;
1.1.1.4 root 478: TA attr;
1.1 root 479: if (e->active) {
1.1.1.4 root 480: attr = TA::Normal;
1.1 root 481: rem = Cycle2Vtime(e->cycle - cycle);
482: } else {
483: attr = TA::Disable;
484: rem = 0;
485: }
486: monitor.Print(x, y++, attr,
1.1.1.2 root 487: "%3u.%03u'%03u'%03u %3u.%03u'%03u'%03u %-25s $%08x",
1.1 root 488: (uint)(e->time / (1000 * 1000 * 1000)),
489: (uint)((e->time / 1000 / 1000) % 1000),
490: (uint)((e->time / 1000) % 1000),
491: (uint)(e->time % 1000),
492: (uint)(rem / (1000 * 1000 * 1000)),
493: (uint)((rem / 1000 / 1000) % 1000),
494: (uint)((rem / 1000) % 1000),
495: (uint)(rem % 1000),
1.1.1.2 root 496: e->GetName().c_str(),
1.1 root 497: e->code);
498: }
499: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.