|
|
1.1 root 1: //
2: // nono
3: // Copyright (C) 2020 nono project
4: // Licensed under nono-license.txt
5: //
6:
7: // MC88200(CMMU)
8:
9: #include "m88100.h"
10: #include "m88200.h"
11:
1.1.1.3 root 12: // static 変数
13: m88200 *m88200::mbus_master = NULL;
14:
1.1 root 15: // m88200 コンストラクタ
1.1.1.7 root 16: //
17: // ここのオブジェクト名は仮で、ID が確定した時点でもう一度セットする
18: // (see SetID())
1.1 root 19: m88200::m88200()
1.1.1.7 root 20: : inherited("CMMU")
1.1 root 21: {
1.1.1.7 root 22: // objname 等は SetID() で確定する
1.1 root 23:
1.1.1.9 root 24: reg_monitor.func = ToMonitorCallback(&m88200::MonitorUpdateReg);
1.1.1.7 root 25: reg_monitor.SetSize(52, 8);
1.1.1.9 root 26: atc_monitor.func = ToMonitorCallback(&m88200::MonitorUpdateATC);
27: #if defined(M88200_STAT)
28: atc_monitor.SetSize(77, 39 + 15);
29: #else
30: atc_monitor.SetSize(77, 39);
31: #endif
32: cache_monitor.func = ToMonitorCallback(&m88200::MonitorUpdateCache);
1.1.1.7 root 33: cache_monitor.SetSize(82, 23);
34: // モニタ名も SetID() で決まるので、続きは向こうでやる
1.1 root 35:
36: // ログ表示用の名前
37: sapr.name = "SAPR";
38: uapr.name = "UAPR";
39:
40: // 暗黙 BATC を初期化
1.1.1.9 root 41: SetBWP(8, 0xfff7ffb5); // $fff8xxxx,S,WT,CI,V
42: SetBWP(9, 0xfffffff5); // $ffffxxxx,S,WT,CI,V
1.1 root 43:
44: // セットインデックスを初期化
45: for (int i = 0; i < setarray.size(); i++) {
46: auto& set = setarray[i];
47: set.setidx = i;
48: }
49: }
50:
51: // m88200 デストラクタ
52: m88200::~m88200()
53: {
54: }
55:
56: // 本当はコンストラクタ渡ししたかったもの
57: void
58: m88200::Ctor(m88kcpu *parent_)
59: {
60: parent = parent_;
61: }
62:
63: // リセット
64: void
65: m88200::Reset()
66: {
1.1.1.3 root 67: const uint32 Undefined = 0xcccccccc;
68:
1.1 root 69: // レジスタ (p6-4, Table6-3)
1.1.1.3 root 70: // ID, version は別途 Set される。
1.1 root 71: command = 0;
72: ssr = 0;
1.1.1.3 root 73: sar = Undefined;
1.1 root 74: sctr = 0;
75: fault_code = 0;
1.1.1.3 root 76: fault_addr = Undefined;
1.1 root 77: sapr.enable = false;
78: sapr.stat = APR_CI;
79: uapr.enable = false;
80: uapr.stat = APR_CI;
81: }
82:
1.1.1.7 root 83: // モニター更新 (CMMU レジスタ)
1.1.1.2 root 84: void
1.1.1.7 root 85: m88200::MonitorUpdateReg(Monitor *, TextScreen& screen)
1.1 root 86: {
1.1.1.3 root 87: uint32 baseaddr;
1.1 root 88: uint32 reg;
89: int x;
90: int y;
91:
1.1.1.3 root 92: baseaddr = 0xfff00000 + (id << 12);
93:
1.1.1.7 root 94: screen.Clear();
1.1.1.3 root 95: x = 24;
1.1 root 96:
97: y = 0;
1.1.1.7 root 98: screen.Print(0, y++, "$%08x IDR:%08x ID=$%02x Type=5(88200) Ver=$%x",
1.1.1.3 root 99: baseaddr + 0, GetIDR(), id, version);
1.1 root 100:
101: reg = GetSSR();
1.1.1.7 root 102: screen.Print(0, y, "$%08x SSR:%08x", baseaddr + 0x008, reg);
103: screen.Puts(x, y, TA::OnOff(reg & SSR_CE), "CE");
104: screen.Puts(x + 3, y, TA::OnOff(reg & SSR_BE), "BE");
105: screen.Puts(x + 6, y, TA::OnOff(reg & SSR_WT), "WT");
106: screen.Puts(x + 9, y, TA::OnOff(reg & SSR_SP), "SP");
107: screen.Puts(x + 12, y, TA::OnOff(reg & SSR_G), "G");
108: screen.Puts(x + 14, y, TA::OnOff(reg & SSR_CI), "CI");
109: screen.Puts(x + 17, y, TA::OnOff(reg & SSR_M), "M");
110: screen.Puts(x + 19, y, TA::OnOff(reg & SSR_U), "U");
111: screen.Puts(x + 21, y, TA::OnOff(reg & SSR_WP), "WP");
112: screen.Puts(x + 24, y, TA::OnOff(reg & SSR_BH), "BH");
113: screen.Puts(x + 27, y, TA::OnOff(reg & SSR_V), "V");
1.1 root 114: y++;
115:
1.1.1.7 root 116: screen.Print(0, y++, "$%08x SAR:%08x", baseaddr + 0x00c, GetSAR());
1.1 root 117:
118: reg = GetSCTR();
1.1.1.7 root 119: screen.Print(0, y, "$%08x SCTR:%08x", baseaddr + 0x104, GetSCTR());
120: screen.Puts(x, y, TA::OnOff(reg & SCTR_PE), "PE");
121: screen.Puts(x + 3, y, TA::OnOff(reg & SCTR_SE), "SE");
122: screen.Puts(x + 6, y, TA::OnOff(reg & SCTR_PR), "PR");
1.1 root 123: y++;
124:
125: reg = GetPFSR();
126: static const char * const codestr[] = {
127: "Success",
128: "1?",
129: "2?",
130: "Bus Error",
131: "Segment Fault",
132: "Page Fault",
133: "Supervisor Violation",
134: "Write Violation",
135: };
1.1.1.7 root 136: screen.Print(0, y++, "$%08x PFSR:%08x %s", baseaddr + 0x108,
1.1.1.3 root 137: reg, codestr[fault_code]);
1.1 root 138:
1.1.1.7 root 139: screen.Print(0, y++, "$%08x PFAR:%08x", baseaddr + 0x10c, GetPFAR());
1.1 root 140:
141: for (int i = 0; i < 2; i++) {
142: int s = 1 - i;
143: reg = GetAPR(s);
1.1.1.7 root 144: screen.Print(0, y, "$%08x %cAPR:%08x", baseaddr + 0x200 + i * 4,
1.1.1.3 root 145: s ? 'S' : 'U', reg);
1.1.1.7 root 146: screen.Print(x, y, "STBA=%05x'000 %c%c%c%c",
1.1 root 147: reg >> 12,
148: (reg & APR_WT) ? 'T' : '-',
149: (reg & APR_G) ? 'G' : '-',
150: (reg & APR_CI) ? 'C' : '-',
151: (reg & APR_TE) ? 'E' : '-');
152: y++;
153: }
154: }
155:
1.1.1.7 root 156: // モニター更新 (ATC)
157: void
158: m88200::MonitorUpdateATC(Monitor *, TextScreen& screen)
159: {
160: int x;
161: int y;
162:
163: screen.Clear();
164:
165: screen.Print(0, 0, "SAPR %05x'000 TE=%d %c%c%c",
166: (sapr.addr >> 12) & 0xfffff,
167: sapr.enable ? 1 : 0,
1.1.1.9 root 168: (sapr.stat & APR_WT) ? 'T' : '-',
169: (sapr.stat & APR_G) ? 'G' : '-',
170: (sapr.stat & APR_CI) ? 'C' : '-');
1.1.1.7 root 171:
1.1.1.9 root 172: screen.Print(40, 0, "UAPR %05x'000 TE=%d %c%c%c",
1.1.1.7 root 173: (uapr.addr >> 12) & 0xfffff,
174: uapr.enable ? 1 : 0,
1.1.1.9 root 175: (uapr.stat & APR_WT) ? 'T' : '-',
176: (uapr.stat & APR_G) ? 'G' : '-',
177: (uapr.stat & APR_CI) ? 'C' : '-');
1.1.1.7 root 178:
179: screen.Puts(0, 1, "<BATC>");
1.1.1.9 root 180: screen.Puts(0, 2, "No. LBA PBA Stat Hit%");
181: screen.Puts(40, 2, "No. LBA PBA Stat Hit%");
1.1.1.7 root 182: x = 0;
183: y = 3;
1.1.1.9 root 184: uint64 batc_total = 0;
1.1.1.7 root 185: for (int i = 0; i < 10; i++) {
186: if (i == batc.size() / 2) {
1.1.1.9 root 187: x = 40;
1.1.1.7 root 188: y = 3;
189: }
190: if (i < 8) {
191: screen.Print(x, y, " %d:", i);
192: } else {
193: screen.Print(x, y, "(%d)", i);
194: }
195:
196: const m88200BATC& b = batc[i];
1.1.1.9 root 197: if (b.IsValid()) {
1.1.1.7 root 198: screen.Print(x + 4, y, "%c:%04x'0000 %04x'0000 %c%c%c%c",
1.1.1.9 root 199: b.IsS() ? 'S' : 'U',
1.1.1.7 root 200: b.lba >> 16, b.pba >> 16,
1.1.1.9 root 201: (b.stat & DESC_WT) ? 'T' : '-',
202: (b.stat & DESC_G) ? 'G' : '-',
203: (b.stat & DESC_CI) ? 'C' : '-',
204: (b.stat & DESC_WP) ? 'P' : '-');
205: }
206: if (batc_hit[i] == 0) {
207: screen.Puts(x + 32, y, "--.-%");
208: } else {
209: screen.Print(x + 32, y, "%4.1f%%",
210: (double)batc_hit[i] / translate_total * 100);
211: batc_total += batc_hit[i];
1.1.1.7 root 212: }
213: y++;
214: }
215:
216: screen.Puts(0, 8, "<PATC>");
217: screen.Puts(0, 9, "No. LPA PFA Stat");
1.1.1.9 root 218: screen.Puts(40, 9, "No. LPA PFA Stat");
1.1.1.7 root 219:
220: x = 0;
221: y = 10;
222: for (int i = 0; i < patc.size(); i++) {
223: if (i == patc.size() / 2) {
1.1.1.9 root 224: x = 40;
1.1.1.7 root 225: y = 10;
226: }
227: screen.Print(x, y, "%2d:", i);
228:
229: const m88200PATC& p = patc[i];
1.1.1.9 root 230: if (p.IsValid()) {
1.1.1.7 root 231: screen.Print(x + 4, y, "%c:%05x'000 %05x'000 %c%c%c%c%c",
1.1.1.9 root 232: p.IsS() ? 'S' : 'U',
1.1.1.7 root 233: p.lpa >> 12, p.pfa >> 12,
1.1.1.9 root 234: (p.stat & DESC_WT) ? 'T' : '-',
235: (p.stat & DESC_G) ? 'G' : '-',
236: (p.stat & DESC_CI) ? 'C' : '-',
237: (p.stat & DESC_WP) ? 'P' : '-',
238: p.m ? 'M' : '-');
1.1.1.7 root 239: }
240: y++;
241: }
1.1.1.9 root 242:
243: if (__predict_false(translate_total == 0)) {
244: screen.Puts(40, y, "BATC --.--%, PATC --.--%, Miss --.--%");
245: } else {
246: screen.Print(40, y, "BATC%6.2f%%, PATC%6.2f%%, Miss%6.2f%%",
247: (double)batc_total / translate_total * 100,
248: (double)patc_hit / translate_total * 100,
249: (double)atc_miss / translate_total * 100);
250: }
251:
252: #if defined(M88200_STAT)
253: y++;
254: double v;
255: screen.Puts(0, y++, "<Statistics>");
256: screen.Print(0, y++, "PATC hash search %26s",
257: format_number(stat_patc_search).c_str());
258: // ヒット率
259: screen.Print(0, y, "PATC hash hit %26s (hit/search ",
260: format_number(stat_patc_hit).c_str());
261: if (stat_patc_search == 0) {
262: screen.Puts(60, y, "---.-%)");
263: } else {
264: screen.Print(60, y, "%5.1f%%)",
265: (double)stat_patc_hit / stat_patc_search * 100);
266: }
267: y++;
268: // ミスのうち1回目のハッシュ比較で外れが確定した率
269: screen.Print(0, y, "PATC hash miss 1 %26s (miss1/miss ",
270: format_number(stat_patc_miss1).c_str());
271: v = stat_patc_miss1 + stat_patc_miss2;
272: if (v == 0) {
273: screen.Puts(60, y, "---.-%)");
274: } else {
275: screen.Print(60, y, "%5.1f%%)", (double)stat_patc_miss1 / v * 100);
276: }
277: y++;
278: screen.Print(0, y++, "PATC hash miss 2 %26s",
279: format_number(stat_patc_miss2).c_str());
280: screen.Print(0, y++, "PATC create %26s",
281: format_number(stat_patc_create).c_str());
282: screen.Print(0, y++, "PATC invalidate %26s",
283: format_number(stat_patc_invalidate).c_str());
284: screen.Print(0, y++, "PATC SCR InvAll %26s",
285: format_number(stat_patc_invcmd_all).c_str());
286: screen.Print(0, y++, "PATC SCR InvSegment %26s",
287: format_number(stat_patc_invcmd_seg).c_str());
288: screen.Print(0, y++, "PATC SCR InvPage %26s",
289: format_number(stat_patc_invcmd_page).c_str());
290:
291: screen.Puts(0, y++, "Hash 0 1 2 3 4 5 6 >=7");
292:
293: // BATC ハッシュの衝突状況は BATC 更新時に計算してある
294: // (BATC の更新頻度は低いため)
295: screen.Puts(0, y++, "BATC HASH.S");
296: for (auto n : stat_batc_hash_s) {
297: screen.Print(" %3d", n);
298: }
299: screen.Puts(0, y++, "BATC HASH.U");
300: for (auto n : stat_batc_hash_u) {
301: screen.Print(" %3d", n);
302: }
303: // PATC ハッシュの衝突状況は、表示時に都度計算する
304: // (PATC ハッシュの更新頻度はおそらく表示周期より速いため)
305: auto print_phash = [&](const auto& hash) {
306: std::array<int, 8> st {};
307: for (const auto hashmap : hash) {
308: int n = __builtin_popcount(hashmap);
309: if (n >= st.size()) {
310: n = st.size() - 1;
311: }
312: st[n]++;
313: }
314: for (auto n : st) {
315: screen.Print(" %3d", n);
316: }
317: };
318: screen.Puts(0, y++, "PATC HASH.S");
319: print_phash(patc_hash_s);
320: screen.Puts(0, y++, "PATC HASH.U");
321: print_phash(patc_hash_u);
322: #endif
1.1.1.7 root 323: }
324:
325: // モニター更新 (キャッシュ、概要と詳細の両方、GUI から呼ばれる)
326: // screen.userdata は注目しているセット番号。
327: void
328: m88200::MonitorUpdateCache(Monitor *, TextScreen& screen)
329: {
330: int setidx = screen.userdata;
331:
332: // 上半分(概要)
333: MonitorCacheOverview(screen, 0, setidx, true);
334: // 下半分(セット詳細)
335: MonitorCacheSet(screen, 18, setidx);
336: }
337:
1.1 root 338: // データキャッシュの特定セットの詳細を TextScreen に出力する。
339: // y は開始オフセット。
340: // TextScreen は (70, 5) 必要。
341: void
342: m88200::MonitorCacheSet(TextScreen& s, int y, int setidx)
343: {
1.1.1.7 root 344: assertmsg(0 <= setidx && setidx < setarray.size(), "setidx=%d", setidx);
1.1 root 345: const m88200CacheSet& set = setarray[setidx];
346:
347: /*
348: 012345678901234567890123456789012345678901234567890123456789
349: L Tag Status
350: 0 $11223'344 VV 12345678 12345678 12345678 12345678
351: */
352: s.Puts(0, y, "L Tag Status Word");
353: s.Puts(58, y, "Order");
354: y++;
355:
356: // ラインの古い順に評価して順序を0-3でつける
357: int Lorder[4];
358: int tmpL = set.L;
359: for (int i = 0; i < 4; i++) {
360: int line = m88200CacheSet::TryGetOldestLine(tmpL);
361: Lorder[line] = 3 - i;
362: tmpL = m88200CacheSet::TryUseLine(tmpL, line);
363: }
364:
365: for (int line = 0; line < 4; line++, y++) {
366: TA attr;
367: // ステータスによって属性を選択
368: switch (set.vv[line]) {
369: case m88200CacheSet::Status::IV:
370: attr = TA::Disable;
371: break;
372: case m88200CacheSet::Status::EU:
373: case m88200CacheSet::Status::SU:
374: attr = TA::Off;
375: break;
376: case m88200CacheSet::Status::EM:
377: attr = TA::Em;
378: break;
379: }
380: s.Print(0, y, attr, "%d", line);
1.1.1.3 root 381: static const char statusstr[][4] = { "EU", "EM", "SU", "IV" };
1.1 root 382: s.Print(2, y, attr, "$%05x'%03x %s",
383: (set.tag[line] >> 12),
384: (setidx << 4),
385: statusstr[set.vv[line]]);
386:
387: if (set.vv[line] == m88200CacheSet::Status::EM) {
388: // メモリに対してダーティならボールドにする
389: for (int w = 0; w < 4; w++) {
390: uint32 addr;
391: uint32 m;
392: addr = (set.tag[line] & 0xfffff000) | (setidx << 4) | (w << 2);
393: m = (vm_phys_peek_8(addr) << 24)
394: | (vm_phys_peek_8(addr + 1) << 16)
395: | (vm_phys_peek_8(addr + 2) << 8)
396: | (vm_phys_peek_8(addr + 3));
397: if (set.word[line * 4 + w] != m) {
398: attr = TA::Em;
399: } else {
400: attr = TA::Off;
401: }
402: s.Print(20 + w * 9, y, attr, "%08x", set.word[line * 4 + w]);
403: }
404: } else {
405: // Unmodified (or Invalid) ならメモリとの比較は不要
406: for (int w = 0; w < 4; w++) {
407: s.Print(20 + w * 9, y, "%08x", set.word[line * 4 + w]);
408: }
409: }
410:
411: // 順序
412: s.Print(58, y, "%d", Lorder[line]);
413: }
414: }
415:
416: // データキャッシュの概要を指定の TextScreen に出力する。
417: // y は開始オフセット。CLI では単独コマンドとして、GUI ではページの一部と
418: // して描画するためこうなっている。
419: // cursor で指定された番号のセットは反転表示する。GUI でのカーソル用。
420: // 負数など範囲外の値を指定すればカーソルは表示されない。
421: // is_gui は GUI かどうか。CLI では80桁を微妙に越えるのは嫌だしどうせ表示
422: // だけなので間を詰めてあるが、GUI では 80桁制約はない代わりにマウス操作が
423: // あるので 1セットごとに間を空けて等間隔にしたい、という違いから。
424: // TextScreen は CLI なら (70, 17)、GUI なら (82, 17) 必要。
425: void
426: m88200::MonitorCacheOverview(TextScreen& s, int y, int cursor, bool is_gui)
427: {
428: // X ガイド
429: for (int i = 0; i < 16; i++) {
430: int x;
431: if (is_gui) {
432: x = 3 + i * 5;
433: } else {
434: x = 3 + i * 4 + (i / 4);
435: }
436: s.Print(x, y, "+0%x", i);
437: }
438: y++;
439:
440: // Y ガイド
441: for (int i = 0; i < 16; i++) {
442: s.Print(0, y + i, "%02x", i * 16);
443: }
444:
445: for (int i = 0; i < setarray.size(); i++) {
446: const auto& set = setarray[i];
1.1.1.3 root 447: const char str[] = "EMS-";
1.1 root 448: int col = i % 16;
449: int row = i / 16;
450: int x;
451: if (is_gui) {
452: x = 3 + col * 5;
453: } else {
454: x = 3 + col * 4 + col / 4;
455: }
456:
457: s.Print(x, y + row, TA::OnOff(i == cursor),
458: "%c%c%c%c",
459: str[set.vv[0]],
460: str[set.vv[1]],
461: str[set.vv[2]],
462: str[set.vv[3]]);
463: }
464: }
465:
466: // ID を設定する (IDR ではない)
467: void
468: m88200::SetID(uint id_)
469: {
470: id = id_;
471:
1.1.1.7 root 472: SetName(string_format("CMMU#%u", id));
1.1.1.8 root 473: ClearAlias();
474: AddAlias(string_format("CMMU%u", id));
1.1.1.7 root 475:
476: reg_monitor.Regist(ID_MONITOR_CMMU(id));
477: atc_monitor.Regist(ID_MONITOR_ATC(id));
478: cache_monitor.Regist(ID_SUBWIN_CACHE(id));
1.1 root 479: }
480:
481: // IDR の Version フィールドを設定する
482: void
483: m88200::SetVersion(uint version_)
484: {
485: version = version_;
486: }
487:
488: // コマンド名
489: // (0-15 は全部 No Operation なので、16以降のみ)
490: /*static*/ const char * const
491: m88200::commandname[] = {
492: "No Operation", // $10
493: "No Operation", // $11
494: "No Operation", // $12
495: "No Operation", // $13
496: "DCache Invalidate, Line", // $14
497: "DCache Invalidate, Page", // $15
498: "DCache Invalidate, Segment", // $16
499: "DCache Invalidate, All", // $17
500:
501: "DCache Copyback, Line", // $18
502: "DCache Copyback, Page", // $19
503: "DCache Copyback, Segment", // $1a
504: "DCache Copyback, All", // $1b
505: "DCache Copy&Inv, Line", // $1c
506: "DCache Copy&Inv, Page", // $1d
507: "DCache Copy&Inv, Segment", // $1e
508: "DCache Copy&Inv, All", // $1f
509:
510: "Probe User Address", // $20
511: "Probe User Address", // $21
512: "Probe User Address", // $22
513: "Probe User Address", // $23
514: "Probe Supervisor Address", // $24
515: "Probe Supervisor Address", // $25
516: "Probe Supervisor Address", // $26
517: "Probe Supervisor Address", // $27
518:
519: "Probe User Address", // $28
520: "Probe User Address", // $29
521: "Probe User Address", // $2a
522: "Probe User Address", // $2b
523: "Probe Supervisor Address", // $2c
524: "Probe Supervisor Address", // $2d
525: "Probe Supervisor Address", // $2e
526: "Probe Supervisor Address", // $2f
527:
528: "Invalidate U PATC, Line", // $30
529: "Invalidate U PATC, Page", // $31
530: "Invalidate U PATC, Segment", // $32
531: "Invalidate U PATC, All", // $33
532: "Invalidate S PATC, Line", // $34
533: "Invalidate S PATC, Page", // $35
534: "Invalidate S PATC, Segment", // $36
535: "Invalidate S PATC, All", // $37
536:
537: "Invalidate U PATC, Line", // $38
538: "Invalidate U PATC, Page", // $39
539: "Invalidate U PATC, Segment", // $3a
540: "Invalidate U PATC, All", // $3b
541: "Invalidate S PATC, Line", // $3c
542: "Invalidate S PATC, Page", // $3d
543: "Invalidate S PATC, Segment", // $3e
544: "Invalidate S PATC, All", // $3f
545: };
546:
547: // SCR の読み出し
548: uint32
549: m88200::GetSCR() const
550: {
551: // b31-b6 (reserved) の読み出し値は未定義らしい。
552: // b5-b0 (Command Code) の読み出し値はマニュアルに記載がないけど
553: // たぶんそのまま読めるのかな。
554: return command;
555: }
556:
557: // SCR への書き込み
558: void
559: m88200::SetSCR(uint32 data)
560: {
561: command = data & 0x3f;
562:
563: // %00'XXXX No Operation
564: // %01'00XX No Operation
565: // %01'01gg Data Cache Invalidate
566: // %01'10gg Data Cache Copyback to Memory
567: // %01'11gg Data Cache Copyback and Invalidate
568: // %10'X0XX Probe User Address
569: // %10'X1XX Probe Supervisor Address
570: // %11'X0gg Invalidate User PATC Descriptors
571: // %11'X1gg Invalidate Supervisor PATC Descriptors
572:
573: const char *name;
574: if (command < 0x10) {
575: name = commandname[0];
576: } else {
577: name = commandname[command - 0x10];
578: }
579: putlog(1, "SCR <- $%08x (%s)", data, name);
580:
581: switch (command) {
582: case 0x00 ... 0x13: // No Operation
583: return;
584:
585: case 0x14 ... 0x1f: // Flush Data Cache
586: FlushCache();
587: return;
588:
589: case 0x20 ... 0x23: // Probe User Address
590: case 0x28 ... 0x2b:
1.1.1.10! root 591: putlog(0, "SCR Command: Probe User Address (NOT IMPLEMENTED)");
1.1 root 592: return;
593:
594: case 0x24 ... 0x27: // Probe Supervisor Address
595: case 0x2c ... 0x2f:
1.1.1.10! root 596: putlog(0, "SCR Command: Probe Supervisor Address (NOT IMPLEMENTED)");
1.1 root 597: return;
598:
599: case 0x30 ... 0x3f: // Invalidate {User,Supervisor} PATC Descriptors
600: InvalidatePATC();
601: return;
602: }
603: __unreachable();
604: }
605:
606: // command に応じてデータキャッシュをフラッシュする。SetSCR() の下請け。
607: // command が $14..$1f でのみ呼ぶこと。
608: // p3-18, Section 3.7
609: void
610: m88200::FlushCache()
611: {
612: uint32 op = command & 0x3c;
613: uint32 gg = command & 0x03;
614: uint32 addr = GetSAR();
615: bool copyback;
616: bool invalidate;
617:
618: // op CopyBack Invalidate
619: // %01'01gg false true | Data Cache Invalidate
620: // %01'10gg true false | Data Cache Copyback to Memory
621: // %01'11gg true true | Data Cache Copyback and invalidate
622:
623: // op によって書き戻しと無効化の組み合わせが異なる
624: if (op == 0x14) {
625: copyback = false;
626: invalidate = true;
627: } else if (op == 0x18) {
628: copyback = true;
629: invalidate = false;
630: } else if (op == 0x1c) {
631: copyback = true;
632: invalidate = true;
633: } else {
634: PANIC("unknown op = $%02x", op);
635: }
636:
637: // 影響範囲
638: switch (gg) {
639: case GG_LINE:
640: addr &= 0xfffffffc;
1.1.1.3 root 641: parent->AddCycle(1); // Table.6-1
1.1 root 642: break;
643: case GG_PAGE:
644: addr &= 0xfffff000;
1.1.1.3 root 645: parent->AddCycle(256); // Table.6-1
1.1 root 646: break;
647: case GG_SEG:
648: addr &= 0xffc00000;
1.1.1.3 root 649: parent->AddCycle(1024); // Table.6-1
1.1 root 650: break;
651: case GG_ALL:
1.1.1.3 root 652: parent->AddCycle(1024); // Table.6-1
1.1 root 653: break;
654: default:
655: __unreachable();
656: }
657:
1.1.1.3 root 658: // サイクル(基本部分) Table.6-1
659: uint32 cycle = 0;
660: if (invalidate) {
661: switch (gg) {
662: case GG_LINE: cycle = 1; break;
663: case GG_PAGE: cycle = 256; break;
664: case GG_SEG: cycle = 1024; break;
665: case GG_ALL: cycle = 256; break;
666: default: __unreachable();
667: }
668: }
669: if (copyback) {
670: switch (gg) {
671: case GG_LINE: cycle = 1; break;
672: case GG_PAGE: cycle = 256; break;
673: case GG_SEG: cycle = 1024; break;
674: case GG_ALL: cycle = 1024; break;
675: default: __unreachable();
676: }
677: }
678: // XXX ループ中のサイクル数は正しくないかも。よく分からん
679:
1.1 root 680: for (auto& set : setarray) {
681: for (int line = 0; line < 4; line++) {
682: // 条件にマッチするか
683: bool match;
684: switch (gg) {
685: case GG_LINE:
686: match = (addr == (set.tag[line] | (set.setidx << 4)));
687: break;
688: case GG_PAGE:
689: match = (addr == set.tag[line]);
690: break;
691: case GG_SEG:
692: match = (addr == (set.tag[line] & 0xffc00001));
693: break;
694: case GG_ALL:
695: match = true;
696: break;
697: default:
698: __unreachable();
699: }
700: if (!match)
701: continue;
702:
703: // Copyback ならまず EM なエントリを書き戻す。
704: if (copyback) {
705: if (set.vv[line] == m88200CacheSet::EM) {
1.1.1.3 root 706: cycle += 7; // Table.6-1
707: CopyBackLine(set, line);
1.1 root 708: }
709: }
710:
711: // Invalidate なら無効化する
712: if (invalidate) {
1.1.1.3 root 713: cycle += 1; // Table.6-1
1.1 root 714: set.Update(line, m88200CacheSet::IV);
715: }
716: }
717: }
1.1.1.3 root 718:
719: parent->AddCycle(cycle);
1.1 root 720: }
721:
722: // command に応じて PATC を無効化する。SetSCR() の下請け。
723: // command が $30..$3f で呼ぶこと。
724: // p2-9, Section 2.2.4
725: void
726: m88200::InvalidatePATC()
727: {
728: uint32 gg = command & 0x03;
1.1.1.9 root 729: bool s = (command & 0x04);
730: uint32 addr;
731: uint32 mask;
1.1 root 732:
1.1.1.9 root 733: if (__predict_true(gg == GG_PAGE)) {
734: // 指定の1本だけ無効にする。
735: // Page は呼び出し回数が多いのと1本だけならハッシュで引けるので別対応。
736: #if defined(M88200_STAT)
737: stat_patc_invcmd_page++;
738: #endif
739:
740: // ゲスト側が指定した端数部分は有効部分でマスクする。
741: // 内部では下位ビットはフラグ扱いなのでマスクしないと誤動作する。
742: addr = GetSAR() & 0xfffff000;
743: addr |= (s) ? PATC_S : 0;
1.1 root 744:
1.1.1.9 root 745: auto& hash = (s) ? patc_hash_s : patc_hash_u;
746: uint64 pi = hash[patc_hash_func(addr)];
1.1 root 747:
1.1.1.9 root 748: for (; pi; pi &= pi - 1) {
749: int i = __builtin_ctzll(pi);
750: m88200PATC& p = patc[i];
751: if (p.lpa == addr) {
752: InvalidatePATCHash(i);
753: p.lpa |= PATC_INVALID;
754: #if defined(M88200_STAT)
755: stat_patc_invalidate++;
756: #endif
757: // 1本ヒットすればこれ以上一致することはないはず
758: break;
759: }
1.1 root 760: }
1.1.1.9 root 761: } else {
762: // 指定範囲を無効にする。All と Segment はマスクが違うだけ。
763:
764: if (gg == GG_ALL) {
765: // S/U 指定したほう全体を無効にする
766: #if defined(M88200_STAT)
767: stat_patc_invcmd_all++;
768: #endif
769: mask = 0;
770: } else if (gg == GG_SEG) {
771: // S/U 指定したほうの指定セグメント範囲全部を無効にする
772: #if defined(M88200_STAT)
773: stat_patc_invcmd_seg++;
774: #endif
775: mask = 0xffc00000;
776: } else {
777: // GG_LINE はマニュアルにどうなるか書いてない
1.1.1.10! root 778: putlog(0, "Undefined Invalidate PATC Line");
1.1.1.9 root 779: return;
780: }
781:
782: addr = GetSAR() & mask;
783: addr |= (s) ? PATC_S : 0;
784:
785: mask |= PATC_S | PATC_INVALID;
786:
787: for (int i = 0; i < patc.size(); i++) {
788: m88200PATC& p = patc[i];
789: if ((p.lpa & mask) == addr) {
790: // 無効化
791: // XXX 削除した結果穴が空いても詰める処理は未実装
792: InvalidatePATCHash(i);
793: p.lpa |= PATC_INVALID;
794: #if defined(M88200_STAT)
795: stat_patc_invalidate++;
796: #endif
797: }
1.1 root 798: }
799: }
800: }
801:
802: // SCTR レジスタへの書き込み
803: void
804: m88200::SetSCTR(uint32 data)
805: {
806: sctr = data & (SCTR_PE | SCTR_SE | SCTR_PR);
807:
1.1.1.9 root 808: // 全 CMMU について、それぞれスヌープ相手になる CMMU リストを更新。
809: // どの CMMU の SCTR への書き込みでも毎回全ての CMMU を書き換える。
810: int n = parent->cmmu.size();
811: for (int i = 0; i < n; i++) {
812: parent->cmmu[i].other_cmmu.clear();
813: for (int j = 0; j < n; j++) {
814: if (i == j)
815: continue;
816: if ((parent->cmmu[j].sctr & SCTR_SE)) {
817: parent->cmmu[i].other_cmmu.push_back(&parent->cmmu[j]);
818: }
819: }
820: }
821:
1.1 root 822: std::string msg;
823: if ((sctr & SCTR_PE))
824: msg += ",PE";
825: if ((sctr & SCTR_PR))
826: msg += ",PR";
827: if (msg.length() > 0) {
1.1.1.10! root 828: putlog(0, "SCTR <- $%08x (%s NOT IMPLEMENTED)", data, msg.c_str() + 1);
1.1 root 829: }
830: }
831:
832: // SAPR, UAPR レジスタ値の読み出し (S/U ビット指定)
833: uint32
834: m88200::GetAPR(uint issuper) const
835: {
836: if (issuper) {
837: return GetAPR(sapr);
838: } else {
839: return GetAPR(uapr);
840: }
841: }
842:
843: // SAPR, UAPR レジスタ値の読み出し (実体指定)
844: uint32
845: m88200::GetAPR(const m88200APR& xapr) const
846: {
847: uint32 data;
848:
849: data = xapr.addr & APR_ADDR_MASK;
850: data |= xapr.stat & (APR_WT | APR_G | APR_CI);
851: if (xapr.enable) {
852: data |= APR_TE;
853: }
854: return data;
855: }
856:
857: // SAPR, UAPR レジスタへの書き込み (実体指定)
858: void
859: m88200::SetAPR(m88200APR& xapr, uint32 data)
860: {
861: bool old_enable = xapr.enable;
862: xapr.addr = data & APR_ADDR_MASK;
863: xapr.stat = data & (APR_WT | APR_G | APR_CI);
864: xapr.enable = data & APR_TE;
865:
866: if (old_enable == false && xapr.enable == true) {
867: // 変換開始
868: putlog(1, "%s <- $%08x (Translation Enabled)", xapr.name, data);
869: } else if (old_enable == true && xapr.enable == false) {
870: // 変換停止
871: putlog(1, "%s <- $%08x (Translation Disabled)", xapr.name, data);
872: } else {
873: // 変化なし
874: putlog(1, "%s <- $%08x", xapr.name, data);
875: }
876: }
877:
1.1.1.9 root 878: // BWP(BATC Write Port) #n への書き込み
1.1 root 879: // (暗黙 BATC にも書き込めるよう n = 8,9 も受け付ける)
880: void
1.1.1.9 root 881: m88200::SetBWP(uint n, uint32 data)
1.1 root 882: {
883: assert(n < 10);
884: m88200BATC& b = batc[n];
885: putlog(1, "BWP%u <- $%08x", n, data);
886:
1.1.1.9 root 887: // BWP レジスタへの書き込み値と内部データ構造(stat)ではビット位置が違う
888: // ことに注意。
889: //
890: // 31 10 9 8 7 6 5 4 3 2 1 0
891: // +----..-----+----+----+----+----+----+----+----+----+----+----+
892: // BWP |LBA PBA | S | WT | G | CI | WP | V |
893: // +----..-----+----+----+----+----+----+----+----+----+----+----+
894: //
895: // 31 10 9 8 7 6 5 4 3 2 1 0
896: // +----..-----+----+----+----+----+----+----+----+----+----+----+
897: // stat | 0 0 | WT | SP | G | CI | 0 | M | U | WP | 0 | V |
898: // +----..-----+----+----+----+----+----+----+----+----+----+----+
899:
1.1 root 900: memset(&b, 0, sizeof(b));
1.1.1.9 root 901: b.lba = data & BWP_LBA_MASK;
902: b.lba |= (data & BWP_S);
903: if ((data & BWP_V) == 0) {
904: b.lba |= BATC_INVALID;
905: }
906: b.pba = (data & BWP_PBA_MASK) << 13;
907: if ((data & BWP_WT))
908: b.stat |= DESC_WT;
909: if ((data & BWP_G))
910: b.stat |= DESC_G;
911: if ((data & BWP_CI))
912: b.stat |= DESC_CI;
913: if ((data & BWP_WP)) {
914: b.stat |= DESC_WP;
1.1 root 915: b.wp = true;
916: }
1.1.1.9 root 917:
918: MakeBATCHash();
919: }
920:
921: // BATC ハッシュを作り直す。
922: void
923: m88200::MakeBATCHash()
924: {
925: std::fill(batc_hash_s.begin(), batc_hash_s.end(), 0);
926: std::fill(batc_hash_u.begin(), batc_hash_u.end(), 0);
927:
928: for (int i = 0; i < batc.size(); i++) {
929: m88200BATC& b = batc[i];
930:
931: if (b.IsValid()) {
932: // LBA の下位 8 ビットだけでハッシュを作る
933: uint32 hashkey = batc_hash_func(b.lba);
934:
935: // S によって該当するハッシュの該当するビットを立てておく
936: if (b.IsS()) {
937: batc_hash_s[hashkey] |= 1U << i;
938: } else {
939: batc_hash_u[hashkey] |= 1U << i;
940: }
941: }
942: }
943:
944: #if defined(M88200_STAT)
945: // ハッシュの各要素が何個の衝突を持つか。
946: // (2以上が極力現れないことが望ましい)
947: auto counting = [](auto& stat, const auto& hash) {
948: std::fill(stat.begin(), stat.end(), 0);
949: for (const auto hashmap : hash) {
950: int n = __builtin_popcount(hashmap);
951: if (n >= stat.size()) {
952: n = stat.size() - 1;
953: }
954: stat[n]++;
955: }
956: };
957: counting(stat_batc_hash_s, batc_hash_s);
958: counting(stat_batc_hash_u, batc_hash_u);
959: #endif
1.1 root 960: }
961:
962: // CSSP レジスタの読み出し
963: uint32
964: m88200::GetCSSP() const
965: {
966: uint32 setidx = (GetSAR() >> 4) & 0xff;
967: const m88200CacheSet& set = setarray[setidx];
968:
969: uint32 data = 0;
970: // L5-L0
971: data |= set.L << 24;
972:
973: // XXX D3-D0 未実装
974:
975: // VV3-VV0
976: data |= set.vv[3] << CSSP_VV3_OFFSET;
977: data |= set.vv[2] << CSSP_VV2_OFFSET;
978: data |= set.vv[1] << CSSP_VV1_OFFSET;
979: data |= set.vv[0] << CSSP_VV0_OFFSET;
980:
981: return data;
982: }
983:
984: // CSSP レジスタへの書き込み
985: void
986: m88200::SetCSSP(uint32 data)
987: {
988: uint32 setidx = (GetSAR() >> 4) & 0xff;
989: m88200CacheSet& set = setarray[setidx];
990:
991: putlog(1, "CSSP <- $%08x (set=$%02x)", data,setidx);
992:
993: // L5-L0 書き込み
994: set.L = (data >> 24) & 0x3f;
995:
996: // XXX D3-D0 未実装
997:
998: // VV3-VV0
999: set.vv[3] = (m88200CacheSet::Status)((data >> CSSP_VV3_OFFSET) & 3);
1000: set.vv[2] = (m88200CacheSet::Status)((data >> CSSP_VV2_OFFSET) & 3);
1001: set.vv[1] = (m88200CacheSet::Status)((data >> CSSP_VV1_OFFSET) & 3);
1002: set.vv[0] = (m88200CacheSet::Status)((data >> CSSP_VV0_OFFSET) & 3);
1003: }
1004:
1.1.1.9 root 1005: // S/U 信号線を設定。true なら Super、false なら User。
1006: void
1007: m88200::SetSuper(bool super)
1008: {
1009: acc_super = super;
1010:
1011: if (acc_super) {
1012: acc_apr = &sapr;
1013: batc_hash = &batc_hash_s[0];
1014: patc_hash = &patc_hash_s[0];
1015: } else {
1016: acc_apr = &uapr;
1017: batc_hash = &batc_hash_u[0];
1018: patc_hash = &patc_hash_u[0];
1019: }
1020: }
1021:
1.1 root 1022:
1023: //
1024: // 物理バスアクセス
1025: //
1026:
1027: // MBus から読み込みを行う。
1028: // paddr は size によって 1, 2, 4 バイト境界に整列していること。
1029: // 成功すれば読み込めた値を返す。
1030: // バスエラーが起きれば fault_code、fault_addr をセットして (uint64)-1 を返す。
1031: uint64
1032: m88200::MBusRead(uint32 paddr, int size)
1033: {
1034: uint64 data;
1035:
1036: if (size == 4) {
1037: data = vm_phys_read_32(paddr);
1038: } else if (size == 2) {
1039: data = vm_phys_read_16(paddr);
1040: } else {
1041: data = vm_phys_read_8(paddr);
1042: }
1.1.1.9 root 1043: if (__predict_false((int64)data < 0)) {
1.1 root 1044: fault_code = FAULT_CODE_BUSERR;
1045: fault_addr = paddr;
1046: }
1047: return data;
1048: }
1049:
1050: // MBus に書き込みを行う。
1051: // paddr は size によって 1, 2, 4 バイト境界に整列していること。
1052: // 成功すれば 0 を返す。
1053: // バスエラーが起きれば fault_code、fault_addr をセットして (uint64)-1 を返す。
1054: uint64
1055: m88200::MBusWrite(uint32 paddr, uint32 data, int size)
1056: {
1057: uint64 rv;
1058:
1059: if (size == 4) {
1060: rv = vm_phys_write_32(paddr, data);
1061: } else if (size == 2) {
1062: rv = vm_phys_write_16(paddr, data);
1063: } else {
1064: rv = vm_phys_write_8(paddr, data);
1065: }
1.1.1.9 root 1066: if (__predict_false((int64)rv < 0)) {
1.1 root 1067: fault_code = FAULT_CODE_BUSERR;
1068: fault_addr = paddr;
1069: }
1070: return rv;
1071: }
1072:
1073: // MBus に xmem トランザクションを行う。
1.1.1.6 root 1074: // paddr は size によって 1, 4 バイト境界に整列していること (2バイトはない)。
1.1 root 1075: // MBus Acquire された状態で呼び出すこと。
1076: // 成功すれば読み込めた値を返す。
1077: // バスエラーが起きれば fault_code、fault_addr をセットして (uint64)-1 を返す。
1078: // read/write どちらでエラーが起きたかは acc_read で判別できる。
1079: // p3-13, Figure3-7 の下半分のメインラインあたりに該当。
1080: uint64
1081: m88200::MBusXmem(uint32 paddr, uint32 data, int size)
1082: {
1083: uint64 fetched;
1084: uint64 rv;
1085:
1086: // Read data with intent to modify
1087: acc_read = true;
1088: rv = MBusRead(paddr, size);
1.1.1.9 root 1089: if (__predict_false((int64)rv < 0)) {
1.1 root 1090: goto error;
1091: }
1092: fetched = (uint32)rv;
1093:
1094: // Supply data to PBus
1095: // Reply = Success
1096:
1097: // Write data to memory
1098: acc_read = false;
1099: rv = MBusWrite(paddr, data, size);
1.1.1.9 root 1100: if (__predict_false((int64)rv < 0)) {
1.1 root 1101: goto error;
1102: }
1103:
1104: rv = fetched;
1105: error:
1106: return rv;
1107: }
1108:
1109: //
1110: // アクセス関数
1111: //
1112:
1113: template <int bits> uint64
1114: m88200::load(uint32 addr)
1115: {
1116: uint64 data;
1117:
1118: acc_laddr = addr;
1119: acc_read = true;
1.1.1.9 root 1120: if (__predict_false(Translate() == false)) {
1121: putlog(2, "Translation BusError %c:$%08x",
1122: (acc_super ? 'S' : 'U'), acc_laddr);
1.1 root 1123: return (uint64)-1;
1124: }
1.1.1.9 root 1125: if (__predict_false((acc_stat & DESC_CI))) {
1.1.1.3 root 1126: parent->AddCycle(7); // Table.6-2
1.1 root 1127: MBusAcquire();
1.1.1.9 root 1128: MBusMakeSnoop(acc_paddrH, 0);
1.1 root 1129: data = MBusRead(acc_paddrH + acc_paddrL, bits / 8);
1130: MBusRelease();
1.1.1.9 root 1131: if (__predict_false((int64)data < 0)) {
1132: putlog(2, "MBus BusError %c:$%08x",
1133: (acc_super ? 'S' : 'U'), acc_laddr);
1.1.1.4 root 1134: }
1.1 root 1135: return data;
1136: } else {
1137: data = CacheRead(acc_paddrH);
1.1.1.9 root 1138: if (__predict_false((int64)data < 0)) {
1139: putlog(2, "Cache BusError %c:$%08x",
1140: (acc_super ? 'S' : 'U'), acc_laddr);
1.1 root 1141: return data;
1142: }
1143:
1144: if (bits == 8) {
1145: switch (acc_paddrL) {
1146: case 0:
1147: return (data >> 24);
1148: case 1:
1149: return (data >> 16) & 0xff;
1150: case 2:
1151: return (data >> 8) & 0xff;
1152: case 3:
1153: return data & 0xff;
1154: }
1155: }
1156: if (bits == 16) {
1157: if (acc_paddrL == 0) {
1158: return data >> 16;
1159: } else {
1160: return data & 0xffff;
1161: }
1162: }
1163: if (bits == 32) {
1164: return data;
1165: }
1166: __unreachable();
1167: }
1168: }
1169:
1170: uint64
1171: m88200::load_8(uint32 addr)
1172: {
1173: return load<8>(addr);
1174: }
1175:
1176: uint64
1177: m88200::load_16(uint32 addr)
1178: {
1179: return load<16>(addr);
1180: }
1181:
1182: uint64
1183: m88200::load_32(uint32 addr)
1184: {
1185: return load<32>(addr);
1186: }
1187:
1188: template <int bits> uint64
1189: m88200::store(uint32 addr, uint32 data)
1190: {
1191: uint64 rv;
1192:
1193: acc_laddr = addr;
1194: acc_read = false;
1.1.1.9 root 1195: if (__predict_false(Translate() == false)) {
1.1 root 1196: return (uint64)-1;
1197: }
1.1.1.9 root 1198: if (__predict_false((acc_stat & DESC_CI))) {
1.1.1.3 root 1199: parent->AddCycle(7); // Table.6-2
1.1 root 1200: MBusAcquire();
1.1.1.9 root 1201: MBusMakeSnoop(acc_paddrH, 1);
1.1 root 1202: rv = MBusWrite(acc_paddrH + acc_paddrL, data, bits / 8);
1203: MBusRelease();
1204: return rv;
1205: } else {
1206: return CacheWrite(acc_paddrH + acc_paddrL, data, bits / 8);
1207: }
1208: }
1209:
1210: uint64
1211: m88200::store_8(uint32 addr, uint32 data)
1212: {
1213: return store<8>(addr, data);
1214: }
1215:
1216: uint64
1217: m88200::store_16(uint32 addr, uint32 data)
1218: {
1219: return store<16>(addr, data);
1220: }
1221:
1222: uint64
1223: m88200::store_32(uint32 addr, uint32 data)
1224: {
1225: return store<32>(addr, data);
1226: }
1227:
1228: template <int bits> uint64
1229: m88200::xmem(uint32 addr, uint32 data)
1230: {
1231: uint64 rv;
1232:
1233: acc_laddr = addr;
1234: acc_read = false;
1.1.1.9 root 1235: if (__predict_false(Translate() == false)) {
1.1 root 1236: return (uint64)-1;
1237: }
1.1.1.9 root 1238: if (__predict_false((acc_stat & DESC_CI))) {
1.1 root 1239: // XXX キャッシュ禁止領域だとどうなる?
1240: MBusAcquire();
1.1.1.9 root 1241: MBusMakeSnoop(acc_paddrH, 1);
1.1 root 1242: rv = MBusXmem(acc_paddrH + acc_paddrL, data, bits / 8);
1243: MBusRelease();
1244: return rv;
1245: } else {
1246: return CacheXmem(acc_paddrH + acc_paddrL, data, bits / 8);
1247: }
1248: }
1249:
1250: uint64
1251: m88200::xmem_8(uint32 addr, uint32 data)
1252: {
1253: return xmem<8>(addr, data);
1254: }
1255:
1256: uint64
1257: m88200::xmem_32(uint32 addr, uint32 data)
1258: {
1259: return xmem<32>(addr, data);
1260: }
1261:
1262: //
1263: // アドレス変換
1264: //
1265:
1266: // デバッグ用
1267: /*static*/ std::string
1268: m88200::stat2str(uint32 stat)
1269: {
1270: std::string buf;
1271:
1.1.1.9 root 1272: if ((stat & DESC_WT))
1.1 root 1273: buf += ",WT";
1.1.1.9 root 1274: if ((stat & DESC_G))
1.1 root 1275: buf += ",G";
1.1.1.9 root 1276: if ((stat & DESC_CI))
1.1 root 1277: buf += ",CI";
1.1.1.9 root 1278: if ((stat & DESC_M))
1.1 root 1279: buf += ",M";
1.1.1.9 root 1280: if ((stat & DESC_U))
1.1 root 1281: buf += ",U";
1.1.1.9 root 1282: if ((stat & DESC_WP))
1.1 root 1283: buf += ",WP";
1284:
1285: if (buf.empty()) {
1286: buf = ",0";
1287: }
1288: return buf.substr(1);
1289: }
1290:
1291: // アドレス変換。
1292: // 事前に acc_laddr, acc_super, acc_read を設定しておくこと。
1293: // 成功すれば acc_paddrH, acc_paddrL をセットして true を返す。
1294: // 失敗ならいろいろセットして(?) false を返す。
1295: // p2-3, Figure2-1
1296: bool
1297: m88200::Translate()
1298: {
1299: uint32 la;
1300:
1301: // Logical address(LA) presented on PBus
1302:
1303: putlog(3, "Translate %c:$%08x", (acc_super ? 'S' : 'U'), acc_laddr);
1304:
1305: // 下位2ビットは常にこうなる。
1306: acc_paddrL = acc_laddr & 3;
1307:
1.1.1.9 root 1308: // Select Area Descriptor
1309: if (SelectAreaDesc() == false) {
1310: // XXX 図のこれはたぶん間違いだよなあ…
1311: // 誤: Physical Address <= LA[18-2] :: 00
1312: // 正: Physical Address <= LA[31-2] :: 00
1313: acc_paddrH = acc_laddr - acc_paddrL;
1.1.1.3 root 1314:
1.1.1.9 root 1315: parent->AddCycle(1);
1316: return true;
1317: }
1318:
1319: // 変換する分だけカウントするのでいいか
1320: translate_total++;
1321:
1322: // type == Valid ここから
1.1 root 1323:
1.1.1.9 root 1324: // Search BATC first
1325:
1326: // まずざっくりハッシュを引く
1327: uint32 bi = batc_hash[batc_hash_func(acc_laddr)];
1.1 root 1328:
1.1.1.9 root 1329: if (__predict_false(bi != 0)) {
1330: // ハッシュで一部がヒットしたので、
1331: // 次に LBA + S 全体との完全一致で探す
1.1 root 1332: la = acc_laddr & 0xfff80000;
1333: la |= acc_super ? BATC_S : 0;
1.1.1.9 root 1334:
1335: do {
1336: int i = __builtin_ctz(bi);
1337: if (batc[i].lba == la) {
1338: m88200BATC& b = batc[i];
1.1 root 1339: putlog(4, " BATC[%d] hit", i);
1.1.1.9 root 1340: batc_hit[i]++;
1341: if (b.wp && acc_IsWrite()) {
1.1 root 1342: goto write_violation;
1343: }
1344:
1345: // Physical Address <= PBA :: LA[18-2] :: 00
1346: acc_paddrH = b.pba | (acc_laddr & 0x0007fffc);
1347: acc_stat |= b.stat;
1348: putlog(4, " BATC hit acc=%s paddr=$%08x",
1349: stat2str(acc_stat).c_str(), acc_paddrH);
1.1.1.3 root 1350:
1351: parent->AddCycle(1);
1.1 root 1352: return true;
1353: }
1.1.1.9 root 1354: // ヒットしたビットを 0 にする
1355: bi &= bi - 1;
1356: } while (bi);
1357: }
1358: // 見付からなかったので PATC へ
1.1 root 1359:
1.1.1.9 root 1360: // 2回しかループしないはず
1361: for (int loop = 0; loop < 3; loop++) {
1.1 root 1362: // BATC miss, search PATC then
1.1.1.9 root 1363: acc_patc = NULL;
1364:
1365: uint64 pi = patc_hash[patc_hash_func(acc_laddr)];
1366: #if defined(M88200_STAT)
1367: stat_patc_search++;
1368: #endif
1369:
1370: if (pi == 0) {
1371: #if defined(M88200_STAT)
1372: // 単純なサーチミスのみカウントし、M bit による合流は除きたい
1373: stat_patc_miss1++;
1374: #endif
1375: goto patc_miss;
1376: }
1377:
1.1 root 1378: la = acc_laddr & 0xfffff000;
1379: la |= acc_super ? PATC_S : 0;
1.1.1.9 root 1380:
1381: do {
1382: int i = __builtin_ctzll(pi);
1.1 root 1383: m88200PATC& p = patc[i];
1384: if (p.lpa == la) {
1385: // PATC hit
1.1.1.9 root 1386: patc_hit++;
1387: #if defined(M88200_STAT)
1388: stat_patc_hit++;
1389: #endif
1.1 root 1390: if (acc_IsWrite() && p.wp) {
1391: putlog(4, " PATC[%d] hit; stat=%s m=%d wp=%d", i,
1392: stat2str(p.stat).c_str(), p.m, p.wp);
1393: goto write_violation;
1394: }
1395: if (acc_IsWrite() && p.m == false) {
1396: // Update M bit
1397: // フローチャートでは PATC[M] はこの後のテーブルサーチ中
1398: // の Update Page Descriptor 処理中で更新するように書いて
1399: // あるように読めるのだが、その通りに実装すると、メモリ
1400: // 上の Page Descriptor に最初から M ビットが立っていると
1401: // (というか OpenBSD カーネルが用意した Page Descriptor
1402: // には立っているので) PATC[M] の更新も行われず、PATC[M]
1403: // が立っていないので再びここに来てしまい無限ループになる。
1404: // 誰が間違ってるのか分からないけど、とりあえずここで
1405: // PATC[M] を立てれば問題は起きない。
1406: putlog(4, " PATC[%d] hit; Need to update M bit", i);
1407: p.m = true;
1408:
1409: // この下の PATC miss に合流する
1410: acc_patc = &p;
1.1.1.9 root 1411: goto patc_miss;
1.1 root 1412: }
1413:
1414: // Physical Address <= PFA :: LA[11-2] :: 00
1415: acc_paddrH = p.pfa | (acc_laddr & 0x00000ffc);
1416: acc_stat |= p.stat;
1417: putlog(4, " PATC[%d] hit acc=%s paddr=$%08x", i,
1418: stat2str(acc_stat).c_str(), acc_paddrH);
1.1.1.3 root 1419:
1420: parent->AddCycle(1);
1.1 root 1421: return true;
1422: }
1.1.1.9 root 1423: // ヒットしたビットを 0 にする
1424: pi &= pi - 1;
1425: } while (pi);
1426: #if defined(M88200_STAT)
1427: // 単純なサーチミスのみカウントし、M bit による合流は除きたい
1428: stat_patc_miss2++;
1429: #endif
1430: patc_miss:
1.1.1.2 root 1431: putlog(4, " BATC/PATC miss");
1.1 root 1432:
1433: // PATC miss
1434:
1.1.1.9 root 1435: // PATC にヒットして M bit 更新するものは両方カウントされるけど
1436: atc_miss++;
1437:
1.1 root 1438: // フローチャートでは PATC miss というラベルが付いてるが、コード
1439: // では acc_patc は PATC ループ前に NULL にしてあり(=miss)、
1440: // ループで PATC エントリが見付かれば acc_patc に代入してから
1441: // break してここに来るので、ちょっとだけ見た目と違うが意図してる
1442: // 動作は同じはず。
1443:
1444: // Table search operation
1445: if (TableSearch() == true) {
1.1.1.9 root 1446: // フローチャートでは Select Area Descriptor まで戻るように
1447: // 書いてあるが、PATC ミスによるテーブルサーチでは Area Descriptor
1448: // と BATC は変化せず PATC が変化するだけなので、PATC サーチから
1449: // やり直す。
1.1 root 1450: continue;
1451: }
1452:
1453: // Invalid
1454: // Reply with fault (Figure2-10 だがここではすべて不要)
1455: return false;
1456: }
1457: PANIC("loop detected");
1458:
1459: write_violation:
1460: fault_code = FAULT_CODE_WRITE;
1461: //fault_addr UNKNOWN?
1462: // Reply with fault (Figure2-10 だがここではすべて不要)
1463: return false;
1464: }
1465:
1466:
1467: // Select Area Descriptor.
1468: // 結果が Type=Valid なら true、Type=Untranslated なら false を返す。
1469: // Probe Command からも呼ばれる。
1470: // p2-21, Figure2-5
1471: bool
1472: m88200::SelectAreaDesc()
1473: {
1.1.1.9 root 1474: acc_stat = acc_apr->stat & ACC_STAT_MASK;
1475: if (acc_apr->enable) {
1.1 root 1476: // セグメントディスクリプタのアドレスはここで決まるが、
1477: // 次に行う BATC、PATC サーチでは使わず、それらが全部ミスして
1478: // テーブルサーチまで来た所で初めて使うので、ここでは表示しない。
1.1.1.9 root 1479: acc_sdaddr = acc_apr->addr | ((acc_laddr >> 20) & ~3U);
1480: putlog(4, " %s acc=%s", acc_apr->name, stat2str(acc_stat).c_str());
1.1 root 1481: return true;
1482: } else {
1483: putlog(3, " %s acc=%s Untranslated",
1.1.1.9 root 1484: acc_apr->name, stat2str(acc_stat).c_str());
1.1 root 1485: return false;
1486: }
1487: }
1488:
1489: // Table Search
1490: // Type=Valid なら true、Type=Invalid なら false を返す。Type=Retry はない。
1491: // p2-20, Figure2-4
1492: bool
1493: m88200::TableSearch()
1494: {
1495: putlog(4, " %s $%08x/%s acc=%s", __func__, acc_laddr,
1496: (acc_read ? "Read" : "Write"), stat2str(acc_stat).c_str());
1497:
1498: // XXX フローチャートを厳密に追っかけると MBus の Acquire/Release が
1499: // 対応していないので(orz)、ここでは
1500: // TableSearch() に入ったところで Acquire、
1501: // TableSearch() から出るところで Release だけに統一する。
1502:
1503: MBusAcquire();
1504:
1505: if (FetchSegmentDesc() == false) {
1506: MBusRelease();
1.1.1.3 root 1507:
1508: parent->AddCycle(7); // Table.6-2
1.1 root 1509: return false;
1510: }
1511:
1512: if (FetchPageDesc() == false) {
1513: MBusRelease();
1.1.1.3 root 1514:
1515: parent->AddCycle(11); // Table.6-2
1.1 root 1516: return false;
1517: }
1518:
1519: // 中央の TYPE = VALID のところ
1520: // ( )
1521: // +----------+ +----------+
1522: // | |
1523: // DESC[U]=0 || Otherwise
1524: // DESC[M]=0 && WRITE |
1525: // | |
1526: // UpdatePageDesc |
1527: // | |
1528: // <- RETRY -( )-- INVALID ---------- | -->
1529: // | |
1530: // VALID |
1531: // | |
1532: // ( ) <--------------------+
1533: // | |
1534: // +---+ +------------+
1535: // Otherwise TableSearch due to PATC miss
1536: // | |
1537: // (Only U/M bits CreatePATCEntry
1538: // Updated) |
1539: // | v
1540: // +---------------> ( )
1541:
1542: if ((tmp_desc & DESC_U) == 0 ||
1543: ((tmp_desc & DESC_M) == 0 && acc_IsWrite()))
1544: {
1.1.1.3 root 1545: parent->AddCycle(15); // Table.6-2
1546:
1.1 root 1547: if (UpdatePageDesc() == false) {
1548: MBusRelease();
1549: return false;
1550: }
1.1.1.3 root 1551: } else {
1552: parent->AddCycle(11); // Table.6-2
1.1 root 1553: }
1554:
1555: if (acc_patc == NULL) {
1556: // Table search due to PATC miss
1557: CreatePATCEntry();
1558: }
1559:
1560: MBusRelease();
1561: return true;
1562: }
1563:
1564: // Fetch Segment Descriptor
1565: // Type=Valid なら true、Type=Invalid なら false を返す。Type=Retry はない。
1566: // 公式フローチャートと違って MBus Acquire された状態で呼び出すこと。
1567: // p2-22, Figure2-6
1568: bool
1569: m88200::FetchSegmentDesc()
1570: {
1571: uint64 data;
1572:
1.1.1.9 root 1573: MBusMakeSnoop(acc_sdaddr, 0);
1.1 root 1574: data = MBusRead(acc_sdaddr, 4);
1.1.1.9 root 1575: if (__predict_false((int64)data < 0)) {
1.1 root 1576: return false;
1577: }
1578: tmp_desc = (uint32)data;
1579: tmp_desc &= (0xfffff000 |
1580: DESC_WT | DESC_SP | DESC_G | DESC_CI | DESC_WP | DESC_V);
1581:
1.1.1.9 root 1582: if (__predict_false((tmp_desc & DESC_V) == 0)) {
1.1 root 1583: fault_code = FAULT_CODE_SEGMENT;
1584: fault_addr = acc_sdaddr;
1585: putlog(4, " SD $%08x desc=$%08x SegFault", acc_sdaddr, tmp_desc);
1586: return false;
1587: } else {
1588: // Descriptor is valid
1589:
1.1.1.9 root 1590: if (__predict_false((tmp_desc & DESC_SP) && acc_super == 0)) {
1.1 root 1591: fault_code = FAULT_CODE_SUPERVISOR;
1592: fault_addr = acc_sdaddr;
1593: putlog(4, " SD $%08x desc=$%08x SupervisorFault",
1594: acc_sdaddr, tmp_desc);
1595: return false;
1596: }
1597:
1598: acc_pdaddr = (tmp_desc & 0xfffff000) | ((acc_laddr >> 10) & 0xffc);
1599: acc_stat |= tmp_desc & ACC_STAT_MASK;
1600: putlog(4, " SD $%08x pdaddr=$%08x stat=%s acc=%s",
1601: acc_sdaddr, acc_pdaddr,
1602: stat2str(tmp_desc).c_str(),
1603: stat2str(acc_stat).c_str());
1604: return true;
1605: }
1606: }
1607:
1608: // Fetch Page Descriptor
1609: // Type=Valid なら true、Type=Invalid なら false を返す。Type=Retry はない。
1610: // 公式フローチャートと違って MBus Acquire された状態で呼び出すこと。
1611: // p2-23, Figure2-7
1612: bool
1613: m88200::FetchPageDesc()
1614: {
1615: uint64 data;
1616:
1.1.1.9 root 1617: MBusMakeSnoop(acc_pdaddr, 0);
1.1 root 1618: data = MBusRead(acc_pdaddr, 4);
1.1.1.9 root 1619: if (__predict_false((int64)data < 0)) {
1.1 root 1620: return false;
1621: }
1622: tmp_desc = (uint32)data;
1623: tmp_desc &= (0xfffff000 | DESC_WT | DESC_SP | DESC_G | DESC_CI |
1624: DESC_M | DESC_U | DESC_WP | DESC_V);
1625:
1.1.1.9 root 1626: if (__predict_false((tmp_desc & DESC_V) == 0)) {
1.1 root 1627: fault_code = FAULT_CODE_PAGE;
1628: fault_addr = acc_pdaddr;
1629: putlog(4, " PD $%08x desc=$%08x PageFault", acc_pdaddr, tmp_desc);
1630: return false;
1631: } else {
1632: // Descriptor is valid
1633:
1.1.1.9 root 1634: if (__predict_false((tmp_desc & DESC_SP) && acc_super == 0)) {
1.1 root 1635: fault_code = FAULT_CODE_SUPERVISOR;
1636: fault_addr = acc_pdaddr;
1637: putlog(4, " PD $%08x desc=$%08x SupervisorFault",
1638: acc_pdaddr, tmp_desc);
1639: return false;
1640: }
1641:
1642: acc_stat |= tmp_desc & ACC_STAT_MASK;
1643:
1.1.1.9 root 1644: if (__predict_false((acc_stat & DESC_WP) && acc_IsWrite())) {
1.1 root 1645: fault_code = FAULT_CODE_WRITE;
1646: // fault_addr は invalid data
1647: fault_addr = 0xdeadbeef;
1648: putlog(4, " PD $%08x desc=$%08x WriteFault", acc_pdaddr, tmp_desc);
1649: return false;
1650: } else {
1651: putlog(4, " PD $%08x stat=%s acc=%s", acc_pdaddr,
1652: stat2str(tmp_desc).c_str(),
1653: stat2str(acc_stat).c_str());
1654: return true;
1655: }
1656: }
1657: }
1658:
1659: // Update Page Descriptor
1660: // Type=Valid なら true、Type=Invalid なら false を返す。Type=Retry はない。
1661: // p2-24, Figure2-8
1662: bool
1663: m88200::UpdatePageDesc()
1664: {
1665: uint64 rv;
1666:
1667: if ((tmp_desc & DESC_M) == 0 && acc_IsWrite()) {
1668: // Update Modified bit and accrue status
1669:
1670: // XXX 図中 PATC[M] をセットするとあるが、ここでは遅い気がする。
1671: // Translate() 中の Update M bit のところのコメントも参照。
1672:
1673: // XXX 図中 ACC_STATUS[M] は TEMP_DESCR[M] じゃないの?
1674: tmp_desc |= DESC_M;
1675: }
1676:
1677: // Update Used bit
1678: tmp_desc |= DESC_U;
1679:
1.1.1.9 root 1680: MBusMakeSnoop(acc_pdaddr, 1);
1.1 root 1681: rv = MBusWrite(acc_pdaddr, tmp_desc, 4);
1.1.1.9 root 1682: if (__predict_false((int64)rv < 0)) {
1.1 root 1683: return false;
1684: }
1685: putlog(4, " Update PD $%08x desc=$%08x stat=%s",
1686: acc_pdaddr, tmp_desc, stat2str(tmp_desc).c_str());
1687:
1688: return true;
1689: }
1690:
1.1.1.9 root 1691: // patc[patc_index] に対応する PATC ハッシュを無効にする
1692: void
1693: m88200::InvalidatePATCHash(int patc_index)
1694: {
1695: m88200PATC& p = patc[patc_index];
1696: int hashkey = patc_hash_func(p.lpa);
1697:
1698: uint64 bit = 1ULL << patc_index;
1699: if (p.IsS()) {
1700: patc_hash_s[hashkey] &= ~bit;
1701: } else {
1702: patc_hash_u[hashkey] &= ~bit;
1703: }
1704: }
1705:
1.1 root 1706: // Create PATC Entry
1707: // 公式フローチャートと違って MBus Acquire したまま戻ること。
1708: // p2-25, Figure2-9
1709: void
1710: m88200::CreatePATCEntry()
1711: {
1.1.1.9 root 1712: int hashkey;
1.1 root 1713:
1.1.1.9 root 1714: // 空きがあろうがなかろうが、とにかく一番古いエントリを使う。
1715: // おそらく実機も空きエントリを前に詰めるような複雑な処理はして
1716: // ないだろうという推測。
1717: // 検索はハッシュを使って実装してあるため、途中に空きエントリが
1718: // あっても (線形探索する時のような) デメリットはない。
1719:
1720: #if defined(M88200_STAT)
1721: stat_patc_create++;
1722: #endif
1723: m88200PATC& p = patc[patc_next];
1724: // 有効なら無効化
1725: if (p.IsValid()) {
1726: InvalidatePATCHash(patc_next);
1727: }
1728:
1729: // このエントリを潰して作る
1.1 root 1730: memset(&p, 0, sizeof(p));
1731: p.lpa = acc_laddr & 0xfffff000;
1732: p.pfa = tmp_desc & 0xfffff000;
1733: if (acc_super) {
1734: p.lpa |= PATC_S;
1735: }
1736: p.stat = acc_stat;
1737: putlog(4, " %s %c:$%08x:$%08x stat=%s", __func__,
1.1.1.9 root 1738: p.IsS() ? 'S' : 'U', (p.lpa & 0xfffff000), p.pfa,
1.1 root 1739: stat2str(p.stat).c_str());
1.1.1.9 root 1740:
1741: hashkey = patc_hash_func(p.lpa);
1742: uint64 bit = 1ULL << patc_next;
1743: patc_hash[hashkey] |= bit;
1744:
1745: patc_next++;
1746: if (__predict_false(patc_next >= patc.size())) {
1747: patc_next = 0;
1748: }
1.1 root 1749: }
1750:
1751: // アドレス変換 (デバッガ用)
1752: uint64
1753: m88200::TranslatePeek(uint32 laddr, bool issuper, bool do_search) const
1754: {
1755: const m88200APR *xapr;
1756: uint32 la;
1757:
1758: // Select Area Descriptor
1759: if (issuper) {
1760: xapr = &sapr;
1761: } else {
1762: xapr = &uapr;
1763: }
1764: if (xapr->enable == 0) {
1765: return laddr;
1766: }
1767:
1768: // Search BATC first
1769: la = laddr & 0xfff80000;
1770: la |= issuper ? BATC_S : 0;
1771: for (const auto& b : batc) {
1772: if (b.lba == la) {
1773: // BATC hit
1774: return b.pba | (laddr & 0x0007ffff);
1775: }
1776: }
1777:
1778: // BATC miss, search PATC then
1779: la = laddr & 0xfffff000;
1780: la |= issuper ? PATC_S : 0;
1781: for (const auto& p : patc) {
1782: if (p.lpa == la) {
1783: // PATC hit
1784: return p.pfa | (laddr & 0x00000fff);
1785: }
1786: }
1787:
1788: if (do_search == false) {
1789: return (uint64)-1;
1790: }
1791:
1792: // ここからテーブルサーチ
1793: uint64 data;
1794: uint32 desc_addr;
1795: uint32 desc;
1796:
1797: // Peek Segment Descriptor
1798: desc_addr = xapr->addr | ((laddr >> 20) & ~3U);
1799: data = vm_phys_peek_32(desc_addr);
1800: if ((int64)data < 0) {
1801: return data;
1802: }
1803: desc = (uint32)data;
1804: if ((desc & DESC_V) == 0) {
1805: return (uint64)-1;
1806: }
1807: if ((desc & DESC_SP) && issuper == false) {
1808: return (uint64)-1;
1809: }
1810:
1811: // Peek Page Descriptor
1812: desc_addr = (desc & 0xfffff000) | ((laddr >> 10) & 0xffc);
1813: data = vm_phys_peek_32(desc_addr);
1814: if ((int64)data < 0) {
1815: return data;
1816: }
1817: desc = (uint32)data;
1818: if ((desc & DESC_V) == 0) {
1819: return (uint64)-1;
1820: }
1821: if ((desc & DESC_SP) && issuper == false) {
1822: return (uint64)-1;
1823: }
1824:
1825: return (desc & 0xfffff000) | (laddr & 0x00000fff);
1826: }
1827:
1828:
1829: //
1830: // キャッシュ
1831: //
1832:
1833: // このラインの状態を更新
1834: void
1835: m88200CacheSet::Update(int line, m88200CacheSet::Status status)
1836: {
1837: vv[line] = status;
1.1.1.9 root 1838: if (__predict_false(status == IV)) {
1.1 root 1839: // 無効なら
1840: tag[line] |= TAG_INVALID;
1841: L = TryUnuseLine(L, line);
1842: }
1843: }
1844:
1845: // CSSP L5-L0 の処理。
1846: // 引数 tmpL の状態から line を最新にしたらどうなるか、を返す。
1847: // 表示処理で一時変数に対して処理が必要なため分離してある。
1.1.1.9 root 1848: /*static*/ int
1.1 root 1849: m88200CacheSet::TryUseLine(int tmpL, int line)
1850: {
1851: // L フィールドは 3bit, 2bit, 1bit で構成され、
1852: // 他のラインの対応ビットを落として、
1853: // 自分のラインの対応ビットを全部立てれば、
1854: // 他のラインの順序を保存した状態で、自分が最新になるように
1855: // 順序をつけられる。
1856:
1857: if (line == 3) {
1858: tmpL |= 0b111'00'0;
1859: } else if (line == 2) {
1860: tmpL &= 0b011'11'1;
1861: tmpL |= 0b000'11'0;
1862: } else if (line == 1) {
1863: tmpL &= 0b101'01'1;
1864: tmpL |= 0b000'00'1;
1865: } else {
1866: tmpL &= 0b110'10'0;
1867: }
1868: return tmpL;
1869: }
1870:
1871: // CSSP L5-L0 の処理。
1872: // 引数 tmpL の状態から line を最古にしたらどうなるか、を返す。
1.1.1.9 root 1873: /*static*/ int
1.1 root 1874: m88200CacheSet::TryUnuseLine(int tmpL, int line)
1875: {
1876: // Use のちょうど反転論理
1877: if (line == 3) {
1878: tmpL &= 0b000'11'1;
1879: } else if (line == 2) {
1880: tmpL |= 0b100'00'0;
1881: tmpL &= 0b111'00'1;
1882: } else if (line == 1) {
1883: tmpL |= 0b010'10'0;
1884: tmpL &= 0b111'11'0;
1885: } else {
1886: tmpL |= 0b001'01'1;
1887: }
1888: return tmpL;
1889: }
1890:
1891: // CSSP L5-L0 の処理。
1892: // 引数 tmpL の状態で、最新の line を返す。
1893: // 表示処理で一時変数に対して処理が必要なため分離してある。
1.1.1.9 root 1894: /*static*/ int
1.1 root 1895: m88200CacheSet::TryGetOldestLine(int tmpL)
1896: {
1897: if (tmpL < 8) {
1898: return 3;
1899: }
1900: tmpL &= 7;
1901: if (tmpL < 2) {
1902: return 2;
1903: }
1904: tmpL &= 1;
1905: if (tmpL == 0) {
1906: return 1;
1907: } else {
1908: return 0;
1909: }
1910: }
1911:
1912: // 更新用に最も古いラインを選んで差し出す
1913: int
1914: m88200CacheSet::SelectOldestLine() const
1915: {
1916: // なければ、最も古いラインを差し出す
1917: return TryGetOldestLine(L);
1918: }
1919:
1920: // キャッシュの指定の line, word に data を書き込む。
1921: // size は 1, 2, 4 バイト。
1922: void
1923: m88200CacheSet::Write(int line, uint32 paddr, uint32 data, int size)
1924: {
1925: uint32 wordidx = (paddr >> 2) & 0x03;
1926: uint32 data32;
1927:
1928: if (__predict_true(size == 4)) {
1929: word[line * 4 + wordidx] = data;
1930: return;
1931: }
1932:
1933: data32 = word[line * 4 + wordidx];
1934: if (size == 2) {
1935: if ((paddr & 2) == 0) {
1936: data32 = (data32 & 0x0000ffff) | (data << 16);
1937: } else {
1938: data32 = (data32 & 0xffff0000) | data;
1939: }
1940: } else {
1941: switch (paddr & 3) {
1942: case 0:
1943: data32 = (data32 & 0x00ffffff) | (data << 24);
1944: break;
1945: case 1:
1946: data32 = (data32 & 0xff00ffff) | (data << 16);
1947: break;
1948: case 2:
1949: data32 = (data32 & 0xffff00ff) | (data << 8);
1950: break;
1951: case 3:
1952: data32 = (data32 & 0xffffff00) | data;
1953: break;
1954: default:
1955: __unreachable();
1956: }
1957: }
1958: word[line * 4 + wordidx] = data32;
1959: }
1960:
1.1.1.3 root 1961: // キャッシュを検索。
1962: // ヒットすれば line 番号(0..3) を返す。ヒットしなければ -1 を返す。
1963: int
1964: m88200CacheSet::Lookup(uint32 tagaddr) const
1965: {
1966: for (int line = 0; line < 4; line++) {
1967: if (tag[line] == tagaddr) {
1968: return line;
1969: }
1970: }
1971: return -1;
1972: }
1973:
1974:
1975: // キャッシュの指定の set, line にメモリから 1 ライン(4word) 読み込む。
1.1 root 1976: // 成功すれば 0 を返す。
1.1.1.3 root 1977: // バスエラーなら fault_code, fault_addr をセットし (uint64)-1 を返す。
1.1 root 1978: uint64
1.1.1.3 root 1979: m88200::ReadLine(m88200CacheSet& set, int line, uint32 tagaddr)
1.1 root 1980: {
1981: uint32 addr;
1.1.1.3 root 1982: uint64 data;
1983:
1984: // キャッシュラインの充填は実機では Burst Read されているらしいが
1985: // nono では Burst Read 用のメソッドは用意しておらず通常の 32bit
1986: // アクセスをしているため、このままでは所要ウェイト数が正しくない。
1987: // そこでここで差を調整することにする。
1988: // vm_phys_read_32() 4回なのでこれによって 12 wait が加算される。一方
1989: // 実機の Burst Read は取扱説明書によると 320ns (8 サイクル) で完了
1990: // するようなので、その差 4 をあらかじめ引いておく。
1991: // 途中でバスエラーが起きたらとかそこまでは考慮しない。
1.1.1.4 root 1992: AddCycle(-4);
1.1.1.3 root 1993:
1994: // タグを更新
1995: set.tag[line] = tagaddr;
1.1 root 1996:
1.1.1.3 root 1997: addr = set.tag[line] | (set.setidx << 4);
1.1 root 1998: for (int i = 0; i < 4; i++) {
1.1.1.3 root 1999: data = vm_phys_read_32(addr);
1.1.1.9 root 2000: if (__predict_false((int64)data < 0)) {
1.1.1.3 root 2001: fault_code = FAULT_CODE_BUSERR;
2002: fault_addr = addr;
2003: return data;
1.1 root 2004: }
1.1.1.3 root 2005: set.word[line * 4 + i] = data;
1.1 root 2006: addr += 4;
2007: }
2008: return 0;
2009: }
2010:
1.1.1.3 root 2011: // キャッシュの指定の set, line を1ライン、メモリに書き出す(コピーバック)。
2012: // 成功すれば 0 を返す。
2013: // バスエラーなら fault_code, fault_addr をセットし (uint64)-1 を返す。
2014: uint64
2015: m88200::CopyBackLine(m88200CacheSet& set, int line)
1.1 root 2016: {
1.1.1.3 root 2017: uint32 addr;
2018:
2019: // キャッシュラインの書き出しは実機では Burst Write されているらしいが
2020: // nono では Burst Write 用のメソッドは用意しておらず通常の 32bit
2021: // アクセスをしているため、このままでは所要ウェイト数が正しくない。
2022: // そこでここで差を調整することにする。
2023: // vm_phys_write_32() 4回なのでこれによって 8 wait が加算される。一方
2024: // 実機の Burst Write は取扱説明書によると 280ns (7サイクル) で完了する
2025: // ようなので、その差 1 をあらかじめて引いておく。
2026: // 途中でバスエラーが起きたらとかそこまでは考慮しない。
1.1.1.4 root 2027: AddCycle(-1);
1.1.1.3 root 2028:
2029: addr = set.tag[line] | (set.setidx << 4);
2030: for (int i = 0; i < 4; i++) {
2031: uint64 rv = vm_phys_write_32(addr, set.word[line * 4 + i]);
1.1.1.9 root 2032: if (__predict_false((int64)rv < 0)) {
1.1.1.3 root 2033: fault_code = FAULT_CODE_BUSERR;
2034: fault_addr = addr;
2035: return rv;
1.1 root 2036: }
1.1.1.3 root 2037: addr += 4;
1.1 root 2038: }
1.1.1.3 root 2039: return 0;
1.1 root 2040: }
2041:
2042: // キャッシュに対して paddr の読み込みを行う。
2043: // paddr は 32bit 境界のアドレスであること。
2044: // 読み込めれば該当の32bitワードを返す。
2045: // 何らかエラーが起きれば (uint64)-1 を返す。エラー要因は fault_code とか参照。
2046: // p.3-8 Figure 3-3
2047: uint64
2048: m88200::CacheRead(uint32 paddr)
2049: {
2050: int line;
2051: uint64 rv;
2052:
2053: assert((paddr & 3) == 0);
2054:
2055: // タグとセット番号
2056: uint32 tagaddr = (paddr & 0xfffff000);
2057: uint32 setidx = (paddr >> 4) & 0xff;
2058: uint32 wordidx = (paddr >> 2) & 0x03;
2059:
2060: m88200CacheSet& set = setarray[setidx];
2061: putlog(3, "CacheRead paddr=$%08x (set=$%02x)", paddr, setidx);
2062:
2063: line = set.Lookup(tagaddr);
1.1.1.9 root 2064: if (__predict_true(line >= 0)) {
1.1 root 2065: // Cache Hit
2066: putlog(4, " CacheRead hit (line=%d,word=%d)", line, wordidx);
1.1.1.3 root 2067: parent->AddCycle(1);
1.1 root 2068: goto success;
2069: }
2070:
2071: // Cache Miss
2072:
1.1.1.3 root 2073: parent->AddCycle(10); // Table.6-2
2074:
1.1 root 2075: // reply <- Wait
2076: MBusAcquire();
2077:
2078: // Select cache line for replacement
2079: line = set.SelectOldestLine();
2080: putlog(4, " CacheRead miss (replace line=%d)", line);
2081:
2082: if (set.vv[line] == m88200CacheSet::EM) {
1.1.1.3 root 2083: parent->AddCycle(7); // Table.6-2
2084: rv = CopyBackLine(set, line);
1.1 root 2085: if ((int64)rv < 0) {
2086: goto error;
2087: }
2088: }
2089:
2090: // Mark cache line invalid
2091: set.Update(line, m88200CacheSet::IV);
2092:
2093: // Read line from memory
1.1.1.9 root 2094: MBusMakeSnoop(paddr, 0);
1.1.1.3 root 2095: rv = ReadLine(set, line, tagaddr);
1.1.1.9 root 2096: if (__predict_false((int64)rv < 0)) {
1.1 root 2097: goto error;
2098: }
2099:
2100: MBusRelease();
2101:
2102: // Update cache line
2103: set.Update(line, m88200CacheSet::SU);
2104:
2105: putlog(4, " CacheRead updated (line=%d,word=%d)", line, wordidx);
2106: success:
2107: set.Use(line);
2108: return set.word[line * 4 + wordidx];
2109:
2110: error:
2111: MBusRelease();
2112: return (uint64)-1;
2113: }
2114:
2115: // キャッシュに対して paddr への data の書き込みを行う。size は 1, 2, 4 バイト。
2116: // paddr は size に応じた境界にあること。
2117: // 書き込めれば 0、エラーが起きれば (uint64)-1 を返す。
2118: // エラー要因は fault_code とか参照。
2119: // p.3-10 Figure 3-5
2120: uint64
2121: m88200::CacheWrite(uint32 paddr, uint32 data, int size)
2122: {
2123: int line;
2124: uint64 rv;
2125:
2126: assert((paddr & (size - 1)) == 0);
2127:
2128: // タグとセット番号
2129: uint32 tagaddr = (paddr & 0xfffff000);
2130: uint32 setidx = (paddr >> 4) & 0xff;
2131:
2132: m88200CacheSet& set = setarray[setidx];
2133: putlog(3, "CacheWrite paddr=$%08x (set=$%02x)", paddr, setidx);
2134:
2135: line = set.Lookup(tagaddr);
1.1.1.9 root 2136: if (__predict_true(line >= 0)) {
1.1 root 2137: // Cache Hit
1.1.1.3 root 2138: parent->AddCycle(1);
1.1 root 2139: return CacheWriteHit(set, line, paddr, data, size);
2140: }
2141:
2142: // Cache Miss
2143:
1.1.1.3 root 2144: parent->AddCycle(14); // Table.6-2
2145:
1.1 root 2146: // reply <- Wait
2147: MBusAcquire();
2148:
2149: // Select cache line for replacement
2150: line = set.SelectOldestLine();
2151: putlog(4, " CacheWrite miss (replace line=%d)", line);
2152:
2153: if (set.vv[line] == m88200CacheSet::EM) {
1.1.1.3 root 2154: parent->AddCycle(7); // Table.6-2
2155: rv = CopyBackLine(set, line);
1.1 root 2156: if ((int64)rv < 0) {
2157: goto error;
2158: }
2159: }
2160:
2161: // Mark line invalid
2162: set.Update(line, m88200CacheSet::IV);
2163:
2164: // Read line with intent to modify
1.1.1.9 root 2165: MBusMakeSnoop(paddr, 1);
1.1.1.3 root 2166: rv = ReadLine(set, line, tagaddr);
1.1 root 2167: if ((int64)rv < 0) {
2168: goto error;
2169: }
2170:
2171: // Write data to memory
2172: rv = MBusWrite(paddr, data, size);
2173: if ((int64)rv < 0) {
1.1.1.3 root 2174: goto error;
1.1 root 2175: }
2176:
2177: // Write data into cache
2178: set.Write(line, paddr, data, size);
2179:
2180: // Mark line exclusive unmodified
2181: set.Update(line, m88200CacheSet::EU);
2182: set.Use(line);
2183:
2184: MBusRelease();
2185: return 0;
2186:
2187: error:
2188: MBusRelease();
2189: return (uint64)-1;
2190: }
2191:
2192: // キャッシュがヒットした場合。
2193: // 書き込めれば 0、エラーが起きれば (uint64)-1 を返す。
2194: // p3-11 Figure 3-6
2195: uint64
2196: m88200::CacheWriteHit(m88200CacheSet& set, int line,
2197: uint32 paddr, uint32 data, int size)
2198: {
2199: uint64 rv;
2200:
1.1.1.9 root 2201: if (__predict_false(set.vv[line] == m88200CacheSet::SU)) {
1.1 root 2202: // Line Shared Unmodified の場合
2203:
2204: putlog(4, " CacheWrite hit shared unmodified (line=%d)", line);
2205:
2206: // WriteThrough と Global は最後の状態変化だけが違う
2207: if ((acc_stat & (DESC_WT | DESC_G))) {
2208: // reply = Wait;
2209: MBusAcquire();
2210:
2211: // Write data to cache
2212: set.Write(line, paddr, data, size);
2213:
2214: // Write data to memory
1.1.1.9 root 2215: MBusMakeSnoop(paddr, 1);
1.1 root 2216: rv = MBusWrite(paddr, data, size);
2217: if ((int64)rv < 0) {
2218: MBusRelease();
2219: return rv;
2220: }
2221:
2222: if ((acc_stat & DESC_WT)) {
2223: // Mark line shared unmodified
2224: set.Update(line, m88200CacheSet::SU);
2225: } else {
2226: // Mark line exclusive unmodified
2227: set.Update(line, m88200CacheSet::EU);
2228: }
2229: set.Use(line);
2230:
2231: MBusRelease();
2232: return 0;
2233: }
2234:
2235: // どちらでもない場合は Line Exclusive と同じ処理に落ちる
1.1.1.9 root 2236: } else {
2237: // Line Exclusive の場合
2238: putlog(4, " CacheWrite hit exclusive (line=%d)", line);
1.1 root 2239: }
2240:
2241: // Write data into cache
2242: putlog(4, " CacheWrite line=%d $%08x sz=%d", line, paddr, size);
2243: set.Write(line, paddr, data, size);
2244:
2245: // Mark line exclusive modified
2246: set.Update(line, m88200CacheSet::EM);
2247: set.Use(line);
2248: return 0;
2249: }
2250:
2251: // paddr への xmem を行う。size は 1, 2, 4 バイト。
2252: // 成功すれば読み出した値、エラーが起きれば (uint64)-1 を返す。
2253: // p3-13, Figure3-7
2254: uint64
2255: m88200::CacheXmem(uint32 paddr, uint32 data, int size)
2256: {
2257: int line;
2258: uint64 rv;
2259:
2260: assert((paddr & (size - 1)) == 0);
2261:
2262: // タグとセット番号
2263: uint32 tagaddr = (paddr & 0xfffff000);
2264: uint32 setidx = (paddr >> 4) & 0xff;
2265:
2266: m88200CacheSet& set = setarray[setidx];
2267: putlog(3, "CacheXmem paddr=$%08x (set=$%02x)", paddr, setidx);
2268:
2269: line = set.Lookup(tagaddr);
1.1.1.9 root 2270: if (__predict_false(line >= 0 && set.vv[line] == m88200CacheSet::EM)) {
1.1 root 2271: // Cache Hit (and Line exclusive modified)
2272: putlog(4, " CacheXmem hit and EM (line=%d)", line);
2273:
2274: MBusAcquire();
2275:
1.1.1.3 root 2276: parent->AddCycle(7); // Table.6-2
1.1.1.9 root 2277: MBusMakeSnoop(paddr, 1);
1.1.1.3 root 2278: rv = CopyBackLine(set, line);
1.1.1.9 root 2279: if (__predict_false((int64)rv < 0)) {
1.1 root 2280: goto error;
2281: }
2282:
2283: set.Update(line, m88200CacheSet::IV);
2284: } else {
2285: // Cache Miss or (Cache Hit but other than exclusive modified)
2286:
2287: if (line >= 0) {
2288: // Cache Hit and Otherwise (= !EM)
2289: putlog(4, " CacheXmem hit but !EM (line=%d)", line);
2290: set.Update(line, m88200CacheSet::IV);
2291: } else {
2292: putlog(4, " CacheXmem miss");
2293: }
2294:
2295: MBusAcquire();
2296: }
2297:
1.1.1.9 root 2298: MBusMakeSnoop(paddr, 1);
1.1 root 2299: rv = MBusXmem(paddr, data, size);
2300: MBusRelease();
2301: return rv;
2302:
2303: error:
2304: MBusRelease();
2305: return rv;
2306: }
1.1.1.3 root 2307:
2308: // サイクルを加算する (VM から使う)
2309: void
1.1.1.4 root 2310: m88200::AddCycle(int32 cycle)
1.1.1.3 root 2311: {
2312: parent->AddCycle(cycle);
2313: }
2314:
2315: // MBus 使用権を取得する
2316: void
2317: m88200::MBusAcquire()
2318: {
2319: // アービトレーションのたびに1クロックかかる(?)
2320: // よく分からんけど、とりあえず、CMMU は一度所有権を持ったら放さない、
2321: // 別の CMMU がバスリクエストすると所有権はその CMMU に移る、とする。
1.1.1.9 root 2322: if (__predict_false(mbus_master != this)) {
1.1.1.3 root 2323: mbus_master = this;
2324: parent->AddCycle(1);
2325: }
2326: }
1.1.1.9 root 2327:
2328: // 他 CMMU にバススヌープさせるポイント。
2329: // 本来はバスマスタ CMMU が MBus にアドレスと IM を出すと、それを監視して
2330: // いる他 CMMU が必要に応じて反応するのだが、ここでは全部マスタ主導で行う。
2331: // Figure 3-8, 3-9
2332: void
2333: m88200::MBusMakeSnoop(uint32 paddr, int im)
2334: {
2335: if ((acc_stat & DESC_G) == 0) {
2336: return;
2337: }
2338:
2339: // MBus Status <- Wait (2 clocks)
2340: // 本来はスヌープをするスレーブ側が 2 クロックかかるのだが、
2341: // 個別の CMMU がクロックを持っていない実装なのでマスタ側で
2342: // クロックを消費したことにする。
2343: parent->AddCycle(2);
2344:
2345: // 本来はここで解放ではなく、スレーブ側が反応したい時にバスリクエストを
2346: // 出してそれによって手放すのだが、その機構はないので、こちらが自主的に
2347: // 一旦手放したようにしておく。とは言っても現状 MBusRelease() はダミーで
2348: // 実際には他の誰かが MBusAcquire() を呼ぶまで握りっぱなしなので、
2349: // あまり問題ないはず。
2350: MBusRelease();
2351: for (const auto other : other_cmmu) {
2352: other->Snoop(paddr, im);
2353: }
2354: MBusAcquire();
2355: }
2356:
2357: // バススヌープ (スレーブ側)。
2358: // 本来はバスマスタでない CMMU は、バスマスタが MBus に出すアドレス情報を
2359: // 監視し、必要ならそれに反応するのだが、ここでは全部マスタ主導で行う。
2360: // こっちはそのマスタから呼ばれるスレーブ側。
2361: // Figure 3-8, 3-9
2362: void
2363: m88200::Snoop(uint32 paddr, int im)
2364: {
2365: assert((sctr & SCTR_SE));
2366:
2367: uint32 tagaddr = (paddr & 0xfffff000);
2368: uint32 setidx = (paddr >> 4) & 0xff;
2369:
2370: m88200CacheSet& set = setarray[setidx];
2371: int line = set.Lookup(tagaddr);
2372:
2373: if (line >= 0) {
2374: // Cache hit
2375: if (set.vv[line] == m88200CacheSet::EM) {
2376: // Line exclusive modified
2377:
2378: // Assert retry
2379:
2380: // Request MBus
2381: // 実際にはここで MBus 使用権を要求して獲得するのだが
2382: // その機構はなくその代わり Acquire を呼ぶだけでいける。
2383: MBusAcquire();
2384: uint64 rv = CopyBackLine(set, line);
2385: MBusRelease();
2386: if ((int64)rv < 0) {
2387: // Set CE bit in system status register
2388: return;
2389: }
2390:
2391: if (im == 0) {
2392: // Mark line shared unmodified (if IM=0)
2393: set.Update(line, m88200CacheSet::SU);
2394: } else {
2395: // Mark line invalid (if IM=1)
2396: set.Update(line, m88200CacheSet::IV);
2397: }
2398: }
2399: }
2400: // Resume servicing PBus
2401: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.