|
|
1.1 root 1: //
2: // nono
3: // Copyright (C) 2022 nono project
4: // Licensed under nono-license.txt
5: //
6:
7: //
8: // 実時間同期
9: //
10:
11: #include "syncer.h"
12: #include "config.h"
1.1.1.4 root 13: #include "monitor.h"
1.1.1.3 root 14: #include "mpu680x0.h"
1.1 root 15: #include "scheduler.h"
16:
17: //#define DEBUG
18:
19: #if defined(DEBUG)
20: #define DPRINTF(fmt...) printf(fmt);
21: #else
22: #define DPRINTF(fmt...) /**/
23: #endif
24:
25: // パフォーマンス測定を行う実時間間隔
26: static constexpr uint64 PERF_INTERVAL = 1000_msec;
27:
28: // コンストラクタ
29: Syncer::Syncer()
30: : inherited(OBJ_SYNCER)
31: {
32: for (int i = 0; i < perfq.Capacity(); i++) {
33: perfq.Enqueue(1000);
34: }
35: }
36:
37: // デストラクタ
38: Syncer::~Syncer()
39: {
40: }
41:
42: // 初期化
43: bool
44: Syncer::Init()
45: {
1.1.1.3 root 46: mpu680x0 = dynamic_cast<MPU680x0Device *>(mpu);
47:
1.1.1.5 ! root 48: mode = SYNCMODE_POWEROFF;
1.1 root 49: overslept = 0;
1.1.1.5 ! root 50: dmac_idle = 1;
1.1 root 51:
52: // 高速モード指定
53: bool fast_mode = gConfig->Find("fast-mode").AsInt();
54: RequestFullSpeed(fast_mode);
55:
56: scheduler->ConnectMessage(MessageID::SCHEDULER_SYNCMODE, this,
1.1.1.2 root 57: ToMessageCallback(&Syncer::ChangeModeMessage));
1.1 root 58:
1.1.1.3 root 59: sync_event.func = ToEventCallback(&Syncer::SyncCallback);
60: sync_event.time = 50_msec;
61: sync_event.SetName("Sync");
62: scheduler->RegistEvent(sync_event);
63:
1.1 root 64: return true;
65: }
66:
67: void
68: Syncer::StartRealTime()
69: {
70: realtime.Start();
71: }
72:
73: void
74: Syncer::StopRealTime()
75: {
76: realtime.Stop();
77: }
78:
79: // 時間の始まり
80: void
81: Syncer::StartTime()
82: {
83: scheduler->StartEvent(sync_event);
84:
85: // 諸々の初期化が終わった頃にタイマーを開始。
86: // Stopwatch.Restart() は .Net 由来なので
87: // Restart() は経過時間をリセットしてからタイマーを開始の意。
88: realtime.Restart();
89:
90: rtime = realtime.Elapsed();
91: rtime_epoch = rtime;
92: vtime_epoch = scheduler->GetVirtTime();
93:
94: // パフォーマンス表示用。
95: // 最初の集計が行われる起動後1秒までは、この値が表示されるので
96: // インチキして 100% にしておく。
97: perf_counter = 100;
98: last_perf_rtime = rtime_epoch;
99: last_perf_vtime = vtime_epoch;
100: next_perf_rtime = PERF_INTERVAL;
101: }
102:
103: // CPU 状態変更(通常/STOP/HALT)を指示する。
104: // CPU が呼ぶはずなので自スレッドからのみ呼ばれるはず。
105: void
106: Syncer::RequestCPUMode(uint32 cpumode)
107: {
108: uint32 newmode;
109:
1.1.1.5 ! root 110: halt = cpumode & SYNCMODE_CPU_HALT;
! 111: cpu_idle = cpumode & SYNCMODE_CPU_STOP;
! 112:
! 113: newmode = (mode & ~SYNCMODE_CPU_MASK) | halt | (cpu_idle & dmac_idle);
! 114:
! 115: ChangeMode(newmode);
! 116: }
! 117:
! 118: // DMAC のアクティブ状態を指示する。
! 119: void
! 120: Syncer::RequestDMACActive(bool active)
! 121: {
! 122: uint32 newmode;
! 123:
! 124: dmac_idle = active ? 0 : SYNCMODE_IDLE;
! 125:
! 126: newmode = (mode & ~SYNCMODE_IDLE) | (cpu_idle & dmac_idle);
1.1 root 127:
128: ChangeMode(newmode);
129: }
130:
131: // 高速モード/同期モードの変更を指示する。
132: // true なら高速モード、false なら同期モード。
133: // 他スレッドから呼んでもよい。
134: void
135: Syncer::RequestFullSpeed(bool enable)
136: {
137: scheduler->SendMessage(MessageID::SCHEDULER_SYNCMODE, enable);
138: }
139:
140: // ブートページモードの変更を指示する。
1.1.1.2 root 141: // (MainbusBaseDevice::SwitchBootPage から呼ぶので個別には呼ばないこと)
1.1 root 142: void
143: Syncer::RequestBootPageMode(bool isrom)
144: {
145: uint32 newmode;
146:
147: if (isrom) {
1.1.1.5 ! root 148: newmode = mode | SYNCMODE_BOOT;
1.1 root 149: } else {
1.1.1.5 ! root 150: newmode = mode & ~SYNCMODE_BOOT;
1.1 root 151: }
152:
153: ChangeMode(newmode);
154: }
155:
156: // キー入力状態の変更を指示する。
157: void
158: Syncer::RequestKeyPressed(bool pressed)
159: {
160: uint32 newmode;
161:
162: if (pressed) {
1.1.1.5 ! root 163: newmode = mode | SYNCMODE_KEY;
1.1 root 164: } else {
1.1.1.5 ! root 165: newmode = mode & ~SYNCMODE_KEY;
1.1 root 166: }
167:
168: ChangeMode(newmode);
169: }
170:
171: // 電源状態の変更を指示する。
172: void
173: Syncer::RequestPowerOffMode(bool poweroff)
174: {
175: uint32 newmode;
176:
177: if (poweroff) {
1.1.1.5 ! root 178: newmode = mode | SYNCMODE_POWEROFF;
1.1 root 179: } else {
1.1.1.5 ! root 180: newmode = mode & ~SYNCMODE_POWEROFF;
1.1 root 181: }
182:
183: ChangeMode(newmode);
184: }
185:
186: // 動作状態変更のメッセージコールバック
187: void
1.1.1.2 root 188: Syncer::ChangeModeMessage(MessageID msgid, uint32 arg)
1.1 root 189: {
190: uint32 newmode;
191:
192: if (msgid == MessageID::SCHEDULER_SYNCMODE) {
193: if (arg) {
1.1.1.5 ! root 194: newmode = mode & ~SYNCMODE_SYNC;
1.1 root 195: } else {
1.1.1.5 ! root 196: newmode = mode | SYNCMODE_SYNC;
1.1 root 197: }
198:
199: ChangeMode(newmode);
200: }
201: }
202:
1.1.1.5 ! root 203: // newmode に従ってスケジューラの動作状態を変更。
1.1 root 204: // 自スレッドから呼ぶこと。
205: void
206: Syncer::ChangeMode(uint32 newmode)
207: {
208: // いずれかでも変化していれば..
209: if (mode != newmode) {
210: // 動作モード変更要因を全部調べた結果、変更が必要か。
211: // mode、newmode どちらもゼロ(高速モード)か非ゼロ(通常モード)か
212: // なので、等値比較ではなく二値にしてから比較。
213: if ((bool)mode != (bool)newmode) {
214: // モードが変わるのでここで一旦同期をとる
215: DoSync();
216:
217: if (newmode == 0) {
218: // 高速モードに
219: putlog(2, "Fast mode");
220: } else {
221: // 同期モードに
222: putlog(2, "Sync mode");
223:
224: // 同期用の基準点を設置。
225: // ゲストが STOP 命令による定常状態にいる時は
226: // 割り込みなど短期間だけ高速モードになるのを繰り返す。
227: // スリープ粒度の粗い NetBSD ホストでは同期モードに
228: // 切り替えるたびに基準点を移動していると、スリープ
229: // しすぎてしまうので、実時間と仮想時間のズレが小さい場合は
230: // 基準点を移動せず無視する。
231: uint64 vtime = scheduler->GetVirtTime();
232: uint64 rspan = rtime - rtime_epoch;
233: uint64 vspan = vtime - vtime_epoch;
234: if (vspan > rspan + overslept) {
235: rtime_epoch = rtime;
236: vtime_epoch = vtime;
237: }
238: }
239: }
240:
241: // ブール比較の後で、全ビットを維持したまま代入。
242: mode = newmode;
243: }
244: DPRINTF("ChangeMode end mode=$%x\n", mode);
245: }
246:
247: // 同期イベント
248: void
249: Syncer::SyncCallback(Event& ev)
250: {
251: DPRINTF("SyncCallback\n");
252: DoSync();
253:
254: // 次回のイベントを再登録
255: scheduler->StartEvent(ev);
256: }
257:
258: // 同期処理
259: // ここで仮想時間を実時間と同期させる。
260: // スリープしすぎた実時間を返す。
261: void
262: Syncer::DoSync()
263: {
264: // 実時間をここで更新
265: rtime = realtime.Elapsed();
266: DPRINTF("Sync begin rtime=%s\n", SecToStr(rtime).c_str());
267:
268: // 統計情報
269: sync_count++;
270: // XXX どこでやる?
271: if (__predict_false(rtime >= next_perf_rtime)) {
272: CalcPerf();
273: }
274:
275: // XXX 次回 Start する時のイベント間隔をここ(DoSync())で事前に決めている
276: sync_event.time = 50_msec;
277:
278: // 同期モードなら..
279: if (mode != 0) {
280: uint64 vtime = scheduler->GetVirtTime();
281: uint64 rspan = rtime - rtime_epoch;
282: uint64 vspan = vtime - vtime_epoch;
283: if (vspan > rspan) {
284: // 仮想時間のほうが進んでいれば、スリープして待つ
285: uint64 diff = vspan - rspan;
286: uint64 prev = rtime;
287:
288: DPRINTF("Sync wait_for=%s\n", SecToStr(diff).c_str());
289: scheduler->Sleep(diff);
290:
291: // スリープしすぎた時間を計測。
292: // この時間にはスリープ時間を計算するための時間も含む。
293: rtime = realtime.Elapsed();
294: DPRINTF("Sync wakeup rtime=%s\n", SecToStr(rtime).c_str());
295: uint64 realslept = rtime - prev;
296: if (realslept > diff) {
297: uint64 over = realslept - diff;
298: if (over > 1_sec) {
299: // スリープ中にサスペンドされたと推定する
300: putlog(1, "時刻再同期(スリープ中の実時間跳躍)");
301: rtime_epoch = rtime;
302: vtime_epoch = vtime;
303: } else if (over > overslept) {
304: overslept = over;
305: }
306: } else if (realslept < diff) {
307: uint64 under = diff - realslept;
308: if (under > overslept) {
309: sync_event.time = 0;
310: }
311: }
312: } else if (rspan > vspan + overslept) {
313: // 実時間のほうが時間精度より進んでいれば、サスペンドとかから
314: // 復帰してきた状態だと推定して、基準時刻を再同期する。
315: putlog(1, "時刻再同期(実行中の実時間跳躍)");
316: rtime_epoch = rtime;
317: vtime_epoch = vtime;
318: } else {
319: // 同期の範囲内にいるので何もしなくていい
320: }
321: }
322: }
323:
324: void
325: Syncer::CalcPerf()
326: {
327: uint64 vtime = scheduler->GetVirtTime();
328: uint64 rperf = rtime - last_perf_rtime;
329: uint64 vperf = vtime - last_perf_vtime;
330:
331: // 百分率で小数以下1桁分までとなるよう整数で計算する
332: perfq.EnqueueForce((uint32)(vperf * 1000 / rperf));
333: // 移動平均を求める (perfq は常に要素数分埋めてある)
1.1.1.3 root 334: uint ma = 0;
335: uint qlen = perfq.Length();
336: __assume(qlen != 0);
1.1 root 337: for (int i = 0; i < qlen; i++) {
338: auto v = perfq.Peek(i);
339: ma += v;
340: }
341: ma /= qlen;
342: // 最下位を四捨五入して捨てる (整数%になる)
343: ma = (ma + 5) / 10;
344: perf_counter = ma;
345:
346: // 直近1秒の Sync 回数
347: last_sync_count = sync_count;
348: sync_count = 0;
349:
1.1.1.3 root 350: // 関係ないけど、ついでにここで MPU 内の各種移動平均をとる。
351: if (mpu680x0) {
352: mpu680x0->CalcStat();
353: }
354:
1.1 root 355: last_perf_rtime = rtime;
356: last_perf_vtime = vtime;
357: next_perf_rtime = rtime + PERF_INTERVAL;
358: }
359:
360: // パフォーマンス測定用に実時間をログ出力
361: void
362: Syncer::PutlogRealtime()
363: {
364: uint64 vtime = scheduler->GetVirtTime();
365:
366: // 電源オン時からここまでの時間を表示するだけでリセット等はしない。
1.1.1.3 root 367: putlog(0, "Elapsed since power-on: %u.%03u sec, %6.2f%%",
368: (uint)(rtime / (1000 * 1000 * 1000)),
369: (uint)((rtime / (1000 * 1000)) % 1000),
1.1 root 370: (double)vtime / rtime * 100);
371:
372: #if 0
373: // イベントキューの性能評価用
374: uint64 sum = 0;
375: for (const auto e : events) {
376: sum += e->count;
377: }
378: putlog(0, "%lu events done, %6.3fM/sec", sum, (double)sum / rtime * 1000);
379: #endif
380: }
381:
382: // モニタ更新の下請け (Scheduler から呼ばれる)
383: int
384: Syncer::MonitorUpdateSub(TextScreen& screen, uint64 vtime)
385: {
386: int y = 0;
387:
388: screen.Print(0, y, "Mode: 0x%04x (", mode);
1.1.1.5 ! root 389: switch (mode & SYNCMODE_CPU_MASK) {
! 390: case SYNCMODE_CPU_NORMAL:
1.1 root 391: screen.Puts(14, y, TA::Off, "Run");
392: break;
1.1.1.5 ! root 393: case SYNCMODE_CPU_STOP:
1.1 root 394: screen.Puts(14, y, TA::On, "STOP");
395: break;
1.1.1.5 ! root 396: case SYNCMODE_CPU_HALT:
1.1 root 397: screen.Puts(14, y, TA::On, "HALT");
398: break;
399: }
1.1.1.5 ! root 400: if ((mode & SYNCMODE_SYNC)) {
1.1 root 401: screen.Puts(19, y, TA::On, "Sync");
402: } else {
403: screen.Puts(19, y, TA::Off, "Full");
404: }
1.1.1.5 ! root 405: screen.Puts(24, y, TA::OnOff((mode & SYNCMODE_BOOT)), "Boot");
! 406: screen.Puts(29, y, TA::OnOff((mode & SYNCMODE_KEY)), "Key");
! 407: screen.Puts(34, y, TA::OnOff((mode & SYNCMODE_POWEROFF)), "POff");
1.1 root 408: screen.Puts(38, y, ")");
409: y++;
410: uint64 rt = rtime;
411: uint64 vt = vtime;
412:
413: auto s_rt = TimeToStr(rt);
414: auto s_vt = TimeToStr(vt);
415: std::string s_sync_rt {};
416: std::string s_sync_vt {};
417: if (mode != 0) {
418: uint64 sync_rt = rtime - rtime_epoch;
419: uint64 sync_vt = vtime - vtime_epoch;
420: s_sync_rt = TimeToStr(sync_rt);
421: s_sync_vt = TimeToStr(sync_vt);
422: }
423: // 日まで表示しようとするときは桁位置を動かす
424: int width = std::max(rt, vt) < 86400_sec ? 20 : 25;
425:
426: screen.Print(0, y++, "Total Real Time: %*s", width, s_rt.c_str());
427: screen.Print(4, y++, "Virtual Time: %*s", width, s_vt.c_str());
428: screen.Print(0, y++, "Sync Real Time: %*s", width, s_sync_rt.c_str());
429: screen.Print(4, y++, "Virtual Time: %*s", width, s_sync_vt.c_str());
430:
1.1.1.3 root 431: screen.Puts(0, y, "Rating MPU Speed :");
432: uint clock_khz = mpu->GetClockSpeed();
433: if (__predict_true((clock_khz % 1000) == 0)) {
434: screen.Print(21, y++, "%4u.%1uMHz",
435: clock_khz / 1000, (clock_khz / 100) % 10);
436: } else {
437: screen.Print(21, y++, "%4u.%03uMHz",
438: clock_khz / 1000, clock_khz % 1000);
439: }
440: uint eff_khz = clock_khz * perf_counter / 100;
441: screen.Print(0, y++, "Effective MPU Speed: %4u.%1uMHz (%4u%%)",
442: (eff_khz / 1000), (eff_khz / 100) % 10, perf_counter);
1.1 root 443:
444: int x = 52;
445: int y1 = 0;
446: screen.Print(x, y1++, "Max OverSlept: %13s", SecToStr(overslept).c_str());
447: screen.Print(x, y1++, "Sync per sec : %4u", last_sync_count);
448:
449: return y;
450: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.