|
|
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 "scsi.h"
99:
100: class SCSIDevice;
101: class SCSIDisk;
102: class SCSIHostDevice;
103: class SCSITarget;
1.1 root 104:
105: //
106: // SCSI バス
107: //
108: class SCSIBus : public Device
109: {
1.1.1.6 root 110: using inherited = Device;
1.1.1.9 root 111:
112: public:
113: // (1<<id) が1つだけ立ててあるビットマップから ID を返す。
114: // ビットが立っていなければ -1 を返す? 複数立ってたら?
115: static inline int DecodeID(uint32 mask)
116: {
117: mask &= 0xff;
118: if (mask == 0)
119: return -1;
120: return 31 - __builtin_clz(mask);
121: }
122:
1.1 root 123: public:
1.1.1.9 root 124: SCSIBus(SCSIHostDevice *parent_, const char *config_name_);
1.1.1.8 root 125: ~SCSIBus() override;
1.1 root 126:
1.1.1.9 root 127: bool Create() override;
128: bool Init() override;
1.1.1.7 root 129: void ResetHard(bool poweron) override;
1.1.1.3 root 130:
1.1.1.9 root 131: // このバスにデバイスを接続する。
132: bool AttachDevice(SCSIDevice *dev, int id);
1.1 root 133:
134: // 信号線の操作
135: bool GetRST() const { return rst; }
136: void AssertRST();
137: void NegateRST();
138: bool GetREQ() const { return req; }
139: void AssertREQ();
140: void NegateREQ();
141: bool GetACK() const { return ack; }
142: void AssertACK();
143: void NegateACK();
144: bool GetATN() const { return atn; }
145: void AssertATN();
146: void NegateATN();
147: uint8 GetSEL() const { return sel; }
148: void AssertSEL(uint id);
149: void NegateSEL(uint id);
150: uint8 GetBSY() const { return bsy; }
151: void AssertBSY(uint id);
152: void NegateBSY(uint id);
153: bool GetMSG() const { return (xfer & SCSI::MSG); }
154: bool GetCD() const { return (xfer & SCSI::CD); }
155: bool GetIO() const { return (xfer & SCSI::IO); }
156:
157: // MSG,CD,IO で示されるビットを val で指定した状態に変更する。
158: void SetXfer(uint8 val);
159:
160: // 現在の情報転送フェーズ(の信号線の状態) を返す。
161: // 現在のフェーズが Transfer の時のみ戻り値の内容は有効。
162: SCSI::XferPhase GetXfer() const { return (SCSI::XferPhase)xfer; }
163:
164: uint8 GetData() const { return data; }
165: void SetData(uint8 val) { data = val; }
166:
167: // 現在のフェーズを返す
168: SCSI::Phase GetPhase() const { return phase; }
169: // フェーズを変更する
170: void SetPhase(SCSI::Phase val) { phase = val; }
171: // 現在のフェーズ名を返す
172: const char *GetPhaseName() const { return SCSI::GetPhaseName(phase); }
173:
1.1.1.9 root 174: // 設定ファイル上のこのホストデバイスを示す接頭語を返す
175: const char *GetConfigName() const { return config_name; }
176:
177: // 接続済みの (イニシエータを除く) デバイス数を返す。
178: uint GetTargetCount() const;
179:
180: // 接続済みの (イニシエータがあれば含む) すべてのデバイスのリストを返す。
181: const std::vector<SCSIDevice*>& GetConnectedDevices() const {
182: return connected_devices;
183: }
184:
185: // ターゲットデバイス取得 (ROM エミュレーション/UI用)
186: SCSITarget *GetTarget(int id) const { return target[id].get(); }
187:
1.1 root 188: // 現在選択中のターゲットを返す、なければ NULL を返す。(モニタ用)
1.1.1.9 root 189: SCSITarget *GetSelectedTarget() const {
1.1 root 190: if (0 <= target_id && target_id < 8) {
1.1.1.9 root 191: return target[target_id].get();
1.1 root 192: }
193: return NULL;
194: }
195:
1.1.1.3 root 196: // 次の REQ アサートの前に指定のウェイトを入れる
197: // (シークタイムなどを簡易的に再現するため)。
198: // ウェイトは一度適用されると 0 に戻る。
199: void SetOneshotReqWait(uint64 nsec) { req_wait += nsec; }
200:
1.1 root 201: private:
1.1.1.9 root 202: DECLARE_MONITOR_CALLBACK(MonitorUpdate);
203:
204: // connected_devices を更新する。
205: void MakeConnectedDevices();
206:
207: void BusFree(uint id);
1.1.1.3 root 208: void SelectionStart(Event& ev);
209: void Selected(Event& ev);
210: void SelectionAck(Event& ev);
211: void StartTransfer(Event& ev);
212: void TransferReq(Event& ev);
1.1 root 213:
214: // 現在のフェーズ
215: SCSI::Phase phase {};
216:
217: // SEL, BSY は同時に複数が重畳出来るので、アサートしたデバイスがそれぞれ
218: // 自身の ID を立てる。ネゲートは 0 かどうかで判断する。
219: // 現在の実装では実際には同時に立つことはないけど。
220: bool rst {};
221: bool req {};
222: bool ack {};
223: bool atn {};
224: uint8 sel {};
225: uint8 bsy {};
226: uint8 xfer {}; // MSG,CD,IO
227: uint8 data {};
228: // bool parity; 未実装
229:
230: // 前回 ACK を立てた時刻。
231: // 転送速度をここで律速させている。
1.1.1.3 root 232: uint64 last_req_time {};
233:
234: // 次の REQ アサートに入れるウェイト [nsec]
235: uint64 req_wait {};
1.1 root 236:
1.1.1.9 root 237: // 設定ファイルに使われるこのホストデバイスを示す接頭語。
238: // ("spc0" とか)
239: const char *config_name {};
240:
1.1 root 241: // 実際のバスではそんなことはないが、ここではバスがイニシエータと
242: // ターゲットの ID を把握している。どちらも非選択状態なら -1。
243: int init_id {};
244: int target_id {};
245:
1.1.1.9 root 246: // SCSI ターゲット (所有)。イニシエータは含まない。
247: std::unique_ptr<SCSITarget> target[8] /*{}*/;
248:
249: // SCSI デバイス(イニシエータも含む)。非接続なら NULL。
1.1 root 250: // 所有権を持たない。
251: SCSIDevice *device[8] {};
252:
1.1.1.9 root 253: // 接続中のデバイス(イニシエータ含む)を ID 順に並べたリスト。
254: std::vector<SCSIDevice*> connected_devices {};
255:
256: // 親(イニシエータ)
257: SCSIHostDevice *parent {};
258:
1.1 root 259: // イベント
1.1.1.6 root 260: Event event { this };
1.1 root 261:
1.1.1.10! root 262: Monitor *monitor {};
1.1.1.9 root 263: };
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.