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

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

unix.superglobalmegacorp.com

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