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