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