|
|
1.1 root 1: //
2: // nono
3: // Copyright (C) 2024 nono project
4: // Licensed under nono-license.txt
5: //
6:
7: //
8: // Goldfish (Qemu) RTC+Timer (の Timer のほう)
9: //
10:
11: // Google Goldfish RTC/Timer 仕様と QEMU 実装の仕様には互換性がない。
12: // オリジナルもまあまあ首をかしげる仕様だったので、それを QEMU が跡形なく
13: // 変更して使っているのかも知れない。知らんけど。
14: // ここで実装しているのは QEMU 仕様のほう。
15: //
16: // デバイスには 2 種類あり、Timer と RTC という名前で区別する。
17: // で、これはどこの仕様なのか QEMU の実装なのか分からないが、何故か
18: // RTC デバイスのベースアドレスは Timer デバイスのベースアドレス + 0x1000 の
19: // 位置らしい。
20: // どうして別デバイスにして BootInfo で別々に渡すよう設計しなかったのか…。
21: //
22: // 元仕様書にエンディアンのエの字もないので、たぶん何の考察もしてないのだと
23: // 思うが、ここではゲストエンディアン (ビッグエンディアン) デバイスに見せる。
24: // NetBSD/virt68k もそのように動いているようなので。
25:
26: // Timer デバイスのレジスタは次の通り。
27: //
28: // +$00.L TIME_LOW R-: ホスト時刻 [nsec] の下位 32 ビット
29: // +$04.L TIME_HIGH R-: ホスト時刻 [nsec] の上位 32 ビット
30: // +$08.L ALARM_LOW -W: アラーム時刻 [nsec] の下位 32 ビットとアラーム開始
31: // +$0c.L ALARM_HIGH -W: アラーム時刻 [nsec] の上位 32 ビット
32: // +$10.L INTR_STATUS RW: 割り込み許可禁止の取得(R)/設定(W)
33: // +$14.L ALARM_CLEAR -W: 動作中のアラームクリア
34: // +$18.L ALARM_STATUS R-: アラーム動作中なら $1、そうでなければ $0
35: // +$1c.L INTR_CLEAR -W: 割り込みクリア
36: //
37: // TIME_LOW、TIME_HIGH はホスト時刻を [nsec] で返す。
38: // TIME_LOW 読み込み時に 64 ビットをラッチするので必ず TIME_LOW、HIGH の順で
39: // 読み出すこと。ここでいうホスト時刻は、ホストのある時点 (大抵は VM の開始
40: // 時点) からの相対時刻。
41: //
42: // ALARM_LOW、ALARM_HIGH はアラーム時刻 [nsec] の設定。
43: // 現時刻からの間隔ではなくこのホスト時間軸上の時刻。
44: // 過去の時刻だったらどうなるとかの定義は一切ない…。
45: // ALARM_LOW がアラーム開始を兼ねているので必ず HIGH、LOW の順で書き込むこと。
46: //
47: // INTR_STATUS は書き込み時は最下位ビットが %1 なら割り込み許可、%0 なら禁止。
48: // 読み込み時は割り込み許可なら $1、禁止なら $0。
49:
50: #include "goldfish_timer.h"
51: #include "goldfish_pic.h"
52: #include "goldfish_rtc.h"
53: #include "scheduler.h"
54: #include "syncer.h"
55:
56: //
57: // GFTimer
58: //
59:
60: // コンストラクタ
61: GFTimerDevice::GFTimerDevice()
62: : inherited(OBJ_GFTIMER)
63: {
64: // func は Init() で設定する
65: event.code = 0;
66: event.Regist("Goldfish Timer");
67:
68: // モニタは GFRTC + GFTimer で共有。
69: monitor.func = ToMonitorCallback(&GFTimerDevice::MonitorUpdate);
70: monitor.SetSize(38, 7);
71: monitor.Regist(ID_MONITOR_RTC);
72: }
73:
74: // デストラクタ
75: GFTimerDevice::~GFTimerDevice()
76: {
77: }
78:
79: // 初期化
80: bool
81: GFTimerDevice::Init()
82: {
83: if (inherited::Init() == false) {
84: return false;
85: }
86:
87: // 割り込み信号線の接続先
88: auto gfpic = GetGFPICDevice(6);
89: gfpic->RegistIRQ(1, "GFTimer", this);
90: interrupt = gfpic;
91:
92: gfrtc = dynamic_cast<GFRTCDevice *>(GetRTCDevice());
93: syncer = GetSyncer();
94:
95: // クロックの同期方法
96: sync_rt = GetRTCDevice()->GetSyncRealtime();
97: // それによってイベントを分けてある。(time はどちらも都度セットする)
98: if (sync_rt) {
99: event.func = ToEventCallback(&GFTimerDevice::CallbackRT);
100: } else {
101: event.func = ToEventCallback(&GFTimerDevice::CallbackVT);
102: }
103:
104: return true;
105: }
106:
107: // 電源オン/リセット
108: void
109: GFTimerDevice::ResetHard(bool poweron)
110: {
111: scheduler->StopEvent(event);
112: elapsed = 0;
113: alarm = 0;
114: mtime = 0;
115: mtime_epoch = 0;
116:
117: intr_enable = false;
118: intr_assert = false;
119: ChangeInterrupt();
120: }
121:
122: busdata
123: GFTimerDevice::Read32(uint32 addr)
124: {
125: uint32 offset = addr & 0xfff;
126: busdata data;
127:
128: switch (offset) {
129: case GFRTC::TIME_LOW:
130: elapsed = Now();
131: data = (uint32)elapsed;
132: if (__predict_false(loglevel >= 2)) {
133: if (loglevel >= 3) {
134: putlogn("TIME_LOW -> $%08x", data.Data());
135: } else {
136: putlogn("TIME -> $%08x'%08x",
137: (uint32)(elapsed >> 32), data.Data());
138: }
139: }
140: break;
141:
142: case GFRTC::TIME_HIGH:
143: data = (uint32)(elapsed >> 32);
144: putlog(3, "TIME_HIGH -> $%08x", data.Data());
145: break;
146:
147: case GFRTC::INTR_STATUS:
148: data = intr_enable ? 1 : 0;
149: putlog(2, "INTR_STATUS -> $%08x", data.Data());
150: break;
151:
152: case GFRTC::ALARM_STATUS:
153: data = event.IsRunning() ? 1 : 0;
154: putlog(2, "ALARM_STATUS -> $%08x", data.Data());
155: break;
156:
157: default:
158: putlog(1, "Read unknown $%08x", addr);
159: data.SetBusErr();
160: break;
161: }
162: return data;
163: }
164:
165: busdata
166: GFTimerDevice::Write32(uint32 addr, uint32 data)
167: {
168: uint32 offset = addr & 0xfff;
169: busdata r;
170:
171: switch (offset) {
172: case GFRTC::ALARM_LOW:
173: alarm = alarm_high | data;
174: alarm_high = 0;
175: if (__predict_false(loglevel >= 2)) {
176: if (loglevel >= 3) {
177: putlogn("ALARM_LOW <- $%08x", data);
178: } else {
179: putlogn("ALARM <- $%08x'%08x",
180: (uint32)(alarm >> 32), (uint32)alarm);
181: }
182: }
183: StartAlarm();
184: break;
185:
186: case GFRTC::ALARM_HIGH:
187: putlog(3, "ALARM_HIGH <- $%08x", data);
188: alarm_high = (uint64)data << 32;
189: break;
190:
191: case GFRTC::INTR_STATUS:
192: intr_enable = data & 1;
193: if (__predict_false(loglevel >= 1)) {
194: if (intr_enable) {
195: putlogn("Enable interrupt");
196: } else {
197: putlogn("Disable interrupt");
198: }
199: }
200: ChangeInterrupt();
201: break;
202:
203: case GFRTC::ALARM_CLEAR:
204: putlog(1, "Clear alarm");
205: alarm = 0;
206: break;
207:
208: case GFRTC::INTR_CLEAR:
209: putlog(2, "Clear interrupt");
210: intr_assert = false;
211: ChangeInterrupt();
212: break;
213:
214: default:
215: putlog(1, "Write unknown $%08x <- $%08x", addr, data);
216: r = busdata::BusErr;
217: break;
218: }
219:
220: return r;
221: }
222:
223: busdata
224: GFTimerDevice::Peek8(uint32 addr)
225: {
226: uint32 offset = addr & 0xfff;
227: switch (offset) {
228: case GFRTC::TIME_LOW + 0:
229: case GFRTC::TIME_LOW + 1:
230: case GFRTC::TIME_LOW + 2:
231: case GFRTC::TIME_LOW + 3:
232: case GFRTC::TIME_HIGH + 0:
233: case GFRTC::TIME_HIGH + 1:
234: case GFRTC::TIME_HIGH + 2:
235: case GFRTC::TIME_HIGH + 3:
236: {
237: uint64 now = Now();
238: switch (offset) {
239: case GFRTC::TIME_LOW + 0:
240: return (now >> 24) & 0xff;
241: case GFRTC::TIME_LOW + 1:
242: return (now >> 16) & 0xff;
243: case GFRTC::TIME_LOW + 2:
244: return (now >> 8) & 0xff;
245: case GFRTC::TIME_LOW + 3:
246: return now & 0xff;
247:
248: case GFRTC::TIME_HIGH + 0:
249: return (now >> 56) & 0xff;
250: case GFRTC::TIME_HIGH + 1:
251: return (now >> 48) & 0xff;
252: case GFRTC::TIME_HIGH + 2:
253: return (now >> 40) & 0xff;
254: case GFRTC::TIME_HIGH + 3:
255: return (now >> 32) & 0xff;
256: default:
257: __unreachable();
258: }
259: }
260:
261: case GFRTC::INTR_STATUS + 0:
262: case GFRTC::INTR_STATUS + 1:
263: case GFRTC::INTR_STATUS + 2:
264: return 0;
265: case GFRTC::INTR_STATUS + 3:
266: return intr_assert ? 1 : 0;
267:
268: case GFRTC::ALARM_STATUS + 0:
269: case GFRTC::ALARM_STATUS + 1:
270: case GFRTC::ALARM_STATUS + 2:
271: return 0;
272: case GFRTC::ALARM_STATUS + 3:
273: return event.IsRunning() ? 1 : 0;
274:
275: default:
276: return busdata::BusErr;
277: }
278: }
279:
280: void
281: GFTimerDevice::MonitorUpdate(Monitor *, TextScreen& screen)
282: {
283: int y = 0;
284:
285: screen.Clear();
286:
287: uint64 now = Now();
288:
289: screen.Puts(0, y++, "<Timer>");
290: screen.Print(0, y++, "Time: %26s", TimeToStr(now).c_str());
291: screen.Print(0, y++, "Alarm: %26s", TimeToStr(alarm).c_str());
292: y++;
293:
294: gfrtc->MonitorUpdateRTC(screen, y);
295: }
296:
297: // 現在時刻を返す
298: uint64
299: GFTimerDevice::Now() const
300: {
301: uint64 now;
302:
303: if (sync_rt) {
304: now = syncer->GetRealTime();
305: } else {
306: now = scheduler->GetVirtTime();
307: }
308: return now;
309: }
310:
311: // アラーム開始。(ALARM_LOW の書き込みで呼ばれる)
312: void
313: GFTimerDevice::StartAlarm()
314: {
315: if (__predict_false(mtime_epoch == 0)) {
316: // 最初のアラーム書き込み時を修正実時間軸の基準点にする。
317: mtime_epoch = syncer->GetRealTime();
318: mtime = mtime_epoch;
319: }
320:
321: // 仮想時間モードなら ALARM 時刻に発火するイベントをセットするだけ。
322: // 実時間モードならひとまず仮に本来の仮想時刻にイベントは起こすが、
323: // 割り込みを上げるかどうかは向こうで調整する。
324: // event.code は VT モードの時には不要だが気にせず立てる。
325: event.time = alarm - Now();
326: event.code = 1;
327: scheduler->RestartEvent(event);
328: }
329:
330: // 実時間モードでのコールバック。
331: void
332: GFTimerDevice::CallbackRT(Event& ev)
333: {
334: // アラーム開始後最初の1回だけ自分の時間軸をアラーム分だけ進める。
335: if (event.code) {
336: mtime += ev.time;
337: event.code = 0;
338: }
339:
340: uint64 mnow = syncer->GetRealTime() - mtime_epoch;
341: if (mtime >= mnow) {
342: // システムの実時間軸を追い越していたら一回休んで
343: // もう一度このイベントに来る。
344: scheduler->RestartEvent(event);
345: } else {
346: // システムの実時間軸に追い付いてなければ(通常動作)、アラーム発砲。
347: intr_assert = true;
348: ChangeInterrupt();
349: }
350: }
351:
352: // 仮想時間モードでのコールバック。
353: void
354: GFTimerDevice::CallbackVT(Event& ev)
355: {
356: // 仮想時間モードなら、イベントが起きたここがゴール。
357: // アラームは単発イベントなのでリスタート不要。
358: intr_assert = true;
359: ChangeInterrupt();
360: }
361:
362: void
363: GFTimerDevice::ChangeInterrupt()
364: {
365: bool irq = (intr_enable && intr_assert);
366: interrupt->ChangeINT(this, irq);
367: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.