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