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

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

unix.superglobalmegacorp.com

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