Annotation of nono/m88xx0/m88200.cpp, revision 1.1.1.16

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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.