Annotation of nono/vm/scsibus.h, revision 1.1.1.3

1.1       root        1: //
                      2: // nono
1.1.1.2   root        3: // Copyright (C) 2020 nono project
                      4: // Licensed under nono-license.txt
1.1       root        5: //
                      6: 
                      7: // SCSI バス
                      8: 
                      9: #pragma once
                     10: 
                     11: #include "device.h"
                     12: #include "scheduler.h"
                     13: #include "scsi.h"
                     14: 
                     15: // フェーズ遷移についてのメモ。
                     16: //
                     17: // イニシエータ(この時点ではまだ候補だが)が BSY を立てることで
                     18: // (バスフリーフェーズから) アービトレーションフェーズに移る。
                     19: // 現行では規定時間後に自動的にイニシエータが勝利する(バスは何もしない)。
                     20: // イニシエータが SEL 立てて BSY を下ろすことで、セレクションフェーズに移る。
                     21: // この間の遷移は以下の通り。
                     22: // BSY SEL
                     23: //   0   0 BusFree
                     24: //         ↓ イニシエータ(候補)が BSY を立てる
                     25: //   1   0 Arbitration (バスは何もしない)
                     26: //         ↓ イニシエータが SEL を立てる
                     27: //   1   1 Arbitration
                     28: //         ↓ イニシエータがデータバスにターゲットをセットして BSY を下ろす
                     29: //   0   1 Selection
                     30: //
                     31: // バスはセレクションフェーズ遷移後、規定時間でターゲットに通知。
                     32: // ターゲットは即 BSY を立てるのでバスはこれをイニシエータに通知。
                     33: // イニシエータは SEL を下げるのでバスはこれをターゲットに通知。
                     34: // この間の遷移は以下の通り。
                     35: // BSY SEL
                     36: //   0   1 Selection (ターゲットに通知)
                     37: //         ↓ ターゲットが BSY を立てる
                     38: //   1   1 Selection (イニシエータに通知)
                     39: //         ↓ イニシエータがデータバスと SEL を下げる
                     40: //   1   0 Transfer (ターゲットに通知)
                     41: //
                     42: // バスは Transfer 後、規定時間でターゲットに通知。
                     43: // ターゲットは転送フェーズ(の最初のコマンドフェーズ) に移行する。
                     44: 
                     45: // 転送フェーズについてのメモ。
                     46: //
                     47: //       -----> (実線) は関数呼び出し
                     48: //       - - -> (破線) は関数からの戻り
                     49: //       〜〜-> (波線) はイベントコールバックによる呼び出し
                     50: //
                     51: // マニュアル転送は以下のようになる。
                     52: // 基本的にはターゲットが REQ を上げたことをイベントでイニシエータに通知し、
                     53: // イニシエータは基本的に関数呼び出しでターゲットを操作することにする。
                     54: //
                     55: // 非同期転送時の REQ/ACK シーケンスに時間規定はないが、時間を考慮せずに
                     56: // 実行してしまうと現実より速い転送が出来てしまう可能性があるので、ここでは
                     57: // AssertREQ が TransferReqCB を呼び出すところでタイミングを調整する。
                     58: // 具体的には前回の TransferReqCB から 1usec 以上空けるというのはどうか。
                     59: //
                     60: //       イニシエータ                        ターゲット
                     61: //       ------------                        ----------
                     62: //                              Event 〜〜-> StartTransferCB
                     63: //                                            ↓
                     64: //                                           REQ を上げる
                     65: //       TransferReqCB <-〜〜〜 Event 〜〜〜
                     66: //       ACK を上げる
                     67: //                  +------ AssertACK() ---> TransferCB
                     68: //                                           1バイト送受信
                     69: //                                           REQ を下げる
                     70: //                  < - - - - - - - - - - -  return;
                     71: //       (誰かが)
                     72: //       ACK を下げる
                     73: //                  +------ NegateACK() ---> TransferAckCB
                     74: //                                           REQ を上げる 〜〜+
                     75: //                  < - - - - - - - - - - -  return;          :
                     76: //                                                            :
                     77: //       TransferReqCB <-〜〜〜 Event 〜〜〜〜〜〜〜〜〜〜〜〜+
                     78: //
                     79: // ハードウェア転送の場合は ACK 一往復を省略して以下のようにする。
                     80: // Transfer() (TransferCB()) は ACK が立っていなければハードウェア転送と
                     81: // 判断する。
                     82: //
                     83: //       イニシエータ                        ターゲット
                     84: //       ------------                        ----------
                     85: //                              Event 〜〜-> StartTransferCB
                     86: //                                            ↓
                     87: //                                           REQ を上げる
                     88: //       TransferReqCB <-〜〜〜 Event 〜〜〜
                     89: //                  |
                     90: //                  +------- Transfer() ---> TransferCB
                     91: //                                           1バイト送受信
                     92: //                                           REQ を下げる
                     93: //                                           REQ を上げる 〜〜+
                     94: //                  < - - - - - - - - - - -  return;          :
                     95: //                                                            :
                     96: //       TransferReqCB <-〜〜〜 Event 〜〜〜〜〜〜〜〜〜〜〜〜+
                     97: //
                     98: 
                     99: 
                    100: //
                    101: // SCSI バス
                    102: //
                    103: class SCSIBus : public Device
                    104: {
                    105:  public:
                    106:        SCSIBus();
1.1.1.2   root      107:        ~SCSIBus() override;
1.1       root      108: 
1.1.1.3 ! root      109:        void ResetHard() override;
        !           110: 
1.1       root      111:        // このバスにデバイスを接続する
                    112:        bool Attach(SCSIDevice *dev, int id);
                    113: 
                    114:        // 信号線の操作
                    115:        bool GetRST() const { return rst; }
                    116:        void AssertRST();
                    117:        void NegateRST();
                    118:        bool GetREQ() const { return req; }
                    119:        void AssertREQ();
                    120:        void NegateREQ();
                    121:        bool GetACK() const { return ack; }
                    122:        void AssertACK();
                    123:        void NegateACK();
                    124:        bool GetATN() const { return atn; }
                    125:        void AssertATN();
                    126:        void NegateATN();
                    127:        uint8 GetSEL() const { return sel; }
                    128:        void AssertSEL(uint id);
                    129:        void NegateSEL(uint id);
                    130:        uint8 GetBSY() const { return bsy; }
                    131:        void AssertBSY(uint id);
                    132:        void NegateBSY(uint id);
                    133:        bool GetMSG() const     { return (xfer & SCSI::MSG); }
                    134:        bool GetCD() const      { return (xfer & SCSI::CD); }
                    135:        bool GetIO() const      { return (xfer & SCSI::IO); }
                    136: 
                    137:        // MSG,CD,IO で示されるビットを val で指定した状態に変更する。
                    138:        void SetXfer(uint8 val);
                    139: 
                    140:        // 現在の情報転送フェーズ(の信号線の状態) を返す。
                    141:        // 現在のフェーズが Transfer の時のみ戻り値の内容は有効。
                    142:        SCSI::XferPhase GetXfer() const { return (SCSI::XferPhase)xfer; }
                    143: 
                    144:        uint8 GetData() const { return data; }
                    145:        void SetData(uint8 val) { data = val; }
                    146: 
                    147:        // 現在のフェーズを返す
                    148:        SCSI::Phase GetPhase() const { return phase; }
                    149:        // フェーズを変更する
                    150:        void SetPhase(SCSI::Phase val) { phase = val; }
                    151:        // 現在のフェーズ名を返す
                    152:        const char *GetPhaseName() const { return SCSI::GetPhaseName(phase); }
                    153: 
                    154:        // 現在選択中のターゲットを返す、なければ NULL を返す。(モニタ用)
                    155:        SCSIDevice *GetConnectedTarget() const {
                    156:                if (0 <= target_id && target_id < 8) {
                    157:                        return device[target_id];
                    158:                }
                    159:                return NULL;
                    160:        }
                    161: 
1.1.1.3 ! root      162:        // 次の REQ アサートの前に指定のウェイトを入れる
        !           163:        // (シークタイムなどを簡易的に再現するため)。
        !           164:        // ウェイトは一度適用されると 0 に戻る。
        !           165:        void SetOneshotReqWait(uint64 nsec) { req_wait += nsec; }
        !           166: 
1.1       root      167:  private:
                    168:        void BusFree(int id);
1.1.1.3 ! root      169:        void SelectionStart(Event& ev);
        !           170:        void Selected(Event& ev);
        !           171:        void SelectionAck(Event& ev);
        !           172:        void StartTransfer(Event& ev);
        !           173:        void TransferReq(Event& ev);
1.1       root      174: 
                    175:        // 現在のフェーズ
                    176:        SCSI::Phase phase {};
                    177: 
                    178:        // SEL, BSY は同時に複数が重畳出来るので、アサートしたデバイスがそれぞれ
                    179:        // 自身の ID を立てる。ネゲートは 0 かどうかで判断する。
                    180:        // 現在の実装では実際には同時に立つことはないけど。
                    181:        bool rst {};
                    182:        bool req {};
                    183:        bool ack {};
                    184:        bool atn {};
                    185:        uint8 sel {};
                    186:        uint8 bsy {};
                    187:        uint8 xfer {};  // MSG,CD,IO
                    188:        uint8 data {};
                    189:        // bool parity; 未実装
                    190: 
                    191:        // 前回 ACK を立てた時刻。
                    192:        // 転送速度をここで律速させている。
1.1.1.3 ! root      193:        uint64 last_req_time {};
        !           194: 
        !           195:        // 次の REQ アサートに入れるウェイト [nsec]
        !           196:        uint64 req_wait {};
1.1       root      197: 
                    198:        // 実際のバスではそんなことはないが、ここではバスがイニシエータと
                    199:        // ターゲットの ID を把握している。どちらも非選択状態なら -1。
                    200:        int init_id {};
                    201:        int target_id {};
                    202: 
                    203:        // SCSI デバイス(イニシエータも含む)。非接続なら NULL
                    204:        // 所有権を持たない。
                    205:        SCSIDevice *device[8] {};
                    206: 
                    207:        // イベント
                    208:        Event event {};
                    209: };
                    210: 
                    211: // (1<<id) が1つだけ立ててあるビットマップから ID を返す。
                    212: // ビットが立っていなければ -1 を返す? 複数立ってたら?
                    213: // SCSIBus と SPC でも使うのでグローバルなインライン関数にしておく。
                    214: static inline int DecodeID(uint32 mask)
                    215: {
                    216:        mask &= 0xff;
                    217:        if (mask == 0)
                    218:                return -1;
                    219:        return 31 - __builtin_clz(mask);
                    220: }

unix.superglobalmegacorp.com

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