|
|
1.1 root 1: //
2: // nono
1.1.1.3 ! root 3: // Copyright (C) 2020 nono project
! 4: // Licensed under nono-license.txt
1.1 root 5: //
6:
7: // SCSI バス
8:
9: #include "scsibus.h"
10: #include "scsidev.h"
11:
12: // time 時間後に呼び出すコールバックをセットする。
13: // func の書式が面倒なのを省略して書きたいため。
14: #define CallAfter(func_, name_, time_) do { \
15: event.func = (DeviceCallback_t)&SCSIBus::func_; \
16: event.SetName(name_); \
17: event.code = 0; \
18: event.time = time_; \
1.1.1.2 root 19: event.Start(); \
1.1 root 20: } while (0)
21:
22: // コンストラクタ
23: SCSIBus::SCSIBus()
24: {
25: logname = "scsibus";
26: devname = "SCSIBus";
27:
28: event.dev = this;
29: event.SetName("SCSIBus"); // 複数出来たら名前どうするべか
30: }
31:
32: // デストラクタ
33: SCSIBus::~SCSIBus()
34: {
35: }
36:
37: // このバスにデバイスを接続する (初期化時に使う)
38: bool
39: SCSIBus::Attach(SCSIDevice *dev, int id)
40: {
41: if (device[id] != NULL) {
42: putlog(0, "id=%d already exist?", id);
43: return false;
44: }
45: putlog(3, "Attach #%d", id);
46:
47: // バスとデバイスは相互に接続する。どちらも所有のないただのポインタ。
48: device[id] = dev;
49: dev->Attach(this, id);
50:
51: return true;
52: }
53:
54: // RST を上げる
55: void
56: SCSIBus::AssertRST()
57: {
58: putlog(4, "%s", __func__);
59:
60: // すでに上がってたら何もしない
61: if (rst) {
62: return;
63: }
64:
65: rst = true;
66:
67: // 即バスリセットを全員に通知
68: for (int i = 0; i < 8; i++) {
69: if (device[i]) {
70: device[i]->BusResetCB();
71: }
72: }
73:
74: // コールバック終了後に内部状態もクリア
75: init_id = -1;
76: target_id = -1;
77: }
78:
79: // RST を下げる
80: void
81: SCSIBus::NegateRST()
82: {
83: putlog(4, "%s", __func__);
84:
85: rst = false;
86:
87: // 特にすることはない
88: }
89:
90: // SEL を上げる
91: void
92: SCSIBus::AssertSEL(uint id)
93: {
94: putlog(4, "%s(#%d)", __func__, id);
95:
96: // すでに上がってたら何もしない
97: if ((sel & (1 << id))) {
98: return;
99: }
100:
101: sel |= (1 << id);
102:
103: if (GetPhase() == SCSI::Phase::BusFree) {
104: // バスフリーフェーズなら、アービトレーションなしでセレクション
105: // フェーズに移行。
106: //
107: // 仕様Phase |Free |<---- Selection ---->|
108: //
109: // BSY ~~\__________________________/~~~~~~~
110: //
111: // Data -----------< Initiator&Target ID >---
112: //
113: // SEL ________________/~~~~~~~~~~~~~~~~\___
114: // ^イマココ
115: //
116: // 実装Phase |<- Free ->|<-- Selection ->|
117: //
118: // SCSI 本にはデータバスが変化したところから Selection フェーズと
119: // 書いてあるように読めるけど(上)、ここでは SEL がアサートされた
120: // ところからとする(下)。
121:
122: SetPhase(SCSI::Phase::Selection);
123:
124: // SEL を上げた人がイニシエータ
125: init_id = id;
126:
127: // 即セレクション開始。
128: CallAfter(SelectionStart, "SCSIBus Selection Start", 0);
129:
130: } else if (GetPhase() == SCSI::Phase::Arbitration) {
131: // アービトレーションフェーズなら、
132: // アービトレーションに勝ってセレクションフェーズに移行途中。
133: // この場合はセレクションフェーズに移行するのはここではなく
134: // BSY の立ち下がりなので、そっち参照。
135: //
136: // 仕様Phase Arbitration |---|<--- Selection -->|
137: //
138: // BSY /~~~~~~~~~~~~~~~\_______________/~~~~~~
139: //
140: // Data <InitiatorID>< Initiator&Target ID >---
141: //
142: // SEL __________/~~~~~~~~~~~~~~~~~~~~~~~~\___
143: // ^イマココ
144: //
145: // 実装Phase <-Arbitration ->|<--- Selection -->|
146:
147: // SEL を上げた人がイニシエータなことだけはここで覚えておく。
148: init_id = id;
149:
150: } else {
151: // どうする?
152: PANIC("AssertSEL in %s phase\n", GetPhaseName());
153: }
154: }
155:
156: // SEL を下げる
157: void
158: SCSIBus::NegateSEL(uint id)
159: {
160: putlog(4, "%s(#%d)", __func__, id);
161:
162: // すでに下りてたら何もしない
163: if ((sel & (1 << id)) == 0) {
164: return;
165: }
166:
167: sel &= ~(1 << id);
168:
169: if (GetPhase() == SCSI::Phase::Selection) {
170: // セレクションフェーズで SEL を下げる
171: if (bsy) {
172: // BSY が立っていれば、ターゲットがセレクションに応答したので
173: // イニシエータが SEL を下げて、これから任意時間後にターゲットが
174: // 情報転送フェーズに移行する。
175:
176: CallAfter(StartTransfer, "SCSIBus Transfer Start", 400_nsec);
177: } else {
178: // BSY が立ってなければ、ターゲットが応答しなかったことに
179: // イニシエータがあきらめて SEL を下げたので、何もせず
180: // バスフリーフェーズに移行。
181: BusFree(id);
182: }
183: } else {
184: // どうする?
185: PANIC("NegateSEL in %s phase\n", GetPhaseName());
186: }
187: }
188:
189: // BSY を上げる
190: void
191: SCSIBus::AssertBSY(uint id)
192: {
193: putlog(4, "%s(#%d)", __func__, id);
194:
195: // すでに上がってたら何もしない
196: if ((bsy & (1 << id))) {
197: return;
198: }
199:
200: bsy |= (1 << id);
201:
202: if (GetPhase() == SCSI::Phase::BusFree) {
203: // バスフリーフェーズだったらアービトレーション開始
204:
205: // 実際のアービトレーションは、
206: // バスフリーを検出してから規定時間経過後に、データバスに自身の ID を
207: // 出して BSY を立てる。規定時間内に他のデバイスもアービトレーションに
208: // 参加すれば ID の高い方が勝つ。負けた方は規定時間内にアービトレー
209: // ションを取り下げる。規定期間経って勝っていればアービトレーション
210: // 成功となり、セレクションフェーズに移る。
211: // のだが、エミュレータ上では、今の所アービトレーションで競合が起きる
212: // ことはないため、アービトレーションは規定時間経過して必ず成功する。
213: // ちゃんとしたアービトレーションを実装する場合はたぶんこれだけでは
214: // 済まないと思うので書き換えること。
215:
216: SetPhase(SCSI::Phase::Arbitration);
217:
218: } else if (GetPhase() == SCSI::Phase::Arbitration) {
219: // アービトレーションフェーズだったら、アービトレーションに参加。
220: // 今は起きない。
221: PANIC("AssertBSY(#%d) in arbitration", id);
222:
223: } else if (GetPhase() == SCSI::Phase::Selection) {
224: // セレクションフェーズだったら、転送フェーズに移行中。
225: // ターゲットが BSY をアサートしたので次は規定時間経過後に
226: // SelectionAck() を呼び出してイニシエータに SEL を下げさせる。
227:
228: CallAfter(SelectionAck, "SCSIBus Selection Ack", 90_nsec);
229: } else {
230: // どうする?
231: PANIC("AssertBSY(#%d) in %s phase\n", id, GetPhaseName());
232: }
233: }
234:
235: // BSY を下げる
236: void
237: SCSIBus::NegateBSY(uint id)
238: {
239: putlog(4, "%s(#%d)", __func__, id);
240:
241: // すでに下りてたら何もしない
242: if ((bsy & (1 << id)) == 0) {
243: return;
244: }
245:
246: bsy &= ~(1 << id);
247:
248: if (GetPhase() == SCSI::Phase::Transfer) {
249: // 転送フェーズから BSY を下げたらバスフリーに移行。
250: BusFree(id);
251:
252: } else if (GetPhase() == SCSI::Phase::Arbitration) {
253: // アービトレーションフェーズなら、
254: // イニシエータが BSY を下ろすことでセレクションフェーズに移行。
255: //
256: // この少し上の AssertSEL() 中のタイムチャート参照。
257:
258: SetPhase(SCSI::Phase::Selection);
259:
260: // 即セレクション開始。
261: CallAfter(SelectionStart, "SCSIBus Selection Start", 0);
262:
263: } else {
264: // どうする?
265: PANIC("NegateBSY(#%d) in %s phase\n", id, GetPhaseName());
266: }
267: }
268:
269: // MSG, CD, IO 信号線を変更する。
270: void
271: SCSIBus::SetXfer(uint8 val)
272: {
273: uint8 oldxfer = xfer;
274:
275: xfer = val & (SCSI::MSG | SCSI::CD | SCSI::IO);
276: if (oldxfer != xfer) {
277: if (GetPhase() == SCSI::Phase::Transfer) {
278: putlog(4, "SetXfer(%x:%s)", xfer, SCSI::GetXferPhaseName(xfer));
279: } else {
280: putlog(4, "SetXfer(%x)", xfer);
281: }
282: }
283: }
284:
285: // REQ を上げる
286: void
287: SCSIBus::AssertREQ()
288: {
289: if (GetPhase() == SCSI::Phase::Transfer) {
290: // REQ を上げると言いつつ、ここで転送レートの時間調整をしているので
291: // (scsibus.h の解説参照) REQ を実際に上げるのはイベント発生後。
292: // XXX 本来ターゲットデバイスがタイマー持っててやるべきだけど、
293: // イベントがもう一つ増えるのもうざいので。どのみち、ここの REQ
294: // 通知はイベントにしないといけないので。
295:
296: uint64 after = 0;
297: uint64 now = gScheduler->GetVirtTime();
298: if (now < last_req_time + 1_usec) {
299: after = (last_req_time + 1_usec) - now;
300: }
301: CallAfter(TransferReq, "SCSIBus AssertREQ", after);
302: } else {
303: // 転送フェーズでないのに上げることはないと思うけど
304: // とりあえず通知もせずその場で上げておくか?
305: putlog(4, "%s", __func__);
306: req = true;
307: }
308: }
309:
310: // ターゲットが REQ を立てようとして所定時間経ったので REQ を立てて
311: // イニシエータの TransferReqCB を呼び出すところ。
312: void
313: SCSIBus::TransferReq(int dummy)
314: {
315: putlog(4, "AssertREQ");
316: req = true;
317:
318: assert(device[init_id]);
319: device[init_id]->TransferReqCB();
320: }
321:
322: // REQ を下げる
323: void
324: SCSIBus::NegateREQ()
325: {
326: putlog(4, "%s", __func__);
327: req = false;
328: }
329:
330: // ACK を上げる
331: void
332: SCSIBus::AssertACK()
333: {
334: putlog(4, "%s", __func__);
335: ack = true;
336:
337: if (GetPhase() == SCSI::Phase::Transfer) {
338: assert(device[target_id]);
339: device[target_id]->TransferCB();
340: }
341: }
342:
343: // ACK を下げる
344: void
345: SCSIBus::NegateACK()
346: {
347: putlog(4, "%s", __func__);
348: ack = false;
349:
350: if (GetPhase() == SCSI::Phase::Transfer) {
351: assert(device[target_id]);
352: device[target_id]->TransferAckCB();
353: }
354: }
355:
356: // ATN を上げる
357: void
358: SCSIBus::AssertATN()
359: {
360: putlog(4, "%s", __func__);
361: atn = true;
362: }
363:
364: // ATN を下げる
365: void
366: SCSIBus::NegateATN()
367: {
368: putlog(4, "%s", __func__);
369: atn = false;
370: }
371:
372: // バスフリーフェーズに移行。
373: // id はこのバスフリーを行ったデバイス (0..7)
374: void
375: SCSIBus::BusFree(int id)
376: {
377: putlog(4, "%s", __func__);
378:
379: SetPhase(SCSI::Phase::BusFree);
380:
381: // 即全デバイスに通知。
382: for (int i = 0; i < countof(device); i++) {
383: if (device[i]) {
384: device[i]->BusFreeCB(id);
385: }
386: }
387:
388: // コールバック終了後に内部状態もクリア
389: init_id = -1;
390: target_id = -1;
391:
392: // 通知された各デバイスは規定時間内に他の信号線も取り下げなければ
393: // ならない。
394: }
395:
396: // セレクションから情報転送フェーズまで
397: //
398: // 仕様Phase <- Selection ->|
399: //
400: // BSY _______________/~~~~~~~~~
401: //
402: // Data Initiator&Target ID >----
403: //
404: // SEL ~~~~~~~~~~~~~~~~~~~~~~\__
405: //
406: // -(1)->|--(2)-->|-(3)->|
407: //
408: // セレクションフェーズ開始後、(ターゲット)デバイスは、SEL がアサート、
409: // (IO がネゲート)、データバスの自身の ID がアサート、が 400nsec 以上
410: // 継続することで、自身がターゲットに選択されていることを認識する。(1)
411: // ターゲットは (1) から 200usec 以内に BSY をアサートする。(2)
412: // イニシエータは (2) から 90nsec 以上後に SEL (とデータ) を下げる。(3)
413: //
414: // 実装では、セレクションフェーズ開始が SelectionStart() の呼び出しに対応。
415: //
416: // ターゲットデバイスが存在していない場合はタイムアウトとなるが、これは
417: // ホストが処理するのでバスは何もしない。
418: // ターゲットデバイスが存在していれば SelectionStart() から 400nsec 後 +
419: // 200usec 以内…なので適当に間をとって 100usec 後に Selection() コールバック
420: // を呼び出す(1+2)。
421: //
422: // Selection() コールバック(が呼び出す device->SelectionCB()) は通常 BSY を
423: // アサートするはずであり、そこから 90nsec 後に SelectionAck() コールバック
424: // を呼び出す(3)。
425: //
426: // SelectionAck() コールバック(が呼び出す device->SelectionAckCB()) は通常
427: // SEL をネゲートするはずであり、そこから情報転送フェーズへの移行には特に
428: // 規定はなさそうなので、400nsec 後に StartTransfer() をコールバックして
429: // デバイスを情報転送フェーズに移行させる。
430:
431: // セレクションフェーズ開始。
432: void
433: SCSIBus::SelectionStart()
434: {
435: // この時点でデータバスには自身の ID と相手の ID が立っているはず。
436: // 一方 sel は SEL 信号線を立てた人のビットを持っているので、
437: // これでターゲットを特定する。
438: uint target_mask = data ^ sel;
439: if (__builtin_popcount(target_mask) != 1) {
440: // データバスから自身を除いた結果立ってるビットが1個以外だったら
441: // 誰も応答しないので、何もせず帰ってよい。
1.1.1.2 root 442: putlog(3, "%s (No target specified; databus=$%02x)", __func__, data);
1.1 root 443: return;
444: }
445:
446: // ターゲット ID はここで取得
447: target_id = DecodeID(target_mask);
448: if (device[target_id] == NULL) {
449: // ターゲットデバイスがいなければ、バスは何もしない。
450: // (その結果イニシエータでタイムアウトが起きるはず)
451: putlog(4, "%s (No target)", __func__);
452: return;
453: }
454:
455: // ターゲットデバイスがいれば、規定時間経過後ターゲットに通知。
456: putlog(4, "%s", __func__);
457: CallAfter(Selected, "SCSIBus Selected", 100_usec);
458: }
459:
460: // SelectionStart() にてターゲットが選択されて規定時間経ったので
461: // そろそろターゲットが BSY を立てる頃に呼ばれるコールバック。
462: void
463: SCSIBus::Selected(int dummy)
464: {
465: // ターゲットに通知。ターゲットがいることは確定している。
466: assert(device[target_id]);
467: device[target_id]->SelectionCB();
468: }
469:
470: // ターゲットが BSY を立てて規定時間経ったので
471: // そろそろイニシエータが SEL を下げることに呼ばれるコールバック。
472: void
473: SCSIBus::SelectionAck(int dummy)
474: {
475: // イニシエータに通知。
476: assert(device[init_id]);
477: device[init_id]->SelectionAckCB();
478: }
479:
480: // 情報転送フェーズ開始を通知する。
481: // BSY を上げて情報転送フェーズに移行したところで呼ばれる。
482: void
483: SCSIBus::StartTransfer(int dummy)
484: {
485: SetPhase(SCSI::Phase::Transfer);
486:
487: assert(device[target_id]);
488: device[target_id]->StartTransferCB();
489: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.