--- nono/vm/lance.cpp 2026/04/29 17:04:28 1.1.1.1 +++ nono/vm/lance.cpp 2026/04/29 17:04:58 1.1.1.10 @@ -1,47 +1,34 @@ // // nono -// Copyright (C) 2018 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt // #include "lance.h" -#include "configfile.h" #include "bus.h" -#include "lunarom.h" -#include "mpu.h" +#include "config.h" +#include "interrupt.h" +#include "mythread.h" #include "zram.h" -#include "mystring.h" - -LanceDevice *gLance; - -static void *lance_run(void *); LanceDevice::LanceDevice() + : inherited("Lance") { - logname = "lance"; - devname = "Lance"; - devaddr = 0xf1000000; + AddAlias("Ethernet"); - monitor.Init(80, 34); + devaddr = 0xf1000000; - rx_event.dev = this; - rx_event.func = (DeviceCallback_t)&LanceDevice::Callback; - rx_event.code = 0; - rx_event.time = 0; // 都度設定する - rx_event.name = "Lance RX"; - - tx_event.dev = this; - tx_event.func = (DeviceCallback_t)&LanceDevice::Callback; - tx_event.code = 0; - tx_event.time = 0; // 都度設定する - tx_event.name = "Lance TX"; + // func, time は都度設定する + rx_event.SetName("Lance RX"); + tx_event.SetName("Lance TX"); + + monitor.func = (MonitorCallback_t)&LanceDevice::MonitorUpdate; + monitor.SetSize(80, 34); + monitor.Regist(ID_MONITOR_LANCE); } LanceDevice::~LanceDevice() { - // スレッド終了を指示 - std::unique_lock lock(mtx); - request |= REQ_TERMINATE; - cv.notify_one(); } bool @@ -52,9 +39,8 @@ LanceDevice::Init() return false; } - const char *key = "ethernet.macaddr"; - const std::string config_macaddr = gConfig->ReadStr(key, "auto"); - macaddr_t macaddr; + const ConfigItem& item = gConfig->Find("ethernet-macaddr"); + const std::string& config_macaddr = item.AsString(); if (config_macaddr == "rom") { // ROM に書いてあるのを使うので、ここでは何もしなくていい } else { @@ -68,25 +54,14 @@ LanceDevice::Init() } else { // ユーザ指定 if (ParseMacAddr(macaddr, config_macaddr) == false) { - gConfig->Err(key, "%s: invalid MAC address", - config_macaddr.c_str()); + item.Err("invalid MAC address"); return false; } } - putmsg(1, "macaddr=%s", macaddr.to_string().c_str()); - - // ROM に書き込む - if (gPROM->SetMacAddr(macaddr) == false) { - putmsg(1, "Cannot set macaddr in PROM"); - return false; - } + putmsg(1, "macaddr=%s", macaddr.to_string(':').c_str()); } - // ワーカスレッド起動 - pthread_t th; - pthread_create(&th, NULL, lance_run, NULL); - return true; } @@ -100,22 +75,15 @@ LanceDevice::ResetHard() } uint64 -LanceDevice::Read8(uint32 addr) -{ - putlog(0, "未実装バイト読み込み $%08x", addr); - return 0xff; -} - -uint64 -LanceDevice::Read16(uint32 addr) +LanceDevice::Read(uint32 offset) { uint32 data; - switch (addr - devaddr) { + switch (offset) { case 0: // RDP data = ReadData(); return data; - case 2: // RAP + case 1: // RAP { std::unique_lock lock(mtx); data = reg.rap; @@ -123,57 +91,58 @@ LanceDevice::Read16(uint32 addr) return data; } } - putlog(0, "未実装ワード読み込み $%08x", addr); - return 0xffff; -} - -uint64 -LanceDevice::Read32(uint32 addr) -{ - putlog(0, "未実装ロング読み込み $%08x", addr); - return 0xffffffff; -} - -uint64 -LanceDevice::Write8(uint32 addr, uint32 data) -{ - putlog(0, "未実装バイト書き込み $%08x <- %02x", addr, data); - return 0; + __unreachable(); } uint64 -LanceDevice::Write16(uint32 addr, uint32 data) +LanceDevice::Write(uint32 offset, uint32 data) { - switch (addr - devaddr) { + switch (offset) { case 0: // RDP WriteData(data); return 0; - case 2: // RAP + case 1: // RAP { std::unique_lock lock(mtx); reg.rap = data & 3; return 0; } } - putlog(0, "未実装ワード書き込み $%08x <- %04x", addr, data); - return 0; + __unreachable(); } uint64 -LanceDevice::Write32(uint32 addr, uint32 data) +LanceDevice::Peek(uint32 offset) { - putlog(0, "未実装ロング書き込み $%08x <- %08x", addr, data); - return 0; -} - -uint64 -LanceDevice::Peek8(uint32 addr) -{ - return 0xff; + switch (offset) { + case 0: // RDP + { + // 今読めるはずのデータ。 + // CSR[123] は STOP 中でなければ不定だがとりあえずそれは無視。 + std::unique_lock lock(mtx); + switch (reg.rap) { + case 0: + return reg.GetCSR0(); + case 1: + return reg.iadr & 0xffff; + case 2: + return reg.iadr >> 16; + case 3: + return reg.csr3; + } + break; + } + case 1: // RAP + { + std::unique_lock lock(mtx); + return reg.rap; + } + } + __unreachable(); } -bool -LanceDevice::MonitorUpdate() +void +LanceDevice::MonitorUpdate(Monitor *, TextScreen& screen) { /* CSR0: $1111 ERR BABL CERR MISS MERR RINT TINT IDON IADR:$123456 @@ -193,9 +162,7 @@ LADRF:$12345678_12345678 TDRP: // name を表示。(var & bit) ならオン(反転)表示。 // bitpos は 0..15 で [0] が MSB にあたる。 #define MonitorBit(var, bit, name) do { \ - monitor.Print(x, y, \ - (((var) & (bit)) ? TA::On : TA::Off), \ - "%s", name); \ + screen.Puts(x, y, TA::OnOff((var) & (bit)), name); \ x += strlen(name) + 1; \ } while (0) @@ -203,19 +170,19 @@ LADRF:$12345678_12345678 TDRP: int y; int i; - monitor.Clear(); + screen.Clear(); // XXX lock y = 0; // CSR uint16 csr0 = reg.GetCSR0(); - monitor.Print(0, y, "CSR0:$%04x", csr0); - MonitorReg(11, y, csr0 >> 8, csr0names); - MonitorReg(11, y + 1, csr0, csr0names + 8); + screen.Print(0, y, "CSR0:$%04x", csr0); + MonitorReg(screen, 11, y, csr0 >> 8, csr0names); + MonitorReg(screen, 11, y + 1, csr0, csr0names + 8); - monitor.Print(54, y++, "IADR:$%06x", reg.iadr); - monitor.Print(54, y, "CSR3:$%04x", reg.csr3); + screen.Print(54, y++, "IADR:$%06x", reg.iadr); + screen.Print(54, y, "CSR3:$%04x", reg.csr3); // CSR3 は下位3ビットだけなので手動で配置 x = 65; MonitorBit(reg.csr3, AM7990::CSR3_BSWP, csr3names[13]); @@ -223,11 +190,11 @@ LADRF:$12345678_12345678 TDRP: MonitorBit(reg.csr3, AM7990::CSR3_BCON, csr3names[15]); y++; - monitor.Print(0, y, ""); + screen.Puts(0, y, ""); y++; // MODE は bit15 と bit6-0 の8ビットだけ使われてるので // 途中を省略して 8ビットとして表示する - monitor.Print(0, y, "MODE:$%04x", initblock[0]); + screen.Print(0, y, "MODE:$%04x", initblock[0]); x = 11; MonitorBit(initblock[0], AM7990::IBMODE_PROM, ib_mode_names[0]); MonitorBit(initblock[0], AM7990::IBMODE_INTL, ib_mode_names[1]); @@ -239,12 +206,12 @@ LADRF:$12345678_12345678 TDRP: MonitorBit(initblock[0], AM7990::IBMODE_DRX, ib_mode_names[7]); y++; - monitor.Print(0, y, "PADR: %02x:%02x:%02x:%02x:%02x:%02x", + screen.Print(0, y, "PADR: %02x:%02x:%02x:%02x:%02x:%02x", initblock[1] & 0xff, initblock[1] >> 8, initblock[2] & 0xff, initblock[2] >> 8, initblock[3] & 0xff, initblock[3] >> 8); // XXX LADRF のバイト順未検証 - monitor.Print(0, y + 1, "LADRF:$%04x%04x_%04x%04x", + screen.Print(0, y + 1, "LADRF:$%04x%04x_%04x%04x", initblock[7], initblock[6], initblock[5], @@ -252,19 +219,19 @@ LADRF:$12345678_12345678 TDRP: // RDRP uint32 rdrp = initblock[8] | (initblock[9] << 16); - monitor.Print(31, y, "RDRP:$%08x (RDRA=$%06x entries=%d)", + screen.Print(31, y, "RDRP:$%08x (RDRA=$%06x entries=%d)", rdrp, rmd_base, rmd_num); y++; // TDRP uint32 tdrp = initblock[10] | (initblock[11] << 16); - monitor.Print(31, y, "TDRP:$%08x (TDRA=$%06x entries=%d)", + screen.Print(31, y, "TDRP:$%08x (TDRA=$%06x entries=%d)", tdrp, tmd_base, tmd_num); y++; y++; // RMD - monitor.Print(0, y, ""); + screen.Puts(0, y, ""); uint16 rmd[4]; uint32 rmd_addr; @@ -275,7 +242,7 @@ LADRF:$12345678_12345678 TDRP: rmd[j] = PeekMem(rmd_addr + j * 2); } x = 6; - monitor.Print(x, y, "Current %02x", rmd_cur); x += 11; + screen.Print(x, y, "Current %02x", rmd_cur); x += 11; MonitorBit(rmd[1], AM7990::RMD1_OWN, rmd1_names[0]); MonitorBit(rmd[1], AM7990::RMD1_ERR, rmd1_names[1]); // 発生し得ないので省略 RMD1_FRAM rmd1_names[2] @@ -285,11 +252,11 @@ LADRF:$12345678_12345678 TDRP: MonitorBit(rmd[1], AM7990::RMD1_STP, rmd1_names[6]); MonitorBit(rmd[1], AM7990::RMD1_ENP, rmd1_names[7]); // BCNT は負数(2の補数)でエンコードされているので正数にデコード - monitor.Print(x, y, "MCNT=$%03x BCNT=$%03x", + screen.Print(x, y, "MCNT=$%03x BCNT=$%03x", rmd[3] & 0xfff, (-(int16)rmd[2]) & 0xfff); x += 20; - monitor.Print(x, y, "buf=$%06x", ((rmd[1] & 0xff) << 16) | rmd[0]); + screen.Print(x, y, "buf=$%06x", ((rmd[1] & 0xff) << 16) | rmd[0]); } y++; @@ -309,7 +276,7 @@ LADRF:$12345678_12345678 TDRP: for (int j = 0; j < countof(rmd); j++) { rmd[j] = PeekMem(rmd_addr + j * 2); } - monitor.Print(x * 27, y + i, + screen.Print(x * 27, y + i, (rs == rmd_cur) ? TA::Em : TA::Normal, "%02x %c%c%c%c%c%c $%05x $%03x/$%03x", rs, @@ -334,7 +301,7 @@ LADRF:$12345678_12345678 TDRP: y += 17; // TMD - monitor.Print(0, y, ""); + screen.Puts(0, y, ""); uint16 tmd[4]; uint32 tmd_addr; @@ -346,7 +313,7 @@ LADRF:$12345678_12345678 TDRP: } // MORE, ONE, DEF と LCOL, LCAR, RTRY, TDR は発生し得ないので省略 x = 6; - monitor.Print(x, y, "Current %02x ", tmd_cur); x += 11; + screen.Print(x, y, "Current %02x ", tmd_cur); x += 11; MonitorBit(tmd[1], AM7990::TMD1_OWN, tmd1_names[0]); MonitorBit(tmd[1], AM7990::TMD1_ERR, tmd1_names[1]); MonitorBit(tmd[1], AM7990::TMD1_STP, tmd1_names[6]); @@ -355,9 +322,9 @@ LADRF:$12345678_12345678 TDRP: MonitorBit(tmd[3], AM7990::TMD3_BUFF, tmd3_names[0]); MonitorBit(tmd[3], AM7990::TMD3_UFLO, tmd3_names[1]); // BCNT は負数(2の補数)でエンコードされているので正数にデコード - monitor.Print(x, y, "BCNT=$%03x", (-(int16)tmd[2]) & 0xfff); + screen.Print(x, y, "BCNT=$%03x", (-(int16)tmd[2]) & 0xfff); x += 10; - monitor.Print(x, y, "buf=$%06x", ((tmd[1] & 0xff) << 16) | tmd[0]); + screen.Print(x, y, "buf=$%06x", ((tmd[1] & 0xff) << 16) | tmd[0]); } y++; @@ -377,7 +344,7 @@ LADRF:$12345678_12345678 TDRP: for (int j = 0; j < countof(tmd); j++) { tmd[j] = PeekMem(tmd_addr + j * 2); } - monitor.Print(x * 27, y + i, + screen.Print(x * 27, y + i, (ts == tmd_cur) ? TA::Em : TA::Normal, "%02x %c%c%c%c%c $%05x $%03x", ts, @@ -397,19 +364,17 @@ LADRF:$12345678_12345678 TDRP: } } } - - return true; } // レジスタをビットごとに表示する。 // data は下位8ビットのみ使用し、上位は無視する。 void -LanceDevice::MonitorReg(int x, int y, uint32 data, const char * const *names) +LanceDevice::MonitorReg(TextScreen& screen, + int x, int y, uint32 data, const char * const *names) { for (int i = 0; i < 8; i++) { bool b = data & (1 << (7 - i)); - uint attr = b ? TA::On : TA::Off; - monitor.Print(x + i * 5, y, attr, "%s", names[i]); + screen.Puts(x + i * 5, y, TA::OnOff(b), names[i]); } } @@ -495,6 +460,10 @@ LanceDevice::WriteData(uint32 data) // TDMD はセットオンリー (チップがクリアする) if ((data & AM7990::CSR0_TDMD)) { reg.csr0 |= AM7990::CSR0_TDMD; + // TX ポーリングの待ち時間ならこのあとすぐ起動 + if (tx_phase == TXPhase::IDLE) { + ChangePhase(TXPhase::IDLE); + } } // 書き込みがビットクリアになる人たち @@ -523,16 +492,11 @@ LanceDevice::WriteData(uint32 data) // INEA は %0 も %1 も書き込める // INEA は STOP 中は設定できない if (!reg.IsSTOP()) { - uint16 old_inea = reg.csr0 & AM7990::CSR0_INEA; - uint16 new_inea = data & AM7990::CSR0_INEA; reg.csr0 &= ~AM7990::CSR0_INEA; - reg.csr0 |= new_inea; - - // INEA が 0->1 に変化して、INTR 立ってたら割り込み上げる? - if (old_inea == 0 && new_inea != 0 && reg.IsINTR()) { - Interrupt(); - } + reg.csr0 |= data & AM7990::CSR0_INEA; } + + ChangeInterrupt(); break; } @@ -582,7 +546,7 @@ LanceDevice::ReadMem(uint32 paddr) { uint64 data; - assert((paddr & 1) == 0); + assertmsg((paddr & 1) == 0, "paddr=$%x", paddr); // XXX 3ポートRAM のエンディアンとかを調べること data = gZRAM->Read16(paddr); @@ -599,7 +563,7 @@ LanceDevice::ReadMem(uint32 paddr) uint64 LanceDevice::WriteMem(uint32 paddr, uint32 data) { - assert((paddr & 1) == 0); + assertmsg((paddr & 1) == 0, "paddr=$%x", paddr); if (!reg.IsBSWP()) { data = __builtin_bswap16(data); @@ -622,12 +586,12 @@ LanceDevice::PeekMem(uint32 paddr) if (reg.IsBSWP()) { // ビッグエンディアンアクセス - data = m68030_phys_peek_8(paddr) << 8; - data |= m68030_phys_peek_8(paddr + 1); + data = vm_phys_peek_8(paddr) << 8; + data |= vm_phys_peek_8(paddr + 1); } else { // リトルエンディアンアクセス - data = m68030_phys_peek_8(paddr); - data |= m68030_phys_peek_8(paddr + 1) << 8; + data = vm_phys_peek_8(paddr); + data |= vm_phys_peek_8(paddr + 1) << 8; } return data; } @@ -662,6 +626,12 @@ 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]); + // XXX 本当はいろいろ時間がかかりそうなはずだが // 初期化完了 SetInterrupt(AM7990::CSR0_IDON); @@ -697,9 +667,8 @@ LanceDevice::ChipStart() rmd1_ahead = (uint32)-1; tmd1_ahead = (uint32)-1; - // ワーカスレッドに通知 - request |= REQ_TXIDLE | REQ_RXIDLE; - cv.notify_one(); + ChangePhase(TXPhase::IDLE, 0_nsec); + ChangePhase(RXPhase::IDLE, 0_nsec); } // STOP ビットによるチップリセット。 @@ -721,108 +690,54 @@ LanceDevice::ChipStop() rmd_cur = 0; // イベントを(あれば)停止。 - gScheduler->DelEvent(&rx_event); - gScheduler->DelEvent(&tx_event); + rx_event.Stop(); + tx_event.Stop(); + rx_phase = RXPhase::IDLE; + tx_phase = TXPhase::IDLE; // 受信を停止 RecvEnabled = false; + + // 受信バッファクリア + while (!rxq.empty()) { + rxq.pop(); + } + rxpacket.reset(); } -// 割り込み要因を CSR0 にセットし、許可されていれば割り込みを上げる。 +// 割り込み要因を CSR0 にセットし、必要なら割り込みを上げる。 void LanceDevice::SetInterrupt(uint32 bit) { reg.csr0 |= bit; - - Interrupt(); + ChangeInterrupt(); } -// 許可されていれば割り込みを上げる。 +// 現状に合わせて割り込み信号線の状態を変える。 void -LanceDevice::Interrupt() +LanceDevice::ChangeInterrupt() { - if ((reg.csr0 & AM7990::CSR0_INEA)) { - putlog(2, "割り込み"); - gMPU->Interrupt(3); + if (reg.IsINTR() && (reg.csr0 & AM7990::CSR0_INEA)) { + gInterrupt->AssertINT(this); } else { - putlog(2, "割り込みはマスクされている"); - } -} - -// ワーカスレッドのエントリポイント -void * -lance_run(void *dummy) -{ - pthread_setname_np("Lance worker"); - pthread_detach(pthread_self()); - - gLance->ThreadRun(); - return NULL; -} - -// ワーカスレッド -void -LanceDevice::ThreadRun() -{ - for (;;) { - uint32 req; - - // 何か起きるまで待つ - std::unique_lock lock(mtx); - cv.wait(lock, [&] { return (request != 0); }); - // request のほうはもうここでリセットして、コピーのほうを使う - req = request; - request = 0; - - if ((req & REQ_TERMINATE)) { - // 終了するので、他のフラグは無視 - break; - } - - // デバッグ用に TMD の OWN,STP,ENP に変更があれば表示する - if (1) { - std::string tmdstr; - for (int i = 0; i < tmd_num; i++) { - uint16 tmd1_ = PeekMem(tmd_base + i * 8 + 2); - tmdstr += string_format(" %c%c%c", - (tmd1_ & AM7990::TMD1_OWN) ? 'W' : '-', - (tmd1_ & AM7990::TMD1_STP) ? 'S' : '-', - (tmd1_ & AM7990::TMD1_ENP) ? 'E' : '-'); - } - if (tmdstr != last_tmdstr) { - putlog(3, "TMD %s", tmdstr.c_str()); - last_tmdstr = tmdstr; - } - } - -#define dispatch(flag, func) \ - if ((req & (flag))) { \ - req &= ~(flag); \ - (func)(); \ - } - - dispatch(REQ_TXIDLE, TXIdle); - dispatch(REQ_TXCOPY, TXCopy); - dispatch(REQ_TXSEND, TXSend); - dispatch(REQ_TXNEXT, TXNext); - - dispatch(REQ_RXIDLE, RXIdle); - dispatch(REQ_RXCOPY, RXCopy); - dispatch(REQ_RXNEXT, RXNext); - dispatch(REQ_RXMISS, RXMiss); - dispatch(REQ_RXRECV, RXRecv); - - assert(req == 0); + gInterrupt->NegateINT(this); } } // TXIDLE 送信アイドルフェーズ。 // ディスクリプタを所有できるかポーリングする。 -// - 所有できれば PUSH フェーズへ。 -// - 所有できない場合、TDMD オンなら即、オフなら 1.6ms 待ってから再び POLL へ。 +// - 所有できれば TXCOPY フェーズへ。 +// - 所有できない場合、TDMD オンなら即、 +// オフなら 1.6ms 待ってから再び TXIDLE へ。 void -LanceDevice::TXIdle() +LanceDevice::TXIdle(Event& event) { + std::lock_guard lock(mtx); + tx_phase = TXPhase::IDLE; + + // TDMD クリア + reg.csr0 &= ~AM7990::CSR0_TDMD; + // TMD1 を読み込む if ((int32)tmd1_ahead >= 0) { // すでに先読みしてあればそれを使う @@ -838,31 +753,26 @@ LanceDevice::TXIdle() // その TMD1 に OWN と STP が立っているか if ((tmd1 & AM7990::TMD1_OWN) && (tmd1 & AM7990::TMD1_STP)) { // 取り込みへ - request |= REQ_TXCOPY; + ChangePhase(TXPhase::COPY); return; } - // 立っていなければ - if ((reg.csr0 & AM7990::CSR0_TDMD)) { - // TDMD がオンなら即再実行。 - request |= REQ_TXIDLE; - } else { - // TDMD がオフなら 1.6ms 待って再度 POLL へ。 - tx_event.time = 1.6_msec; - tx_event.code = REQ_TXIDLE; - gScheduler->AddEvent(&tx_event); - } + // 立っていなければ次回は 1.6ms 後 + ChangePhase(TXPhase::IDLE, 1.6_msec); } // TXCOPY バッファ処理フェーズ。 // 現ディスクリプタのバッファの内容をメモリから読み込み、 // それにかかる時間相当分をウェイトする。 -// - ENP ならウェイトした後 SEND フェーズへ。 -// - 次 TMD が OWN できればウェイトした後 NEXT フェーズへ。 -// - 次 TMD が OWN 出来なければウェイトした後 SEND フェーズ。 +// - ENP ならウェイトした後 TXSEND フェーズへ。 +// - 次 TMD が OWN できればウェイトした後 TXNEXT フェーズへ。 +// - 次 TMD が OWN 出来なければウェイトした後 TXSEND フェーズ。 void -LanceDevice::TXCopy() +LanceDevice::TXCopy(Event& event) { + std::lock_guard lock(mtx); + tx_phase = TXPhase::COPY; + // TMD0, TMD2 は送信することが確定してから読み出す uint32 tmd_addr = tmd_base + tmd_cur * 8; uint16 tmd0 = ReadMem(tmd_addr); @@ -900,9 +810,13 @@ LanceDevice::TXCopy() } } + // メモリからの転送にかかった時間をざっくり計算 XXX まだ適当 + // そのウェイト後に指定のフェーズへ。 + uint64 t = 700 * (src - bufsrc) / 16; + // ここが ENP なら、送信のため TXSEND フェーズへ if ((tmd1 & AM7990::TMD1_ENP)) { - tx_event.code = REQ_TXSEND; + ChangePhase(TXPhase::SEND, t); } else { // 次のディスクリプタを先読み tmd_addr = tmd_base + ((tmd_cur + 1) % tmd_num) * 8; @@ -910,34 +824,29 @@ LanceDevice::TXCopy() if ((tmd1_ahead & AM7990::TMD1_OWN)) { // 次ディスクリプタが所有できれば TXNEXT フェーズへ - tx_event.code = REQ_TXNEXT; + ChangePhase(TXPhase::NEXT, t); } else { // この時点で所有できなければアンダーフロー。 // ここまでのバッファでパケットを送信するため TXSEND へ。 // エラーフラグの処理は TXSEND 側で行う。 - tx_event.code = REQ_TXSEND; + ChangePhase(TXPhase::SEND, t); } } - - // メモリからの転送にかかった時間をざっくり計算 XXX まだ適当 - // そのウェイト後に指定のフェーズへ。 - tx_event.time = 700 * (src - bufsrc) / 16; - gScheduler->AddEvent(&tx_event); } // TXSEND フェーズ。パケットを送信。 // ただし ENP でなければアンダーフローによるパケット送信なのでフラグ処理。 // その後 TXNEXT フェーズへ。 void -LanceDevice::TXSend() +LanceDevice::TXSend(Event& event) { + std::lock_guard lock(mtx); + tx_phase = TXPhase::SEND; + // 送信 SendPacket(txq); txq.clear(); - // TDMD は1回送ると下ろすのかな - reg.csr0 &= ~AM7990::CSR0_TDMD; - // ENP でなければアンダーフローによるものだった if ((tmd1 & AM7990::TMD1_ENP) == 0) { // TMD3 はここで書き出す @@ -947,59 +856,82 @@ LanceDevice::TXSend() tmd3 |= AM7990::TMD3_BUFF | AM7990::TMD3_UFLO; WriteMem(tmd_addr + 6, tmd3); - // TMD1 は NEXT フェーズで書き戻す + // TMD1 は TXNEXT フェーズで書き戻す tmd1 |= AM7990::TMD1_ERR; } // どちらにしても送信割り込み SetInterrupt(AM7990::CSR0_TINT); - request |= REQ_TXNEXT; + ChangePhase(TXPhase::NEXT); } // TXNEXT フェーズ。次のディスクリプタを指して TXIDLE へ。 void -LanceDevice::TXNext() +LanceDevice::TXNext(Event& event) { + std::lock_guard lock(mtx); + tx_phase = TXPhase::NEXT; + // このディスクリプタの所有権を放棄 tmd1 &= ~AM7990::TMD1_OWN; uint32 tmd_addr = tmd_base + tmd_cur * 8; WriteMem(tmd_addr + 2, tmd1); - // 次のディスクリプタへ + // 即、次のディスクリプタの所有を試みる tmd_cur = (tmd_cur + 1) % tmd_num; - - request |= REQ_TXIDLE; + ChangePhase(TXPhase::IDLE); } -// パケット受信。 -// これは mtx 取得状態で Nerdriver スレッドから呼ばれるので、 -// パケットを手元にコピーして VM スレッドに通知を出すだけ。 -bool -LanceDevice::RecvPacket(qvector& buf) +// 指定時間後に TX フェーズを遷移する。 +// (変化があれば)変更するではないことに注意。名前がアレだが IDLE フェーズは +// ポーリングしているので IDLE から IDLE への遷移も必ず起こす必要がある。 +// TX か RX かは関数オーバーロードで区別している。 +void +LanceDevice::ChangePhase(TXPhase new_phase, uint64 time) { - if (rxq.empty() == false) { - // 前のパケットをまだ処理中なので、このパケットは捨てる - putlog(2, "%s: discard %zd bytes", __func__, buf.size()); - return true; + tx_event.Stop(); + tx_event.time = time; + switch (new_phase) { + case TXPhase::IDLE: + tx_event.func = (DeviceCallback_t)&LanceDevice::TXIdle; + break; + case TXPhase::COPY: + tx_event.func = (DeviceCallback_t)&LanceDevice::TXCopy; + break; + case TXPhase::SEND: + tx_event.func = (DeviceCallback_t)&LanceDevice::TXSend; + break; + case TXPhase::NEXT: + tx_event.func = (DeviceCallback_t)&LanceDevice::TXNext; + break; + default: + assertmsg(false, "phase %d", (int)new_phase); } - putlog(3, "%s: %zd bytes", __func__, buf.size()); - - // コピー - rxq = buf; + tx_event.Start(); +} - // 通知 - request |= REQ_RXRECV; - cv.notify_one(); - return true; +// パケット受信通知。NetDriver スレッドから mtx を保持した状態で呼ばれる。 +void +LanceDevice::NotifyRecvPacket() +{ + // RXIDLE ポーリングのウェイト中ならすぐに始める。 + // そうでないときは RXIDLE に戻ったところで処理される。 + if (rx_phase == RXPhase::IDLE) { + ChangePhase(RXPhase::IDLE); + } } // RXIDLE 受信アイドルフェーズ。 // (最初の)ディスクリプタを所有できるかポーリングする。 void -LanceDevice::RXIdle() +LanceDevice::RXIdle(Event& event) { + std::lock_guard lock(mtx); + rx_phase = RXPhase::IDLE; + putlog(3, "%s[%d]", __func__, rmd_cur); + // RMD1 を読み込む if ((int32)rmd1_ahead >= 0) { // すでにあればそれを使う @@ -1012,37 +944,53 @@ LanceDevice::RXIdle() // どちらにしても先読みバッファは無効に戻す rmd1_ahead = (uint32)-1; - // その RMD1 に OWN が立っているか - if ((rmd1 & AM7990::RMD1_OWN)) { - // ここが STP になる - rmd1 |= AM7990::RMD1_STP; - - // パケットを受信するまで何もすることはないのでフラグ立てずに戻る - return; - } - - // 立っていなければ - // 1.6ms 待って再度 RXIDLE へ。 - rx_event.time = 1.6_msec; - rx_event.code = REQ_RXIDLE; - gScheduler->AddEvent(&rx_event); -} + // 受信しているか + if (rxq.size() != 0) { + // その 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; + } + } + } -// RXRECV 先頭ディスクリプタ開始フェーズ。 -// - OWN していれば RXCOPY へ。 -// - していなければ、パケット長相当分ウェイトしてから RXMISS へ。 -void -LanceDevice::RXRecv() -{ - putlog(3, "%s[%d]", __func__, rmd_cur); - if ((rmd1 & AM7990::RMD1_OWN)) { - // 所有しているのでコピー処理へ - request |= REQ_RXCOPY; + // RXCOPY へ移行する + ChangePhase(RXPhase::COPY); + } else { + // バッファが無いので MISS + // MISS までの時間は受信処理によって表現されているはず + ChangePhase(RXPhase::MISS); + } } else { - // パケットを受信してる時間に相当するウェイト XXX 適当 - rx_event.time = 700 * rxq.size() / 16; - rx_event.code = REQ_RXMISS; - gScheduler->AddEvent(&rx_event); + if ((rmd1 & AM7990::RMD1_OWN)) { + // ここが STP になる + rmd1 |= AM7990::RMD1_STP; + } + // 1.6ms 待って再度 RXIDLE へ。 + ChangePhase(RXPhase::IDLE, 1.6_msec); } } @@ -1050,9 +998,13 @@ LanceDevice::RXRecv() // 受信バッファからディスクリプタのバッファに書き込み、 // 書き込みにかかる時間相当分をウェイトする。 void -LanceDevice::RXCopy() +LanceDevice::RXCopy(Event& event) { - putlog(3, "%s[%d]", __func__, rmd_cur); + std::lock_guard lock(mtx); + rx_phase = RXPhase::COPY; + + putlog(1, "%s[%d]", __func__, rmd_cur); + // RMD0, RMD2 は受信することが確定してから読み出す uint32 rmd_addr = rmd_base + rmd_cur * 8; uint16 rmd0 = ReadMem(rmd_addr); @@ -1063,9 +1015,13 @@ LanceDevice::RXCopy() uint32 dst = bufdst; int32 bcnt = (int32)(int16)rmd2; + if (-bcnt < rxpacket->size()) { + putlog(1, "will be remain: %d < %zd", -bcnt, rxpacket->size()); + } + // ローカルの受信バッファからディスクリプタバッファにコピー for (; bcnt < 0; ) { - int remsize = rxq.size(); + int remsize = rxpacket->size(); if (remsize == 0) { break; } @@ -1075,22 +1031,22 @@ LanceDevice::RXCopy() uint16 data; if (bcnt <= -2 && remsize >= 2 && (dst & 1) == 0) { // 2バイト以上残っててワード境界ならワードアクセス - data = rxq.pop() << 8; - data |= rxq.pop(); + data = rxpacket->dequeue() << 8; + data |= rxpacket->dequeue(); WriteMem(dst, data); dst += 2; bcnt += 2; } else if ((dst & 1) == 0) { // 偶数アドレスへ1バイト // 奇数バイトには不定データが出るらしいがここでは 0 にする - data = rxq.pop() << 8; + data = rxpacket->dequeue() << 8; WriteMem(dst, data); dst += 1; bcnt += 1; } else { // 奇数アドレスへ1バイト // 偶数バイトには不定データが出るらしいがここでは 0 にする - data = rxq.pop(); + data = rxpacket->dequeue(); WriteMem(dst & ~1, data); dst += 1; bcnt += 1; @@ -1098,18 +1054,20 @@ LanceDevice::RXCopy() } // 全部書き出せたらここが ENP - if (rxq.size() == 0) { + if (rxpacket->size() == 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()); } // メモリへの転送にかかった時間をざっくり計算 XXX まだ適当 // そのウェイト後に RXNEXT フェーズへ。 - rx_event.time = 700 * (dst - bufdst) / 16; - rx_event.code = REQ_RXNEXT; - gScheduler->AddEvent(&rx_event); + uint64 t = 700 * (dst - bufdst) / 16; + ChangePhase(RXPhase::NEXT, t); } // RXNEXT フェーズ。 @@ -1118,18 +1076,18 @@ LanceDevice::RXCopy() // - 次のディスクリプタが所有できれば引き続き書き出すため RXCOPY へ。 // - 次のディスクリプタが所有できなければエラーにして RXIDLE へ。 void -LanceDevice::RXNext() +LanceDevice::RXNext(Event& event) { - putlog(3, "%s[%d]", __func__, rmd_cur); + std::lock_guard lock(mtx); + rx_phase = RXPhase::NEXT; - // RXCOPY フェーズで rxq が empty() になっても、(Lance スレッドの) - // メッセージループでここ(RXNEXT)に来るまでの間に、rxq に再び着信する - // 可能性はあるので、ここでは rxq を調べたり触ったりしてはいけない、 - // はず。 + putlog(1, "%s[%d]", __func__, rmd_cur); + + bool is_chained = false; if ((rmd1 & AM7990::RMD1_ENP)) { // ENP なら、RXIDLE へ。 - request |= REQ_RXIDLE; + ChangePhase(RXPhase::IDLE); } else { // 次のディスクリプタを先読み uint32 rmd_addr = rmd_base + ((rmd_cur + 1) % rmd_num) * 8; @@ -1137,57 +1095,83 @@ LanceDevice::RXNext() if ((rmd1_ahead & AM7990::RMD1_OWN)) { // 次ディスクリプタが所有できれば RXCOPY へ。 - request |= REQ_RXCOPY; + ChangePhase(RXPhase::COPY); + is_chained = true; } else { // この時点で所有できなければオーバーフロー。 // XXX BUFF と OFLO の違いがよくわからん rmd1 |= AM7990::RMD1_BUFF | AM7990::RMD1_OFLO; // 次のディスクリプタをポールするため RXIDLE へ。 - request |= REQ_RXIDLE; + ChangePhase(RXPhase::IDLE); } } // このディスクリプタの所有権を放棄 rmd1 &= ~AM7990::RMD1_OWN; uint32 rmd_addr = rmd_base + rmd_cur * 8; -putlog(3, "%s[%d] addr=%x RMD1=%02x", __func__, rmd_cur, rmd_addr + 2, rmd1); WriteMem(rmd_addr + 2, rmd1); // 次のディスクリプタへ rmd_cur = (rmd_cur + 1) % rmd_num; + if (is_chained) { + rmd1 = rmd1_ahead; + rmd1_ahead = -1; + } + // どちらにしても受信割り込み SetInterrupt(AM7990::CSR0_RINT); } // RXMISS 受信失敗処理。 void -LanceDevice::RXMiss() +LanceDevice::RXMiss(Event& event) { - putlog(3, "%s", __func__); + std::lock_guard lock(mtx); + rx_phase = RXPhase::MISS; + + putlog(1, "%s", __func__); // 受信データがあるのにディスクリプタを所有してなければエラー SetInterrupt(AM7990::CSR0_MISS | AM7990::CSR0_RINT); // 受信データを破棄。 - rxq.clear(); + rxpacket.reset(); // そのまますぐにアイドルフェーズに戻るんだろうか - request |= REQ_RXIDLE; + ChangePhase(RXPhase::IDLE); } -// イベントハンドラ +// 指定時間後に RX フェーズを遷移する。 +// (変化があれば)変更するではないことに注意。名前がアレだが IDLE フェーズは +// ポーリングしているので IDLE から IDLE への遷移も必ず起こす必要がある。 +// TX か RX かは関数オーバーロードで区別している。 void -LanceDevice::Callback(int arg) +LanceDevice::ChangePhase(RXPhase new_phase, uint64 time) { - std::unique_lock lock(mtx); - - // ワーカスレッドにフェーズ遷移を伝えるだけ - request |= arg; - cv.notify_one(); + rx_event.Stop(); + rx_event.time = time; + switch (new_phase) { + case RXPhase::IDLE: + rx_event.func = (DeviceCallback_t)&LanceDevice::RXIdle; + break; + case RXPhase::COPY: + rx_event.func = (DeviceCallback_t)&LanceDevice::RXCopy; + break; + case RXPhase::NEXT: + rx_event.func = (DeviceCallback_t)&LanceDevice::RXNext; + break; + case RXPhase::MISS: + rx_event.func = (DeviceCallback_t)&LanceDevice::RXMiss; + break; + default: + assertmsg(false, "phase %d", (int)new_phase); + } + rx_event.Start(); } + // ビット名文字列配列は [0] が MSB 側の順で並べる /*static*/ const char * const LanceDevice::csr0names[] = { @@ -1196,7 +1180,7 @@ LanceDevice::Callback(int arg) }; /*static*/ const char * const LanceDevice::csr3names[] = { - "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", "", "", "", "BSWP", "ACON", "BCON", };