|
|
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:
109: // このバスにデバイスを接続する
110: bool Attach(SCSIDevice *dev, int id);
111:
112: // 信号線の操作
113: bool GetRST() const { return rst; }
114: void AssertRST();
115: void NegateRST();
116: bool GetREQ() const { return req; }
117: void AssertREQ();
118: void NegateREQ();
119: bool GetACK() const { return ack; }
120: void AssertACK();
121: void NegateACK();
122: bool GetATN() const { return atn; }
123: void AssertATN();
124: void NegateATN();
125: uint8 GetSEL() const { return sel; }
126: void AssertSEL(uint id);
127: void NegateSEL(uint id);
128: uint8 GetBSY() const { return bsy; }
129: void AssertBSY(uint id);
130: void NegateBSY(uint id);
131: bool GetMSG() const { return (xfer & SCSI::MSG); }
132: bool GetCD() const { return (xfer & SCSI::CD); }
133: bool GetIO() const { return (xfer & SCSI::IO); }
134:
135: // MSG,CD,IO で示されるビットを val で指定した状態に変更する。
136: void SetXfer(uint8 val);
137:
138: // 現在の情報転送フェーズ(の信号線の状態) を返す。
139: // 現在のフェーズが Transfer の時のみ戻り値の内容は有効。
140: SCSI::XferPhase GetXfer() const { return (SCSI::XferPhase)xfer; }
141:
142: uint8 GetData() const { return data; }
143: void SetData(uint8 val) { data = val; }
144:
145: // 現在のフェーズを返す
146: SCSI::Phase GetPhase() const { return phase; }
147: // フェーズを変更する
148: void SetPhase(SCSI::Phase val) { phase = val; }
149: // 現在のフェーズ名を返す
150: const char *GetPhaseName() const { return SCSI::GetPhaseName(phase); }
151:
152: // 現在選択中のターゲットを返す、なければ NULL を返す。(モニタ用)
153: SCSIDevice *GetConnectedTarget() const {
154: if (0 <= target_id && target_id < 8) {
155: return device[target_id];
156: }
157: return NULL;
158: }
159:
160: private:
161: void BusFree(int id);
162: void SelectionStart();
163: void Selected(int code);
164: void SelectionAck(int code);
165: void StartTransfer(int code);
166: void TransferReq(int code);
167:
168: // 現在のフェーズ
169: SCSI::Phase phase {};
170:
171: // SEL, BSY は同時に複数が重畳出来るので、アサートしたデバイスがそれぞれ
172: // 自身の ID を立てる。ネゲートは 0 かどうかで判断する。
173: // 現在の実装では実際には同時に立つことはないけど。
174: bool rst {};
175: bool req {};
176: bool ack {};
177: bool atn {};
178: uint8 sel {};
179: uint8 bsy {};
180: uint8 xfer {}; // MSG,CD,IO
181: uint8 data {};
182: // bool parity; 未実装
183:
184: // 前回 ACK を立てた時刻。
185: // 転送速度をここで律速させている。
186: uint64 last_req_time = 0;
187:
188: // 実際のバスではそんなことはないが、ここではバスがイニシエータと
189: // ターゲットの ID を把握している。どちらも非選択状態なら -1。
190: int init_id {};
191: int target_id {};
192:
193: // SCSI デバイス(イニシエータも含む)。非接続なら NULL
194: // 所有権を持たない。
195: SCSIDevice *device[8] {};
196:
197: // イベント
198: Event event {};
199: };
200:
201: // (1<<id) が1つだけ立ててあるビットマップから ID を返す。
202: // ビットが立っていなければ -1 を返す? 複数立ってたら?
203: // SCSIBus と SPC でも使うのでグローバルなインライン関数にしておく。
204: static inline int DecodeID(uint32 mask)
205: {
206: mask &= 0xff;
207: if (mask == 0)
208: return -1;
209: return 31 - __builtin_clz(mask);
210: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.