|
|
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"
1.1.1.6 root 51: #include "event.h"
1.1 root 52: #include "goldfish_pic.h"
53: #include "goldfish_rtc.h"
1.1.1.7 ! root 54: #include "mainbus.h"
1.1.1.3 root 55: #include "monitor.h"
1.1 root 56: #include "scheduler.h"
57: #include "syncer.h"
58:
59: //
60: // GFTimer
61: //
62:
63: // コンストラクタ
64: GFTimerDevice::GFTimerDevice()
65: : inherited(OBJ_GFTIMER)
66: {
67: // モニタは GFRTC + GFTimer で共有。
1.1.1.3 root 68: monitor = gMonitorManager->Regist(ID_MONITOR_RTC, this);
1.1.1.7 ! root 69: monitor->SetCallback(&GFTimerDevice::MonitorScreen);
1.1.1.3 root 70: monitor->SetSize(38, 7);
1.1 root 71: }
72:
73: // デストラクタ
74: GFTimerDevice::~GFTimerDevice()
75: {
76: }
77:
78: // 初期化
79: bool
80: GFTimerDevice::Init()
81: {
82: // 割り込み信号線の接続先
83: auto gfpic = GetGFPICDevice(6);
84: gfpic->RegistIRQ(1, "GFTimer", this);
85: interrupt = gfpic;
86:
87: gfrtc = dynamic_cast<GFRTCDevice *>(GetRTCDevice());
88: syncer = GetSyncer();
89:
1.1.1.6 root 90: auto evman = GetEventManager();
91: event = evman->Regist(this, NULL, "Goldfish Timer");
92:
1.1 root 93: // クロックの同期方法
1.1.1.5 root 94: sync_rt = syncer->GetSyncRealtime();
1.1.1.2 root 95:
1.1 root 96: // それによってイベントを分けてある。(time はどちらも都度セットする)
97: if (sync_rt) {
1.1.1.7 ! root 98: event->SetCallback(&GFTimerDevice::CallbackRT);
1.1 root 99: } else {
1.1.1.7 ! root 100: event->SetCallback(&GFTimerDevice::CallbackVT);
1.1 root 101: }
102:
103: return true;
104: }
105:
106: // 電源オン/リセット
107: void
108: GFTimerDevice::ResetHard(bool poweron)
109: {
110: scheduler->StopEvent(event);
1.1.1.7 ! root 111: elapsed.q = 0;
! 112: alarm.q = 0;
1.1 root 113: mtime = 0;
114: mtime_epoch = 0;
115:
116: intr_enable = false;
117: intr_assert = false;
118: ChangeInterrupt();
119: }
120:
121: busdata
1.1.1.2 root 122: GFTimerDevice::ReadPort(uint32 offset)
1.1 root 123: {
124: busdata data;
125:
126: switch (offset) {
127: case GFRTC::TIME_LOW:
1.1.1.7 ! root 128: elapsed.q = Now();
! 129: data = elapsed.l;
1.1 root 130: if (__predict_false(loglevel >= 2)) {
131: if (loglevel >= 3) {
132: putlogn("TIME_LOW -> $%08x", data.Data());
133: } else {
1.1.1.7 ! root 134: putlogn("TIME -> $%08x'%08x", elapsed.h, data.Data());
1.1 root 135: }
136: }
137: break;
138:
139: case GFRTC::TIME_HIGH:
1.1.1.7 ! root 140: data = elapsed.h;
1.1 root 141: putlog(3, "TIME_HIGH -> $%08x", data.Data());
142: break;
143:
144: case GFRTC::INTR_STATUS:
145: data = intr_enable ? 1 : 0;
146: putlog(2, "INTR_STATUS -> $%08x", data.Data());
147: break;
148:
149: case GFRTC::ALARM_STATUS:
1.1.1.6 root 150: data = event->IsRunning() ? 1 : 0;
1.1 root 151: putlog(2, "ALARM_STATUS -> $%08x", data.Data());
152: break;
153:
154: default:
1.1.1.7 ! root 155: putlog(1, "Read unknown $%08x", GetMainbusDevice()->GetPaddr());
1.1 root 156: data.SetBusErr();
157: break;
158: }
1.1.1.2 root 159:
160: data |= BusData::Size4;
1.1 root 161: return data;
162: }
163:
164: busdata
1.1.1.2 root 165: GFTimerDevice::WritePort(uint32 offset, uint32 data)
1.1 root 166: {
167: busdata r;
168:
169: switch (offset) {
170: case GFRTC::ALARM_LOW:
1.1.1.7 ! root 171: alarm.h = alarm_high;
! 172: alarm.l = data;
1.1 root 173: alarm_high = 0;
174: if (__predict_false(loglevel >= 2)) {
175: if (loglevel >= 3) {
176: putlogn("ALARM_LOW <- $%08x", data);
177: } else {
1.1.1.7 ! root 178: putlogn("ALARM <- $%08x'%08x", alarm.h, alarm.l);
1.1 root 179: }
180: }
181: StartAlarm();
182: break;
183:
184: case GFRTC::ALARM_HIGH:
185: putlog(3, "ALARM_HIGH <- $%08x", data);
1.1.1.7 ! root 186: alarm_high = data;
1.1 root 187: break;
188:
189: case GFRTC::INTR_STATUS:
190: intr_enable = data & 1;
191: if (__predict_false(loglevel >= 1)) {
192: if (intr_enable) {
193: putlogn("Enable interrupt");
194: } else {
195: putlogn("Disable interrupt");
196: }
197: }
198: ChangeInterrupt();
199: break;
200:
201: case GFRTC::ALARM_CLEAR:
202: putlog(1, "Clear alarm");
1.1.1.7 ! root 203: alarm.q = 0;
1.1 root 204: break;
205:
206: case GFRTC::INTR_CLEAR:
207: putlog(2, "Clear interrupt");
208: intr_assert = false;
209: ChangeInterrupt();
210: break;
211:
212: default:
1.1.1.7 ! root 213: putlog(1, "Write unknown $%08x <- $%08x",
! 214: GetMainbusDevice()->GetPaddr(), data);
1.1.1.2 root 215: r = BusData::BusErr;
1.1 root 216: break;
217: }
218:
1.1.1.2 root 219: r |= BusData::Size4;
1.1 root 220: return r;
221: }
222:
223: busdata
1.1.1.2 root 224: GFTimerDevice::PeekPort(uint32 offset)
1.1 root 225: {
226: switch (offset) {
1.1.1.2 root 227: case GFRTC::TIME_LOW:
228: return Now() & 0xffffffffU;
229: case GFRTC::TIME_HIGH:
230: return Now() >> 32;
231: case GFRTC::INTR_STATUS:
1.1 root 232: return intr_assert ? 1 : 0;
1.1.1.2 root 233: case GFRTC::ALARM_STATUS:
1.1.1.6 root 234: return event->IsRunning() ? 1 : 0;
1.1 root 235: default:
1.1.1.2 root 236: return BusData::BusErr;
1.1 root 237: }
238: }
239:
240: void
1.1.1.7 ! root 241: GFTimerDevice::MonitorScreen(Monitor *, TextScreen& screen)
1.1 root 242: {
243: int y = 0;
244:
245: screen.Clear();
246:
247: uint64 now = Now();
248:
249: screen.Puts(0, y++, "<Timer>");
250: screen.Print(0, y++, "Time: %26s", TimeToStr(now).c_str());
1.1.1.7 ! root 251: screen.Print(0, y++, "Alarm: %26s", TimeToStr(alarm.q).c_str());
1.1 root 252: y++;
253:
1.1.1.7 ! root 254: gfrtc->MonitorScreenRTC(screen, y);
1.1 root 255: }
256:
257: // 現在時刻を返す
258: uint64
259: GFTimerDevice::Now() const
260: {
261: uint64 now;
262:
263: if (sync_rt) {
264: now = syncer->GetRealTime();
265: } else {
266: now = scheduler->GetVirtTime();
267: }
268: return now;
269: }
270:
271: // アラーム開始。(ALARM_LOW の書き込みで呼ばれる)
272: void
273: GFTimerDevice::StartAlarm()
274: {
1.1.1.3 root 275: uint64 now = Now();
276:
1.1 root 277: if (__predict_false(mtime_epoch == 0)) {
278: // 最初のアラーム書き込み時を修正実時間軸の基準点にする。
1.1.1.3 root 279: mtime_epoch = now;
280: mtime = 0;
1.1 root 281: }
282:
283: // 仮想時間モードなら ALARM 時刻に発火するイベントをセットするだけ。
284: // 実時間モードならひとまず仮に本来の仮想時刻にイベントは起こすが、
285: // 割り込みを上げるかどうかは向こうで調整する。
1.1.1.6 root 286: // event->code は VT モードの時には不要だが気にせず立てる。
1.1.1.2 root 287: // 指定されたアラームがすでに過去の時刻なら QEMU は即発火するようだ。
1.1.1.7 ! root 288: if (__predict_true(alarm.q > now)) {
! 289: event->time = alarm.q - now;
1.1.1.2 root 290: } else {
291: if (__predict_false(loglevel >= 1)) {
1.1.1.7 ! root 292: uint64 delta = now - alarm.q;
1.1.1.2 root 293: putlogn("ALARM: past alarm -%s", TimeToStr(delta).c_str());
294: }
1.1.1.6 root 295: event->time = 0;
1.1.1.2 root 296: }
1.1.1.6 root 297: event->code = 1;
1.1 root 298: scheduler->RestartEvent(event);
299: }
300:
301: // 実時間モードでのコールバック。
302: void
1.1.1.6 root 303: GFTimerDevice::CallbackRT(Event *ev)
1.1 root 304: {
305: // アラーム開始後最初の1回だけ自分の時間軸をアラーム分だけ進める。
1.1.1.6 root 306: if (event->code) {
307: mtime += event->time;
308: event->code = 0;
1.1 root 309: }
310:
311: uint64 mnow = syncer->GetRealTime() - mtime_epoch;
312: if (mtime >= mnow) {
313: // システムの実時間軸を追い越していたら一回休んで
314: // もう一度このイベントに来る。
315: scheduler->RestartEvent(event);
316: } else {
1.1.1.2 root 317: // システムの実時間軸に追い付いてなければ(通常動作)、アラーム発報。
1.1 root 318: intr_assert = true;
319: ChangeInterrupt();
320: }
321: }
322:
323: // 仮想時間モードでのコールバック。
324: void
1.1.1.6 root 325: GFTimerDevice::CallbackVT(Event *ev)
1.1 root 326: {
327: // 仮想時間モードなら、イベントが起きたここがゴール。
328: // アラームは単発イベントなのでリスタート不要。
329: intr_assert = true;
330: ChangeInterrupt();
331: }
332:
333: void
334: GFTimerDevice::ChangeInterrupt()
335: {
336: bool irq = (intr_enable && intr_assert);
337: interrupt->ChangeINT(this, irq);
338: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.