--- nono/vm/lance.cpp 2026/04/29 17:04:55 1.1.1.9 +++ nono/vm/lance.cpp 2026/04/29 17:05:17 1.1.1.13 @@ -4,72 +4,62 @@ // Licensed under nono-license.txt // +// +// Lance (AM7990) +// + #include "lance.h" -#include "bus.h" -#include "config.h" +#include "bitops.h" #include "interrupt.h" -#include "mythread.h" -#include "zram.h" +#include "mainbus.h" +#include "scheduler.h" +#include "subram.h" +// コンストラクタ LanceDevice::LanceDevice() - : inherited("Lance") + : inherited() { + SetName("Lance"); + ClearAlias(); + AddAlias("Lance"); AddAlias("Ethernet"); - devaddr = 0xf1000000; - // func, time は都度設定する - rx_event.SetName("Lance RX"); - tx_event.SetName("Lance TX"); + rx_event.Regist("Lance RX"); + tx_event.Regist("Lance TX"); - monitor.func = (MonitorCallback_t)&LanceDevice::MonitorUpdate; + monitor.func = ToMonitorCallback(&LanceDevice::MonitorUpdate); monitor.SetSize(80, 34); monitor.Regist(ID_MONITOR_LANCE); } +// デストラクタ LanceDevice::~LanceDevice() { } +// 初期化 bool LanceDevice::Init() { - // 親も Init() を持っている if (inherited::Init() == false) { return false; } - const ConfigItem& item = gConfig->Find("ethernet-macaddr"); - const std::string& config_macaddr = item.AsString(); - if (config_macaddr == "rom") { - // ROM に書いてあるのを使うので、ここでは何もしなくていい - } else { - if (config_macaddr == "auto") { - // 自動生成 - // すでに記録されていれば使う。なければ生成して記録したい - if (0) { - } else { - GenerateMacAddr(&macaddr); - } - } else { - // ユーザ指定 - if (ParseMacAddr(macaddr, config_macaddr) == false) { - item.Err("invalid MAC address"); - return false; - } - } + interrupt = GetInterruptDevice(); + subram = GetSubRAMDevice(); - putmsg(1, "macaddr=%s", macaddr.to_string(':').c_str()); - } + // ホストからの受信コールバックを登録 + scheduler->ConnectMessage(MessageID::HOSTNET_RX, this, + ToMessageCallback(&LanceDevice::HostRxCallback)); return true; } +// リセット void -LanceDevice::ResetHard() +LanceDevice::ResetHard(bool poweron) { - std::unique_lock lock(mtx); - reg.rap = 0; ChipStop(); } @@ -84,14 +74,13 @@ LanceDevice::Read(uint32 offset) data = ReadData(); return data; case 1: // RAP - { - std::unique_lock lock(mtx); data = reg.rap; putlog(2, "RAP -> %d", reg.rap); return data; - } + default: + VMPANIC("corrupted offset=%d", offset); + return 0xff; } - __unreachable(); } uint64 @@ -102,13 +91,12 @@ LanceDevice::Write(uint32 offset, uint32 WriteData(data); return 0; case 1: // RAP - { - std::unique_lock lock(mtx); reg.rap = data & 3; return 0; - } + default: + VMPANIC("corrupted offset=%d", offset); + return 0; } - __unreachable(); } uint64 @@ -116,10 +104,8 @@ LanceDevice::Peek(uint32 offset) { switch (offset) { case 0: // RDP - { // 今読めるはずのデータ。 // CSR[123] は STOP 中でなければ不定だがとりあえずそれは無視。 - std::unique_lock lock(mtx); switch (reg.rap) { case 0: return reg.GetCSR0(); @@ -129,16 +115,16 @@ LanceDevice::Peek(uint32 offset) return reg.iadr >> 16; case 3: return reg.csr3; + default: + break; } break; - } case 1: // RAP - { - std::unique_lock lock(mtx); return reg.rap; - } + default: + break; } - __unreachable(); + return 0xff; } void @@ -172,8 +158,6 @@ LADRF:$12345678_12345678 TDRP: screen.Clear(); - // XXX lock - y = 0; // CSR uint16 csr0 = reg.GetCSR0(); @@ -385,15 +369,13 @@ LanceDevice::ReadData() { uint32 data; - std::unique_lock lock(mtx); - // XXX CSR[123] は STOP 中だけ読める。そうでない時は未定義値になる。 switch (reg.rap) { case 0: { data = reg.GetCSR0(); const std::string name = CSR0ToString(data); - putlog(2, "CSR0 -> $%04x<%s>", data, name.c_str()); + putlog(3, "CSR0 -> $%04x<%s>", data, name.c_str()); break; } case 1: @@ -402,7 +384,7 @@ LanceDevice::ReadData() putlog(2, "CSR%d -> $%04x", reg.rap, data); } else { data = 0xcccc & AM7990::CSR1_MASK; - putlog(2, "CSR%d -> $%04x (不定読み出し)", reg.rap, data); + putlog(2, "CSR%d -> $%04x (indefinate read)", reg.rap, data); } break; case 2: @@ -411,7 +393,7 @@ LanceDevice::ReadData() putlog(2, "CSR%d -> $%04x", reg.rap, data); } else { data = 0xcccc & AM7990::CSR2_MASK; - putlog(2, "CSR%d -> $%04x (不定読み出し)", reg.rap, data); + putlog(2, "CSR%d -> $%04x (indefinate read)", reg.rap, data); } break; case 3: @@ -421,11 +403,13 @@ LanceDevice::ReadData() putlog(2, "CSR%d -> $%04x<%s>", reg.rap, data, name.c_str()); } else { data = 0xcccc & AM7990::CSR3_MASK; - putlog(2, "CSR%d -> $%04x (不定読み出し)", reg.rap, data); + putlog(2, "CSR%d -> $%04x (indefinate read)", reg.rap, data); } break; default: - __unreachable(); + VMPANIC("corrupted reg.rap=%d", reg.rap); + data = 0xff; + break; } return data; } @@ -434,8 +418,6 @@ LanceDevice::ReadData() void LanceDevice::WriteData(uint32 data) { - std::unique_lock lock(mtx); - switch (reg.rap) { case 0: { @@ -450,10 +432,16 @@ LanceDevice::WriteData(uint32 data) break; } - if ((data & AM7990::CSR0_INIT)) { + // INIT, STRT は立ち上がりで実行にしてみる。 + // OpenBSD が割り込みクリアするために読んだ CSR0 をそのまま書いて + // くるようなので、毎回 INIT と STRT が走ってしまう。 + // とりあえず立ち上がりでやってみる。 + // XXX 要実機調査 + + if (bit_rising(reg.csr0, data, AM7990::CSR0_INIT)) { ChipInit(); } - if ((data & AM7990::CSR0_STRT)) { + if (bit_rising(reg.csr0, data, AM7990::CSR0_STRT)) { ChipStart(); } @@ -505,6 +493,11 @@ LanceDevice::WriteData(uint32 data) if (reg.IsSTOP()) { reg.iadr = (reg.iadr & 0xff0000) | (data & AM7990::IADR_L_MASK); putlog(2, "IDAR(L)=$%06x", reg.iadr); + +#if 0 + // IADR 設定時にパフォーマンス測定用実時間表示 + scheduler->PutlogRealtime(); +#endif } break; @@ -527,20 +520,25 @@ LanceDevice::WriteData(uint32 data) break; default: - __unreachable(); + VMPANIC("corrupted reg.rap=%d", reg.rap); } } // メモリアクセスについて。 -// AM7990 から見たメモリは LUNA の 3ポートSRAM (ここでは ZRAM デバイス)。 +// LANCE(AM7990) から見たメモリは、メインメモリではなく 3ポートSRAM +// (ここでは SubRAM デバイス)。 +// LANCE のメモリバスは 16bit で、DAL15..8 が SubRAM の偶数アドレス、 +// DAL7..0 が SubRAM の奇数アドレスに接続されている。 +// +// LANCE | DAL15..DAL8 | DAL7..DAL0 +// -------+-------------+------------ +// SubRAM | +0 byte | +1 byte +// | +2 byte | +3 byte // -// AM7990 は CSR3.BSWP が %0 ならリトルエンディアンアクセス、 -// %1 ならビッグエンディアンアクセス。 -// 一方、RAMDevice.Read16() はビッグエンディアン動作になるので、 -// BSWP==%0 ならバイトスワップする。ホストのエンディアンには無関係。 +// メモリアクセスは常にこの構成で、CSR3.BSWP はこの1ワードを SILO(FIFO) に +// 書き込む/から読み出す順序を入れ替えるもの。 // メモリから1ワード読み込む。paddr はワードアラインしていること。 -// バスエラーは起きない。 uint64 LanceDevice::ReadMem(uint32 paddr) { @@ -548,14 +546,8 @@ LanceDevice::ReadMem(uint32 paddr) assertmsg((paddr & 1) == 0, "paddr=$%x", paddr); - // XXX 3ポートRAM のエンディアンとかを調べること - data = gZRAM->Read16(paddr); - if (!reg.IsBSWP()) { - data = __builtin_bswap16(data); - } - // XXX 所要クロックを考察すること - + data = subram->Read16(paddr); return data; } @@ -565,13 +557,8 @@ LanceDevice::WriteMem(uint32 paddr, uint { assertmsg((paddr & 1) == 0, "paddr=$%x", paddr); - if (!reg.IsBSWP()) { - data = __builtin_bswap16(data); - } - // XXX 所要クロックを考察すること - - gZRAM->Write16(paddr, data); + subram->Write16(paddr, data); return 0; } @@ -581,18 +568,9 @@ LanceDevice::PeekMem(uint32 paddr) { uint64 data; - paddr &= 0x1ffff; - paddr += 0x71000000; // XXX 定数にしたほうがいい + data = subram->Peek8(paddr) << 8; + data |= subram->Peek8(paddr + 1); - if (reg.IsBSWP()) { - // ビッグエンディアンアクセス - data = vm_phys_peek_8(paddr) << 8; - data |= vm_phys_peek_8(paddr + 1); - } else { - // リトルエンディアンアクセス - data = vm_phys_peek_8(paddr); - data |= vm_phys_peek_8(paddr + 1) << 8; - } return data; } @@ -601,6 +579,8 @@ LanceDevice::PeekMem(uint32 paddr) void LanceDevice::ChipInit() { + putlog(1, "ChipInit"); + // INIT をセットすると STOP ビットはクリア reg.csr0 |= AM7990::CSR0_INIT; reg.csr0 &= ~AM7990::CSR0_STOP; @@ -611,7 +591,7 @@ LanceDevice::ChipInit() } if (0) { for (int i = 0; i < countof(initblock); i++) { - putlog(3, "initblock[%d] = $%04x", i, initblock[i]); + putlog(2, "initblock[%d] = $%04x", i, initblock[i]); } } @@ -626,11 +606,16 @@ LanceDevice::ChipInit() int tlen = tdrp >> 29; tmd_num = (1 << tlen); - padr = __builtin_bswap16(initblock[1]); - padr <<= 16; - padr |= __builtin_bswap16(initblock[2]); - padr <<= 16; - padr |= __builtin_bswap16(initblock[3]); + padr[0] = initblock[1] & 0xff; + padr[1] = initblock[1] >> 8; + padr[2] = initblock[2] & 0xff; + padr[3] = initblock[2] >> 8; + padr[4] = initblock[3] & 0xff; + padr[5] = initblock[3] >> 8; + + // ホストデバイスに設定 + hostnet->SetMyAddr(padr); + hostnet->SetPromisc((initblock[0] & AM7990::IBMODE_PROM)); // XXX 本当はいろいろ時間がかかりそうなはずだが // 初期化完了 @@ -642,7 +627,7 @@ LanceDevice::ChipInit() void LanceDevice::ChipStart() { - putlog(1, "スタート"); + putlog(1, "Start"); // STRT を書き込むと STOP はクリア reg.csr0 |= AM7990::CSR0_STRT; @@ -659,16 +644,20 @@ LanceDevice::ChipStart() } if ((initblock[0] & AM7990::IBMODE_DRX) == 0) { reg.csr0 |= AM7990::CSR0_RXON; - // ホストからのパケット受信を許可。 - RecvEnabled = true; } // 初回読み込みに備えて RMD1/TMD1 の先読みバッファを無効化する rmd1_ahead = (uint32)-1; tmd1_ahead = (uint32)-1; + // バッファクリア + tx_packet.Clear(); + ChangePhase(TXPhase::IDLE, 0_nsec); ChangePhase(RXPhase::IDLE, 0_nsec); + + // ホストの受信を開始する + hostnet->EnableRx(true); } // STOP ビットによるチップリセット。 @@ -677,7 +666,7 @@ LanceDevice::ChipStart() void LanceDevice::ChipStop() { - putlog(1, "リセット"); + putlog(1, "Reset"); // STOPセットで CSR0 は STOP セット、他は全クリア reg.csr0 = AM7990::CSR0_STOP; @@ -690,19 +679,15 @@ LanceDevice::ChipStop() rmd_cur = 0; // イベントを(あれば)停止。 - rx_event.Stop(); - tx_event.Stop(); - rx_phase = RXPhase::IDLE; - tx_phase = TXPhase::IDLE; + ChangePhase(RXPhase::STOP); + ChangePhase(TXPhase::STOP); - // 受信を停止 - RecvEnabled = false; + // バッファクリア + rx_packet.Clear(); + tx_packet.Clear(); - // 受信バッファクリア - while (!rxq.empty()) { - rxq.pop(); - } - rxpacket.reset(); + // ホストの受信を停止する + hostnet->EnableRx(false); } // 割り込み要因を CSR0 にセットし、必要なら割り込みを上げる。 @@ -718,9 +703,9 @@ void LanceDevice::ChangeInterrupt() { if (reg.IsINTR() && (reg.csr0 & AM7990::CSR0_INEA)) { - gInterrupt->AssertINT(this); + interrupt->AssertINT(this); } else { - gInterrupt->NegateINT(this); + interrupt->NegateINT(this); } } @@ -732,7 +717,6 @@ LanceDevice::ChangeInterrupt() void LanceDevice::TXIdle(Event& event) { - std::lock_guard lock(mtx); tx_phase = TXPhase::IDLE; // TDMD クリア @@ -753,7 +737,7 @@ LanceDevice::TXIdle(Event& event) // その TMD1 に OWN と STP が立っているか if ((tmd1 & AM7990::TMD1_OWN) && (tmd1 & AM7990::TMD1_STP)) { // 取り込みへ - ChangePhase(TXPhase::COPY); + TXCopy(); return; } @@ -768,9 +752,8 @@ LanceDevice::TXIdle(Event& event) // - 次 TMD が OWN できればウェイトした後 TXNEXT フェーズへ。 // - 次 TMD が OWN 出来なければウェイトした後 TXSEND フェーズ。 void -LanceDevice::TXCopy(Event& event) +LanceDevice::TXCopy() { - std::lock_guard lock(mtx); tx_phase = TXPhase::COPY; // TMD0, TMD2 は送信することが確定してから読み出す @@ -778,41 +761,84 @@ LanceDevice::TXCopy(Event& event) uint16 tmd0 = ReadMem(tmd_addr); uint16 tmd2 = ReadMem(tmd_addr + 4); - // アドレスと送信バイト数。送信バイト数は2の補数。 - uint32 bufsrc = ((tmd1 & 0x00ff) << 16) | tmd0; - uint32 src = bufsrc; - int32 bcnt = (int32)(int16)tmd2; + // アドレスと送信バイト数。 + // tmd2 には 2 の補数で送信バイト数が入っているので符号反転する。 + uint32 src = ((tmd1 & 0x00ff) << 16) | tmd0; + int32 bcnt = -(int32)(int16)tmd2; + + // copycnt は bcnt のうち実際にコピーするバイト数 + int32 copycnt; + if (tx_packet.length + bcnt > tx_packet.size()) { + // コピーするとバッファが溢れてしまう計算なら、溢れないように打ち切る。 + copycnt = tx_packet.size() - tx_packet.length; + } else { + // 全部コピーできる。 + copycnt = bcnt; + } - // ローカルの送信バッファにコピー - for (; bcnt < 0; ) { - // XXX バーストコピー未対応 - // XXX BSWP==0の時のバッファ内のエンディアンとかは? + // 送信パケットキュー上の書き込み中バッファに追記する + uint8 *dst = &tx_packet[tx_packet.length]; + tx_packet.length += copycnt; + + // ここから、SubRAM からローカルの送信バッファにコピー + // XXX バーストコピー未対応 + const uint32 srcend = src + bcnt; + const uint8 *dstend = dst + copycnt; + + // src が奇数アドレスから始まっている(そしてコピー可能な)場合 + // (バッファを奇数アドレスから始めたりする奇特な人はいないと思うが) + if (__predict_false((src & 1) != 0) && dst < dstend) { + uint16 data = ReadMem(src & ~1); + if (reg.IsBSWP() == false) { + *dst++ = data >> 8; + } else { + *dst++ = data & 0xff; + } + src++; + } - if (bcnt <= -2 && (src & 1) == 0) { - // 2バイト以上残っててワード境界ならワードアクセス - uint16 data = ReadMem(src); - txq.push_back(data >> 8); - txq.push_back(data & 0xff); - src += 2; - bcnt += 2; - } else if ((src & 1) == 0) { - // 偶数アドレスから1バイト - uint16 data = ReadMem(src); - txq.push_back(data >> 8); - src += 1; - bcnt += 1; + // 残り2バイト以上コピー可能な間 (ここで src はワード境界になっている) + const uint8 *dstend_even; + dstend_even = (const uint8 *)((uintptr_t)dstend & ~(uintptr_t)1); + while (dst < dstend_even) { + uint16 data = ReadMem(src); + if (reg.IsBSWP() == false) { + *dst++ = data & 0xff; + *dst++ = data >> 8; + } else { + *dst++ = data >> 8; + *dst++ = data & 0xff; + } + src += 2; + } + + // src が奇数アドレスで終わっている(そしてコピー可能な)場合 + if (__predict_false(dst < dstend)) { + uint16 data = ReadMem(src); + if (reg.IsBSWP() == false) { + *dst++ = data & 0xff; } else { - // 奇数アドレスから1バイト - uint16 data = ReadMem(src & ~1); - txq.push_back(data & 0xff); - src += 1; - bcnt += 1; + *dst++ = data >> 8; } + src += 2; + } + + // tx_packet が一杯になったら後はディスクリプタが終わるまで SubRAM から + // 読み捨てる。tx_packet の長さに上限があるのはこっち(エミュレータ)の + // 都合であって LANCE はディスクリプタが終わるまで延々読み込んでは出力を + // 続けるようだ。ここでは同じだけ読み込むところ、それにかかる時間を求める + // ところを再現する。tx_packet がイーサネットフレーム長を超過していると + // この後 SEND フェーズで BABBLE ビットを立てるところも再現する。 + // 正確には 1519バイト目の送信で立てるのだが、通常は起きない上に面倒 + // なので省略。1519バイト以上のパケットをホストに流すこともしない。 + while (src < srcend) { + ReadMem(src); + src += 2; } // メモリからの転送にかかった時間をざっくり計算 XXX まだ適当 // そのウェイト後に指定のフェーズへ。 - int t = 700 * (src - bufsrc) / 16; + uint64 t = 700 * bcnt / 16; // ここが ENP なら、送信のため TXSEND フェーズへ if ((tmd1 & AM7990::TMD1_ENP)) { @@ -840,12 +866,19 @@ LanceDevice::TXCopy(Event& event) void LanceDevice::TXSend(Event& event) { - std::lock_guard lock(mtx); tx_phase = TXPhase::SEND; - // 送信 - SendPacket(txq); - txq.clear(); + if (__predict_false(tx_packet.length > 1518)) { + // 本当は 1519バイト目を送ったところで BABL を立てるのだが + // まず起きない上に面倒なので、ここで行う。 + SetInterrupt(AM7990::CSR0_BABL); + + // 1519バイト以上のパケットはホストにも投げない。 + } else { + // 送信 + hostnet->Tx(tx_packet); + } + tx_packet.Clear(); // ENP でなければアンダーフローによるものだった if ((tmd1 & AM7990::TMD1_ENP) == 0) { @@ -870,7 +903,6 @@ LanceDevice::TXSend(Event& event) void LanceDevice::TXNext(Event& event) { - std::lock_guard lock(mtx); tx_phase = TXPhase::NEXT; // このディスクリプタの所有権を放棄 @@ -890,30 +922,53 @@ LanceDevice::TXNext(Event& event) void LanceDevice::ChangePhase(TXPhase new_phase, uint64 time) { - tx_event.Stop(); tx_event.time = time; switch (new_phase) { + case TXPhase::STOP: + scheduler->StopEvent(tx_event); + return; case TXPhase::IDLE: - tx_event.func = (DeviceCallback_t)&LanceDevice::TXIdle; - break; - case TXPhase::COPY: - tx_event.func = (DeviceCallback_t)&LanceDevice::TXCopy; + tx_event.func = ToEventCallback(&LanceDevice::TXIdle); break; case TXPhase::SEND: - tx_event.func = (DeviceCallback_t)&LanceDevice::TXSend; + tx_event.func = ToEventCallback(&LanceDevice::TXSend); break; case TXPhase::NEXT: - tx_event.func = (DeviceCallback_t)&LanceDevice::TXNext; + tx_event.func = ToEventCallback(&LanceDevice::TXNext); break; default: assertmsg(false, "phase %d", (int)new_phase); } - tx_event.Start(); + scheduler->RestartEvent(tx_event); } -// パケット受信通知。NetDriver スレッドから mtx を保持した状態で呼ばれる。 +// 受信フェーズ遷移図 +// +// +--------+ +// | RXSTOP |<- - - (CSR0.STOP 書き込み / リセット) +// +--------+ +// : +// : +‥‥‥+ <‥‥ HostRxCallback (パケット受信) +// v v : +// +-------------+ : +// +‥‥‥>| RXIDLE |‥‥+ Idle +// : +-------------+ +// : | | ^ +// : | | : +// : v | : +// : +--------+ | +--------+ +// +‥| RXMISS | | | RXNEXT | +// +--------+ | +--------+ +// | ^ | +// | : | +// v : v +// +-----------+ +// | RXCOPY | 関数呼び出し ----> +// +-----------+ イベントによる遷移 ‥‥> + +// パケット受信通知。HostNet から呼ばれる。 void -LanceDevice::NotifyRecvPacket() +LanceDevice::HostRxCallback(MessageID msgid, uint32 arg) { // RXIDLE ポーリングのウェイト中ならすぐに始める。 // そうでないときは RXIDLE に戻ったところで処理される。 @@ -927,7 +982,6 @@ LanceDevice::NotifyRecvPacket() void LanceDevice::RXIdle(Event& event) { - std::lock_guard lock(mtx); rx_phase = RXPhase::IDLE; putlog(3, "%s[%d]", __func__, rmd_cur); @@ -945,44 +999,17 @@ LanceDevice::RXIdle(Event& event) rmd1_ahead = (uint32)-1; // 受信しているか - if (rxq.size() != 0) { + if (hostnet->Rx(&rx_packet)) { // その RMD1 に OWN が立っているか if ((rmd1 & AM7990::RMD1_OWN)) { // ここが STP になる rmd1 |= AM7990::RMD1_STP; - // キューから取り出す - // ムーブ代入 - rxpacket = std::move(rxq.front()); - rxq.pop(); - - // PROMISC と PADR と ブロードキャストとマルチキャスト。 - if (__predict_true(!(initblock[0] & AM7990::IBMODE_PROM))) { - uint64 dst; - dst = rxpacket->peek(0); - // XXX Logical Address Filter を実装すること - if ((dst & 1) == 0) { - // ユニキャスト - // 残りの 5byte を覗き見 - for (int i = 1; i < 6; i++) { - dst <<= 8; - dst |= rxpacket->peek(i); - } - if (dst != padr) { - // 自己宛ではない - // ドロップして次のパケット待ち - rxpacket.reset(); - ChangePhase(RXPhase::IDLE); - return; - } - } - } - - // RXCOPY へ移行する - ChangePhase(RXPhase::COPY); + // RXCOPY へ直接移行する。 + RXCopy(); } else { - // バッファが無いので MISS + // バッファが無いので MISS へ直接移行する。 // MISS までの時間は受信処理によって表現されているはず - ChangePhase(RXPhase::MISS); + RXMiss(); } } else { if ((rmd1 & AM7990::RMD1_OWN)) { @@ -998,9 +1025,8 @@ LanceDevice::RXIdle(Event& event) // 受信バッファからディスクリプタのバッファに書き込み、 // 書き込みにかかる時間相当分をウェイトする。 void -LanceDevice::RXCopy(Event& event) +LanceDevice::RXCopy() { - std::lock_guard lock(mtx); rx_phase = RXPhase::COPY; putlog(1, "%s[%d]", __func__, rmd_cur); @@ -1010,63 +1036,85 @@ LanceDevice::RXCopy(Event& event) uint16 rmd0 = ReadMem(rmd_addr); uint16 rmd2 = ReadMem(rmd_addr + 4); - // アドレスと受信バッファのバイト数。バッファサイズは2の補数。 + // アドレスと受信バッファのバイト数。 uint32 bufdst = ((rmd1 & 0x00ff) << 16) | rmd0; uint32 dst = bufdst; - int32 bcnt = (int32)(int16)rmd2; + // rmd2 は 2 の補数 + int32 bcnt = -(int32)(int16)rmd2; - if (-bcnt < rxpacket->size()) { - putlog(1, "will be remain: %d < %zd", -bcnt, rxpacket->size()); + if (bcnt < rx_packet.length) { + putlog(1, "will be remain: %d < %d", bcnt, rx_packet.length); } - // ローカルの受信バッファからディスクリプタバッファにコピー - for (; bcnt < 0; ) { - int remsize = rxpacket->size(); - if (remsize == 0) { - break; - } + uint8 *src = &rx_packet[0]; + int remsize = rx_packet.length; + + if (bcnt > remsize) { + bcnt = remsize; + } + + // コピー後の残りサイズにしておく + remsize -= bcnt; + // ローカルの受信バッファからディスクリプタバッファにコピー + for (; bcnt > 0; ) { // XXX バーストコピー未対応 - // XXX BSWP==0の時のバッファ内のエンディアンとかは? uint16 data; - if (bcnt <= -2 && remsize >= 2 && (dst & 1) == 0) { + if (bcnt >= 2 && (dst & 1) == 0) { // 2バイト以上残っててワード境界ならワードアクセス - data = rxpacket->dequeue() << 8; - data |= rxpacket->dequeue(); + if (reg.IsBSWP() == false) { + data = *src++; + data |= (*src++) << 8; + } else { + data = (*src++) << 8; + data |= *src++; + } WriteMem(dst, data); dst += 2; - bcnt += 2; + bcnt -= 2; } else if ((dst & 1) == 0) { // 偶数アドレスへ1バイト // 奇数バイトには不定データが出るらしいがここでは 0 にする - data = rxpacket->dequeue() << 8; + if (reg.IsBSWP() == false) { + data = *src++; + } else { + data = (*src++) << 8; + } WriteMem(dst, data); dst += 1; - bcnt += 1; + bcnt -= 1; } else { // 奇数アドレスへ1バイト // 偶数バイトには不定データが出るらしいがここでは 0 にする - data = rxpacket->dequeue(); + if (reg.IsBSWP() == false) { + data = (*src++) << 8; + } else { + data = *src++; + } WriteMem(dst & ~1, data); dst += 1; - bcnt += 1; + bcnt -= 1; } } // 全部書き出せたらここが ENP - if (rxpacket->size() == 0) { + if (remsize == 0) { rmd1 |= AM7990::RMD1_ENP; // ENP なら MCNT を更新 WriteMem(rmd_addr + 6, (dst - bufdst) & 0xfff); } else { rmd1 &= ~AM7990::RMD1_ENP; - putlog(1, "copy remain %zd", rxpacket->size()); + putlog(1, "copy remain %d", remsize); + + // 前詰めにコピーする + memmove(&rx_packet[0], src, remsize); + rx_packet.length = remsize; } // メモリへの転送にかかった時間をざっくり計算 XXX まだ適当 // そのウェイト後に RXNEXT フェーズへ。 - int t = 700 * (dst - bufdst) / 16; + uint64 t = 700 * (dst - bufdst) / 16; ChangePhase(RXPhase::NEXT, t); } @@ -1078,7 +1126,6 @@ LanceDevice::RXCopy(Event& event) void LanceDevice::RXNext(Event& event) { - std::lock_guard lock(mtx); rx_phase = RXPhase::NEXT; putlog(1, "%s[%d]", __func__, rmd_cur); @@ -1087,6 +1134,7 @@ LanceDevice::RXNext(Event& event) if ((rmd1 & AM7990::RMD1_ENP)) { // ENP なら、RXIDLE へ。 + rx_packet.Clear(); ChangePhase(RXPhase::IDLE); } else { // 次のディスクリプタを先読み @@ -1095,13 +1143,15 @@ LanceDevice::RXNext(Event& event) if ((rmd1_ahead & AM7990::RMD1_OWN)) { // 次ディスクリプタが所有できれば RXCOPY へ。 - ChangePhase(RXPhase::COPY); is_chained = true; } else { // この時点で所有できなければオーバーフロー。 // XXX BUFF と OFLO の違いがよくわからん rmd1 |= AM7990::RMD1_BUFF | AM7990::RMD1_OFLO; + // 書き込み先が無いのでこのパケットは打ち切ってみる + rx_packet.Clear(); + // 次のディスクリプタをポールするため RXIDLE へ。 ChangePhase(RXPhase::IDLE); } @@ -1115,20 +1165,21 @@ LanceDevice::RXNext(Event& event) // 次のディスクリプタへ rmd_cur = (rmd_cur + 1) % rmd_num; + // どちらにしても受信割り込み + SetInterrupt(AM7990::CSR0_RINT); + if (is_chained) { + // 次のディスクリプタがある場合は RXCopy に直接遷移する。 rmd1 = rmd1_ahead; rmd1_ahead = -1; + RXCopy(); } - - // どちらにしても受信割り込み - SetInterrupt(AM7990::CSR0_RINT); } // RXMISS 受信失敗処理。 void -LanceDevice::RXMiss(Event& event) +LanceDevice::RXMiss() { - std::lock_guard lock(mtx); rx_phase = RXPhase::MISS; putlog(1, "%s", __func__); @@ -1137,7 +1188,7 @@ LanceDevice::RXMiss(Event& event) SetInterrupt(AM7990::CSR0_MISS | AM7990::CSR0_RINT); // 受信データを破棄。 - rxpacket.reset(); + rx_packet.Clear(); // そのまますぐにアイドルフェーズに戻るんだろうか ChangePhase(RXPhase::IDLE); @@ -1150,25 +1201,21 @@ LanceDevice::RXMiss(Event& event) void LanceDevice::ChangePhase(RXPhase new_phase, uint64 time) { - rx_event.Stop(); rx_event.time = time; switch (new_phase) { + case RXPhase::STOP: + scheduler->StopEvent(rx_event); + return; case RXPhase::IDLE: - rx_event.func = (DeviceCallback_t)&LanceDevice::RXIdle; - break; - case RXPhase::COPY: - rx_event.func = (DeviceCallback_t)&LanceDevice::RXCopy; + rx_event.func = ToEventCallback(&LanceDevice::RXIdle); break; case RXPhase::NEXT: - rx_event.func = (DeviceCallback_t)&LanceDevice::RXNext; - break; - case RXPhase::MISS: - rx_event.func = (DeviceCallback_t)&LanceDevice::RXMiss; + rx_event.func = ToEventCallback(&LanceDevice::RXNext); break; default: assertmsg(false, "phase %d", (int)new_phase); } - rx_event.Start(); + scheduler->RestartEvent(rx_event); }