--- nono/m88xx0/m88200.cpp 2026/04/29 17:05:11 1.1.1.10 +++ nono/m88xx0/m88200.cpp 2026/04/29 17:05:39 1.1.1.15 @@ -6,58 +6,87 @@ // MC88200(CMMU) -#include "m88100.h" #include "m88200.h" +#include "mainbus.h" +#include "monitor.h" +#include "mpu88xx0.h" // static 変数 m88200 *m88200::mbus_master = NULL; // m88200 コンストラクタ -// -// ここのオブジェクト名は仮で、ID が確定した時点でもう一度セットする -// (see SetID()) -m88200::m88200() - : inherited("CMMU") -{ - // objname 等は SetID() で確定する - - reg_monitor.func = ToMonitorCallback(&m88200::MonitorUpdateReg); - reg_monitor.SetSize(52, 8); - atc_monitor.func = ToMonitorCallback(&m88200::MonitorUpdateATC); +m88200::m88200(MPU88xx0Device *parent_, uint id_) + : inherited(OBJ_M88200(id_)) +{ + parent = parent_; + + // ID は IDR レジスタへの書き込みで後から変更できるように読めるが、 + // ハードウェアパラメータのはずだし変更する意味はないのと、仮に変更しても + // 実際訳の分からんことにしかならんと思う。 + // そして PROM も OpenBSD/luna88k も IDR へ書き込んでいないので、やはり + // あらかじめ決まっているハードウェアパラメータだと思うことにする。 + // それと、コンストラクタ時点で ID が決まってないのは何かと困るという + // こちらの都合もある。 + id = id_; + + ClearAlias(); + AddAlias(string_format("CMMU%u", id)); + + 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); #if defined(M88200_STAT) - atc_monitor.SetSize(77, 39 + 15); + atc_monitor->SetSize(77, 44 + 5); #else - atc_monitor.SetSize(77, 39); + atc_monitor->SetSize(77, 44); #endif - cache_monitor.func = ToMonitorCallback(&m88200::MonitorUpdateCache); - cache_monitor.SetSize(82, 23); - // モニタ名も SetID() で決まるので、続きは向こうでやる + + 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"; +} + +// m88200 デストラクタ +m88200::~m88200() +{ +} + +// 初期化 +bool +m88200::Init() +{ + if (inherited::Init() == false) { + return false; + } + + mainbus = GetMainbusDevice(); + + memset(&atc_hash_all[0], 0, atc_hash_all.size()); + for (auto& b : batc) { + b.lba |= BATC_INVALID; + } + for (auto& p : patc) { + p.lpa |= PATC_INVALID; + } // 暗黙 BATC を初期化 - SetBWP(8, 0xfff7ffb5); // $fff8xxxx,S,WT,CI,V - SetBWP(9, 0xfffffff5); // $ffffxxxx,S,WT,CI,V + SetBATC(8, 0xfff00000, 0xfff00000, BWP_S | BWP_WT | BWP_CI | BWP_V); + SetBATC(9, 0xfff80000, 0xfff80000, BWP_S | BWP_WT | BWP_CI | BWP_V); // セットインデックスを初期化 for (int i = 0; i < setarray.size(); i++) { auto& set = setarray[i]; set.setidx = i; } -} - -// m88200 デストラクタ -m88200::~m88200() -{ -} -// 本当はコンストラクタ渡ししたかったもの -void -m88200::Ctor(m88kcpu *parent_) -{ - parent = parent_; + return true; } // リセット @@ -78,6 +107,8 @@ m88200::Reset() sapr.stat = APR_CI; uapr.enable = false; uapr.stat = APR_CI; + + // BATC, PATC はリセットで初期化されないようだが確認のしようがない。 } // モニター更新 (CMMU レジスタ) @@ -162,14 +193,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' : '-', @@ -188,9 +219,9 @@ m88200::MonitorUpdateATC(Monitor *, Text 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]; @@ -224,7 +255,7 @@ m88200::MonitorUpdateATC(Monitor *, Text x = 40; y = 10; } - screen.Print(x, y, "%2d:", i); + screen.Print(x, y, "%2u:", i); const m88200PATC& p = patc[i]; if (p.IsValid()) { @@ -240,43 +271,39 @@ m88200::MonitorUpdateATC(Monitor *, Text y++; } - if (__predict_false(translate_total == 0)) { - screen.Puts(40, y, "BATC --.--%, PATC --.--%, Miss --.--%"); + x = 49; + y++; + screen.Puts(0, y++, ""); + 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(40, y, "BATC%6.2f%%, PATC%6.2f%%, Miss%6.2f%%", - (double)batc_total / translate_total * 100, - (double)patc_hit / translate_total * 100, - (double)atc_miss / translate_total * 100); + screen.Print(x, y, "%5.1f%%)", + (double)batc_total / translate_total * 100); } - -#if defined(M88200_STAT) 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()); - if (stat_patc_search == 0) { - screen.Puts(60, 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(60, y, "%5.1f%%)", - (double)stat_patc_hit / stat_patc_search * 100); + screen.Print(x, y, "%5.1f%%)", + (double)patc_hit / translate_total * 100); } y++; - // ミスのうち1回目のハッシュ比較で外れが確定した率 - screen.Print(0, y, "PATC hash miss 1 %26s (miss1/miss ", - format_number(stat_patc_miss1).c_str()); - v = stat_patc_miss1 + stat_patc_miss2; - if (v == 0) { - screen.Puts(60, 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(60, y, "%5.1f%%)", (double)stat_patc_miss1 / v * 100); + screen.Print(x, y, "%5.1f%%)", + (double)atc_miss / translate_total * 100); } +#if defined(M88200_STAT) 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", @@ -287,38 +314,6 @@ m88200::MonitorUpdateATC(Monitor *, Text 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"); - for (auto n : stat_batc_hash_s) { - screen.Print(" %3d", n); - } - screen.Puts(0, y++, "BATC HASH.U"); - for (auto n : stat_batc_hash_u) { - screen.Print(" %3d", n); - } - // PATC ハッシュの衝突状況は、表示時に都度計算する - // (PATC ハッシュの更新頻度はおそらく表示周期より速いため) - auto print_phash = [&](const auto& hash) { - std::array st {}; - for (const auto hashmap : hash) { - int n = __builtin_popcount(hashmap); - if (n >= st.size()) { - n = st.size() - 1; - } - st[n]++; - } - for (auto n : st) { - screen.Print(" %3d", n); - } - }; - screen.Puts(0, y++, "PATC HASH.S"); - print_phash(patc_hash_s); - screen.Puts(0, y++, "PATC HASH.U"); - print_phash(patc_hash_u); #endif } @@ -327,7 +322,7 @@ m88200::MonitorUpdateATC(Monitor *, Text void m88200::MonitorUpdateCache(Monitor *, TextScreen& screen) { - int setidx = screen.userdata; + uint setidx = screen.userdata; // 上半分(概要) MonitorCacheOverview(screen, 0, setidx, true); @@ -339,9 +334,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]; /* @@ -377,7 +372,7 @@ m88200::MonitorCacheSet(TextScreen& s, i 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), @@ -386,14 +381,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 = (vm_phys_peek_8(addr) << 24) - | (vm_phys_peek_8(addr + 1) << 16) - | (vm_phys_peek_8(addr + 2) << 8) - | (vm_phys_peek_8(addr + 3)); + 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 { @@ -409,7 +404,7 @@ m88200::MonitorCacheSet(TextScreen& s, i } // 順序 - s.Print(58, y, "%d", Lorder[line]); + s.Print(58, y, "%u", Lorder[line]); } } @@ -423,11 +418,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 { @@ -438,16 +433,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 { @@ -463,21 +458,6 @@ m88200::MonitorCacheOverview(TextScreen& } } -// ID を設定する (IDR ではない) -void -m88200::SetID(uint id_) -{ - id = id_; - - SetName(string_format("CMMU#%u", id)); - ClearAlias(); - AddAlias(string_format("CMMU%u", id)); - - reg_monitor.Regist(ID_MONITOR_CMMU(id)); - atc_monitor.Regist(ID_MONITOR_ATC(id)); - cache_monitor.Regist(ID_SUBWIN_CACHE(id)); -} - // IDR の Version フィールドを設定する void m88200::SetVersion(uint version_) @@ -600,7 +580,7 @@ m88200::SetSCR(uint32 data) InvalidatePATC(); return; } - __unreachable(); + PANIC("should not reach: command=$%02x", command); } // command に応じてデータキャッシュをフラッシュする。SetSCR() の下請け。 @@ -615,6 +595,8 @@ m88200::FlushCache() bool copyback; bool invalidate; + assert(0x14 <= op && op <= 0x1f); + // op CopyBack Invalidate // %01'01gg false true | Data Cache Invalidate // %01'10gg true false | Data Cache Copyback to Memory @@ -627,17 +609,15 @@ m88200::FlushCache() } else if (op == 0x18) { copyback = true; invalidate = false; - } else if (op == 0x1c) { + } else { copyback = true; invalidate = true; - } else { - PANIC("unknown op = $%02x", op); } // 影響範囲 switch (gg) { case GG_LINE: - addr &= 0xfffffffc; + addr &= 0xfffffff0; parent->AddCycle(1); // Table.6-1 break; case GG_PAGE: @@ -719,6 +699,15 @@ m88200::FlushCache() parent->AddCycle(cycle); } +// issuper, addr から atc_hash_all[] のインデックスを返す。 +// (_all でないほうのインデックスは (laddr >> 12) だけなので用意しない) +inline uint32 +m88200::addr2hash(bool issuper, uint32 addr) const +{ + uint64 la = (issuper ? 0x1'00000000ULL : 0) | addr; + return la >> 12; +} + // command に応じて PATC を無効化する。SetSCR() の下請け。 // command が $30..$3f で呼ぶこと。 // p2-9, Section 2.2.4 @@ -727,8 +716,6 @@ m88200::InvalidatePATC() { uint32 gg = command & 0x03; bool s = (command & 0x04); - uint32 addr; - uint32 mask; if (__predict_true(gg == GG_PAGE)) { // 指定の1本だけ無効にする。 @@ -737,29 +724,20 @@ m88200::InvalidatePATC() stat_patc_invcmd_page++; #endif - // ゲスト側が指定した端数部分は有効部分でマスクする。 - // 内部では下位ビットはフラグ扱いなのでマスクしないと誤動作する。 - addr = GetSAR() & 0xfffff000; - addr |= (s) ? PATC_S : 0; - - auto& hash = (s) ? patc_hash_s : patc_hash_u; - uint64 pi = hash[patc_hash_func(addr)]; + uint32 la = addr2hash(s, GetSAR()); + uint8 n = atc_hash_all[la]; - for (; pi; pi &= pi - 1) { - 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本ヒットすればこれ以上一致することはないはず - break; - } + if (__predict_true((int8)n > 0)) { + // PATC#(n-1) + n--; + putlog(4, "Invalidate PATCEntry from GG_PAGE n=%u", n); + InvalidatePATCEntry(patc[n]); + // 1本ヒットすればこれ以上一致することはないはず } } else { // 指定範囲を無効にする。All と Segment はマスクが違うだけ。 + uint32 addr; + uint32 mask; if (gg == GG_ALL) { // S/U 指定したほう全体を無効にする @@ -789,11 +767,8 @@ m88200::InvalidatePATC() if ((p.lpa & mask) == addr) { // 無効化 // XXX 削除した結果穴が空いても詰める処理は未実装 - InvalidatePATCHash(i); - p.lpa |= PATC_INVALID; -#if defined(M88200_STAT) - stat_patc_invalidate++; -#endif + putlog(4, "Invalidate PATCEntry from GG_* n=%u", i); + InvalidatePATCEntry(p); } } } @@ -807,14 +782,20 @@ m88200::SetSCTR(uint32 data) // 全 CMMU について、それぞれスヌープ相手になる CMMU リストを更新。 // どの CMMU の SCTR への書き込みでも毎回全ての CMMU を書き換える。 - int n = parent->cmmu.size(); + std::array cmmu {}; + uint n = cmmu.size(); + for (int i = 0; i < n; i++) { + cmmu[i] = gMainApp.FindObject(OBJ_M88200(i)); + } for (int i = 0; i < n; i++) { - parent->cmmu[i].other_cmmu.clear(); + if (cmmu[i] == NULL) + continue; + cmmu[i]->other_cmmu.clear(); for (int j = 0; j < n; j++) { - if (i == j) + if (cmmu[j] == NULL || i == j) continue; - if ((parent->cmmu[j].sctr & SCTR_SE)) { - parent->cmmu[i].other_cmmu.push_back(&parent->cmmu[j]); + if ((cmmu[j]->sctr & SCTR_SE)) { + cmmu[i]->other_cmmu.push_back(cmmu[j]); } } } @@ -876,13 +857,41 @@ 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 >= 0xfff00000) { + return; + } + + // それ以外の BATC (#0-#7) と衝突する設定は先着優先としておく。 + uint32 la = addr2hash((flags & BWP_S), laddr); + uint8 n = atc_hash_all[la]; + if (n != 0 && ~n != bn) { + 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)ではビット位置が違う // ことに注意。 @@ -897,66 +906,58 @@ m88200::SetBWP(uint n, uint32 data) // stat | 0 0 | WT | SP | G | CI | 0 | M | U | WP | 0 | V | // +----..-----+----+----+----+----+----+----+----+----+----+----+ + // 更新前の BATC が有効なら、BATC を書き換える前にハッシュをクリア。 + if (b.IsValid()) { + uint32 la = addr2hash(b.IsS(), b.lba); + memset(&atc_hash_all[la], 0, 128); + + // BATC が消えたことによってここを指していた PATC が見えるように + // なったら、復活させる。 + for (int i = 0; i < patc.size(); i++) { + const auto& p = patc[i]; + if (p.IsValid() && + p.IsS() == b.IsS() && + (p.lpa & m88200BATC::LBAMASK) == b.Laddr()) + { + la = addr2hash(p.IsS(), p.lpa); + atc_hash_all[la] = i + 1; + } + } + } + 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; } - MakeBATCHash(); -} - -// BATC ハッシュを作り直す。 -void -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++) { - m88200BATC& b = batc[i]; - - if (b.IsValid()) { - // LBA の下位 8 ビットだけでハッシュを作る - uint32 hashkey = batc_hash_func(b.lba); - - // S によって該当するハッシュの該当するビットを立てておく - if (b.IsS()) { - batc_hash_s[hashkey] |= 1U << i; - } else { - batc_hash_u[hashkey] |= 1U << i; + // 更新後の BATC が有効なら、ハッシュもセット。 + if (b.IsValid()) { + uint32 la = addr2hash(b.IsS(), b.lba); + // この領域に PATC があれば追い出す。 + for (int i = la; i < la + 128; i++) { + uint8 pn = atc_hash_all[i]; + if ((int8)pn > 0) { + pn--; + putlog(4, "Invalidate PATCEntry from SetBATC(%u) n=%u", n, pn); + InvalidatePATCEntry(patc[pn]); } } + memset(&atc_hash_all[la], ~n, 128); } - -#if defined(M88200_STAT) - // ハッシュの各要素が何個の衝突を持つか。 - // (2以上が極力現れないことが望ましい) - auto counting = [](auto& stat, const auto& hash) { - std::fill(stat.begin(), stat.end(), 0); - for (const auto hashmap : hash) { - int n = __builtin_popcount(hashmap); - if (n >= stat.size()) { - n = stat.size() - 1; - } - stat[n]++; - } - }; - counting(stat_batc_hash_s, batc_hash_s); - counting(stat_batc_hash_u, batc_hash_u); -#endif + //DumpATC(); } // CSSP レジスタの読み出し @@ -1006,16 +1007,15 @@ 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]; + atc_hash = &atc_hash_all[0x10'0000]; } else { + acc_super = BusAddr::U; acc_apr = &uapr; - batc_hash = &batc_hash_u[0]; - patc_hash = &patc_hash_u[0]; + atc_hash = &atc_hash_all[0]; } } @@ -1026,91 +1026,88 @@ m88200::SetSuper(bool super) // MBus から読み込みを行う。 // paddr は size によって 1, 2, 4 バイト境界に整列していること。 -// 成功すれば読み込めた値を返す。 -// バスエラーが起きれば fault_code、fault_addr をセットして (uint64)-1 を返す。 -uint64 +// バスエラーなら呼び出し側で SetFault(FAULT_CODE_BUSERR, paddr) を呼ぶこと。 +busdata m88200::MBusRead(uint32 paddr, int size) { - uint64 data; + busaddr baddr = busaddr(paddr) | acc_super | busaddr::Size(size); + busdata bd; + + // m88k システムに接続しているデバイスはすべて、m68030 システムでいう + // ところのロングワードポートデバイスなので応答は常に 4 バイト分ある。 + bd = mainbus->Read(baddr); if (size == 4) { - data = vm_phys_read_32(paddr); } else if (size == 2) { - data = vm_phys_read_16(paddr); + if ((paddr & 2) == 0) { + bd >>= 16; + } else { + bd &= 0xffff; + } } else { - data = vm_phys_read_8(paddr); + bd >>= (3 - (paddr & 3)) * 8; + bd &= 0xff; } - if (__predict_false((int64)data < 0)) { - fault_code = FAULT_CODE_BUSERR; - fault_addr = paddr; - } - return data; + + parent->AddWait(bd.GetWait()); + return bd; } // MBus に書き込みを行う。 // paddr は size によって 1, 2, 4 バイト境界に整列していること。 -// 成功すれば 0 を返す。 -// バスエラーが起きれば fault_code、fault_addr をセットして (uint64)-1 を返す。 -uint64 +// バスエラーなら呼び出し側で SetFault(FAULT_CODE_BUSERR, paddr) を呼ぶこと。 +busdata m88200::MBusWrite(uint32 paddr, uint32 data, int size) { - uint64 rv; + busaddr baddr = busaddr(paddr) | acc_super | busaddr::Size(size); + busdata bd; - if (size == 4) { - rv = vm_phys_write_32(paddr, data); - } else if (size == 2) { - rv = vm_phys_write_16(paddr, data); - } else { - rv = vm_phys_write_8(paddr, data); - } - if (__predict_false((int64)rv < 0)) { - fault_code = FAULT_CODE_BUSERR; - fault_addr = paddr; - } - return rv; + bd = mainbus->Write(baddr, data); + parent->AddWait(bd.GetWait()); + return bd; } // MBus に xmem トランザクションを行う。 // paddr は size によって 1, 4 バイト境界に整列していること (2バイトはない)。 // MBus Acquire された状態で呼び出すこと。 // 成功すれば読み込めた値を返す。 -// バスエラーが起きれば fault_code、fault_addr をセットして (uint64)-1 を返す。 +// バスエラーなら呼び出し側で SetFault(FAULT_CODE_BUSERR, paddr) を呼ぶこと。 // read/write どちらでエラーが起きたかは acc_read で判別できる。 // p3-13, Figure3-7 の下半分のメインラインあたりに該当。 -uint64 +busdata m88200::MBusXmem(uint32 paddr, uint32 data, int size) { - 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, size); + 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, size); + parent->AddWait(bd.GetWait()); + if (__predict_false(bd.IsBusErr())) { + return bd; } - rv = fetched; - error: - return rv; + return fetched; } // // アクセス関数 // -template uint64 +template uint64 m88200::load(uint32 addr) { uint64 data; @@ -1119,29 +1116,32 @@ m88200::load(uint32 addr) acc_read = true; if (__predict_false(Translate() == false)) { putlog(2, "Translation BusError %c:$%08x", - (acc_super ? 'S' : 'U'), acc_laddr); + (IsSuper() ? 'S' : 'U'), acc_laddr); return (uint64)-1; } 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); + uint32 paddr = acc_paddrH + acc_paddrL; + busdata bd = MBusRead(paddr, size); MBusRelease(); - if (__predict_false((int64)data < 0)) { + if (__predict_false(bd.IsBusErr())) { putlog(2, "MBus BusError %c:$%08x", - (acc_super ? 'S' : 'U'), acc_laddr); + (IsSuper() ? 'S' : 'U'), acc_laddr); + SetFault(FAULT_CODE_BUSERR, paddr); + return (uint64)-1; } - return data; + return bd.Data(); } else { data = CacheRead(acc_paddrH); if (__predict_false((int64)data < 0)) { putlog(2, "Cache BusError %c:$%08x", - (acc_super ? 'S' : 'U'), acc_laddr); + (IsSuper() ? 'S' : 'U'), acc_laddr); return data; } - if (bits == 8) { + if (size == 1) { switch (acc_paddrL) { case 0: return (data >> 24); @@ -1151,112 +1151,119 @@ m88200::load(uint32 addr) return (data >> 8) & 0xff; case 3: return data & 0xff; + default: + PANIC("corrupted acc_paddrL=%u", acc_paddrL); } - } - if (bits == 16) { + } else if (size == 2) { if (acc_paddrL == 0) { return data >> 16; } else { return data & 0xffff; } - } - if (bits == 32) { + } else if (size == 4) { return data; + } else { + PANIC("bits must be 8, 16, 32"); } - __unreachable(); } } uint64 -m88200::load_8(uint32 addr) +m88200::load_1(uint32 addr) { - return load<8>(addr); + return load<1>(addr); } uint64 -m88200::load_16(uint32 addr) +m88200::load_2(uint32 addr) { - return load<16>(addr); + return load<2>(addr); } uint64 -m88200::load_32(uint32 addr) +m88200::load_4(uint32 addr) { - return load<32>(addr); + return load<4>(addr); } -template uint64 +template uint64 m88200::store(uint32 addr, uint32 data) { - uint64 rv; - acc_laddr = addr; acc_read = false; if (__predict_false(Translate() == false)) { return (uint64)-1; } + uint32 paddr = acc_paddrH + acc_paddrL; 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); + busdata bd = MBusWrite(paddr, data, size); MBusRelease(); - return rv; + if (__predict_false(bd.IsBusErr())) { + SetFault(FAULT_CODE_BUSERR, paddr); + return (uint64)-1; + } + return 0; } else { - return CacheWrite(acc_paddrH + acc_paddrL, data, bits / 8); + return CacheWrite(paddr, data, size); } } uint64 -m88200::store_8(uint32 addr, uint32 data) +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) +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) +m88200::store_4(uint32 addr, uint32 data) { - return store<32>(addr, data); + return store<4>(addr, data); } -template uint64 +template uint64 m88200::xmem(uint32 addr, uint32 data) { - uint64 rv; - acc_laddr = addr; acc_read = false; if (__predict_false(Translate() == false)) { return (uint64)-1; } + uint32 paddr = acc_paddrH + acc_paddrL; if (__predict_false((acc_stat & DESC_CI))) { // XXX キャッシュ禁止領域だとどうなる? MBusAcquire(); MBusMakeSnoop(acc_paddrH, 1); - rv = MBusXmem(acc_paddrH + acc_paddrL, data, bits / 8); + busdata bd = MBusXmem(paddr, data, size); MBusRelease(); - return rv; + if (__predict_false(bd.IsBusErr())) { + SetFault(FAULT_CODE_BUSERR, paddr); + return (uint64)-1; + } + return bd.Data(); } else { - return CacheXmem(acc_paddrH + acc_paddrL, data, bits / 8); + return CacheXmem(paddr, data, size); } } uint64 -m88200::xmem_8(uint32 addr, uint32 data) +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) +m88200::xmem_4(uint32 addr, uint32 data) { - return xmem<32>(addr, data); + return xmem<4>(addr, data); } // @@ -1288,6 +1295,33 @@ m88200::stat2str(uint32 stat) return buf.substr(1); } +// ATC ハッシュを表示。 +void +m88200::DumpATC() +{ + uint8 zero[16] {}; + + printf("DumpATC(#%u)\n", id); + for (uint i = 0; i < atc_hash_all.size(); i += 16) { + const uint8 *a = &atc_hash_all[i]; + if (memcmp(a, zero, 16) != 0) { + printf("#%u %c.%08x:", id, ((i >> 20) ? 'S' : 'U'), i << 12); + + for (int j = 0; j < 16; j++) { + uint8 n = a[j]; + if (__predict_true(n == 0)) { + printf(" "); + } else if (__predict_false((int8)n < 0)) { + printf("~%u ", (uint)(uint8)~n); + } else { + printf("%-2u ", n - 1); + } + } + printf("\n"); + } + } +} + // アドレス変換。 // 事前に acc_laddr, acc_super, acc_read を設定しておくこと。 // 成功すれば acc_paddrH, acc_paddrL をセットして true を返す。 @@ -1297,16 +1331,24 @@ bool m88200::Translate() { uint32 la; + uint8 n; // Logical address(LA) presented on PBus - putlog(3, "Translate %c:$%08x", (acc_super ? 'S' : 'U'), acc_laddr); + putlog(3, "Translate %c:$%08x", (IsSuper() ? 'S' : 'U'), acc_laddr); // 下位2ビットは常にこうなる。 acc_paddrL = acc_laddr & 3; // Select Area Descriptor if (SelectAreaDesc() == false) { + // 変換しない場合でも暗黙 BWP は有効…。 + // マッチまではここで独自に行い、マッチしたら下の BWP へ合流。 + if (__predict_false(acc_laddr >= 0xfff00000) && IsSuper()) { + n = (acc_laddr < 0xfff80000) ? 8 : 9; + goto batc; + } + // XXX 図のこれはたぶん間違いだよなあ… // 誤: Physical Address <= LA[18-2] :: 00 // 正: Physical Address <= LA[31-2] :: 00 @@ -1321,152 +1363,115 @@ m88200::Translate() // type == Valid ここから - // Search BATC first - - // まずざっくりハッシュを引く - uint32 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; - - do { - int i = __builtin_ctz(bi); - if (batc[i].lba == la) { - m88200BATC& b = batc[i]; - putlog(4, " BATC[%d] hit", i); - batc_hit[i]++; - if (b.wp && acc_IsWrite()) { - goto write_violation; - } - - // Physical Address <= PBA :: LA[18-2] :: 00 - acc_paddrH = b.pba | (acc_laddr & 0x0007fffc); - acc_stat |= b.stat; - putlog(4, " BATC hit acc=%s paddr=$%08x", - stat2str(acc_stat).c_str(), acc_paddrH); + // 論理アドレスで現在の S/U のハッシュを引く。 + // 実際にはまず BATC を引いて、なければ次に PATC を引くのだが、 + // BATC と PATC が同じアドレスを指すことはなく、BATC が同じところを + // 指すような指定は (出来るけど結果不定とマニュアルに書いてあるので) + // 起きないものとすると、アドレスと BATC もしくは PATC は 1:1 で + // マッピング出来るので、この全域を一回で検索する。 + la = acc_laddr >> 12; + try_again: + n = atc_hash[la]; + acc_patc = NULL; - parent->AddCycle(1); - return true; - } - // ヒットしたビットを 0 にする - bi &= bi - 1; - } while (bi); - } - // 見付からなかったので PATC へ - - // 2回しかループしないはず - for (int loop = 0; loop < 3; loop++) { - // BATC miss, search PATC then - acc_patc = NULL; + if (n == 0) { + // BATC, PATC どちらにも載ってない。 + atc_miss++; + // patc_miss に合流する + } else if ((int8)n < 0) { + // BATC #(~n) でヒットした。 + // n=0xff が BATC#0 を、n=0xf6 が BATC#9 を示す。 + n = ~n; + batc: + m88200BATC& b = batc[n]; + assert(b.IsValid()); + putlog(4, " BATC[%u] hit", n); + batc_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_stat |= b.stat; + putlog(4, " BATC hit acc=%s paddr=$%08x", + stat2str(acc_stat).c_str(), acc_paddrH); - uint64 pi = patc_hash[patc_hash_func(acc_laddr)]; -#if defined(M88200_STAT) - stat_patc_search++; -#endif + parent->AddCycle(1); + return true; + } else { + // PATC #(n-1) でヒットした。 + // n=1 が PATC#0 を、n=56 が PATC#55 を示す。 + n--; + m88200PATC& p = patc[n]; + assert(p.IsValid()); + patc_hit++; + + if (acc_IsWrite() && p.wp) { + putlog(4, " PATC[%u] hit; stat=%s m=%u wp=%u", n, + stat2str(p.stat).c_str(), p.m, p.wp); + goto write_violation; + } + if (acc_IsWrite() && p.m == false) { + // Update M bit + // フローチャートでは PATC[M] はこの後のテーブルサーチ中 + // の Update Page Descriptor 処理中で更新するように書いて + // あるように読めるのだが、その通りに実装すると、メモリ + // 上の Page Descriptor に最初から M ビットが立っていると + // (というか OpenBSD カーネルが用意した Page Descriptor + // には立っているので) PATC[M] の更新も行われず、PATC[M] + // が立っていないので再びここに来てしまい無限ループになる。 + // 誰が間違ってるのか分からないけど、とりあえずここで + // PATC[M] を立てれば問題は起きない。 + putlog(4, " PATC[%u] hit; Need to update M bit", n); + p.m = true; - if (pi == 0) { -#if defined(M88200_STAT) - // 単純なサーチミスのみカウントし、M bit による合流は除きたい - stat_patc_miss1++; -#endif + // この下の PATC miss に合流する + acc_patc = &p; goto patc_miss; } - la = acc_laddr & 0xfffff000; - la |= acc_super ? PATC_S : 0; - - do { - int i = __builtin_ctzll(pi); - m88200PATC& p = patc[i]; - 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); - goto write_violation; - } - if (acc_IsWrite() && p.m == false) { - // Update M bit - // フローチャートでは PATC[M] はこの後のテーブルサーチ中 - // の Update Page Descriptor 処理中で更新するように書いて - // あるように読めるのだが、その通りに実装すると、メモリ - // 上の Page Descriptor に最初から M ビットが立っていると - // (というか OpenBSD カーネルが用意した Page Descriptor - // には立っているので) PATC[M] の更新も行われず、PATC[M] - // が立っていないので再びここに来てしまい無限ループになる。 - // 誰が間違ってるのか分からないけど、とりあえずここで - // PATC[M] を立てれば問題は起きない。 - putlog(4, " PATC[%d] hit; Need to update M bit", i); - p.m = true; - - // この下の PATC miss に合流する - acc_patc = &p; - goto patc_miss; - } + // Physical Address <= PFA :: LA[11-2] :: 00 + acc_paddrH = p.pfa | (acc_laddr & 0x00000ffc); + acc_stat |= p.stat; + putlog(4, " PATC[%u] hit acc=%s paddr=$%08x", n, + stat2str(acc_stat).c_str(), acc_paddrH); - // Physical Address <= PFA :: LA[11-2] :: 00 - acc_paddrH = p.pfa | (acc_laddr & 0x00000ffc); - acc_stat |= p.stat; - putlog(4, " PATC[%d] hit acc=%s paddr=$%08x", i, - stat2str(acc_stat).c_str(), acc_paddrH); + parent->AddCycle(1); + return true; + } - parent->AddCycle(1); - return true; - } - // ヒットしたビットを 0 にする - pi &= pi - 1; - } while (pi); -#if defined(M88200_STAT) - // 単純なサーチミスのみカウントし、M bit による合流は除きたい - stat_patc_miss2++; -#endif + // PATC miss patc_miss: - putlog(4, " BATC/PATC miss"); - - // PATC miss - - // PATC にヒットして M bit 更新するものは両方カウントされるけど - atc_miss++; + putlog(4, " BATC/PATC miss"); - // フローチャートでは PATC miss というラベルが付いてるが、コード - // では acc_patc は PATC ループ前に NULL にしてあり(=miss)、 - // ループで PATC エントリが見付かれば acc_patc に代入してから - // break してここに来るので、ちょっとだけ見た目と違うが意図してる - // 動作は同じはず。 - - // Table search operation - if (TableSearch() == true) { - // フローチャートでは Select Area Descriptor まで戻るように - // 書いてあるが、PATC ミスによるテーブルサーチでは Area Descriptor - // と BATC は変化せず PATC が変化するだけなので、PATC サーチから - // やり直す。 - continue; - } + // PATC にヒットして M bit 更新するものは両方カウントされるけど + atc_miss++; - // Invalid - // Reply with fault (Figure2-10 だがここではすべて不要) - return false; + // Table search operation + if (TableSearch() == true) { + // テーブルサーチの結果、エントリが見付かって PATC が更新されたので + // もう一度やり直す。 + // フローチャートでは Select Area Descriptor まで戻るように書いて + // あるが、PATC ミスによるテーブルサーチでは Area Descriptor と BATC + // は変化しないので、Area Descriptor の検索は省略。 + goto try_again; } - PANIC("loop detected"); + + // Invalid + // Reply with fault (Figure2-10 だがここではすべて不要) + return false; write_violation: - fault_code = FAULT_CODE_WRITE; - //fault_addr UNKNOWN? + // XXX fault_addr 不明 + SetFault(FAULT_CODE_WRITE, 0xcccccccc); // Reply with fault (Figure2-10 だがここではすべて不要) return false; } - // Select Area Descriptor. // 結果が Type=Valid なら true、Type=Untranslated なら false を返す。 -// Probe Command からも呼ばれる。 +// Probe Command からも呼ばれる (はずだが未実装)。 // p2-21, Figure2-5 bool m88200::SelectAreaDesc() @@ -1568,28 +1573,27 @@ m88200::TableSearch() bool m88200::FetchSegmentDesc() { - uint64 data; + busdata data; MBusMakeSnoop(acc_sdaddr, 0); data = MBusRead(acc_sdaddr, 4); - if (__predict_false((int64)data < 0)) { + if (__predict_false(data.IsBusErr())) { + SetFault(FAULT_CODE_BUSERR, acc_sdaddr); return false; } - tmp_desc = (uint32)data; + tmp_desc = data.Data(); tmp_desc &= (0xfffff000 | 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; @@ -1612,28 +1616,27 @@ m88200::FetchSegmentDesc() bool m88200::FetchPageDesc() { - uint64 data; + busdata data; MBusMakeSnoop(acc_pdaddr, 0); data = MBusRead(acc_pdaddr, 4); - if (__predict_false((int64)data < 0)) { + if (__predict_false(data.IsBusErr())) { + SetFault(FAULT_CODE_BUSERR, acc_pdaddr); return false; } - tmp_desc = (uint32)data; + tmp_desc = data.Data(); tmp_desc &= (0xfffff000 | 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; + SetFault(FAULT_CODE_PAGE, acc_pdaddr); putlog(4, " PD $%08x desc=$%08x PageFault", acc_pdaddr, 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); putlog(4, " PD $%08x desc=$%08x SupervisorFault", acc_pdaddr, tmp_desc); return false; @@ -1642,9 +1645,8 @@ m88200::FetchPageDesc() acc_stat |= tmp_desc & ACC_STAT_MASK; if (__predict_false((acc_stat & DESC_WP) && acc_IsWrite())) { - fault_code = FAULT_CODE_WRITE; // fault_addr は invalid data - fault_addr = 0xdeadbeef; + SetFault(FAULT_CODE_WRITE, 0xcccccccc); putlog(4, " PD $%08x desc=$%08x WriteFault", acc_pdaddr, tmp_desc); return false; } else { @@ -1662,7 +1664,7 @@ m88200::FetchPageDesc() bool m88200::UpdatePageDesc() { - uint64 rv; + busdata bd; if ((tmp_desc & DESC_M) == 0 && acc_IsWrite()) { // Update Modified bit and accrue status @@ -1678,8 +1680,9 @@ m88200::UpdatePageDesc() tmp_desc |= DESC_U; MBusMakeSnoop(acc_pdaddr, 1); - rv = MBusWrite(acc_pdaddr, tmp_desc, 4); - if (__predict_false((int64)rv < 0)) { + bd = MBusWrite(acc_pdaddr, tmp_desc, 4); + if (__predict_false(bd.IsBusErr())) { + SetFault(FAULT_CODE_BUSERR, acc_pdaddr); return false; } putlog(4, " Update PD $%08x desc=$%08x stat=%s", @@ -1688,19 +1691,27 @@ m88200::UpdatePageDesc() return true; } -// patc[patc_index] に対応する PATC ハッシュを無効にする +// 指定の PATC を無効にする。 void -m88200::InvalidatePATCHash(int patc_index) +m88200::InvalidatePATCEntry(m88200PATC& p) { - m88200PATC& p = patc[patc_index]; - int hashkey = patc_hash_func(p.lpa); + uint32 la = addr2hash(p.IsS(), p.lpa); - uint64 bit = 1ULL << patc_index; - if (p.IsS()) { - patc_hash_s[hashkey] &= ~bit; - } else { - patc_hash_u[hashkey] &= ~bit; + if (p.IsValid()) { + uint8 n = atc_hash_all[la]; + // - BATC (n<0) なら、atc_hash は触らず PATC エントリだけ消す。 + // この PATC が作成された後にここに BATC がセットされたとかなので。 + // - PATC (n>0) なら (自分のはずなので) atc_hash ごと消す。 + if ((int8)n > 0) { + atc_hash_all[la] = 0; + } } + //DumpATC(); + + p.lpa |= PATC_INVALID; +#if defined(M88200_STAT) + stat_patc_invalidate++; +#endif } // Create PATC Entry @@ -1709,13 +1720,9 @@ m88200::InvalidatePATCHash(int patc_inde void m88200::CreatePATCEntry() { - int hashkey; - // 空きがあろうがなかろうが、とにかく一番古いエントリを使う。 // おそらく実機も空きエントリを前に詰めるような複雑な処理はして // ないだろうという推測。 - // 検索はハッシュを使って実装してあるため、途中に空きエントリが - // あっても (線形探索する時のような) デメリットはない。 #if defined(M88200_STAT) stat_patc_create++; @@ -1723,14 +1730,14 @@ m88200::CreatePATCEntry() m88200PATC& p = patc[patc_next]; // 有効なら無効化 if (p.IsValid()) { - InvalidatePATCHash(patc_next); + InvalidatePATCEntry(p); } // このエントリを潰して作る memset(&p, 0, sizeof(p)); p.lpa = acc_laddr & 0xfffff000; p.pfa = tmp_desc & 0xfffff000; - if (acc_super) { + if (IsSuper()) { p.lpa |= PATC_S; } p.stat = acc_stat; @@ -1738,9 +1745,10 @@ m88200::CreatePATCEntry() p.IsS() ? 'S' : 'U', (p.lpa & 0xfffff000), p.pfa, stat2str(p.stat).c_str()); - hashkey = patc_hash_func(p.lpa); - uint64 bit = 1ULL << patc_next; - patc_hash[hashkey] |= bit; + // ハッシュの値は PATC#0 が 1。 + uint32 la = p.lpa >> 12; + atc_hash[la] = patc_next + 1; + //DumpATC(); patc_next++; if (__predict_false(patc_next >= patc.size())) { @@ -1749,10 +1757,12 @@ m88200::CreatePATCEntry() } // アドレス変換 (デバッガ用) -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; // Select Area Descriptor @@ -1762,31 +1772,24 @@ m88200::TranslatePeek(uint32 laddr, bool xapr = &uapr; } if (xapr->enable == 0) { - return laddr; + return busaddr(laddr); } - // Search BATC first - la = laddr & 0xfff80000; - la |= issuper ? BATC_S : 0; - for (const auto& b : batc) { - if (b.lba == la) { - // BATC hit - return b.pba | (laddr & 0x0007ffff); - } - } - - // BATC miss, search PATC then - la = laddr & 0xfffff000; - la |= issuper ? PATC_S : 0; - for (const auto& p : patc) { - if (p.lpa == la) { - // PATC hit - return p.pfa | (laddr & 0x00000fff); - } - } + la = addr2hash(issuper, laddr); + uint8 n = atc_hash_all[la]; - if (do_search == false) { - return (uint64)-1; + if (n == 0) { + // FALLTHROUGH + } else if ((int8)n < 0) { + // BATC #(~n) hit + n = ~n; + const m88200BATC& b = batc[n]; + return busaddr(b.pba | (laddr & 0x0007ffff)); + } else { + // BATC #(n-1) hit + n--; + const m88200PATC& p = patc[n]; + return busaddr(p.pfa | (laddr & 0x00000fff)); } // ここからテーブルサーチ @@ -1796,33 +1799,35 @@ m88200::TranslatePeek(uint32 laddr, bool // Peek Segment Descriptor desc_addr = xapr->addr | ((laddr >> 20) & ~3U); - data = vm_phys_peek_32(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 = vm_phys_peek_32(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; } - return (desc & 0xfffff000) | (laddr & 0x00000fff); + busaddr r((desc & 0xfffff000) | (laddr & 0x00000fff)); + r |= BusAddr::TableSearched; + return r; } @@ -1832,7 +1837,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)) { @@ -1920,7 +1925,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; @@ -1976,65 +1981,53 @@ m88200CacheSet::Lookup(uint32 tagaddr) c // 成功すれば 0 を返す。 // バスエラーなら fault_code, fault_addr をセットし (uint64)-1 を返す。 uint64 -m88200::ReadLine(m88200CacheSet& set, int line, uint32 tagaddr) +m88200::ReadLine(m88200CacheSet& set, uint line, uint32 tagaddr) { - uint32 addr; - uint64 data; - - // キャッシュラインの充填は実機では Burst Read されているらしいが - // nono では Burst Read 用のメソッドは用意しておらず通常の 32bit - // アクセスをしているため、このままでは所要ウェイト数が正しくない。 - // そこでここで差を調整することにする。 - // vm_phys_read_32() 4回なのでこれによって 12 wait が加算される。一方 - // 実機の Burst Read は取扱説明書によると 320ns (8 サイクル) で完了 - // するようなので、その差 4 をあらかじめ引いておく。 - // 途中でバスエラーが起きたらとかそこまでは考慮しない。 - AddCycle(-4); - // タグを更新 set.tag[line] = tagaddr; - addr = set.tag[line] | (set.setidx << 4); - for (int i = 0; i < 4; i++) { - data = vm_phys_read_32(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 0; + } else { + addr |= BusAddr::Size4; + for (int 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 (uint64)-1; + } + set.word[line * 4 + i] = bd.Data(); + addr += 4; } - set.word[line * 4 + i] = data; - addr += 4; } return 0; } -// キャッシュの指定の set, line を1ライン、メモリに書き出す(コピーバック)。 +// キャッシュの指定の set, line の1ライン(4word) を書き出す (コピーバック)。 // 成功すれば 0 を返す。 // バスエラーなら fault_code, fault_addr をセットし (uint64)-1 を返す。 uint64 -m88200::CopyBackLine(m88200CacheSet& set, int line) +m88200::CopyBackLine(m88200CacheSet& set, uint line) { - uint32 addr; - - // キャッシュラインの書き出しは実機では Burst Write されているらしいが - // nono では Burst Write 用のメソッドは用意しておらず通常の 32bit - // アクセスをしているため、このままでは所要ウェイト数が正しくない。 - // そこでここで差を調整することにする。 - // vm_phys_write_32() 4回なのでこれによって 8 wait が加算される。一方 - // 実機の Burst Write は取扱説明書によると 280ns (7サイクル) で完了する - // ようなので、その差 1 をあらかじめて引いておく。 - // 途中でバスエラーが起きたらとかそこまでは考慮しない。 - AddCycle(-1); - - addr = set.tag[line] | (set.setidx << 4); - for (int i = 0; i < 4; i++) { - uint64 rv = vm_phys_write_32(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 0; + } else { + addr |= BusAddr::Size4; + for (int i = 0; i < 4; i++) { + busdata bd = mainbus->Write(addr, set.word[line * 4 + i]); + if (__predict_false(bd.IsBusErr())) { + SetFault(FAULT_CODE_BUSERR, addr.Addr()); + return (uint64)-1; + } + addr += 4; } - addr += 4; } return 0; } @@ -2063,7 +2056,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; } @@ -2077,7 +2070,7 @@ 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 @@ -2102,7 +2095,7 @@ 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]; @@ -2118,9 +2111,10 @@ m88200::CacheRead(uint32 paddr) // エラー要因は fault_code とか参照。 // p.3-10 Figure 3-5 uint64 -m88200::CacheWrite(uint32 paddr, uint32 data, int size) +m88200::CacheWrite(uint32 paddr, uint32 data, uint size) { int line; + busdata bd; uint64 rv; assert((paddr & (size - 1)) == 0); @@ -2148,7 +2142,7 @@ 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 @@ -2169,8 +2163,9 @@ m88200::CacheWrite(uint32 paddr, uint32 } // Write data to memory - rv = MBusWrite(paddr, data, size); - if ((int64)rv < 0) { + bd = MBusWrite(paddr, data, size); + if (__predict_false(bd.IsBusErr())) { + SetFault(FAULT_CODE_BUSERR, paddr); goto error; } @@ -2178,7 +2173,15 @@ m88200::CacheWrite(uint32 paddr, uint32 set.Write(line, paddr, data, size); // Mark line exclusive unmodified - set.Update(line, m88200CacheSet::EU); + // 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 にならないと矛盾する。 + if ((acc_stat & DESC_WT)) { + set.Update(line, m88200CacheSet::SU); + } else { + set.Update(line, m88200CacheSet::EU); + } set.Use(line); MBusRelease(); @@ -2193,15 +2196,13 @@ m88200::CacheWrite(uint32 paddr, uint32 // 書き込めれば 0、エラーが起きれば (uint64)-1 を返す。 // p3-11 Figure 3-6 uint64 -m88200::CacheWriteHit(m88200CacheSet& set, int line, - uint32 paddr, uint32 data, int size) +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))) { @@ -2213,10 +2214,11 @@ m88200::CacheWriteHit(m88200CacheSet& se // Write data to memory MBusMakeSnoop(paddr, 1); - rv = MBusWrite(paddr, data, size); - if ((int64)rv < 0) { + busdata bd = MBusWrite(paddr, data, size); + if (__predict_false(bd.IsBusErr())) { MBusRelease(); - return rv; + SetFault(FAULT_CODE_BUSERR, paddr); + return (uint64)-1; } if ((acc_stat & DESC_WT)) { @@ -2235,11 +2237,11 @@ m88200::CacheWriteHit(m88200CacheSet& se // どちらでもない場合は 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 @@ -2252,9 +2254,10 @@ m88200::CacheWriteHit(m88200CacheSet& se // 成功すれば読み出した値、エラーが起きれば (uint64)-1 を返す。 // p3-13, Figure3-7 uint64 -m88200::CacheXmem(uint32 paddr, uint32 data, int size) +m88200::CacheXmem(uint32 paddr, uint32 data, uint size) { int line; + busdata bd; uint64 rv; assert((paddr & (size - 1)) == 0); @@ -2269,7 +2272,7 @@ m88200::CacheXmem(uint32 paddr, uint32 d 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); + putlog(4, " CacheXmem hit and EM (line=%u)", line); MBusAcquire(); @@ -2286,7 +2289,7 @@ m88200::CacheXmem(uint32 paddr, uint32 d if (line >= 0) { // Cache Hit and Otherwise (= !EM) - putlog(4, " CacheXmem hit but !EM (line=%d)", line); + putlog(4, " CacheXmem hit but !EM (line=%u)", line); set.Update(line, m88200CacheSet::IV); } else { putlog(4, " CacheXmem miss"); @@ -2296,22 +2299,19 @@ m88200::CacheXmem(uint32 paddr, uint32 d } MBusMakeSnoop(paddr, 1); - rv = MBusXmem(paddr, data, size); + bd = MBusXmem(paddr, data, size); MBusRelease(); - return rv; + if (__predict_false(bd.IsBusErr())) { + SetFault(FAULT_CODE_BUSERR, paddr); + return (uint64)-1; + } + return bd.Data(); error: MBusRelease(); return rv; } -// サイクルを加算する (VM から使う) -void -m88200::AddCycle(int32 cycle) -{ - parent->AddCycle(cycle); -} - // MBus 使用権を取得する void m88200::MBusAcquire() @@ -2330,7 +2330,7 @@ m88200::MBusAcquire() // いる他 CMMU が必要に応じて反応するのだが、ここでは全部マスタ主導で行う。 // Figure 3-8, 3-9 void -m88200::MBusMakeSnoop(uint32 paddr, int im) +m88200::MBusMakeSnoop(uint32 paddr, uint im) { if ((acc_stat & DESC_G) == 0) { return; @@ -2360,7 +2360,7 @@ m88200::MBusMakeSnoop(uint32 paddr, int // こっちはそのマスタから呼ばれるスレーブ側。 // Figure 3-8, 3-9 void -m88200::Snoop(uint32 paddr, int im) +m88200::Snoop(uint32 paddr, uint im) { assert((sctr & SCTR_SE));