#![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];
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<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(())
}
}
#[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;
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);
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);
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);
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);
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);
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);
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);
}
{
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);
}
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<usize> {
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 {
let status = unsafe { read_volatile(UART_STATUS_REG) };
(status & UART_TXFIFO_CNT_MASK) >> UART_TXFIFO_CNT_SHIFT
}