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

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

unix.superglobalmegacorp.com

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