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