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