#![no_std] #![no_main] use core::fmt::{self, Write}; use core::hint::spin_loop; use core::ptr::{read_volatile, write_volatile}; use panic_halt as _; use riscv_rt::entry; use esp_p4_eth::{ mdio_read, release_waveshare_phy_reset, Dma, Ethernet, StaticDmaResources, ANAR, ANLPAR, BMCR, BMSR, BUF_SIZE, PHYIDR1, PHYIDR2, }; use static_cell::ConstStaticCell; const UART0_BASE: usize = 0x500C_A000; const UART_FIFO_REG: *mut u32 = UART0_BASE as *mut u32; const UART_STATUS_REG: *const u32 = (UART0_BASE + 0x1C) as *const u32; const UART_TXFIFO_CNT_SHIFT: u32 = 16; const UART_TXFIFO_CNT_MASK: u32 = 0xFF << UART_TXFIFO_CNT_SHIFT; const UART_TXFIFO_CAPACITY: u32 = 128; const MAC_ADDR: [u8; 6] = [0x02, 0x00, 0x00, 0x00, 0x00, 0x01]; // Peer-to-peer link with PC. Windows has APIPA 169.254.144.33/16 on the Realtek port. const SENDER_IP: [u8; 4] = [169, 254, 144, 50]; const TARGET_IP: [u8; 4] = [169, 254, 144, 33]; const BROADCAST_MAC: [u8; 6] = [0xFF; 6]; const LINK_POLL_SPINS: usize = 200_000; const RX_POLL_SPINS: usize = 200_000; static DMA_RESOURCES: ConstStaticCell = ConstStaticCell::new(StaticDmaResources::new()); struct Uart0; impl Uart0 { fn write_byte(&mut self, byte: u8) { while txfifo_count() >= UART_TXFIFO_CAPACITY { spin_loop(); } // SAFETY: `UART_FIFO_REG` is the fixed UART0 TX FIFO MMIO register. Volatile write is // required so the compiler preserves the hardware side effect. unsafe { write_volatile(UART_FIFO_REG, byte as u32); } } } impl Write for Uart0 { fn write_str(&mut self, s: &str) -> fmt::Result { for byte in s.bytes() { if byte == b'\n' { self.write_byte(b'\r'); } self.write_byte(byte); } Ok(()) } } #[entry] fn main() -> ! { let mut uart = Uart0; let _ = writeln!(uart, "EMAC raw PHY smoke test"); release_waveshare_phy_reset(); delay_cycles(3_000_000); let dma_resources = DMA_RESOURCES.take(); let ethernet = Ethernet::new_from_static_resources(MAC_ADDR, dma_resources); let mut ethernet = ethernet; // RETRY hypothesis #2 with fixed pin routing: now OUT_SEL=256, peripheral-direct path is // active. start() may now have a real effect. let _ = writeln!(uart, "calling ethernet.start()..."); match ethernet.start() { Ok(()) => { let _ = writeln!(uart, " start() OK"); } Err(e) => { let _ = writeln!(uart, " start() ERR: {:?}", e); } } delay_cycles(2_000_000); // GPIO matrix / IO_MUX / GPIO_ENABLE diagnostic dump for MDC/MDIO/REF_CLK. unsafe { const GPIO_BASE: usize = 0x500E_0000; const IO_MUX_BASE: usize = 0x500E_1000; let rd = |a: usize| read_volatile(a as *const u32); let _ = writeln!(uart, "--- routing dump ---"); let _ = writeln!(uart, "IO_MUX_GPIO31 = 0x{:08X}", rd(IO_MUX_BASE + 4 + 31 * 4)); let _ = writeln!(uart, "IO_MUX_GPIO52 = 0x{:08X}", rd(IO_MUX_BASE + 4 + 52 * 4)); let _ = writeln!(uart, "IO_MUX_GPIO50 = 0x{:08X}", rd(IO_MUX_BASE + 4 + 50 * 4)); let _ = writeln!(uart, "GPIO31_OUT_CFG = 0x{:08X}", rd(GPIO_BASE + 0x554 + 31 * 4)); let _ = writeln!(uart, "GPIO52_OUT_CFG = 0x{:08X}", rd(GPIO_BASE + 0x554 + 52 * 4)); let _ = writeln!(uart, "GPIO_FUNC107_IN= 0x{:08X}", rd(GPIO_BASE + 0x154 + 107 * 4)); let _ = writeln!(uart, "GPIO_FUNC108_IN= 0x{:08X}", rd(GPIO_BASE + 0x154 + 108 * 4)); let _ = writeln!(uart, "GPIO_FUNC109_IN= 0x{:08X}", rd(GPIO_BASE + 0x154 + 109 * 4)); let en0 = rd(GPIO_BASE + 0x20); let en1 = rd(GPIO_BASE + 0x2C); let _ = writeln!(uart, "GPIO_ENABLE0 = 0x{:08X} (bit31 MDC = {})", en0, (en0 >> 31) & 1); let _ = writeln!(uart, "GPIO_ENABLE1 = 0x{:08X} (bit20 MDIO52 = {})", en1, (en1 >> (52 - 32)) & 1); // GPIO_OUT (level register) — bit per pin. Pin 51 RESET should be HIGH for IP101 to run. let out0 = rd(GPIO_BASE + 0x04); let out1 = rd(GPIO_BASE + 0x10); let _ = writeln!(uart, "GPIO_OUT0 = 0x{:08X}", out0); let _ = writeln!(uart, "GPIO_OUT1 = 0x{:08X} (bit19 RESET51 level = {})", out1, (out1 >> (51 - 32)) & 1); // GPIO_IN — what the pin actually reads (after pad mux). For pin 50 (REF_CLK) high // bit means the clock is currently high; toggling = clock running. let in0 = rd(GPIO_BASE + 0x3C); let in1 = rd(GPIO_BASE + 0x44); let _ = writeln!(uart, "GPIO_IN0 = 0x{:08X}", in0); let _ = writeln!(uart, "GPIO_IN1 = 0x{:08X} (bit18 REFCLK50 = {})", in1, (in1 >> (50 - 32)) & 1); // Sample REFCLK50 (GPIO50, bit18 in IN1) many times. If a 50 MHz clock is running, // we should see a mix of 0 and 1. If it's stuck high or low — clock is dead. let mut zeros = 0u32; let mut ones = 0u32; for _ in 0..200 { let v = rd(GPIO_BASE + 0x44); if (v >> 18) & 1 == 0 { zeros += 1; } else { ones += 1; } } let _ = writeln!(uart, "REFCLK50 sample: zeros={} ones={} (over 200 reads)", zeros, ones); // Same for MDC pin (GPIO31, bit31 in IN0). MDC only pulses during MDIO transactions // (and we're idle right now), so it's expected to be near-constant. But if we see // ones or transitions, then peripheral OE works and pin can drive. let mut mdc_zeros = 0u32; let mut mdc_ones = 0u32; for _ in 0..200 { let v = rd(GPIO_BASE + 0x3C); if (v >> 31) & 1 == 0 { mdc_zeros += 1; } else { mdc_ones += 1; } } let _ = writeln!(uart, "MDC31 sample: zeros={} ones={} (idle, expect mostly low)", mdc_zeros, mdc_ones); // EMAC MII_ADDR — CSR_CLOCK_RANGE field is bits[5:2]; show full reg. // EMAC_BASE on P4 = 0x5009_8000, MII_ADDR offset = 0x10. let mii_addr = rd(0x5009_8010); let _ = writeln!(uart, "EMAC MII_ADDR = 0x{:08X} (csr_clk_range = 0b{:04b})", mii_addr, (mii_addr >> 2) & 0xF); } // Drive ONE explicit mdio_read for addr 0 / BMSR and dump the post-state of MII_ADDR // and MII_DATA. This tells us whether the transaction actually completes (MII_BUSY // clears) and what the data register reads back. If BUSY stays set => MDC clock or // peripheral state issue. If BUSY=0 but data=0 => bus driven low (EMAC or floating). { let v = mdio_read(0, BMSR).unwrap_or(0xDEAD); let mii_addr_after = unsafe { read_volatile(0x5009_8010 as *const u32) }; let mii_data_after = unsafe { read_volatile(0x5009_8014 as *const u32) }; let _ = writeln!(uart, "single read addr=0 reg=BMSR -> 0x{:04X}", v); let _ = writeln!(uart, " MII_ADDR after = 0x{:08X} (BUSY={})", mii_addr_after, mii_addr_after & 1); let _ = writeln!(uart, " MII_DATA after = 0x{:08X}", mii_data_after); } // MDIO scan to confirm PHY address + bus health. let mut found = 0xFFu8; for addr in 0u8..32 { let v = mdio_read(addr, BMSR).unwrap_or(0xDEAD); let live = v != 0x0000 && v != 0xFFFF && v != 0xDEAD; if live { let _ = writeln!(uart, " scan addr={:>2} BMSR=0x{:04X} <- live", addr, v); if found == 0xFF { found = addr; } } } if found == 0xFF { let _ = writeln!(uart, " scan: NO live PHY addresses found"); found = 0; } let id1 = mdio_read(found, PHYIDR1).unwrap_or(0xDEAD); let id2 = mdio_read(found, PHYIDR2).unwrap_or(0xDEAD); let bmcr = mdio_read(found, BMCR).unwrap_or(0xDEAD); let bmsr = mdio_read(found, BMSR).unwrap_or(0xDEAD); let anar = mdio_read(found, ANAR).unwrap_or(0xDEAD); let anlpar = mdio_read(found, ANLPAR).unwrap_or(0xDEAD); let _ = writeln!(uart, "PHY@{} regs: ID1=0x{:04X} ID2=0x{:04X} BMCR=0x{:04X} BMSR=0x{:04X} ANAR=0x{:04X} ANLPAR=0x{:04X}", found, id1, id2, bmcr, bmsr, anar, anlpar); let _ = writeln!(uart, "BMSR decoded: link={} an_done={}", (bmsr >> 2) & 1, (bmsr >> 5) & 1); let _ = writeln!(uart, "Waiting for link up (poll BMSR)..."); let mut link_up = false; let mut last_bmsr = bmsr; for iter in 0..60u32 { let v = mdio_read(found, BMSR).unwrap_or(0xDEAD); if v != last_bmsr { let _ = writeln!(uart, "[{iter}] BMSR 0x{:04X} -> 0x{:04X} link={} an_done={}", last_bmsr, v, (v >> 2) & 1, (v >> 5) & 1); last_bmsr = v; } if v & 0x0004 != 0 { link_up = true; let _ = writeln!(uart, "[{iter}] LINK UP", ); break; } delay_cycles(LINK_POLL_SPINS); } if !link_up { let _ = writeln!(uart, "link still down after 60 polls (last BMSR=0x{:04X}); starting MAC anyway", last_bmsr); } let _ = writeln!(uart, "ethernet.start()..."); ethernet.start().expect("failed to start EMAC"); let _ = writeln!(uart, "phy_negotiate()..."); let (speed, duplex) = ethernet.phy_negotiate().expect("PHY negotiation failed"); log_link_mode(&mut uart, speed, duplex); let mut arp_request = [0u8; 64]; let arp_len = build_arp_request(&mut arp_request, MAC_ADDR, SENDER_IP, TARGET_IP); ethernet .transmit(&arp_request[..arp_len]) .expect("failed to send ARP request"); let _ = writeln!(uart, "ARP request sent"); let mut rx_frame = [0u8; BUF_SIZE]; let mut echo_frame = [0u8; BUF_SIZE]; loop { Dma::demand_rx_poll(); if let Some(len) = wait_for_frame(&mut ethernet, &mut rx_frame) { log_frame(&mut uart, "rx", &rx_frame[..len]); if len >= 14 { let echo_len = build_echo_frame(&mut echo_frame, &rx_frame[..len], MAC_ADDR); ethernet .transmit(&echo_frame[..echo_len]) .expect("failed to echo frame"); let _ = writeln!(uart, "echo sent: len={echo_len}"); } } } } fn wait_for_frame(ethernet: &mut Ethernet<'_>, rx_frame: &mut [u8; BUF_SIZE]) -> Option { for _ in 0..RX_POLL_SPINS { if let Some((len, data)) = ethernet.receive() { rx_frame[..len].copy_from_slice(&data[..len]); ethernet.pop_rx(); return Some(len); } spin_loop(); } None } fn build_arp_request( frame: &mut [u8; 64], source_mac: [u8; 6], sender_ip: [u8; 4], target_ip: [u8; 4], ) -> usize { frame.fill(0); frame[0..6].copy_from_slice(&BROADCAST_MAC); frame[6..12].copy_from_slice(&source_mac); frame[12..14].copy_from_slice(&0x0806u16.to_be_bytes()); frame[14..16].copy_from_slice(&0x0001u16.to_be_bytes()); frame[16..18].copy_from_slice(&0x0800u16.to_be_bytes()); frame[18] = 6; frame[19] = 4; frame[20..22].copy_from_slice(&0x0001u16.to_be_bytes()); frame[22..28].copy_from_slice(&source_mac); frame[28..32].copy_from_slice(&sender_ip); frame[32..38].fill(0); frame[38..42].copy_from_slice(&target_ip); 64 } fn build_echo_frame(out: &mut [u8; BUF_SIZE], input: &[u8], source_mac: [u8; 6]) -> usize { let len = input.len(); out[..len].copy_from_slice(input); out[0..6].copy_from_slice(&input[6..12]); out[6..12].copy_from_slice(&source_mac); len } fn log_link_mode(uart: &mut Uart0, speed: esp_p4_eth::Speed, duplex: esp_p4_eth::Duplex) { let speed_str = match speed { esp_p4_eth::Speed::Mbps10 => "10M", esp_p4_eth::Speed::Mbps100 => "100M", }; let duplex_str = match duplex { esp_p4_eth::Duplex::Half => "half", esp_p4_eth::Duplex::Full => "full", }; let _ = writeln!(uart, "link up: speed={speed_str} duplex={duplex_str}"); } fn log_frame(uart: &mut Uart0, prefix: &str, frame: &[u8]) { if frame.len() < 14 { let _ = writeln!(uart, "{prefix}: short frame len={}", frame.len()); return; } let dst = &frame[0..6]; let src = &frame[6..12]; let ether_type = u16::from_be_bytes([frame[12], frame[13]]); let _ = writeln!( uart, "{prefix}: len={} dst={:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x} src={:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x} ethertype=0x{:04x}", frame.len(), dst[0], dst[1], dst[2], dst[3], dst[4], dst[5], src[0], src[1], src[2], src[3], src[4], src[5], ether_type ); } fn delay_cycles(spins: usize) { for _ in 0..spins { spin_loop(); } } fn txfifo_count() -> u32 { // SAFETY: `UART_STATUS_REG` is the fixed UART0 status MMIO register. Volatile read is // required because the value changes independently from the CPU. let status = unsafe { read_volatile(UART_STATUS_REG) }; (status & UART_TXFIFO_CNT_MASK) >> UART_TXFIFO_CNT_SHIFT }