From c221c515027cf408eaa777e14437c10d296aaafc Mon Sep 17 00:00:00 2001 From: James Logan Date: Sat, 5 Aug 2023 15:52:05 -0400 Subject: [PATCH 1/7] Move ethernet DMA reset slightly earlier to eliminate freeze on h723 --- src/ethernet/eth.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/ethernet/eth.rs b/src/ethernet/eth.rs index d786dac0..ebe6c485 100644 --- a/src/ethernet/eth.rs +++ b/src/ethernet/eth.rs @@ -455,6 +455,12 @@ pub unsafe fn new_unchecked( // Ensure syscfg is enabled (for PMCR) rcc.apb4enr.modify(|_, w| w.syscfgen().set_bit()); + // Reset ETH_DMA - write 1 and wait for 0. + // On the H723, we have to do this before prec.enable() + // or the DMA will never come out of reset + eth_dma.dmamr.modify(|_, w| w.swr().set_bit()); + while eth_dma.dmamr.read().swr().bit_is_set() {} + // AHB1 ETH1MACEN prec.enable(); @@ -465,6 +471,7 @@ pub unsafe fn new_unchecked( .modify(|_, w| w.eth1txen().set_bit().eth1rxen().set_bit()); syscfg.pmcr.modify(|_, w| w.epis().bits(0b100)); // RMII + } // reset ETH_MAC - write 1 then 0 @@ -472,9 +479,6 @@ pub unsafe fn new_unchecked( //rcc.ahb1rstr.modify(|_, w| w.eth1macrst().clear_bit()); cortex_m::interrupt::free(|_cs| { - // reset ETH_DMA - write 1 and wait for 0 - eth_dma.dmamr.modify(|_, w| w.swr().set_bit()); - while eth_dma.dmamr.read().swr().bit_is_set() {} // 200 MHz eth_mac From 6b21ffce9c21a4d9d2288ddb85f4f0fcf6dd49df Mon Sep 17 00:00:00 2001 From: James Logan Date: Sat, 5 Aug 2023 17:35:09 -0400 Subject: [PATCH 2/7] add example that brings up the ethernet link on the nucleo-h723zg --- examples/ethernet-rtic-nucleo-h723zg.rs | 236 ++++++++++++++++++++++++ 1 file changed, 236 insertions(+) create mode 100644 examples/ethernet-rtic-nucleo-h723zg.rs diff --git a/examples/ethernet-rtic-nucleo-h723zg.rs b/examples/ethernet-rtic-nucleo-h723zg.rs new file mode 100644 index 00000000..c629e482 --- /dev/null +++ b/examples/ethernet-rtic-nucleo-h723zg.rs @@ -0,0 +1,236 @@ +//! Demo for STM32H735G-DK eval board using the Real Time for the Masses +//! (RTIC) framework. +//! +//! This demo responds to pings on 192.168.1.99 (IP address hardcoded below) +//! +//! We use the SysTick timer to create a 1ms timebase for use with smoltcp. +//! +//! The ethernet ring buffers are placed in AXI SRAM, where they can be +//! accessed by both the core and the Ethernet DMA. +//! +//! Run like +//! +//! `cargo flash --example ethernet-rtic-nucleo-h723zg --features=ethernet,stm32h735 --chip=STM32H723ZGTx` +#![deny(warnings)] +#![no_main] +#![no_std] + +#[macro_use] +#[allow(unused)] +mod utilities; + +use core::sync::atomic::AtomicU32; + +use smoltcp::iface::{Config, Interface, SocketSet, SocketStorage}; +use smoltcp::time::Instant; +use smoltcp::wire::{HardwareAddress, IpAddress, IpCidr}; + +use stm32h7xx_hal::{ethernet, rcc::CoreClocks, stm32}; + +/// Configure SYSTICK for 1ms timebase +fn systick_init(mut syst: stm32::SYST, clocks: CoreClocks) { + let c_ck_mhz = clocks.c_ck().to_MHz(); + + let syst_calib = 0x3E8; + + syst.set_clock_source(cortex_m::peripheral::syst::SystClkSource::Core); + syst.set_reload((syst_calib * c_ck_mhz) - 1); + syst.enable_interrupt(); + syst.enable_counter(); +} + +/// TIME is an atomic u32 that counts milliseconds. +static TIME: AtomicU32 = AtomicU32::new(0); + +/// Locally administered MAC address +const MAC_ADDRESS: [u8; 6] = [0x02, 0x00, 0x11, 0x22, 0x33, 0x44]; + +/// Ethernet descriptor rings are a global singleton +#[link_section = ".axisram.eth"] +static mut DES_RING: ethernet::DesRing<4, 4> = ethernet::DesRing::new(); + +/// Net storage with static initialisation - another global singleton +pub struct NetStorageStatic<'a> { + socket_storage: [SocketStorage<'a>; 8], +} +static mut STORE: NetStorageStatic = NetStorageStatic { + // Garbage + socket_storage: [SocketStorage::EMPTY; 8], +}; + +pub struct Net<'a> { + iface: Interface, + ethdev: ethernet::EthernetDMA<4, 4>, + sockets: SocketSet<'a>, +} +impl<'a> Net<'a> { + pub fn new( + store: &'a mut NetStorageStatic<'a>, + mut ethdev: ethernet::EthernetDMA<4, 4>, + ethernet_addr: HardwareAddress, + ) -> Self { + let mut config = Config::new(); + config.hardware_addr = Some(ethernet_addr); + + let mut iface = Interface::new(config, &mut ethdev); + // Set IP address + iface.update_ip_addrs(|addrs| { + let _ = addrs.push(IpCidr::new(IpAddress::v4(192, 168, 1, 99), 0)); + }); + + let sockets = SocketSet::new(&mut store.socket_storage[..]); + + Net::<'a> { + iface, + ethdev, + sockets, + } + } + + /// Polls on the ethernet interface. You should refer to the smoltcp + /// documentation for poll() to understand how to call poll efficiently + pub fn poll(&mut self, now: i64) { + let timestamp = Instant::from_millis(now); + + self.iface + .poll(timestamp, &mut self.ethdev, &mut self.sockets); + } +} + +#[rtic::app(device = stm32h7xx_hal::stm32, peripherals = true)] +mod app { + use stm32h7xx_hal::{ethernet, ethernet::PHY, gpio, prelude::*}; + + use super::*; + use core::sync::atomic::Ordering; + + #[shared] + struct SharedResources {} + #[local] + struct LocalResources { + net: Net<'static>, + lan8742a: ethernet::phy::LAN8742A, + link_led: gpio::gpioe::PE1>, + } + + #[init] + fn init( + mut ctx: init::Context, + ) -> (SharedResources, LocalResources, init::Monotonics) { + utilities::logger::init(); + // Initialise power... + let pwr = ctx.device.PWR.constrain(); + let pwrcfg = pwr.ldo().freeze(); // nucleo-h723zg board doesn't have SMPS + + // Initialise clocks... + let rcc = ctx.device.RCC.constrain(); + let ccdr = rcc + .sys_ck(200.MHz()) + .hclk(200.MHz()) + .freeze(pwrcfg, &ctx.device.SYSCFG); + + // Initialise system... + ctx.core.SCB.invalidate_icache(); + ctx.core.SCB.enable_icache(); + // TODO: ETH DMA coherence issues + // ctx.core.SCB.enable_dcache(&mut ctx.core.CPUID); + ctx.core.DWT.enable_cycle_counter(); + + // Initialise IO... + let gpioa = ctx.device.GPIOA.split(ccdr.peripheral.GPIOA); + let gpioc = ctx.device.GPIOC.split(ccdr.peripheral.GPIOC); + let gpioe = ctx.device.GPIOE.split(ccdr.peripheral.GPIOE); + let gpiob = ctx.device.GPIOB.split(ccdr.peripheral.GPIOB); + let mut link_led = gpioe.pe1.into_push_pull_output(); // USR LED1 + link_led.set_high(); + + let rmii_ref_clk = gpioa.pa1.into_alternate(); + let rmii_mdio = gpioa.pa2.into_alternate(); + let rmii_mdc = gpioc.pc1.into_alternate(); + let rmii_crs_dv = gpioa.pa7.into_alternate(); + let rmii_rxd0 = gpioc.pc4.into_alternate(); + let rmii_rxd1 = gpioc.pc5.into_alternate(); + let rmii_tx_en = gpiob.pb11.into_alternate(); + let rmii_txd0 = gpiob.pb12.into_alternate(); + let rmii_txd1 = gpiob.pb13.into_alternate(); + + // Initialise ethernet... + assert_eq!(ccdr.clocks.hclk().raw(), 200_000_000); // HCLK 200MHz + assert_eq!(ccdr.clocks.pclk1().raw(), 100_000_000); // PCLK 100MHz + assert_eq!(ccdr.clocks.pclk2().raw(), 100_000_000); // PCLK 100MHz + assert_eq!(ccdr.clocks.pclk4().raw(), 100_000_000); // PCLK 100MHz + + let mac_addr = smoltcp::wire::EthernetAddress::from_bytes(&MAC_ADDRESS); + let (eth_dma, eth_mac) = unsafe { + ethernet::new( + ctx.device.ETHERNET_MAC, + ctx.device.ETHERNET_MTL, + ctx.device.ETHERNET_DMA, + ( + rmii_ref_clk, + rmii_mdio, + rmii_mdc, + rmii_crs_dv, + rmii_rxd0, + rmii_rxd1, + rmii_tx_en, + rmii_txd0, + rmii_txd1, + ), + &mut DES_RING, + mac_addr, + ccdr.peripheral.ETH1MAC, + &ccdr.clocks, + ) + }; + + // Initialise ethernet PHY... + let mut lan8742a = ethernet::phy::LAN8742A::new(eth_mac); + lan8742a.phy_reset(); + lan8742a.phy_init(); + // The eth_dma should not be used until the PHY reports the link is up + + unsafe { ethernet::enable_interrupt() }; + + // unsafe: mutable reference to static storage, we only do this once + let store = unsafe { &mut STORE }; + let net = Net::new(store, eth_dma, mac_addr.into()); + + // 1ms tick + systick_init(ctx.core.SYST, ccdr.clocks); + + ( + SharedResources {}, + LocalResources { + net, + lan8742a, + link_led, + }, + init::Monotonics(), + ) + } + + #[idle(local = [lan8742a, link_led])] + fn idle(ctx: idle::Context) -> ! { + loop { + // Ethernet + match ctx.local.lan8742a.poll_link() { + true => ctx.local.link_led.set_low(), + _ => ctx.local.link_led.set_high(), + } + } + } + + #[task(binds = ETH, local = [net])] + fn ethernet_event(ctx: ethernet_event::Context) { + unsafe { ethernet::interrupt_handler() } + + let time = TIME.load(Ordering::Relaxed); + ctx.local.net.poll(time as i64); + } + + #[task(binds = SysTick, priority=15)] + fn systick_tick(_: systick_tick::Context) { + TIME.fetch_add(1, Ordering::Relaxed); + } +} From 2c835a2c0804c34083a1bdd9b9e35f6ff205ecc3 Mon Sep 17 00:00:00 2001 From: James Logan Date: Sat, 5 Aug 2023 17:49:55 -0400 Subject: [PATCH 3/7] run cargo fmt --- examples/ethernet-rtic-nucleo-h723zg.rs | 6 +++--- src/ethernet/eth.rs | 2 -- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/examples/ethernet-rtic-nucleo-h723zg.rs b/examples/ethernet-rtic-nucleo-h723zg.rs index c629e482..5fb05100 100644 --- a/examples/ethernet-rtic-nucleo-h723zg.rs +++ b/examples/ethernet-rtic-nucleo-h723zg.rs @@ -7,9 +7,9 @@ //! //! The ethernet ring buffers are placed in AXI SRAM, where they can be //! accessed by both the core and the Ethernet DMA. -//! +//! //! Run like -//! +//! //! `cargo flash --example ethernet-rtic-nucleo-h723zg --features=ethernet,stm32h735 --chip=STM32H723ZGTx` #![deny(warnings)] #![no_main] @@ -120,7 +120,7 @@ mod app { utilities::logger::init(); // Initialise power... let pwr = ctx.device.PWR.constrain(); - let pwrcfg = pwr.ldo().freeze(); // nucleo-h723zg board doesn't have SMPS + let pwrcfg = pwr.ldo().freeze(); // nucleo-h723zg board doesn't have SMPS // Initialise clocks... let rcc = ctx.device.RCC.constrain(); diff --git a/src/ethernet/eth.rs b/src/ethernet/eth.rs index ebe6c485..dc51ed92 100644 --- a/src/ethernet/eth.rs +++ b/src/ethernet/eth.rs @@ -471,7 +471,6 @@ pub unsafe fn new_unchecked( .modify(|_, w| w.eth1txen().set_bit().eth1rxen().set_bit()); syscfg.pmcr.modify(|_, w| w.epis().bits(0b100)); // RMII - } // reset ETH_MAC - write 1 then 0 @@ -479,7 +478,6 @@ pub unsafe fn new_unchecked( //rcc.ahb1rstr.modify(|_, w| w.eth1macrst().clear_bit()); cortex_m::interrupt::free(|_cs| { - // 200 MHz eth_mac .mac1ustcr From 0acd6361871155b6402500917532112ee4dc5bd0 Mon Sep 17 00:00:00 2001 From: James Logan Date: Sat, 5 Aug 2023 18:02:33 -0400 Subject: [PATCH 4/7] update example docstring --- examples/ethernet-rtic-nucleo-h723zg.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/ethernet-rtic-nucleo-h723zg.rs b/examples/ethernet-rtic-nucleo-h723zg.rs index 5fb05100..7cbc0384 100644 --- a/examples/ethernet-rtic-nucleo-h723zg.rs +++ b/examples/ethernet-rtic-nucleo-h723zg.rs @@ -1,4 +1,4 @@ -//! Demo for STM32H735G-DK eval board using the Real Time for the Masses +//! Demo for Nucleo-H723ZG eval board using the Real Time for the Masses //! (RTIC) framework. //! //! This demo responds to pings on 192.168.1.99 (IP address hardcoded below) From e8269a3eab1a44797aece957858c7c9643aca924 Mon Sep 17 00:00:00 2001 From: James Logan Date: Sun, 6 Aug 2023 16:12:32 -0400 Subject: [PATCH 5/7] add feature overrides for new example --- Cargo.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index a51868ae..bb13905e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -215,6 +215,10 @@ required-features = ["rt", "stm32h747cm7", "ethernet"] name = "ethernet-rtic-stm32h735g-dk" required-features = ["rt", "stm32h735", "ethernet"] +[[example]] +name = "ethernet-rtic-nucleo-h723zg" +required-features = ["rt", "stm32h735", "ethernet"] + [[example]] name = "ethernet-nucleo-h743zi2" required-features = ["rt", "revision_v", "stm32h743v", "ethernet"] From ff7efaa924a0c9f34e8acf3054d1b54838261863 Mon Sep 17 00:00:00 2001 From: James Logan Date: Mon, 21 Aug 2023 19:20:40 -0400 Subject: [PATCH 6/7] update example for compatibility with smoltcp 0.10.x --- examples/ethernet-rtic-nucleo-h723zg.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/ethernet-rtic-nucleo-h723zg.rs b/examples/ethernet-rtic-nucleo-h723zg.rs index 7cbc0384..d5009a6b 100644 --- a/examples/ethernet-rtic-nucleo-h723zg.rs +++ b/examples/ethernet-rtic-nucleo-h723zg.rs @@ -69,10 +69,9 @@ impl<'a> Net<'a> { mut ethdev: ethernet::EthernetDMA<4, 4>, ethernet_addr: HardwareAddress, ) -> Self { - let mut config = Config::new(); - config.hardware_addr = Some(ethernet_addr); + let config = Config::new(ethernet_addr); - let mut iface = Interface::new(config, &mut ethdev); + let mut iface = Interface::new(config, &mut ethdev, Instant::ZERO); // Set IP address iface.update_ip_addrs(|addrs| { let _ = addrs.push(IpCidr::new(IpAddress::v4(192, 168, 1, 99), 0)); From d2a215e22d69b81c82f5aa23ad914eeb41496124 Mon Sep 17 00:00:00 2001 From: James Logan Date: Tue, 22 Aug 2023 22:55:00 -0400 Subject: [PATCH 7/7] remove defunct comment --- examples/ethernet-rtic-nucleo-h723zg.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/ethernet-rtic-nucleo-h723zg.rs b/examples/ethernet-rtic-nucleo-h723zg.rs index d5009a6b..fb13fb8d 100644 --- a/examples/ethernet-rtic-nucleo-h723zg.rs +++ b/examples/ethernet-rtic-nucleo-h723zg.rs @@ -53,8 +53,8 @@ static mut DES_RING: ethernet::DesRing<4, 4> = ethernet::DesRing::new(); pub struct NetStorageStatic<'a> { socket_storage: [SocketStorage<'a>; 8], } + static mut STORE: NetStorageStatic = NetStorageStatic { - // Garbage socket_storage: [SocketStorage::EMPTY; 8], };