--- nono/vm/lance.cpp 2026/04/29 17:04:29 1.1.1.2 +++ nono/vm/lance.cpp 2026/04/29 17:05:59 1.1.1.21 @@ -1,187 +1,150 @@ // // 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 "zram.h" -#include "mystring.h" - -LanceDevice *gLance; +// +// Lance (AM7990) +// -static void *lance_run(void *); +#include "lance.h" +#include "bitops.h" +#include "event.h" +#include "interrupt.h" +#include "mainbus.h" +#include "monitor.h" +#include "scheduler.h" +#include "subram.h" +// コンストラクタ LanceDevice::LanceDevice() + : inherited(OBJ_ETHERNET(0)) { - logname = "lance"; - devname = "Lance"; - devaddr = 0xf1000000; - - monitor.Init(80, 34); - - rx_event.dev = this; - rx_event.func = (DeviceCallback_t)&LanceDevice::Callback; - rx_event.code = 0; - rx_event.time = 0; // 都度設定する - rx_event.SetName("Lance RX"); - - tx_event.dev = this; - tx_event.func = (DeviceCallback_t)&LanceDevice::Callback; - tx_event.code = 0; - tx_event.time = 0; // 都度設定する - tx_event.SetName("Lance TX"); + SetName("Lance"); + ClearAlias(); + AddAlias("Lance"); + + monitor = gMonitorManager->Regist(ID_MONITOR_LANCE, this); + monitor->SetCallback(&LanceDevice::MonitorScreen); + monitor->SetSize(80, 34); } +// デストラクタ LanceDevice::~LanceDevice() { - // スレッド終了を指示 - std::unique_lock lock(mtx); - request |= REQ_TERMINATE; - cv.notify_one(); } +// 初期化 bool LanceDevice::Init() { - // 親も Init() を持っている - if (inherited::Init() == false) { - return false; - } - - const char *key = "ethernet.macaddr"; - const std::string config_macaddr = gConfig->ReadStr(key, "auto"); - macaddr_t macaddr; - if (config_macaddr == "rom") { - // ROM に書いてあるのを使うので、ここでは何もしなくていい - } else { - if (config_macaddr == "auto") { - // 自動生成 - // すでに記録されていれば使う。なければ生成して記録したい - if (0) { - } else { - GenerateMacAddr(&macaddr); - } - } else { - // ユーザ指定 - if (ParseMacAddr(macaddr, config_macaddr) == false) { - gConfig->Err(key, "%s: invalid MAC address", - config_macaddr.c_str()); - return false; - } - } - - putmsg(1, "macaddr=%s", macaddr.to_string().c_str()); + interrupt = GetInterruptDevice(); + subram = GetSubRAMDevice(); - // ROM に書き込む - if (gPROM->SetMacAddr(macaddr) == false) { - putmsg(1, "Cannot set macaddr in PROM"); - return false; - } - } + hostnet->SetPortName("Lance"); - // ワーカスレッド起動 - pthread_t th; - pthread_create(&th, NULL, lance_run, NULL); + // ホストからの受信コールバックを登録 + scheduler->ConnectMessage(MessageID::HOSTNET_RX(0), this, + ToMessageCallback(&LanceDevice::RxMessage)); + + auto evman = GetEventManager(); + rx_event = evman->Regist(this, NULL, "Lance RX"); + tx_event = evman->Regist(this, NULL, "Lance TX"); return true; } +// リセット void -LanceDevice::ResetHard() +LanceDevice::ResetHard(bool poweron) { - std::unique_lock lock(mtx); - reg.rap = 0; ChipStop(); } -uint64 -LanceDevice::Read8(uint32 addr) -{ - putlog(0, "未実装バイト読み込み $%08x", addr); - return 0xff; -} - -uint64 -LanceDevice::Read16(uint32 addr) +busdata +LanceDevice::ReadPort(uint32 offset) { - uint32 data; + busdata data; - switch (addr - devaddr) { + switch (offset) { case 0: // RDP data = ReadData(); - return data; - case 2: // RAP - { - std::unique_lock lock(mtx); + break; + case 1: // RAP data = reg.rap; - putlog(2, "RAP -> %d", reg.rap); - return data; - } + putlog(2, "RAP -> %u", reg.rap); + break; + default: + VMPANIC("corrupted offset=%u", offset); + data = 0xffff; + break; } - 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; + // XXX wait? + data |= BusData::Size2; + return data; } -uint64 -LanceDevice::Write16(uint32 addr, uint32 data) +busdata +LanceDevice::WritePort(uint32 offset, uint32 data) { - switch (addr - devaddr) { + switch (offset) { case 0: // RDP WriteData(data); - return 0; - case 2: // RAP - { - std::unique_lock lock(mtx); + break; + case 1: // RAP reg.rap = data & 3; - return 0; - } + break; + default: + VMPANIC("corrupted offset=%u", offset); + break; } - putlog(0, "未実装ワード書き込み $%08x <- %04x", addr, data); - return 0; -} -uint64 -LanceDevice::Write32(uint32 addr, uint32 data) -{ - putlog(0, "未実装ロング書き込み $%08x <- %08x", addr, data); - return 0; + busdata r = BusData::Size2; + // XXX wait? + return r; } -uint64 -LanceDevice::Peek8(uint32 addr) +busdata +LanceDevice::PeekPort(uint32 offset) { + switch (offset) { + case 0: // RDP + // 今読めるはずのデータ。 + // CSR[123] は STOP 中でなければ不定だがとりあえずそれは無視。 + 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; + default: + break; + } + break; + case 1: // RAP + return reg.rap; + default: + break; + } return 0xff; } -bool -LanceDevice::MonitorUpdate() +void +LanceDevice::MonitorScreen(Monitor *, TextScreen& screen) { /* CSR0: $1111 ERR BABL CERR MISS MERR RINT TINT IDON IADR:$123456 CSR3:$0004 BSWP ACON BCON MODE: $0000 PROM INTL DRTY COLL DTCR LOOP DTX DRX -PADR: 00:11:22:33:44:55 RDRP: $12345678 (RDRA=$123456 entries=32) -LADRF:$12345678_12345678 TDRP: +PADR: 00:11:22:33:44:55 RDRP: $12345678 (RDRA=$123456 entries=32) +LADRF:$fedcba98'76543210 TDRP: Current 00 OWN ERR OFLO CRC BUFF STP ENP BCNT=$123 MCNT=$123 buf=$123456 01234567890123456789012345678901234567890 00 @@ -193,9 +156,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 +164,17 @@ LADRF:$12345678_12345678 TDRP: int y; int i; - monitor.Clear(); - - // XXX lock + screen.Clear(); 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 +182,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 +198,11 @@ 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 +210,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=%u)", 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=%u)", tdrp, tmd_base, tmd_num); y++; y++; // RMD - monitor.Print(0, y, ""); + screen.Puts(0, y, ""); uint16 rmd[4]; uint32 rmd_addr; @@ -275,7 +233,8 @@ 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 +244,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 +268,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 +293,7 @@ LADRF:$12345678_12345678 TDRP: y += 17; // TMD - monitor.Print(0, y, ""); + screen.Puts(0, y, ""); uint16 tmd[4]; uint32 tmd_addr; @@ -346,7 +305,8 @@ 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 +315,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 +337,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, @@ -390,26 +350,24 @@ LADRF:$12345678_12345678 TDRP: (-(int16)tmd[2]) & 0xfff); // 画面内に収まらない場合は折り返し。 - // 収まる場合はこのまま rmd_num を超えればループを抜ける。 + // 収まる場合はこのまま tmd_num を超えればループを抜ける。 ts++; if (tmd_num > 24 && ts >= tmd_num) { ts = 0; } } } - - 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]); + bool b = data & (1U << (7 - i)); + screen.Puts(x + i * 5, y, TA::OnOff(b), names[i]); } } @@ -420,47 +378,47 @@ 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: if (reg.IsSTOP()) { data = reg.iadr & 0xffff; - putlog(2, "CSR%d -> $%04x", reg.rap, data); + putlog(2, "CSR%u -> $%04x", reg.rap, data); } else { data = 0xcccc & AM7990::CSR1_MASK; - putlog(2, "CSR%d -> $%04x (不定読み出し)", reg.rap, data); + putlog(2, "CSR%u -> $%04x (indefinate read)", reg.rap, data); } break; case 2: if (reg.IsSTOP()) { data = reg.iadr >> 16; - putlog(2, "CSR%d -> $%04x", reg.rap, data); + putlog(2, "CSR%u -> $%04x", reg.rap, data); } else { data = 0xcccc & AM7990::CSR2_MASK; - putlog(2, "CSR%d -> $%04x (不定読み出し)", reg.rap, data); + putlog(2, "CSR%u -> $%04x (indefinate read)", reg.rap, data); } break; case 3: if (reg.IsSTOP()) { data = reg.csr3; const std::string name = CSR3ToString(data); - putlog(2, "CSR%d -> $%04x<%s>", reg.rap, data, name.c_str()); + putlog(2, "CSR%u -> $%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%u -> $%04x (indefinate read)", reg.rap, data); } break; default: - __unreachable(); + VMPANIC("corrupted reg.rap=%u", reg.rap); + data = 0xffff; + break; } return data; } @@ -469,8 +427,6 @@ LanceDevice::ReadData() void LanceDevice::WriteData(uint32 data) { - std::unique_lock lock(mtx); - switch (reg.rap) { case 0: { @@ -485,16 +441,26 @@ 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(); } // TDMD はセットオンリー (チップがクリアする) if ((data & AM7990::CSR0_TDMD)) { reg.csr0 |= AM7990::CSR0_TDMD; + // TX ポーリングの待ち時間ならこのあとすぐ起動 + if (tx_phase == TXPhase::IDLE) { + ChangePhase(TXPhase::IDLE); + } } // 書き込みがビットクリアになる人たち @@ -523,16 +489,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; } @@ -563,80 +524,59 @@ LanceDevice::WriteData(uint32 data) break; default: - __unreachable(); + VMPANIC("corrupted reg.rap=%u", reg.rap); } } // メモリアクセスについて。 -// AM7990 から見たメモリは LUNA の 3ポートSRAM (ここでは ZRAM デバイス)。 +// LANCE(AM7990) から見たメモリは、メインメモリではなく 3ポートSRAM +// (ここでは SubRAM デバイス)。 +// LANCE のメモリバスは 16bit で、DAL15..8 が SubRAM の偶数アドレス、 +// DAL7..0 が SubRAM の奇数アドレスに接続されている。 // -// AM7990 は CSR3.BSWP が %0 ならリトルエンディアンアクセス、 -// %1 ならビッグエンディアンアクセス。 -// 一方、RAMDevice.Read16() はビッグエンディアン動作になるので、 -// BSWP==%0 ならバイトスワップする。ホストのエンディアンには無関係。 - -// メモリから1ワード読み込む。paddr はワードアラインしていること。 -// バスエラーは起きない。 -uint64 -LanceDevice::ReadMem(uint32 paddr) -{ - uint64 data; - - assert((paddr & 1) == 0); - - // XXX 3ポートRAM のエンディアンとかを調べること - data = gZRAM->Read16(paddr); - if (!reg.IsBSWP()) { - data = __builtin_bswap16(data); - } +// LANCE | DAL15..DAL8 | DAL7..DAL0 +// -------+-------------+------------ +// SubRAM | +0 byte | +1 byte +// | +2 byte | +3 byte +// +// メモリアクセスは常にこの構成で、CSR3.BSWP はこの1ワードを SILO(FIFO) に +// 書き込む/から読み出す順序を入れ替えるもの。 - // XXX 所要クロックを考察すること +// メモリから1ワード読み込む。addr はワードアラインしていること。 +uint32 +LanceDevice::ReadMem(uint32 addr) +{ + assertmsg((addr & 1) == 0, "addr=$%x", addr); - return data; + // XXX 所要クロックを考察すること + busdata data = subram->Read2(addr); + return data.Data(); } -// メモリに1ワード書き込む。paddr はワードアラインしていること。 -uint64 -LanceDevice::WriteMem(uint32 paddr, uint32 data) +// メモリに1ワード書き込む。addr はワードアラインしていること。 +void +LanceDevice::WriteMem(uint32 addr, uint32 data) { - assert((paddr & 1) == 0); - - if (!reg.IsBSWP()) { - data = __builtin_bswap16(data); - } + assertmsg((addr & 1) == 0, "addr=$%x", addr); // XXX 所要クロックを考察すること - - gZRAM->Write16(paddr, data); - return 0; + subram->Write2(addr, data); } // メモリ (3ポートSRAM) から1ワードピークする。バスエラーは起きない。 -uint64 -LanceDevice::PeekMem(uint32 paddr) +uint32 +LanceDevice::PeekMem(uint32 addr) const { - uint64 data; - - paddr &= 0x1ffff; - paddr += 0x71000000; // XXX 定数にしたほうがいい - - if (reg.IsBSWP()) { - // ビッグエンディアンアクセス - data = m68030_phys_peek_8(paddr) << 8; - data |= m68030_phys_peek_8(paddr + 1); - } else { - // リトルエンディアンアクセス - data = m68030_phys_peek_8(paddr); - data |= m68030_phys_peek_8(paddr + 1) << 8; - } - return data; + busdata data = subram->Peek2(addr); + return data.Data(); } // INIT ビットによるチップ初期化。 -// ロック状態で呼ばれる。 void LanceDevice::ChipInit() { + putlog(1, "ChipInit"); + // INIT をセットすると STOP ビットはクリア reg.csr0 |= AM7990::CSR0_INIT; reg.csr0 &= ~AM7990::CSR0_STOP; @@ -647,7 +587,7 @@ LanceDevice::ChipInit() } if (0) { for (int i = 0; i < countof(initblock); i++) { - putlog(3, "initblock[%d] = $%04x", i, initblock[i]); + putlog(2, "initblock[%u] = $%04x", i, initblock[i]); } } @@ -655,12 +595,32 @@ LanceDevice::ChipInit() uint32 rdrp = initblock[8] | (initblock[9] << 16); rmd_base = rdrp & 0xfffffc; int rlen = rdrp >> 29; - rmd_num = (1 << rlen); + rmd_num = (1U << rlen); uint32 tdrp = initblock[10] | (initblock[11] << 16); tmd_base = tdrp & 0xfffffc; int tlen = tdrp >> 29; - tmd_num = (1 << tlen); + tmd_num = (1U << tlen); + + padr = MacAddr( + initblock[1] & 0xff, + initblock[1] >> 8, + initblock[2] & 0xff, + initblock[2] >> 8, + initblock[3] & 0xff, + initblock[3] >> 8); + + // LADRF は以下のように格納されていて、そのまま LADRF(63:00) に対応する。 + // IADR+$08 LADRF(15:00) + // IADR+$0a LADRF(31:16) + // IADR+$0c LADRF(47:32) + // IADR+$0e LADRF(63:48) + ladrf = ((uint64)initblock[4] << 0) + | ((uint64)initblock[5] << 16) + | ((uint64)initblock[6] << 32) + | ((uint64)initblock[7] << 48); + + promisc = (initblock[0] & AM7990::IBMODE_PROM); // XXX 本当はいろいろ時間がかかりそうなはずだが // 初期化完了 @@ -668,11 +628,10 @@ LanceDevice::ChipInit() } // STRT ビットによるスタート。 -// ロック状態で呼ばれる。 void LanceDevice::ChipStart() { - putlog(1, "スタート"); + putlog(1, "Start"); // STRT を書き込むと STOP はクリア reg.csr0 |= AM7990::CSR0_STRT; @@ -689,29 +648,32 @@ 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; - // ワーカスレッドに通知 - request |= REQ_TXIDLE | REQ_RXIDLE; - cv.notify_one(); + // バッファクリア + tx_packet.Clear(); + + ChangePhase(TXPhase::IDLE, 0_nsec); + ChangePhase(RXPhase::IDLE, 0_nsec); + + // ホストの受信を開始する + hostnet->EnableRx(true); } // STOP ビットによるチップリセット。 // ハードリセットもここに来る。 -// ロック状態で呼ばれる。 void LanceDevice::ChipStop() { - putlog(1, "リセット"); + putlog(1, "Reset"); // STOPセットで CSR0 は STOP セット、他は全クリア reg.csr0 = AM7990::CSR0_STOP; + ChangeInterrupt(); // CSR1, 2 は不定 // CSR3 はクリア reg.csr3 = 0; @@ -721,108 +683,46 @@ LanceDevice::ChipStop() rmd_cur = 0; // イベントを(あれば)停止。 - gScheduler->DelEvent(&rx_event); - gScheduler->DelEvent(&tx_event); + ChangePhase(RXPhase::STOP); + ChangePhase(TXPhase::STOP); + + // バッファクリア + rx_packet.Clear(); + tx_packet.Clear(); - // 受信を停止 - RecvEnabled = false; + // ホストの受信を停止する + hostnet->EnableRx(false); } -// 割り込み要因を CSR0 にセットし、許可されていれば割り込みを上げる。 +// 割り込み要因を CSR0 にセットし、必要なら割り込みを上げる。 void LanceDevice::SetInterrupt(uint32 bit) { reg.csr0 |= bit; - - Interrupt(); -} - -// 許可されていれば割り込みを上げる。 -void -LanceDevice::Interrupt() -{ - if ((reg.csr0 & AM7990::CSR0_INEA)) { - putlog(2, "割り込み"); - gMPU->Interrupt(3); - } else { - putlog(2, "割り込みはマスクされている"); - } -} - -// ワーカスレッドのエントリポイント -void * -lance_run(void *dummy) -{ - pthread_setname_np("Lance worker"); - pthread_detach(pthread_self()); - - gLance->ThreadRun(); - return NULL; + ChangeInterrupt(); } -// ワーカスレッド +// 現状に合わせて割り込み信号線の状態を変える。 void -LanceDevice::ThreadRun() +LanceDevice::ChangeInterrupt() { - 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); - } + bool irq = (reg.IsINTR() && (reg.csr0 & AM7990::CSR0_INEA)); + interrupt->ChangeINT(this, irq); } // TXIDLE 送信アイドルフェーズ。 // ディスクリプタを所有できるかポーリングする。 -// - 所有できれば PUSH フェーズへ。 -// - 所有できない場合、TDMD オンなら即、オフなら 1.6ms 待ってから再び POLL へ。 +// - 所有できれば TXCOPY フェーズへ。 +// - 所有できない場合、TDMD オンなら即、 +// オフなら 1.6ms 待ってから再び TXIDLE へ。 void -LanceDevice::TXIdle() +LanceDevice::TXIdle(Event *ev) { + tx_phase = TXPhase::IDLE; + + // TDMD クリア + reg.csr0 &= ~AM7990::CSR0_TDMD; + // TMD1 を読み込む if ((int32)tmd1_ahead >= 0) { // すでに先読みしてあればそれを使う @@ -838,71 +738,81 @@ LanceDevice::TXIdle() // その TMD1 に OWN と STP が立っているか if ((tmd1 & AM7990::TMD1_OWN) && (tmd1 & AM7990::TMD1_STP)) { // 取り込みへ - request |= REQ_TXCOPY; + TXCopy(); 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() { + tx_phase = TXPhase::COPY; + // TMD0, TMD2 は送信することが確定してから読み出す uint32 tmd_addr = tmd_base + tmd_cur * 8; 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; - - // ローカルの送信バッファにコピー - for (; bcnt < 0; ) { - // XXX バーストコピー未対応 - // XXX BSWP==0の時のバッファ内のエンディアンとかは? - - 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; + // アドレスと送信バイト数。 + // tmd2 には 2 の補数で送信バイト数が入っているので符号反転する。 + uint32 src = ((tmd1 & 0x00ff) << 16) | tmd0; + int32 bcnt = -(int32)(int16)tmd2; + + // ここから、SubRAM からローカルの送信バッファにコピー + // XXX バーストコピー未対応 + uint32 srcend = src + bcnt; + + // src が奇数アドレスから始まっている(そしてコピー可能な)場合 + // (バッファを奇数アドレスから始めたりする奇特な人はいないと思うが) + if (__predict_false((src & 1) != 0) && src < srcend) { + uint16 data = ReadMem(src & ~1); + src++; + if (reg.IsBSWP() == false) { + tx_packet.Append(data >> 8); + } else { + tx_packet.Append(data & 0xff); + } + } + + // 残り2バイト以上コピー可能な間 (ここで src はワード境界になっている) + for (; src + 1 < srcend; ) { + uint16 data = ReadMem(src); + src += 2; + if (reg.IsBSWP() == false) { + tx_packet.Append(data & 0xff); + tx_packet.Append(data >> 8); + } else { + tx_packet.Append(data >> 8); + tx_packet.Append(data & 0xff); + } + } + + // src が奇数アドレスで終わっている(そしてコピー可能な)場合 + if (src < srcend) { + uint16 data = ReadMem(src); + if (reg.IsBSWP() == false) { + tx_packet.Append(data & 0xff); } else { - // 奇数アドレスから1バイト - uint16 data = ReadMem(src & ~1); - txq.push_back(data & 0xff); - src += 1; - bcnt += 1; + tx_packet.Append(data >> 8); } } + // メモリからの転送にかかった時間をざっくり計算 XXX まだ適当 + // そのウェイト後に指定のフェーズへ。 + uint64 t = nsec_to_tsec(700 * bcnt / 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,33 +820,35 @@ 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 *ev) { - // 送信 - SendPacket(txq); - txq.clear(); + tx_phase = TXPhase::SEND; - // TDMD は1回送ると下ろすのかな - reg.csr0 &= ~AM7990::CSR0_TDMD; + 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) { @@ -947,59 +859,103 @@ 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 *ev) { + 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->time = time; + switch (new_phase) { + case TXPhase::STOP: + scheduler->StopEvent(tx_event); + return; + case TXPhase::IDLE: + tx_event->SetCallback(&LanceDevice::TXIdle); + break; + case TXPhase::SEND: + tx_event->SetCallback(&LanceDevice::TXSend); + break; + case TXPhase::NEXT: + tx_event->SetCallback(&LanceDevice::TXNext); + break; + default: + assertmsg(false, "phase %u", (uint)new_phase); } - putlog(3, "%s: %zd bytes", __func__, buf.size()); + scheduler->RestartEvent(tx_event); +} - // コピー - rxq = buf; +// 受信フェーズ遷移図 +// +// +--------+ +// | RXSTOP |<- - - (CSR0.STOP 書き込み / リセット) +// +--------+ +// : +// : +‥‥‥+ <‥‥ RxMessage (パケット受信) +// v v : +// +-------------+ : +// +‥‥‥>| RXIDLE |‥‥+ Idle +// : +-------------+ +// : | | ^ +// : | | : +// : v | : +// : +--------+ | +--------+ +// +‥| RXMISS | | | RXNEXT | +// +--------+ | +--------+ +// | ^ | +// | : | +// v : v +// +-----------+ +// | RXCOPY | 関数呼び出し ----> +// +-----------+ イベントによる遷移 ‥‥> - // 通知 - request |= REQ_RXRECV; - cv.notify_one(); - return true; +// パケット受信通知。HostNet から (EthernetDevice 経由で) 呼ばれる。 +void +LanceDevice::RxMessage(MessageID msgid, uint32 arg) +{ + // RXIDLE ポーリングのウェイト中ならすぐに始める。 + // そうでないときは RXIDLE に戻ったところで処理される。 + if (rx_phase == RXPhase::IDLE) { + ChangePhase(RXPhase::IDLE); + } } // RXIDLE 受信アイドルフェーズ。 // (最初の)ディスクリプタを所有できるかポーリングする。 void -LanceDevice::RXIdle() +LanceDevice::RXIdle(Event *ev) { - putlog(3, "%s[%d]", __func__, rmd_cur); + rx_phase = RXPhase::IDLE; + + putlog(3, "%s[%u]", __func__, rmd_cur); + // RMD1 を読み込む if ((int32)rmd1_ahead >= 0) { // すでにあればそれを使う @@ -1012,37 +968,26 @@ 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); -} - -// RXRECV 先頭ディスクリプタ開始フェーズ。 -// - OWN していれば RXCOPY へ。 -// - していなければ、パケット長相当分ウェイトしてから RXMISS へ。 -void -LanceDevice::RXRecv() -{ - putlog(3, "%s[%d]", __func__, rmd_cur); - if ((rmd1 & AM7990::RMD1_OWN)) { - // 所有しているのでコピー処理へ - request |= REQ_RXCOPY; + // 受信しているか + if (hostnet->Rx(&rx_packet)) { + // その RMD1 に OWN が立っているか + if ((rmd1 & AM7990::RMD1_OWN)) { + // ここが STP になる + rmd1 |= AM7990::RMD1_STP; + // RXCOPY へ直接移行する。 + RXCopy(); + } else { + // バッファが無いので MISS へ直接移行する。 + // MISS までの時間は受信処理によって表現されているはず + RXMiss(); + } } 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); } } @@ -1052,64 +997,81 @@ LanceDevice::RXRecv() void LanceDevice::RXCopy() { - putlog(3, "%s[%d]", __func__, rmd_cur); + rx_phase = RXPhase::COPY; + + putlog(1, "%s[%u]", __func__, rmd_cur); + // RMD0, RMD2 は受信することが確定してから読み出す uint32 rmd_addr = rmd_base + rmd_cur * 8; 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; - - // ローカルの受信バッファからディスクリプタバッファにコピー - for (; bcnt < 0; ) { - int remsize = rxq.size(); - if (remsize == 0) { - break; + // アドレスと受信バッファのバイト数。 + uint32 dst = ((rmd1 & 0x00ff) << 16) | rmd0; + // rmd2 は 2 の補数 + int32 bcnt = -(int32)(int16)rmd2; + + // 今回のコピーバイト数。 + uint copycnt = std::min((uint)bcnt, rx_packet.GetRemain()); + uint32 dstend = dst + copycnt; + + // ローカルの受信バッファからディスクリプタバッファにコピー。 + // XXX バーストコピー未対応 + + // dst が奇数アドレスから始まっている場合。 + // 奇数バイトには不定データが出るらしいがここでは 0 にする + if (__predict_false((dst & 1) != 0) && dst < dstend) { + uint16 data; + if (reg.IsBSWP() == false) { + data = rx_packet.Read() << 8; + } else { + data = rx_packet.Read(); } + WriteMem(dst & ~1, data); + dst += 1; + } + + // 残り2バイト以上コピー可能な間 (ここで dst はワード境界になっている)。 + for (; dst + 1 < dstend; ) { + uint16 data; + if (reg.IsBSWP() == false) { + data = rx_packet.Read(); + data |= rx_packet.Read() << 8; + } else { + data = rx_packet.Read() << 8; + data |= rx_packet.Read(); + } + WriteMem(dst, data); + dst += 2; + } - // XXX バーストコピー未対応 - // XXX BSWP==0の時のバッファ内のエンディアンとかは? + // dst が奇数アドレスで終わっている場合。 + // 奇数バイトには不定データが出るらしいがここでは 0 にする + if (dst < dstend) { uint16 data; - if (bcnt <= -2 && remsize >= 2 && (dst & 1) == 0) { - // 2バイト以上残っててワード境界ならワードアクセス - data = rxq.pop() << 8; - data |= rxq.pop(); - WriteMem(dst, data); - dst += 2; - bcnt += 2; - } else if ((dst & 1) == 0) { - // 偶数アドレスへ1バイト - // 奇数バイトには不定データが出るらしいがここでは 0 にする - data = rxq.pop() << 8; - WriteMem(dst, data); - dst += 1; - bcnt += 1; + if (reg.IsBSWP() == false) { + data = rx_packet.Read(); } else { - // 奇数アドレスへ1バイト - // 偶数バイトには不定データが出るらしいがここでは 0 にする - data = rxq.pop(); - WriteMem(dst & ~1, data); - dst += 1; - bcnt += 1; + data = rx_packet.Read() << 8; } + WriteMem(dst, data); } // 全部書き出せたらここが ENP - if (rxq.size() == 0) { + if (rx_packet.GetRemain() == 0) { rmd1 |= AM7990::RMD1_ENP; // ENP なら MCNT を更新 - WriteMem(rmd_addr + 6, (dst - bufdst) & 0xfff); + WriteMem(rmd_addr + 6, copycnt & 0xfff); + } else { + rmd1 &= ~AM7990::RMD1_ENP; + putlog(1, "copy remain %u", rx_packet.GetRemain()); } // メモリへの転送にかかった時間をざっくり計算 XXX まだ適当 // そのウェイト後に RXNEXT フェーズへ。 - rx_event.time = 700 * (dst - bufdst) / 16; - rx_event.code = REQ_RXNEXT; - gScheduler->AddEvent(&rx_event); + uint64 t = nsec_to_tsec(700 * copycnt / 16); + ChangePhase(RXPhase::NEXT, t); } // RXNEXT フェーズ。 @@ -1118,18 +1080,18 @@ LanceDevice::RXCopy() // - 次のディスクリプタが所有できれば引き続き書き出すため RXCOPY へ。 // - 次のディスクリプタが所有できなければエラーにして RXIDLE へ。 void -LanceDevice::RXNext() +LanceDevice::RXNext(Event *ev) { - putlog(3, "%s[%d]", __func__, rmd_cur); + rx_phase = RXPhase::NEXT; + + putlog(1, "%s[%u]", __func__, rmd_cur); - // RXCOPY フェーズで rxq が empty() になっても、(Lance スレッドの) - // メッセージループでここ(RXNEXT)に来るまでの間に、rxq に再び着信する - // 可能性はあるので、ここでは rxq を調べたり触ったりしてはいけない、 - // はず。 + bool is_chained = false; if ((rmd1 & AM7990::RMD1_ENP)) { // ENP なら、RXIDLE へ。 - request |= REQ_RXIDLE; + rx_packet.Clear(); + ChangePhase(RXPhase::IDLE); } else { // 次のディスクリプタを先読み uint32 rmd_addr = rmd_base + ((rmd_cur + 1) % rmd_num) * 8; @@ -1137,21 +1099,23 @@ LanceDevice::RXNext() if ((rmd1_ahead & AM7990::RMD1_OWN)) { // 次ディスクリプタが所有できれば RXCOPY へ。 - request |= REQ_RXCOPY; + is_chained = true; } else { // この時点で所有できなければオーバーフロー。 // XXX BUFF と OFLO の違いがよくわからん rmd1 |= AM7990::RMD1_BUFF | AM7990::RMD1_OFLO; + // 書き込み先が無いのでこのパケットは打ち切ってみる + rx_packet.Clear(); + // 次のディスクリプタをポールするため 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); // 次のディスクリプタへ @@ -1159,35 +1123,106 @@ putlog(3, "%s[%d] addr=%x RMD1=%02x", __ // どちらにしても受信割り込み SetInterrupt(AM7990::CSR0_RINT); + + if (is_chained) { + // 次のディスクリプタがある場合は RXCopy に直接遷移する。 + rmd1 = rmd1_ahead; + rmd1_ahead = -1; + RXCopy(); + } } // RXMISS 受信失敗処理。 void LanceDevice::RXMiss() { - putlog(3, "%s", __func__); + rx_phase = RXPhase::MISS; + + putlog(1, "%s", __func__); // 受信データがあるのにディスクリプタを所有してなければエラー SetInterrupt(AM7990::CSR0_MISS | AM7990::CSR0_RINT); // 受信データを破棄。 - rxq.clear(); + rx_packet.Clear(); // そのまますぐにアイドルフェーズに戻るんだろうか - request |= REQ_RXIDLE; + ChangePhase(RXPhase::IDLE); } -// イベントハンドラ -void -LanceDevice::Callback(int arg) +// この宛先アドレスを受信するかどうか。 +// これは HostNet スレッドで呼ばれる。 +int +LanceDevice::HWAddrFilter(const MacAddr& dstaddr) const { - std::unique_lock lock(mtx); + if (promisc) { + return HPF_PASS; + } + + if (dstaddr.IsUnicast()) { + if (dstaddr != padr) { + return HPF_DROP_UNICAST; + } + } else { + if (dstaddr.IsBroadcast()) { + return HPF_PASS; + } + + // マルチキャスト。 + // + // AM7990 のマルチキャストフィルタ(ハッシュ) は宛先 MAC アドレスの + // CRC (32ビット) のうち、下位6ビット *のビット順を逆順にしたもの* + // を pos として、LADRF の下から pos ビット目が立っていれば受理する + // というもの。 + // 31 7 6 5 4 3 2 1 0 + // +--- -+---+---+---+---+---+---+---+---+ + // CRC32(dstaddr) = | .. | X X | p0| p1| p2| p3| p4| p5| + // +--- -+---+---+---+---+---+---+---+---+ + // + // +---+---+---+---+---+---+ + // pos = | p5| p4| p3| p2| p1| p0| + // +---+---+---+---+---+---+ + + uint32 crc = EthernetDevice::CRC32(dstaddr); + uint pos = bitrev8(crc) >> 2; + bool mcast_pass = (ladrf & (1ULL << pos)); + putmsg(2, "mcast %02x:%02x:%02x:%02x:%02x:%02x pos=%u %s", + dstaddr[0], dstaddr[1], dstaddr[2], + dstaddr[3], dstaddr[4], dstaddr[5], + pos, (mcast_pass ? "pass" : "drop")); + if (mcast_pass == false) { + return HPF_DROP_MULTICAST; + } + } - // ワーカスレッドにフェーズ遷移を伝えるだけ - request |= arg; - cv.notify_one(); + return HPF_PASS; +} + +// 指定時間後に RX フェーズを遷移する。 +// (変化があれば)変更するではないことに注意。名前がアレだが IDLE フェーズは +// ポーリングしているので IDLE から IDLE への遷移も必ず起こす必要がある。 +// TX か RX かは関数オーバーロードで区別している。 +void +LanceDevice::ChangePhase(RXPhase new_phase, uint64 time) +{ + rx_event->time = time; + switch (new_phase) { + case RXPhase::STOP: + scheduler->StopEvent(rx_event); + return; + case RXPhase::IDLE: + rx_event->SetCallback(&LanceDevice::RXIdle); + break; + case RXPhase::NEXT: + rx_event->SetCallback(&LanceDevice::RXNext); + break; + default: + assertmsg(false, "phase %u", (uint)new_phase); + } + scheduler->RestartEvent(rx_event); } + // ビット名文字列配列は [0] が MSB 側の順で並べる /*static*/ const char * const LanceDevice::csr0names[] = { @@ -1196,7 +1231,7 @@ LanceDevice::Callback(int arg) }; /*static*/ const char * const LanceDevice::csr3names[] = { - "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", "", "", "", "BSWP", "ACON", "BCON", };