#![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::{
diag::CPU_DESC0_SNAPSHOT, mdio_read, regs, release_waveshare_phy_reset, Dma, Ethernet,
StaticDmaResources, ANAR, ANLPAR, BMCR, BMSR, 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] = [0xE8, 0xF6, 0x0A, 0xE0, 0x93, 0xF5];
static DMA_RESOURCES: ConstStaticCell<StaticDmaResources> =
ConstStaticCell::new(StaticDmaResources::new());
struct Uart0;
impl Uart0 {
fn write_byte(&mut self, byte: u8) {
while txfifo_count() >= UART_TXFIFO_CAPACITY {
spin_loop();
}
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(())
}
}
fn txfifo_count() -> u32 {
let status = unsafe { read_volatile(UART_STATUS_REG) };
(status & UART_TXFIFO_CNT_MASK) >> UART_TXFIFO_CNT_SHIFT
}
fn delay_cycles(spins: usize) {
for _ in 0..spins {
spin_loop();
}
}
#[entry]
fn main() -> ! {
let mut uart = Uart0;
let _ = writeln!(uart, "\r\n=== mdio_test alive ===");
release_waveshare_phy_reset();
delay_cycles(3_000_000);
let _ = writeln!(uart, "phy reset released");
let resources = DMA_RESOURCES.take();
let (tx_desc, rx_desc, tx_buf, rx_buf) = resources.split();
let _ = writeln!(uart, "DMA resources allocated");
let mut ethernet = match Ethernet::try_new(MAC_ADDR, tx_desc, rx_desc, tx_buf, rx_buf) {
Ok(eth) => {
let _ = writeln!(uart, "Ethernet::try_new OK");
eth
}
Err(e) => {
let _ = writeln!(uart, "Ethernet::try_new ERR: {:?}", e);
loop {
spin_loop();
}
}
};
match ethernet.start() {
Ok(()) => {
let _ = writeln!(uart, "ethernet.start() OK");
}
Err(e) => {
let _ = writeln!(uart, "ethernet.start() ERR: {:?}", e);
}
}
delay_cycles(2_000_000);
{
let base = regs::read(regs::dma::RX_DESC_LIST);
let mem = unsafe { Dma::peek_rdes(base) };
let cached = unsafe { Dma::peek_rdes_cached(base) };
use core::sync::atomic::Ordering;
let _ = writeln!(uart, "--- DESCRIPTOR[0] PEEK CONTROL ---");
let _ = writeln!(uart, " RX_DESC_LIST register = 0x{:08X}", base);
let _ = writeln!(
uart,
" CPU snapshot : rdes0=0x{:08X} rdes1=0x{:08X} buf=0x{:08X} next=0x{:08X}",
CPU_DESC0_SNAPSHOT[0].load(Ordering::Relaxed),
CPU_DESC0_SNAPSHOT[1].load(Ordering::Relaxed),
CPU_DESC0_SNAPSHOT[2].load(Ordering::Relaxed),
CPU_DESC0_SNAPSHOT[3].load(Ordering::Relaxed),
);
let _ = writeln!(
uart,
" cached (no inv): rdes0=0x{:08X} rdes1=0x{:08X} buf=0x{:08X} next=0x{:08X}",
cached[0], cached[1], cached[2], cached[3],
);
let _ = writeln!(
uart,
" mem (after inv): rdes0=0x{:08X} rdes1=0x{:08X} buf=0x{:08X} next=0x{:08X}",
mem[0], mem[1], mem[2], mem[3],
);
}
let _ = writeln!(uart, "--- post-init register dump ---");
let r = |a: usize| unsafe { read_volatile(a as *const u32) };
let _ = writeln!(uart, " IO_MUX_GPIO31 = 0x{:08X}", r(0x500E_1004 + 31 * 4));
let _ = writeln!(uart, " IO_MUX_GPIO52 = 0x{:08X}", r(0x500E_1004 + 52 * 4));
let _ = writeln!(uart, " IO_MUX_GPIO50 = 0x{:08X}", r(0x500E_1004 + 50 * 4));
let _ = writeln!(uart, " GPIO31_OUT_CFG = 0x{:08X}", r(0x500E_0558 + 31 * 4));
let _ = writeln!(uart, " GPIO52_OUT_CFG = 0x{:08X}", r(0x500E_0558 + 52 * 4));
let _ = writeln!(uart, " FUNC107_IN_SEL = 0x{:08X}", r(0x500E_0158 + 107 * 4));
let _ = writeln!(uart, " FUNC108_IN_SEL = 0x{:08X}", r(0x500E_0158 + 108 * 4));
let _ = writeln!(uart, " FUNC109_IN_SEL = 0x{:08X}", r(0x500E_0158 + 109 * 4));
let _ = writeln!(uart, " GPIO_ENABLE0 = 0x{:08X}", r(0x500E_0020));
let _ = writeln!(uart, " GPIO_ENABLE1 = 0x{:08X}", r(0x500E_002C));
let _ = writeln!(uart, " GPIO_OUT0 = 0x{:08X}", r(0x500E_0004));
let _ = writeln!(uart, " GPIO_OUT1 = 0x{:08X}", r(0x500E_0010));
let _ = writeln!(uart, " IO_MUX_GPIO51 = 0x{:08X}", r(0x500E_1004 + 51 * 4));
let _ = writeln!(uart, " PERI_CLK_CTRL00 = 0x{:08X}", r(0x500E_6030));
let _ = writeln!(uart, " PERI_CLK_CTRL01 = 0x{:08X}", r(0x500E_6034));
let _ = writeln!(uart, " LP_HP_CLK_CTRL = 0x{:08X}", r(0x5011_1040));
let _ = writeln!(uart, " SYS_GMAC_CTRL0 = 0x{:08X}", r(0x500E_514C));
let _ = writeln!(uart, " MAC_CONFIG = 0x{:08X}", r(0x5009_8000));
let _ = writeln!(uart, " MAC_FRAME_FILTER = 0x{:08X}", r(0x5009_8004));
let mii_addr = r(0x5009_8010);
let _ = writeln!(
uart,
" MII_ADDR = 0x{:08X} (csr_clk_range bits[5:2] = 0b{:04b})",
mii_addr,
(mii_addr >> 2) & 0xF
);
let _ = writeln!(uart, " MII_DATA = 0x{:08X}", r(0x5009_8014));
let _ = writeln!(uart, " MAC_VERSION = 0x{:08X}", r(0x5009_8020));
let _ = writeln!(uart, " DMA_BUS_MODE = 0x{:08X}", r(0x5009_9000));
let _ = writeln!(uart, " DMA_OP_MODE = 0x{:08X}", r(0x5009_9018));
let _ = writeln!(uart, " DMA_STATUS = 0x{:08X}", r(0x5009_9014));
unsafe {
let mii_addr = 0x5009_8010 as *mut u32;
let v = read_volatile(mii_addr);
let v_new = v & !(0xF << 2);
write_volatile(mii_addr, v_new);
let _ = writeln!(
uart,
" MII_ADDR forced csr_clk=0 -> 0x{:08X}",
read_volatile(mii_addr as *const u32)
);
}
let _ = writeln!(uart, "--- single MDIO probe (PHY=1 BMSR) ---");
let pre_addr = r(0x5009_8010);
let pre_data = r(0x5009_8014);
let _ = writeln!(
uart,
" pre-read MII_ADDR=0x{:08X} MII_DATA=0x{:08X}",
pre_addr, pre_data
);
let v = mdio_read(1, BMSR).unwrap_or(0xDEAD);
let post_addr = r(0x5009_8010);
let post_data = r(0x5009_8014);
let _ = writeln!(
uart,
" post-read MII_ADDR=0x{:08X} MII_DATA=0x{:08X} return=0x{:04X}",
post_addr, post_data, v
);
let _ = writeln!(uart, "--- MDIO scan (32 addresses) ---");
let mut found: Option<u8> = None;
for addr in 0u8..32 {
let v = mdio_read(addr, BMSR).unwrap_or(0xDEAD);
let alive = v != 0x0000 && v != 0xFFFF && v != 0xDEAD;
if alive {
let _ = writeln!(uart, " addr={:>2} BMSR=0x{:04X} <- LIVE", addr, v);
if found.is_none() {
found = Some(addr);
}
}
}
let phy_addr = match found {
Some(addr) => {
let _ = writeln!(uart, "PHY found at addr {}", addr);
addr
}
None => {
let _ = writeln!(uart, "NO live PHY on the bus (all addresses returned 0x0000)");
loop {
spin_loop();
}
}
};
let id1 = mdio_read(phy_addr, PHYIDR1).unwrap_or(0xDEAD);
let id2 = mdio_read(phy_addr, PHYIDR2).unwrap_or(0xDEAD);
let bmcr = mdio_read(phy_addr, BMCR).unwrap_or(0xDEAD);
let bmsr = mdio_read(phy_addr, BMSR).unwrap_or(0xDEAD);
let anar = mdio_read(phy_addr, ANAR).unwrap_or(0xDEAD);
let anlpar = mdio_read(phy_addr, ANLPAR).unwrap_or(0xDEAD);
let _ = writeln!(uart, "PHY@{}: ID1=0x{:04X} ID2=0x{:04X}", phy_addr, id1, id2);
let _ = writeln!(
uart,
" BMCR=0x{:04X} BMSR=0x{:04X} ANAR=0x{:04X} ANLPAR=0x{:04X}",
bmcr, bmsr, anar, anlpar
);
if id1 == 0x0243 {
let _ = writeln!(uart, "PHY ID matches IP101 family (0x0243). MDIO IS WORKING.");
}
let _ = writeln!(uart, "--- waiting for auto-neg (poll BMSR up to 15s) ---");
let mut last_bmsr = bmsr;
let mut linked = false;
for tick in 0..150u32 {
let v = mdio_read(phy_addr, BMSR).unwrap_or(0xDEAD);
if v != last_bmsr {
let lp = mdio_read(phy_addr, ANLPAR).unwrap_or(0xDEAD);
let _ = writeln!(
uart,
"[t={:>3}] BMSR 0x{:04X} -> 0x{:04X} link={} an_done={} ANLPAR=0x{:04X}",
tick * 100,
last_bmsr,
v,
(v >> 2) & 1,
(v >> 5) & 1,
lp
);
last_bmsr = v;
}
if (v >> 2) & 1 != 0 && (v >> 5) & 1 != 0 {
linked = true;
let _ = writeln!(uart, "[t={:>3}ms] LINK UP", tick * 100);
break;
}
delay_cycles(200_000);
}
if !linked {
let _ = writeln!(uart, "auto-neg did NOT complete in 15s. Final state:");
} else {
let _ = writeln!(uart, "Final post-link state:");
}
let bmcr2 = mdio_read(phy_addr, BMCR).unwrap_or(0xDEAD);
let bmsr2 = mdio_read(phy_addr, BMSR).unwrap_or(0xDEAD);
let anar2 = mdio_read(phy_addr, ANAR).unwrap_or(0xDEAD);
let anlpar2 = mdio_read(phy_addr, ANLPAR).unwrap_or(0xDEAD);
let _ = writeln!(
uart,
" BMCR=0x{:04X} BMSR=0x{:04X} ANAR=0x{:04X} ANLPAR=0x{:04X}",
bmcr2, bmsr2, anar2, anlpar2
);
let _ = writeln!(
uart,
" decoded: link={} an_done={} 100M={} duplex_full={}",
(bmsr2 >> 2) & 1,
(bmsr2 >> 5) & 1,
((anar2 & anlpar2) >> 7) & 0x3, ((anar2 & anlpar2) >> 6) & 0x3, );
let ip101_status = mdio_read(phy_addr, 0x11).unwrap_or(0xDEAD);
let _ = writeln!(uart, " IP101 SPECIFIC_STATUS (reg 0x11) = 0x{:04X}", ip101_status);
if !linked {
let _ = writeln!(uart, "=== no link, skipping TX test ===");
loop {
spin_loop();
}
}
match ethernet.phy_negotiate() {
Ok((speed, duplex)) => {
let s = match speed {
esp_p4_eth::Speed::Mbps10 => "10M",
esp_p4_eth::Speed::Mbps100 => "100M",
};
let d = match duplex {
esp_p4_eth::Duplex::Half => "half",
esp_p4_eth::Duplex::Full => "full",
};
let _ = writeln!(uart, "phy_negotiate OK: speed={} duplex={}", s, d);
}
Err(e) => {
let _ = writeln!(uart, "phy_negotiate ERR: {:?}", e);
}
}
const SENDER_IP: [u8; 4] = [169, 254, 144, 50];
const TARGET_IP: [u8; 4] = [169, 254, 144, 33];
let mut arp = [0u8; 64];
build_arp_request(&mut arp, MAC_ADDR, SENDER_IP, TARGET_IP);
let _ = writeln!(
uart,
"ARP who-has {}.{}.{}.{} tell {}.{}.{}.{}",
TARGET_IP[0], TARGET_IP[1], TARGET_IP[2], TARGET_IP[3],
SENDER_IP[0], SENDER_IP[1], SENDER_IP[2], SENDER_IP[3]
);
match ethernet.transmit(&arp) {
Ok(()) => {
let _ = writeln!(uart, "TX OK (60-byte ARP request sent)");
}
Err(e) => {
let _ = writeln!(uart, "TX ERR: {:?}", e);
}
}
let _ = writeln!(uart, "--- listening for RX frames (5s) ---");
let mut rx_count = 0u32;
for _ in 0..50u32 {
esp_p4_eth::Dma::demand_rx_poll();
if let Some((len, data)) = ethernet.receive() {
rx_count += 1;
let _ = writeln!(
uart,
"RX #{}: len={} dst={:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x} src={:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x} ethertype=0x{:02x}{:02x}",
rx_count,
len,
data[0], data[1], data[2], data[3], data[4], data[5],
data[6], data[7], data[8], data[9], data[10], data[11],
data[12], data[13]
);
ethernet.pop_rx();
}
delay_cycles(200_000); }
let _ = writeln!(uart, "RX total: {} frames in 5s", rx_count);
if rx_count == 0 {
let _ = writeln!(uart, "(no frames seen — check cable, IP, or ARP cache)");
}
let _ = writeln!(uart, "=== mdio_test done ===");
loop {
spin_loop();
}
}
const BROADCAST_MAC: [u8; 6] = [0xFF; 6];
fn build_arp_request(
frame: &mut [u8; 64],
source_mac: [u8; 6],
sender_ip: [u8; 4],
target_ip: [u8; 4],
) {
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[38..42].copy_from_slice(&target_ip);
}