Annotation of nono/vm/scsibus.cpp, revision 1.1.1.11

1.1       root        1: //
                      2: // nono
1.1.1.3   root        3: // Copyright (C) 2020 nono project
                      4: // Licensed under nono-license.txt
1.1       root        5: //
                      6: 
1.1.1.10  root        7: //
1.1       root        8: // SCSI バス
1.1.1.10  root        9: //
1.1       root       10: 
                     11: #include "scsibus.h"
                     12: #include "scsidev.h"
1.1.1.10  root       13: #include "scheduler.h"
1.1       root       14: 
                     15: // time 時間後に呼び出すコールバックをセットする。
                     16: // func の書式が面倒なのを省略して書きたいため。
                     17: #define CallAfter(func_, name_, time_) do { \
1.1.1.10  root       18:        event.func = ToEventCallback(&SCSIBus::func_); \
1.1       root       19:        event.time = time_; \
1.1.1.10  root       20:        event.SetName(name_); \
1.1.1.11! root       21:        scheduler->RestartEvent(event); \
1.1       root       22: } while (0)
                     23: 
                     24: // コンストラクタ
                     25: SCSIBus::SCSIBus()
1.1.1.11! root       26:        : inherited(OBJ_SCSIBUS)
1.1       root       27: {
1.1.1.10  root       28:        event.Regist("SCSIBus");        // 複数出来たら名前どうするべか
1.1       root       29: }
                     30: 
                     31: // デストラクタ
                     32: SCSIBus::~SCSIBus()
                     33: {
                     34: }
                     35: 
1.1.1.10  root       36: // 電源オン/リセット
1.1.1.4   root       37: void
1.1.1.10  root       38: SCSIBus::ResetHard(bool poweron)
1.1.1.4   root       39: {
1.1.1.10  root       40:        if (poweron) {
                     41:                phase = SCSI::Phase::BusFree;
                     42:                rst = false;
                     43:                req = false;
                     44:                ack = false;
                     45:                atn = false;
                     46:                sel = 0;
                     47:                bsy = 0;
                     48:                xfer = 0;
                     49:                data = 0;
                     50: 
                     51:                last_req_time = 0;
                     52:                req_wait = 0;
                     53:                init_id = -1;
                     54:                target_id = -1;
                     55:        }
                     56: 
1.1.1.4   root       57:        // ここでのイベントは実際にはイニシエータかターゲットのどちらかが
                     58:        // 時間を伴う処理をしている最中であることを示しているものなので、
                     59:        // イニシエータがリセットされたこととバスの動作とは本来関係ないのだが
                     60:        // ここでは特にイニシエータに次のバイトを転送させる、という目的で
                     61:        // タイマーイベントが使われており、本体をリセットしたのに入れ違いで
                     62:        // イニシエータの転送処理が呼び出されてしまうというような事故を
                     63:        // 防ぐためにもここでタイマーを停止する。
1.1.1.11! root       64:        scheduler->StopEvent(event);
1.1.1.4   root       65: }
                     66: 
1.1       root       67: // このバスにデバイスを接続する (初期化時に使う)
                     68: bool
                     69: SCSIBus::Attach(SCSIDevice *dev, int id)
                     70: {
                     71:        if (device[id] != NULL) {
                     72:                putlog(0, "id=%d already exist?", id);
                     73:                return false;
                     74:        }
                     75:        putlog(3, "Attach #%d", id);
                     76: 
                     77:        // バスとデバイスは相互に接続する。どちらも所有のないただのポインタ。
                     78:        device[id] = dev;
                     79:        dev->Attach(this, id);
                     80: 
                     81:        return true;
                     82: }
                     83: 
                     84: // RST を上げる
                     85: void
                     86: SCSIBus::AssertRST()
                     87: {
                     88:        putlog(4, "%s", __func__);
                     89: 
                     90:        // すでに上がってたら何もしない
                     91:        if (rst) {
                     92:                return;
                     93:        }
                     94: 
                     95:        rst = true;
                     96: 
                     97:        // 即バスリセットを全員に通知
                     98:        for (int i = 0; i < 8; i++) {
                     99:                if (device[i]) {
                    100:                        device[i]->BusResetCB();
                    101:                }
                    102:        }
                    103: 
                    104:        // コールバック終了後に内部状態もクリア
                    105:        init_id = -1;
                    106:        target_id = -1;
                    107: }
                    108: 
                    109: // RST を下げる
                    110: void
                    111: SCSIBus::NegateRST()
                    112: {
                    113:        putlog(4, "%s", __func__);
                    114: 
                    115:        rst = false;
                    116: 
                    117:        // 特にすることはない
                    118: }
                    119: 
                    120: // SEL を上げる
                    121: void
                    122: SCSIBus::AssertSEL(uint id)
                    123: {
                    124:        putlog(4, "%s(#%d)", __func__, id);
                    125: 
                    126:        // すでに上がってたら何もしない
                    127:        if ((sel & (1 << id))) {
                    128:                return;
                    129:        }
                    130: 
                    131:        sel |= (1 << id);
                    132: 
                    133:        if (GetPhase() == SCSI::Phase::BusFree) {
                    134:                // バスフリーフェーズなら、アービトレーションなしでセレクション
                    135:                // フェーズに移行。
                    136:                //
                    137:                // 仕様Phase      |Free |<---- Selection ---->|
                    138:                //
                    139:                // BSY       ~~\__________________________/~~~~~~~
                    140:                //
                    141:                // Data      -----------< Initiator&Target ID >---
                    142:                //
                    143:                // SEL       ________________/~~~~~~~~~~~~~~~~\___
                    144:                //                           ^イマココ
                    145:                //
                    146:                // 実装Phase      |<- Free ->|<-- Selection ->|
                    147:                //
                    148:                // SCSI 本にはデータバスが変化したところから Selection フェーズと
                    149:                // 書いてあるように読めるけど(上)、ここでは SEL がアサートされた
                    150:                // ところからとする(下)。
                    151: 
                    152:                SetPhase(SCSI::Phase::Selection);
                    153: 
                    154:                // SEL を上げた人がイニシエータ
                    155:                init_id = id;
                    156: 
                    157:                // 即セレクション開始。
                    158:                CallAfter(SelectionStart, "SCSIBus Selection Start", 0);
                    159: 
                    160:        } else if (GetPhase() == SCSI::Phase::Arbitration) {
                    161:                // アービトレーションフェーズなら、
                    162:                // アービトレーションに勝ってセレクションフェーズに移行途中。
                    163:                // この場合はセレクションフェーズに移行するのはここではなく
                    164:                // BSY の立ち下がりなので、そっち参照。
                    165:                //
                    166:                // 仕様Phase Arbitration |---|<--- Selection -->|
                    167:                //
                    168:                // BSY       /~~~~~~~~~~~~~~~\_______________/~~~~~~
                    169:                //
                    170:                // Data      <InitiatorID>< Initiator&Target ID >---
                    171:                //
                    172:                // SEL       __________/~~~~~~~~~~~~~~~~~~~~~~~~\___
                    173:                //                     ^イマココ
                    174:                //
                    175:                // 実装Phase <-Arbitration ->|<--- Selection -->|
                    176: 
                    177:                // SEL を上げた人がイニシエータなことだけはここで覚えておく。
                    178:                init_id = id;
                    179: 
                    180:        } else {
1.1.1.6   root      181:                // それ以外(転送フェーズ)中に SEL が立てられたらどうすればいいか。
                    182:                // これは転送中に LUNA をリセットすると起きる。LUNA の PROM は SPC に
                    183:                // 対してバスリセットを発行せずに、セレクションを行おうとするため。
                    184:                // SCSI 本には載ってないので分からないが、このような規約違反はたいてい
                    185:                // イニシエータが検出してバスフリーかバスリセットを行うことが意図
                    186:                // されているんじゃないかと想像するけど、今回そのイニシエータが違反
                    187:                // してるので、どうするんだろうね。
                    188:                // 賢いターゲットデバイスだとこれを検出して自主的にバスフリーへ落ちる
                    189:                // だろうか。賢くないターゲットデバイスは検出せずにそのまま (そして
                    190:                // デッドロック) になるだろうか。
                    191: 
1.1.1.7   root      192: #if 1
                    193:                // XXX とりあえず LUNA で SCSI がなんか刺さった時に本体リセットでも
                    194:                // 直らない状況を真似するため、ターゲットは何もしないでおく。
                    195: #else
1.1.1.6   root      196:                // ターゲットデバイスは賢いので、全員イニシエータ様に忖度して
                    197:                // 即座にバスフリーへ移行するというのはどうか。
                    198:                for (uint i = 0; i < 8; i++) {
                    199:                        if (i != id) {
                    200:                                NegateBSY(i);
                    201:                        }
                    202:                }
1.1.1.7   root      203: #endif
1.1       root      204:        }
                    205: }
                    206: 
                    207: // SEL を下げる
                    208: void
                    209: SCSIBus::NegateSEL(uint id)
                    210: {
                    211:        putlog(4, "%s(#%d)", __func__, id);
                    212: 
                    213:        // すでに下りてたら何もしない
                    214:        if ((sel & (1 << id)) == 0) {
                    215:                return;
                    216:        }
                    217: 
                    218:        sel &= ~(1 << id);
                    219: 
1.1.1.6   root      220:        if (GetPhase() == SCSI::Phase::BusFree) {
                    221:                // バスフリーフェーズで SEL を下げる
                    222: 
                    223:                // 通常は起きないはずだけど、転送フェーズ中に LUNA をリセットすると
                    224:                // -> LUNA の PROM が SEL を上げる (プロトコル違反)
                    225:                // -> ターゲットは転送フェーズ中なのに SEL が上がったのを検出して
                    226:                //    BSY を取り下げてバスフリーに移行する
                    227:                // -> バスフリーがイニシエータに通知され、イニシエータが SEL を下げる
                    228:                // となるので、このケースはスルーしてよい。
                    229: 
                    230:        } else if (GetPhase() == SCSI::Phase::Selection) {
1.1       root      231:                // セレクションフェーズで SEL を下げる
                    232:                if (bsy) {
                    233:                        // BSY が立っていれば、ターゲットがセレクションに応答したので
                    234:                        // イニシエータが SEL を下げて、これから任意時間後にターゲットが
                    235:                        // 情報転送フェーズに移行する。
                    236: 
                    237:                        CallAfter(StartTransfer, "SCSIBus Transfer Start", 400_nsec);
                    238:                } else {
                    239:                        // BSY が立ってなければ、ターゲットが応答しなかったことに
                    240:                        // イニシエータがあきらめて SEL を下げたので、何もせず
                    241:                        // バスフリーフェーズに移行。
                    242:                        BusFree(id);
                    243:                }
                    244:        } else {
1.1.1.6   root      245:                // 下げる分には無視してもいいはずだけど、どうする?
1.1.1.10  root      246:                VMPANIC("NegateSEL in %s phase", GetPhaseName());
1.1       root      247:        }
                    248: }
                    249: 
                    250: // BSY を上げる
                    251: void
                    252: SCSIBus::AssertBSY(uint id)
                    253: {
                    254:        putlog(4, "%s(#%d)", __func__, id);
                    255: 
                    256:        // すでに上がってたら何もしない
                    257:        if ((bsy & (1 << id))) {
                    258:                return;
                    259:        }
                    260: 
                    261:        bsy |= (1 << id);
                    262: 
                    263:        if (GetPhase() == SCSI::Phase::BusFree) {
                    264:                // バスフリーフェーズだったらアービトレーション開始
                    265: 
                    266:                // 実際のアービトレーションは、
                    267:                // バスフリーを検出してから規定時間経過後に、データバスに自身の ID を
                    268:                // 出して BSY を立てる。規定時間内に他のデバイスもアービトレーションに
                    269:                // 参加すれば ID の高い方が勝つ。負けた方は規定時間内にアービトレー
                    270:                // ションを取り下げる。規定期間経って勝っていればアービトレーション
                    271:                // 成功となり、セレクションフェーズに移る。
                    272:                // のだが、エミュレータ上では、今の所アービトレーションで競合が起きる
                    273:                // ことはないため、アービトレーションは規定時間経過して必ず成功する。
                    274:                // ちゃんとしたアービトレーションを実装する場合はたぶんこれだけでは
                    275:                // 済まないと思うので書き換えること。
                    276: 
                    277:                SetPhase(SCSI::Phase::Arbitration);
                    278: 
                    279:        } else if (GetPhase() == SCSI::Phase::Arbitration) {
                    280:                // アービトレーションフェーズだったら、アービトレーションに参加。
                    281:                // 今は起きない。
1.1.1.10  root      282:                VMPANIC("AssertBSY(#%d) in arbitration", id);
1.1       root      283: 
                    284:        } else if (GetPhase() == SCSI::Phase::Selection) {
                    285:                // セレクションフェーズだったら、転送フェーズに移行中。
                    286:                // ターゲットが BSY をアサートしたので次は規定時間経過後に
                    287:                // SelectionAck() を呼び出してイニシエータに SEL を下げさせる。
                    288: 
                    289:                CallAfter(SelectionAck, "SCSIBus Selection Ack", 90_nsec);
                    290:        } else {
                    291:                // どうする?
1.1.1.10  root      292:                VMPANIC("AssertBSY(#%d) in %s phase", id, GetPhaseName());
1.1       root      293:        }
                    294: }
                    295: 
                    296: // BSY を下げる
                    297: void
                    298: SCSIBus::NegateBSY(uint id)
                    299: {
                    300:        putlog(4, "%s(#%d)", __func__, id);
                    301: 
                    302:        // すでに下りてたら何もしない
                    303:        if ((bsy & (1 << id)) == 0) {
                    304:                return;
                    305:        }
                    306: 
                    307:        bsy &= ~(1 << id);
                    308: 
                    309:        if (GetPhase() == SCSI::Phase::Transfer) {
                    310:                // 転送フェーズから BSY を下げたらバスフリーに移行。
                    311:                BusFree(id);
                    312: 
                    313:        } else if (GetPhase() == SCSI::Phase::Arbitration) {
                    314:                // アービトレーションフェーズなら、
                    315:                // イニシエータが BSY を下ろすことでセレクションフェーズに移行。
                    316:                //
                    317:                // この少し上の AssertSEL() 中のタイムチャート参照。
                    318: 
                    319:                SetPhase(SCSI::Phase::Selection);
                    320: 
                    321:                // 即セレクション開始。
                    322:                CallAfter(SelectionStart, "SCSIBus Selection Start", 0);
                    323: 
                    324:        } else {
                    325:                // どうする?
1.1.1.10  root      326:                VMPANIC("NegateBSY(#%d) in %s phase", id, GetPhaseName());
1.1       root      327:        }
                    328: }
                    329: 
                    330: // MSG, CD, IO 信号線を変更する。
                    331: void
                    332: SCSIBus::SetXfer(uint8 val)
                    333: {
                    334:        uint8 oldxfer = xfer;
                    335: 
                    336:        xfer = val & (SCSI::MSG | SCSI::CD | SCSI::IO);
                    337:        if (oldxfer != xfer) {
                    338:                if (GetPhase() == SCSI::Phase::Transfer) {
                    339:                        putlog(4, "SetXfer(%x:%s)", xfer, SCSI::GetXferPhaseName(xfer));
                    340:                } else {
                    341:                        putlog(4, "SetXfer(%x)", xfer);
                    342:                }
                    343:        }
                    344: }
                    345: 
                    346: // REQ を上げる
                    347: void
                    348: SCSIBus::AssertREQ()
                    349: {
                    350:        if (GetPhase() == SCSI::Phase::Transfer) {
                    351:                // REQ を上げると言いつつ、ここで転送レートの時間調整をしているので
                    352:                // (scsibus.h の解説参照) REQ を実際に上げるのはイベント発生後。
                    353:                // XXX 本来ターゲットデバイスがタイマー持っててやるべきだけど、
                    354:                // イベントがもう一つ増えるのもうざいので。どのみち、ここの REQ
                    355:                // 通知はイベントにしないといけないので。
                    356: 
                    357:                uint64 after = 0;
1.1.1.11! root      358:                uint64 now = scheduler->GetVirtTime();
1.1       root      359:                if (now < last_req_time + 1_usec) {
                    360:                        after = (last_req_time + 1_usec) - now;
                    361:                }
1.1.1.4   root      362:                // ウェイトが指示されていれば入れる (シークタイムなど)
                    363:                if (req_wait != 0) {
                    364:                        after += req_wait;
                    365:                        req_wait = 0;
                    366:                }
1.1.1.9   root      367:                last_req_time = now + after;
1.1       root      368:                CallAfter(TransferReq, "SCSIBus AssertREQ", after);
                    369:        } else {
                    370:                // 転送フェーズでないのに上げることはないと思うけど
                    371:                // とりあえず通知もせずその場で上げておくか?
                    372:                putlog(4, "%s", __func__);
                    373:                req = true;
                    374:        }
                    375: }
                    376: 
                    377: // ターゲットが REQ を立てようとして所定時間経ったので REQ を立てて
                    378: // イニシエータの TransferReqCB を呼び出すところ。
                    379: void
1.1.1.4   root      380: SCSIBus::TransferReq(Event& ev)
1.1       root      381: {
                    382:        putlog(4, "AssertREQ");
                    383:        req = true;
                    384: 
                    385:        assert(device[init_id]);
                    386:        device[init_id]->TransferReqCB();
                    387: }
                    388: 
                    389: // REQ を下げる
                    390: void
                    391: SCSIBus::NegateREQ()
                    392: {
                    393:        putlog(4, "%s", __func__);
                    394:        req = false;
                    395: }
                    396: 
                    397: // ACK を上げる
                    398: void
                    399: SCSIBus::AssertACK()
                    400: {
                    401:        putlog(4, "%s", __func__);
                    402:        ack = true;
                    403: 
                    404:        if (GetPhase() == SCSI::Phase::Transfer) {
                    405:                assert(device[target_id]);
                    406:                device[target_id]->TransferCB();
                    407:        }
                    408: }
                    409: 
                    410: // ACK を下げる
                    411: void
                    412: SCSIBus::NegateACK()
                    413: {
                    414:        putlog(4, "%s", __func__);
                    415:        ack = false;
                    416: 
                    417:        if (GetPhase() == SCSI::Phase::Transfer) {
                    418:                assert(device[target_id]);
                    419:                device[target_id]->TransferAckCB();
                    420:        }
                    421: }
                    422: 
                    423: // ATN を上げる
                    424: void
                    425: SCSIBus::AssertATN()
                    426: {
                    427:        putlog(4, "%s", __func__);
                    428:        atn = true;
                    429: }
                    430: 
                    431: // ATN を下げる
                    432: void
                    433: SCSIBus::NegateATN()
                    434: {
                    435:        putlog(4, "%s", __func__);
                    436:        atn = false;
                    437: }
                    438: 
                    439: // バスフリーフェーズに移行。
                    440: // id はこのバスフリーを行ったデバイス (0..7)
                    441: void
                    442: SCSIBus::BusFree(int id)
                    443: {
                    444:        putlog(4, "%s", __func__);
                    445: 
                    446:        SetPhase(SCSI::Phase::BusFree);
                    447: 
                    448:        // 即全デバイスに通知。
                    449:        for (int i = 0; i < countof(device); i++) {
                    450:                if (device[i]) {
                    451:                        device[i]->BusFreeCB(id);
                    452:                }
                    453:        }
                    454: 
                    455:        // コールバック終了後に内部状態もクリア
                    456:        init_id = -1;
                    457:        target_id = -1;
                    458: 
                    459:        // 通知された各デバイスは規定時間内に他の信号線も取り下げなければ
                    460:        // ならない。
                    461: }
                    462: 
                    463: // セレクションから情報転送フェーズまで
                    464: //
                    465: // 仕様Phase <- Selection ->|
                    466: //
                    467: // BSY       _______________/~~~~~~~~~
                    468: //
                    469: // Data      Initiator&Target ID >----
                    470: //
                    471: // SEL       ~~~~~~~~~~~~~~~~~~~~~~\__
                    472: //
                    473: //           -(1)->|--(2)-->|-(3)->|
                    474: //
                    475: // セレクションフェーズ開始後、(ターゲット)デバイスは、SEL がアサート、
                    476: // (IO がネゲート)、データバスの自身の ID がアサート、が 400nsec 以上
                    477: // 継続することで、自身がターゲットに選択されていることを認識する。(1)
                    478: // ターゲットは (1) から 200usec 以内に BSY をアサートする。(2)
                    479: // イニシエータは (2) から 90nsec 以上後に SEL (とデータ) を下げる。(3)
                    480: //
                    481: // 実装では、セレクションフェーズ開始が SelectionStart() の呼び出しに対応。
                    482: //
                    483: // ターゲットデバイスが存在していない場合はタイムアウトとなるが、これは
                    484: // ホストが処理するのでバスは何もしない。
                    485: // ターゲットデバイスが存在していれば SelectionStart() から 400nsec 後 +
                    486: // 200usec 以内…なので適当に間をとって 100usec 後に Selection() コールバック
                    487: // を呼び出す(1+2)。
                    488: //
                    489: // Selection() コールバック(が呼び出す device->SelectionCB()) は通常 BSY を
                    490: // アサートするはずであり、そこから 90nsec 後に SelectionAck() コールバック
                    491: // を呼び出す(3)。
                    492: //
                    493: // SelectionAck() コールバック(が呼び出す device->SelectionAckCB()) は通常
                    494: // SEL をネゲートするはずであり、そこから情報転送フェーズへの移行には特に
                    495: // 規定はなさそうなので、400nsec 後に StartTransfer() をコールバックして
                    496: // デバイスを情報転送フェーズに移行させる。
                    497: 
                    498: // セレクションフェーズ開始。
                    499: void
1.1.1.4   root      500: SCSIBus::SelectionStart(Event& ev)
1.1       root      501: {
                    502:        // この時点でデータバスには自身の ID と相手の ID が立っているはず。
                    503:        // 一方 sel は SEL 信号線を立てた人のビットを持っているので、
                    504:        // これでターゲットを特定する。
                    505:        uint target_mask = data ^ sel;
                    506:        if (__builtin_popcount(target_mask) != 1) {
                    507:                // データバスから自身を除いた結果立ってるビットが1個以外だったら
                    508:                // 誰も応答しないので、何もせず帰ってよい。
1.1.1.2   root      509:                putlog(3, "%s (No target specified; databus=$%02x)", __func__, data);
1.1       root      510:                return;
                    511:        }
                    512: 
                    513:        // ターゲット ID はここで取得
                    514:        target_id = DecodeID(target_mask);
                    515:        if (device[target_id] == NULL) {
                    516:                // ターゲットデバイスがいなければ、バスは何もしない。
                    517:                // (その結果イニシエータでタイムアウトが起きるはず)
                    518:                putlog(4, "%s (No target)", __func__);
                    519:                return;
                    520:        }
                    521: 
                    522:        // ターゲットデバイスがいれば、規定時間経過後ターゲットに通知。
                    523:        putlog(4, "%s", __func__);
                    524:        CallAfter(Selected, "SCSIBus Selected", 100_usec);
                    525: }
                    526: 
                    527: // SelectionStart() にてターゲットが選択されて規定時間経ったので
                    528: // そろそろターゲットが BSY を立てる頃に呼ばれるコールバック。
                    529: void
1.1.1.4   root      530: SCSIBus::Selected(Event& ev)
1.1       root      531: {
                    532:        // ターゲットに通知。ターゲットがいることは確定している。
                    533:        assert(device[target_id]);
                    534:        device[target_id]->SelectionCB();
                    535: }
                    536: 
                    537: // ターゲットが BSY を立てて規定時間経ったので
1.1.1.4   root      538: // そろそろイニシエータが SEL を下げる頃に呼ばれるコールバック。
1.1       root      539: void
1.1.1.4   root      540: SCSIBus::SelectionAck(Event& ev)
1.1       root      541: {
                    542:        // イニシエータに通知。
                    543:        assert(device[init_id]);
                    544:        device[init_id]->SelectionAckCB();
                    545: }
                    546: 
                    547: // 情報転送フェーズ開始を通知する。
                    548: // BSY を上げて情報転送フェーズに移行したところで呼ばれる。
                    549: void
1.1.1.4   root      550: SCSIBus::StartTransfer(Event& ev)
1.1       root      551: {
                    552:        SetPhase(SCSI::Phase::Transfer);
                    553: 
                    554:        assert(device[target_id]);
                    555:        device[target_id]->StartTransferCB();
                    556: }

unix.superglobalmegacorp.com

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