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