--- nono/m88xx0/m88200.cpp 2026/04/29 17:05:15 1.1.1.11 +++ nono/m88xx0/m88200.cpp 2026/04/29 17:05:47 1.1.1.16 @@ -8,8 +8,15 @@ #include "m88200.h" #include "mainbus.h" +#include "monitor.h" #include "mpu88xx0.h" +#if defined(M88200_STAT) +#define STAT(stmt) (stmt)++ +#else +#define STAT(stmt) /**/ +#endif + // static 変数 m88200 *m88200::mbus_master = NULL; @@ -31,35 +38,25 @@ m88200::m88200(MPU88xx0Device *parent_, ClearAlias(); AddAlias(string_format("CMMU%u", id)); - reg_monitor.func = ToMonitorCallback(&m88200::MonitorUpdateReg); - reg_monitor.SetSize(52, 8); - reg_monitor.Regist(ID_MONITOR_CMMU(id)); - - atc_monitor.func = ToMonitorCallback(&m88200::MonitorUpdateATC); + reg_monitor = gMonitorManager->Regist(ID_MONITOR_CMMU(id), this); + reg_monitor->func = ToMonitorCallback(&m88200::MonitorUpdateReg); + reg_monitor->SetSize(52, 8); + + atc_monitor = gMonitorManager->Regist(ID_MONITOR_ATC(id), this); + atc_monitor->func = ToMonitorCallback(&m88200::MonitorUpdateATC); + uint h = 44; #if defined(M88200_STAT) - atc_monitor.SetSize(77, 39 + 15); -#else - atc_monitor.SetSize(77, 39); + h += 16; #endif - atc_monitor.Regist(ID_MONITOR_ATC(id)); + atc_monitor->SetSize(77, h); - cache_monitor.func = ToMonitorCallback(&m88200::MonitorUpdateCache); - cache_monitor.SetSize(82, 23); - cache_monitor.Regist(ID_SUBWIN_CACHE(id)); + cache_monitor = gMonitorManager->Regist(ID_SUBWIN_CACHE(id), this); + cache_monitor->func = ToMonitorCallback(&m88200::MonitorUpdateCache); + cache_monitor->SetSize(82, 23); // ログ表示用の名前 sapr.name = "SAPR"; uapr.name = "UAPR"; - - // 暗黙 BATC を初期化 - SetBWP(8, 0xfff7ffb5); // $fff8xxxx,S,WT,CI,V - SetBWP(9, 0xfffffff5); // $ffffxxxx,S,WT,CI,V - - // セットインデックスを初期化 - for (int i = 0; i < setarray.size(); i++) { - auto& set = setarray[i]; - set.setidx = i; - } } // m88200 デストラクタ @@ -77,6 +74,16 @@ m88200::Init() mainbus = GetMainbusDevice(); + // 暗黙 BATC を初期化 + SetBATC(8, 0xfff00000U, 0xfff00000U, BWP_S | BWP_WT | BWP_CI | BWP_V); + SetBATC(9, 0xfff80000U, 0xfff80000U, BWP_S | BWP_WT | BWP_CI | BWP_V); + + // セットインデックスを初期化 + for (uint i = 0; i < setarray.size(); i++) { + auto& set = setarray[i]; + set.setidx = i; + } + return true; } @@ -84,7 +91,7 @@ m88200::Init() void m88200::Reset() { - const uint32 Undefined = 0xcccccccc; + const uint32 Undefined = 0xccccccccU; // レジスタ (p6-4, Table6-3) // ID, version は別途 Set される。 @@ -98,6 +105,8 @@ m88200::Reset() sapr.stat = APR_CI; uapr.enable = false; uapr.stat = APR_CI; + + // BATC, PATC はリセットで初期化されないようだが確認のしようがない。 } // モニター更新 (CMMU レジスタ) @@ -109,7 +118,7 @@ m88200::MonitorUpdateReg(Monitor *, Text int x; int y; - baseaddr = 0xfff00000 + (id << 12); + baseaddr = 0xfff00000U + (id << 12); screen.Clear(); x = 24; @@ -158,8 +167,8 @@ m88200::MonitorUpdateReg(Monitor *, Text screen.Print(0, y++, "$%08x PFAR:%08x", baseaddr + 0x10c, GetPFAR()); - for (int i = 0; i < 2; i++) { - int s = 1 - i; + for (uint i = 0; i < 2; i++) { + uint s = 1 - i; reg = GetAPR(s); screen.Print(0, y, "$%08x %cAPR:%08x", baseaddr + 0x200 + i * 4, s ? 'S' : 'U', reg); @@ -182,14 +191,14 @@ m88200::MonitorUpdateATC(Monitor *, Text screen.Clear(); - screen.Print(0, 0, "SAPR %05x'000 TE=%d %c%c%c", + screen.Print(0, 0, "SAPR %05x'000 TE=%u %c%c%c", (sapr.addr >> 12) & 0xfffff, sapr.enable ? 1 : 0, (sapr.stat & APR_WT) ? 'T' : '-', (sapr.stat & APR_G) ? 'G' : '-', (sapr.stat & APR_CI) ? 'C' : '-'); - screen.Print(40, 0, "UAPR %05x'000 TE=%d %c%c%c", + screen.Print(40, 0, "UAPR %05x'000 TE=%u %c%c%c", (uapr.addr >> 12) & 0xfffff, uapr.enable ? 1 : 0, (uapr.stat & APR_WT) ? 'T' : '-', @@ -202,15 +211,15 @@ m88200::MonitorUpdateATC(Monitor *, Text x = 0; y = 3; uint64 batc_total = 0; - for (int i = 0; i < 10; i++) { + for (uint i = 0; i < 10; i++) { if (i == batc.size() / 2) { x = 40; y = 3; } if (i < 8) { - screen.Print(x, y, " %d:", i); + screen.Print(x, y, " %u:", i); } else { - screen.Print(x, y, "(%d)", i); + screen.Print(x, y, "(%u)", i); } const m88200BATC& b = batc[i]; @@ -239,12 +248,12 @@ m88200::MonitorUpdateATC(Monitor *, Text x = 0; y = 10; - for (int i = 0; i < patc.size(); i++) { + for (uint i = 0; i < patc.size(); i++) { if (i == patc.size() / 2) { x = 40; y = 10; } - screen.Print(x, y, "%2d:", i); + screen.Print(x, y, "%2u:", i); const m88200PATC& p = patc[i]; if (p.IsValid()) { @@ -260,29 +269,64 @@ m88200::MonitorUpdateATC(Monitor *, Text y++; } - if (__predict_false(translate_total == 0)) { - screen.Puts(40, y, "BATC --.--%, PATC --.--%, Miss --.--%"); + y++; + screen.Puts(0, y++, ""); + x = 49; + screen.Print(0, y++, "Translate %26s", + format_number(translate_total).c_str()); + screen.Print(0, y, " BATC hit %26s (", + format_number(batc_total).c_str()); + if (__predict_false(batc_total == 0)) { + screen.Puts(x, y, "---.-%)"); + } else { + screen.Print(x, y, "%5.1f%%)", + (double)batc_total / translate_total * 100); + } + y++; + screen.Print(0, y, " PATC hit %26s (", + format_number(patc_hit).c_str()); + if (__predict_false(patc_hit == 0)) { + screen.Puts(x, y, "---.-%)"); } else { - screen.Print(40, y, "BATC%6.2f%%, PATC%6.2f%%, Miss%6.2f%%", - (double)batc_total / translate_total * 100, - (double)patc_hit / translate_total * 100, + screen.Print(x, y, "%5.1f%%)", + (double)patc_hit / translate_total * 100); + } + y++; + screen.Print(0, y, " BATC/PATC miss %26s (", + format_number(atc_miss).c_str()); + if (__predict_false(atc_miss == 0)) { + screen.Puts(x, y, "---.-%)"); + } else { + screen.Print(x, y, "%5.1f%%)", (double)atc_miss / translate_total * 100); } - + y++; #if defined(M88200_STAT) + // 開発用の統計情報。 + y++; + screen.Print(0, y++, "PATC create %26s", + format_number(stat_patc_create).c_str()); + screen.Print(0, y++, "PATC invalidate %26s", + format_number(stat_patc_invalidate).c_str()); + screen.Print(0, y++, "PATC SCR InvAll %26s", + format_number(stat_patc_invcmd_all).c_str()); + screen.Print(0, y++, "PATC SCR InvSegment %26s", + format_number(stat_patc_invcmd_seg).c_str()); + screen.Print(0, y++, "PATC SCR InvPage %26s", + format_number(stat_patc_invcmd_page).c_str()); + y++; double v; - screen.Puts(0, y++, ""); screen.Print(0, y++, "PATC hash search %26s", format_number(stat_patc_search).c_str()); // ヒット率 screen.Print(0, y, "PATC hash hit %26s (hit/search ", - format_number(stat_patc_hit).c_str()); + format_number(patc_hit).c_str()); if (stat_patc_search == 0) { screen.Puts(60, y, "---.-%)"); } else { screen.Print(60, y, "%5.1f%%)", - (double)stat_patc_hit / stat_patc_search * 100); + (double)patc_hit / stat_patc_search * 100); } y++; // ミスのうち1回目のハッシュ比較で外れが確定した率 @@ -297,19 +341,8 @@ m88200::MonitorUpdateATC(Monitor *, Text y++; screen.Print(0, y++, "PATC hash miss 2 %26s", format_number(stat_patc_miss2).c_str()); - screen.Print(0, y++, "PATC create %26s", - format_number(stat_patc_create).c_str()); - screen.Print(0, y++, "PATC invalidate %26s", - format_number(stat_patc_invalidate).c_str()); - screen.Print(0, y++, "PATC SCR InvAll %26s", - format_number(stat_patc_invcmd_all).c_str()); - screen.Print(0, y++, "PATC SCR InvSegment %26s", - format_number(stat_patc_invcmd_seg).c_str()); - screen.Print(0, y++, "PATC SCR InvPage %26s", - format_number(stat_patc_invcmd_page).c_str()); screen.Puts(0, y++, "Hash 0 1 2 3 4 5 6 >=7"); - // BATC ハッシュの衝突状況は BATC 更新時に計算してある // (BATC の更新頻度は低いため) screen.Puts(0, y++, "BATC HASH.S"); @@ -347,7 +380,7 @@ m88200::MonitorUpdateATC(Monitor *, Text void m88200::MonitorUpdateCache(Monitor *, TextScreen& screen) { - int setidx = screen.userdata; + uint setidx = screen.userdata; // 上半分(概要) MonitorCacheOverview(screen, 0, setidx, true); @@ -359,9 +392,9 @@ m88200::MonitorUpdateCache(Monitor *, Te // y は開始オフセット。 // TextScreen は (70, 5) 必要。 void -m88200::MonitorCacheSet(TextScreen& s, int y, int setidx) +m88200::MonitorCacheSet(TextScreen& s, int y, uint setidx) { - assertmsg(0 <= setidx && setidx < setarray.size(), "setidx=%d", setidx); + assertmsg(setidx < setarray.size(), "setidx=%u", setidx); const m88200CacheSet& set = setarray[setidx]; /* @@ -375,14 +408,14 @@ m88200::MonitorCacheSet(TextScreen& s, i // ラインの古い順に評価して順序を0-3でつける int Lorder[4]; - int tmpL = set.L; - for (int i = 0; i < 4; i++) { + uint tmpL = set.L; + for (uint i = 0; i < 4; i++) { int line = m88200CacheSet::TryGetOldestLine(tmpL); Lorder[line] = 3 - i; tmpL = m88200CacheSet::TryUseLine(tmpL, line); } - for (int line = 0; line < 4; line++, y++) { + for (uint line = 0; line < 4; line++, y++) { TA attr; // ステータスによって属性を選択 switch (set.vv[line]) { @@ -391,13 +424,13 @@ m88200::MonitorCacheSet(TextScreen& s, i break; case m88200CacheSet::Status::EU: case m88200CacheSet::Status::SU: - attr = TA::Off; + attr = TA::Normal; break; case m88200CacheSet::Status::EM: attr = TA::Em; break; } - s.Print(0, y, attr, "%d", line); + s.Print(0, y, attr, "%u", line); static const char statusstr[][4] = { "EU", "EM", "SU", "IV" }; s.Print(2, y, attr, "$%05x'%03x %s", (set.tag[line] >> 12), @@ -406,14 +439,14 @@ m88200::MonitorCacheSet(TextScreen& s, i if (set.vv[line] == m88200CacheSet::Status::EM) { // メモリに対してダーティならボールドにする - for (int w = 0; w < 4; w++) { + for (uint w = 0; w < 4; w++) { uint32 addr; uint32 m; - addr = (set.tag[line] & 0xfffff000) | (setidx << 4) | (w << 2); - m = (mainbus->Peek8(addr) << 24) - | (mainbus->Peek8(addr + 1) << 16) - | (mainbus->Peek8(addr + 2) << 8) - | (mainbus->Peek8(addr + 3)); + addr = (set.tag[line] & 0xfffff000U) | (setidx << 4) | (w << 2); + m = (mainbus->Peek1(addr) << 24) + | (mainbus->Peek1(addr + 1) << 16) + | (mainbus->Peek1(addr + 2) << 8) + | (mainbus->Peek1(addr + 3)); if (set.word[line * 4 + w] != m) { attr = TA::Em; } else { @@ -423,13 +456,13 @@ m88200::MonitorCacheSet(TextScreen& s, i } } else { // Unmodified (or Invalid) ならメモリとの比較は不要 - for (int w = 0; w < 4; w++) { + for (uint w = 0; w < 4; w++) { s.Print(20 + w * 9, y, "%08x", set.word[line * 4 + w]); } } // 順序 - s.Print(58, y, "%d", Lorder[line]); + s.Print(58, y, "%u", Lorder[line]); } } @@ -443,11 +476,11 @@ m88200::MonitorCacheSet(TextScreen& s, i // あるので 1セットごとに間を空けて等間隔にしたい、という違いから。 // TextScreen は CLI なら (70, 17)、GUI なら (82, 17) 必要。 void -m88200::MonitorCacheOverview(TextScreen& s, int y, int cursor, bool is_gui) +m88200::MonitorCacheOverview(TextScreen& s, int y, uint cursor, bool is_gui) { // X ガイド - for (int i = 0; i < 16; i++) { - int x; + for (uint i = 0; i < 16; i++) { + uint x; if (is_gui) { x = 3 + i * 5; } else { @@ -458,16 +491,16 @@ m88200::MonitorCacheOverview(TextScreen& y++; // Y ガイド - for (int i = 0; i < 16; i++) { + for (uint i = 0; i < 16; i++) { s.Print(0, y + i, "%02x", i * 16); } - for (int i = 0; i < setarray.size(); i++) { + for (uint i = 0; i < setarray.size(); i++) { const auto& set = setarray[i]; const char str[] = "EMS-"; - int col = i % 16; - int row = i / 16; - int x; + uint col = i % 16; + uint row = i / 16; + uint x; if (is_gui) { x = 3 + col * 5; } else { @@ -498,55 +531,55 @@ m88200::commandname[] = { "No Operation", // $11 "No Operation", // $12 "No Operation", // $13 - "DCache Invalidate, Line", // $14 - "DCache Invalidate, Page", // $15 - "DCache Invalidate, Segment", // $16 - "DCache Invalidate, All", // $17 - - "DCache Copyback, Line", // $18 - "DCache Copyback, Page", // $19 - "DCache Copyback, Segment", // $1a - "DCache Copyback, All", // $1b - "DCache Copy&Inv, Line", // $1c - "DCache Copy&Inv, Page", // $1d - "DCache Copy&Inv, Segment", // $1e - "DCache Copy&Inv, All", // $1f - - "Probe User Address", // $20 - "Probe User Address", // $21 - "Probe User Address", // $22 - "Probe User Address", // $23 - "Probe Supervisor Address", // $24 - "Probe Supervisor Address", // $25 - "Probe Supervisor Address", // $26 - "Probe Supervisor Address", // $27 - - "Probe User Address", // $28 - "Probe User Address", // $29 - "Probe User Address", // $2a - "Probe User Address", // $2b - "Probe Supervisor Address", // $2c - "Probe Supervisor Address", // $2d - "Probe Supervisor Address", // $2e - "Probe Supervisor Address", // $2f - - "Invalidate U PATC, Line", // $30 - "Invalidate U PATC, Page", // $31 - "Invalidate U PATC, Segment", // $32 - "Invalidate U PATC, All", // $33 - "Invalidate S PATC, Line", // $34 - "Invalidate S PATC, Page", // $35 - "Invalidate S PATC, Segment", // $36 - "Invalidate S PATC, All", // $37 - - "Invalidate U PATC, Line", // $38 - "Invalidate U PATC, Page", // $39 - "Invalidate U PATC, Segment", // $3a - "Invalidate U PATC, All", // $3b - "Invalidate S PATC, Line", // $3c - "Invalidate S PATC, Page", // $3d - "Invalidate S PATC, Segment", // $3e - "Invalidate S PATC, All", // $3f + "Inv DCache Line", // $14 + "Inv DCache Page", // $15 + "Inv DCache Seg", // $16 + "Inv DCache All", // $17 + + "Copyback DCache Line", // $18 + "Copyback DCache Page", // $19 + "Copyback DCache Seg", // $1a + "Copyback DCache All", // $1b + "Copy&Inv DCache Line", // $1c + "Copy&Inv DCache Page", // $1d + "Copy&Inv DCache Seg", // $1e + "Copy&Inv DCache All", // $1f + + "Probe.U", // $20 + "Probe.U", // $21 + "Probe.U", // $22 + "Probe.U", // $23 + "Probe.S", // $24 + "Probe.S", // $25 + "Probe.S", // $26 + "Probe.S", // $27 + + "Probe.U", // $28 + "Probe.U", // $29 + "Probe.U", // $2a + "Probe.U", // $2b + "Probe.S", // $2c + "Probe.S", // $2d + "Probe.S", // $2e + "Probe.S", // $2f + + "InvPATC.U Line", // $30 + "InvPATC.U Page", // $31 + "InvPATC.U Seg", // $32 + "InvPATC.U All", // $33 + "InvPATC.S Line", // $34 + "InvPATC.S Page", // $35 + "InvPATC.S Seg", // $36 + "InvPATC.S All", // $37 + + "InvPATC.U Line", // $38 + "InvPATC.U Page", // $39 + "InvPATC.U Seg", // $3a + "InvPATC.U All", // $3b + "InvPATC.S Line", // $3c + "InvPATC.S Page", // $3d + "InvPATC.S Seg", // $3e + "InvPATC.S All", // $3f }; // SCR の読み出し @@ -575,20 +608,32 @@ m88200::SetSCR(uint32 data) // %11'X0gg Invalidate User PATC Descriptors // %11'X1gg Invalidate Supervisor PATC Descriptors - const char *name; - if (command < 0x10) { - name = commandname[0]; - } else { - name = commandname[command - 0x10]; + if (__predict_false(loglevel >= 1)) { + char sarbuf[16]; + const char *name; + if (command < 0x10) { + name = commandname[0]; + } else { + name = commandname[command - 0x10]; + } + + // ログレベル1ならまとめて表示。 + if (loglevel == 1) { + snprintf(sarbuf, sizeof(sarbuf), " SAR=$%08x", GetSAR()); + } else { + sarbuf[0] = '\0'; + } + + putlogn("SCR <- $%0*x (%s)%s", + (data > 0x3f ? 8 : 2), data, name, sarbuf); } - putlog(1, "SCR <- $%08x (%s)", data, name); switch (command) { case 0x00 ... 0x13: // No Operation return; case 0x14 ... 0x1f: // Flush Data Cache - FlushCache(); + FlushCacheCmd(); return; case 0x20 ... 0x23: // Probe User Address @@ -602,7 +647,7 @@ m88200::SetSCR(uint32 data) return; case 0x30 ... 0x3f: // Invalidate {User,Supervisor} PATC Descriptors - InvalidatePATC(); + InvalidatePATCCmd(); return; } PANIC("should not reach: command=$%02x", command); @@ -612,11 +657,11 @@ m88200::SetSCR(uint32 data) // command が $14..$1f でのみ呼ぶこと。 // p3-18, Section 3.7 void -m88200::FlushCache() +m88200::FlushCacheCmd() { uint32 op = command & 0x3c; uint32 gg = command & 0x03; - uint32 addr = GetSAR(); + uint32 addr; bool copyback; bool invalidate; @@ -628,62 +673,42 @@ m88200::FlushCache() // %01'11gg true true | Data Cache Copyback and invalidate // op によって書き戻しと無効化の組み合わせが異なる - if (op == 0x14) { - copyback = false; - invalidate = true; - } else if (op == 0x18) { - copyback = true; - invalidate = false; - } else { - copyback = true; - invalidate = true; - } + invalidate = (bool)(op & 0x04); + copyback = (bool)(op & 0x08); // 影響範囲 + addr = GetSAR(); switch (gg) { case GG_LINE: - addr &= 0xfffffff0; - parent->AddCycle(1); // Table.6-1 + addr &= 0xfffffff0U; break; case GG_PAGE: - addr &= 0xfffff000; - parent->AddCycle(256); // Table.6-1 + addr &= 0xfffff000U; break; case GG_SEG: - addr &= 0xffc00000; - parent->AddCycle(1024); // Table.6-1 + addr &= 0xffc00000U; break; case GG_ALL: - parent->AddCycle(1024); // Table.6-1 break; default: __unreachable(); } - // サイクル(基本部分) Table.6-1 + // サイクル数のうち固定費部分 (Table.6-1)。 + // Copyback&Invalidate の場合どうなるのか不明だが、 + // とりあえず悪い方に見積もって単純加算しておくか。 uint32 cycle = 0; if (invalidate) { - switch (gg) { - case GG_LINE: cycle = 1; break; - case GG_PAGE: cycle = 256; break; - case GG_SEG: cycle = 1024; break; - case GG_ALL: cycle = 256; break; - default: __unreachable(); - } + static uint32 invalidate_cycles[4] = { 1, 256, 1024, 256 }; + cycle += invalidate_cycles[gg]; } if (copyback) { - switch (gg) { - case GG_LINE: cycle = 1; break; - case GG_PAGE: cycle = 256; break; - case GG_SEG: cycle = 1024; break; - case GG_ALL: cycle = 1024; break; - default: __unreachable(); - } + static uint32 copyback_cycles[4] = { 1, 256, 1024, 1024 }; + cycle += copyback_cycles[gg]; } - // XXX ループ中のサイクル数は正しくないかも。よく分からん for (auto& set : setarray) { - for (int line = 0; line < 4; line++) { + for (uint line = 0; line < 4; line++) { // 条件にマッチするか bool match; switch (gg) { @@ -694,7 +719,7 @@ m88200::FlushCache() match = (addr == set.tag[line]); break; case GG_SEG: - match = (addr == (set.tag[line] & 0xffc00001)); + match = (addr == (set.tag[line] & 0xffc00001U)); break; case GG_ALL: match = true; @@ -707,15 +732,17 @@ m88200::FlushCache() // Copyback ならまず EM なエントリを書き戻す。 if (copyback) { + // 本文には見当たらないが Fig.3-10、Fig.3-11 の状態遷移図を + // 見ると、Unmodified なら変化なし、EM なら EU に移行するはず。 if (set.vv[line] == m88200CacheSet::EM) { cycle += 7; // Table.6-1 CopyBackLine(set, line); + set.Update(line, m88200CacheSet::EU); } } // Invalidate なら無効化する if (invalidate) { - cycle += 1; // Table.6-1 set.Update(line, m88200CacheSet::IV); } } @@ -728,7 +755,7 @@ m88200::FlushCache() // command が $30..$3f で呼ぶこと。 // p2-9, Section 2.2.4 void -m88200::InvalidatePATC() +m88200::InvalidatePATCCmd() { uint32 gg = command & 0x03; bool s = (command & 0x04); @@ -738,13 +765,11 @@ m88200::InvalidatePATC() if (__predict_true(gg == GG_PAGE)) { // 指定の1本だけ無効にする。 // Page は呼び出し回数が多いのと1本だけならハッシュで引けるので別対応。 -#if defined(M88200_STAT) - stat_patc_invcmd_page++; -#endif + STAT(stat_patc_invcmd_page); // ゲスト側が指定した端数部分は有効部分でマスクする。 // 内部では下位ビットはフラグ扱いなのでマスクしないと誤動作する。 - addr = GetSAR() & 0xfffff000; + addr = GetSAR() & 0xfffff000U; addr |= (s) ? PATC_S : 0; auto& hash = (s) ? patc_hash_s : patc_hash_u; @@ -754,12 +779,11 @@ m88200::InvalidatePATC() int i = __builtin_ctzll(pi); m88200PATC& p = patc[i]; if (p.lpa == addr) { - InvalidatePATCHash(i); p.lpa |= PATC_INVALID; -#if defined(M88200_STAT) - stat_patc_invalidate++; -#endif - // 1本ヒットすればこれ以上一致することはないはず + InvalidatePATCHash(i); + patc_free |= 1ULL << i; + STAT(stat_patc_invalidate); + // 1本ヒットすればこれ以上一致することはないはず。 break; } } @@ -768,16 +792,12 @@ m88200::InvalidatePATC() if (gg == GG_ALL) { // S/U 指定したほう全体を無効にする -#if defined(M88200_STAT) - stat_patc_invcmd_all++; -#endif + STAT(stat_patc_invcmd_all); mask = 0; } else if (gg == GG_SEG) { // S/U 指定したほうの指定セグメント範囲全部を無効にする -#if defined(M88200_STAT) - stat_patc_invcmd_seg++; -#endif - mask = 0xffc00000; + STAT(stat_patc_invcmd_seg); + mask = 0xffc00000U; } else { // GG_LINE はマニュアルにどうなるか書いてない putlog(0, "Undefined Invalidate PATC Line"); @@ -789,16 +809,15 @@ m88200::InvalidatePATC() mask |= PATC_S | PATC_INVALID; - for (int i = 0; i < patc.size(); i++) { + for (uint i = 0; i < patc.size(); i++) { m88200PATC& p = patc[i]; if ((p.lpa & mask) == addr) { // 無効化 // XXX 削除した結果穴が空いても詰める処理は未実装 - InvalidatePATCHash(i); p.lpa |= PATC_INVALID; -#if defined(M88200_STAT) - stat_patc_invalidate++; -#endif + InvalidatePATCHash(i); + patc_free |= 1ULL << i; + STAT(stat_patc_invalidate); } } } @@ -813,15 +832,15 @@ m88200::SetSCTR(uint32 data) // 全 CMMU について、それぞれスヌープ相手になる CMMU リストを更新。 // どの CMMU の SCTR への書き込みでも毎回全ての CMMU を書き換える。 std::array cmmu {}; - int n = cmmu.size(); - for (int i = 0; i < n; i++) { + uint n = cmmu.size(); + for (uint i = 0; i < n; i++) { cmmu[i] = gMainApp.FindObject(OBJ_M88200(i)); } - for (int i = 0; i < n; i++) { + for (uint i = 0; i < n; i++) { if (cmmu[i] == NULL) continue; cmmu[i]->other_cmmu.clear(); - for (int j = 0; j < n; j++) { + for (uint j = 0; j < n; j++) { if (cmmu[j] == NULL || i == j) continue; if ((cmmu[j]->sctr & SCTR_SE)) { @@ -887,13 +906,34 @@ m88200::SetAPR(m88200APR& xapr, uint32 d } // BWP(BATC Write Port) #n への書き込み -// (暗黙 BATC にも書き込めるよう n = 8,9 も受け付ける) void -m88200::SetBWP(uint n, uint32 data) +m88200::SetBWP(uint bn, uint32 data) +{ + assert(bn < 8); + putlog(1, "BWP%u <- $%08x", bn, data); + + uint32 laddr = data & BWP_LBA_MASK; + uint32 paddr = (data & BWP_PBA_MASK) << 13; + uint32 flags = data & BWP_FLAG_MASK; + + // BATC の LBA は衝突してはいけない (が、どうなるかは書いてない)。 + // XXX 要実機検証… + + // とりあえず暗黙 BATC (#8, #9) と衝突する設定は無視しておく。 + if (laddr >= 0xfff00000U) { + return; + } + + SetBATC(bn, laddr, paddr, flags); +} + +// BATC #n を更新する。 +// n は 0-9 (暗黙 BATC も含む)。 +// laddr の衝突は呼び出し側で回避してある。 +void +m88200::SetBATC(uint n, uint32 laddr, uint32 paddr, uint32 flags) { - assert(n < 10); m88200BATC& b = batc[n]; - putlog(1, "BWP%u <- $%08x", n, data); // BWP レジスタへの書き込み値と内部データ構造(stat)ではビット位置が違う // ことに注意。 @@ -909,19 +949,19 @@ m88200::SetBWP(uint n, uint32 data) // +----..-----+----+----+----+----+----+----+----+----+----+----+ memset(&b, 0, sizeof(b)); - b.lba = data & BWP_LBA_MASK; - b.lba |= (data & BWP_S); - if ((data & BWP_V) == 0) { - b.lba |= BATC_INVALID; + b.lba = laddr; + b.lba |= (flags & BWP_S); + if ((flags & BWP_V) == 0) { + b.lba |= BATC_INVALID; } - b.pba = (data & BWP_PBA_MASK) << 13; - if ((data & BWP_WT)) + b.pba = paddr; + if ((flags & BWP_WT)) b.stat |= DESC_WT; - if ((data & BWP_G)) + if ((flags & BWP_G)) b.stat |= DESC_G; - if ((data & BWP_CI)) + if ((flags & BWP_CI)) b.stat |= DESC_CI; - if ((data & BWP_WP)) { + if ((flags & BWP_WP)) { b.stat |= DESC_WP; b.wp = true; } @@ -936,14 +976,14 @@ m88200::MakeBATCHash() std::fill(batc_hash_s.begin(), batc_hash_s.end(), 0); std::fill(batc_hash_u.begin(), batc_hash_u.end(), 0); - for (int i = 0; i < batc.size(); i++) { + for (uint i = 0; i < batc.size(); i++) { m88200BATC& b = batc[i]; if (b.IsValid()) { - // LBA の下位 8 ビットだけでハッシュを作る + // LBA の下位 8 ビットだけでハッシュを作る。 uint32 hashkey = batc_hash_func(b.lba); - // S によって該当するハッシュの該当するビットを立てておく + // S によって該当するハッシュの該当するビットを立てておく。 if (b.IsS()) { batc_hash_s[hashkey] |= 1U << i; } else { @@ -1017,13 +1057,14 @@ m88200::SetCSSP(uint32 data) void m88200::SetSuper(bool super) { - acc_super = super; - - if (acc_super) { + // acc_super は S/U ビットだけだと知っているので OR ではなく代入。 + if (super) { + acc_super = BusAddr::S; acc_apr = &sapr; batc_hash = &batc_hash_s[0]; patc_hash = &patc_hash_s[0]; } else { + acc_super = BusAddr::U; acc_apr = &uapr; batc_hash = &batc_hash_u[0]; patc_hash = &patc_hash_u[0]; @@ -1036,239 +1077,187 @@ m88200::SetSuper(bool super) // // MBus から読み込みを行う。 -// paddr は size によって 1, 2, 4 バイト境界に整列していること。 -// 成功すれば読み込めた値を返す。 -// バスエラーが起きれば fault_code、fault_addr をセットして (uint64)-1 を返す。 -uint64 -m88200::MBusRead(uint32 paddr, int size) -{ - uint64 data; +// paddr はアドレス、サイズ、S/U ビットのみを参照する。 +// paddr のアドレスはそのサイズによって 1, 2, 4 バイト境界に整列していること。 +// バスエラーなら SetFault() して BusData::BusErr を返す。 +busdata +m88200::MBusRead(busaddr paddr) +{ + busdata bd; + + // m88k システムに接続しているデバイスはすべて、m68030 システムでいう + // ところのロングワードポートデバイスなので応答は常に 4 バイト分ある。 + bd = mainbus->Read(paddr); + parent->AddWait(bd.GetWait()); + if (__predict_false(bd.IsBusErr())) { + SetFault(FAULT_CODE_BUSERR, paddr.Addr()); + return bd; + } + uint size = paddr.GetSize(); if (size == 4) { - data = mainbus->Read32(paddr); } else if (size == 2) { - data = mainbus->Read16(paddr); + if ((paddr.Addr() & 2) == 0) { + bd >>= 16; + } else { + bd &= 0xffff; + } } else { - data = mainbus->Read8(paddr); + bd >>= (3 - (paddr.Addr() & 3)) * 8; + bd &= 0xff; } - if (__predict_false((int64)data < 0)) { - fault_code = FAULT_CODE_BUSERR; - fault_addr = paddr; - } - return data; + + return bd; } // MBus に書き込みを行う。 -// paddr は size によって 1, 2, 4 バイト境界に整列していること。 -// 成功すれば 0 を返す。 -// バスエラーが起きれば fault_code、fault_addr をセットして (uint64)-1 を返す。 -uint64 -m88200::MBusWrite(uint32 paddr, uint32 data, int size) -{ - uint64 rv; - - if (size == 4) { - rv = mainbus->Write32(paddr, data); - } else if (size == 2) { - rv = mainbus->Write16(paddr, data); - } else { - rv = mainbus->Write8(paddr, data); +// paddr はアドレス、サイズ、S/U ビットのみを参照する。 +// paddr のアドレスはそのサイズによって 1, 2, 4 バイト境界に整列していること。 +// バスエラーなら SetFault() して BusData::BusErr を返す。 +busdata +m88200::MBusWrite(busaddr paddr, uint32 data) +{ + busdata bd; + + bd = mainbus->Write(paddr, data); + parent->AddWait(bd.GetWait()); + if (__predict_false(bd.IsBusErr())) { + SetFault(FAULT_CODE_BUSERR, paddr.Addr()); } - if (__predict_false((int64)rv < 0)) { - fault_code = FAULT_CODE_BUSERR; - fault_addr = paddr; - } - return rv; + return bd; } // MBus に xmem トランザクションを行う。 -// paddr は size によって 1, 4 バイト境界に整列していること (2バイトはない)。 +// paddr はアドレス、サイズ、S/U ビットのみを参照する。 +// paddr のアドレスはそのサイズによって 1, 4 バイト境界に整列していること +// (2バイトはない)。 // MBus Acquire された状態で呼び出すこと。 // 成功すれば読み込めた値を返す。 -// バスエラーが起きれば fault_code、fault_addr をセットして (uint64)-1 を返す。 +// バスエラーなら SetFault() して BusData::BusErr を返す。 // read/write どちらでエラーが起きたかは acc_read で判別できる。 // p3-13, Figure3-7 の下半分のメインラインあたりに該当。 -uint64 -m88200::MBusXmem(uint32 paddr, uint32 data, int size) +busdata +m88200::MBusXmem(busaddr paddr, uint32 data) { - uint64 fetched; - uint64 rv; + busdata fetched; + busdata bd; // Read data with intent to modify acc_read = true; - rv = MBusRead(paddr, size); - if (__predict_false((int64)rv < 0)) { - goto error; + bd = MBusRead(paddr); + parent->AddWait(bd.GetWait()); + if (__predict_false(bd.IsBusErr())) { + return bd; } - fetched = (uint32)rv; + fetched = bd; // Supply data to PBus // Reply = Success // Write data to memory acc_read = false; - rv = MBusWrite(paddr, data, size); - if (__predict_false((int64)rv < 0)) { - goto error; + bd = MBusWrite(paddr, data); + parent->AddWait(bd.GetWait()); + if (__predict_false(bd.IsBusErr())) { + return bd; } - rv = fetched; - error: - return rv; + return fetched; } // // アクセス関数 // -template uint64 +template busdata m88200::load(uint32 addr) { - uint64 data; - acc_laddr = addr; acc_read = true; if (__predict_false(Translate() == false)) { putlog(2, "Translation BusError %c:$%08x", - (acc_super ? 'S' : 'U'), acc_laddr); - return (uint64)-1; + (IsSuper() ? 'S' : 'U'), acc_laddr); + return BusData::BusErr; } - if (__predict_false((acc_stat & DESC_CI))) { - parent->AddCycle(7); // Table.6-2 - MBusAcquire(); - MBusMakeSnoop(acc_paddrH, 0); - data = MBusRead(acc_paddrH + acc_paddrL, bits / 8); - MBusRelease(); - if (__predict_false((int64)data < 0)) { - putlog(2, "MBus BusError %c:$%08x", - (acc_super ? 'S' : 'U'), acc_laddr); - } - return data; - } else { - data = CacheRead(acc_paddrH); - if (__predict_false((int64)data < 0)) { - putlog(2, "Cache BusError %c:$%08x", - (acc_super ? 'S' : 'U'), acc_laddr); - return data; - } - if (bits == 8) { - switch (acc_paddrL) { - case 0: - return (data >> 24); - case 1: - return (data >> 16) & 0xff; - case 2: - return (data >> 8) & 0xff; - case 3: - return data & 0xff; - default: - PANIC("corrupted acc_paddrL=%d", acc_paddrL); - } - } else if (bits == 16) { - if (acc_paddrL == 0) { - return data >> 16; - } else { - return data & 0xffff; - } - } else if (bits == 32) { - return data; - } else { - PANIC("bits must be 8, 16, 32"); - } - } + busaddr baddr = busaddr(acc_paddr) | acc_super | busaddr::Size(size); + busdata bd = PhysRead(baddr, acc_stat); + return bd; } -uint64 -m88200::load_8(uint32 addr) +busdata +m88200::load_1(uint32 addr) { - return load<8>(addr); + return load<1>(addr); } -uint64 -m88200::load_16(uint32 addr) +busdata +m88200::load_2(uint32 addr) { - return load<16>(addr); + return load<2>(addr); } -uint64 -m88200::load_32(uint32 addr) +busdata +m88200::load_4(uint32 addr) { - return load<32>(addr); + return load<4>(addr); } -template uint64 +template busdata m88200::store(uint32 addr, uint32 data) { - uint64 rv; - acc_laddr = addr; acc_read = false; if (__predict_false(Translate() == false)) { - return (uint64)-1; - } - if (__predict_false((acc_stat & DESC_CI))) { - parent->AddCycle(7); // Table.6-2 - MBusAcquire(); - MBusMakeSnoop(acc_paddrH, 1); - rv = MBusWrite(acc_paddrH + acc_paddrL, data, bits / 8); - MBusRelease(); - return rv; - } else { - return CacheWrite(acc_paddrH + acc_paddrL, data, bits / 8); + return BusData::BusErr; } + + busaddr baddr = busaddr(acc_paddr) | acc_super | busaddr::Size(size); + busdata r = PhysWrite(baddr, data, acc_stat); + return r; } -uint64 -m88200::store_8(uint32 addr, uint32 data) +busdata +m88200::store_1(uint32 addr, uint32 data) { - return store<8>(addr, data); + return store<1>(addr, data); } -uint64 -m88200::store_16(uint32 addr, uint32 data) +busdata +m88200::store_2(uint32 addr, uint32 data) { - return store<16>(addr, data); + return store<2>(addr, data); } -uint64 -m88200::store_32(uint32 addr, uint32 data) +busdata +m88200::store_4(uint32 addr, uint32 data) { - return store<32>(addr, data); + return store<4>(addr, data); } -template uint64 +template busdata m88200::xmem(uint32 addr, uint32 data) { - uint64 rv; - acc_laddr = addr; acc_read = false; if (__predict_false(Translate() == false)) { - return (uint64)-1; - } - if (__predict_false((acc_stat & DESC_CI))) { - // XXX キャッシュ禁止領域だとどうなる? - MBusAcquire(); - MBusMakeSnoop(acc_paddrH, 1); - rv = MBusXmem(acc_paddrH + acc_paddrL, data, bits / 8); - MBusRelease(); - return rv; - } else { - return CacheXmem(acc_paddrH + acc_paddrL, data, bits / 8); + return BusData::BusErr; } + + busdata bd = CacheXmem(acc_paddr, data, size); + return bd; } -uint64 -m88200::xmem_8(uint32 addr, uint32 data) +busdata +m88200::xmem_1(uint32 addr, uint32 data) { - return xmem<8>(addr, data); + return xmem<1>(addr, data); } -uint64 -m88200::xmem_32(uint32 addr, uint32 data) +busdata +m88200::xmem_4(uint32 addr, uint32 data) { - return xmem<32>(addr, data); + return xmem<4>(addr, data); } // @@ -1302,27 +1291,33 @@ m88200::stat2str(uint32 stat) // アドレス変換。 // 事前に acc_laddr, acc_super, acc_read を設定しておくこと。 -// 成功すれば acc_paddrH, acc_paddrL をセットして true を返す。 -// 失敗ならいろいろセットして(?) false を返す。 +// 成功すれば acc_paddr, acc_stat をセットして true を返す。 +// 失敗なら SetFault() して false を返す。 // p2-3, Figure2-1 bool m88200::Translate() { uint32 la; + uint8 n; + uint32 bi; // Logical address(LA) presented on PBus - putlog(3, "Translate %c:$%08x", (acc_super ? 'S' : 'U'), acc_laddr); - - // 下位2ビットは常にこうなる。 - acc_paddrL = acc_laddr & 3; + putlog(3, "Translate %c:$%08x", (IsSuper() ? 'S' : 'U'), acc_laddr); // Select Area Descriptor if (SelectAreaDesc() == false) { + // 変換しない場合でも暗黙 BWP は有効…。 + // マッチまではここで独自に行い、マッチしたら下の BWP へ合流。 + if (__predict_false(acc_laddr >= 0xfff00000U) && IsSuper()) { + n = (acc_laddr < 0xfff80000U) ? 8 : 9; + goto batc; + } + // XXX 図のこれはたぶん間違いだよなあ… // 誤: Physical Address <= LA[18-2] :: 00 // 正: Physical Address <= LA[31-2] :: 00 - acc_paddrH = acc_laddr - acc_paddrL; + acc_paddr = acc_laddr; parent->AddCycle(1); return true; @@ -1335,60 +1330,59 @@ m88200::Translate() // Search BATC first - // まずざっくりハッシュを引く - uint32 bi = batc_hash[batc_hash_func(acc_laddr)]; + // まずざっくりハッシュを引く。 + bi = batc_hash[batc_hash_func(acc_laddr)]; if (__predict_false(bi != 0)) { // ハッシュで一部がヒットしたので、 - // 次に LBA + S 全体との完全一致で探す - la = acc_laddr & 0xfff80000; - la |= acc_super ? BATC_S : 0; + // 次に LBA + S 全体との完全一致で探す。 + la = acc_laddr & 0xfff80000U; + la |= IsSuper() ? BATC_S : 0; do { - int i = __builtin_ctz(bi); - if (batc[i].lba == la) { - m88200BATC& b = batc[i]; - putlog(4, " BATC[%d] hit", i); - batc_hit[i]++; + n = __builtin_ctz(bi); + if (batc[n].lba == la) { + // 無変換時に暗黙 BATC にヒットしたのはカウントしない。 + // ここは変換の何割が BATC にヒットしたかを知りたいので。 + batc_hit[n]++; + batc: + m88200BATC& b = batc[n]; + putlog(4, " BATC[%d] hit", n); if (b.wp && acc_IsWrite()) { goto write_violation; } // Physical Address <= PBA :: LA[18-2] :: 00 - acc_paddrH = b.pba | (acc_laddr & 0x0007fffc); + acc_paddr = b.pba | (acc_laddr & 0x0007ffffU); acc_stat |= b.stat; putlog(4, " BATC hit acc=%s paddr=$%08x", - stat2str(acc_stat).c_str(), acc_paddrH); + stat2str(acc_stat).c_str(), acc_paddr); parent->AddCycle(1); return true; } - // ヒットしたビットを 0 にする + // ヒットしたビットを 0 にする。 bi &= bi - 1; } while (bi); } - // 見付からなかったので PATC へ + // 見付からなかったので PATC へ。 - // 2回しかループしないはず - for (int loop = 0; loop < 3; loop++) { + // 2回しかループしないはず。 + for (uint loop = 0; loop < 3; loop++) { // BATC miss, search PATC then acc_patc = NULL; uint64 pi = patc_hash[patc_hash_func(acc_laddr)]; -#if defined(M88200_STAT) - stat_patc_search++; -#endif + STAT(stat_patc_search); if (pi == 0) { -#if defined(M88200_STAT) - // 単純なサーチミスのみカウントし、M bit による合流は除きたい - stat_patc_miss1++; -#endif + // 単純なサーチミスのみカウントし、M bit による合流は除きたい。 + STAT(stat_patc_miss1); goto patc_miss; } - la = acc_laddr & 0xfffff000; - la |= acc_super ? PATC_S : 0; + la = acc_laddr & 0xfffff000U; + la |= IsSuper() ? PATC_S : 0; do { int i = __builtin_ctzll(pi); @@ -1396,9 +1390,6 @@ m88200::Translate() if (p.lpa == la) { // PATC hit patc_hit++; -#if defined(M88200_STAT) - stat_patc_hit++; -#endif if (acc_IsWrite() && p.wp) { putlog(4, " PATC[%d] hit; stat=%s m=%d wp=%d", i, stat2str(p.stat).c_str(), p.m, p.wp); @@ -1418,33 +1409,32 @@ m88200::Translate() putlog(4, " PATC[%d] hit; Need to update M bit", i); p.m = true; - // この下の PATC miss に合流する + // この下の PATC miss に合流する。 acc_patc = &p; goto patc_miss; } // Physical Address <= PFA :: LA[11-2] :: 00 - acc_paddrH = p.pfa | (acc_laddr & 0x00000ffc); + acc_paddr = p.pfa | (acc_laddr & 0x00000fffU); acc_stat |= p.stat; putlog(4, " PATC[%d] hit acc=%s paddr=$%08x", i, - stat2str(acc_stat).c_str(), acc_paddrH); + stat2str(acc_stat).c_str(), acc_paddr); parent->AddCycle(1); return true; } - // ヒットしたビットを 0 にする + // ヒットしたビットを 0 にする。 pi &= pi - 1; } while (pi); -#if defined(M88200_STAT) - // 単純なサーチミスのみカウントし、M bit による合流は除きたい - stat_patc_miss2++; -#endif + + // 単純なサーチミスのみカウントし、M bit による合流は除きたい。 + STAT(stat_patc_miss2); patc_miss: putlog(4, " BATC/PATC miss"); // PATC miss - // PATC にヒットして M bit 更新するものは両方カウントされるけど + // PATC にヒットして M bit 更新するものは両方カウントされるけど。 atc_miss++; // フローチャートでは PATC miss というラベルが付いてるが、コード @@ -1454,36 +1444,38 @@ m88200::Translate() // 動作は同じはず。 // Table search operation - if (TableSearch() == true) { - // フローチャートでは Select Area Descriptor まで戻るように - // 書いてあるが、PATC ミスによるテーブルサーチでは Area Descriptor - // と BATC は変化せず PATC が変化するだけなので、PATC サーチから - // やり直す。 - continue; + if (TableSearch() == false) { + goto invalid; } - // Invalid - // Reply with fault (Figure2-10 だがここではすべて不要) - return false; + // フローチャートでは Select Area Descriptor まで戻るように + // 書いてあるが、PATC ミスによるテーブルサーチでは Area Descriptor + // と BATC は変化せず PATC が変化するだけなので、PATC サーチから + // やり直す。 + continue; } PANIC("loop detected"); write_violation: - fault_code = FAULT_CODE_WRITE; - //fault_addr UNKNOWN? + // この場合アドレス (PFAR) は破棄 (Table 2-2)。 + SetFault(FAULT_CODE_WRITE, 0xccccccccU); + invalid: // Reply with fault (Figure2-10 だがここではすべて不要) return false; } - // Select Area Descriptor. -// 結果が Type=Valid なら true、Type=Untranslated なら false を返す。 -// Probe Command からも呼ばれる。 +// 結果が Type=Valid なら acc_sdaddr にセグメントアドレス、acc_stat に +// WT, G, C ビットを代入し true を返す。 +// Type=Untranslated なら acc_stat のみ代入し false を返す。 +// Probe Command からも呼ばれる (はずだが未実装)。 // p2-21, Figure2-5 bool m88200::SelectAreaDesc() { - acc_stat = acc_apr->stat & ACC_STAT_MASK; + // TE=0 の場合でも有効。 + acc_stat = acc_apr->stat; + if (acc_apr->enable) { // セグメントディスクリプタのアドレスはここで決まるが、 // 次に行う BATC、PATC サーチでは使わず、それらが全部ミスして @@ -1499,11 +1491,14 @@ m88200::SelectAreaDesc() } // Table Search -// Type=Valid なら true、Type=Invalid なら false を返す。Type=Retry はない。 +// Type=Valid なら true、Type=Invalid なら SetFault() して false を返す。 +// Type=Retry はない。 // p2-20, Figure2-4 bool m88200::TableSearch() { + bool rv = false; + putlog(4, " %s $%08x/%s acc=%s", __func__, acc_laddr, (acc_read ? "Read" : "Write"), stat2str(acc_stat).c_str()); @@ -1515,17 +1510,15 @@ m88200::TableSearch() MBusAcquire(); if (FetchSegmentDesc() == false) { - MBusRelease(); - + // SetFault() 済み。 parent->AddCycle(7); // Table.6-2 - return false; + goto done; } if (FetchPageDesc() == false) { - MBusRelease(); - + // SetFault() 済み。 parent->AddCycle(11); // Table.6-2 - return false; + goto done; } // 中央の TYPE = VALID のところ @@ -1533,7 +1526,7 @@ m88200::TableSearch() // +----------+ +----------+ // | | // DESC[U]=0 || Otherwise - // DESC[M]=0 && WRITE | + // (DESC[M]=0 && WRITE) | // | | // UpdatePageDesc | // | | @@ -1557,8 +1550,8 @@ m88200::TableSearch() parent->AddCycle(15); // Table.6-2 if (UpdatePageDesc() == false) { - MBusRelease(); - return false; + // SetFault() 済み。 + goto done; } } else { parent->AddCycle(11); // Table.6-2 @@ -1569,48 +1562,54 @@ m88200::TableSearch() CreatePATCEntry(); } + rv = true; + done: MBusRelease(); - return true; + return rv; } // Fetch Segment Descriptor -// Type=Valid なら true、Type=Invalid なら false を返す。Type=Retry はない。 +// Type=Valid なら true、Type=Invalid なら SetFault() して false を返す。 +// Type=Retry はない。 // 公式フローチャートと違って MBus Acquire された状態で呼び出すこと。 // p2-22, Figure2-6 bool m88200::FetchSegmentDesc() { - uint64 data; + busdata data; - MBusMakeSnoop(acc_sdaddr, 0); - data = MBusRead(acc_sdaddr, 4); - if (__predict_false((int64)data < 0)) { + MBusMakeSnoop(acc_sdaddr, IM_0); + busaddr baddr = busaddr(acc_sdaddr) | BusAddr::S | BusAddr::Size4; + // ディスクリプタの読み込みは自動的に CI (p3-18 3.6)。 + data = PhysRead(baddr, DESC_CI); + if (__predict_false(data.IsBusErr())) { + // SetFault() 済み。 return false; } - tmp_desc = (uint32)data; - tmp_desc &= (0xfffff000 | + tmp_desc = data.Data(); + tmp_desc &= (0xfffff000U | DESC_WT | DESC_SP | DESC_G | DESC_CI | DESC_WP | DESC_V); if (__predict_false((tmp_desc & DESC_V) == 0)) { - fault_code = FAULT_CODE_SEGMENT; - fault_addr = acc_sdaddr; + SetFault(FAULT_CODE_SEGMENT, acc_sdaddr); putlog(4, " SD $%08x desc=$%08x SegFault", acc_sdaddr, tmp_desc); return false; } else { // Descriptor is valid - if (__predict_false((tmp_desc & DESC_SP) && acc_super == 0)) { - fault_code = FAULT_CODE_SUPERVISOR; - fault_addr = acc_sdaddr; + if (__predict_false((tmp_desc & DESC_SP) && IsUser())) { + SetFault(FAULT_CODE_SUPERVISOR, acc_sdaddr); putlog(4, " SD $%08x desc=$%08x SupervisorFault", acc_sdaddr, tmp_desc); return false; } - acc_pdaddr = (tmp_desc & 0xfffff000) | ((acc_laddr >> 10) & 0xffc); + acc_pdaddr = + busaddr((tmp_desc & 0xfffff000U) | ((acc_laddr >> 10) & 0xffc)) + | BusAddr::S | BusAddr::Size4; acc_stat |= tmp_desc & ACC_STAT_MASK; putlog(4, " SD $%08x pdaddr=$%08x stat=%s acc=%s", - acc_sdaddr, acc_pdaddr, + acc_sdaddr, acc_pdaddr.Addr(), stat2str(tmp_desc).c_str(), stat2str(acc_stat).c_str()); return true; @@ -1618,49 +1617,57 @@ m88200::FetchSegmentDesc() } // Fetch Page Descriptor -// Type=Valid なら true、Type=Invalid なら false を返す。Type=Retry はない。 +// Type=Valid なら true、Type=Invalid なら SetFault() して false を返す。 +// Type=Retry はない。 // 公式フローチャートと違って MBus Acquire された状態で呼び出すこと。 // p2-23, Figure2-7 bool m88200::FetchPageDesc() { - uint64 data; + busdata data; - MBusMakeSnoop(acc_pdaddr, 0); - data = MBusRead(acc_pdaddr, 4); - if (__predict_false((int64)data < 0)) { + MBusMakeSnoop(acc_pdaddr.Addr(), IM_0); + // ディスクリプタの読み込みは自動的に CI (p3-18 3.6)。 + data = PhysRead(acc_pdaddr, DESC_CI); + if (__predict_false(data.IsBusErr())) { + // SetFault() 済み。 return false; } - tmp_desc = (uint32)data; - tmp_desc &= (0xfffff000 | DESC_WT | DESC_SP | DESC_G | DESC_CI | + tmp_desc = data.Data(); + tmp_desc &= (0xfffff000U | DESC_WT | DESC_SP | DESC_G | DESC_CI | DESC_M | DESC_U | DESC_WP | DESC_V); if (__predict_false((tmp_desc & DESC_V) == 0)) { - fault_code = FAULT_CODE_PAGE; - fault_addr = acc_pdaddr; - putlog(4, " PD $%08x desc=$%08x PageFault", acc_pdaddr, tmp_desc); + SetFault(FAULT_CODE_PAGE, acc_pdaddr.Addr()); + putlog(4, " PD $%08x desc=$%08x PageFault", + acc_pdaddr.Addr(), tmp_desc); return false; } else { // Descriptor is valid - if (__predict_false((tmp_desc & DESC_SP) && acc_super == 0)) { - fault_code = FAULT_CODE_SUPERVISOR; - fault_addr = acc_pdaddr; + if (__predict_false((tmp_desc & DESC_SP) && IsUser())) { + SetFault(FAULT_CODE_SUPERVISOR, acc_pdaddr.Addr()); putlog(4, " PD $%08x desc=$%08x SupervisorFault", - acc_pdaddr, tmp_desc); + acc_pdaddr.Addr(), tmp_desc); return false; } acc_stat |= tmp_desc & ACC_STAT_MASK; + // Fig.2-7 ではここで Write Violation のテストをするとある。 + // 一方、p2-19 の本文ではここではテストせず、PATC エントリを作って + // 再サーチでこの PATC に当たってそこでテストすると書いてある + // (Table 2-2 にもディスクリプタ取得中の Write Violation はないので + // 本文の内容を支持しているように見える) が、 + // そうすると動かないので、本文が誤りだと思うことにする…。 if (__predict_false((acc_stat & DESC_WP) && acc_IsWrite())) { - fault_code = FAULT_CODE_WRITE; // fault_addr は invalid data - fault_addr = 0xdeadbeef; - putlog(4, " PD $%08x desc=$%08x WriteFault", acc_pdaddr, tmp_desc); + SetFault(FAULT_CODE_WRITE, 0xccccccccU); + putlog(4, " PD $%08x desc=$%08x WriteFault", + acc_pdaddr.Addr(), tmp_desc); return false; } else { - putlog(4, " PD $%08x stat=%s acc=%s", acc_pdaddr, + putlog(4, " PD $%08x stat=%s acc=%s", acc_pdaddr.Addr(), stat2str(tmp_desc).c_str(), stat2str(acc_stat).c_str()); return true; @@ -1669,13 +1676,12 @@ m88200::FetchPageDesc() } // Update Page Descriptor -// Type=Valid なら true、Type=Invalid なら false を返す。Type=Retry はない。 +// Type=Valid なら true、Type=Invalid なら SetFault() して false を返す。 +// Type=Retry はない。 // p2-24, Figure2-8 bool m88200::UpdatePageDesc() { - uint64 rv; - if ((tmp_desc & DESC_M) == 0 && acc_IsWrite()) { // Update Modified bit and accrue status @@ -1689,18 +1695,20 @@ m88200::UpdatePageDesc() // Update Used bit tmp_desc |= DESC_U; - MBusMakeSnoop(acc_pdaddr, 1); - rv = MBusWrite(acc_pdaddr, tmp_desc, 4); - if (__predict_false((int64)rv < 0)) { + // ディスクリプタへのアクセスは自動的に CI (p3-18 3.6)。 + busdata r = PhysWrite(acc_pdaddr, tmp_desc, DESC_CI); + if (__predict_false(r.IsBusErr())) { + // SetFault() 済み。 return false; } + putlog(4, " Update PD $%08x desc=$%08x stat=%s", - acc_pdaddr, tmp_desc, stat2str(tmp_desc).c_str()); + acc_pdaddr.Addr(), tmp_desc, stat2str(tmp_desc).c_str()); return true; } -// patc[patc_index] に対応する PATC ハッシュを無効にする +// patc[patc_index] に対応する PATC ハッシュを無効にする。 void m88200::InvalidatePATCHash(int patc_index) { @@ -1721,51 +1729,57 @@ m88200::InvalidatePATCHash(int patc_inde void m88200::CreatePATCEntry() { - int hashkey; - - // 空きがあろうがなかろうが、とにかく一番古いエントリを使う。 - // おそらく実機も空きエントリを前に詰めるような複雑な処理はして - // ないだろうという推測。 - // 検索はハッシュを使って実装してあるため、途中に空きエントリが + // 空きエントリがあればそれを使う。なければ一番古いエントリを使う。 + // 検索は線形探索ではなくハッシュで実装してあるので、途中に空きエントリが // あっても (線形探索する時のような) デメリットはない。 + // 最初は全エントリが空きで、エントリが埋まるまでは patc_next は使われない。 + // 一度エントリが埋まると patc_next が生贄になる。 -#if defined(M88200_STAT) - stat_patc_create++; -#endif - m88200PATC& p = patc[patc_next]; - // 有効なら無効化 - if (p.IsValid()) { - InvalidatePATCHash(patc_next); + STAT(stat_patc_create); + + uint pi; + if (patc_free != 0) { + // 空きがあればそれを使う。 + pi = __builtin_ctzll(patc_free); + patc_free &= ~(1ULL << pi); + } else { + // 空きがなければ FIFO。 + pi = patc_next; + if (__predict_false(++patc_next >= patc.size())) { + patc_next = 0; + } + + // 有効なはず。 + InvalidatePATCHash(pi); } + m88200PATC& p = patc[pi]; // このエントリを潰して作る memset(&p, 0, sizeof(p)); - p.lpa = acc_laddr & 0xfffff000; - p.pfa = tmp_desc & 0xfffff000; - if (acc_super) { + p.lpa = acc_laddr & 0xfffff000U; + p.pfa = tmp_desc & 0xfffff000U; + if (IsSuper()) { p.lpa |= PATC_S; } p.stat = acc_stat; putlog(4, " %s %c:$%08x:$%08x stat=%s", __func__, - p.IsS() ? 'S' : 'U', (p.lpa & 0xfffff000), p.pfa, + p.IsS() ? 'S' : 'U', (p.lpa & 0xfffff000U), p.pfa, stat2str(p.stat).c_str()); - hashkey = patc_hash_func(p.lpa); - uint64 bit = 1ULL << patc_next; + uint64 hashkey = patc_hash_func(p.lpa); + uint64 bit = 1ULL << pi; patc_hash[hashkey] |= bit; - - patc_next++; - if (__predict_false(patc_next >= patc.size())) { - patc_next = 0; - } } // アドレス変換 (デバッガ用) -uint64 -m88200::TranslatePeek(uint32 laddr, bool issuper, bool do_search) const +busaddr +m88200::TranslatePeek(busaddr addr_) const { const m88200APR *xapr; + uint32 laddr = addr_.Addr(); + bool issuper = addr_.IsSuper(); uint32 la; + uint n; // Select Area Descriptor if (issuper) { @@ -1774,33 +1788,38 @@ m88200::TranslatePeek(uint32 laddr, bool xapr = &uapr; } if (xapr->enable == 0) { - return laddr; + // 変換しない場合でも暗黙 BWP は有効…。 + if (laddr >= 0xfff00000U && issuper) { + // 必ず成立するはず。 + n = 8; + goto batc; + } + return busaddr(laddr); } // Search BATC first - la = laddr & 0xfff80000; + n = 0; + batc: + la = laddr & 0xfff80000U; la |= issuper ? BATC_S : 0; - for (const auto& b : batc) { + for (; n < batc.size(); n++) { + const auto& b = batc[n]; if (b.lba == la) { // BATC hit - return b.pba | (laddr & 0x0007ffff); + return busaddr(b.pba | (laddr & 0x0007ffffU)); } } // BATC miss, search PATC then - la = laddr & 0xfffff000; + la = laddr & 0xfffff000U; la |= issuper ? PATC_S : 0; for (const auto& p : patc) { if (p.lpa == la) { // PATC hit - return p.pfa | (laddr & 0x00000fff); + return busaddr(p.pfa | (laddr & 0x00000fffU)); } } - if (do_search == false) { - return (uint64)-1; - } - // ここからテーブルサーチ uint64 data; uint32 desc_addr; @@ -1808,33 +1827,147 @@ m88200::TranslatePeek(uint32 laddr, bool // Peek Segment Descriptor desc_addr = xapr->addr | ((laddr >> 20) & ~3U); - data = mainbus->Peek32(desc_addr); + data = mainbus->Peek4(desc_addr); if ((int64)data < 0) { - return data; + return BusAddr::BusErr | BusAddr::TableSearched; } desc = (uint32)data; if ((desc & DESC_V) == 0) { - return (uint64)-1; + return BusAddr::BusErr | BusAddr::TableSearched; } if ((desc & DESC_SP) && issuper == false) { - return (uint64)-1; + return BusAddr::BusErr | BusAddr::TableSearched; } // Peek Page Descriptor - desc_addr = (desc & 0xfffff000) | ((laddr >> 10) & 0xffc); - data = mainbus->Peek32(desc_addr); + desc_addr = (desc & 0xfffff000U) | ((laddr >> 10) & 0xffc); + data = mainbus->Peek4(desc_addr); if ((int64)data < 0) { - return data; + return BusAddr::BusErr | BusAddr::TableSearched; } desc = (uint32)data; if ((desc & DESC_V) == 0) { - return (uint64)-1; + return BusAddr::BusErr | BusAddr::TableSearched; } if ((desc & DESC_SP) && issuper == false) { - return (uint64)-1; + return BusAddr::BusErr | BusAddr::TableSearched; } - return (desc & 0xfffff000) | (laddr & 0x00000fff); + busaddr r((desc & 0xfffff000U) | (laddr & 0x00000fffU)); + r |= BusAddr::TableSearched; + return r; +} + + +// +// 物理アドレスアクセス +// + +// paddr はアドレス、サイズ、S/U ビットのみを参照する。 +// paddr のアドレスはそのサイズによって 1, 2, 4 バイト境界に整列していること。 +// stat はこのアクセスの制御ビット DESC_WT, DESC_G, DESC_CI。 +// 戻り値は、読み込めればその値 (右詰め)。 +// バスエラーが起きれば SetFault して BusData::BusErr を返す。 +busdata +m88200::PhysRead(busaddr paddr, uint32 stat) +{ + if ((stat & DESC_CI)) { + // キャッシュ禁止の場合の説明が少ない…。 + // メモリから読み込んで、キャッシュには置かない。 + // その際キャッシュに同エントリが存在していれば + // *たとえ EM でもコピーバックせずに* 破棄する (p3-18 の 3.6 あたり)。 + + uint32 tagaddr = (paddr.Addr() & 0xfffff000U); + uint32 setidx = (paddr.Addr() >> 4) & 0xff; + m88200CacheSet& set = setarray[setidx]; + + int line = set.Lookup(tagaddr); + if (__predict_false(line >= 0)) { + set.Update(line, m88200CacheSet::IV); + } + + // とりあえず。Table 6-2 + parent->AddCycle(7); + + MBusAcquire(); + MBusMakeSnoop(paddr.Addr(), IM_0); + busdata bd = MBusRead(paddr); + MBusRelease(); + // bd.Data は下詰めになっている。エラー時は SetFault() 済み。 + return bd; + } else { + // キャッシュ許可の場合。 + busdata data; + + data = CacheRead(paddr.Addr() & ~3U); + if (__predict_false(data.IsBusErr())) { + // SetFault() 済み。 + return data; + } + + uint size = paddr.GetSize(); + if (__predict_true(size == 4)) { + return data; + } else if (__predict_true(size == 1)) { + switch (paddr.Addr() & 3U) { + case 0: + return (data >> 24); + case 1: + return (data >> 16) & 0xff; + case 2: + return (data >> 8) & 0xff; + case 3: + return data & 0xff; + default: + __unreachable(); + } + } else if (__predict_true(size == 2)) { + if ((paddr.Addr() & 3U) == 0) { + return data >> 16; + } else { + return data & 0xffff; + } + } else { + PANIC("size must be 1, 2, 4"); + } + } +} + +// paddr はアドレス、サイズ、S/U ビットのみを参照する。 +// paddr のアドレスはそのサイズによって 1, 2, 4 バイト境界に整列していること。 +// data は右詰め。 +// stat はこのアクセスの制御ビット DESC_WT, DESC_G, DESC_CI。 +// 書き込めれば 0、バスエラーが起きれば SetFault() して BusData::BusErr を返す。 +busdata +m88200::PhysWrite(busaddr paddr, uint32 data, uint32 stat) +{ + if ((stat & DESC_CI)) { + // キャッシュ禁止の場合の説明が少ない…。 + // キャッシュには置かずにメモリに書き出す。 + // その際キャッシュに同エントリが存在していれば + // *たとえ EM でもコピーバックせずに* 破棄する (p3-18 の 3.6 あたり)。 + + uint32 tagaddr = (paddr.Addr() & 0xfffff000U); + uint32 setidx = (paddr.Addr() >> 4) & 0xff; + m88200CacheSet& set = setarray[setidx]; + + int line = set.Lookup(tagaddr); + if (__predict_false(line >= 0)) { + set.Update(line, m88200CacheSet::IV); + } + + // とりあえず。Table 6-2 + parent->AddCycle(7); + + MBusAcquire(); + MBusMakeSnoop(paddr.Addr(), IM_1); + busdata bd = MBusWrite(paddr, data); + MBusRelease(); + return bd; + } else { + // キャッシュ許可の場合。 + return CacheWrite(paddr.Addr(), data, paddr.GetSize()); + } } @@ -1844,7 +1977,7 @@ m88200::TranslatePeek(uint32 laddr, bool // このラインの状態を更新 void -m88200CacheSet::Update(int line, m88200CacheSet::Status status) +m88200CacheSet::Update(uint line, m88200CacheSet::Status status) { vv[line] = status; if (__predict_false(status == IV)) { @@ -1857,8 +1990,8 @@ m88200CacheSet::Update(int line, m88200C // CSSP L5-L0 の処理。 // 引数 tmpL の状態から line を最新にしたらどうなるか、を返す。 // 表示処理で一時変数に対して処理が必要なため分離してある。 -/*static*/ int -m88200CacheSet::TryUseLine(int tmpL, int line) +/*static*/ uint +m88200CacheSet::TryUseLine(uint tmpL, int line) { // L フィールドは 3bit, 2bit, 1bit で構成され、 // 他のラインの対応ビットを落として、 @@ -1882,8 +2015,8 @@ m88200CacheSet::TryUseLine(int tmpL, int // CSSP L5-L0 の処理。 // 引数 tmpL の状態から line を最古にしたらどうなるか、を返す。 -/*static*/ int -m88200CacheSet::TryUnuseLine(int tmpL, int line) +/*static*/ uint +m88200CacheSet::TryUnuseLine(uint tmpL, int line) { // Use のちょうど反転論理 if (line == 3) { @@ -1904,7 +2037,7 @@ m88200CacheSet::TryUnuseLine(int tmpL, i // 引数 tmpL の状態で、最新の line を返す。 // 表示処理で一時変数に対して処理が必要なため分離してある。 /*static*/ int -m88200CacheSet::TryGetOldestLine(int tmpL) +m88200CacheSet::TryGetOldestLine(uint tmpL) { if (tmpL < 8) { return 3; @@ -1932,7 +2065,7 @@ m88200CacheSet::SelectOldestLine() const // キャッシュの指定の line, word に data を書き込む。 // size は 1, 2, 4 バイト。 void -m88200CacheSet::Write(int line, uint32 paddr, uint32 data, int size) +m88200CacheSet::Write(uint line, uint32 paddr, uint32 data, uint size) { uint32 wordidx = (paddr >> 2) & 0x03; uint32 data32; @@ -1945,23 +2078,23 @@ m88200CacheSet::Write(int line, uint32 p data32 = word[line * 4 + wordidx]; if (size == 2) { if ((paddr & 2) == 0) { - data32 = (data32 & 0x0000ffff) | (data << 16); + data32 = (data32 & 0x0000ffffU) | (data << 16); } else { - data32 = (data32 & 0xffff0000) | data; + data32 = (data32 & 0xffff0000U) | data; } } else { switch (paddr & 3) { case 0: - data32 = (data32 & 0x00ffffff) | (data << 24); + data32 = (data32 & 0x00ffffffU) | (data << 24); break; case 1: - data32 = (data32 & 0xff00ffff) | (data << 16); + data32 = (data32 & 0xff00ffffU) | (data << 16); break; case 2: - data32 = (data32 & 0xffff00ff) | (data << 8); + data32 = (data32 & 0xffff00ffU) | (data << 8); break; case 3: - data32 = (data32 & 0xffffff00) | data; + data32 = (data32 & 0xffffff00U) | data; break; default: __unreachable(); @@ -1975,7 +2108,7 @@ m88200CacheSet::Write(int line, uint32 p int m88200CacheSet::Lookup(uint32 tagaddr) const { - for (int line = 0; line < 4; line++) { + for (uint line = 0; line < 4; line++) { if (tag[line] == tagaddr) { return line; } @@ -1985,87 +2118,76 @@ m88200CacheSet::Lookup(uint32 tagaddr) c // キャッシュの指定の set, line にメモリから 1 ライン(4word) 読み込む。 -// 成功すれば 0 を返す。 -// バスエラーなら fault_code, fault_addr をセットし (uint64)-1 を返す。 -uint64 -m88200::ReadLine(m88200CacheSet& set, int line, uint32 tagaddr) +// 成功すれば true を返す。 +// バスエラーが起きれば SetFault() して false を返す。 +bool +m88200::ReadLine(m88200CacheSet& set, uint line, uint32 tagaddr) { - uint32 addr; - uint64 data; - - // キャッシュラインの充填は実機では Burst Read されているらしいが - // nono では Burst Read 用のメソッドは用意しておらず通常の 32bit - // アクセスをしているため、このままでは所要ウェイト数が正しくない。 - // そこでここで差を調整することにする。 - // mainbus->Read32() 4回なのでこれによって 12 wait が加算される。一方 - // 実機の Burst Read は取扱説明書によると 320ns (8 サイクル) で完了 - // するようなので、その差 4 をあらかじめ引いておく。 - // 途中でバスエラーが起きたらとかそこまでは考慮しない。 - parent->AddCycle(-4); - // タグを更新 set.tag[line] = tagaddr; - addr = set.tag[line] | (set.setidx << 4); - for (int i = 0; i < 4; i++) { - data = mainbus->Read32(addr); - if (__predict_false((int64)data < 0)) { - fault_code = FAULT_CODE_BUSERR; - fault_addr = addr; - return data; + busaddr addr = busaddr(set.tag[line] | (set.setidx << 4)) | acc_super; + busdata r = mainbus->ReadBurst16(addr, &set.word[line * 4]); + if (__predict_true(r.IsBusErr() == false)) { + parent->AddWait(r.GetWait()); + return true; + } else { + addr |= BusAddr::Size4; + for (uint i = 0; i < 4; i++) { + busdata bd = mainbus->Read(addr); + parent->AddWait(r.GetWait()); + if (__predict_false(bd.IsBusErr())) { + SetFault(FAULT_CODE_BUSERR, addr.Addr()); + return false; + } + set.word[line * 4 + i] = bd.Data(); + addr += 4; } - set.word[line * 4 + i] = data; - addr += 4; } - return 0; + return true; } -// キャッシュの指定の set, line を1ライン、メモリに書き出す(コピーバック)。 -// 成功すれば 0 を返す。 -// バスエラーなら fault_code, fault_addr をセットし (uint64)-1 を返す。 -uint64 -m88200::CopyBackLine(m88200CacheSet& set, int line) +// キャッシュの指定の set, line の1ライン(4word) を書き出す (コピーバック)。 +// 成功すれば true を返す。 +// バスエラーが起きれば SetFault() して false を返す。 +// コピーバックの成功/失敗など状況に応じて、呼び出し元が set.Update() すること。 +bool +m88200::CopyBackLine(m88200CacheSet& set, uint line) { - uint32 addr; - - // キャッシュラインの書き出しは実機では Burst Write されているらしいが - // nono では Burst Write 用のメソッドは用意しておらず通常の 32bit - // アクセスをしているため、このままでは所要ウェイト数が正しくない。 - // そこでここで差を調整することにする。 - // mainbus->Write32() 4回なのでこれによって 8 wait が加算される。一方 - // 実機の Burst Write は取扱説明書によると 280ns (7サイクル) で完了する - // ようなので、その差 1 をあらかじめて引いておく。 - // 途中でバスエラーが起きたらとかそこまでは考慮しない。 - parent->AddCycle(-1); - - addr = set.tag[line] | (set.setidx << 4); - for (int i = 0; i < 4; i++) { - uint64 rv = mainbus->Write32(addr, set.word[line * 4 + i]); - if (__predict_false((int64)rv < 0)) { - fault_code = FAULT_CODE_BUSERR; - fault_addr = addr; - return rv; + busaddr addr = busaddr(set.tag[line] | (set.setidx << 4)) | acc_super; + busdata r = mainbus->WriteBurst16(addr, &set.word[line * 4]); + if (__predict_true(r.IsBusErr() == false)) { + parent->AddWait(r.GetWait()); + return true; + } else { + addr |= BusAddr::Size4; + for (uint i = 0; i < 4; i++) { + busdata bd = mainbus->Write(addr, set.word[line * 4 + i]); + parent->AddWait(bd.GetWait()); + if (__predict_false(bd.IsBusErr())) { + SetFault(FAULT_CODE_BUSERR, addr.Addr()); + return false; + } + addr += 4; } - addr += 4; } - return 0; + return true; } // キャッシュに対して paddr の読み込みを行う。 // paddr は 32bit 境界のアドレスであること。 // 読み込めれば該当の32bitワードを返す。 -// 何らかエラーが起きれば (uint64)-1 を返す。エラー要因は fault_code とか参照。 +// バスエラーが起きれば SetFault() して BusData::BusErr を返す。 // p.3-8 Figure 3-3 -uint64 +busdata m88200::CacheRead(uint32 paddr) { int line; - uint64 rv; assert((paddr & 3) == 0); // タグとセット番号 - uint32 tagaddr = (paddr & 0xfffff000); + uint32 tagaddr = (paddr & 0xfffff000U); uint32 setidx = (paddr >> 4) & 0xff; uint32 wordidx = (paddr >> 2) & 0x03; @@ -2075,7 +2197,7 @@ m88200::CacheRead(uint32 paddr) line = set.Lookup(tagaddr); if (__predict_true(line >= 0)) { // Cache Hit - putlog(4, " CacheRead hit (line=%d,word=%d)", line, wordidx); + putlog(4, " CacheRead hit (line=%u,word=%u)", line, wordidx); parent->AddCycle(1); goto success; } @@ -2089,12 +2211,12 @@ m88200::CacheRead(uint32 paddr) // Select cache line for replacement line = set.SelectOldestLine(); - putlog(4, " CacheRead miss (replace line=%d)", line); + putlog(4, " CacheRead miss (replace line=%u)", line); if (set.vv[line] == m88200CacheSet::EM) { parent->AddCycle(7); // Table.6-2 - rv = CopyBackLine(set, line); - if ((int64)rv < 0) { + if (CopyBackLine(set, line) == false) { + // SetFault() 済み。 goto error; } } @@ -2103,9 +2225,9 @@ m88200::CacheRead(uint32 paddr) set.Update(line, m88200CacheSet::IV); // Read line from memory - MBusMakeSnoop(paddr, 0); - rv = ReadLine(set, line, tagaddr); - if (__predict_false((int64)rv < 0)) { + MBusMakeSnoop(paddr, IM_0); + if (ReadLine(set, line, tagaddr) == false) { + // SetFault() 済み。 goto error; } @@ -2114,31 +2236,31 @@ m88200::CacheRead(uint32 paddr) // Update cache line set.Update(line, m88200CacheSet::SU); - putlog(4, " CacheRead updated (line=%d,word=%d)", line, wordidx); + putlog(4, " CacheRead updated (line=%u,word=%u)", line, wordidx); success: set.Use(line); return set.word[line * 4 + wordidx]; error: MBusRelease(); - return (uint64)-1; + return BusData::BusErr; } // キャッシュに対して paddr への data の書き込みを行う。size は 1, 2, 4 バイト。 // paddr は size に応じた境界にあること。 -// 書き込めれば 0、エラーが起きれば (uint64)-1 を返す。 -// エラー要因は fault_code とか参照。 +// 書き込めれば 0、バスエラーが起きれば SetFault() して BusData::BusErr を返す。 // p.3-10 Figure 3-5 -uint64 -m88200::CacheWrite(uint32 paddr, uint32 data, int size) +busdata +m88200::CacheWrite(uint32 paddr, uint32 data, uint size) { int line; - uint64 rv; + busaddr baddr; + busdata bd; assert((paddr & (size - 1)) == 0); // タグとセット番号 - uint32 tagaddr = (paddr & 0xfffff000); + uint32 tagaddr = (paddr & 0xfffff000U); uint32 setidx = (paddr >> 4) & 0xff; m88200CacheSet& set = setarray[setidx]; @@ -2160,12 +2282,12 @@ m88200::CacheWrite(uint32 paddr, uint32 // Select cache line for replacement line = set.SelectOldestLine(); - putlog(4, " CacheWrite miss (replace line=%d)", line); + putlog(4, " CacheWrite miss (replace line=%u)", line); if (set.vv[line] == m88200CacheSet::EM) { parent->AddCycle(7); // Table.6-2 - rv = CopyBackLine(set, line); - if ((int64)rv < 0) { + if (CopyBackLine(set, line) == false) { + // SetFault() 済み。 goto error; } } @@ -2174,15 +2296,17 @@ m88200::CacheWrite(uint32 paddr, uint32 set.Update(line, m88200CacheSet::IV); // Read line with intent to modify - MBusMakeSnoop(paddr, 1); - rv = ReadLine(set, line, tagaddr); - if ((int64)rv < 0) { + MBusMakeSnoop(paddr, IM_1); + if (ReadLine(set, line, tagaddr) == false) { + // SetFault() 済み。 goto error; } // Write data to memory - rv = MBusWrite(paddr, data, size); - if ((int64)rv < 0) { + baddr = busaddr(paddr) | acc_super | busaddr::Size(size); + bd = MBusWrite(baddr, data); + if (__predict_false(bd.IsBusErr())) { + // SetFault() 済み。 goto error; } @@ -2190,10 +2314,11 @@ m88200::CacheWrite(uint32 paddr, uint32 set.Write(line, paddr, data, size); // Mark line exclusive unmodified - // p3-9 3.4.2.2 CACHE WRITE:CACHE HIT. の本文に writethrough のときは - // It marks the line as shared unmodified... と書いてある。 - // Fig 3-5 には記載はないが、キャッシュエントリが作られるときも - // Fig 3-6 のキャッシュヒット時と同じように SU にならないと矛盾する。 + // ただし Fig.3-12 によると、WriteThrough の時は常に IV と SU の + // 二状態しかないはずなので、ここは正しくは + // Mark line shared unmodified (If WT), + // Mark line exclusive unmodified (Otherwise) + // なのでは。これなら Fig.3-6 とも整合する。 if ((acc_stat & DESC_WT)) { set.Update(line, m88200CacheSet::SU); } else { @@ -2206,25 +2331,22 @@ m88200::CacheWrite(uint32 paddr, uint32 error: MBusRelease(); - return (uint64)-1; + return BusData::BusErr; } // キャッシュがヒットした場合。 -// 書き込めれば 0、エラーが起きれば (uint64)-1 を返す。 +// 書き込めれば 0、バスエラーが起きれば SetFault して BusData::BusErr を返す。 // p3-11 Figure 3-6 -uint64 -m88200::CacheWriteHit(m88200CacheSet& set, int line, - uint32 paddr, uint32 data, int size) +busdata +m88200::CacheWriteHit(m88200CacheSet& set, uint line, + uint32 paddr, uint32 data, uint size) { - uint64 rv; - if (__predict_false(set.vv[line] == m88200CacheSet::SU)) { // Line Shared Unmodified の場合 - putlog(4, " CacheWrite hit shared unmodified (line=%d)", line); + putlog(4, " CacheWrite hit shared unmodified (line=%u)", line); - // WriteThrough と Global は最後の状態変化だけが違う - if ((acc_stat & (DESC_WT | DESC_G))) { + if ((acc_stat & DESC_WT)) { // reply = Wait; MBusAcquire(); @@ -2232,34 +2354,48 @@ m88200::CacheWriteHit(m88200CacheSet& se set.Write(line, paddr, data, size); // Write data to memory - MBusMakeSnoop(paddr, 1); - rv = MBusWrite(paddr, data, size); - if ((int64)rv < 0) { - MBusRelease(); - return rv; - } + MBusMakeSnoop(paddr, IM_1); + busaddr baddr = busaddr(paddr) | acc_super | busaddr::Size(size); + busdata bd = MBusWrite(baddr, data); - if ((acc_stat & DESC_WT)) { + if (__predict_true(bd.IsOK())) { // Mark line shared unmodified set.Update(line, m88200CacheSet::SU); - } else { + set.Use(line); + } + + MBusRelease(); + return bd; + } else if ((acc_stat & DESC_G)) { + // reply = Wait; + MBusAcquire(); + + // Write data to cache + set.Write(line, paddr, data, size); + + // Write data to memory + MBusMakeSnoop(paddr, IM_1); + busaddr baddr = busaddr(paddr) | acc_super | busaddr::Size(size); + busdata bd = MBusWrite(baddr, data); + + if (__predict_true(bd.IsOK())) { // Mark line exclusive unmodified set.Update(line, m88200CacheSet::EU); + set.Use(line); } - set.Use(line); MBusRelease(); - return 0; + return bd; } // どちらでもない場合は Line Exclusive と同じ処理に落ちる } else { // Line Exclusive の場合 - putlog(4, " CacheWrite hit exclusive (line=%d)", line); + putlog(4, " CacheWrite hit exclusive (line=%u)", line); } // Write data into cache - putlog(4, " CacheWrite line=%d $%08x sz=%d", line, paddr, size); + putlog(4, " CacheWrite line=%u $%08x sz=%u", line, paddr, size); set.Write(line, paddr, data, size); // Mark line exclusive modified @@ -2268,61 +2404,62 @@ m88200::CacheWriteHit(m88200CacheSet& se return 0; } -// paddr への xmem を行う。size は 1, 2, 4 バイト。 -// 成功すれば読み出した値、エラーが起きれば (uint64)-1 を返す。 +// paddr への xmem を行う。size は 1, 4 バイト (2バイトはない)。 +// 成功すれば読み出し値、エラーが起きれば SetFault して BusData::BusErr を返す。 // p3-13, Figure3-7 -uint64 -m88200::CacheXmem(uint32 paddr, uint32 data, int size) +busdata +m88200::CacheXmem(uint32 paddr, uint32 data, uint size) { int line; - uint64 rv; + busdata bd; assert((paddr & (size - 1)) == 0); // タグとセット番号 - uint32 tagaddr = (paddr & 0xfffff000); + uint32 tagaddr = (paddr & 0xfffff000U); uint32 setidx = (paddr >> 4) & 0xff; m88200CacheSet& set = setarray[setidx]; putlog(3, "CacheXmem paddr=$%08x (set=$%02x)", paddr, setidx); + // ちょっとフローチャートとは一対一対応しないけど、 + // 要はどのパスを通っても必要になる前に一度だけ MBusAcquire + Snoop する + // ということのはず。 + // うちではキャッシュ操作と Mbus 操作の順を入れ替えても影響はない。 + MBusAcquire(); + MBusMakeSnoop(paddr, IM_1); + line = set.Lookup(tagaddr); - if (__predict_false(line >= 0 && set.vv[line] == m88200CacheSet::EM)) { - // Cache Hit (and Line exclusive modified) - putlog(4, " CacheXmem hit and EM (line=%d)", line); + if (__predict_true(line >= 0)) { + // Cache Hit - MBusAcquire(); + // xmem はそもそも CI 相当だが、Read/Write と違って EM なら + // コピーバックする。(p3-12 3.4.3) + if (set.vv[line] == m88200CacheSet::EM) { + // Line exclusive modified + putlog(4, " CacheXmem hit and EM (line=%u)", line); - parent->AddCycle(7); // Table.6-2 - MBusMakeSnoop(paddr, 1); - rv = CopyBackLine(set, line); - if (__predict_false((int64)rv < 0)) { - goto error; + parent->AddCycle(7); // Table.6-2 + if (CopyBackLine(set, line) == false) { + // SetFault() 済み。 + MBusRelease(); + return BusData::BusErr; + } + } else { + // Otherwise + putlog(4, " CacheXmem hit and unmodified (line=%u)", line); } - set.Update(line, m88200CacheSet::IV); } else { - // Cache Miss or (Cache Hit but other than exclusive modified) - - if (line >= 0) { - // Cache Hit and Otherwise (= !EM) - putlog(4, " CacheXmem hit but !EM (line=%d)", line); - set.Update(line, m88200CacheSet::IV); - } else { - putlog(4, " CacheXmem miss"); - } - - MBusAcquire(); + // Cache Miss + putlog(4, " CacheXmem miss"); } - MBusMakeSnoop(paddr, 1); - rv = MBusXmem(paddr, data, size); - MBusRelease(); - return rv; - - error: + busaddr baddr = busaddr(paddr) | acc_super | busaddr::Size(size); + bd = MBusXmem(baddr, data); MBusRelease(); - return rv; + // 失敗なら SetFault() 済み。 + return bd; } // MBus 使用権を取得する @@ -2343,7 +2480,7 @@ m88200::MBusAcquire() // いる他 CMMU が必要に応じて反応するのだが、ここでは全部マスタ主導で行う。 // Figure 3-8, 3-9 void -m88200::MBusMakeSnoop(uint32 paddr, int im) +m88200::MBusMakeSnoop(uint32 paddr, bool im) { if ((acc_stat & DESC_G) == 0) { return; @@ -2373,11 +2510,11 @@ m88200::MBusMakeSnoop(uint32 paddr, int // こっちはそのマスタから呼ばれるスレーブ側。 // Figure 3-8, 3-9 void -m88200::Snoop(uint32 paddr, int im) +m88200::Snoop(uint32 paddr, bool im) { assert((sctr & SCTR_SE)); - uint32 tagaddr = (paddr & 0xfffff000); + uint32 tagaddr = (paddr & 0xfffff000U); uint32 setidx = (paddr >> 4) & 0xff; m88200CacheSet& set = setarray[setidx]; @@ -2394,20 +2531,24 @@ m88200::Snoop(uint32 paddr, int im) // 実際にはここで MBus 使用権を要求して獲得するのだが // その機構はなくその代わり Acquire を呼ぶだけでいける。 MBusAcquire(); - uint64 rv = CopyBackLine(set, line); + bool r = CopyBackLine(set, line); MBusRelease(); - if ((int64)rv < 0) { + if (r == false) { // Set CE bit in system status register return; } + } else { + // Otherwise + } - if (im == 0) { - // Mark line shared unmodified (if IM=0) - set.Update(line, m88200CacheSet::SU); - } else { - // Mark line invalid (if IM=1) - set.Update(line, m88200CacheSet::IV); - } + // Modified でも Unmodified でも IM によって SU/IV に変えるところは同じ。 + if (im == IM_0) { + // Mark line shared unmodified (if IM=0) + set.Update(line, m88200CacheSet::SU); + // よそによって使われただけなのでこちらの L は更新しなくていいか。 + } else { + // Mark line invalid (if IM=1) + set.Update(line, m88200CacheSet::IV); } } // Resume servicing PBus