--- nono/m88xx0/m88200.cpp 2026/04/29 17:04:38 1.1.1.2 +++ nono/m88xx0/m88200.cpp 2026/04/29 17:04:46 1.1.1.5 @@ -10,12 +10,15 @@ #include "m88200.h" #include "mystring.h" +// static 変数 +m88200 *m88200::mbus_master = NULL; + // m88200 コンストラクタ m88200::m88200() { // logname, devname は Init() で確定する - monitor_size = nnSize(48, 8); + monitor_size = nnSize(52, 8); // ログ表示用の名前 sapr.name = "SAPR"; @@ -48,13 +51,16 @@ m88200::Ctor(m88kcpu *parent_) void m88200::Reset() { + const uint32 Undefined = 0xcccccccc; + // レジスタ (p6-4, Table6-3) + // ID, version は別途 Set される。 command = 0; ssr = 0; - sar = 0xdeadbeef; // undefined + sar = Undefined; sctr = 0; fault_code = 0; - fault_addr = 0xdeadbeef; // undefined + fault_addr = Undefined; sapr.enable = false; sapr.stat = APR_CI; uapr.enable = false; @@ -64,19 +70,22 @@ m88200::Reset() void m88200::MonitorUpdate(TextScreen& monitor) { + uint32 baseaddr; uint32 reg; int x; int y; + baseaddr = 0xfff00000 + (id << 12); + monitor.Clear(); - x = 20; + x = 24; y = 0; - monitor.Print(0, y++, "+$000 IDR:%08x ID=$%02x Type=5(88200) Ver=$%x", - GetIDR(), id, version); + monitor.Print(0, y++, "$%08x IDR:%08x ID=$%02x Type=5(88200) Ver=$%x", + baseaddr + 0, GetIDR(), id, version); reg = GetSSR(); - monitor.Print(0, y, "+$008 SSR:%08x", reg); + monitor.Print(0, y, "$%08x SSR:%08x", baseaddr + 0x008, reg); monitor.Puts(x, y, TA::OnOff(reg & SSR_CE), "CE"); monitor.Puts(x + 3, y, TA::OnOff(reg & SSR_BE), "BE"); monitor.Puts(x + 6, y, TA::OnOff(reg & SSR_WT), "WT"); @@ -90,10 +99,10 @@ m88200::MonitorUpdate(TextScreen& monito monitor.Puts(x + 27, y, TA::OnOff(reg & SSR_V), "V"); y++; - monitor.Print(0, y++, "+$00c SAR:%08x", GetSAR()); + monitor.Print(0, y++, "$%08x SAR:%08x", baseaddr + 0x00c, GetSAR()); reg = GetSCTR(); - monitor.Print(0, y, "+$104 SCTR:%08x", GetSCTR()); + monitor.Print(0, y, "$%08x SCTR:%08x", baseaddr + 0x104, GetSCTR()); monitor.Puts(x, y, TA::OnOff(reg & SCTR_PE), "PE"); monitor.Puts(x + 3, y, TA::OnOff(reg & SCTR_SE), "SE"); monitor.Puts(x + 6, y, TA::OnOff(reg & SCTR_PR), "PR"); @@ -110,14 +119,16 @@ m88200::MonitorUpdate(TextScreen& monito "Supervisor Violation", "Write Violation", }; - monitor.Print(0, y++, "+$108 PFSR:%08x %s", reg, codestr[fault_code]); + monitor.Print(0, y++, "$%08x PFSR:%08x %s", baseaddr + 0x108, + reg, codestr[fault_code]); - monitor.Print(0, y++, "+$10c PFAR:%08x", GetPFAR()); + monitor.Print(0, y++, "$%08x PFAR:%08x", baseaddr + 0x10c, GetPFAR()); for (int i = 0; i < 2; i++) { int s = 1 - i; reg = GetAPR(s); - monitor.Print(0, y, "+$20%d %cAPR:%08x", i * 4, s ? 'S' : 'U', reg); + monitor.Print(0, y, "$%08x %cAPR:%08x", baseaddr + 0x200 + i * 4, + s ? 'S' : 'U', reg); monitor.Print(x, y, "STBA=%05x'000 %c%c%c%c", reg >> 12, (reg & APR_WT) ? 'T' : '-', @@ -170,7 +181,7 @@ m88200::MonitorCacheSet(TextScreen& s, i break; } s.Print(0, y, attr, "%d", line); - static const char * const statusstr[] = { "EU", "EM", "SU", "IV" }; + static const char statusstr[][4] = { "EU", "EM", "SU", "IV" }; s.Print(2, y, attr, "$%05x'%03x %s", (set.tag[line] >> 12), (setidx << 4), @@ -236,7 +247,7 @@ m88200::MonitorCacheOverview(TextScreen& for (int i = 0; i < setarray.size(); i++) { const auto& set = setarray[i]; - const char * const str = "EMS-"; + const char str[] = "EMS-"; int col = i % 16; int row = i / 16; int x; @@ -425,19 +436,45 @@ m88200::FlushCache() switch (gg) { case GG_LINE: addr &= 0xfffffffc; + parent->AddCycle(1); // Table.6-1 break; case GG_PAGE: addr &= 0xfffff000; + parent->AddCycle(256); // Table.6-1 break; case GG_SEG: addr &= 0xffc00000; + parent->AddCycle(1024); // Table.6-1 break; case GG_ALL: + parent->AddCycle(1024); // Table.6-1 break; default: __unreachable(); } + // サイクル(基本部分) Table.6-1 + 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(); + } + } + 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(); + } + } + // XXX ループ中のサイクル数は正しくないかも。よく分からん + for (auto& set : setarray) { for (int line = 0; line < 4; line++) { // 条件にマッチするか @@ -464,16 +501,20 @@ m88200::FlushCache() // Copyback ならまず EM なエントリを書き戻す。 if (copyback) { if (set.vv[line] == m88200CacheSet::EM) { - set.CopyBackLine(line); + cycle += 7; // Table.6-1 + CopyBackLine(set, line); } } // Invalidate なら無効化する if (invalidate) { + cycle += 1; // Table.6-1 set.Update(line, m88200CacheSet::IV); } } } + + parent->AddCycle(cycle); } // command に応じて PATC を無効化する。SetSCR() の下請け。 @@ -796,16 +837,22 @@ m88200::load(uint32 addr) acc_laddr = addr; acc_read = true; if (Translate() == false) { + putlog(2, "Translation BusError %c:$%08x", (acc_super ? 'S' : 'U'), acc_laddr); return (uint64)-1; } if ((acc_stat & DESC_CI)) { + parent->AddCycle(7); // Table.6-2 MBusAcquire(); data = MBusRead(acc_paddrH + acc_paddrL, bits / 8); MBusRelease(); + if ((int64)data < 0) { + putlog(2, "MBus BusError %c:$%08x", (acc_super ? 'S' : 'U'), acc_laddr); + } return data; } else { data = CacheRead(acc_paddrH); if ((int64)data < 0) { + putlog(2, "Cache BusError %c:$%08x", (acc_super ? 'S' : 'U'), acc_laddr); return data; } @@ -866,13 +913,17 @@ m88200::store(uint32 addr, uint32 data) return (uint64)-1; } if ((acc_stat & DESC_CI)) { + parent->AddCycle(7); // Table.6-2 MBusAcquire(); rv = MBusWrite(acc_paddrH + acc_paddrL, data, bits / 8); MBusRelease(); return rv; } else { - if (DEBUG_WT) + if (DEBUG_WT) { + MBusAcquire(); MBusWrite(acc_paddrH + acc_paddrL, data, bits / 8); + MBusRelease(); + } return CacheWrite(acc_paddrH + acc_paddrL, data, bits / 8); } } @@ -923,12 +974,6 @@ m88200::xmem_8(uint32 addr, uint32 data) } uint64 -m88200::xmem_16(uint32 addr, uint32 data) -{ - return xmem<16>(addr, data); -} - -uint64 m88200::xmem_32(uint32 addr, uint32 data) { return xmem<32>(addr, data); @@ -984,11 +1029,12 @@ m88200::Translate() for (int loop = 0; loop < 3; loop++) { // Select Area Descriptor if (SelectAreaDesc() == false) { - // XXX 図には - // Physical Address <= LA[18-2] :: 00 - // と書いてあるけどたぶんこうだよなあ... - // Physical Address <= LA[31-2] :: 00 + // XXX 図のこれはたぶん間違いだよなあ… + // 誤: Physical Address <= LA[18-2] :: 00 + // 正: Physical Address <= LA[31-2] :: 00 acc_paddrH = acc_laddr - acc_paddrL; + + parent->AddCycle(1); return true; } @@ -1011,6 +1057,8 @@ m88200::Translate() acc_stat |= b.stat; putlog(4, " BATC hit acc=%s paddr=$%08x", stat2str(acc_stat).c_str(), acc_paddrH); + + parent->AddCycle(1); return true; } } @@ -1052,6 +1100,8 @@ m88200::Translate() 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; } } @@ -1132,11 +1182,15 @@ m88200::TableSearch() if (FetchSegmentDesc() == false) { MBusRelease(); + + parent->AddCycle(7); // Table.6-2 return false; } if (FetchPageDesc() == false) { MBusRelease(); + + parent->AddCycle(11); // Table.6-2 return false; } @@ -1166,10 +1220,14 @@ m88200::TableSearch() if ((tmp_desc & DESC_U) == 0 || ((tmp_desc & DESC_M) == 0 && acc_IsWrite())) { + parent->AddCycle(15); // Table.6-2 + if (UpdatePageDesc() == false) { MBusRelease(); return false; } + } else { + parent->AddCycle(11); // Table.6-2 } if (acc_patc == NULL) { @@ -1493,48 +1551,10 @@ m88200CacheSet::TryGetOldestLine(int tmp int m88200CacheSet::SelectOldestLine() const { -#if 0 - int line; - - // Invalid にしたときに順序を逆回しすればこの比較は不要? - - // Invalid line があればそれを選択 - for (line = 0; line < 4; line++) { - if (vv[line] == Status::IV) { - return line; - } - } -#endif - // なければ、最も古いラインを差し出す return TryGetOldestLine(L); } -// 指定の line に1ライン(4word)読み込む。 -// 成功すれば 0 を返す。 -// バスエラーなら (uint64)-1 を返す。この場合呼び出し側で fault_code, -// fault_addr を設定すること。(ここは m88200 クラスではないのでセットできない) -uint64 -m88200CacheSet::ReadLine(int line, uint32 tagaddr) -{ - uint32 addr; - uint64 data; - - // タグを更新 - tag[line] = tagaddr; - - addr = tag[line] | (setidx << 4); - for (int i = 0; i < 4; i++) { - data = vm_phys_read_32(addr); - if ((int64)data < 0) { - return data; - } - word[line * 4 + i] = data; - addr += 4; - } - return 0; -} - // キャッシュの指定の line, word に data を書き込む。 // size は 1, 2, 4 バイト。 void @@ -1576,41 +1596,87 @@ m88200CacheSet::Write(int line, uint32 p word[line * 4 + wordidx] = data32; } -// 指定の set,line をコピーバックする。 +// キャッシュを検索。 +// ヒットすれば line 番号(0..3) を返す。ヒットしなければ -1 を返す。 +int +m88200CacheSet::Lookup(uint32 tagaddr) const +{ + for (int line = 0; line < 4; line++) { + if (tag[line] == tagaddr) { + return line; + } + } + return -1; +} + + +// キャッシュの指定の set, line にメモリから 1 ライン(4word) 読み込む。 // 成功すれば 0 を返す。 -// バスエラーなら (uint64)-1 を返す。この場合呼び出し側で fault_code, -// fault_addr を設定すること。(ここは m88200 クラスではないのでセットできない) +// バスエラーなら fault_code, fault_addr をセットし (uint64)-1 を返す。 uint64 -m88200CacheSet::CopyBackLine(int line) +m88200::ReadLine(m88200CacheSet& set, int 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 = tag[line] | (setidx << 4); + addr = set.tag[line] | (set.setidx << 4); for (int i = 0; i < 4; i++) { - uint64 rv = vm_phys_write_32(addr, word[line * 4 + i]); - if ((int64)rv < 0) { - return rv; + data = vm_phys_read_32(addr); + if ((int64)data < 0) { + fault_code = FAULT_CODE_BUSERR; + fault_addr = addr; + return data; } + set.word[line * 4 + i] = data; addr += 4; } return 0; } -// キャッシュを検索。 -// ヒットすれば line 番号(0..3) を返す。ヒットしなければ -1 を返す。 -int -m88200CacheSet::Lookup(uint32 tagaddr) const +// キャッシュの指定の set, line を1ライン、メモリに書き出す(コピーバック)。 +// 成功すれば 0 を返す。 +// バスエラーなら fault_code, fault_addr をセットし (uint64)-1 を返す。 +uint64 +m88200::CopyBackLine(m88200CacheSet& set, int line) { - for (int line = 0; line < 4; line++) { - if (tag[line] == tagaddr) { - return 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 ((int64)rv < 0) { + fault_code = FAULT_CODE_BUSERR; + fault_addr = addr; + return rv; } + addr += 4; } - return -1; + return 0; } - - // キャッシュに対して paddr の読み込みを行う。 // paddr は 32bit 境界のアドレスであること。 // 読み込めれば該当の32bitワードを返す。 @@ -1636,11 +1702,14 @@ m88200::CacheRead(uint32 paddr) if (line >= 0) { // Cache Hit putlog(4, " CacheRead hit (line=%d,word=%d)", line, wordidx); + parent->AddCycle(1); goto success; } // Cache Miss + parent->AddCycle(10); // Table.6-2 + // reply <- Wait MBusAcquire(); @@ -1649,7 +1718,8 @@ m88200::CacheRead(uint32 paddr) putlog(4, " CacheRead miss (replace line=%d)", line); if (set.vv[line] == m88200CacheSet::EM) { - rv = set.CopyBackLine(line); + parent->AddCycle(7); // Table.6-2 + rv = CopyBackLine(set, line); if ((int64)rv < 0) { goto error; } @@ -1659,7 +1729,7 @@ m88200::CacheRead(uint32 paddr) set.Update(line, m88200CacheSet::IV); // Read line from memory - rv = set.ReadLine(line, tagaddr); + rv = ReadLine(set, line, tagaddr); if ((int64)rv < 0) { goto error; } @@ -1676,8 +1746,6 @@ m88200::CacheRead(uint32 paddr) error: MBusRelease(); - fault_code = FAULT_CODE_BUSERR; - fault_addr = paddr; return (uint64)-1; } @@ -1704,11 +1772,14 @@ m88200::CacheWrite(uint32 paddr, uint32 line = set.Lookup(tagaddr); if (line >= 0) { // Cache Hit + parent->AddCycle(1); return CacheWriteHit(set, line, paddr, data, size); } // Cache Miss + parent->AddCycle(14); // Table.6-2 + // reply <- Wait MBusAcquire(); @@ -1717,7 +1788,8 @@ m88200::CacheWrite(uint32 paddr, uint32 putlog(4, " CacheWrite miss (replace line=%d)", line); if (set.vv[line] == m88200CacheSet::EM) { - rv = set.CopyBackLine(line); + parent->AddCycle(7); // Table.6-2 + rv = CopyBackLine(set, line); if ((int64)rv < 0) { goto error; } @@ -1727,7 +1799,7 @@ m88200::CacheWrite(uint32 paddr, uint32 set.Update(line, m88200CacheSet::IV); // Read line with intent to modify - rv = set.ReadLine(line, tagaddr); + rv = ReadLine(set, line, tagaddr); if ((int64)rv < 0) { goto error; } @@ -1735,7 +1807,7 @@ m88200::CacheWrite(uint32 paddr, uint32 // Write data to memory rv = MBusWrite(paddr, data, size); if ((int64)rv < 0) { - return rv; + goto error; } // Write data into cache @@ -1750,8 +1822,6 @@ m88200::CacheWrite(uint32 paddr, uint32 error: MBusRelease(); - fault_code = FAULT_CODE_BUSERR; - fault_addr = paddr; return (uint64)-1; } @@ -1839,7 +1909,8 @@ m88200::CacheXmem(uint32 paddr, uint32 d MBusAcquire(); - rv = set.CopyBackLine(line); + parent->AddCycle(7); // Table.6-2 + rv = CopyBackLine(set, line); if ((int64)rv < 0) { goto error; } @@ -1867,3 +1938,23 @@ m88200::CacheXmem(uint32 paddr, uint32 d MBusRelease(); return rv; } + +// サイクルを加算する (VM から使う) +void +m88200::AddCycle(int32 cycle) +{ + parent->AddCycle(cycle); +} + +// MBus 使用権を取得する +void +m88200::MBusAcquire() +{ + // アービトレーションのたびに1クロックかかる(?) + // よく分からんけど、とりあえず、CMMU は一度所有権を持ったら放さない、 + // 別の CMMU がバスリクエストすると所有権はその CMMU に移る、とする。 + if (mbus_master != this) { + mbus_master = this; + parent->AddCycle(1); + } +}