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