diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs index 76a3dfab..fd1b4853 100644 --- a/embassy-stm32/src/eth/mod.rs +++ b/embassy-stm32/src/eth/mod.rs @@ -1,14 +1,131 @@ #![macro_use] +#![cfg_attr(not(feature = "embassy-net"), allow(unused))] -#[cfg(feature = "net")] #[cfg_attr(any(eth_v1a, eth_v1b, eth_v1c), path = "v1/mod.rs")] #[cfg_attr(eth_v2, path = "v2/mod.rs")] mod _version; pub mod generic_smi; -#[cfg(feature = "net")] pub use _version::*; +use embassy_sync::waitqueue::AtomicWaker; +#[allow(unused)] +const MTU: usize = 1514; +const TX_BUFFER_SIZE: usize = 1514; +const RX_BUFFER_SIZE: usize = 1536; + +#[repr(C, align(8))] +#[derive(Copy, Clone)] +pub(crate) struct Packet([u8; N]); + +pub struct PacketQueue { + tx_desc: [TDes; TX], + rx_desc: [RDes; RX], + tx_buf: [Packet; TX], + rx_buf: [Packet; RX], +} + +impl PacketQueue { + pub const fn new() -> Self { + const NEW_TDES: TDes = TDes::new(); + const NEW_RDES: RDes = RDes::new(); + Self { + tx_desc: [NEW_TDES; TX], + rx_desc: [NEW_RDES; RX], + tx_buf: [Packet([0; TX_BUFFER_SIZE]); TX], + rx_buf: [Packet([0; RX_BUFFER_SIZE]); RX], + } + } +} + +static WAKER: AtomicWaker = AtomicWaker::new(); + +#[cfg(feature = "embassy-net")] +mod embassy_net_impl { + use core::task::Context; + + use embassy_net::device::{Device, DeviceCapabilities, LinkState}; + + use super::*; + + impl<'d, T: Instance, P: PHY> Device for Ethernet<'d, T, P> { + type RxToken<'a> = RxToken<'a, 'd> where Self: 'a; + type TxToken<'a> = TxToken<'a, 'd> where Self: 'a; + + fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + WAKER.register(cx.waker()); + if self.rx.available().is_some() && self.tx.available().is_some() { + Some((RxToken { rx: &mut self.rx }, TxToken { tx: &mut self.tx })) + } else { + None + } + } + + fn transmit(&mut self, cx: &mut Context) -> Option> { + WAKER.register(cx.waker()); + if self.tx.available().is_some() { + Some(TxToken { tx: &mut self.tx }) + } else { + None + } + } + + fn capabilities(&self) -> DeviceCapabilities { + let mut caps = DeviceCapabilities::default(); + caps.max_transmission_unit = MTU; + caps.max_burst_size = Some(self.tx.len()); + caps + } + + fn link_state(&mut self, cx: &mut Context) -> LinkState { + // TODO: wake cx.waker on link state change + cx.waker().wake_by_ref(); + if P::poll_link(self) { + LinkState::Up + } else { + LinkState::Down + } + } + + fn ethernet_address(&self) -> [u8; 6] { + self.mac_addr + } + } + + pub struct RxToken<'a, 'd> { + rx: &'a mut RDesRing<'d>, + } + + impl<'a, 'd> embassy_net::device::RxToken for RxToken<'a, 'd> { + fn consume(self, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + // NOTE(unwrap): we checked the queue wasn't full when creating the token. + let pkt = unwrap!(self.rx.available()); + let r = f(pkt); + self.rx.pop_packet(); + r + } + } + + pub struct TxToken<'a, 'd> { + tx: &'a mut TDesRing<'d>, + } + + impl<'a, 'd> embassy_net::device::TxToken for TxToken<'a, 'd> { + fn consume(self, len: usize, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + // NOTE(unwrap): we checked the queue wasn't full when creating the token. + let pkt = unwrap!(self.tx.available()); + let r = f(&mut pkt[..len]); + self.tx.transmit(len); + r + } + } +} /// Station Management Interface (SMI) on an ethernet PHY /// /// # Safety diff --git a/embassy-stm32/src/eth/v1/descriptors.rs b/embassy-stm32/src/eth/v1/descriptors.rs deleted file mode 100644 index 25f21ce1..00000000 --- a/embassy-stm32/src/eth/v1/descriptors.rs +++ /dev/null @@ -1,21 +0,0 @@ -use crate::eth::_version::rx_desc::RDesRing; -use crate::eth::_version::tx_desc::TDesRing; - -pub struct DescriptorRing { - pub(crate) tx: TDesRing, - pub(crate) rx: RDesRing, -} - -impl DescriptorRing { - pub const fn new() -> Self { - Self { - tx: TDesRing::new(), - rx: RDesRing::new(), - } - } - - pub fn init(&mut self) { - self.tx.init(); - self.rx.init(); - } -} diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs index 38629a93..de36d3da 100644 --- a/embassy-stm32/src/eth/v1/mod.rs +++ b/embassy-stm32/src/eth/v1/mod.rs @@ -1,14 +1,17 @@ // The v1c ethernet driver was ported to embassy from the awesome stm32-eth project (https://github.com/stm32-rs/stm32-eth). -use core::marker::PhantomData; +mod rx_desc; +mod tx_desc; + use core::sync::atomic::{fence, Ordering}; -use core::task::Waker; -use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorage}; +use embassy_cortex_m::interrupt::InterruptExt; use embassy_hal_common::{into_ref, PeripheralRef}; -use embassy_net::{Device, DeviceCapabilities, LinkState, PacketBuf, MTU}; -use embassy_sync::waitqueue::AtomicWaker; +use stm32_metapac::eth::vals::{Apcs, Cr, Dm, DmaomrSr, Fes, Ftf, Ifg, MbProgress, Mw, Pbl, Rsf, St, Tsf}; +pub(crate) use self::rx_desc::{RDes, RDesRing}; +pub(crate) use self::tx_desc::{TDes, TDesRing}; +use super::*; use crate::gpio::sealed::{AFType, Pin as __GpioPin}; use crate::gpio::{AnyPin, Speed}; #[cfg(eth_v1a)] @@ -18,29 +21,16 @@ use crate::pac::SYSCFG; use crate::pac::{ETH, RCC}; use crate::Peripheral; -mod descriptors; -mod rx_desc; -mod tx_desc; +pub struct Ethernet<'d, T: Instance, P: PHY> { + _peri: PeripheralRef<'d, T>, + pub(crate) tx: TDesRing<'d>, + pub(crate) rx: RDesRing<'d>, -use descriptors::DescriptorRing; -use stm32_metapac::eth::vals::{Apcs, Cr, Dm, DmaomrSr, Fes, Ftf, Ifg, MbProgress, Mw, Pbl, Rsf, St, Tsf}; - -use super::*; - -pub struct State<'d, T: Instance, const TX: usize, const RX: usize>(StateStorage>); -impl<'d, T: Instance, const TX: usize, const RX: usize> State<'d, T, TX, RX> { - pub const fn new() -> Self { - Self(StateStorage::new()) - } -} - -pub struct Ethernet<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> { - state: PeripheralMutex<'d, Inner<'d, T, TX, RX>>, pins: [PeripheralRef<'d, AnyPin>; 9], _phy: P, clock_range: Cr, phy_addr: u8, - mac_addr: [u8; 6], + pub(crate) mac_addr: [u8; 6], } #[cfg(eth_v1a)] @@ -82,10 +72,10 @@ macro_rules! config_pins { }; } -impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Ethernet<'d, T, P, TX, RX> { +impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { /// safety: the returned instance is not leak-safe - pub unsafe fn new( - state: &'d mut State<'d, T, TX, RX>, + pub fn new( + queue: &'d mut PacketQueue, peri: impl Peripheral

+ 'd, interrupt: impl Peripheral

+ 'd, ref_clk: impl Peripheral

> + 'd, @@ -101,134 +91,131 @@ impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Ethernet<'d, T, mac_addr: [u8; 6], phy_addr: u8, ) -> Self { - into_ref!(interrupt, ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); + into_ref!(peri, interrupt, ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); - // Enable the necessary Clocks - // NOTE(unsafe) We have exclusive access to the registers - #[cfg(eth_v1a)] - critical_section::with(|_| { - RCC.apb2enr().modify(|w| w.set_afioen(true)); + unsafe { + // Enable the necessary Clocks + // NOTE(unsafe) We have exclusive access to the registers + #[cfg(eth_v1a)] + critical_section::with(|_| { + RCC.apb2enr().modify(|w| w.set_afioen(true)); - // Select RMII (Reduced Media Independent Interface) - // Must be done prior to enabling peripheral clock - AFIO.mapr().modify(|w| w.set_mii_rmii_sel(true)); + // Select RMII (Reduced Media Independent Interface) + // Must be done prior to enabling peripheral clock + AFIO.mapr().modify(|w| w.set_mii_rmii_sel(true)); - RCC.ahbenr().modify(|w| { - w.set_ethen(true); - w.set_ethtxen(true); - w.set_ethrxen(true); - }); - }); - - #[cfg(any(eth_v1b, eth_v1c))] - critical_section::with(|_| { - RCC.apb2enr().modify(|w| w.set_syscfgen(true)); - RCC.ahb1enr().modify(|w| { - w.set_ethen(true); - w.set_ethtxen(true); - w.set_ethrxen(true); + RCC.ahbenr().modify(|w| { + w.set_ethen(true); + w.set_ethtxen(true); + w.set_ethrxen(true); + }); }); - // RMII (Reduced Media Independent Interface) - SYSCFG.pmc().modify(|w| w.set_mii_rmii_sel(true)); - }); + #[cfg(any(eth_v1b, eth_v1c))] + critical_section::with(|_| { + RCC.apb2enr().modify(|w| w.set_syscfgen(true)); + RCC.ahb1enr().modify(|w| { + w.set_ethen(true); + w.set_ethtxen(true); + w.set_ethrxen(true); + }); - #[cfg(eth_v1a)] - { - config_in_pins!(ref_clk, rx_d0, rx_d1); - config_af_pins!(mdio, mdc, tx_d0, tx_d1, tx_en); - } + // RMII (Reduced Media Independent Interface) + SYSCFG.pmc().modify(|w| w.set_mii_rmii_sel(true)); + }); - #[cfg(any(eth_v1b, eth_v1c))] - config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); - - // NOTE(unsafe) We are ourselves not leak-safe. - let state = PeripheralMutex::new(interrupt, &mut state.0, || Inner::new(peri)); - - // NOTE(unsafe) We have exclusive access to the registers - let dma = ETH.ethernet_dma(); - let mac = ETH.ethernet_mac(); - - // Reset and wait - dma.dmabmr().modify(|w| w.set_sr(true)); - while dma.dmabmr().read().sr() {} - - mac.maccr().modify(|w| { - w.set_ifg(Ifg::IFG96); // inter frame gap 96 bit times - w.set_apcs(Apcs::STRIP); // automatic padding and crc stripping - w.set_fes(Fes::FES100); // fast ethernet speed - w.set_dm(Dm::FULLDUPLEX); // full duplex - // TODO: Carrier sense ? ECRSFD - }); - - // Note: Writing to LR triggers synchronisation of both LR and HR into the MAC core, - // so the LR write must happen after the HR write. - mac.maca0hr() - .modify(|w| w.set_maca0h(u16::from(mac_addr[4]) | (u16::from(mac_addr[5]) << 8))); - mac.maca0lr().write(|w| { - w.set_maca0l( - u32::from(mac_addr[0]) - | (u32::from(mac_addr[1]) << 8) - | (u32::from(mac_addr[2]) << 16) - | (u32::from(mac_addr[3]) << 24), - ) - }); - - // pause time - mac.macfcr().modify(|w| w.set_pt(0x100)); - - // Transfer and Forward, Receive and Forward - dma.dmaomr().modify(|w| { - w.set_tsf(Tsf::STOREFORWARD); - w.set_rsf(Rsf::STOREFORWARD); - }); - - dma.dmabmr().modify(|w| { - w.set_pbl(Pbl::PBL32) // programmable burst length - 32 ? - }); - - // TODO MTU size setting not found for v1 ethernet, check if correct - - // NOTE(unsafe) We got the peripheral singleton, which means that `rcc::init` was called - let hclk = crate::rcc::get_freqs().ahb1; - let hclk_mhz = hclk.0 / 1_000_000; - - // Set the MDC clock frequency in the range 1MHz - 2.5MHz - let clock_range = match hclk_mhz { - 0..=24 => panic!("Invalid HCLK frequency - should be at least 25 MHz."), - 25..=34 => Cr::CR_20_35, // Divide by 16 - 35..=59 => Cr::CR_35_60, // Divide by 26 - 60..=99 => Cr::CR_60_100, // Divide by 42 - 100..=149 => Cr::CR_100_150, // Divide by 62 - 150..=216 => Cr::CR_150_168, // Divide by 102 - _ => { - panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider") + #[cfg(eth_v1a)] + { + config_in_pins!(ref_clk, rx_d0, rx_d1); + config_af_pins!(mdio, mdc, tx_d0, tx_d1, tx_en); } - }; - let pins = [ - ref_clk.map_into(), - mdio.map_into(), - mdc.map_into(), - crs.map_into(), - rx_d0.map_into(), - rx_d1.map_into(), - tx_d0.map_into(), - tx_d1.map_into(), - tx_en.map_into(), - ]; + #[cfg(any(eth_v1b, eth_v1c))] + config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); - let mut this = Self { - state, - pins, - _phy: phy, - clock_range, - phy_addr, - mac_addr, - }; + // NOTE(unsafe) We have exclusive access to the registers + let dma = ETH.ethernet_dma(); + let mac = ETH.ethernet_mac(); - this.state.with(|s| { - s.desc_ring.init(); + // Reset and wait + dma.dmabmr().modify(|w| w.set_sr(true)); + while dma.dmabmr().read().sr() {} + + mac.maccr().modify(|w| { + w.set_ifg(Ifg::IFG96); // inter frame gap 96 bit times + w.set_apcs(Apcs::STRIP); // automatic padding and crc stripping + w.set_fes(Fes::FES100); // fast ethernet speed + w.set_dm(Dm::FULLDUPLEX); // full duplex + // TODO: Carrier sense ? ECRSFD + }); + + // Note: Writing to LR triggers synchronisation of both LR and HR into the MAC core, + // so the LR write must happen after the HR write. + mac.maca0hr() + .modify(|w| w.set_maca0h(u16::from(mac_addr[4]) | (u16::from(mac_addr[5]) << 8))); + mac.maca0lr().write(|w| { + w.set_maca0l( + u32::from(mac_addr[0]) + | (u32::from(mac_addr[1]) << 8) + | (u32::from(mac_addr[2]) << 16) + | (u32::from(mac_addr[3]) << 24), + ) + }); + + // pause time + mac.macfcr().modify(|w| w.set_pt(0x100)); + + // Transfer and Forward, Receive and Forward + dma.dmaomr().modify(|w| { + w.set_tsf(Tsf::STOREFORWARD); + w.set_rsf(Rsf::STOREFORWARD); + }); + + dma.dmabmr().modify(|w| { + w.set_pbl(Pbl::PBL32) // programmable burst length - 32 ? + }); + + // TODO MTU size setting not found for v1 ethernet, check if correct + + // NOTE(unsafe) We got the peripheral singleton, which means that `rcc::init` was called + let hclk = crate::rcc::get_freqs().ahb1; + let hclk_mhz = hclk.0 / 1_000_000; + + // Set the MDC clock frequency in the range 1MHz - 2.5MHz + let clock_range = match hclk_mhz { + 0..=24 => panic!("Invalid HCLK frequency - should be at least 25 MHz."), + 25..=34 => Cr::CR_20_35, // Divide by 16 + 35..=59 => Cr::CR_35_60, // Divide by 26 + 60..=99 => Cr::CR_60_100, // Divide by 42 + 100..=149 => Cr::CR_100_150, // Divide by 62 + 150..=216 => Cr::CR_150_168, // Divide by 102 + _ => { + panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider") + } + }; + + let pins = [ + ref_clk.map_into(), + mdio.map_into(), + mdc.map_into(), + crs.map_into(), + rx_d0.map_into(), + rx_d1.map_into(), + tx_d0.map_into(), + tx_d1.map_into(), + tx_en.map_into(), + ]; + + let mut this = Self { + _peri: peri, + pins, + _phy: phy, + clock_range, + phy_addr, + mac_addr, + tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf), + rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf), + }; fence(Ordering::SeqCst); @@ -245,23 +232,45 @@ impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Ethernet<'d, T, w.set_sr(DmaomrSr::STARTED); // start receiving channel }); + this.rx.demand_poll(); + // Enable interrupts dma.dmaier().modify(|w| { w.set_nise(true); w.set_rie(true); w.set_tie(true); }); - }); - P::phy_reset(&mut this); - P::phy_init(&mut this); - this + P::phy_reset(&mut this); + P::phy_init(&mut this); + + interrupt.set_handler(Self::on_interrupt); + interrupt.enable(); + + this + } + } + + fn on_interrupt(_cx: *mut ()) { + WAKER.wake(); + + // TODO: Check and clear more flags + unsafe { + let dma = ETH.ethernet_dma(); + + dma.dmasr().modify(|w| { + w.set_ts(true); + w.set_rs(true); + w.set_nis(true); + }); + // Delay two peripheral's clock + dma.dmasr().read(); + dma.dmasr().read(); + } } } -unsafe impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> StationManagement - for Ethernet<'d, T, P, TX, RX> -{ +unsafe impl<'d, T: Instance, P: PHY> StationManagement for Ethernet<'d, T, P> { fn smi_read(&mut self, reg: u8) -> u16 { // NOTE(unsafe) These registers aren't used in the interrupt and we have `&mut self` unsafe { @@ -297,44 +306,7 @@ unsafe impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> StationMa } } -impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Device for Ethernet<'d, T, P, TX, RX> { - fn is_transmit_ready(&mut self) -> bool { - self.state.with(|s| s.desc_ring.tx.available()) - } - - fn transmit(&mut self, pkt: PacketBuf) { - self.state.with(|s| unwrap!(s.desc_ring.tx.transmit(pkt))); - } - - fn receive(&mut self) -> Option { - self.state.with(|s| s.desc_ring.rx.pop_packet()) - } - - fn register_waker(&mut self, waker: &Waker) { - WAKER.register(waker); - } - - fn capabilities(&self) -> DeviceCapabilities { - let mut caps = DeviceCapabilities::default(); - caps.max_transmission_unit = MTU; - caps.max_burst_size = Some(TX.min(RX)); - caps - } - - fn link_state(&mut self) -> LinkState { - if P::poll_link(self) { - LinkState::Up - } else { - LinkState::Down - } - } - - fn ethernet_address(&self) -> [u8; 6] { - self.mac_addr - } -} - -impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Drop for Ethernet<'d, T, P, TX, RX> { +impl<'d, T: Instance, P: PHY> Drop for Ethernet<'d, T, P> { fn drop(&mut self) { // NOTE(unsafe) We have `&mut self` and the interrupt doesn't use this registers unsafe { @@ -361,46 +333,3 @@ impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Drop for Etherne }) } } - -//---------------------------------------------------------------------- - -struct Inner<'d, T: Instance, const TX: usize, const RX: usize> { - _peri: PhantomData<&'d mut T>, - desc_ring: DescriptorRing, -} - -impl<'d, T: Instance, const TX: usize, const RX: usize> Inner<'d, T, TX, RX> { - pub fn new(_peri: impl Peripheral

+ 'd) -> Self { - Self { - _peri: PhantomData, - desc_ring: DescriptorRing::new(), - } - } -} - -impl<'d, T: Instance, const TX: usize, const RX: usize> PeripheralState for Inner<'d, T, TX, RX> { - type Interrupt = crate::interrupt::ETH; - - fn on_interrupt(&mut self) { - unwrap!(self.desc_ring.tx.on_interrupt()); - self.desc_ring.rx.on_interrupt(); - - WAKER.wake(); - - // TODO: Check and clear more flags - unsafe { - let dma = ETH.ethernet_dma(); - - dma.dmasr().modify(|w| { - w.set_ts(true); - w.set_rs(true); - w.set_nis(true); - }); - // Delay two peripheral's clock - dma.dmasr().read(); - dma.dmasr().read(); - } - } -} - -static WAKER: AtomicWaker = AtomicWaker::new(); diff --git a/embassy-stm32/src/eth/v1/rx_desc.rs b/embassy-stm32/src/eth/v1/rx_desc.rs index d482590a..8b8566d9 100644 --- a/embassy-stm32/src/eth/v1/rx_desc.rs +++ b/embassy-stm32/src/eth/v1/rx_desc.rs @@ -1,9 +1,9 @@ use core::sync::atomic::{compiler_fence, fence, Ordering}; -use embassy_net::{Packet, PacketBox, PacketBoxExt, PacketBuf}; -use stm32_metapac::eth::vals::{DmaomrSr, Rpd, Rps}; +use stm32_metapac::eth::vals::{Rpd, Rps}; use vcell::VolatileCell; +use crate::eth::RX_BUFFER_SIZE; use crate::pac::ETH; mod rx_consts { @@ -28,6 +28,8 @@ mod rx_consts { use rx_consts::*; +use super::Packet; + /// Receive Descriptor representation /// /// * rdes0: OWN and Status @@ -35,7 +37,7 @@ use rx_consts::*; /// * rdes2: data buffer address /// * rdes3: next descriptor address #[repr(C)] -struct RDes { +pub(crate) struct RDes { rdes0: VolatileCell, rdes1: VolatileCell, rdes2: VolatileCell, @@ -54,7 +56,7 @@ impl RDes { /// Return true if this RDes is acceptable to us #[inline(always)] - pub fn valid(&self) -> bool { + fn valid(&self) -> bool { // Write-back descriptor is valid if: // // Contains first buffer of packet AND contains last buf of @@ -64,15 +66,16 @@ impl RDes { /// Return true if this RDes is not currently owned by the DMA #[inline(always)] - pub fn available(&self) -> bool { + fn available(&self) -> bool { self.rdes0.get() & RXDESC_0_OWN == 0 // Owned by us } /// Configures the reception buffer address and length and passed descriptor ownership to the DMA #[inline(always)] - pub fn set_ready(&mut self, buf_addr: u32, buf_len: usize) { - self.rdes1.set(self.rdes1.get() | (buf_len as u32) & RXDESC_1_RBS_MASK); - self.rdes2.set(buf_addr); + fn set_ready(&self, buf: *mut u8) { + self.rdes1 + .set(self.rdes1.get() | (RX_BUFFER_SIZE as u32) & RXDESC_1_RBS_MASK); + self.rdes2.set(buf as u32); // "Preceding reads and writes cannot be moved past subsequent writes." fence(Ordering::Release); @@ -88,12 +91,12 @@ impl RDes { // points to next descriptor (RCH) #[inline(always)] - fn set_buffer2(&mut self, buffer: *const u8) { + fn set_buffer2(&self, buffer: *const u8) { self.rdes3.set(buffer as u32); } #[inline(always)] - fn set_end_of_ring(&mut self) { + fn set_end_of_ring(&self) { self.rdes1.set(self.rdes1.get() | RXDESC_1_RER); } @@ -102,7 +105,7 @@ impl RDes { ((self.rdes0.get() >> RXDESC_0_FL_SHIFT) & RXDESC_0_FL_MASK) as usize } - pub fn setup(&mut self, next: Option<&Self>) { + fn setup(&self, next: Option<&Self>, buf: *mut u8) { // Defer this initialization to this function, so we can have `RingEntry` on bss. self.rdes1.set(self.rdes1.get() | RXDESC_1_RCH); @@ -113,8 +116,11 @@ impl RDes { self.set_end_of_ring(); } } + + self.set_ready(buf); } } + /// Running state of the `RxRing` #[derive(PartialEq, Eq, Debug)] pub enum RunningState { @@ -123,116 +129,42 @@ pub enum RunningState { Running, } -impl RunningState { - /// whether self equals to `RunningState::Running` - pub fn is_running(&self) -> bool { - *self == RunningState::Running - } -} - /// Rx ring of descriptors and packets -/// -/// This ring has three major locations that work in lock-step. The DMA will never write to the tail -/// index, so the `read_index` must never pass the tail index. The `next_tail_index` is always 1 -/// slot ahead of the real tail index, and it must never pass the `read_index` or it could overwrite -/// a packet still to be passed to the application. -/// -/// nt can't pass r (no alloc) -/// +---+---+---+---+ Read ok +---+---+---+---+ No Read +---+---+---+---+ -/// | | | | | ------------> | | | | | ------------> | | | | | -/// +---+---+---+---+ Allocation ok +---+---+---+---+ +---+---+---+---+ -/// ^ ^t ^t ^ ^t ^ -/// |r |r |r -/// |nt |nt |nt -/// -/// -/// +---+---+---+---+ Read ok +---+---+---+---+ Can't read +---+---+---+---+ -/// | | | | | ------------> | | | | | ------------> | | | | | -/// +---+---+---+---+ Allocation fail +---+---+---+---+ Allocation ok +---+---+---+---+ -/// ^ ^t ^ ^t ^ ^ ^ ^t -/// |r | |r | | |r -/// |nt |nt |nt -/// -pub(crate) struct RDesRing { - descriptors: [RDes; N], - buffers: [Option; N], - read_index: usize, - next_tail_index: usize, +pub(crate) struct RDesRing<'a> { + descriptors: &'a mut [RDes], + buffers: &'a mut [Packet], + index: usize, } -impl RDesRing { - pub const fn new() -> Self { - const RDES: RDes = RDes::new(); - const BUFFERS: Option = None; +impl<'a> RDesRing<'a> { + pub(crate) fn new(descriptors: &'a mut [RDes], buffers: &'a mut [Packet]) -> Self { + assert!(descriptors.len() > 1); + assert!(descriptors.len() == buffers.len()); - Self { - descriptors: [RDES; N], - buffers: [BUFFERS; N], - read_index: 0, - next_tail_index: 0, - } - } - - pub(crate) fn init(&mut self) { - assert!(N > 1); - let mut last_index = 0; - for (index, buf) in self.buffers.iter_mut().enumerate() { - let pkt = match PacketBox::new(Packet::new()) { - Some(p) => p, - None => { - if index == 0 { - panic!("Could not allocate at least one buffer for Ethernet receiving"); - } else { - break; - } - } - }; - self.descriptors[index].set_ready(pkt.as_ptr() as u32, pkt.len()); - *buf = Some(pkt); - last_index = index; - } - self.next_tail_index = (last_index + 1) % N; - - // not sure if this is supposed to span all of the descriptor or just those that contain buffers - { - let mut previous: Option<&mut RDes> = None; - for entry in self.descriptors.iter_mut() { - if let Some(prev) = &mut previous { - prev.setup(Some(entry)); - } - previous = Some(entry); - } - - if let Some(entry) = &mut previous { - entry.setup(None); - } + for (i, entry) in descriptors.iter().enumerate() { + entry.setup(descriptors.get(i + 1), buffers[i].0.as_mut_ptr()); } - // Register txdescriptor start + // Register rx descriptor start // NOTE (unsafe) Used for atomic writes unsafe { ETH.ethernet_dma() .dmardlar() - .write(|w| w.0 = &self.descriptors as *const _ as u32); + .write(|w| w.0 = descriptors.as_ptr() as u32); }; // We already have fences in `set_owned`, which is called in `setup` - // Start receive - unsafe { ETH.ethernet_dma().dmaomr().modify(|w| w.set_sr(DmaomrSr::STARTED)) }; - - self.demand_poll(); + Self { + descriptors, + buffers, + index: 0, + } } - fn demand_poll(&self) { + pub(crate) fn demand_poll(&self) { unsafe { ETH.ethernet_dma().dmarpdr().write(|w| w.set_rpd(Rpd::POLL)) }; } - pub(crate) fn on_interrupt(&mut self) { - // XXX: Do we need to do anything here ? Maybe we should try to advance the tail ptr, but it - // would soon hit the read ptr anyway, and we will wake smoltcp's stack on the interrupt - // which should try to pop a packet... - } - /// Get current `RunningState` fn running_state(&self) -> RunningState { match unsafe { ETH.ethernet_dma().dmasr().read().rps() } { @@ -252,52 +184,52 @@ impl RDesRing { } } - pub(crate) fn pop_packet(&mut self) -> Option { - if !self.running_state().is_running() { + /// Get a received packet if any, or None. + pub(crate) fn available(&mut self) -> Option<&mut [u8]> { + if self.running_state() != RunningState::Running { self.demand_poll(); } + // Not sure if the contents of the write buffer on the M7 can affects reads, so we are using // a DMB here just in case, it also serves as a hint to the compiler that we're syncing the // buffer (I think .-.) fence(Ordering::SeqCst); - let read_available = self.descriptors[self.read_index].available(); - let tail_index = (self.next_tail_index + N - 1) % N; - - let pkt = if read_available && self.read_index != tail_index { - let pkt = self.buffers[self.read_index].take(); - let len = self.descriptors[self.read_index].packet_len(); - - assert!(pkt.is_some()); - let valid = self.descriptors[self.read_index].valid(); - - self.read_index = (self.read_index + 1) % N; - if valid { - pkt.map(|p| p.slice(0..len)) - } else { - None + // We might have to process many packets, in case some have been rx'd but are invalid. + loop { + let descriptor = &mut self.descriptors[self.index]; + if !descriptor.available() { + return None; } - } else { - None - }; - // Try to advance the tail_index - if self.next_tail_index != self.read_index { - match PacketBox::new(Packet::new()) { - Some(b) => { - let addr = b.as_ptr() as u32; - let buffer_len = b.len(); - self.buffers[self.next_tail_index].replace(b); - self.descriptors[self.next_tail_index].set_ready(addr, buffer_len); - - // "Preceding reads and writes cannot be moved past subsequent writes." - fence(Ordering::Release); - - self.next_tail_index = (self.next_tail_index + 1) % N; - } - None => {} + // If packet is invalid, pop it and try again. + if !descriptor.valid() { + warn!("invalid packet: {:08x}", descriptor.rdes0.get()); + self.pop_packet(); + continue; } + + break; + } + + let descriptor = &mut self.descriptors[self.index]; + let len = descriptor.packet_len(); + return Some(&mut self.buffers[self.index].0[..len]); + } + + /// Pop the packet previously returned by `available`. + pub(crate) fn pop_packet(&mut self) { + let descriptor = &mut self.descriptors[self.index]; + assert!(descriptor.available()); + + self.descriptors[self.index].set_ready(self.buffers[self.index].0.as_mut_ptr()); + + self.demand_poll(); + + // Increment index. + self.index += 1; + if self.index == self.descriptors.len() { + self.index = 0 } - pkt } } diff --git a/embassy-stm32/src/eth/v1/tx_desc.rs b/embassy-stm32/src/eth/v1/tx_desc.rs index f2889b55..0e63c544 100644 --- a/embassy-stm32/src/eth/v1/tx_desc.rs +++ b/embassy-stm32/src/eth/v1/tx_desc.rs @@ -1,20 +1,10 @@ use core::sync::atomic::{compiler_fence, fence, Ordering}; -use embassy_net::PacketBuf; -use stm32_metapac::eth::vals::St; use vcell::VolatileCell; +use crate::eth::TX_BUFFER_SIZE; use crate::pac::ETH; -#[non_exhaustive] -#[derive(Debug, Copy, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Error { - NoBufferAvailable, - // TODO: Break down this error into several others - TransmissionError, -} - /// Transmit and Receive Descriptor fields #[allow(dead_code)] mod tx_consts { @@ -37,6 +27,8 @@ mod tx_consts { } use tx_consts::*; +use super::Packet; + /// Transmit Descriptor representation /// /// * tdes0: control @@ -44,7 +36,7 @@ use tx_consts::*; /// * tdes2: data buffer address /// * tdes3: next descriptor address #[repr(C)] -struct TDes { +pub(crate) struct TDes { tdes0: VolatileCell, tdes1: VolatileCell, tdes2: VolatileCell, @@ -62,7 +54,7 @@ impl TDes { } /// Return true if this TDes is not currently owned by the DMA - pub fn available(&self) -> bool { + fn available(&self) -> bool { (self.tdes0.get() & TXDESC_0_OWN) == 0 } @@ -79,26 +71,26 @@ impl TDes { fence(Ordering::SeqCst); } - fn set_buffer1(&mut self, buffer: *const u8) { + fn set_buffer1(&self, buffer: *const u8) { self.tdes2.set(buffer as u32); } - fn set_buffer1_len(&mut self, len: usize) { + fn set_buffer1_len(&self, len: usize) { self.tdes1 .set((self.tdes1.get() & !TXDESC_1_TBS_MASK) | ((len as u32) << TXDESC_1_TBS_SHIFT)); } // points to next descriptor (RCH) - fn set_buffer2(&mut self, buffer: *const u8) { + fn set_buffer2(&self, buffer: *const u8) { self.tdes3.set(buffer as u32); } - fn set_end_of_ring(&mut self) { + fn set_end_of_ring(&self) { self.tdes0.set(self.tdes0.get() | TXDESC_0_TER); } // set up as a part fo the ring buffer - configures the tdes - pub fn setup(&mut self, next: Option<&Self>) { + fn setup(&self, next: Option<&Self>) { // Defer this initialization to this function, so we can have `RingEntry` on bss. self.tdes0.set(TXDESC_0_TCH | TXDESC_0_IOC | TXDESC_0_FS | TXDESC_0_LS); match next { @@ -111,85 +103,58 @@ impl TDes { } } -pub(crate) struct TDesRing { - descriptors: [TDes; N], - buffers: [Option; N], - next_entry: usize, +pub(crate) struct TDesRing<'a> { + descriptors: &'a mut [TDes], + buffers: &'a mut [Packet], + index: usize, } -impl TDesRing { - pub const fn new() -> Self { - const TDES: TDes = TDes::new(); - const BUFFERS: Option = None; - - Self { - descriptors: [TDES; N], - buffers: [BUFFERS; N], - next_entry: 0, - } - } - +impl<'a> TDesRing<'a> { /// Initialise this TDesRing. Assume TDesRing is corrupt - /// - /// The current memory address of the buffers inside this TDesRing - /// will be stored in the descriptors, so ensure the TDesRing is - /// not moved after initialisation. - pub(crate) fn init(&mut self) { - assert!(N > 0); + pub(crate) fn new(descriptors: &'a mut [TDes], buffers: &'a mut [Packet]) -> Self { + assert!(descriptors.len() > 0); + assert!(descriptors.len() == buffers.len()); - { - let mut previous: Option<&mut TDes> = None; - for entry in self.descriptors.iter_mut() { - if let Some(prev) = &mut previous { - prev.setup(Some(entry)); - } - previous = Some(entry); - } - - if let Some(entry) = &mut previous { - entry.setup(None); - } + for (i, entry) in descriptors.iter().enumerate() { + entry.setup(descriptors.get(i + 1)); } - self.next_entry = 0; // Register txdescriptor start // NOTE (unsafe) Used for atomic writes unsafe { ETH.ethernet_dma() .dmatdlar() - .write(|w| w.0 = &self.descriptors as *const _ as u32); + .write(|w| w.0 = descriptors.as_ptr() as u32); } - // "Preceding reads and writes cannot be moved past subsequent writes." - #[cfg(feature = "fence")] - fence(Ordering::Release); - - // We don't need a compiler fence here because all interactions with `Descriptor` are - // volatiles - - // Start transmission - unsafe { ETH.ethernet_dma().dmaomr().modify(|w| w.set_st(St::STARTED)) }; - } - - /// Return true if a TDes is available for use - pub(crate) fn available(&self) -> bool { - self.descriptors[self.next_entry].available() - } - - pub(crate) fn transmit(&mut self, pkt: PacketBuf) -> Result<(), Error> { - if !self.available() { - return Err(Error::NoBufferAvailable); + Self { + descriptors, + buffers, + index: 0, } + } - let descriptor = &mut self.descriptors[self.next_entry]; + pub(crate) fn len(&self) -> usize { + self.descriptors.len() + } - let pkt_len = pkt.len(); - let address = pkt.as_ptr() as *const u8; + /// Return the next available packet buffer for transmitting, or None + pub(crate) fn available(&mut self) -> Option<&mut [u8]> { + let descriptor = &mut self.descriptors[self.index]; + if descriptor.available() { + Some(&mut self.buffers[self.index].0) + } else { + None + } + } - descriptor.set_buffer1(address); - descriptor.set_buffer1_len(pkt_len); + /// Transmit the packet written in a buffer returned by `available`. + pub(crate) fn transmit(&mut self, len: usize) { + let descriptor = &mut self.descriptors[self.index]; + assert!(descriptor.available()); - self.buffers[self.next_entry].replace(pkt); + descriptor.set_buffer1(self.buffers[self.index].0.as_ptr()); + descriptor.set_buffer1_len(len); descriptor.set_owned(); @@ -198,36 +163,12 @@ impl TDesRing { // "Preceding reads and writes cannot be moved past subsequent writes." fence(Ordering::Release); - // Move the tail pointer (TPR) to the next descriptor - self.next_entry = (self.next_entry + 1) % N; - + // Move the index to the next descriptor + self.index += 1; + if self.index == self.descriptors.len() { + self.index = 0 + } // Request the DMA engine to poll the latest tx descriptor unsafe { ETH.ethernet_dma().dmatpdr().modify(|w| w.0 = 1) } - Ok(()) - } - - pub(crate) fn on_interrupt(&mut self) -> Result<(), Error> { - let previous = (self.next_entry + N - 1) % N; - let td = &self.descriptors[previous]; - - // DMB to ensure that we are reading an updated value, probably not needed at the hardware - // level, but this is also a hint to the compiler that we're syncing on the buffer. - fence(Ordering::SeqCst); - - let tdes0 = td.tdes0.get(); - - if tdes0 & TXDESC_0_OWN != 0 { - // Transmission isn't done yet, probably a receive interrupt that fired this - return Ok(()); - } - - // Release the buffer - self.buffers[previous].take(); - - if tdes0 & TXDESC_0_ES != 0 { - Err(Error::TransmissionError) - } else { - Ok(()) - } } } diff --git a/examples/stm32f7/src/bin/eth.rs b/examples/stm32f7/src/bin/eth.rs index 5202edf6..224cc202 100644 --- a/examples/stm32f7/src/bin/eth.rs +++ b/examples/stm32f7/src/bin/eth.rs @@ -7,7 +7,7 @@ use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; use embassy_net::{Ipv4Address, Stack, StackResources}; use embassy_stm32::eth::generic_smi::GenericSMI; -use embassy_stm32::eth::{Ethernet, State}; +use embassy_stm32::eth::{Ethernet, PacketQueue}; use embassy_stm32::peripherals::ETH; use embassy_stm32::rng::Rng; use embassy_stm32::time::mhz; @@ -22,11 +22,12 @@ macro_rules! singleton { ($val:expr) => {{ type T = impl Sized; static STATIC_CELL: StaticCell = StaticCell::new(); - STATIC_CELL.init_with(move || $val) + let (x,) = STATIC_CELL.init(($val,)); + x }}; } -type Device = Ethernet<'static, ETH, GenericSMI, 4, 4>; +type Device = Ethernet<'static, ETH, GenericSMI>; #[embassy_executor::task] async fn net_task(stack: &'static Stack) -> ! { @@ -50,25 +51,23 @@ async fn main(spawner: Spawner) -> ! { let eth_int = interrupt::take!(ETH); let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; - let device = unsafe { - Ethernet::new( - singleton!(State::new()), - p.ETH, - eth_int, - p.PA1, - p.PA2, - p.PC1, - p.PA7, - p.PC4, - p.PC5, - p.PG13, - p.PB13, - p.PG11, - GenericSMI, - mac_addr, - 0, - ) - }; + let device = Ethernet::new( + singleton!(PacketQueue::<16, 16>::new()), + p.ETH, + eth_int, + p.PA1, + p.PA2, + p.PC1, + p.PA7, + p.PC4, + p.PC5, + p.PG13, + p.PB13, + p.PG11, + GenericSMI, + mac_addr, + 0, + ); let config = embassy_net::ConfigStrategy::Dhcp; //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { @@ -91,8 +90,8 @@ async fn main(spawner: Spawner) -> ! { info!("Network task initialized"); // Then we can use it! - let mut rx_buffer = [0; 1024]; - let mut tx_buffer = [0; 1024]; + let mut rx_buffer = [0; 4096]; + let mut tx_buffer = [0; 4096]; loop { let mut socket = TcpSocket::new(&stack, &mut rx_buffer, &mut tx_buffer); @@ -107,8 +106,9 @@ async fn main(spawner: Spawner) -> ! { continue; } info!("connected!"); + let buf = [0; 1024]; loop { - let r = socket.write_all(b"Hello\n").await; + let r = socket.write_all(&buf).await; if let Err(e) = r { info!("write error: {:?}", e); return;