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

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

unix.superglobalmegacorp.com

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