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