From 3d577bfbdd57bc56148c9f20214228aa92682e7a Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Wed, 1 Feb 2023 07:37:06 +0100 Subject: [PATCH 01/35] Convert to ring mode (instead of chain mode) --- Cargo.toml | 1 + examples/arp.rs | 45 ++-- examples/rtic-echo.rs | 12 +- examples/rtic-timestamp.rs | 14 +- src/dma/desc.rs | 62 ------ src/dma/mod.rs | 42 +++- src/dma/packet_id.rs | 2 +- src/dma/raw_descriptor.rs | 100 +++++++++ src/dma/ring.rs | 87 -------- src/dma/rx.rs | 445 ------------------------------------- src/dma/rx/descriptor.rs | 207 +++++++++++++++++ src/dma/rx/mod.rs | 201 +++++++++++++++++ src/dma/tx.rs | 441 ------------------------------------ src/dma/tx/descriptor.rs | 190 ++++++++++++++++ src/dma/tx/mod.rs | 202 +++++++++++++++++ src/lib.rs | 13 +- src/mac/mod.rs | 2 + src/ptp/timestamp.rs | 4 +- 18 files changed, 992 insertions(+), 1078 deletions(-) delete mode 100644 src/dma/desc.rs create mode 100644 src/dma/raw_descriptor.rs delete mode 100644 src/dma/ring.rs delete mode 100644 src/dma/rx.rs create mode 100644 src/dma/rx/descriptor.rs create mode 100644 src/dma/rx/mod.rs delete mode 100644 src/dma/tx.rs create mode 100644 src/dma/tx/descriptor.rs create mode 100644 src/dma/tx/mod.rs diff --git a/Cargo.toml b/Cargo.toml index b827280..3ca9e23 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,7 @@ ieee802_3_miim = "0.8" cortex-m = "0.7" log = { version = "0.4", optional = true } defmt = { version = "0.3", optional = true } +siphasher = "*" [dependencies.smoltcp] version = "0.8.2" diff --git a/examples/arp.rs b/examples/arp.rs index 1406a28..8d9c973 100644 --- a/examples/arp.rs +++ b/examples/arp.rs @@ -16,14 +16,15 @@ use cortex_m_rt::{entry, exception}; use cortex_m::interrupt::Mutex; use stm32_eth::{ + dma::{RxDescriptor, TxDescriptor}, mac::{phy::BarePhy, Phy}, stm32::{interrupt, CorePeripherals, Peripherals, SYST}, - Parts, + Parts, MTU, }; pub mod common; -use stm32_eth::dma::{RxRingEntry, TxError, TxRingEntry}; +use stm32_eth::dma::{RxDescriptorRing, TxDescriptorRing, TxError}; const PHY_ADDR: u8 = 0; @@ -43,22 +44,20 @@ fn main() -> ! { let (eth_pins, mdio, mdc, _) = common::setup_pins(gpio); - let mut rx_ring: [RxRingEntry; 2] = Default::default(); - let mut tx_ring: [TxRingEntry; 2] = Default::default(); + let mut rx_descriptors: [RxDescriptor; 2] = Default::default(); + let mut rx_buffers: [[u8; MTU]; 2] = [[0u8; MTU]; 2]; + let rx_ring = RxDescriptorRing::new(&mut rx_descriptors, &mut rx_buffers); + + let mut tx_descriptors: [TxDescriptor; 2] = Default::default(); + let mut tx_buffers: [[u8; MTU]; 2] = [[0u8; MTU]; 2]; + let tx_ring = TxDescriptorRing::new(&mut tx_descriptors, &mut tx_buffers); let Parts { mut dma, mac, #[cfg(feature = "ptp")] ptp: _, - } = stm32_eth::new( - ethernet, - &mut rx_ring[..], - &mut tx_ring[..], - clocks, - eth_pins, - ) - .unwrap(); + } = stm32_eth::new(ethernet, rx_ring, tx_ring, clocks, eth_pins).unwrap(); dma.enable_interrupt(); let mut last_link_up = false; @@ -109,14 +108,32 @@ fn main() -> ! { buf[38..42].copy_from_slice(&TARGET_IP); }); + loop { + use core::hash::{Hash, Hasher}; + + if let Ok(rx_packet) = dma.recv_next(None) { + let mut hasher = siphasher::sip::SipHasher::new(); + rx_packet.hash(&mut hasher); + + defmt::info!( + "Received {} bytes. Hash: {:016X}", + rx_packet.len(), + hasher.finish() + ); + break; + } + } + match r { Ok(()) => { defmt::info!("ARP sent"); } - Err(TxError::WouldBlock) => defmt::info!("ARP failed"), + Err(TxError::WouldBlock) => { + defmt::panic!("ARP failed. {}", dma.tx_state()) + } } } else { - defmt::info!("Down"); + // defmt::info!("Down"); } cortex_m::interrupt::free(|cs| { diff --git a/examples/rtic-echo.rs b/examples/rtic-echo.rs index 88d727d..ea836c2 100644 --- a/examples/rtic-echo.rs +++ b/examples/rtic-echo.rs @@ -29,7 +29,7 @@ mod app { use systick_monotonic::Systick; use stm32_eth::{ - dma::{EthernetDMA, RxRingEntry, TxRingEntry}, + dma::{EthernetDMA, RxDescriptor, RxDescriptorRing, TxDescriptor, TxDescriptorRing}, mac::Speed, Parts, }; @@ -61,8 +61,10 @@ mod app { } #[init(local = [ - rx_ring: [RxRingEntry; 2] = [RxRingEntry::new(),RxRingEntry::new()], - tx_ring: [TxRingEntry; 2] = [TxRingEntry::new(),TxRingEntry::new()], + rx_desc: [RxDescriptor; 2] = [RxDescriptor::new(); 2], + tx_desc: [TxDescriptor; 2] = [TxDescriptor::new(); 2], + rx_buffers: [[u8; 1522]; 2] = [[0u8; stm32_eth::MTU]; 2], + tx_buffers: [[u8; 1522]; 2] = [[0u8; stm32_eth::MTU]; 2], storage: NetworkStorage = NetworkStorage::new(), dma: core::mem::MaybeUninit> = core::mem::MaybeUninit::uninit(), ])] @@ -71,8 +73,8 @@ mod app { let core = cx.core; let p = cx.device; - let rx_ring = cx.local.rx_ring; - let tx_ring = cx.local.tx_ring; + let rx_ring = RxDescriptorRing::new(cx.local.rx_desc, cx.local.rx_buffers); + let tx_ring = TxDescriptorRing::new(cx.local.tx_desc, cx.local.tx_buffers); let (clocks, gpio, ethernet) = crate::common::setup_peripherals(p); let mono = Systick::new(core.SYST, clocks.hclk().raw()); diff --git a/examples/rtic-timestamp.rs b/examples/rtic-timestamp.rs index 4bdfa67..b414776 100644 --- a/examples/rtic-timestamp.rs +++ b/examples/rtic-timestamp.rs @@ -43,7 +43,9 @@ mod app { use systick_monotonic::Systick; use stm32_eth::{ - dma::{EthernetDMA, PacketId, RxRingEntry, TxRingEntry}, + dma::{ + EthernetDMA, PacketId, RxDescriptor, RxDescriptorRing, TxDescriptor, TxDescriptorRing, + }, mac::Speed, ptp::{EthernetPTP, Timestamp}, Parts, @@ -64,16 +66,18 @@ mod app { type Monotonic = Systick<1000>; #[init(local = [ - rx_ring: [RxRingEntry; 2] = [RxRingEntry::new(),RxRingEntry::new()], - tx_ring: [TxRingEntry; 2] = [TxRingEntry::new(),TxRingEntry::new()], + rx_desc: [RxDescriptor; 2] = [RxDescriptor::new(); 2], + tx_desc: [TxDescriptor; 2] = [TxDescriptor::new(); 2], + rx_buffers: [[u8; 1522]; 2] = [[0u8; stm32_eth::MTU]; 2], + tx_buffers: [[u8; 1522]; 2] = [[0u8; stm32_eth::MTU]; 2], ])] fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { defmt::info!("Pre-init"); let core = cx.core; let p = cx.device; - let rx_ring = cx.local.rx_ring; - let tx_ring = cx.local.tx_ring; + let rx_ring = RxDescriptorRing::new(cx.local.rx_desc, cx.local.rx_buffers); + let tx_ring = TxDescriptorRing::new(cx.local.tx_desc, cx.local.tx_buffers); let (clocks, gpio, ethernet) = crate::common::setup_peripherals(p); let mono = Systick::new(core.SYST, clocks.hclk().raw()); diff --git a/src/dma/desc.rs b/src/dma/desc.rs deleted file mode 100644 index dbd9aad..0000000 --- a/src/dma/desc.rs +++ /dev/null @@ -1,62 +0,0 @@ -use core::ops::{Deref, DerefMut}; - -use aligned::{Aligned, A8}; -use volatile_register::{RO, RW}; - -#[cfg(not(feature = "stm32f1xx-hal"))] -const DESC_SIZE: usize = 8; - -#[cfg(feature = "stm32f1xx-hal")] -const DESC_SIZE: usize = 4; - -#[repr(C)] -pub struct Descriptor { - pub(crate) desc: Aligned, -} - -impl Clone for Descriptor { - fn clone(&self) -> Self { - Descriptor { - desc: Aligned(*self.desc), - } - } -} - -impl Default for Descriptor { - fn default() -> Self { - Self::new() - } -} - -impl Descriptor { - pub const fn new() -> Self { - Self { - desc: Aligned([0; DESC_SIZE]), - } - } - - fn r(&self, n: usize) -> &RO { - let ro = &self.desc.deref()[n] as *const _ as *const RO; - unsafe { &*ro } - } - - unsafe fn rw(&mut self, n: usize) -> &mut RW { - let rw = &mut self.desc.deref_mut()[n] as *mut _ as *mut RW; - &mut *rw - } - - pub fn read(&self, n: usize) -> u32 { - self.r(n).read() - } - - pub unsafe fn write(&mut self, n: usize, value: u32) { - self.rw(n).write(value) - } - - pub unsafe fn modify(&mut self, n: usize, f: F) - where - F: FnOnce(u32) -> u32, - { - self.rw(n).modify(f) - } -} diff --git a/src/dma/mod.rs b/src/dma/mod.rs index fec3693..fb5a711 100644 --- a/src/dma/mod.rs +++ b/src/dma/mod.rs @@ -11,17 +11,13 @@ mod smoltcp_phy; #[cfg(feature = "smoltcp-phy")] pub use smoltcp_phy::*; -pub(crate) mod desc; - -pub(crate) mod ring; +pub(crate) mod raw_descriptor; mod rx; -use rx::RxRing; -pub use rx::{RxError, RxPacket, RxRingEntry}; +pub use rx::{RxDescriptor, RxDescriptorRing, RxError, RxPacket}; mod tx; -use tx::TxRing; -pub use tx::{TxError, TxRingEntry}; +pub use tx::{TxDescriptor, TxDescriptorRing, TxError}; #[cfg(feature = "ptp")] use crate::ptp::Timestamp; @@ -29,8 +25,26 @@ use crate::ptp::Timestamp; mod packet_id; pub use packet_id::PacketId; +use rx::RxRing; +use tx::{RunningState, TxRing}; + +use self::raw_descriptor::DESC_SIZE; + +const _RXDESC_SIZE: usize = core::mem::size_of::(); +const _TXDESC_SIZE: usize = core::mem::size_of::(); + +/// Assert that our descriptors have the same size. +/// +/// This is necessary as we only have a single Descriptor Skip Length +/// value which applies to both TX and RX descriptors. +const _ASSERT_DESCRIPTOR_SIZES: () = assert!(_RXDESC_SIZE == _TXDESC_SIZE); + +const DESC_WORD_SKIP: u8 = (core::mem::size_of::() / 4 - DESC_SIZE) as u8; + +/// The maximum transmission unit of this Ethernet peripheral. +/// /// From the datasheet: *VLAN Frame maxsize = 1522* -pub(crate) const MTU: usize = 1522; +pub const MTU: usize = 1522; /// An error that can occur when retrieving a timestamp from an /// RX or TX descriptor handled by the DMA. @@ -60,8 +74,8 @@ impl<'rx, 'tx> EthernetDMA<'rx, 'tx> { /// usually not accessible. pub(crate) fn new( eth_dma: ETHERNET_DMA, - rx_buffer: &'rx mut [RxRingEntry], - tx_buffer: &'tx mut [TxRingEntry], + rx_buffer: RxDescriptorRing<'rx>, + tx_buffer: TxDescriptorRing<'tx>, ) -> Self { // reset DMA bus mode register eth_dma.dmabmr.modify(|_, w| w.sr().set_bit()); @@ -120,6 +134,9 @@ impl<'rx, 'tx> EthernetDMA<'rx, 'tx> { } }); + // Configure word skip length. + eth_dma.dmabmr.modify(|_, w| w.dsl().bits(DESC_WORD_SKIP)); + let mut dma = EthernetDMA { eth_dma, rx_ring: RxRing::new(rx_buffer), @@ -179,6 +196,11 @@ impl<'rx, 'tx> EthernetDMA<'rx, 'tx> { self.rx_ring.running_state(&self.eth_dma).is_running() } + /// + pub fn tx_state(&self) -> RunningState { + self.tx_ring.running_state(&self.eth_dma) + } + pub(crate) fn recv_next_impl<'rx_borrow>( eth_dma: ÐERNET_DMA, rx_ring: &'rx_borrow mut RxRing, diff --git a/src/dma/packet_id.rs b/src/dma/packet_id.rs index a842364..4b54e74 100644 --- a/src/dma/packet_id.rs +++ b/src/dma/packet_id.rs @@ -10,7 +10,7 @@ The main use is obtaining timestamps for frames using [`EthernetDMA::get_timesta " )] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Copy)] pub struct PacketId(pub u32); impl PacketId { diff --git a/src/dma/raw_descriptor.rs b/src/dma/raw_descriptor.rs new file mode 100644 index 0000000..b659230 --- /dev/null +++ b/src/dma/raw_descriptor.rs @@ -0,0 +1,100 @@ +use volatile_register::{RO, RW}; + +use crate::MTU; + +#[cfg(not(feature = "stm32f1xx-hal"))] +pub(crate) const DESC_SIZE: usize = 8; + +#[cfg(feature = "stm32f1xx-hal")] +pub(crate) const DESC_SIZE: usize = 4; + +#[repr(C)] +#[repr(align(8))] +#[derive(Clone, Copy)] +pub struct RawDescriptor { + pub(crate) desc: [u32; DESC_SIZE], +} + +impl Default for RawDescriptor { + fn default() -> Self { + Self::new() + } +} + +impl RawDescriptor { + pub const fn new() -> Self { + Self { + desc: [0; DESC_SIZE], + } + } + + fn r(&self, n: usize) -> &RO { + let ro = &self.desc[n] as *const _ as *const RO; + unsafe { &*ro } + } + + unsafe fn rw(&mut self, n: usize) -> &mut RW { + let rw = &mut self.desc[n] as *mut _ as *mut RW; + &mut *rw + } + + pub fn read(&self, n: usize) -> u32 { + self.r(n).read() + } + + pub unsafe fn write(&mut self, n: usize, value: u32) { + self.rw(n).write(value) + } + + pub unsafe fn modify(&mut self, n: usize, f: F) + where + F: FnOnce(u32) -> u32, + { + self.rw(n).modify(f) + } +} + +pub struct DescriptorRing<'data, T> { + descriptors: &'data mut [T], + buffers: &'data mut [[u8; MTU]], +} + +pub trait DescriptorRingEntry { + /// Set up this [`Descriptor`] with the given buffer. + fn setup(&mut self, buffer: &mut [u8]); +} + +impl<'data, T> DescriptorRing<'data, T> +where + T: DescriptorRingEntry, +{ + pub fn new(descriptors: &'data mut [T], buffers: &'data mut [[u8; MTU]]) -> Self { + assert!(descriptors.len() == buffers.len()); + buffers.iter().for_each(|b| assert!(b.len() <= super::MTU)); + + Self { + descriptors, + buffers, + } + } + + pub fn len(&self) -> usize { + self.descriptors.len() + } + + pub fn get(&mut self, index: usize) -> (&mut T, &mut [u8]) { + (&mut self.descriptors[index], &mut self.buffers[index]) + } + + pub fn descriptors(&mut self) -> impl Iterator { + self.descriptors.iter_mut() + } + + pub fn descriptors_and_buffers(&mut self) -> impl Iterator { + self.descriptors.iter_mut().zip(self.buffers.iter_mut()) + } + + pub fn descriptors_start_address(&self) -> *const T { + self.descriptors.as_ptr() + } +} diff --git a/src/dma/ring.rs b/src/dma/ring.rs deleted file mode 100644 index ca9aae8..0000000 --- a/src/dma/ring.rs +++ /dev/null @@ -1,87 +0,0 @@ -use aligned::{Aligned, A8}; -use core::ops::{Deref, DerefMut}; - -use super::{rx::RxDescriptor, tx::TxDescriptor, MTU}; - -pub trait RingDescriptor { - fn setup(&mut self, buffer: *const u8, len: usize, next: Option<&Self>); -} - -/// An entry in a DMA Descriptor ring -pub struct RingEntry { - desc: Aligned, - buffer: Aligned, -} - -impl Clone for RingEntry { - fn clone(&self) -> Self { - RingEntry { - desc: Aligned((*self.desc).clone()), - buffer: Aligned(*self.buffer), - } - } -} - -impl Default for RingEntry { - fn default() -> Self { - RingEntry { - desc: Aligned([T::default()]), - buffer: Aligned([0; MTU]), - } - } -} - -impl RingEntry { - /// The initial value of a TxRingDescriptor - pub const INIT: Self = Self::new(); - - /// Creates a RingEntry with a TxDescriptor. - pub const fn new() -> Self { - RingEntry { - desc: Aligned([TxDescriptor::new()]), - buffer: Aligned([0; MTU]), - } - } -} - -impl RingEntry { - /// The initial value of an RxRingDescriptor - pub const INIT: Self = Self::new(); - - /// Creates a RingEntry with a RxDescriptor. - pub const fn new() -> Self { - RingEntry { - desc: Aligned([RxDescriptor::new()]), - buffer: Aligned([0; MTU]), - } - } -} - -impl RingEntry { - pub(crate) fn setup(&mut self, next: Option<&Self>) { - let buffer = self.buffer.as_ptr(); - let len = self.buffer.len(); - self.desc_mut() - .setup(buffer, len, next.map(|next| next.desc())); - } - - #[inline] - pub(crate) fn desc(&self) -> &T { - &self.desc.deref()[0] - } - - #[inline] - pub(crate) fn desc_mut(&mut self) -> &mut T { - &mut self.desc.deref_mut()[0] - } - - #[inline] - pub(crate) fn as_slice(&self) -> &[u8] { - &(*self.buffer)[..] - } - - #[inline] - pub(crate) fn as_mut_slice(&mut self) -> &mut [u8] { - &mut (*self.buffer)[..] - } -} diff --git a/src/dma/rx.rs b/src/dma/rx.rs deleted file mode 100644 index a47349e..0000000 --- a/src/dma/rx.rs +++ /dev/null @@ -1,445 +0,0 @@ -use super::PacketId; -use crate::peripherals::ETHERNET_DMA; - -#[cfg(feature = "ptp")] -use crate::{dma::TimestampError, ptp::Timestamp}; - -use core::{ - ops::{Deref, DerefMut}, - sync::atomic::{self, Ordering}, -}; - -use super::{ - desc::Descriptor, - ring::{RingDescriptor, RingEntry}, -}; - -/// Errors that can occur during RX -#[derive(Debug, PartialEq)] -pub enum RxError { - /// Receiving would block - WouldBlock, - /// The received packet was truncated - Truncated, - /// An error occured with the DMA - DmaError, -} - -/// Owned by DMA engine -const RXDESC_0_OWN: u32 = 1 << 31; -/// First descriptor -const RXDESC_0_FS: u32 = 1 << 9; -/// Last descriptor -const RXDESC_0_LS: u32 = 1 << 8; -/// Error summary -const RXDESC_0_ES: u32 = 1 << 15; -/// Frame length -const RXDESC_0_FL_MASK: u32 = 0x3FFF; -const RXDESC_0_FL_SHIFT: usize = 16; - -const RXDESC_1_RBS_SHIFT: usize = 0; -const RXDESC_1_RBS_MASK: u32 = 0x0fff << RXDESC_1_RBS_SHIFT; -/// Second address chained -const RXDESC_1_RCH: u32 = 1 << 14; -/// End Of Ring -const RXDESC_1_RER: u32 = 1 << 15; - -#[repr(C)] -#[derive(Clone)] -/// An RX DMA Descriptor -pub struct RxDescriptor { - desc: Descriptor, - buffer_address: Option, - next_descriptor: Option, - #[cfg(feature = "ptp")] - timestamp_info: Option<(PacketId, Timestamp)>, -} - -impl Default for RxDescriptor { - fn default() -> Self { - Self::new() - } -} - -impl RxDescriptor { - /// Creates an zeroed RxDescriptor. - pub const fn new() -> Self { - Self { - desc: Descriptor::new(), - buffer_address: None, - next_descriptor: None, - #[cfg(feature = "ptp")] - timestamp_info: None, - } - } - - /// Is owned by the DMA engine? - fn is_owned(&self) -> bool { - (self.desc.read(0) & RXDESC_0_OWN) == RXDESC_0_OWN - } - - /// Pass ownership to the DMA engine - /// - /// Overrides old timestamp data - fn set_owned(&mut self) { - self.write_buffer1(); - self.write_buffer2(); - - // "Preceding reads and writes cannot be moved past subsequent writes." - #[cfg(feature = "fence")] - atomic::fence(Ordering::Release); - atomic::compiler_fence(Ordering::Release); - - unsafe { - self.desc.modify(0, |w| w | RXDESC_0_OWN); - } - - // Used to flush the store buffer as fast as possible to make the buffer available for the - // DMA. - #[cfg(feature = "fence")] - atomic::fence(Ordering::SeqCst); - } - - fn has_error(&self) -> bool { - (self.desc.read(0) & RXDESC_0_ES) == RXDESC_0_ES - } - - /// Descriptor contains first buffer of frame - fn is_first(&self) -> bool { - (self.desc.read(0) & RXDESC_0_FS) == RXDESC_0_FS - } - - /// Descriptor contains last buffers of frame - fn is_last(&self) -> bool { - (self.desc.read(0) & RXDESC_0_LS) == RXDESC_0_LS - } - - /// Get PTP timestamps if available - #[cfg(feature = "ptp")] - pub fn timestamp(&self) -> Option { - #[cfg(not(feature = "stm32f1xx-hal"))] - let is_valid = { - /// RX timestamp - const RXDESC_0_TIMESTAMP_VALID: u32 = 1 << 7; - self.desc.read(0) & RXDESC_0_TIMESTAMP_VALID == RXDESC_0_TIMESTAMP_VALID - }; - - #[cfg(feature = "stm32f1xx-hal")] - // There is no "timestamp valid" indicator bit - // on STM32F1XX - let is_valid = true; - - let timestamp = Timestamp::from_descriptor(&self.desc); - - if is_valid && self.is_last() { - timestamp - } else { - None - } - } - - /// Rewrite buffer1 to the last value we wrote to it - /// - /// In our case, the address of the data buffer for this descriptor - /// - /// This only has to be done on stm32f107. For f4 and f7, enhanced descriptors - /// must be enabled for timestamping support, which we enable by default. - fn write_buffer1(&mut self) { - let buffer_addr = self - .buffer_address - .expect("Writing buffer1 of an RX descriptor, but `buffer_address` is None"); - - unsafe { - self.desc.write(2, buffer_addr); - } - } - - fn set_buffer1(&mut self, buffer: *const u8, len: usize) { - self.buffer_address = Some(buffer as u32); - self.write_buffer1(); - unsafe { - self.desc.modify(1, |w| { - (w & !RXDESC_1_RBS_MASK) | ((len as u32) << RXDESC_1_RBS_SHIFT) - }); - } - } - - /// Rewrite buffer2 to the last value we wrote it to - /// - /// In our case, the address of the next descriptor (may be zero) - /// - /// This only has to be done on stm32f107. For f4 and f7, enhanced descriptors - /// must be enabled for timestamping support, which we enable by default. - fn write_buffer2(&mut self) { - let addr = self - .next_descriptor - .expect("Writing buffer2 of an RX descriptor, but `next_descriptor` is None"); - - unsafe { - self.desc.write(3, addr); - } - } - - // points to next descriptor (RCH) - fn set_buffer2(&mut self, buffer: *const u8) { - self.next_descriptor = Some(buffer as u32); - self.write_buffer2(); - } - - fn set_end_of_ring(&mut self) { - unsafe { - self.desc.modify(1, |w| w | RXDESC_1_RER); - } - } - - fn get_frame_len(&self) -> usize { - ((self.desc.read(0) >> RXDESC_0_FL_SHIFT) & RXDESC_0_FL_MASK) as usize - } -} - -/// An RX DMA Ring Descriptor entry -pub type RxRingEntry = RingEntry; - -impl RingDescriptor for RxDescriptor { - fn setup(&mut self, buffer: *const u8, len: usize, next: Option<&Self>) { - // Defer this initialization to this function, so we can have `RingEntry` on bss. - unsafe { - self.desc.write(1, RXDESC_1_RCH); - } - self.set_buffer1(buffer, len); - match next { - Some(next) => self.set_buffer2(&next.desc as *const Descriptor as *const u8), - None => { - #[allow(clippy::zero_ptr)] - self.set_buffer2(0 as *const u8); - self.set_end_of_ring(); - } - }; - self.set_owned(); - } -} - -impl RxRingEntry { - /// The initial value for an Rx Ring Entry - pub const RX_INIT: Self = Self::new(); - - fn take_received(&mut self) -> Result { - if self.desc().is_owned() { - Err(RxError::WouldBlock) - } else if self.desc().has_error() { - self.desc_mut().set_owned(); - Err(RxError::DmaError) - } else if self.desc().is_first() && self.desc().is_last() { - let frame_len = self.desc().get_frame_len(); - - // "Subsequent reads and writes cannot be moved ahead of preceding reads." - atomic::compiler_fence(Ordering::Acquire); - - #[cfg(feature = "ptp")] - let timestamp = Timestamp::from_descriptor(&self.desc().desc); - - // TODO: obtain ethernet frame type (RDESC_1_FT) - let pkt = RxPacket { - entry: self, - length: frame_len, - #[cfg(feature = "ptp")] - timestamp, - }; - Ok(pkt) - } else { - self.desc_mut().set_owned(); - Err(RxError::Truncated) - } - } -} - -#[cfg(feature = "ptp")] -impl RxRingEntry { - fn attach_timestamp(&mut self, packet_id: Option) { - match (packet_id, self.desc().timestamp()) { - (Some(packet_id), Some(timestamp)) => { - self.desc_mut().timestamp_info = Some((packet_id, timestamp)) - } - _ => {} - } - } -} - -/// A received packet. -/// -/// This packet implements [Deref<\[u8\]>](core::ops::Deref) and should be used -/// as a slice. -pub struct RxPacket<'a> { - entry: &'a mut RxRingEntry, - length: usize, - #[cfg(feature = "ptp")] - timestamp: Option, -} - -impl<'a> Deref for RxPacket<'a> { - type Target = [u8]; - - fn deref(&self) -> &Self::Target { - &self.entry.as_slice()[0..self.length] - } -} - -impl<'a> DerefMut for RxPacket<'a> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.entry.as_mut_slice()[0..self.length] - } -} - -impl<'a> Drop for RxPacket<'a> { - fn drop(&mut self) { - self.entry.desc_mut().set_owned(); - } -} - -impl<'a> RxPacket<'a> { - /// Pass the received packet back to the DMA engine. - pub fn free(self) { - drop(self) - } - - /// Get the timestamp associated with this packet - #[cfg(feature = "ptp")] - pub fn timestamp(&self) -> Option { - self.timestamp - } -} - -/// Rx DMA state -pub struct RxRing<'a> { - entries: &'a mut [RxRingEntry], - next_entry: usize, -} - -impl<'a> RxRing<'a> { - /// Allocate - pub fn new(entries: &'a mut [RxRingEntry]) -> Self { - RxRing { - entries, - next_entry: 0, - } - } - - /// Setup the DMA engine (**required**) - pub fn start(&mut self, eth_dma: ÐERNET_DMA) { - // Setup ring - { - let mut previous: Option<&mut RxRingEntry> = None; - for entry in self.entries.iter_mut() { - if let Some(prev_entry) = &mut previous { - prev_entry.setup(Some(entry)); - } - previous = Some(entry); - } - if let Some(entry) = &mut previous { - entry.setup(None); - } - } - self.next_entry = 0; - let ring_ptr = self.entries[0].desc() as *const RxDescriptor; - - // Register RxDescriptor - eth_dma - .dmardlar - .write(|w| unsafe { w.srl().bits(ring_ptr as u32) }); - - // We already have fences in `set_owned`, which is called in `setup` - - // Start receive - eth_dma.dmaomr.modify(|_, w| w.sr().set_bit()); - - self.demand_poll(eth_dma); - } - - /// Demand that the DMA engine polls the current `RxDescriptor` - /// (when in `RunningState::Stopped`.) - pub fn demand_poll(&self, eth_dma: ÐERNET_DMA) { - eth_dma.dmarpdr.write(|w| unsafe { w.rpd().bits(1) }); - } - - /// Get current `RunningState` - pub fn running_state(&self, eth_dma: ÐERNET_DMA) -> RunningState { - match eth_dma.dmasr.read().rps().bits() { - // Reset or Stop Receive Command issued - 0b000 => RunningState::Stopped, - // Fetching receive transfer descriptor - 0b001 => RunningState::Running, - // Waiting for receive packet - 0b011 => RunningState::Running, - // Receive descriptor unavailable - 0b100 => RunningState::Stopped, - // Closing receive descriptor - 0b101 => RunningState::Running, - // Transferring the receive packet data from receive buffer to host memory - 0b111 => RunningState::Running, - _ => RunningState::Unknown, - } - } - - /// Receive the next packet (if any is ready), or return `None` - /// immediately. - pub fn recv_next( - &mut self, - eth_dma: ÐERNET_DMA, - // NOTE(allow): packet_id is unused if ptp is disabled. - #[allow(unused_variables)] packet_id: Option, - ) -> Result { - if !self.running_state(eth_dma).is_running() { - self.demand_poll(eth_dma); - } - - let entries_len = self.entries.len(); - let mut result = self.entries[self.next_entry].take_received(); - - if result.as_mut().err() != Some(&mut RxError::WouldBlock) { - self.next_entry += 1; - if self.next_entry >= entries_len { - self.next_entry = 0; - } - - // Cache the PTP timestamps if PTP is enabled. - #[cfg(feature = "ptp")] - if let Ok(entry) = &mut result { - entry.entry.attach_timestamp(packet_id); - } - } - - result - } -} - -#[cfg(feature = "ptp")] -impl<'a> RxRing<'a> { - pub fn get_timestamp_for_id(&mut self, id: PacketId) -> Result { - for entry in self.entries.iter_mut() { - if let Some((packet_id, timestamp)) = &mut entry.desc_mut().timestamp_info { - if packet_id == &id { - let ts = *timestamp; - entry.desc_mut().timestamp_info.take(); - return Ok(ts); - } - } - } - - return Err(TimestampError::IdNotFound); - } -} - -/// Running state of the `RxRing` -#[derive(PartialEq, Eq, Debug)] -pub enum RunningState { - Unknown, - Stopped, - Running, -} - -impl RunningState { - /// whether self equals to `RunningState::Running` - pub fn is_running(&self) -> bool { - *self == RunningState::Running - } -} diff --git a/src/dma/rx/descriptor.rs b/src/dma/rx/descriptor.rs new file mode 100644 index 0000000..c38c800 --- /dev/null +++ b/src/dma/rx/descriptor.rs @@ -0,0 +1,207 @@ +use core::sync::atomic::{self, Ordering}; + +use crate::{ + dma::{ + raw_descriptor::{DescriptorRingEntry, RawDescriptor}, + PacketId, + }, + ptp::Timestamp, +}; + +use super::RxError; + +/// Owned by DMA engine +const RXDESC_0_OWN: u32 = 1 << 31; +/// First descriptor +const RXDESC_0_FS: u32 = 1 << 9; +/// Last descriptor +const RXDESC_0_LS: u32 = 1 << 8; +/// Error summary +const RXDESC_0_ES: u32 = 1 << 15; +/// Frame length +const RXDESC_0_FL_MASK: u32 = 0x3FFF; +const RXDESC_0_FL_SHIFT: usize = 16; + +/// Receive buffer 1 size +const RXDESC_1_RBS1_SHIFT: usize = 0; +/// Receive buffer 1 size mask +const RXDESC_1_RBS1_MASK: u32 = 0x0fff << RXDESC_1_RBS1_SHIFT; + +/// Receive buffer 2 size +const RXDESC_1_RBS2_SHIFT: usize = 16; +/// Receive buffer 2 size mask +const RXDESC_1_RBS2_MASK: u32 = 0x0fff << RXDESC_1_RBS2_SHIFT; + +/// Receive end of ring +const RXDESC_1_RER: u32 = 1 << 15; + +#[repr(C)] +#[repr(align(4))] +#[derive(Clone, Copy)] +/// An RX DMA Descriptor. +pub struct RxDescriptor { + inner_raw: RawDescriptor, + packet_id: Option, + #[cfg(feature = "ptp")] + cached_timestamp: Option, +} + +impl Default for RxDescriptor { + fn default() -> Self { + Self::new() + } +} + +impl DescriptorRingEntry for RxDescriptor { + fn setup(&mut self, buffer: &mut [u8]) { + self.set_buffer(buffer); + self.set_owned(); + } +} + +impl RxDescriptor { + /// Creates a new [`RxDescriptor`]. + pub const fn new() -> Self { + Self { + inner_raw: RawDescriptor::new(), + packet_id: None, + #[cfg(feature = "ptp")] + cached_timestamp: None, + } + } + + /// Is owned by the DMA engine? + fn is_owned(&self) -> bool { + (self.inner_raw.read(0) & RXDESC_0_OWN) == RXDESC_0_OWN + } + + /// Pass ownership to the DMA engine + /// + /// Overrides old timestamp data + pub(super) fn set_owned(&mut self) { + // "Preceding reads and writes cannot be moved past subsequent writes." + #[cfg(feature = "fence")] + atomic::fence(Ordering::Release); + atomic::compiler_fence(Ordering::Release); + + unsafe { + self.inner_raw.modify(0, |w| w | RXDESC_0_OWN); + } + + // Used to flush the store buffer as fast as possible to make the buffer available for the + // DMA. + #[cfg(feature = "fence")] + atomic::fence(Ordering::SeqCst); + } + + fn has_error(&self) -> bool { + (self.inner_raw.read(0) & RXDESC_0_ES) == RXDESC_0_ES + } + + /// Descriptor contains first buffer of frame + fn is_first(&self) -> bool { + (self.inner_raw.read(0) & RXDESC_0_FS) == RXDESC_0_FS + } + + /// Descriptor contains last buffers of frame + fn is_last(&self) -> bool { + (self.inner_raw.read(0) & RXDESC_0_LS) == RXDESC_0_LS + } + + /// Configure the buffer and its length. + fn set_buffer(&mut self, buffer: &[u8]) { + let buffer_ptr = buffer.as_ptr(); + let buffer_len = buffer.len(); + + unsafe { + self.inner_raw.modify(1, |w| { + // If rbs1 == 0, RBS1 will be ignored + let w = w & !(RXDESC_1_RBS1_MASK); + // Mask out any previous value of rbs2 + let w = w & !(RXDESC_1_RBS2_MASK); + // Set the length of RBS2 + let w = w | ((buffer_len << RXDESC_1_RBS2_SHIFT) as u32 & RXDESC_1_RBS2_MASK); + w + }); + + self.inner_raw.write(3, buffer_ptr as u32); + } + } + + pub(super) fn frame_length(&self) -> usize { + ((self.inner_raw.read(0) >> RXDESC_0_FL_SHIFT) & RXDESC_0_FL_MASK) as usize + } + + pub(super) fn take_received( + &mut self, + // NOTE(allow): packet_id is unused if ptp is disabled. + #[allow(unused_variables)] packet_id: Option, + ) -> Result<(), RxError> { + if self.is_owned() { + Err(RxError::WouldBlock) + } else if self.has_error() { + self.set_owned(); + Err(RxError::DmaError) + } else if self.is_first() && self.is_last() { + // "Subsequent reads and writes cannot be moved ahead of preceding reads." + atomic::compiler_fence(Ordering::Acquire); + + // Cache the PTP timestamps if PTP is enabled. + #[cfg(feature = "ptp")] + self.attach_timestamp(packet_id); + + Ok(()) + } else { + self.set_owned(); + Err(RxError::Truncated) + } + } + + pub(super) fn set_end_of_ring(&mut self) { + unsafe { self.inner_raw.modify(1, |w| w | RXDESC_1_RER) } + } + + pub(super) fn packet_id(&self) -> Option<&PacketId> { + self.packet_id.as_ref() + } +} + +#[cfg(feature = "ptp")] +impl RxDescriptor { + /// Get PTP timestamps if available + pub(super) fn read_timestamp(&self) -> Option { + #[cfg(not(feature = "stm32f1xx-hal"))] + let is_valid = { + /// RX timestamp + const RXDESC_0_TIMESTAMP_VALID: u32 = 1 << 7; + self.inner_raw.read(0) & RXDESC_0_TIMESTAMP_VALID == RXDESC_0_TIMESTAMP_VALID + }; + + #[cfg(feature = "stm32f1xx-hal")] + // There is no "timestamp valid" indicator bit + // on STM32F1XX + let is_valid = true; + + let timestamp = Timestamp::from_descriptor(&self.inner_raw); + + if is_valid && self.is_last() { + timestamp + } else { + None + } + } + + pub(super) fn attach_timestamp(&mut self, packet_id: Option) { + if packet_id != self.packet_id { + self.cached_timestamp.take(); + } + + if let (Some(timestamp), None) = (self.read_timestamp(), self.cached_timestamp) { + self.cached_timestamp = Some(timestamp); + } + } + + pub(super) fn timestamp(&self) -> Option<&Timestamp> { + self.cached_timestamp.as_ref() + } +} diff --git a/src/dma/rx/mod.rs b/src/dma/rx/mod.rs new file mode 100644 index 0000000..0179d99 --- /dev/null +++ b/src/dma/rx/mod.rs @@ -0,0 +1,201 @@ +use super::{ + raw_descriptor::{DescriptorRing, DescriptorRingEntry}, + PacketId, +}; +use crate::peripherals::ETHERNET_DMA; + +#[cfg(feature = "ptp")] +use crate::{dma::TimestampError, ptp::Timestamp}; + +mod descriptor; +pub use descriptor::RxDescriptor; + +/// Errors that can occur during RX +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Debug, PartialEq)] +pub enum RxError { + /// Receiving would block + WouldBlock, + /// The received packet was truncated + Truncated, + /// An error occured with the DMA + DmaError, +} + +/// An RX descriptor ring. +pub type RxDescriptorRing<'rx> = DescriptorRing<'rx, RxDescriptor>; + +/// Rx DMA state +pub(crate) struct RxRing<'data> { + ring: RxDescriptorRing<'data>, + next_entry: usize, +} + +impl<'data> RxRing<'data> { + /// Allocate + pub fn new(ring: RxDescriptorRing<'data>) -> Self { + RxRing { + ring, + next_entry: 0, + } + } + + /// Setup the DMA engine (**required**) + pub fn start(&mut self, eth_dma: ÐERNET_DMA) { + // Setup ring + for (entry, buffer) in self.ring.descriptors_and_buffers() { + entry.setup(buffer); + } + + self.ring + .descriptors_and_buffers() + .last() + .map(|(desc, _)| desc.set_end_of_ring()); + + self.next_entry = 0; + let ring_ptr = self.ring.descriptors_start_address(); + + // Set the RxDma ring start address. + eth_dma + .dmardlar + .write(|w| unsafe { w.srl().bits(ring_ptr as u32) }); + + // Start receive + eth_dma.dmaomr.modify(|_, w| w.sr().set_bit()); + + self.demand_poll(eth_dma); + } + + /// Demand that the DMA engine polls the current `RxDescriptor` + /// (when in `RunningState::Stopped`.) + pub fn demand_poll(&self, eth_dma: ÐERNET_DMA) { + eth_dma.dmarpdr.write(|w| unsafe { w.rpd().bits(1) }); + } + + /// Get current `RunningState` + pub fn running_state(&self, eth_dma: ÐERNET_DMA) -> RunningState { + match eth_dma.dmasr.read().rps().bits() { + // Reset or Stop Receive Command issued + 0b000 => RunningState::Stopped, + // Fetching receive transfer descriptor + 0b001 => RunningState::Running, + // Waiting for receive packet + 0b011 => RunningState::Running, + // Receive descriptor unavailable + 0b100 => RunningState::Stopped, + // Closing receive descriptor + 0b101 => RunningState::Running, + // Transferring the receive packet data from receive buffer to host memory + 0b111 => RunningState::Running, + _ => RunningState::Unknown, + } + } + + /// Receive the next packet (if any is ready), or return `None` + /// immediately. + pub fn recv_next( + &mut self, + eth_dma: ÐERNET_DMA, + #[allow(unused_variables)] packet_id: Option, + ) -> Result { + if !self.running_state(eth_dma).is_running() { + self.demand_poll(eth_dma); + } + + let entries_len = self.ring.len(); + let (descriptor, buffer) = self.ring.get(self.next_entry); + + let mut res = descriptor.take_received(packet_id); + + if res.as_mut().err() != Some(&mut RxError::WouldBlock) { + self.next_entry = (self.next_entry + 1) % entries_len; + } + + res.map(move |_| { + #[cfg(feature = "ptp")] + let timestamp = descriptor.read_timestamp(); + + RxPacket { + entry: descriptor, + buffer, + #[cfg(feature = "ptp")] + timestamp, + } + }) + } +} + +#[cfg(feature = "ptp")] +impl<'a> RxRing<'a> { + pub fn get_timestamp_for_id(&mut self, id: PacketId) -> Result { + for descriptor in self.ring.descriptors() { + if let (Some(packet_id), Some(timestamp)) = + (descriptor.packet_id(), descriptor.timestamp()) + { + if packet_id == &id { + return Ok(timestamp.clone()); + } + } + } + return Err(TimestampError::IdNotFound); + } +} + +/// Running state of the `RxRing` +#[derive(PartialEq, Eq, Debug)] +pub enum RunningState { + Unknown, + Stopped, + Running, +} + +impl RunningState { + /// whether self equals to `RunningState::Running` + pub fn is_running(&self) -> bool { + *self == RunningState::Running + } +} + +/// A received packet. +/// +/// This packet implements [Deref<\[u8\]>](core::ops::Deref) and should be used +/// as a slice. +pub struct RxPacket<'a> { + entry: &'a mut RxDescriptor, + buffer: &'a mut [u8], + #[cfg(feature = "ptp")] + timestamp: Option, +} + +impl<'a> core::ops::Deref for RxPacket<'a> { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + &self.buffer[0..self.entry.frame_length()] + } +} + +impl<'a> core::ops::DerefMut for RxPacket<'a> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.buffer[0..self.entry.frame_length()] + } +} + +impl<'a> Drop for RxPacket<'a> { + fn drop(&mut self) { + self.entry.set_owned(); + } +} + +impl<'a> RxPacket<'a> { + /// Pass the received packet back to the DMA engine. + pub fn free(self) { + drop(self) + } + + /// Get the timestamp associated with this packet + #[cfg(feature = "ptp")] + pub fn timestamp(&self) -> Option { + self.timestamp + } +} diff --git a/src/dma/tx.rs b/src/dma/tx.rs deleted file mode 100644 index 4d7fb81..0000000 --- a/src/dma/tx.rs +++ /dev/null @@ -1,441 +0,0 @@ -use super::PacketId; -use crate::peripherals::ETHERNET_DMA; - -#[cfg(feature = "ptp")] -use super::{Timestamp, TimestampError}; - -use core::{ - ops::{Deref, DerefMut}, - sync::atomic::{self, Ordering}, -}; - -use super::{ - desc::Descriptor, - ring::{RingDescriptor, RingEntry}, -}; - -/// Owned by DMA engine -const TXDESC_0_OWN: u32 = 1 << 31; -/// Interrupt on completion -const TXDESC_0_IC: u32 = 1 << 30; -/// First segment of frame -const TXDESC_0_FS: u32 = 1 << 28; -/// Last segment of frame -const TXDESC_0_LS: u32 = 1 << 29; -/// Checksum insertion control -const TXDESC_0_CIC0: u32 = 1 << 23; -const TXDESC_0_CIC1: u32 = 1 << 22; -/// Timestamp this packet -const TXDESC_0_TIMESTAMP_ENABLE: u32 = 1 << 25; -/// This descriptor contains a timestamp -// NOTE(allow): packet_id is unused if ptp is disabled. -#[allow(dead_code)] -const TXDESC_0_TIMESTAMP_STATUS: u32 = 1 << 17; -/// Transmit end of ring -const TXDESC_0_TER: u32 = 1 << 21; -/// Second address chained -const TXDESC_0_TCH: u32 = 1 << 20; -/// Error status -const TXDESC_0_ES: u32 = 1 << 15; -/// TX done bit -const TXDESC_1_TBS_SHIFT: usize = 0; -const TXDESC_1_TBS_MASK: u32 = 0x0fff << TXDESC_1_TBS_SHIFT; -/// An empty mask -const TXDESC_0_MASK_NONE: u32 = 0; - -/// Errors that can occur during Ethernet TX -#[derive(Debug, PartialEq)] -pub enum TxError { - /// Ring buffer is full - WouldBlock, -} - -/// A TX DMA Ring Descriptor -#[repr(C)] -#[derive(Clone)] -pub struct TxDescriptor { - pub(crate) desc: Descriptor, - pub(crate) packet_id: Option, - buffer_address: u32, - next_descriptor: u32, - /// The value that we want TDES0 to have when - /// the OWNED bit is set. - /// - /// We use a value tracked by this descriptor because - /// the DMA may reset any of the control bits in TDES0 - /// when writing to or updating a descriptor. - /// - /// Control bits are: `IC`, `LS`, `FS`, `DC`, `DP`, `CIC[0:1]`, `TER`, `TCH` - tdes0: u32, - #[cfg(feature = "ptp")] - cached_timestamp: Option, -} - -impl Default for TxDescriptor { - fn default() -> Self { - Self::new() - } -} - -impl TxDescriptor { - /// Creates an zeroed TxDescriptor. - pub const fn new() -> Self { - Self { - desc: Descriptor::new(), - packet_id: None, - buffer_address: 0, - next_descriptor: 0, - tdes0: TXDESC_0_TCH | TXDESC_0_FS | TXDESC_0_LS | TXDESC_0_CIC0 | TXDESC_0_CIC1, - #[cfg(feature = "ptp")] - cached_timestamp: None, - } - } - - /// Write the cached `tdes0` value to the actual - /// TDES word in memory. - fn write_tdes0(&mut self, extra_bits: u32) { - unsafe { - let tdes0 = self.tdes0 | extra_bits; - self.desc.write(0, tdes0); - } - } - - #[allow(unused)] - fn has_error(&self) -> bool { - (self.desc.read(0) & TXDESC_0_ES) == TXDESC_0_ES - } - - /// Is owned by the DMA engine? - pub fn is_owned(&self) -> bool { - (self.desc.read(0) & TXDESC_0_OWN) == TXDESC_0_OWN - } - - // NOTE(allow): packet_id is unused if ptp is disabled. - #[allow(dead_code)] - fn is_last(tdes0: u32) -> bool { - tdes0 & TXDESC_0_LS == TXDESC_0_LS - } - - /// Pass ownership to the DMA engine - fn set_owned(&mut self, extra_status_flags: u32) { - self.write_buffer1(); - self.write_buffer2(); - - // "Preceding reads and writes cannot be moved past subsequent writes." - #[cfg(feature = "fence")] - atomic::fence(Ordering::Release); - atomic::compiler_fence(Ordering::Release); - - self.write_tdes0(TXDESC_0_OWN | extra_status_flags); - - // Used to flush the store buffer as fast as possible to make the buffer available for the - // DMA. - #[cfg(feature = "fence")] - atomic::fence(Ordering::SeqCst); - } - - /// Rewrite buffer1 to the last value we wrote to it - /// - /// In our case, the address of the data buffer for this descriptor - fn write_buffer1(&mut self) { - let buffer_addr = self.buffer_address; - unsafe { - self.desc.write(2, buffer_addr); - } - } - - fn set_buffer1_len(&mut self, len: usize) { - unsafe { - self.desc.modify(1, |w| { - (w & !TXDESC_1_TBS_MASK) | ((len as u32) << TXDESC_1_TBS_SHIFT) - }); - } - } - - /// Rewrite buffer2 to the last value we wrote it to - /// - /// In our case, the address of the next descriptor (may be zero) - fn write_buffer2(&mut self) { - let value = self.next_descriptor; - - unsafe { - self.desc.write(3, value); - } - } - - #[cfg(feature = "ptp")] - fn timestamp(&mut self) -> Option { - let tdes0 = self.desc.read(0); - - let contains_timestamp = (tdes0 & TXDESC_0_TIMESTAMP_STATUS) == TXDESC_0_TIMESTAMP_STATUS; - - if !self.is_owned() && contains_timestamp && Self::is_last(tdes0) { - Timestamp::from_descriptor(&self.desc) - } else { - None - } - } -} - -/// A TX DMA Ring Descriptor entry -pub type TxRingEntry = RingEntry; - -impl RingDescriptor for TxDescriptor { - fn setup(&mut self, buffer: *const u8, _len: usize, next: Option<&Self>) { - // Defer this initialization to this function, so we can have `RingEntry` on bss. - - self.buffer_address = buffer as u32; - self.write_buffer1(); - - let next_desc_addr = if let Some(next) = next { - &next.desc as *const Descriptor as *const u8 as u32 - } else { - self.tdes0 |= TXDESC_0_TER; - 0 - }; - - self.next_descriptor = next_desc_addr; - self.write_buffer2(); - - self.write_tdes0(TXDESC_0_MASK_NONE); - } -} - -impl TxRingEntry { - /// The initial value for a TxRingEntry - pub const TX_INIT: Self = Self::new(); - - fn prepare_packet(&mut self, length: usize, packet_id: Option) -> Option { - assert!(length <= self.as_slice().len()); - - if !self.desc().is_owned() { - let mut extra_flags = TXDESC_0_MASK_NONE; - - self.desc_mut().set_buffer1_len(length); - - if packet_id.is_some() { - extra_flags |= TXDESC_0_TIMESTAMP_ENABLE | TXDESC_0_LS | TXDESC_0_FS; - } - self.desc_mut().packet_id = packet_id; - - extra_flags |= TXDESC_0_IC; - - Some(TxPacket { - entry: self, - length, - extra_flags, - }) - } else { - None - } - } -} - -pub struct TxPacket<'a> { - entry: &'a mut TxRingEntry, - length: usize, - extra_flags: u32, -} - -impl<'a> Deref for TxPacket<'a> { - type Target = [u8]; - - fn deref(&self) -> &Self::Target { - &self.entry.as_slice()[0..self.length] - } -} - -impl<'a> DerefMut for TxPacket<'a> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.entry.as_mut_slice()[0..self.length] - } -} - -impl<'a> TxPacket<'a> { - // Pass to DMA engine - pub fn send(self) { - self.entry.desc_mut().set_owned(self.extra_flags); - } -} - -/// Tx DMA state -pub struct TxRing<'a> { - pub(crate) entries: &'a mut [TxRingEntry], - next_entry: usize, -} - -impl<'a> TxRing<'a> { - #[cfg(feature = "ptp")] - pub(crate) fn collect_timestamps(&mut self) { - for entry in self.entries.iter_mut() { - // Clear all old timestamps - entry.desc_mut().cached_timestamp.take(); - - if entry.desc().packet_id.is_some() { - if let Some(timestamp) = entry.desc_mut().timestamp() { - entry.desc_mut().cached_timestamp = Some(timestamp); - } - } - } - } - - #[cfg(feature = "ptp")] - pub(crate) fn get_timestamp_for_id( - &mut self, - id: PacketId, - ) -> Result { - let mut id_found = false; - for entry in self.entries.iter_mut() { - let TxDescriptor { - cached_timestamp: timestamp, - packet_id, - .. - } = entry.desc_mut(); - - if let Some(packet_id) = packet_id { - if packet_id == &id { - id_found = true; - if let Some(timestamp) = timestamp { - let ts = *timestamp; - entry.desc_mut().cached_timestamp.take(); - return Ok(ts); - } - } - } - } - - if !id_found { - Err(TimestampError::IdNotFound) - } else { - Err(TimestampError::NotYetTimestamped) - } - } - - /// Allocate - /// - /// `start()` will be needed before `send()` - pub fn new(entries: &'a mut [TxRingEntry]) -> Self { - TxRing { - entries, - next_entry: 0, - } - } - - /// Start the Tx DMA engine - pub fn start(&mut self, eth_dma: ÐERNET_DMA) { - // Setup ring - { - let mut previous: Option<&mut TxRingEntry> = None; - for entry in self.entries.iter_mut() { - if let Some(prev_entry) = &mut previous { - prev_entry.setup(Some(entry)); - } - previous = Some(entry); - } - if let Some(entry) = &mut previous { - entry.setup(None); - } - } - - let ring_ptr = self.entries[0].desc() as *const TxDescriptor; - // Register TxDescriptor - eth_dma - .dmatdlar - // Note: unsafe block required for `stm32f107`. - .write(|w| unsafe { w.stl().bits(ring_ptr as u32) }); - - // "Preceding reads and writes cannot be moved past subsequent writes." - #[cfg(feature = "fence")] - atomic::fence(Ordering::Release); - - // We don't need a compiler fence here because all interactions with `Descriptor` are - // volatiles - - // Start transmission - eth_dma.dmaomr.modify(|_, w| w.st().set_bit()); - } - - pub fn send R, R>( - &mut self, - length: usize, - packet_id: Option, - f: F, - ) -> Result { - let entries_len = self.entries.len(); - let entry_num = self.next_entry; - - match self.entries[entry_num].prepare_packet(length, packet_id) { - Some(mut pkt) => { - let r = f(pkt.deref_mut()); - pkt.send(); - - self.next_entry += 1; - if self.next_entry >= entries_len { - self.next_entry = 0; - } - Ok(r) - } - None => Err(TxError::WouldBlock), - } - } - - /// Demand that the DMA engine polls the current `TxDescriptor` - /// (when we just transferred ownership to the hardware). - pub fn demand_poll(&self, eth_dma: ÐERNET_DMA) { - eth_dma.dmatpdr.write(|w| { - #[cfg(any(feature = "stm32f4xx-hal", feature = "stm32f7xx-hal"))] - { - w.tpd().poll() - } - #[cfg(feature = "stm32f1xx-hal")] - unsafe { - // TODO: There is no nice `poll` method for `stm32f107`? - w.tpd().bits(0) - } - }); - } - - /// Is the Tx DMA engine running? - pub fn is_running(&self, eth_dma: ÐERNET_DMA) -> bool { - self.running_state(eth_dma).is_running() - } - - fn running_state(&self, eth_dma: ÐERNET_DMA) -> RunningState { - match eth_dma.dmasr.read().tps().bits() { - // Reset or Stop Transmit Command issued - 0b000 => RunningState::Stopped, - // Fetching transmit transfer descriptor - 0b001 => RunningState::Running, - // Waiting for status - 0b010 => RunningState::Running, - // Reading Data from host memory buffer and queuing it to transmit buffer - 0b011 => RunningState::Running, - 0b100 | 0b101 => RunningState::Reserved, - // Transmit descriptor unavailable - 0b110 => RunningState::Suspended, - _ => RunningState::Unknown, - } - } -} - -#[derive(Debug, PartialEq)] -enum RunningState { - /// Reset or Stop Transmit Command issued - Stopped, - /// Fetching transmit transfer descriptor; - /// Waiting for status; - /// Reading Data from host memory buffer and queuing it to transmit buffer - Running, - /// Reserved for future use - Reserved, - /// Transmit descriptor unavailable - Suspended, - /// Invalid value - Unknown, -} - -impl RunningState { - pub fn is_running(&self) -> bool { - *self == RunningState::Running - } -} diff --git a/src/dma/tx/descriptor.rs b/src/dma/tx/descriptor.rs new file mode 100644 index 0000000..f61d6f2 --- /dev/null +++ b/src/dma/tx/descriptor.rs @@ -0,0 +1,190 @@ +use core::sync::atomic::{self, Ordering}; + +use crate::dma::{ + raw_descriptor::{DescriptorRingEntry, RawDescriptor}, + PacketId, +}; + +#[cfg(feature = "ptp")] +use crate::ptp::Timestamp; + +// Transmit end of ring +const TXDESC_0_TER: u32 = 1 << 21; +/// Owned by DMA engine +const TXDESC_0_OWN: u32 = 1 << 31; +/// Interrupt on completion +const TXDESC_0_IC: u32 = 1 << 30; +/// First segment of frame +const TXDESC_0_FS: u32 = 1 << 28; +/// Last segment of frame +const TXDESC_0_LS: u32 = 1 << 29; +/// Checksum insertion control +const TXDESC_0_CIC0: u32 = 1 << 23; +const TXDESC_0_CIC1: u32 = 1 << 22; +/// Timestamp this packet +// NOTE(allow): packet_id is unused if ptp is disabled. +#[allow(dead_code)] +const TXDESC_0_TIMESTAMP_ENABLE: u32 = 1 << 25; +/// This descriptor contains a timestamp +// NOTE(allow): packet_id is unused if ptp is disabled. +#[allow(dead_code)] +const TXDESC_0_TIMESTAMP_STATUS: u32 = 1 << 17; +/// Error status +const TXDESC_0_ES: u32 = 1 << 15; +/// Transmit buffer 1 size +const TXDESC_1_TBS1_SHIFT: usize = 0; +/// Transmit buffer 1 size mask +const TXDESC_1_TBS1_MASK: u32 = 0x0fff << TXDESC_1_TBS1_SHIFT; +/// Transmit buffer 2 size +const TXDESC_1_TBS2_SHIFT: usize = 16; +/// Transmit buffer 2 size mask +const TXDESC_1_TBS2_MASK: u32 = 0x0fff << TXDESC_1_TBS2_SHIFT; + +/// A TX DMA Ring Descriptor +#[repr(C)] +#[repr(align(4))] +#[derive(Clone, Copy)] +pub struct TxDescriptor { + inner_raw: RawDescriptor, + packet_id: Option, + #[cfg(feature = "ptp")] + cached_timestamp: Option, +} + +impl DescriptorRingEntry for TxDescriptor { + fn setup(&mut self, buffer: &mut [u8]) { + self.set_buffer(buffer); + unsafe { + self.inner_raw.write( + 0, + TXDESC_0_CIC0 | TXDESC_0_CIC1 | TXDESC_0_FS | TXDESC_0_LS | TXDESC_0_IC, + ); + } + } +} + +impl Default for TxDescriptor { + fn default() -> Self { + Self::new() + } +} + +impl TxDescriptor { + /// Creates an zeroed TxDescriptor. + pub const fn new() -> Self { + Self { + inner_raw: RawDescriptor::new(), + packet_id: None, + #[cfg(feature = "ptp")] + cached_timestamp: None, + } + } + + #[allow(unused)] + fn has_error(&self) -> bool { + (self.inner_raw.read(0) & TXDESC_0_ES) == TXDESC_0_ES + } + + /// Is owned by the DMA engine? + pub fn is_owned(&self) -> bool { + (self.inner_raw.read(0) & TXDESC_0_OWN) == TXDESC_0_OWN + } + + // NOTE(allow): packet_id is unused if ptp is disabled. + #[allow(dead_code)] + fn is_last(tdes0: u32) -> bool { + tdes0 & TXDESC_0_LS == TXDESC_0_LS + } + + /// Pass ownership to the DMA engine + pub(super) fn send(&mut self, packet_id: Option, buffer: &[u8]) { + self.set_buffer(buffer); + + let extra_flags = if packet_id.is_some() { + if cfg!(feature = "ptp") { + TXDESC_0_TIMESTAMP_ENABLE + } else { + 0 + } + } else { + 0 + }; + + self.packet_id = packet_id; + + // "Preceding reads and writes cannot be moved past subsequent writes." + #[cfg(feature = "fence")] + atomic::fence(Ordering::Release); + atomic::compiler_fence(Ordering::Release); + + unsafe { self.inner_raw.modify(0, |w| w | extra_flags | TXDESC_0_OWN) }; + + // Used to flush the store buffer as fast as possible to make the buffer available for the + // DMA. + #[cfg(feature = "fence")] + atomic::fence(Ordering::SeqCst); + } + + /// Configure the buffer to use for transmitting, + /// setting it to `buffer`. + fn set_buffer(&mut self, buffer: &[u8]) { + unsafe { + let ptr = buffer.as_ptr(); + + // Set buffer pointer 2 to the provided buffer. + self.inner_raw.write(3, ptr as u32); + + self.inner_raw.modify(1, |w| { + // If we set tbs1 to 0, the DMA will + // ignore this buffer. + let w = w & !TXDESC_1_TBS1_MASK; + // Configure RBS2 as the provided buffer. + let w = w & !TXDESC_1_TBS2_MASK; + w | ((buffer.len() as u32) << TXDESC_1_TBS2_SHIFT) & TXDESC_1_TBS2_MASK + }); + } + } + + // Set the end of ring bit. + pub(super) fn set_end_of_ring(&mut self) { + unsafe { self.inner_raw.modify(0, |w| w | TXDESC_0_TER) }; + } + + pub(super) fn packet_id(&self) -> Option<&PacketId> { + self.packet_id.as_ref() + } +} + +#[cfg(feature = "ptp")] +impl TxDescriptor { + fn read_timestamp(&mut self) -> Option { + let tdes0 = self.inner_raw.read(0); + + let contains_timestamp = (tdes0 & TXDESC_0_TIMESTAMP_STATUS) == TXDESC_0_TIMESTAMP_STATUS; + + if !self.is_owned() && contains_timestamp && Self::is_last(tdes0) { + Timestamp::from_descriptor(&self.inner_raw) + } else { + None + } + } + + pub(super) fn attach_timestamp(&mut self) { + if self.packet_id.is_some() { + self.cached_timestamp = self.read_timestamp(); + } + } + + pub(super) fn timestamp(&self) -> Option<&Timestamp> { + self.cached_timestamp.as_ref() + } +} + +impl TxDescriptor { + /// The initial value for a TxDescriptor + pub const TX_INIT: Self = Self::new(); + + pub(crate) fn prepare_packet(&mut self) -> bool { + !self.is_owned() + } +} diff --git a/src/dma/tx/mod.rs b/src/dma/tx/mod.rs new file mode 100644 index 0000000..674cbf9 --- /dev/null +++ b/src/dma/tx/mod.rs @@ -0,0 +1,202 @@ +use super::{ + raw_descriptor::{DescriptorRing, DescriptorRingEntry}, + PacketId, +}; +use crate::peripherals::ETHERNET_DMA; + +#[cfg(feature = "ptp")] +use super::{Timestamp, TimestampError}; + +mod descriptor; +pub use descriptor::TxDescriptor; + +/// A TX descriptor ring. +pub type TxDescriptorRing<'rx> = DescriptorRing<'rx, TxDescriptor>; + +/// Errors that can occur during Ethernet TX +#[derive(Debug, PartialEq)] +pub enum TxError { + /// Ring buffer is full + WouldBlock, +} + +/// Tx DMA state +pub(crate) struct TxRing<'data> { + ring: TxDescriptorRing<'data>, + next_entry: usize, +} + +impl<'data> TxRing<'data> { + /// Allocate + /// + /// `start()` will be needed before `send()` + pub fn new(ring: TxDescriptorRing<'data>) -> Self { + TxRing { + ring, + next_entry: 0, + } + } + + /// Start the Tx DMA engine + pub fn start(&mut self, eth_dma: ÐERNET_DMA) { + // Setup ring + for (descriptor, buffer) in self.ring.descriptors_and_buffers() { + descriptor.setup(buffer); + } + + self.ring + .descriptors_and_buffers() + .last() + .map(|(desc, _)| desc.set_end_of_ring()); + + let ring_ptr = self.ring.descriptors_start_address(); + + // Register TxDescriptor + eth_dma + .dmatdlar + // Note: unsafe block required for `stm32f107`. + .write(|w| unsafe { w.stl().bits(ring_ptr as u32) }); + + // "Preceding reads and writes cannot be moved past subsequent writes." + #[cfg(feature = "fence")] + core::sync::atomic::fence(core::sync::atomic::Ordering::Release); + + // We don't need a compiler fence here because all interactions with `Descriptor` are + // volatiles + + // Start transmission + eth_dma.dmaomr.modify(|_, w| w.st().set_bit()); + } + + pub fn send R, R>( + &mut self, + length: usize, + packet_id: Option, + f: F, + ) -> Result { + let entries_len = self.ring.len(); + let entry_num = self.next_entry; + + let (descriptor, buffer) = self.ring.get(entry_num); + + assert!(length <= buffer.len()); + + if descriptor.prepare_packet() { + let r = f(&mut buffer[0..length]); + + descriptor.send(packet_id, &buffer[0..length]); + + self.next_entry = (self.next_entry + 1) % entries_len; + + Ok(r) + } else { + Err(TxError::WouldBlock) + } + } + + /// Demand that the DMA engine polls the current `TxDescriptor` + /// (when we just transferred ownership to the hardware). + pub fn demand_poll(&self, eth_dma: ÐERNET_DMA) { + eth_dma.dmatpdr.write(|w| { + #[cfg(any(feature = "stm32f4xx-hal", feature = "stm32f7xx-hal"))] + { + w.tpd().poll() + } + #[cfg(feature = "stm32f1xx-hal")] + unsafe { + // TODO: There is no nice `poll` method for `stm32f107`? + w.tpd().bits(0) + } + }); + } + + /// Is the Tx DMA engine running? + pub fn is_running(&self, eth_dma: ÐERNET_DMA) -> bool { + self.running_state(eth_dma).is_running() + } + + pub(crate) fn running_state(&self, eth_dma: ÐERNET_DMA) -> RunningState { + match eth_dma.dmasr.read().tps().bits() { + // Reset or Stop Transmit Command issued + 0b000 => RunningState::Stopped, + // Fetching transmit transfer descriptor + 0b001 => RunningState::Running, + // Waiting for status + 0b010 => RunningState::Running, + // Reading Data from host memory buffer and queuing it to transmit buffer + 0b011 => RunningState::Running, + 0b100 | 0b101 => RunningState::Reserved, + // Transmit descriptor unavailable + 0b110 => RunningState::Suspended, + _ => RunningState::Unknown, + } + } +} + +#[cfg(feature = "ptp")] +impl<'data> TxRing<'data> { + pub(crate) fn collect_timestamps(&mut self) { + for descriptor in self.ring.descriptors() { + descriptor.attach_timestamp(); + } + } + + pub(crate) fn get_timestamp_for_id( + &mut self, + id: PacketId, + ) -> Result { + let descriptor = if let Some(descriptor) = + self.ring.descriptors().find(|d| d.packet_id() == Some(&id)) + { + descriptor + } else { + return Err(TimestampError::IdNotFound); + }; + + descriptor + .timestamp() + .map(|t| *t) + .ok_or(TimestampError::NotYetTimestamped) + } +} + +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Debug, PartialEq)] +pub enum RunningState { + /// Reset or Stop Transmit Command issued + Stopped, + /// Fetching transmit transfer descriptor; + /// Waiting for status; + /// Reading Data from host memory buffer and queuing it to transmit buffer + Running, + /// Reserved for future use + Reserved, + /// Transmit descriptor unavailable + Suspended, + /// Invalid value + Unknown, +} + +impl RunningState { + pub fn is_running(&self) -> bool { + self == &RunningState::Running + } +} + +pub struct TxPacket<'a> { + buffer: &'a mut [u8], +} + +impl<'a> core::ops::Deref for TxPacket<'a> { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + &self.buffer + } +} + +impl<'a> core::ops::DerefMut for TxPacket<'a> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.buffer + } +} diff --git a/src/lib.rs b/src/lib.rs index 6596ac3..cc63ff9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,7 +28,7 @@ use hal::rcc::Clocks; pub mod dma; #[doc(inline)] #[cfg(feature = "device-selected")] -pub use dma::eth_interrupt_handler; +pub use dma::{eth_interrupt_handler, MTU}; #[cfg(feature = "device-selected")] pub mod mac; @@ -50,7 +50,7 @@ pub use smoltcp; #[cfg(feature = "device-selected")] use { - dma::{EthernetDMA, RxRingEntry, TxRingEntry}, + dma::{EthernetDMA, RxDescriptorRing, TxDescriptorRing}, mac::{EthernetMAC, EthernetMACWithMii, MdcPin, MdioPin, Speed, WrongClock}, setup::*, }; @@ -79,8 +79,8 @@ use ptp::EthernetPTP; #[cfg(feature = "device-selected")] pub fn new<'rx, 'tx, REFCLK, CRS, TXEN, TXD0, TXD1, RXD0, RXD1>( parts: PartsIn, - rx_buffer: &'rx mut [RxRingEntry], - tx_buffer: &'tx mut [TxRingEntry], + rx_buffer: RxDescriptorRing<'rx>, + tx_buffer: TxDescriptorRing<'tx>, clocks: Clocks, pins: EthPins, ) -> Result, WrongClock> @@ -94,6 +94,7 @@ where RXD1: RmiiRxD1 + AlternateVeryHighSpeed, { // Configure all of the pins correctly + pins.setup_pins(); // Set up the clocks and reset the MAC periperhal @@ -144,8 +145,8 @@ where #[cfg(feature = "device-selected")] pub fn new_with_mii<'rx, 'tx, REFCLK, CRS, TXEN, TXD0, TXD1, RXD0, RXD1, MDIO, MDC>( parts: PartsIn, - rx_buffer: &'rx mut [RxRingEntry], - tx_buffer: &'tx mut [TxRingEntry], + rx_buffer: RxDescriptorRing<'rx>, + tx_buffer: TxDescriptorRing<'tx>, clocks: Clocks, pins: EthPins, mdio: MDIO, diff --git a/src/mac/mod.rs b/src/mac/mod.rs index 0e7a8b2..88ce38e 100644 --- a/src/mac/mod.rs +++ b/src/mac/mod.rs @@ -210,6 +210,7 @@ impl EthernetMAC { } } + #[cfg(feature = "ptp")] pub(crate) fn mask_timestamp_trigger_interrupt() { // SAFETY: MACIMR only receives atomic writes. let mac = &unsafe { &*ETHERNET_MAC::ptr() }; @@ -217,6 +218,7 @@ impl EthernetMAC { } // NOTE(allow): only used on F4 and F7 + #[cfg(feature = "ptp")] #[allow(dead_code)] pub(crate) fn unmask_timestamp_trigger_interrupt() { // SAFETY: MACIMR only receives atomic writes. diff --git a/src/ptp/timestamp.rs b/src/ptp/timestamp.rs index 3a869a8..bc91d0b 100644 --- a/src/ptp/timestamp.rs +++ b/src/ptp/timestamp.rs @@ -1,4 +1,4 @@ -use crate::dma::desc::Descriptor; +use crate::dma::raw_descriptor::RawDescriptor; use super::Subseconds; @@ -98,7 +98,7 @@ impl Timestamp { } /// Create a timestamp from the given descriptor - pub fn from_descriptor(desc: &Descriptor) -> Option { + pub fn from_descriptor(desc: &RawDescriptor) -> Option { #[cfg(not(feature = "stm32f1xx-hal"))] { let (high, low) = { (desc.read(7), desc.read(6)) }; From e20ea4d4fe7a99e575990b7c72852384a4e309e0 Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Thu, 2 Feb 2023 11:58:07 +0100 Subject: [PATCH 02/35] Type-state-ify Rx and Tx ring, and stop transmissions on drop --- src/dma/mod.rs | 35 ++++++++++------- src/dma/raw_descriptor.rs | 10 +---- src/dma/rx/descriptor.rs | 19 ++++----- src/dma/rx/mod.rs | 83 ++++++++++++++++++++++++--------------- src/dma/tx/descriptor.rs | 27 ++++++------- src/dma/tx/mod.rs | 67 ++++++++++++++++++++----------- 6 files changed, 135 insertions(+), 106 deletions(-) diff --git a/src/dma/mod.rs b/src/dma/mod.rs index fb5a711..73ed791 100644 --- a/src/dma/mod.rs +++ b/src/dma/mod.rs @@ -60,9 +60,9 @@ pub enum TimestampError { /// Ethernet DMA. pub struct EthernetDMA<'rx, 'tx> { - pub(crate) eth_dma: ETHERNET_DMA, - pub(crate) rx_ring: RxRing<'rx>, - pub(crate) tx_ring: TxRing<'tx>, + eth_dma: ETHERNET_DMA, + rx_ring: RxRing<'rx, rx::Running>, + tx_ring: TxRing<'tx, tx::Running>, } impl<'rx, 'tx> EthernetDMA<'rx, 'tx> { @@ -137,16 +137,14 @@ impl<'rx, 'tx> EthernetDMA<'rx, 'tx> { // Configure word skip length. eth_dma.dmabmr.modify(|_, w| w.dsl().bits(DESC_WORD_SKIP)); - let mut dma = EthernetDMA { - eth_dma, - rx_ring: RxRing::new(rx_buffer), - tx_ring: TxRing::new(tx_buffer), - }; - - dma.rx_ring.start(&dma.eth_dma); - dma.tx_ring.start(&dma.eth_dma); + let rx_ring = RxRing::new(rx_buffer).start(ð_dma); + let tx_ring = TxRing::new(tx_buffer).start(ð_dma); - dma + EthernetDMA { + eth_dma, + rx_ring, + tx_ring, + } } /// Enable RX and TX interrupts @@ -201,9 +199,9 @@ impl<'rx, 'tx> EthernetDMA<'rx, 'tx> { self.tx_ring.running_state(&self.eth_dma) } - pub(crate) fn recv_next_impl<'rx_borrow>( + fn recv_next_impl<'rx_borrow>( eth_dma: ÐERNET_DMA, - rx_ring: &'rx_borrow mut RxRing, + rx_ring: &'rx_borrow mut RxRing, rx_packet_id: Option, ) -> Result, RxError> { rx_ring.recv_next(eth_dma, rx_packet_id.map(|p| p.into())) @@ -222,7 +220,7 @@ impl<'rx, 'tx> EthernetDMA<'rx, 'tx> { pub(crate) fn send_impl R, R>( eth_dma: ÐERNET_DMA, - tx_ring: &mut TxRing, + tx_ring: &mut TxRing, length: usize, tx_packet_id: Option, f: F, @@ -276,6 +274,13 @@ impl<'rx, 'tx> EthernetDMA<'rx, 'tx> { } } +impl<'rx, 'tx> Drop for EthernetDMA<'rx, 'tx> { + fn drop(&mut self) { + self.rx_ring.stop(&self.eth_dma); + self.tx_ring.stop(&self.eth_dma); + } +} + /// A summary of the reasons for the interrupt /// that occured #[cfg_attr(feature = "defmt", derive(defmt::Format))] diff --git a/src/dma/raw_descriptor.rs b/src/dma/raw_descriptor.rs index b659230..397d733 100644 --- a/src/dma/raw_descriptor.rs +++ b/src/dma/raw_descriptor.rs @@ -59,15 +59,7 @@ pub struct DescriptorRing<'data, T> { buffers: &'data mut [[u8; MTU]], } -pub trait DescriptorRingEntry { - /// Set up this [`Descriptor`] with the given buffer. - fn setup(&mut self, buffer: &mut [u8]); -} - -impl<'data, T> DescriptorRing<'data, T> -where - T: DescriptorRingEntry, -{ +impl<'data, T> DescriptorRing<'data, T> { pub fn new(descriptors: &'data mut [T], buffers: &'data mut [[u8; MTU]]) -> Self { assert!(descriptors.len() == buffers.len()); buffers.iter().for_each(|b| assert!(b.len() <= super::MTU)); diff --git a/src/dma/rx/descriptor.rs b/src/dma/rx/descriptor.rs index c38c800..5410eaf 100644 --- a/src/dma/rx/descriptor.rs +++ b/src/dma/rx/descriptor.rs @@ -1,10 +1,7 @@ use core::sync::atomic::{self, Ordering}; use crate::{ - dma::{ - raw_descriptor::{DescriptorRingEntry, RawDescriptor}, - PacketId, - }, + dma::{raw_descriptor::RawDescriptor, PacketId}, ptp::Timestamp, }; @@ -52,13 +49,6 @@ impl Default for RxDescriptor { } } -impl DescriptorRingEntry for RxDescriptor { - fn setup(&mut self, buffer: &mut [u8]) { - self.set_buffer(buffer); - self.set_owned(); - } -} - impl RxDescriptor { /// Creates a new [`RxDescriptor`]. pub const fn new() -> Self { @@ -70,6 +60,11 @@ impl RxDescriptor { } } + pub(super) fn setup(&mut self, buffer: &mut [u8]) { + self.set_buffer(buffer); + self.set_owned(); + } + /// Is owned by the DMA engine? fn is_owned(&self) -> bool { (self.inner_raw.read(0) & RXDESC_0_OWN) == RXDESC_0_OWN @@ -191,7 +186,7 @@ impl RxDescriptor { } } - pub(super) fn attach_timestamp(&mut self, packet_id: Option) { + fn attach_timestamp(&mut self, packet_id: Option) { if packet_id != self.packet_id { self.cached_timestamp.take(); } diff --git a/src/dma/rx/mod.rs b/src/dma/rx/mod.rs index 0179d99..5a42551 100644 --- a/src/dma/rx/mod.rs +++ b/src/dma/rx/mod.rs @@ -1,7 +1,6 @@ -use super::{ - raw_descriptor::{DescriptorRing, DescriptorRingEntry}, - PacketId, -}; +use core::marker::PhantomData; + +use super::{raw_descriptor::DescriptorRing, PacketId}; use crate::peripherals::ETHERNET_DMA; #[cfg(feature = "ptp")] @@ -25,23 +24,55 @@ pub enum RxError { /// An RX descriptor ring. pub type RxDescriptorRing<'rx> = DescriptorRing<'rx, RxDescriptor>; +pub struct NotRunning; +pub struct Running; + /// Rx DMA state -pub(crate) struct RxRing<'data> { +pub struct RxRing<'data, STATE> { ring: RxDescriptorRing<'data>, next_entry: usize, + state: PhantomData, +} + +impl<'data, STATE> RxRing<'data, STATE> { + /// Demand that the DMA engine polls the current `RxDescriptor` + /// (when in `RunningState::Stopped`.) + pub fn demand_poll(&self, eth_dma: ÐERNET_DMA) { + eth_dma.dmarpdr.write(|w| unsafe { w.rpd().bits(1) }); + } + + /// Get current `RunningState` + pub fn running_state(&self, eth_dma: ÐERNET_DMA) -> RunningState { + match eth_dma.dmasr.read().rps().bits() { + // Reset or Stop Receive Command issued + 0b000 => RunningState::Stopped, + // Fetching receive transfer descriptor + 0b001 => RunningState::Running, + // Waiting for receive packet + 0b011 => RunningState::Running, + // Receive descriptor unavailable + 0b100 => RunningState::Stopped, + // Closing receive descriptor + 0b101 => RunningState::Running, + // Transferring the receive packet data from receive buffer to host memory + 0b111 => RunningState::Running, + _ => RunningState::Unknown, + } + } } -impl<'data> RxRing<'data> { +impl<'data> RxRing<'data, NotRunning> { /// Allocate pub fn new(ring: RxDescriptorRing<'data>) -> Self { RxRing { ring, next_entry: 0, + state: Default::default(), } } - /// Setup the DMA engine (**required**) - pub fn start(&mut self, eth_dma: ÐERNET_DMA) { + /// Start the RX ring + pub fn start(mut self, eth_dma: ÐERNET_DMA) -> RxRing<'data, Running> { // Setup ring for (entry, buffer) in self.ring.descriptors_and_buffers() { entry.setup(buffer); @@ -64,31 +95,21 @@ impl<'data> RxRing<'data> { eth_dma.dmaomr.modify(|_, w| w.sr().set_bit()); self.demand_poll(eth_dma); - } - /// Demand that the DMA engine polls the current `RxDescriptor` - /// (when in `RunningState::Stopped`.) - pub fn demand_poll(&self, eth_dma: ÐERNET_DMA) { - eth_dma.dmarpdr.write(|w| unsafe { w.rpd().bits(1) }); + RxRing { + ring: self.ring, + next_entry: self.next_entry, + state: Default::default(), + } } +} - /// Get current `RunningState` - pub fn running_state(&self, eth_dma: ÐERNET_DMA) -> RunningState { - match eth_dma.dmasr.read().rps().bits() { - // Reset or Stop Receive Command issued - 0b000 => RunningState::Stopped, - // Fetching receive transfer descriptor - 0b001 => RunningState::Running, - // Waiting for receive packet - 0b011 => RunningState::Running, - // Receive descriptor unavailable - 0b100 => RunningState::Stopped, - // Closing receive descriptor - 0b101 => RunningState::Running, - // Transferring the receive packet data from receive buffer to host memory - 0b111 => RunningState::Running, - _ => RunningState::Unknown, - } +impl<'data> RxRing<'data, Running> { + /// Stop the DMA engine. + pub fn stop(&mut self, eth_dma: ÐERNET_DMA) { + eth_dma.dmaomr.modify(|_, w| w.sr().clear_bit()); + + while self.running_state(eth_dma) != RunningState::Stopped {} } /// Receive the next packet (if any is ready), or return `None` @@ -126,7 +147,7 @@ impl<'data> RxRing<'data> { } #[cfg(feature = "ptp")] -impl<'a> RxRing<'a> { +impl<'data, STATE> RxRing<'data, STATE> { pub fn get_timestamp_for_id(&mut self, id: PacketId) -> Result { for descriptor in self.ring.descriptors() { if let (Some(packet_id), Some(timestamp)) = diff --git a/src/dma/tx/descriptor.rs b/src/dma/tx/descriptor.rs index f61d6f2..c3549a2 100644 --- a/src/dma/tx/descriptor.rs +++ b/src/dma/tx/descriptor.rs @@ -1,9 +1,6 @@ use core::sync::atomic::{self, Ordering}; -use crate::dma::{ - raw_descriptor::{DescriptorRingEntry, RawDescriptor}, - PacketId, -}; +use crate::dma::{raw_descriptor::RawDescriptor, PacketId}; #[cfg(feature = "ptp")] use crate::ptp::Timestamp; @@ -51,18 +48,6 @@ pub struct TxDescriptor { cached_timestamp: Option, } -impl DescriptorRingEntry for TxDescriptor { - fn setup(&mut self, buffer: &mut [u8]) { - self.set_buffer(buffer); - unsafe { - self.inner_raw.write( - 0, - TXDESC_0_CIC0 | TXDESC_0_CIC1 | TXDESC_0_FS | TXDESC_0_LS | TXDESC_0_IC, - ); - } - } -} - impl Default for TxDescriptor { fn default() -> Self { Self::new() @@ -80,6 +65,16 @@ impl TxDescriptor { } } + pub(super) fn setup(&mut self, buffer: &mut [u8]) { + self.set_buffer(buffer); + unsafe { + self.inner_raw.write( + 0, + TXDESC_0_CIC0 | TXDESC_0_CIC1 | TXDESC_0_FS | TXDESC_0_LS | TXDESC_0_IC, + ); + } + } + #[allow(unused)] fn has_error(&self) -> bool { (self.inner_raw.read(0) & TXDESC_0_ES) == TXDESC_0_ES diff --git a/src/dma/tx/mod.rs b/src/dma/tx/mod.rs index 674cbf9..02a9e9e 100644 --- a/src/dma/tx/mod.rs +++ b/src/dma/tx/mod.rs @@ -1,7 +1,6 @@ -use super::{ - raw_descriptor::{DescriptorRing, DescriptorRingEntry}, - PacketId, -}; +use core::marker::PhantomData; + +use super::{raw_descriptor::DescriptorRing, PacketId}; use crate::peripherals::ETHERNET_DMA; #[cfg(feature = "ptp")] @@ -10,6 +9,9 @@ use super::{Timestamp, TimestampError}; mod descriptor; pub use descriptor::TxDescriptor; +pub struct NotRunning; +pub struct Running; + /// A TX descriptor ring. pub type TxDescriptorRing<'rx> = DescriptorRing<'rx, TxDescriptor>; @@ -21,12 +23,32 @@ pub enum TxError { } /// Tx DMA state -pub(crate) struct TxRing<'data> { +pub(crate) struct TxRing<'data, STATE> { ring: TxDescriptorRing<'data>, next_entry: usize, + state: PhantomData, +} + +impl<'data, STATE> TxRing<'data, STATE> { + pub fn running_state(&self, eth_dma: ÐERNET_DMA) -> RunningState { + match eth_dma.dmasr.read().tps().bits() { + // Reset or Stop Transmit Command issued + 0b000 => RunningState::Stopped, + // Fetching transmit transfer descriptor + 0b001 => RunningState::Running, + // Waiting for status + 0b010 => RunningState::Running, + // Reading Data from host memory buffer and queuing it to transmit buffer + 0b011 => RunningState::Running, + 0b100 | 0b101 => RunningState::Reserved, + // Transmit descriptor unavailable + 0b110 => RunningState::Suspended, + _ => RunningState::Unknown, + } + } } -impl<'data> TxRing<'data> { +impl<'data> TxRing<'data, NotRunning> { /// Allocate /// /// `start()` will be needed before `send()` @@ -34,11 +56,12 @@ impl<'data> TxRing<'data> { TxRing { ring, next_entry: 0, + state: Default::default(), } } /// Start the Tx DMA engine - pub fn start(&mut self, eth_dma: ÐERNET_DMA) { + pub fn start(mut self, eth_dma: ÐERNET_DMA) -> TxRing<'data, Running> { // Setup ring for (descriptor, buffer) in self.ring.descriptors_and_buffers() { descriptor.setup(buffer); @@ -66,8 +89,16 @@ impl<'data> TxRing<'data> { // Start transmission eth_dma.dmaomr.modify(|_, w| w.st().set_bit()); + + TxRing { + ring: self.ring, + next_entry: self.next_entry, + state: Default::default(), + } } +} +impl<'data> TxRing<'data, Running> { pub fn send R, R>( &mut self, length: usize, @@ -115,26 +146,16 @@ impl<'data> TxRing<'data> { self.running_state(eth_dma).is_running() } - pub(crate) fn running_state(&self, eth_dma: ÐERNET_DMA) -> RunningState { - match eth_dma.dmasr.read().tps().bits() { - // Reset or Stop Transmit Command issued - 0b000 => RunningState::Stopped, - // Fetching transmit transfer descriptor - 0b001 => RunningState::Running, - // Waiting for status - 0b010 => RunningState::Running, - // Reading Data from host memory buffer and queuing it to transmit buffer - 0b011 => RunningState::Running, - 0b100 | 0b101 => RunningState::Reserved, - // Transmit descriptor unavailable - 0b110 => RunningState::Suspended, - _ => RunningState::Unknown, - } + pub fn stop(&mut self, eth_dma: ÐERNET_DMA) { + // Start transmission + eth_dma.dmaomr.modify(|_, w| w.st().clear_bit()); + + while self.running_state(eth_dma) != RunningState::Stopped {} } } #[cfg(feature = "ptp")] -impl<'data> TxRing<'data> { +impl<'data> TxRing<'data, Running> { pub(crate) fn collect_timestamps(&mut self) { for descriptor in self.ring.descriptors() { descriptor.attach_timestamp(); From 388577e17825943af85237e59f090af0702a755f Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Thu, 2 Feb 2023 12:02:28 +0100 Subject: [PATCH 03/35] Properly feature gate ptp::Timestamp import --- src/dma/rx/descriptor.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/dma/rx/descriptor.rs b/src/dma/rx/descriptor.rs index 5410eaf..fd92777 100644 --- a/src/dma/rx/descriptor.rs +++ b/src/dma/rx/descriptor.rs @@ -1,11 +1,9 @@ use core::sync::atomic::{self, Ordering}; -use crate::{ - dma::{raw_descriptor::RawDescriptor, PacketId}, - ptp::Timestamp, -}; +use crate::dma::{raw_descriptor::RawDescriptor, PacketId, RxError}; -use super::RxError; +#[cfg(feature = "ptp")] +use crate::ptp::Timestamp; /// Owned by DMA engine const RXDESC_0_OWN: u32 = 1 << 31; From 78500520f53921bbfea66d1746544f1a016bb282 Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Thu, 2 Feb 2023 12:09:15 +0100 Subject: [PATCH 04/35] Getting timestamp does not have to be mutable --- src/dma/mod.rs | 5 +---- src/dma/raw_descriptor.rs | 6 +++++- src/dma/rx/mod.rs | 2 +- src/dma/tx/mod.rs | 7 ++----- 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/dma/mod.rs b/src/dma/mod.rs index 73ed791..f96faea 100644 --- a/src/dma/mod.rs +++ b/src/dma/mod.rs @@ -248,10 +248,7 @@ impl<'rx, 'tx> EthernetDMA<'rx, 'tx> { /// 2. Before calling [`interrupt_handler`](EthernetDMA::interrupt_handler) again, retrieve timestamps of sent and received frames using this function. /// /// Retrieving RX timestamps can also be done using [`RxPacket::timestamp`]. - pub fn get_timestamp_for_id<'a, PKT>( - &mut self, - packet_id: PKT, - ) -> Result + pub fn get_timestamp_for_id<'a, PKT>(&self, packet_id: PKT) -> Result where PKT: Into, { diff --git a/src/dma/raw_descriptor.rs b/src/dma/raw_descriptor.rs index 397d733..5d124e1 100644 --- a/src/dma/raw_descriptor.rs +++ b/src/dma/raw_descriptor.rs @@ -78,7 +78,11 @@ impl<'data, T> DescriptorRing<'data, T> { (&mut self.descriptors[index], &mut self.buffers[index]) } - pub fn descriptors(&mut self) -> impl Iterator { + pub fn descriptors(&self) -> impl Iterator { + self.descriptors.iter() + } + + pub fn descriptors_mut(&mut self) -> impl Iterator { self.descriptors.iter_mut() } diff --git a/src/dma/rx/mod.rs b/src/dma/rx/mod.rs index 5a42551..f60ae2f 100644 --- a/src/dma/rx/mod.rs +++ b/src/dma/rx/mod.rs @@ -148,7 +148,7 @@ impl<'data> RxRing<'data, Running> { #[cfg(feature = "ptp")] impl<'data, STATE> RxRing<'data, STATE> { - pub fn get_timestamp_for_id(&mut self, id: PacketId) -> Result { + pub fn get_timestamp_for_id(&self, id: PacketId) -> Result { for descriptor in self.ring.descriptors() { if let (Some(packet_id), Some(timestamp)) = (descriptor.packet_id(), descriptor.timestamp()) diff --git a/src/dma/tx/mod.rs b/src/dma/tx/mod.rs index 02a9e9e..f73b7d1 100644 --- a/src/dma/tx/mod.rs +++ b/src/dma/tx/mod.rs @@ -157,15 +157,12 @@ impl<'data> TxRing<'data, Running> { #[cfg(feature = "ptp")] impl<'data> TxRing<'data, Running> { pub(crate) fn collect_timestamps(&mut self) { - for descriptor in self.ring.descriptors() { + for descriptor in self.ring.descriptors_mut() { descriptor.attach_timestamp(); } } - pub(crate) fn get_timestamp_for_id( - &mut self, - id: PacketId, - ) -> Result { + pub(crate) fn get_timestamp_for_id(&self, id: PacketId) -> Result { let descriptor = if let Some(descriptor) = self.ring.descriptors().find(|d| d.packet_id() == Some(&id)) { From d311ce5520f3adae9cf7dd0ac57f6334b91a8210 Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Thu, 2 Feb 2023 12:19:30 +0100 Subject: [PATCH 05/35] Fix timestamp attaching logic for RxDescriptor and update rtic-timestamp so that it verifies that this works --- examples/rtic-timestamp.rs | 57 ++++++++++++++++++++++++++------------ src/dma/mod.rs | 1 + src/dma/rx/descriptor.rs | 14 ++++------ src/dma/tx/descriptor.rs | 4 +-- 4 files changed, 47 insertions(+), 29 deletions(-) diff --git a/examples/rtic-timestamp.rs b/examples/rtic-timestamp.rs index b414776..894c6d2 100644 --- a/examples/rtic-timestamp.rs +++ b/examples/rtic-timestamp.rs @@ -188,8 +188,10 @@ mod app { }); } - #[task(binds = ETH, shared = [dma, tx_id, ptp, scheduled_time], priority = 2)] + #[task(binds = ETH, shared = [dma, tx_id, ptp, scheduled_time], local = [rx_packet_id: u32 = 0], priority = 2)] fn eth_interrupt(cx: eth_interrupt::Context) { + let packet_id = cx.local.rx_packet_id; + ( cx.shared.dma, cx.shared.tx_id, @@ -212,26 +214,50 @@ mod app { } } - let mut packet_id = 0; + let mut buffer = [0u8; 22]; + + while let Ok((data, rx_timestamp, used_packet_id)) = { + let used_packet_id = *packet_id; + let result = if let Ok(packet) = dma.recv_next(Some(used_packet_id.into())) { + let data_len = packet.len().min(22); + buffer[..data_len].copy_from_slice(&packet[..data_len]); + let data = &buffer[..data_len]; - while let Ok(packet) = dma.recv_next(Some(packet_id.into())) { - let mut dst_mac = [0u8; 6]; - dst_mac.copy_from_slice(&packet[..6]); + // For RX packets, we can grab the timestamp directly or + // indirectly using [`EthernetDMA::get_timestamp_for_id`]. + // + // Using `timestamp` directly is easier, because you don't + // have to re-borrow the DMA for it. + let timestamp = packet.timestamp(); - // Note that, instead of grabbing the timestamp from the `RxPacket` directly, it - // is also possible to retrieve a cached version of the timestamp using - // `EthernetDMA::get_timestamp_for_id` (in the same way as for TX timestamps). - let ts = if let Some(timestamp) = packet.timestamp() { + *packet_id += 1; + *packet_id &= !0x8000_0000; + + Ok((data, timestamp, used_packet_id)) + } else { + Err(()) + }; + result + } { + let rx_timestamp = if let Some(timestamp) = rx_timestamp { timestamp } else { continue; }; - defmt::debug!("RX timestamp: {}", ts); + // Get the timestamp "the long way" around by asking the DMA to retrieve + // the value cached in the RX packet. + let cached_timestamp = dma.get_timestamp_for_id(used_packet_id); + + // Assert that they are the same. + defmt::assert_eq!(cached_timestamp, Ok(rx_timestamp)); - let timestamp = if dst_mac == [0xAB, 0xCD, 0xEF, 0x12, 0x34, 0x56] { + defmt::debug!("RX timestamp: {}", rx_timestamp); + + let dst_mac = &data[..6]; + let tx_timestamp = if dst_mac == [0xAB, 0xCD, 0xEF, 0x12, 0x34, 0x56] { let mut timestamp_data = [0u8; 8]; - timestamp_data.copy_from_slice(&packet[14..22]); + timestamp_data.copy_from_slice(&data[14..22]); let raw = i64::from_be_bytes(timestamp_data); let timestamp = Timestamp::new_raw(raw); @@ -240,9 +266,9 @@ mod app { continue; }; - defmt::debug!("Contained TX timestamp: {}", ts); + defmt::debug!("Contained TX timestamp: {}", rx_timestamp); - let diff = timestamp - ts; + let diff = tx_timestamp - rx_timestamp; defmt::info!("Difference between TX and RX time: {}", diff); @@ -263,9 +289,6 @@ mod app { defmt::warn!("Updated time."); ptp.update_time(diff); } - - packet_id += 1; - packet_id &= !0x8000_0000; } if let Some((tx_id, sent_time)) = tx_id.take() { diff --git a/src/dma/mod.rs b/src/dma/mod.rs index f96faea..3de680a 100644 --- a/src/dma/mod.rs +++ b/src/dma/mod.rs @@ -50,6 +50,7 @@ pub const MTU: usize = 1522; /// RX or TX descriptor handled by the DMA. #[cfg(feature = "ptp")] #[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum TimestampError { /// The descriptor with the given packet ID has not been /// timestamped yet. diff --git a/src/dma/rx/descriptor.rs b/src/dma/rx/descriptor.rs index fd92777..de5f92a 100644 --- a/src/dma/rx/descriptor.rs +++ b/src/dma/rx/descriptor.rs @@ -139,9 +139,11 @@ impl RxDescriptor { // "Subsequent reads and writes cannot be moved ahead of preceding reads." atomic::compiler_fence(Ordering::Acquire); + self.packet_id = packet_id; + // Cache the PTP timestamps if PTP is enabled. #[cfg(feature = "ptp")] - self.attach_timestamp(packet_id); + self.attach_timestamp(); Ok(()) } else { @@ -184,14 +186,8 @@ impl RxDescriptor { } } - fn attach_timestamp(&mut self, packet_id: Option) { - if packet_id != self.packet_id { - self.cached_timestamp.take(); - } - - if let (Some(timestamp), None) = (self.read_timestamp(), self.cached_timestamp) { - self.cached_timestamp = Some(timestamp); - } + fn attach_timestamp(&mut self) { + self.cached_timestamp = self.read_timestamp(); } pub(super) fn timestamp(&self) -> Option<&Timestamp> { diff --git a/src/dma/tx/descriptor.rs b/src/dma/tx/descriptor.rs index c3549a2..2e3e86a 100644 --- a/src/dma/tx/descriptor.rs +++ b/src/dma/tx/descriptor.rs @@ -165,9 +165,7 @@ impl TxDescriptor { } pub(super) fn attach_timestamp(&mut self) { - if self.packet_id.is_some() { - self.cached_timestamp = self.read_timestamp(); - } + self.cached_timestamp = self.read_timestamp(); } pub(super) fn timestamp(&self) -> Option<&Timestamp> { From 26388b6abb965c0155af1d1a99a9fa7e08024afe Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Fri, 3 Feb 2023 07:46:27 +0100 Subject: [PATCH 06/35] Initial H7 support --- Cargo.toml | 10 + build.rs | 1 - examples/arp.rs | 33 +- examples/common.rs | 117 ++++++- memory.x | 62 +++- src/dma/mod.rs | 299 +++++++++++++----- src/dma/raw_descriptor.rs | 37 ++- .../rx/{descriptor.rs => f_series_desc.rs} | 0 src/dma/rx/h_desc.rs | 247 +++++++++++++++ src/dma/rx/mod.rs | 108 ++++++- .../tx/{descriptor.rs => f_series_desc.rs} | 0 src/dma/tx/h_desc.rs | 258 +++++++++++++++ src/dma/tx/mod.rs | 101 +++++- src/lib.rs | 33 +- src/mac/miim.rs | 93 ++++++ src/mac/mod.rs | 240 +++++++------- src/peripherals.rs | 3 + src/setup.rs | 104 ++++-- 18 files changed, 1450 insertions(+), 296 deletions(-) rename src/dma/rx/{descriptor.rs => f_series_desc.rs} (100%) create mode 100644 src/dma/rx/h_desc.rs rename src/dma/tx/{descriptor.rs => f_series_desc.rs} (100%) create mode 100644 src/dma/tx/h_desc.rs diff --git a/Cargo.toml b/Cargo.toml index 3ca9e23..537cd78 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,11 +25,13 @@ stm32f7xx-hal = { version = "0.7.0", optional = true } stm32f4xx-hal = { version = "0.14", optional = true } stm32f4 = { version = "0.15", optional = true } stm32f1xx-hal = { version = "0.10", optional = true } +stm32h7xx-hal = { version = "0.13", optional = true } ieee802_3_miim = "0.8" cortex-m = "0.7" log = { version = "0.4", optional = true } defmt = { version = "0.3", optional = true } siphasher = "*" +paste = { version = "1.0", optional = true } [dependencies.smoltcp] version = "0.8.2" @@ -41,6 +43,12 @@ default = [ "defmt", "ptp" ] device-selected = [] fence = [] ptp = [ ] +f-series = [ ] + +stm32f1xx-hal = [ "dep:stm32f1xx-hal", "f-series" ] +stm32f4xx-hal = [ "dep:stm32f4xx-hal", "f-series" ] +stm32f7xx-hal = [ "dep:stm32f7xx-hal", "f-series" ] +stm32h7xx-hal = [ "dep:stm32h7xx-hal", "paste" ] stm32f107 = ["stm32f1xx-hal/stm32f107", "device-selected"] @@ -63,6 +71,8 @@ stm32f777 = ["stm32f7xx-hal/stm32f777", "device-selected", "fence"] stm32f778 = ["stm32f7xx-hal/stm32f778", "device-selected", "fence"] stm32f779 = ["stm32f7xx-hal/stm32f779", "device-selected", "fence"] +stm32h735 = ["stm32h7xx-hal/stm32h735", "device-selected", "fence"] + smoltcp-phy = ["smoltcp"] [dev-dependencies] diff --git a/build.rs b/build.rs index 9700e20..29386fa 100644 --- a/build.rs +++ b/build.rs @@ -1,5 +1,4 @@ fn main() { - #[cfg(feature = "stm32f1xx-hal")] println!("cargo:rustc-link-search=memory.x"); let hse = std::env::var("STM32_ETH_EXAMPLE_HSE"); diff --git a/examples/arp.rs b/examples/arp.rs index 8d9c973..3043e24 100644 --- a/examples/arp.rs +++ b/examples/arp.rs @@ -17,11 +17,13 @@ use cortex_m_rt::{entry, exception}; use cortex_m::interrupt::Mutex; use stm32_eth::{ dma::{RxDescriptor, TxDescriptor}, - mac::{phy::BarePhy, Phy}, stm32::{interrupt, CorePeripherals, Peripherals, SYST}, Parts, MTU, }; +#[cfg(feature = "f-series")] +use stm32_eth::mac::{phy::BarePhy, Phy}; + pub mod common; use stm32_eth::dma::{RxDescriptorRing, TxDescriptorRing, TxError}; @@ -31,6 +33,17 @@ const PHY_ADDR: u8 = 0; static TIME: Mutex> = Mutex::new(RefCell::new(0)); static ETH_PENDING: Mutex> = Mutex::new(RefCell::new(false)); +/// On H7s, the ethernet DMA does not have access to the normal ram +/// so we must explicitly put them in SRAM. +#[cfg_attr(feature = "stm32h7xx-hal", link_section = ".sram1.eth")] +static mut TX_DESCRIPTORS: [TxDescriptor; 4] = [TxDescriptor::new(); 4]; +#[cfg_attr(feature = "stm32h7xx-hal", link_section = ".sram1.eth")] +static mut TX_BUFFERS: [[u8; MTU + 2]; 4] = [[0u8; MTU + 2]; 4]; +#[cfg_attr(feature = "stm32h7xx-hal", link_section = ".sram1.eth2")] +static mut RX_DESCRIPTORS: [RxDescriptor; 4] = [RxDescriptor::new(); 4]; +#[cfg_attr(feature = "stm32h7xx-hal", link_section = ".sram1.eth2")] +static mut RX_BUFFERS: [[u8; MTU + 2]; 4] = [[0u8; MTU + 2]; 4]; + #[entry] fn main() -> ! { let p = Peripherals::take().unwrap(); @@ -44,13 +57,8 @@ fn main() -> ! { let (eth_pins, mdio, mdc, _) = common::setup_pins(gpio); - let mut rx_descriptors: [RxDescriptor; 2] = Default::default(); - let mut rx_buffers: [[u8; MTU]; 2] = [[0u8; MTU]; 2]; - let rx_ring = RxDescriptorRing::new(&mut rx_descriptors, &mut rx_buffers); - - let mut tx_descriptors: [TxDescriptor; 2] = Default::default(); - let mut tx_buffers: [[u8; MTU]; 2] = [[0u8; MTU]; 2]; - let tx_ring = TxDescriptorRing::new(&mut tx_descriptors, &mut tx_buffers); + let rx_ring = RxDescriptorRing::new(unsafe { &mut RX_DESCRIPTORS }, unsafe { &mut RX_BUFFERS }); + let tx_ring = TxDescriptorRing::new(unsafe { &mut TX_DESCRIPTORS }, unsafe { &mut TX_BUFFERS }); let Parts { mut dma, @@ -62,11 +70,16 @@ fn main() -> ! { let mut last_link_up = false; - let mut bare_phy = BarePhy::new(mac.with_mii(mdio, mdc), PHY_ADDR, Default::default()); + #[cfg(feature = "f-series")] + let mut bare_phy = BarePhy::new(_mac.with_mii(mdio, mdc), PHY_ADDR, Default::default()); loop { + #[cfg(feature = "f-series")] let link_up = bare_phy.phy_link_up(); + #[cfg(feature = "stm32h7xx-hal")] + let link_up = true; + if link_up != last_link_up { if link_up { defmt::info!("Ethernet: link detected"); @@ -129,7 +142,7 @@ fn main() -> ! { defmt::info!("ARP sent"); } Err(TxError::WouldBlock) => { - defmt::panic!("ARP failed. {}", dma.tx_state()) + defmt::info!("ARP failed. {}", dma.tx_state()) } } } else { diff --git a/examples/common.rs b/examples/common.rs index 0bc0e2e..92d73b6 100644 --- a/examples/common.rs +++ b/examples/common.rs @@ -4,10 +4,13 @@ //! //! Note that this module isn't an example by itself. -use stm32_eth::{ - hal::{gpio::GpioExt, rcc::Clocks}, - PartsIn, -}; +use stm32_eth::{hal::gpio::GpioExt, PartsIn}; + +#[cfg(feature = "f-series")] +use stm32_eth::hal::rcc::Clocks; + +#[cfg(feature = "stm32h7xx-hal")] +use stm32_eth::hal::rcc::CoreClocks as Clocks; pub use pins::{setup_pins, Gpio}; @@ -23,6 +26,9 @@ pub fn setup_peripherals(p: stm32_eth::stm32::Peripherals) -> (Clocks, Gpio, Par let ethernet = PartsIn { dma: p.ETHERNET_DMA, mac: p.ETHERNET_MAC, + #[cfg(feature = "stm32h7xx-hal")] + mtl: p.ETHERNET_MTL, + #[cfg(feature = "f-series")] mmc: p.ETHERNET_MMC, #[cfg(feature = "ptp")] ptp: p.ETHERNET_PTP, @@ -99,6 +105,40 @@ pub fn setup_peripherals(p: stm32_eth::stm32::Peripherals) -> (Clocks, Gpio, Par (clocks, gpio, ethernet) } + + #[cfg(feature = "stm32h7xx-hal")] + { + use stm32_eth::hal::pwr::PwrExt; + + let rcc = p.RCC.constrain(); + let pwr = p.PWR.constrain(); + + let syscfg = p.SYSCFG; + + let pwrcfg = pwr.vos0(&syscfg).freeze(); + + let rcc = rcc.hclk(240.MHz()); + + let rcc = if cfg!(hse = "bypass") { + rcc.bypass_hse().use_hse(8.MHz()) + } else if cfg!(hse = "oscillator") { + rcc.use_hse(8.MHz()) + } else { + rcc + }; + + let ccdr = rcc.freeze(pwrcfg, &syscfg); + let clocks = ccdr.clocks; + + let gpio = Gpio { + gpioa: p.GPIOA.split(ccdr.peripheral.GPIOA), + gpiob: p.GPIOB.split(ccdr.peripheral.GPIOB), + gpioc: p.GPIOC.split(ccdr.peripheral.GPIOC), + gpiog: p.GPIOG.split(ccdr.peripheral.GPIOG), + }; + + (clocks, gpio, ethernet) + } } pub use pins::*; @@ -280,6 +320,75 @@ mod pins { } } +#[cfg(feature = "stm32h7xx-hal")] +mod pins { + use stm32_eth::{ + hal::gpio::{Input, PushPull, *}, + EthPins, + }; + + pub struct Gpio { + pub gpioa: gpioa::Parts, + pub gpiob: gpiob::Parts, + pub gpioc: gpioc::Parts, + pub gpiog: gpiog::Parts, + } + + pub type RefClk = PA1; + pub type Crs = PA7; + pub type TxEn = PG11; + pub type TxD0 = PG13; + pub type TxD1 = PB13; + pub type RxD0 = PC4; + pub type RxD1 = PC5; + + pub type Pps = PB5>; + + pub type Mdio = (); + pub type Mdc = (); + + pub fn setup_pins( + gpio: Gpio, + ) -> ( + EthPins, + Mdio, + Mdc, + Pps, + ) { + let Gpio { + gpioa, + gpiob, + gpioc, + gpiog, + } = gpio; + + let ref_clk = gpioa.pa1.into_input(); + let crs = gpioa.pa7.into_input(); + let rx_d0 = gpioc.pc4.into_input(); + let rx_d1 = gpioc.pc5.into_input(); + let tx_en = gpiog.pg11.into_input(); + let tx_d0 = gpiog.pg13.into_input(); + let tx_d1 = gpiob.pb13.into_input(); + + let mdc = (); + let mdio = (); + + let pps = gpiob.pb5.into_push_pull_output(); + + let pins = EthPins { + ref_clk, + crs, + tx_en, + tx_d0, + tx_d1, + rx_d0, + rx_d1, + }; + + (pins, mdio, mdc, pps) + } +} + use ieee802_3_miim::{ phy::{ lan87xxa::{LAN8720A, LAN8742A}, diff --git a/memory.x b/memory.x index 5858599..e141e7a 100644 --- a/memory.x +++ b/memory.x @@ -1,5 +1,63 @@ MEMORY { - FLASH : ORIGIN = 0x08000000, LENGTH = 128k - RAM : ORIGIN = 0x20000000, LENGTH = 32K + /* FLASH and RAM are mandatory memory regions */ + + /* STM32H742xI/743xI/753xI */ + /* STM32H745xI/747xI/755xI/757xI */ + /* STM32H7A3xI/7B3xI */ + FLASH : ORIGIN = 0x08000000, LENGTH = 1M + + /* STM32H742xG/743xG */ + /* STM32H745xG/STM32H747xG */ + /* STM32H7A3xG */ + /* FLASH : ORIGIN = 0x08000000, LENGTH = 512K */ + /* FLASH1 : ORIGIN = 0x08100000, LENGTH = 512K */ + + /* STM32H750xB */ + /* STM32H7B0 */ + /* FLASH : ORIGIN = 0x08000000, LENGTH = 128K */ + + /* DTCM */ + RAM : ORIGIN = 0x20000000, LENGTH = 128K + + /* AXISRAM */ + AXISRAM : ORIGIN = 0x24000000, LENGTH = 512K + + /* SRAM */ + SRAM1 : ORIGIN = 0x30000000, LENGTH = 128K + SRAM2 : ORIGIN = 0x30020000, LENGTH = 128K + /* SRAM3 : ORIGIN = 0x30040000, LENGTH = 32K */ + SRAM4 : ORIGIN = 0x38000000, LENGTH = 64K + + /* Backup SRAM */ + BSRAM : ORIGIN = 0x38800000, LENGTH = 4K + + /* Instruction TCM */ + ITCM : ORIGIN = 0x00000000, LENGTH = 64K } + +/* The location of the stack can be overridden using the + `_stack_start` symbol. Place the stack at the end of RAM */ +_stack_start = ORIGIN(RAM) + LENGTH(RAM); + +/* The location of the .text section can be overridden using the + `_stext` symbol. By default it will place after .vector_table */ +/* _stext = ORIGIN(FLASH) + 0x40c; */ + +/* These sections are used for some of the examples */ +SECTIONS { + .axisram (NOLOAD) : ALIGN(8) { + *(.axisram .axisram.*); + . = ALIGN(8); + } > AXISRAM + /* The SRAM1 and SRAM2 section are commonly used as the stack and heap for the + CM4 core in dualcore versions and should thus not be used in examples*/ + .sram4 (NOLOAD) : ALIGN(4) { + *(.sram4 .sram4.*); + . = ALIGN(4); + } > SRAM4 + .sram1 (NOLOAD) : ALIGN(4) { + *(.sram1 .sram1.*); + . = ALIGN(4); + } > SRAM1 +}; \ No newline at end of file diff --git a/src/dma/mod.rs b/src/dma/mod.rs index 3de680a..3f3d3be 100644 --- a/src/dma/mod.rs +++ b/src/dma/mod.rs @@ -6,6 +6,12 @@ use cortex_m::peripheral::NVIC; use crate::{peripherals::ETHERNET_DMA, stm32::Interrupt}; +#[cfg(feature = "f-series")] +type ETHERNET_MTL = (); + +#[cfg(feature = "stm32h7xx-hal")] +use crate::stm32::ETHERNET_MTL; + #[cfg(feature = "smoltcp-phy")] mod smoltcp_phy; #[cfg(feature = "smoltcp-phy")] @@ -41,6 +47,8 @@ const _ASSERT_DESCRIPTOR_SIZES: () = assert!(_RXDESC_SIZE == _TXDESC_SIZE); const DESC_WORD_SKIP: u8 = (core::mem::size_of::() / 4 - DESC_SIZE) as u8; +const _ASSERT_DESC_WORD_SKIP_SIZE: () = assert!(DESC_WORD_SKIP <= 0b111); + /// The maximum transmission unit of this Ethernet peripheral. /// /// From the datasheet: *VLAN Frame maxsize = 1522* @@ -61,12 +69,22 @@ pub enum TimestampError { /// Ethernet DMA. pub struct EthernetDMA<'rx, 'tx> { - eth_dma: ETHERNET_DMA, + parts: DmaParts, rx_ring: RxRing<'rx, rx::Running>, tx_ring: TxRing<'tx, tx::Running>, } +pub(crate) struct DmaParts { + pub eth_dma: ETHERNET_DMA, + #[cfg(feature = "stm32h7xx-hal")] + pub eth_mtl: ETHERNET_MTL, +} + impl<'rx, 'tx> EthernetDMA<'rx, 'tx> { + fn eth_dma(&self) -> ÐERNET_DMA { + &self.parts.eth_dma + } + /// Create and initialise the ethernet DMA /// /// # Note @@ -74,75 +92,140 @@ impl<'rx, 'tx> EthernetDMA<'rx, 'tx> { /// accessible by the peripheral. Core-Coupled Memory (CCM) is /// usually not accessible. pub(crate) fn new( - eth_dma: ETHERNET_DMA, + parts: DmaParts, rx_buffer: RxDescriptorRing<'rx>, tx_buffer: TxDescriptorRing<'tx>, ) -> Self { - // reset DMA bus mode register - eth_dma.dmabmr.modify(|_, w| w.sr().set_bit()); - - // Wait until done - while eth_dma.dmabmr.read().sr().bit_is_set() {} - - // operation mode register - eth_dma.dmaomr.modify(|_, w| { - // Dropping of TCP/IP checksum error frames disable - w.dtcefd() - .set_bit() - // Receive store and forward - .rsf() - .set_bit() - // Disable flushing of received frames - .dfrf() - .set_bit() - // Transmit store and forward - .tsf() - .set_bit() - // Forward error frames - .fef() - .set_bit() - // Operate on second frame - .osf() - .set_bit() - }); - - // bus mode register - eth_dma.dmabmr.modify(|_, w| { - // For any non-f107 chips, we must use enhanced descriptor format to support checksum - // offloading and/or timestamps. - #[cfg(not(feature = "stm32f1xx-hal"))] - let w = w.edfe().set_bit(); - - unsafe { - // Address-aligned beats - w.aab() + let DmaParts { + eth_dma, + #[cfg(feature = "stm32h7xx-hal")] + eth_mtl, + } = &parts; + + #[cfg(feature = "f-series")] + { + // reset DMA bus mode register + eth_dma.dmabmr.modify(|_, w| w.sr().set_bit()); + // Wait until done + while eth_dma.dmabmr.read().sr().bit_is_set() {} + + // operation mode register + eth_dma.dmaomr.modify(|_, w| { + // Dropping of TCP/IP checksum error frames disable + w.dtcefd() .set_bit() - // Fixed burst - .fb() + // Receive store and forward + .rsf() .set_bit() - // Rx DMA PBL - .rdp() - .bits(32) - // Programmable burst length - .pbl() - .bits(32) - // Rx Tx priority ratio 2:1 - .pm() - .bits(0b01) - // Use separate PBL - .usp() + // Disable flushing of received frames + .dfrf() .set_bit() - } - }); + // Transmit store and forward + .tsf() + .set_bit() + // Forward error frames + .fef() + .set_bit() + // Operate on second frame + .osf() + .set_bit() + }); + + // bus mode register + eth_dma.dmabmr.modify(|_, w| { + // For any non-f107 chips, we must use enhanced descriptor format to support checksum + // offloading and/or timestamps. + #[cfg(not(feature = "stm32f1xx-hal"))] + let w = w.edfe().set_bit(); + + unsafe { + // Address-aligned beats + w.aab() + .set_bit() + // Fixed burst + .fb() + .set_bit() + // Rx DMA PBL + .rdp() + .bits(32) + // Programmable burst length + .pbl() + .bits(32) + // Rx Tx priority ratio 2:1 + .pm() + .bits(0b01) + // Use separate PBL + .usp() + .set_bit() + } + }); + + // Configure word skip length. + eth_dma.dmabmr.modify(|_, w| w.dsl().bits(DESC_WORD_SKIP)); + } - // Configure word skip length. - eth_dma.dmabmr.modify(|_, w| w.dsl().bits(DESC_WORD_SKIP)); + #[cfg(feature = "stm32h7xx-hal")] + { + // reset DMA bus mode register + parts.eth_dma.dmamr.modify(|_, w| w.swr().set_bit()); + // Wait until done + while eth_dma.dmamr.read().swr().bit_is_set() {} + + // Rx Tx priority ratio 2:1 + eth_dma.dmamr.modify(|_, w| w.pr().variant(0b001)); + + eth_dma + .dmaccr + .modify(|_, w| w.dsl().variant(DESC_WORD_SKIP)); + + // Operation mode registers + eth_mtl.mtlrx_qomr.modify(|_, w| { + w + // Dropping of TCP/IP checksum error frames disable + .dis_tcp_ef() + .set_bit() + // Receive store and forward + .rsf() + .set_bit() + // Forward error frames + .fep() + .set_bit() + // Forward undersized but good frames + .fup() + .set_bit() + }); + + // Transmit store and forward + eth_mtl.mtltx_qomr.modify(|_, w| w.tsf().set_bit()); + + eth_dma.dmasbmr.modify(|_, w| w.aal().set_bit()); + + eth_dma.dmacrx_cr.modify(|_, w| { + w + // RX DMA programmable burst length. + .rxpbl() + .variant(32) + // Receive buffer size + .rbsz() + .variant(rx_buffer.first_buffer().len() as u16) + }); + + eth_dma.dmactx_cr.modify(|_, w| { + w + // TX DMA programmable burst length. + .txpbl() + .variant(32) + // Operate on second packet + .osf() + .set_bit() + }); + } let rx_ring = RxRing::new(rx_buffer).start(ð_dma); let tx_ring = TxRing::new(tx_buffer).start(ð_dma); EthernetDMA { - eth_dma, + parts, rx_ring, tx_ring, } @@ -151,11 +234,11 @@ impl<'rx, 'tx> EthernetDMA<'rx, 'tx> { /// Enable RX and TX interrupts /// /// In your handler you must call - /// [`eth_interrupt_handler()`](fn.eth_interrupt_handler.html) to - /// clear interrupt pending bits. Otherwise the interrupt will - /// reoccur immediately. + /// [`eth_interrupt_handler()`] to clear interrupt pending + /// bits. Otherwise the interrupt will reoccur immediately. pub fn enable_interrupt(&self) { - self.eth_dma.dmaier.modify(|_, w| { + #[cfg(feature = "f-series")] + self.eth_dma().dmaier.modify(|_, w| { w // Normal interrupt summary enable .nise() @@ -168,6 +251,20 @@ impl<'rx, 'tx> EthernetDMA<'rx, 'tx> { .set_bit() }); + #[cfg(feature = "stm32h7xx-hal")] + self.eth_dma().dmacier.modify(|_, w| { + w + // Normal interrupt summary enable + .nie() + .set_bit() + // Receive Interrupt Enable + .rie() + .set_bit() + // Transmit Interrupt Enable + .tie() + .set_bit() + }); + // Enable ethernet interrupts unsafe { NVIC::unmask(Interrupt::ETH); @@ -180,8 +277,7 @@ impl<'rx, 'tx> EthernetDMA<'rx, 'tx> { doc = " and collects/caches TX timestamps. (See [`EthernetDMA::get_timestamp_for_id`] for retrieval)" )] pub fn interrupt_handler(&mut self) -> InterruptReasonSummary { - let eth_dma = &self.eth_dma; - let status = eth_interrupt_handler_impl(eth_dma); + let status = eth_interrupt_handler_impl(self.eth_dma()); #[cfg(feature = "ptp")] self.collect_timestamps(); status @@ -192,12 +288,12 @@ impl<'rx, 'tx> EthernetDMA<'rx, 'tx> { /// It stops if the ring is full. Call `recv_next()` to free an /// entry and to demand poll from the hardware. pub fn rx_is_running(&self) -> bool { - self.rx_ring.running_state(&self.eth_dma).is_running() + self.rx_ring.running_state(self.eth_dma()).is_running() } - /// + /// Get the current state of Tx DMA pub fn tx_state(&self) -> RunningState { - self.tx_ring.running_state(&self.eth_dma) + self.tx_ring.running_state(self.eth_dma()) } fn recv_next_impl<'rx_borrow>( @@ -211,12 +307,12 @@ impl<'rx, 'tx> EthernetDMA<'rx, 'tx> { /// Receive the next packet (if any is ready), or return `None` /// immediately. pub fn recv_next(&mut self, packet_id: Option) -> Result { - Self::recv_next_impl(&self.eth_dma, &mut self.rx_ring, packet_id) + Self::recv_next_impl(&self.parts.eth_dma, &mut self.rx_ring, packet_id) } /// Is Tx DMA currently running? pub fn tx_is_running(&self) -> bool { - self.tx_ring.is_running(&self.eth_dma) + self.tx_ring.is_running(self.eth_dma()) } pub(crate) fn send_impl R, R>( @@ -228,6 +324,7 @@ impl<'rx, 'tx> EthernetDMA<'rx, 'tx> { ) -> Result { let result = tx_ring.send(length, tx_packet_id.map(|p| p.into()), f); tx_ring.demand_poll(eth_dma); + result } @@ -238,7 +335,7 @@ impl<'rx, 'tx> EthernetDMA<'rx, 'tx> { packet_id: Option, f: F, ) -> Result { - Self::send_impl(&self.eth_dma, &mut self.tx_ring, length, packet_id, f) + Self::send_impl(&self.parts.eth_dma, &mut self.tx_ring, length, packet_id, f) } #[cfg(feature = "ptp")] @@ -274,8 +371,8 @@ impl<'rx, 'tx> EthernetDMA<'rx, 'tx> { impl<'rx, 'tx> Drop for EthernetDMA<'rx, 'tx> { fn drop(&mut self) { - self.rx_ring.stop(&self.eth_dma); - self.tx_ring.stop(&self.eth_dma); + self.rx_ring.stop(&self.parts.eth_dma); + self.tx_ring.stop(&self.parts.eth_dma); } } @@ -304,17 +401,59 @@ pub fn eth_interrupt_handler(eth_dma: &crate::hal::pac::ETHERNET_DMA) -> Interru } fn eth_interrupt_handler_impl(eth_dma: ÐERNET_DMA) -> InterruptReasonSummary { - let status = eth_dma.dmasr.read(); + #[cfg(feature = "f-series")] + let (is_rx, is_tx, is_error) = { + // Read register + let status = eth_dma.dmasr.read(); + + // Reset bits + eth_dma.dmasr.write(|w| { + w.nis() + .set_bit() + .ts() + .set_bit() + .rs() + .set_bit() + .ais() + .set_bit() + }); - let status = InterruptReasonSummary { - is_rx: status.rs().bit_is_set(), - is_tx: status.ts().bit_is_set(), - is_error: status.ais().bit_is_set(), + ( + status.rs().bit_is_set(), + status.ts().bit_is_set(), + status.ais().bit_is_set(), + ) }; - eth_dma - .dmasr - .write(|w| w.nis().set_bit().ts().set_bit().rs().set_bit()); + #[cfg(feature = "stm32h7xx-hal")] + let (is_rx, is_tx, is_error) = { + // Read register + let status = eth_dma.dmacsr.read(); + + // Reset bits + eth_dma.dmacsr.write(|w| { + w.nis() + .set_bit() + .ais() + .set_bit() + .ti() + .set_bit() + .ri() + .set_bit() + }); + + ( + status.ri().bit_is_set(), + status.ti().bit_is_set(), + status.ais().bit_is_set(), + ) + }; + + let status = InterruptReasonSummary { + is_rx, + is_tx, + is_error, + }; status } diff --git a/src/dma/raw_descriptor.rs b/src/dma/raw_descriptor.rs index 5d124e1..ce847f1 100644 --- a/src/dma/raw_descriptor.rs +++ b/src/dma/raw_descriptor.rs @@ -2,14 +2,17 @@ use volatile_register::{RO, RW}; use crate::MTU; -#[cfg(not(feature = "stm32f1xx-hal"))] +#[cfg(all(not(feature = "stm32f1xx-hal"), feature = "f-series"))] pub(crate) const DESC_SIZE: usize = 8; #[cfg(feature = "stm32f1xx-hal")] pub(crate) const DESC_SIZE: usize = 4; +#[cfg(feature = "stm32h7xx-hal")] +pub(crate) const DESC_SIZE: usize = 4; + #[repr(C)] -#[repr(align(8))] +#[repr(align(4))] #[derive(Clone, Copy)] pub struct RawDescriptor { pub(crate) desc: [u32; DESC_SIZE], @@ -55,14 +58,16 @@ impl RawDescriptor { } pub struct DescriptorRing<'data, T> { - descriptors: &'data mut [T], - buffers: &'data mut [[u8; MTU]], + pub descriptors: &'data mut [T], + buffers: &'data mut [[u8; MTU + 2]], } impl<'data, T> DescriptorRing<'data, T> { - pub fn new(descriptors: &'data mut [T], buffers: &'data mut [[u8; MTU]]) -> Self { + pub fn new(descriptors: &'data mut [T], buffers: &'data mut [[u8; MTU + 2]]) -> Self { assert!(descriptors.len() == buffers.len()); - buffers.iter().for_each(|b| assert!(b.len() <= super::MTU)); + buffers + .iter() + .for_each(|b| assert!(b.len() <= super::MTU + 2)); Self { descriptors, @@ -82,11 +87,29 @@ impl<'data, T> DescriptorRing<'data, T> { self.descriptors.iter() } + pub fn last_descriptor_mut(&mut self) -> &mut T { + &mut self.descriptors[self.descriptors.len() - 1] + } + + pub fn last_descriptor(&self) -> &T { + &self.descriptors[self.descriptors.len() - 1] + } + + pub fn first_buffer(&self) -> &[u8] { + &self.buffers[0] + } + + pub fn last_buffer(&self) -> &[u8] { + &self.buffers[self.buffers.len() - 1] + } + pub fn descriptors_mut(&mut self) -> impl Iterator { self.descriptors.iter_mut() } - pub fn descriptors_and_buffers(&mut self) -> impl Iterator { + pub fn descriptors_and_buffers( + &mut self, + ) -> impl Iterator { self.descriptors.iter_mut().zip(self.buffers.iter_mut()) } diff --git a/src/dma/rx/descriptor.rs b/src/dma/rx/f_series_desc.rs similarity index 100% rename from src/dma/rx/descriptor.rs rename to src/dma/rx/f_series_desc.rs diff --git a/src/dma/rx/h_desc.rs b/src/dma/rx/h_desc.rs new file mode 100644 index 0000000..db1e0c9 --- /dev/null +++ b/src/dma/rx/h_desc.rs @@ -0,0 +1,247 @@ +use core::sync::atomic::{self, Ordering}; + +use crate::dma::{raw_descriptor::RawDescriptor, PacketId, RxError}; + +#[cfg(feature = "ptp")] +use crate::ptp::Timestamp; + +mod consts { + #![allow(unused)] + + /// Owned by DMA + pub const RXDESC_3_OWN: u32 = 1 << 31; + + // Read format bits + /// Interrupt On Completion + pub const RXDESC_3_IOC: u32 = 1 << 30; + /// Buffer 2 Address Valid + pub const RXDESC_3_BUF2V: u32 = 1 << 25; + /// Buffer 1 Address valid + pub const RXDESC_3_BUF1V: u32 = 1 << 24; + + // Write-back bits + /// Context Descriptor + pub const RXDESC_3_CTXT: u32 = 1 << 30; + /// First Descriptor + pub const RXDESC_3_FD: u32 = 1 << 29; + /// Last Descriptor + pub const RXDESC_3_LD: u32 = 1 << 28; + /// Receive Status RDES2 valid + pub const RXDESC_3_RS2V: u32 = 1 << 27; + /// Receive status RDES1 valid + pub const RXDESC_3_RS1V: u32 = 1 << 26; + /// Receive status RDES0 valid + pub const RXDESC_3_RS0V: u32 = 1 << 26; + /// CRC error + pub const RXDESC_3_CE: u32 = 1 << 24; + /// Giant Packet + pub const RXDESC_3_GP: u32 = 1 << 23; + /// Receive Watchdog Timeout + pub const RXDESC_3_RWT: u32 = 1 << 22; + /// Overflow Error + pub const RXDESC_3_OE: u32 = 1 << 21; + /// Receive Error + pub const RXDESC_3_RE: u32 = 1 << 20; + /// Dribble Bit Error + pub const RXDESC_3_DE: u32 = 1 << 19; + + /// Length/Type Field shift + pub const RXDESC_3_LT_SHIFT: u32 = 16; + /// Length/Type Field mask + pub const RXDESC_3_LT_MASK: u32 = 0b111 << RXDESC_3_LT_SHIFT; + /// Length/Type Field + #[allow(non_camel_case_types)] + #[repr(u8)] + pub enum RXDESC_3_LT { + Length = 0b000, + Type = 0b001, + Reserved = 0b010, + ArpRequest = 0b011, + TypeWithVlan = 0b100, + TypeWIthDoubleVlan = 0b101, + MacControl = 0b110, + Oam = 0b111, + } + + /// Error Summary + pub const RXDESC_3_ES: u32 = 1 << 15; + + /// Packet Length shift + pub const RXDESC_3_PL_SHIFT: u32 = 0; + /// Packet Length mask + pub const RXDESC_3_PL_MASK: u32 = 0x3FFF; +} +pub use consts::*; + +#[repr(C)] +#[repr(align(4))] +#[derive(Clone, Copy)] +/// An RX DMA Descriptor. +pub struct RxDescriptor { + inner_raw: RawDescriptor, + packet_id: Option, + #[cfg(feature = "ptp")] + cached_timestamp: Option, +} + +impl Default for RxDescriptor { + fn default() -> Self { + Self::new() + } +} + +impl RxDescriptor { + /// Creates a new [`RxDescriptor`]. + pub const fn new() -> Self { + Self { + inner_raw: RawDescriptor::new(), + packet_id: None, + #[cfg(feature = "ptp")] + cached_timestamp: None, + } + } + + pub(super) fn setup(&mut self, buffer: &[u8]) { + self.set_owned(buffer.as_ptr()); + } + + /// Is owned by the DMA engine? + fn is_owned(&self) -> bool { + (self.inner_raw.read(3) & RXDESC_3_OWN) == RXDESC_3_OWN + } + + /// Pass ownership to the DMA engine + pub(super) fn set_owned(&mut self, buffer: *const u8) { + self.set_buffer(buffer); + + // "Preceding reads and writes cannot be moved past subsequent writes." + #[cfg(feature = "fence")] + atomic::fence(Ordering::Release); + atomic::compiler_fence(Ordering::Release); + + unsafe { + self.inner_raw + .modify(3, |w| w | RXDESC_3_OWN | RXDESC_3_IOC); + } + + cortex_m::asm::dsb(); + + assert!(self.is_owned()); + + // Used to flush the store buffer as fast as possible to make the buffer available for the + // DMA. + #[cfg(feature = "fence")] + atomic::fence(Ordering::SeqCst); + } + + /// Configure the buffer and its length. + fn set_buffer(&mut self, buffer_ptr: *const u8) { + unsafe { + // Set buffer 1 address. + self.inner_raw.modify(0, |_| buffer_ptr as u32); + + // RXDESC does not contain buffer length, it is set + // in register INSERT_HERE instead. The size of all + // buffers is verified by [`TxRing`](super::TxRing) + + self.inner_raw.modify(3, |w| { + // BUF2 is not valid + let w = w & !(RXDESC_3_BUF2V); + // BUF1 is valid + let w = w | RXDESC_3_BUF1V; + w + }); + } + } + + fn has_error(&self) -> bool { + self.inner_raw.read(3) & RXDESC_3_ES == RXDESC_3_ES + } + + fn is_first(&self) -> bool { + self.inner_raw.read(3) & RXDESC_3_FD == RXDESC_3_FD + } + + fn is_last(&self) -> bool { + self.inner_raw.read(3) & RXDESC_3_LD == RXDESC_3_LD + } + + fn is_context(&self) -> bool { + self.inner_raw.read(3) & RXDESC_3_CTXT == RXDESC_3_CTXT + } + + pub(super) fn take_received( + &mut self, + // NOTE(allow): packet_id is unused if ptp is disabled. + #[allow(unused_variables)] packet_id: Option, + buffer: &mut [u8], + ) -> Result<(), RxError> { + if self.is_owned() { + Err(RxError::WouldBlock) + } else + // Only single-frame descriptors and non-context descriptors are supported + // for now. + if self.is_first() && self.is_last() && !self.has_error() && !self.is_context() { + // "Subsequent reads and writes cannot be moved ahead of preceding reads." + atomic::compiler_fence(Ordering::Acquire); + + self.packet_id = packet_id; + + // Cache the PTP timestamps if PTP is enabled. + #[cfg(feature = "ptp")] + self.attach_timestamp(); + + Ok(()) + } else { + self.set_owned(buffer.as_ptr()); + Err(RxError::Truncated) + } + } + + pub(super) fn frame_length(&self) -> usize { + if self.is_owned() { + 0 + } else { + ((self.inner_raw.read(3) & RXDESC_3_PL_MASK) >> RXDESC_3_PL_SHIFT) as usize + } + } + + #[allow(unused)] + pub(super) fn packet_id(&self) -> Option<&PacketId> { + self.packet_id.as_ref() + } +} + +#[cfg(feature = "ptp")] +impl RxDescriptor { + /// Get PTP timestamps if available + pub(super) fn read_timestamp(&self) -> Option { + #[cfg(not(feature = "stm32f1xx-hal"))] + let is_valid = { + /// RX timestamp + const RXDESC_0_TIMESTAMP_VALID: u32 = 1 << 7; + self.inner_raw.read(0) & RXDESC_0_TIMESTAMP_VALID == RXDESC_0_TIMESTAMP_VALID + }; + + #[cfg(feature = "stm32f1xx-hal")] + // There is no "timestamp valid" indicator bit + // on STM32F1XX + let is_valid = true; + + let timestamp = Timestamp::from_descriptor(&self.inner_raw); + + if is_valid && self.is_last() { + timestamp + } else { + None + } + } + + fn attach_timestamp(&mut self) { + self.cached_timestamp = self.read_timestamp(); + } + + pub(super) fn timestamp(&self) -> Option<&Timestamp> { + self.cached_timestamp.as_ref() + } +} diff --git a/src/dma/rx/mod.rs b/src/dma/rx/mod.rs index f60ae2f..cabbb8e 100644 --- a/src/dma/rx/mod.rs +++ b/src/dma/rx/mod.rs @@ -6,7 +6,16 @@ use crate::peripherals::ETHERNET_DMA; #[cfg(feature = "ptp")] use crate::{dma::TimestampError, ptp::Timestamp}; -mod descriptor; +#[cfg(feature = "f-series")] +mod f_series_desc; +#[cfg(feature = "f-series")] +use f_series_desc as descriptor; + +#[cfg(feature = "stm32h7xx-hal")] +mod h_desc; +#[cfg(feature = "stm32h7xx-hal")] +use h_desc as descriptor; + pub use descriptor::RxDescriptor; /// Errors that can occur during RX @@ -38,12 +47,24 @@ impl<'data, STATE> RxRing<'data, STATE> { /// Demand that the DMA engine polls the current `RxDescriptor` /// (when in `RunningState::Stopped`.) pub fn demand_poll(&self, eth_dma: ÐERNET_DMA) { + #[cfg(feature = "f-series")] eth_dma.dmarpdr.write(|w| unsafe { w.rpd().bits(1) }); + + // On H7, we poll by re-writing the tail pointer register. + #[cfg(feature = "stm32h7xx-hal")] + eth_dma + .dmacrx_dtpr + .modify(|r, w| unsafe { w.bits(r.bits()) }); } /// Get current `RunningState` pub fn running_state(&self, eth_dma: ÐERNET_DMA) -> RunningState { - match eth_dma.dmasr.read().rps().bits() { + #[cfg(feature = "f-series")] + let rps = eth_dma.dmasr.read().rps().bits(); + #[cfg(feature = "stm32h7xx-hal")] + let rps = eth_dma.dmadsr.read().rps0().bits(); + + match rps { // Reset or Stop Receive Command issued 0b000 => RunningState::Stopped, // Fetching receive transfer descriptor @@ -56,6 +77,9 @@ impl<'data, STATE> RxRing<'data, STATE> { 0b101 => RunningState::Running, // Transferring the receive packet data from receive buffer to host memory 0b111 => RunningState::Running, + #[cfg(feature = "stm32h7xx-hal")] + // Timestamp write state + 0b110 => RunningState::Running, _ => RunningState::Unknown, } } @@ -78,21 +102,67 @@ impl<'data> RxRing<'data, NotRunning> { entry.setup(buffer); } - self.ring - .descriptors_and_buffers() - .last() - .map(|(desc, _)| desc.set_end_of_ring()); - self.next_entry = 0; let ring_ptr = self.ring.descriptors_start_address(); - // Set the RxDma ring start address. - eth_dma - .dmardlar - .write(|w| unsafe { w.srl().bits(ring_ptr as u32) }); + #[cfg(feature = "f-series")] + { + self.ring.last_descriptor_mut().set_end_of_ring(); + // Set the RxDma ring start address. + eth_dma + .dmardlar + .write(|w| unsafe { w.srl().bits(ring_ptr as u32) }); + + // // Start receive + eth_dma.dmaomr.modify(|_, w| w.sr().set_bit()); + } - // Start receive - eth_dma.dmaomr.modify(|_, w| w.sr().set_bit()); + #[cfg(feature = "stm32h7xx-hal")] + { + // TODO: assert that ethernet DMA can access + // the memory in these rings + assert!(self.ring.descriptors().count() >= 4); + + // Assert that the descriptors are properly aligned. + assert!(ring_ptr as u32 & !0b11 == ring_ptr as u32); + assert!( + self.ring.last_descriptor_mut() as *const _ as u32 & !0b11 + == self.ring.last_descriptor_mut() as *const _ as u32 + ); + + // Set the start pointer. + eth_dma + .dmacrx_dlar + .write(|w| unsafe { w.bits(ring_ptr as u32) }); + + // Set the Receive Descriptor Ring Length + eth_dma.dmacrx_rlr.write(|w| { + w.rdrl() + .variant((self.ring.descriptors().count() - 1) as u16) + }); + + // Set the tail pointer + eth_dma + .dmacrx_dtpr + .write(|w| unsafe { w.bits(self.ring.last_descriptor() as *const _ as u32) }); + + // Set receive buffer size + let receive_buffer_size = self.ring.last_buffer().len() as u16; + assert!(receive_buffer_size % 4 == 0); + + eth_dma.dmacrx_cr.modify(|_, w| unsafe { + w + // Start receive + .sr() + .set_bit() + // Set receive buffer size + .rbsz() + .bits(receive_buffer_size >> 1) + // AUtomatically flush on bus error + .rpf() + .set_bit() + }); + } self.demand_poll(eth_dma); @@ -107,7 +177,13 @@ impl<'data> RxRing<'data, NotRunning> { impl<'data> RxRing<'data, Running> { /// Stop the DMA engine. pub fn stop(&mut self, eth_dma: ÐERNET_DMA) { - eth_dma.dmaomr.modify(|_, w| w.sr().clear_bit()); + #[cfg(feature = "f-series")] + let start_reg = ð_dma.dmaomr; + + #[cfg(feature = "stm32h7xx-hal")] + let start_reg = ð_dma.dmacrx_cr; + + start_reg.modify(|_, w| w.sr().clear_bit()); while self.running_state(eth_dma) != RunningState::Stopped {} } @@ -126,7 +202,7 @@ impl<'data> RxRing<'data, Running> { let entries_len = self.ring.len(); let (descriptor, buffer) = self.ring.get(self.next_entry); - let mut res = descriptor.take_received(packet_id); + let mut res = descriptor.take_received(packet_id, buffer); if res.as_mut().err() != Some(&mut RxError::WouldBlock) { self.next_entry = (self.next_entry + 1) % entries_len; @@ -204,7 +280,7 @@ impl<'a> core::ops::DerefMut for RxPacket<'a> { impl<'a> Drop for RxPacket<'a> { fn drop(&mut self) { - self.entry.set_owned(); + self.entry.set_owned(self.buffer.as_ptr()); } } diff --git a/src/dma/tx/descriptor.rs b/src/dma/tx/f_series_desc.rs similarity index 100% rename from src/dma/tx/descriptor.rs rename to src/dma/tx/f_series_desc.rs diff --git a/src/dma/tx/h_desc.rs b/src/dma/tx/h_desc.rs new file mode 100644 index 0000000..7810417 --- /dev/null +++ b/src/dma/tx/h_desc.rs @@ -0,0 +1,258 @@ +use core::sync::atomic::{self, Ordering}; + +use crate::dma::{raw_descriptor::RawDescriptor, PacketId}; + +#[cfg(feature = "ptp")] +use crate::ptp::Timestamp; + +mod consts { + + #![allow(unused)] + + // Both read and write-back formats + /// OWN bit + pub const TXDESC_3_OWN: u32 = 1 << 31; + /// Context Type + pub const TXDESC_3_CTXT: u32 = 1 << 30; + /// First descriptor + pub const TXDESC_3_FD: u32 = 1 << 29; + /// Last descriptor + pub const TXDESC_3_LD: u32 = 1 << 28; + + // Read format + /// Interrupt On Completion + pub const TXDESC_2_IOC: u32 = 1 << 31; + + /// Transmit Timestamp Enable + pub const TXDESC_2_TTSE: u32 = 1 << 30; + + /// Buffer 2 length shift + pub const TXDESC_2_B2L_SHIFT: u32 = 16; + /// Buffer 2 length mask + pub const TXDESC_2_B2L_MASK: u32 = 0x3FFF << TXDESC_2_B2L_SHIFT; + + /// VLAN Tag Insertion or Replacement shift + pub const TXDESC_2_VTIR_SHIFT: u32 = 14; + /// VLAN Tag Insertion or Replacement + #[repr(u32)] + #[allow(non_camel_case_types)] + pub enum TXDESC_2_VTIR { + DontAdd = 0b00 << 14, + RemoveTransmitVlanTag = 0b01 << TXDESC_2_VTIR_SHIFT, + InsertVlanTag = 0b10 << TXDESC_2_VTIR_SHIFT, + ReplaceVlanTag = 0b11 << TXDESC_2_VTIR_SHIFT, + } + /// VLAN Tag Insertion Or Replacement mask + pub const TXDESC_2_VTIR_MASK: u32 = 0b11 << TXDESC_2_VTIR_SHIFT; + + /// Header or Buffer 1 length shift + pub const TXDESC_2_HEAD_B1L_SHIFT: u32 = 0; + /// Header or Buffer 1 length mask + pub const TXDESC_2_HEAD_B1L_MASK: u32 = 0x3FFF << TXDESC_2_HEAD_B1L_SHIFT; + + // CRC Pad Control shift + pub const TXDESC_3_CPC_SHIFT: u32 = 26; + /// CRC Pad Control + #[repr(u32)] + #[allow(non_camel_case_types)] + pub enum TXDESC_3_CPC { + CRCAndPadInsertion = 0b00 << TXDESC_3_CPC_SHIFT, + CRCInsertionOnly = 0b01 << TXDESC_3_CPC_SHIFT, + Disabled = 0b10 << TXDESC_3_CPC_SHIFT, + CRCReplacement = 0b11 << TXDESC_3_CPC_SHIFT, + } + /// CRC Pad Control mask + pub const TXDESC_3_CPC_MASK: u32 = 0b11 << TXDESC_3_CPC_SHIFT; + + /// Checksum Insertion Control shift + pub const TXDESC_3_CIC_SHIFT: u32 = 16; + /// Checksum Insertion Control + #[repr(u32)] + #[allow(non_camel_case_types)] + pub enum TXDESC_3_CIC { + Disabled = 0b00, + IpHeaderOnly = 0b01, + IpHeaderAndPayloadOnly = 0b10, + IpHeaderAndPayloadAndPseudoHeader = 0b11, + } + /// Checksum Insertion Control mask + pub const TXDESC_3_CIC_MASK: u32 = 0b11 << TXDESC_3_CIC_SHIFT; + + /// Packet length shift + pub const TXDESC_3_FL_SHIFT: u32 = 0; + /// Packet length mask + pub const TXDESC_3_FL_MASK: u32 = 0x3FFF << TXDESC_3_FL_SHIFT; + + // Write back format + /// Tx Timestamp status + pub const TXDESC_3_TTSS: u32 = 1 << 17; + + /// Error Summary + pub const TXDESC_3_ES: u32 = 1 << 15; + + /// Jabber timeout + pub const TXDESC_3_JT: u32 = 1 << 14; + + /// Packet flushed + pub const TXDESC_3_FF: u32 = 1 << 13; + + /// Payload Checksum Error + pub const TXDESC_3_PCE: u32 = 1 << 12; + + /// Loss of Carrier + pub const TXDESC_3_LOC: u32 = 1 << 11; + + /// No Carrier + pub const TXDESC_3_NC: u32 = 1 << 10; + + /// Late Collision + pub const TXDESC_3_LC: u32 = 1 << 9; + + /// Excessive Collision + pub const TXDESC_3_EC: u32 = 1 << 8; + + /// Collision count shift + pub const TXDESC_3_CC_SHIFT: u32 = 4; + /// Collision Count mask + pub const TXDESC_3_CC_MASK: u32 = 0b1111 << TXDESC_3_CC_SHIFT; + + /// Excessive Deferral + pub const TXDESC_3_ED: u32 = 1 << 3; + + /// Underflow error + pub const TXDESC_3_UF: u32 = 1 << 2; + + /// Deferred Bit + pub const TXDESC_3_DB: u32 = 1 << 1; + + /// IP Header Error + pub const TXDESC_3_IHE: u32 = 1 << 0; +} +pub use consts::*; + +/// A TX DMA Ring Descriptor +#[repr(C, align(4))] +#[derive(Clone, Copy)] +pub struct TxDescriptor { + inner_raw: RawDescriptor, + packet_id: Option, + #[cfg(feature = "ptp")] + cached_timestamp: Option, +} + +impl Default for TxDescriptor { + fn default() -> Self { + Self::new() + } +} + +impl TxDescriptor { + /// Creates an zeroed TxDescriptor. + pub const fn new() -> Self { + Self { + inner_raw: RawDescriptor::new(), + packet_id: None, + #[cfg(feature = "ptp")] + cached_timestamp: None, + } + } + + // Placeholder for API parity with f-series descriptor. + pub(super) fn setup(&mut self, _: &[u8]) {} + + pub(super) fn is_owned(&self) -> bool { + (self.inner_raw.read(3) & TXDESC_3_OWN) == TXDESC_3_OWN + } + + /// Pass ownership to the DMA engine + pub(super) fn send(&mut self, packet_id: Option, buffer: &[u8]) { + self.set_buffer(buffer); + + if packet_id.is_some() && cfg!(feature = "ptp") { + unsafe { + self.inner_raw.modify(2, |w| w | TXDESC_2_TTSE); + } + } + + self.packet_id = packet_id; + + // "Preceding reads and writes cannot be moved past subsequent writes." + atomic::fence(Ordering::Release); + atomic::compiler_fence(Ordering::Release); + + unsafe { + self.inner_raw.modify(2, |w| w | TXDESC_2_IOC); + + let tx_len = ((buffer.len() as u32) << TXDESC_3_FL_SHIFT) & TXDESC_3_FL_MASK; + + self.inner_raw.modify(3, |w| { + w | TXDESC_3_OWN + | TXDESC_3_CIC::IpHeaderAndPayloadOnly as u32 + | TXDESC_3_FD + | TXDESC_3_LD + | tx_len + }) + }; + + // Used to flush the store buffer as fast as possible to make the buffer available for the + // DMA. + #[cfg(feature = "fence")] + atomic::fence(Ordering::SeqCst); + } + + /// Configure the buffer to use for transmitting, + /// setting it to `buffer`. + fn set_buffer(&mut self, buffer: &[u8]) { + unsafe { + let ptr = buffer.as_ptr(); + + // Set buffer pointer 1 to the provided buffer. + self.inner_raw.write(0, ptr as u32); + + self.inner_raw.modify(2, |w| { + // Clear out B1L + let w = w & !TXDESC_2_HEAD_B1L_MASK; + // Clear out B2L + let w = w & !TXDESC_2_B2L_MASK; + // Set B1L + w | ((buffer.len() as u32) << TXDESC_2_HEAD_B1L_SHIFT) & TXDESC_2_HEAD_B1L_MASK + }); + } + } + + // pub(super) fn packet_id(&self) -> Option<&PacketId> { + // self.packet_id.as_ref() + // } +} + +#[cfg(feature = "ptp")] +impl TxDescriptor { + fn read_timestamp(&mut self) -> Option { + let tdes0 = self.inner_raw.read(0); + + let contains_timestamp = (tdes0 & TXDESC_0_TIMESTAMP_STATUS) == TXDESC_0_TIMESTAMP_STATUS; + + if !self.is_owned() && contains_timestamp && Self::is_last(tdes0) { + Timestamp::from_descriptor(&self.inner_raw) + } else { + None + } + } + + pub(super) fn attach_timestamp(&mut self) { + self.cached_timestamp = self.read_timestamp(); + } + + pub(super) fn timestamp(&self) -> Option<&Timestamp> { + self.cached_timestamp.as_ref() + } +} + +impl TxDescriptor { + /// The initial value for a TxDescriptor + pub const TX_INIT: Self = Self::new(); + + pub(crate) fn prepare_packet(&mut self) -> bool { + !self.is_owned() + } +} diff --git a/src/dma/tx/mod.rs b/src/dma/tx/mod.rs index f73b7d1..6fe8ecc 100644 --- a/src/dma/tx/mod.rs +++ b/src/dma/tx/mod.rs @@ -6,7 +6,16 @@ use crate::peripherals::ETHERNET_DMA; #[cfg(feature = "ptp")] use super::{Timestamp, TimestampError}; -mod descriptor; +#[cfg(feature = "f-series")] +mod f_series_desc; +#[cfg(feature = "f-series")] +use f_series_desc as descriptor; + +#[cfg(feature = "stm32h7xx-hal")] +mod h_desc; +#[cfg(feature = "stm32h7xx-hal")] +use h_desc as descriptor; + pub use descriptor::TxDescriptor; pub struct NotRunning; @@ -31,7 +40,13 @@ pub(crate) struct TxRing<'data, STATE> { impl<'data, STATE> TxRing<'data, STATE> { pub fn running_state(&self, eth_dma: ÐERNET_DMA) -> RunningState { - match eth_dma.dmasr.read().tps().bits() { + #[cfg(feature = "f-series")] + let tx_status = eth_dma.dmasr().read().tps().bits(); + + #[cfg(feature = "stm32h7xx-hal")] + let tx_status = eth_dma.dmadsr.read().tps0().bits(); + + match tx_status { // Reset or Stop Transmit Command issued 0b000 => RunningState::Stopped, // Fetching transmit transfer descriptor @@ -40,9 +55,22 @@ impl<'data, STATE> TxRing<'data, STATE> { 0b010 => RunningState::Running, // Reading Data from host memory buffer and queuing it to transmit buffer 0b011 => RunningState::Running, - 0b100 | 0b101 => RunningState::Reserved, + + 0b101 => RunningState::Reserved, + // Transmit descriptor unavailable 0b110 => RunningState::Suspended, + + #[cfg(feature = "f-series")] + 0b100 => RunningState::Reserved, + + // Timestamp write + #[cfg(feature = "stm32h7xx-hal")] + 0b100 => RunningState::Running, + // Closing Tx descriptor + #[cfg(feature = "stm32h7xx-hal")] + 0b111 => RunningState::Running, + _ => RunningState::Unknown, } } @@ -67,19 +95,49 @@ impl<'data> TxRing<'data, NotRunning> { descriptor.setup(buffer); } - self.ring - .descriptors_and_buffers() - .last() - .map(|(desc, _)| desc.set_end_of_ring()); + #[cfg(feature = "f-series")] + // Set end of ring register + self.ring.last_descriptor().set_end_of_ring(); let ring_ptr = self.ring.descriptors_start_address(); + #[cfg(feature = "f-series")] // Register TxDescriptor eth_dma .dmatdlar // Note: unsafe block required for `stm32f107`. .write(|w| unsafe { w.stl().bits(ring_ptr as u32) }); + #[cfg(feature = "stm32h7xx-hal")] + { + // TODO: assert that ethernet DMA can access + // the memory in these rings + assert!(self.ring.descriptors().count() >= 4); + + // Assert that the descriptors are properly aligned. + assert!(ring_ptr as u32 & !0b11 == ring_ptr as u32); + assert!( + self.ring.last_descriptor_mut() as *const _ as u32 & !0b11 + == self.ring.last_descriptor_mut() as *const _ as u32 + ); + + // Set the start pointer. + eth_dma + .dmactx_dlar + .write(|w| unsafe { w.bits(ring_ptr as u32) }); + + // Set the Transmit Descriptor Ring Length + eth_dma.dmactx_rlr.write(|w| { + w.tdrl() + .variant((self.ring.descriptors().count() - 1) as u16) + }); + + // Set the tail pointer + eth_dma + .dmactx_dtpr + .write(|w| unsafe { w.bits(self.ring.last_descriptor_mut() as *const _ as u32) }); + } + // "Preceding reads and writes cannot be moved past subsequent writes." #[cfg(feature = "fence")] core::sync::atomic::fence(core::sync::atomic::Ordering::Release); @@ -87,8 +145,13 @@ impl<'data> TxRing<'data, NotRunning> { // We don't need a compiler fence here because all interactions with `Descriptor` are // volatiles + #[cfg(feature = "f-series")] + let start_reg = ð_dma.dmaomr; + #[cfg(feature = "stm32h7xx-hal")] + let start_reg = ð_dma.dmactx_cr; + // Start transmission - eth_dma.dmaomr.modify(|_, w| w.st().set_bit()); + start_reg.modify(|_, w| w.st().set_bit()); TxRing { ring: self.ring, @@ -128,6 +191,15 @@ impl<'data> TxRing<'data, Running> { /// Demand that the DMA engine polls the current `TxDescriptor` /// (when we just transferred ownership to the hardware). pub fn demand_poll(&self, eth_dma: ÐERNET_DMA) { + #[cfg(feature = "stm32h7xx-hal")] + // To issue a poll demand, write a value to + // the tail pointer. We just re-write the + // current value. + eth_dma + .dmactx_dtpr + .modify(|r, w| unsafe { w.bits(r.bits()) }); + + #[cfg(feature = "f-series")] eth_dma.dmatpdr.write(|w| { #[cfg(any(feature = "stm32f4xx-hal", feature = "stm32f7xx-hal"))] { @@ -147,8 +219,13 @@ impl<'data> TxRing<'data, Running> { } pub fn stop(&mut self, eth_dma: ÐERNET_DMA) { + #[cfg(feature = "f-series")] + let start_reg = ð_dma.dmaomr; + #[cfg(feature = "stm32h7xx-hal")] + let start_reg = ð_dma.dmactx_cr; + // Start transmission - eth_dma.dmaomr.modify(|_, w| w.st().clear_bit()); + start_reg.modify(|_, w| w.st().clear_bit()); while self.running_state(eth_dma) != RunningState::Stopped {} } @@ -158,7 +235,7 @@ impl<'data> TxRing<'data, Running> { impl<'data> TxRing<'data, Running> { pub(crate) fn collect_timestamps(&mut self) { for descriptor in self.ring.descriptors_mut() { - descriptor.attach_timestamp(); + f_descriptor.attach_timestamp(); } } @@ -166,12 +243,12 @@ impl<'data> TxRing<'data, Running> { let descriptor = if let Some(descriptor) = self.ring.descriptors().find(|d| d.packet_id() == Some(&id)) { - descriptor + f_descriptor } else { return Err(TimestampError::IdNotFound); }; - descriptor + f_descriptor .timestamp() .map(|t| *t) .ok_or(TimestampError::NotYetTimestamped) diff --git a/src/lib.rs b/src/lib.rs index cc63ff9..0951fb5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,9 @@ #[cfg(not(feature = "device-selected"))] compile_error!("No device was selected! Exactly one stm32fxxx feature must be selected."); +#[cfg(feature = "stm32h7xx-hal")] +pub use stm32h7xx_hal as hal; + /// Re-export #[cfg(feature = "stm32f7xx-hal")] pub use stm32f7xx_hal as hal; @@ -21,8 +24,10 @@ pub use stm32f1xx_hal as hal; #[cfg(feature = "device-selected")] pub use hal::pac as stm32; -#[cfg(feature = "device-selected")] +#[cfg(all(feature = "device-selected", not(feature = "stm32h7xx-hal")))] use hal::rcc::Clocks; +#[cfg(all(feature = "device-selected", feature = "stm32h7xx-hal"))] +use hal::rcc::CoreClocks as Clocks; #[cfg(feature = "device-selected")] pub mod dma; @@ -50,11 +55,14 @@ pub use smoltcp; #[cfg(feature = "device-selected")] use { - dma::{EthernetDMA, RxDescriptorRing, TxDescriptorRing}, - mac::{EthernetMAC, EthernetMACWithMii, MdcPin, MdioPin, Speed, WrongClock}, + dma::{DmaParts, EthernetDMA, RxDescriptorRing, TxDescriptorRing}, + mac::{EthernetMAC, Speed, WrongClock}, setup::*, }; +#[cfg(all(feature = "device-selected", feature = "f-series"))] +use mac::{EthernetMACWithMii, MdcPin, MdioPin}; + #[cfg(all(feature = "device-selected", feature = "ptp"))] use ptp::EthernetPTP; @@ -103,14 +111,27 @@ where let eth_mac = parts.mac.into(); // Congfigure and start up the ethernet DMA. - let dma = EthernetDMA::new(parts.dma.into(), rx_buffer, tx_buffer); + let dma = EthernetDMA::new( + DmaParts { + eth_dma: parts.dma, + #[cfg(feature = "stm32h7xx-hal")] + eth_mtl: parts.mtl, + }, + rx_buffer, + tx_buffer, + ); // Configure the ethernet PTP #[cfg(feature = "ptp")] let ptp = EthernetPTP::new(parts.ptp.into(), clocks, &dma); + #[cfg(not(feature = "stm32h7xx-hal"))] + let mmc = parts.mmc; + #[cfg(feature = "stm32h7xx-hal")] + let mmc = (); + // Configure the ethernet MAC - let mac = EthernetMAC::new(eth_mac, parts.mmc, clocks, Speed::FullDuplexBase100Tx, &dma)?; + let mac = EthernetMAC::new(eth_mac, mmc, clocks, Speed::FullDuplexBase100Tx, &dma)?; let parts = Parts { mac, @@ -142,7 +163,7 @@ where /// accessible by the peripheral. Core-Coupled Memory (CCM) is /// usually not accessible. /// - HCLK must be at least 25 MHz. -#[cfg(feature = "device-selected")] +#[cfg(all(feature = "device-selected", feature = "f-series"))] pub fn new_with_mii<'rx, 'tx, REFCLK, CRS, TXEN, TXD0, TXD1, RXD0, RXD1, MDIO, MDC>( parts: PartsIn, rx_buffer: RxDescriptorRing<'rx>, diff --git a/src/mac/miim.rs b/src/mac/miim.rs index 635898f..e6cb0f0 100644 --- a/src/mac/miim.rs +++ b/src/mac/miim.rs @@ -146,3 +146,96 @@ mod pin_impls { unsafe impl super::MdioPin for PA2> {} unsafe impl super::MdcPin for PC1> {} } + +/// Ethernet media access control (MAC) with owned MII +/// +/// This version of the struct owns it's MII pins, +/// allowing it to be used directly, instead of requiring +/// that a [`Miim`] is created. +pub struct EthernetMACWithMii +where + MDIO: MdioPin, + MDC: MdcPin, +{ + pub(crate) eth_mac: EthernetMAC, + mdio: MDIO, + mdc: MDC, +} + +impl EthernetMACWithMii +where + MDIO: MdioPin, + MDC: MdcPin, +{ + /// Create a new EthernetMAC with owned MDIO and MDC pins. + /// + /// To interact with a connected Phy, use the `read` and `write` functions. + /// + /// Functionality for interacting with PHYs from the `ieee802_3_miim` crate + /// is available. + pub fn new(eth_mac: EthernetMAC, mdio: MDIO, mdc: MDC) -> Self { + Self { eth_mac, mdio, mdc } + } + + /// Release the owned MDIO and MDC pins, and return an EthernetMAC that + /// has to borrow the MDIO and MDC pins. + pub fn release_pins(self) -> (EthernetMAC, MDIO, MDC) { + (self.eth_mac, self.mdio, self.mdc) + } +} + +impl Deref for EthernetMACWithMii +where + MDIO: MdioPin, + MDC: MdcPin, +{ + type Target = EthernetMAC; + + fn deref(&self) -> &Self::Target { + &self.eth_mac + } +} + +impl DerefMut for EthernetMACWithMii +where + MDIO: MdioPin, + MDC: MdcPin, +{ + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.eth_mac + } +} + +impl EthernetMACWithMii +where + MDIO: MdioPin, + MDC: MdcPin, +{ + /// Read MII register `reg` from the PHY at address `phy` + pub fn read(&mut self, phy: u8, reg: u8) -> u16 { + self.eth_mac + .mii(&mut self.mdio, &mut self.mdc) + .read(phy, reg) + } + + /// Write the value `data` to MII register `reg` to the PHY at address `phy` + pub fn write(&mut self, phy: u8, reg: u8, data: u16) { + self.eth_mac + .mii(&mut self.mdio, &mut self.mdc) + .write(phy, reg, data) + } +} + +impl miim::Miim for EthernetMACWithMii +where + MDIO: MdioPin, + MDC: MdcPin, +{ + fn read(&mut self, phy: u8, reg: u8) -> u16 { + self.read(phy, reg) + } + + fn write(&mut self, phy: u8, reg: u8, data: u16) { + self.write(phy, reg, data) + } +} diff --git a/src/mac/mod.rs b/src/mac/mod.rs index 88ce38e..c00c108 100644 --- a/src/mac/mod.rs +++ b/src/mac/mod.rs @@ -1,10 +1,17 @@ //! Ethernet MAC access and configuration. -use core::ops::{Deref, DerefMut}; +use crate::{dma::EthernetDMA, peripherals::ETHERNET_MAC, Clocks}; -use crate::{dma::EthernetDMA, hal::rcc::Clocks, peripherals::ETHERNET_MAC, stm32::ETHERNET_MMC}; +#[cfg(feature = "f-series")] +use crate::stm32::ETHERNET_MMC; +#[cfg(feature = "stm32h7xx-hal")] +#[allow(non_camel_case_types)] +type ETHERNET_MMC = (); + +#[cfg(feature = "f-series")] mod miim; +#[cfg(feature = "f-series")] pub use miim::*; /// Speeds at which this MAC can be configured @@ -21,6 +28,9 @@ pub enum Speed { FullDuplexBase100Tx, } +#[cfg(feature = "f-series")] +use self::consts::*; +#[cfg(feature = "f-series")] mod consts { /* For HCLK 60-100 MHz */ pub const ETH_MACMIIAR_CR_HCLK_DIV_42: u8 = 0; @@ -33,7 +43,6 @@ mod consts { /* For HCLK over 150 MHz */ pub const ETH_MACMIIAR_CR_HCLK_DIV_102: u8 = 4; } -use self::consts::*; /// HCLK must be at least 25MHz to use the ethernet peripheral. /// This (empty) struct is returned to indicate that it is not set @@ -69,21 +78,21 @@ impl EthernetMAC { // it doesn't work. _dma: &EthernetDMA, ) -> Result { - let clock_frequency = clocks.hclk().to_Hz(); - - let clock_range = match clock_frequency { - 0..=24_999_999 => return Err(WrongClock), - 25_000_000..=34_999_999 => ETH_MACMIIAR_CR_HCLK_DIV_16, - 35_000_000..=59_999_999 => ETH_MACMIIAR_CR_HCLK_DIV_26, - 60_000_000..=99_999_999 => ETH_MACMIIAR_CR_HCLK_DIV_42, - 100_000_000..=149_999_999 => ETH_MACMIIAR_CR_HCLK_DIV_62, - _ => ETH_MACMIIAR_CR_HCLK_DIV_102, - }; + // let clock_frequency = clocks.hclk().to_Hz(); + // TODO: configure MDIOS + // let clock_range = match clock_frequency { + // 0..=24_999_999 => return Err(WrongClock), + // 25_000_000..=34_999_999 => ETH_MACMIIAR_CR_HCLK_DIV_16, + // 35_000_000..=59_999_999 => ETH_MACMIIAR_CR_HCLK_DIV_26, + // 60_000_000..=99_999_999 => ETH_MACMIIAR_CR_HCLK_DIV_42, + // 100_000_000..=149_999_999 => ETH_MACMIIAR_CR_HCLK_DIV_62, + // _ => ETH_MACMIIAR_CR_HCLK_DIV_102, + // }; // Set clock range in MAC MII address register - eth_mac - .macmiiar - .modify(|_, w| unsafe { w.cr().bits(clock_range) }); + // eth_mac + // .macmiiar + // .modify(|_, w| unsafe { w.cr().bits(clock_range) }); // Configuration Register eth_mac.maccr.modify(|_, w| { @@ -91,12 +100,8 @@ impl EthernetMAC { #[cfg(any(feature = "stm32f4xx-hal", feature = "stm32f7xx-hal"))] let w = w.cstf().set_bit(); - // Fast Ethernet speed - w.fes() - .set_bit() - // Duplex mode - .dm() - .set_bit() + #[cfg(feature = "f-series")] + let w = w // IPv4 checksum offload .ipco() .set_bit() @@ -105,6 +110,25 @@ impl EthernetMAC { .set_bit() // Retry disable in half-duplex mode .rd() + .set_bit(); + + #[cfg(feature = "stm32h7xx-hal")] + let w = w + // IPv4 checksum offload + .acs() + .set_bit() + // Automatic pad/CRC stripping + .ipc() + .set_bit() + // Retry disable in half-duplex mode + .dr() + .set_bit(); + + // Fast Ethernet speed + w.fes() + .set_bit() + // Duplex mode + .dm() .set_bit() // Receiver enable .re() @@ -114,8 +138,13 @@ impl EthernetMAC { .set_bit() }); + #[cfg(feature = "f-series")] + let (mac_filter_reg, flow_control) = (ð_mac.macffr, ð_mac.macfcr); + #[cfg(feature = "stm32h7xx-hal")] + let (mac_filter, flow_control) = (ð_mac.macpfr, ð_mac.macqtx_fcr); + // Frame filter register - eth_mac.macffr.modify(|_, w| { + mac_filter.modify(|_, w| { // Receive All w.ra() .set_bit() @@ -123,28 +152,64 @@ impl EthernetMAC { .pm() .set_bit() }); - // Flow Control Register - eth_mac.macfcr.modify(|_, w| { + flow_control.modify(|_, w| { // Pause time - w.pt().bits(0x100) + #[allow(unused_unsafe)] + unsafe { + w.pt().bits(0x100) + } }); - // Disable all MMC RX interrupts - eth_mmc - .mmcrimr - .write(|w| w.rgufm().set_bit().rfaem().set_bit().rfcem().set_bit()); - - // Disable all MMC TX interrupts - eth_mmc - .mmctimr - .write(|w| w.tgfm().set_bit().tgfmscm().set_bit().tgfscm().set_bit()); + #[cfg(feature = "f-series")] + { + // Disable all MMC RX interrupts + eth_mmc + .mmcrimr + .write(|w| w.rgufm().set_bit().rfaem().set_bit().rfcem().set_bit()); + + // Disable all MMC TX interrupts + eth_mmc + .mmctimr + .write(|w| w.tgfm().set_bit().tgfmscm().set_bit().tgfscm().set_bit()); + + // Fix incorrect TGFM bit position until https://github.com/stm32-rs/stm32-rs/pull/689 + // is released and used by HALs. + eth_mmc + .mmctimr + .modify(|r, w| unsafe { w.bits(r.bits() | (1 << 21)) }); + } - // Fix incorrect TGFM bit position until https://github.com/stm32-rs/stm32-rs/pull/689 - // is released and used by HALs. - eth_mmc - .mmctimr - .modify(|r, w| unsafe { w.bits(r.bits() | (1 << 21)) }); + #[cfg(feature = "stm32h7xx-hal")] + { + // Disable all MMC RX interrupts + eth_mac.mmc_rx_interrupt_mask.write(|w| { + w.rxlpitrcim() + .set_bit() + .rxlpiuscim() + .set_bit() + .rxucgpim() + .set_bit() + .rxalgnerpim() + .set_bit() + .rxcrcerpim() + .set_bit() + }); + + // Disable all MMC TX interrupts + eth_mac.mmc_tx_interrupt_mask.write(|w| { + w.txlpitrcim() + .set_bit() + .txlpiuscim() + .set_bit() + .txgpktim() + .set_bit() + .txmcolgpim() + .set_bit() + .txscolgpim() + .set_bit() + }); + } let mut me = Self { eth_mac }; @@ -160,6 +225,7 @@ impl EthernetMAC { /// /// Exclusive access to the `MDIO` and `MDC` is required to ensure that are not used elsewhere /// for the duration of Mii communication. + #[cfg(feature = "f-series")] pub fn mii<'eth, 'pins, Mdio, Mdc>( &'eth mut self, mdio: &'pins mut Mdio, @@ -173,6 +239,7 @@ impl EthernetMAC { } /// Turn this [`EthernetMAC`] into an [`EthernetMACWithMii`] + #[cfg(feature = "f-series")] pub fn with_mii(self, mdio: MDIO, mdc: MDC) -> EthernetMACWithMii where MDIO: MdioPin, @@ -226,96 +293,3 @@ impl EthernetMAC { macimr.write(|w| w.tstim().clear_bit()); } } - -/// Ethernet media access control (MAC) with owned MII -/// -/// This version of the struct owns it's MII pins, -/// allowing it to be used directly, instead of requiring -/// that a [`Miim`] is created. -pub struct EthernetMACWithMii -where - MDIO: MdioPin, - MDC: MdcPin, -{ - pub(crate) eth_mac: EthernetMAC, - mdio: MDIO, - mdc: MDC, -} - -impl EthernetMACWithMii -where - MDIO: MdioPin, - MDC: MdcPin, -{ - /// Create a new EthernetMAC with owned MDIO and MDC pins. - /// - /// To interact with a connected Phy, use the `read` and `write` functions. - /// - /// Functionality for interacting with PHYs from the `ieee802_3_miim` crate - /// is available. - pub fn new(eth_mac: EthernetMAC, mdio: MDIO, mdc: MDC) -> Self { - Self { eth_mac, mdio, mdc } - } - - /// Release the owned MDIO and MDC pins, and return an EthernetMAC that - /// has to borrow the MDIO and MDC pins. - pub fn release_pins(self) -> (EthernetMAC, MDIO, MDC) { - (self.eth_mac, self.mdio, self.mdc) - } -} - -impl Deref for EthernetMACWithMii -where - MDIO: MdioPin, - MDC: MdcPin, -{ - type Target = EthernetMAC; - - fn deref(&self) -> &Self::Target { - &self.eth_mac - } -} - -impl DerefMut for EthernetMACWithMii -where - MDIO: MdioPin, - MDC: MdcPin, -{ - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.eth_mac - } -} - -impl EthernetMACWithMii -where - MDIO: MdioPin, - MDC: MdcPin, -{ - /// Read MII register `reg` from the PHY at address `phy` - pub fn read(&mut self, phy: u8, reg: u8) -> u16 { - self.eth_mac - .mii(&mut self.mdio, &mut self.mdc) - .read(phy, reg) - } - - /// Write the value `data` to MII register `reg` to the PHY at address `phy` - pub fn write(&mut self, phy: u8, reg: u8, data: u16) { - self.eth_mac - .mii(&mut self.mdio, &mut self.mdc) - .write(phy, reg, data) - } -} - -impl miim::Miim for EthernetMACWithMii -where - MDIO: MdioPin, - MDC: MdcPin, -{ - fn read(&mut self, phy: u8, reg: u8) -> u16 { - self.read(phy, reg) - } - - fn write(&mut self, phy: u8, reg: u8, data: u16) { - self.write(phy, reg, data) - } -} diff --git a/src/peripherals.rs b/src/peripherals.rs index c645ac0..dbeef4e 100644 --- a/src/peripherals.rs +++ b/src/peripherals.rs @@ -4,6 +4,9 @@ #[cfg(any(feature = "stm32f107", feature = "stm32f7xx-hal"))] pub use crate::hal::pac::{ETHERNET_DMA, ETHERNET_MAC, ETHERNET_PTP}; +#[cfg(feature = "stm32h7xx-hal")] +pub use crate::hal::pac::{ETHERNET_DMA, ETHERNET_MAC}; + #[cfg(feature = "stm32f4xx-hal")] pub use pac_override_impl::{ETHERNET_DMA, ETHERNET_MAC, ETHERNET_PTP}; diff --git a/src/setup.rs b/src/setup.rs index 7397bbd..20282d4 100644 --- a/src/setup.rs +++ b/src/setup.rs @@ -18,7 +18,7 @@ use stm32f4xx_hal::{ pac::{RCC, SYSCFG}, }; -#[cfg(feature = "stm32f7xx-hal")] +#[cfg(any(feature = "stm32f7xx-hal", feature = "stm32h7xx-hal"))] use cortex_m::interrupt; #[cfg(feature = "stm32f7xx-hal")] @@ -34,16 +34,59 @@ use stm32f7xx_hal::{ pac::{RCC, SYSCFG}, }; +#[cfg(not(feature = "stm32h7xx-hal"))] use crate::{ dma::EthernetDMA, stm32::{ETHERNET_DMA, ETHERNET_MAC, ETHERNET_MMC}, }; +#[cfg(feature = "stm32h7xx-hal")] +// TODO: implement all allowed GPIO pins. +#[allow(unused_imports)] +use crate::{ + dma::EthernetDMA, + hal::gpio::{ + gpioa::{PA0, PA1, PA2, PA3, PA7}, + gpiob::{PB0, PB1, PB10, PB11, PB12, PB13, PB5, PB8}, + gpioc::{PC1, PC2, PC3, PC4, PC5}, + gpiog::{PG11, PG13}, + Input, + Speed::VeryHigh, + }, + stm32::{ETHERNET_DMA, ETHERNET_MAC, ETHERNET_MTL, RCC, SYSCFG}, +}; + #[cfg(feature = "ptp")] use crate::{ptp::EthernetPTP, stm32::ETHERNET_PTP}; // Enable syscfg and ethernet clocks. Reset the Ethernet MAC. pub(crate) fn setup() { + #[cfg(feature = "stm32h7xx-hal")] + interrupt::free(|_| { + // SAFETY: we only perform interrupt-free modifications of RCC. + let rcc = unsafe { &*RCC::ptr() }; + let syscfg = unsafe { &*SYSCFG::ptr() }; + + rcc.apb4enr.modify(|_, w| w.syscfgen().set_bit()); + rcc.ahb1enr.modify(|_, w| { + w.eth1macen() + .set_bit() + .eth1rxen() + .set_bit() + .eth1txen() + .set_bit() + }); + + // Select RMII mode + // + // SAFETY: this is the correct value for RMII mode. + syscfg.pmcr.modify(|_, w| unsafe { w.epis().bits(0b100) }); + + // Reset pulse to MAC. + rcc.ahb1rstr.modify(|_, w| w.eth1macrst().set_bit()); + rcc.ahb1rstr.modify(|_, w| w.eth1macrst().clear_bit()); + }); + #[cfg(feature = "stm32f4xx-hal")] unsafe { const SYSCFG_BIT: u8 = 14; @@ -76,6 +119,7 @@ pub(crate) fn setup() { bb::set(&rcc.ahb1rstr, ETH_MAC_BIT); bb::clear(&rcc.ahb1rstr, ETH_MAC_BIT); } + #[cfg(feature = "stm32f7xx-hal")] //stm32f7xx-hal does not currently have bitbanding interrupt::free(|_| unsafe { @@ -189,33 +233,16 @@ pub trait AlternateVeryHighSpeed { #[allow(missing_docs)] pub struct PartsIn { pub mac: ETHERNET_MAC, - pub mmc: ETHERNET_MMC, pub dma: ETHERNET_DMA, - #[cfg(feature = "ptp")] - pub ptp: ETHERNET_PTP, -} -#[cfg(feature = "ptp")] -impl From<(ETHERNET_MAC, ETHERNET_MMC, ETHERNET_DMA, ETHERNET_PTP)> for PartsIn { - fn from(value: (ETHERNET_MAC, ETHERNET_MMC, ETHERNET_DMA, ETHERNET_PTP)) -> Self { - Self { - mac: value.0, - mmc: value.1, - dma: value.2, - ptp: value.3, - } - } -} + #[cfg(feature = "f-series")] + pub mmc: ETHERNET_MMC, -#[cfg(not(feature = "ptp"))] -impl From<(ETHERNET_MAC, ETHERNET_MMC, ETHERNET_DMA)> for PartsIn { - fn from(value: (ETHERNET_MAC, ETHERNET_MMC, ETHERNET_DMA)) -> Self { - Self { - mac: value.0, - mmc: value.1, - dma: value.2, - } - } + #[cfg(feature = "stm32h7xx-hal")] + pub mtl: ETHERNET_MTL, + + #[cfg(feature = "ptp")] + pub ptp: ETHERNET_PTP, } /// Access to all configured parts of the ethernet peripheral. @@ -305,6 +332,33 @@ macro_rules! impl_pins { }; } +#[cfg(feature = "stm32h7xx-hal")] +impl_pins!( + RmiiRefClk: [ + PA1, + ], + RmiiCrsDv: [ + PA7, + ], + RmiiTxEN: [ + PB11, + PG11, + ], + RmiiTxD0: [ + PB12, + PG13, + ], + RmiiTxD1: [ + PB13, + ], + RmiiRxD0: [ + PC4, + ], + RmiiRxD1: [ + PC5, + ], +); + #[cfg(any(feature = "stm32f4xx-hal", feature = "stm32f7xx-hal"))] impl_pins!( RmiiRefClk: [ From a2e3061f2e68b20297165e50dcfe33fd08f59792 Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Sat, 4 Feb 2023 14:23:41 +0100 Subject: [PATCH 07/35] Update some things --- src/dma/rx/f_series_desc.rs | 6 +- src/dma/rx/h_desc.rs | 69 ++++++----- src/dma/tx/h_desc.rs | 22 +--- src/lib.rs | 29 +++-- src/mac/mod.rs | 220 ++++++++++++++++++++---------------- 5 files changed, 181 insertions(+), 165 deletions(-) diff --git a/src/dma/rx/f_series_desc.rs b/src/dma/rx/f_series_desc.rs index de5f92a..f33f90b 100644 --- a/src/dma/rx/f_series_desc.rs +++ b/src/dma/rx/f_series_desc.rs @@ -125,11 +125,7 @@ impl RxDescriptor { ((self.inner_raw.read(0) >> RXDESC_0_FL_SHIFT) & RXDESC_0_FL_MASK) as usize } - pub(super) fn take_received( - &mut self, - // NOTE(allow): packet_id is unused if ptp is disabled. - #[allow(unused_variables)] packet_id: Option, - ) -> Result<(), RxError> { + pub(super) fn take_received(&mut self, packet_id: Option) -> Result<(), RxError> { if self.is_owned() { Err(RxError::WouldBlock) } else if self.has_error() { diff --git a/src/dma/rx/h_desc.rs b/src/dma/rx/h_desc.rs index db1e0c9..0a4898a 100644 --- a/src/dma/rx/h_desc.rs +++ b/src/dma/rx/h_desc.rs @@ -101,15 +101,44 @@ impl RxDescriptor { } } - pub(super) fn setup(&mut self, buffer: &[u8]) { - self.set_owned(buffer.as_ptr()); - } - /// Is owned by the DMA engine? fn is_owned(&self) -> bool { (self.inner_raw.read(3) & RXDESC_3_OWN) == RXDESC_3_OWN } + fn has_error(&self) -> bool { + self.inner_raw.read(3) & RXDESC_3_ES == RXDESC_3_ES + } + + fn is_first(&self) -> bool { + self.inner_raw.read(3) & RXDESC_3_FD == RXDESC_3_FD + } + + fn is_last(&self) -> bool { + self.inner_raw.read(3) & RXDESC_3_LD == RXDESC_3_LD + } + + fn is_context(&self) -> bool { + self.inner_raw.read(3) & RXDESC_3_CTXT == RXDESC_3_CTXT + } + + pub(super) fn frame_length(&self) -> usize { + if self.is_owned() { + 0 + } else { + ((self.inner_raw.read(3) & RXDESC_3_PL_MASK) >> RXDESC_3_PL_SHIFT) as usize + } + } + + #[allow(unused)] + pub(super) fn packet_id(&self) -> Option<&PacketId> { + self.packet_id.as_ref() + } + + pub(super) fn setup(&mut self, buffer: &[u8]) { + self.set_owned(buffer.as_ptr()); + } + /// Pass ownership to the DMA engine pub(super) fn set_owned(&mut self, buffer: *const u8) { self.set_buffer(buffer); @@ -154,26 +183,9 @@ impl RxDescriptor { } } - fn has_error(&self) -> bool { - self.inner_raw.read(3) & RXDESC_3_ES == RXDESC_3_ES - } - - fn is_first(&self) -> bool { - self.inner_raw.read(3) & RXDESC_3_FD == RXDESC_3_FD - } - - fn is_last(&self) -> bool { - self.inner_raw.read(3) & RXDESC_3_LD == RXDESC_3_LD - } - - fn is_context(&self) -> bool { - self.inner_raw.read(3) & RXDESC_3_CTXT == RXDESC_3_CTXT - } - pub(super) fn take_received( &mut self, - // NOTE(allow): packet_id is unused if ptp is disabled. - #[allow(unused_variables)] packet_id: Option, + packet_id: Option, buffer: &mut [u8], ) -> Result<(), RxError> { if self.is_owned() { @@ -197,19 +209,6 @@ impl RxDescriptor { Err(RxError::Truncated) } } - - pub(super) fn frame_length(&self) -> usize { - if self.is_owned() { - 0 - } else { - ((self.inner_raw.read(3) & RXDESC_3_PL_MASK) >> RXDESC_3_PL_SHIFT) as usize - } - } - - #[allow(unused)] - pub(super) fn packet_id(&self) -> Option<&PacketId> { - self.packet_id.as_ref() - } } #[cfg(feature = "ptp")] diff --git a/src/dma/tx/h_desc.rs b/src/dma/tx/h_desc.rs index 7810417..d62ce33 100644 --- a/src/dma/tx/h_desc.rs +++ b/src/dma/tx/h_desc.rs @@ -22,10 +22,8 @@ mod consts { // Read format /// Interrupt On Completion pub const TXDESC_2_IOC: u32 = 1 << 31; - /// Transmit Timestamp Enable pub const TXDESC_2_TTSE: u32 = 1 << 30; - /// Buffer 2 length shift pub const TXDESC_2_B2L_SHIFT: u32 = 16; /// Buffer 2 length mask @@ -86,28 +84,20 @@ mod consts { // Write back format /// Tx Timestamp status pub const TXDESC_3_TTSS: u32 = 1 << 17; - /// Error Summary pub const TXDESC_3_ES: u32 = 1 << 15; - /// Jabber timeout pub const TXDESC_3_JT: u32 = 1 << 14; - /// Packet flushed pub const TXDESC_3_FF: u32 = 1 << 13; - /// Payload Checksum Error pub const TXDESC_3_PCE: u32 = 1 << 12; - /// Loss of Carrier pub const TXDESC_3_LOC: u32 = 1 << 11; - /// No Carrier pub const TXDESC_3_NC: u32 = 1 << 10; - /// Late Collision pub const TXDESC_3_LC: u32 = 1 << 9; - /// Excessive Collision pub const TXDESC_3_EC: u32 = 1 << 8; @@ -118,13 +108,10 @@ mod consts { /// Excessive Deferral pub const TXDESC_3_ED: u32 = 1 << 3; - /// Underflow error pub const TXDESC_3_UF: u32 = 1 << 2; - /// Deferred Bit pub const TXDESC_3_DB: u32 = 1 << 1; - /// IP Header Error pub const TXDESC_3_IHE: u32 = 1 << 0; } @@ -164,6 +151,11 @@ impl TxDescriptor { (self.inner_raw.read(3) & TXDESC_3_OWN) == TXDESC_3_OWN } + #[allow(unused)] + pub(super) fn packet_id(&self) -> Option<&PacketId> { + self.packet_id.as_ref() + } + /// Pass ownership to the DMA engine pub(super) fn send(&mut self, packet_id: Option, buffer: &[u8]) { self.set_buffer(buffer); @@ -219,10 +211,6 @@ impl TxDescriptor { }); } } - - // pub(super) fn packet_id(&self) -> Option<&PacketId> { - // self.packet_id.as_ref() - // } } #[cfg(feature = "ptp")] diff --git a/src/lib.rs b/src/lib.rs index 0951fb5..ef19fee 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -56,7 +56,7 @@ pub use smoltcp; #[cfg(feature = "device-selected")] use { dma::{DmaParts, EthernetDMA, RxDescriptorRing, TxDescriptorRing}, - mac::{EthernetMAC, Speed, WrongClock}, + mac::{EthernetMAC, MacParts, Speed, WrongClock}, setup::*, }; @@ -108,8 +108,6 @@ where // Set up the clocks and reset the MAC periperhal setup::setup(); - let eth_mac = parts.mac.into(); - // Congfigure and start up the ethernet DMA. let dma = EthernetDMA::new( DmaParts { @@ -125,13 +123,17 @@ where #[cfg(feature = "ptp")] let ptp = EthernetPTP::new(parts.ptp.into(), clocks, &dma); - #[cfg(not(feature = "stm32h7xx-hal"))] - let mmc = parts.mmc; - #[cfg(feature = "stm32h7xx-hal")] - let mmc = (); - // Configure the ethernet MAC - let mac = EthernetMAC::new(eth_mac, mmc, clocks, Speed::FullDuplexBase100Tx, &dma)?; + let mac = EthernetMAC::new( + MacParts { + eth_mac: parts.mac.into(), + #[cfg(feature = "f-series")] + eth_mmc: parts.mmc.into(), + }, + clocks, + Speed::FullDuplexBase100Tx, + &dma, + )?; let parts = Parts { mac, @@ -200,8 +202,13 @@ where let ptp = EthernetPTP::new(parts.ptp.into(), clocks, &dma); // Configure the ethernet MAC - let mac = EthernetMAC::new(eth_mac, parts.mmc, clocks, Speed::FullDuplexBase100Tx, &dma)? - .with_mii(mdio, mdc); + let mac = EthernetMAC::new( + MacParts { eth_mac, eth_mmc }, + clocks, + Speed::FullDuplexBase100Tx, + &dma, + )? + .with_mii(mdio, mdc); let parts = Parts { mac, diff --git a/src/mac/mod.rs b/src/mac/mod.rs index c00c108..aa6fe9f 100644 --- a/src/mac/mod.rs +++ b/src/mac/mod.rs @@ -2,18 +2,104 @@ use crate::{dma::EthernetDMA, peripherals::ETHERNET_MAC, Clocks}; -#[cfg(feature = "f-series")] -use crate::stm32::ETHERNET_MMC; - -#[cfg(feature = "stm32h7xx-hal")] -#[allow(non_camel_case_types)] -type ETHERNET_MMC = (); - #[cfg(feature = "f-series")] mod miim; #[cfg(feature = "f-series")] pub use miim::*; +pub(crate) struct MacParts { + pub eth_mac: ETHERNET_MAC, + #[cfg(feature = "f-series")] + pub eth_mmc: ETHERNET_MMC, +} + +impl MacParts { + fn enable_promicious_mode(&self) { + let Self { eth_mac, .. } = self; + + #[cfg(feature = "f-series")] + let (mac_filter_reg, flow_control) = (ð_mac.macffr, ð_mac.macfcr); + #[cfg(feature = "stm32h7xx-hal")] + let (mac_filter, flow_control) = (ð_mac.macpfr, ð_mac.macqtx_fcr); + + // Frame filter register + mac_filter.modify(|_, w| { + // Receive All + w.ra() + .set_bit() + // Promiscuous mode + .pm() + .set_bit() + }); + // Flow Control Register + flow_control.modify(|_, w| { + // Pause time + #[allow(unused_unsafe)] + unsafe { + w.pt().bits(0x100) + } + }); + } + + fn disable_mmc_interrupts(&self) { + let Self { + eth_mac, + #[cfg(feature = "f-series")] + eth_mmc, + } = self; + + #[cfg(feature = "f-series")] + { + // Disable all MMC RX interrupts + eth_mmc + .mmcrimr + .write(|w| w.rgufm().set_bit().rfaem().set_bit().rfcem().set_bit()); + + // Disable all MMC TX interrupts + eth_mmc + .mmctimr + .write(|w| w.tgfm().set_bit().tgfmscm().set_bit().tgfscm().set_bit()); + + // Fix incorrect TGFM bit position until https://github.com/stm32-rs/stm32-rs/pull/689 + // is released and used by HALs. + eth_mmc + .mmctimr + .modify(|r, w| unsafe { w.bits(r.bits() | (1 << 21)) }); + } + + #[cfg(feature = "stm32h7xx-hal")] + { + // Disable all MMC RX interrupts + eth_mac.mmc_rx_interrupt_mask.write(|w| { + w.rxlpitrcim() + .set_bit() + .rxlpiuscim() + .set_bit() + .rxucgpim() + .set_bit() + .rxalgnerpim() + .set_bit() + .rxcrcerpim() + .set_bit() + }); + + // Disable all MMC TX interrupts + eth_mac.mmc_tx_interrupt_mask.write(|w| { + w.txlpitrcim() + .set_bit() + .txlpiuscim() + .set_bit() + .txgpktim() + .set_bit() + .txmcolgpim() + .set_bit() + .txscolgpim() + .set_bit() + }); + } + } +} + /// Speeds at which this MAC can be configured #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -69,30 +155,38 @@ impl EthernetMAC { /// Additionally, an `impl` of the [`ieee802_3_miim::Miim`] trait is available /// for PHY communication. pub(crate) fn new( - eth_mac: ETHERNET_MAC, - eth_mmc: ETHERNET_MMC, - clocks: Clocks, + parts: MacParts, + #[allow(unused)] clocks: Clocks, initial_speed: Speed, // Note(_dma): this field exists to ensure that the MAC is not // initialized before the DMA. If MAC is started before the DMA, // it doesn't work. _dma: &EthernetDMA, ) -> Result { - // let clock_frequency = clocks.hclk().to_Hz(); + let MacParts { + eth_mac, + #[cfg(feature = "f-series")] + eth_mmc, + } = &parts; + // TODO: configure MDIOS - // let clock_range = match clock_frequency { - // 0..=24_999_999 => return Err(WrongClock), - // 25_000_000..=34_999_999 => ETH_MACMIIAR_CR_HCLK_DIV_16, - // 35_000_000..=59_999_999 => ETH_MACMIIAR_CR_HCLK_DIV_26, - // 60_000_000..=99_999_999 => ETH_MACMIIAR_CR_HCLK_DIV_42, - // 100_000_000..=149_999_999 => ETH_MACMIIAR_CR_HCLK_DIV_62, - // _ => ETH_MACMIIAR_CR_HCLK_DIV_102, - // }; - - // Set clock range in MAC MII address register - // eth_mac - // .macmiiar - // .modify(|_, w| unsafe { w.cr().bits(clock_range) }); + #[cfg(feature = "f-series")] + { + let clock_frequency = clocks.hclk().to_Hz(); + let clock_range = match clock_frequency { + 0..=24_999_999 => return Err(WrongClock), + 25_000_000..=34_999_999 => ETH_MACMIIAR_CR_HCLK_DIV_16, + 35_000_000..=59_999_999 => ETH_MACMIIAR_CR_HCLK_DIV_26, + 60_000_000..=99_999_999 => ETH_MACMIIAR_CR_HCLK_DIV_42, + 100_000_000..=149_999_999 => ETH_MACMIIAR_CR_HCLK_DIV_62, + _ => ETH_MACMIIAR_CR_HCLK_DIV_102, + }; + + // Set clock range in MAC MII address register + eth_mac + .macmiiar + .modify(|_, w| unsafe { w.cr().bits(clock_range) }); + } // Configuration Register eth_mac.maccr.modify(|_, w| { @@ -138,80 +232,12 @@ impl EthernetMAC { .set_bit() }); - #[cfg(feature = "f-series")] - let (mac_filter_reg, flow_control) = (ð_mac.macffr, ð_mac.macfcr); - #[cfg(feature = "stm32h7xx-hal")] - let (mac_filter, flow_control) = (ð_mac.macpfr, ð_mac.macqtx_fcr); - - // Frame filter register - mac_filter.modify(|_, w| { - // Receive All - w.ra() - .set_bit() - // Promiscuous mode - .pm() - .set_bit() - }); - // Flow Control Register - flow_control.modify(|_, w| { - // Pause time - #[allow(unused_unsafe)] - unsafe { - w.pt().bits(0x100) - } - }); - - #[cfg(feature = "f-series")] - { - // Disable all MMC RX interrupts - eth_mmc - .mmcrimr - .write(|w| w.rgufm().set_bit().rfaem().set_bit().rfcem().set_bit()); - - // Disable all MMC TX interrupts - eth_mmc - .mmctimr - .write(|w| w.tgfm().set_bit().tgfmscm().set_bit().tgfscm().set_bit()); - - // Fix incorrect TGFM bit position until https://github.com/stm32-rs/stm32-rs/pull/689 - // is released and used by HALs. - eth_mmc - .mmctimr - .modify(|r, w| unsafe { w.bits(r.bits() | (1 << 21)) }); - } - - #[cfg(feature = "stm32h7xx-hal")] - { - // Disable all MMC RX interrupts - eth_mac.mmc_rx_interrupt_mask.write(|w| { - w.rxlpitrcim() - .set_bit() - .rxlpiuscim() - .set_bit() - .rxucgpim() - .set_bit() - .rxalgnerpim() - .set_bit() - .rxcrcerpim() - .set_bit() - }); - - // Disable all MMC TX interrupts - eth_mac.mmc_tx_interrupt_mask.write(|w| { - w.txlpitrcim() - .set_bit() - .txlpiuscim() - .set_bit() - .txgpktim() - .set_bit() - .txmcolgpim() - .set_bit() - .txscolgpim() - .set_bit() - }); - } + parts.enable_promicious_mode(); + parts.disable_mmc_interrupts(); - let mut me = Self { eth_mac }; + let mut me = Self { + eth_mac: parts.eth_mac, + }; me.set_speed(initial_speed); From e9828f4c6564f7a69ba44cb18aaa958be5b9bf7d Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Sat, 4 Feb 2023 15:21:27 +0100 Subject: [PATCH 08/35] Make f series compile again --- src/dma/mod.rs | 8 +--- src/dma/rx/f_series_desc.rs | 23 +++++++---- src/dma/rx/h_desc.rs | 7 ++-- src/dma/rx/mod.rs | 2 +- src/dma/tx/f_series_desc.rs | 24 ++++++----- src/dma/tx/mod.rs | 10 ++--- src/lib.rs | 53 +++++++++++------------- src/mac/miim.rs | 7 ++-- src/mac/mod.rs | 82 +++++++++++++++---------------------- src/peripherals.rs | 1 + 10 files changed, 102 insertions(+), 115 deletions(-) diff --git a/src/dma/mod.rs b/src/dma/mod.rs index 3f3d3be..d7a311b 100644 --- a/src/dma/mod.rs +++ b/src/dma/mod.rs @@ -6,12 +6,6 @@ use cortex_m::peripheral::NVIC; use crate::{peripherals::ETHERNET_DMA, stm32::Interrupt}; -#[cfg(feature = "f-series")] -type ETHERNET_MTL = (); - -#[cfg(feature = "stm32h7xx-hal")] -use crate::stm32::ETHERNET_MTL; - #[cfg(feature = "smoltcp-phy")] mod smoltcp_phy; #[cfg(feature = "smoltcp-phy")] @@ -77,7 +71,7 @@ pub struct EthernetDMA<'rx, 'tx> { pub(crate) struct DmaParts { pub eth_dma: ETHERNET_DMA, #[cfg(feature = "stm32h7xx-hal")] - pub eth_mtl: ETHERNET_MTL, + pub eth_mtl: crate::stm32::ETHERNET_MTL, } impl<'rx, 'tx> EthernetDMA<'rx, 'tx> { diff --git a/src/dma/rx/f_series_desc.rs b/src/dma/rx/f_series_desc.rs index f33f90b..70cf85d 100644 --- a/src/dma/rx/f_series_desc.rs +++ b/src/dma/rx/f_series_desc.rs @@ -59,8 +59,7 @@ impl RxDescriptor { } pub(super) fn setup(&mut self, buffer: &mut [u8]) { - self.set_buffer(buffer); - self.set_owned(); + self.set_owned(buffer); } /// Is owned by the DMA engine? @@ -71,7 +70,9 @@ impl RxDescriptor { /// Pass ownership to the DMA engine /// /// Overrides old timestamp data - pub(super) fn set_owned(&mut self) { + pub(super) fn set_owned(&mut self, buffer: &mut [u8]) { + self.set_buffer(buffer); + // "Preceding reads and writes cannot be moved past subsequent writes." #[cfg(feature = "fence")] atomic::fence(Ordering::Release); @@ -125,11 +126,15 @@ impl RxDescriptor { ((self.inner_raw.read(0) >> RXDESC_0_FL_SHIFT) & RXDESC_0_FL_MASK) as usize } - pub(super) fn take_received(&mut self, packet_id: Option) -> Result<(), RxError> { + pub(super) fn take_received( + &mut self, + packet_id: Option, + buffer: &mut [u8], + ) -> Result<(), RxError> { if self.is_owned() { Err(RxError::WouldBlock) } else if self.has_error() { - self.set_owned(); + self.set_owned(buffer); Err(RxError::DmaError) } else if self.is_first() && self.is_last() { // "Subsequent reads and writes cannot be moved ahead of preceding reads." @@ -143,7 +148,7 @@ impl RxDescriptor { Ok(()) } else { - self.set_owned(); + self.set_owned(buffer); Err(RxError::Truncated) } } @@ -151,14 +156,14 @@ impl RxDescriptor { pub(super) fn set_end_of_ring(&mut self) { unsafe { self.inner_raw.modify(1, |w| w | RXDESC_1_RER) } } +} +#[cfg(feature = "ptp")] +impl RxDescriptor { pub(super) fn packet_id(&self) -> Option<&PacketId> { self.packet_id.as_ref() } -} -#[cfg(feature = "ptp")] -impl RxDescriptor { /// Get PTP timestamps if available pub(super) fn read_timestamp(&self) -> Option { #[cfg(not(feature = "stm32f1xx-hal"))] diff --git a/src/dma/rx/h_desc.rs b/src/dma/rx/h_desc.rs index 0a4898a..567e17a 100644 --- a/src/dma/rx/h_desc.rs +++ b/src/dma/rx/h_desc.rs @@ -136,11 +136,12 @@ impl RxDescriptor { } pub(super) fn setup(&mut self, buffer: &[u8]) { - self.set_owned(buffer.as_ptr()); + self.set_owned(buffer); } /// Pass ownership to the DMA engine - pub(super) fn set_owned(&mut self, buffer: *const u8) { + pub(super) fn set_owned(&mut self, buffer: &[u8]) { + let buffer = buffer.as_ptr(); self.set_buffer(buffer); // "Preceding reads and writes cannot be moved past subsequent writes." @@ -205,7 +206,7 @@ impl RxDescriptor { Ok(()) } else { - self.set_owned(buffer.as_ptr()); + self.set_owned(buffer); Err(RxError::Truncated) } } diff --git a/src/dma/rx/mod.rs b/src/dma/rx/mod.rs index cabbb8e..1cea58d 100644 --- a/src/dma/rx/mod.rs +++ b/src/dma/rx/mod.rs @@ -280,7 +280,7 @@ impl<'a> core::ops::DerefMut for RxPacket<'a> { impl<'a> Drop for RxPacket<'a> { fn drop(&mut self) { - self.entry.set_owned(self.buffer.as_ptr()); + self.entry.set_owned(self.buffer); } } diff --git a/src/dma/tx/f_series_desc.rs b/src/dma/tx/f_series_desc.rs index 2e3e86a..a201dc8 100644 --- a/src/dma/tx/f_series_desc.rs +++ b/src/dma/tx/f_series_desc.rs @@ -67,12 +67,6 @@ impl TxDescriptor { pub(super) fn setup(&mut self, buffer: &mut [u8]) { self.set_buffer(buffer); - unsafe { - self.inner_raw.write( - 0, - TXDESC_0_CIC0 | TXDESC_0_CIC1 | TXDESC_0_FS | TXDESC_0_LS | TXDESC_0_IC, - ); - } } #[allow(unused)] @@ -112,7 +106,17 @@ impl TxDescriptor { atomic::fence(Ordering::Release); atomic::compiler_fence(Ordering::Release); - unsafe { self.inner_raw.modify(0, |w| w | extra_flags | TXDESC_0_OWN) }; + unsafe { + self.inner_raw.modify(0, |w| { + w | extra_flags + | TXDESC_0_OWN + | TXDESC_0_CIC0 + | TXDESC_0_CIC1 + | TXDESC_0_FS + | TXDESC_0_LS + | TXDESC_0_IC + }) + }; // Used to flush the store buffer as fast as possible to make the buffer available for the // DMA. @@ -144,14 +148,14 @@ impl TxDescriptor { pub(super) fn set_end_of_ring(&mut self) { unsafe { self.inner_raw.modify(0, |w| w | TXDESC_0_TER) }; } +} +#[cfg(feature = "ptp")] +impl TxDescriptor { pub(super) fn packet_id(&self) -> Option<&PacketId> { self.packet_id.as_ref() } -} -#[cfg(feature = "ptp")] -impl TxDescriptor { fn read_timestamp(&mut self) -> Option { let tdes0 = self.inner_raw.read(0); diff --git a/src/dma/tx/mod.rs b/src/dma/tx/mod.rs index 6fe8ecc..5e0db78 100644 --- a/src/dma/tx/mod.rs +++ b/src/dma/tx/mod.rs @@ -41,7 +41,7 @@ pub(crate) struct TxRing<'data, STATE> { impl<'data, STATE> TxRing<'data, STATE> { pub fn running_state(&self, eth_dma: ÐERNET_DMA) -> RunningState { #[cfg(feature = "f-series")] - let tx_status = eth_dma.dmasr().read().tps().bits(); + let tx_status = eth_dma.dmasr.read().tps().bits(); #[cfg(feature = "stm32h7xx-hal")] let tx_status = eth_dma.dmadsr.read().tps0().bits(); @@ -97,7 +97,7 @@ impl<'data> TxRing<'data, NotRunning> { #[cfg(feature = "f-series")] // Set end of ring register - self.ring.last_descriptor().set_end_of_ring(); + self.ring.last_descriptor_mut().set_end_of_ring(); let ring_ptr = self.ring.descriptors_start_address(); @@ -235,7 +235,7 @@ impl<'data> TxRing<'data, Running> { impl<'data> TxRing<'data, Running> { pub(crate) fn collect_timestamps(&mut self) { for descriptor in self.ring.descriptors_mut() { - f_descriptor.attach_timestamp(); + descriptor.attach_timestamp(); } } @@ -243,12 +243,12 @@ impl<'data> TxRing<'data, Running> { let descriptor = if let Some(descriptor) = self.ring.descriptors().find(|d| d.packet_id() == Some(&id)) { - f_descriptor + descriptor } else { return Err(TimestampError::IdNotFound); }; - f_descriptor + descriptor .timestamp() .map(|t| *t) .ok_or(TimestampError::NotYetTimestamped) diff --git a/src/lib.rs b/src/lib.rs index ef19fee..00eb90c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -108,32 +108,27 @@ where // Set up the clocks and reset the MAC periperhal setup::setup(); + let dma_parts = DmaParts { + eth_dma: parts.dma.into(), + #[cfg(feature = "stm32h7xx-hal")] + eth_mtl: parts.mtl, + }; + + let mac_parts = MacParts { + eth_mac: parts.mac.into(), + #[cfg(feature = "f-series")] + eth_mmc: parts.mmc.into(), + }; + // Congfigure and start up the ethernet DMA. - let dma = EthernetDMA::new( - DmaParts { - eth_dma: parts.dma, - #[cfg(feature = "stm32h7xx-hal")] - eth_mtl: parts.mtl, - }, - rx_buffer, - tx_buffer, - ); + let dma = EthernetDMA::new(dma_parts, rx_buffer, tx_buffer); // Configure the ethernet PTP #[cfg(feature = "ptp")] let ptp = EthernetPTP::new(parts.ptp.into(), clocks, &dma); // Configure the ethernet MAC - let mac = EthernetMAC::new( - MacParts { - eth_mac: parts.mac.into(), - #[cfg(feature = "f-series")] - eth_mmc: parts.mmc.into(), - }, - clocks, - Speed::FullDuplexBase100Tx, - &dma, - )?; + let mac = EthernetMAC::new(mac_parts, clocks, Speed::FullDuplexBase100Tx, &dma)?; let parts = Parts { mac, @@ -192,23 +187,25 @@ where // Set up the clocks and reset the MAC periperhal setup::setup(); - let eth_mac = parts.mac.into(); + let dma_parts = DmaParts { + eth_dma: parts.dma.into(), + }; + + let mac_parts = MacParts { + eth_mac: parts.mac.into(), + eth_mmc: parts.mmc.into(), + }; // Congfigure and start up the ethernet DMA. - let dma = EthernetDMA::new(parts.dma.into(), rx_buffer, tx_buffer); + let dma = EthernetDMA::new(dma_parts, rx_buffer, tx_buffer); // Configure the ethernet PTP #[cfg(feature = "ptp")] let ptp = EthernetPTP::new(parts.ptp.into(), clocks, &dma); // Configure the ethernet MAC - let mac = EthernetMAC::new( - MacParts { eth_mac, eth_mmc }, - clocks, - Speed::FullDuplexBase100Tx, - &dma, - )? - .with_mii(mdio, mdc); + let mac = + EthernetMAC::new(mac_parts, clocks, Speed::FullDuplexBase100Tx, &dma)?.with_mii(mdio, mdc); let parts = Parts { mac, diff --git a/src/mac/miim.rs b/src/mac/miim.rs index e6cb0f0..fdf3c59 100644 --- a/src/mac/miim.rs +++ b/src/mac/miim.rs @@ -1,7 +1,8 @@ pub use ieee802_3_miim::Miim; - pub use ieee802_3_miim::*; +use core::ops::{Deref, DerefMut}; + use crate::{peripherals::ETHERNET_MAC, stm32::ethernet_mac::MACMIIAR}; use super::EthernetMAC; @@ -157,7 +158,7 @@ where MDIO: MdioPin, MDC: MdcPin, { - pub(crate) eth_mac: EthernetMAC, + eth_mac: EthernetMAC, mdio: MDIO, mdc: MDC, } @@ -226,7 +227,7 @@ where } } -impl miim::Miim for EthernetMACWithMii +impl Miim for EthernetMACWithMii where MDIO: MdioPin, MDC: MdcPin, diff --git a/src/mac/mod.rs b/src/mac/mod.rs index aa6fe9f..3a5d6bf 100644 --- a/src/mac/mod.rs +++ b/src/mac/mod.rs @@ -10,7 +10,7 @@ pub use miim::*; pub(crate) struct MacParts { pub eth_mac: ETHERNET_MAC, #[cfg(feature = "f-series")] - pub eth_mmc: ETHERNET_MMC, + pub eth_mmc: crate::stm32::ETHERNET_MMC, } impl MacParts { @@ -18,7 +18,7 @@ impl MacParts { let Self { eth_mac, .. } = self; #[cfg(feature = "f-series")] - let (mac_filter_reg, flow_control) = (ð_mac.macffr, ð_mac.macfcr); + let (mac_filter, flow_control) = (ð_mac.macffr, ð_mac.macfcr); #[cfg(feature = "stm32h7xx-hal")] let (mac_filter, flow_control) = (ð_mac.macpfr, ð_mac.macqtx_fcr); @@ -42,14 +42,9 @@ impl MacParts { } fn disable_mmc_interrupts(&self) { - let Self { - eth_mac, - #[cfg(feature = "f-series")] - eth_mmc, - } = self; - #[cfg(feature = "f-series")] { + let eth_mmc = &self.eth_mmc; // Disable all MMC RX interrupts eth_mmc .mmcrimr @@ -69,6 +64,8 @@ impl MacParts { #[cfg(feature = "stm32h7xx-hal")] { + let eth_mac = &self.eth_mac; + // Disable all MMC RX interrupts eth_mac.mmc_rx_interrupt_mask.write(|w| { w.rxlpitrcim() @@ -163,11 +160,7 @@ impl EthernetMAC { // it doesn't work. _dma: &EthernetDMA, ) -> Result { - let MacParts { - eth_mac, - #[cfg(feature = "f-series")] - eth_mmc, - } = &parts; + let eth_mac = &parts.eth_mac; // TODO: configure MDIOS #[cfg(feature = "f-series")] @@ -218,12 +211,7 @@ impl EthernetMAC { .dr() .set_bit(); - // Fast Ethernet speed - w.fes() - .set_bit() - // Duplex mode - .dm() - .set_bit() + w // Receiver enable .re() .set_bit() @@ -244,6 +232,31 @@ impl EthernetMAC { Ok(me) } + /// Set the Ethernet Speed at which the MAC communicates + /// + /// Note that this does _not_ affect the PHY in any way. To + /// configure the PHY, use [`EthernetMACWithMii`] (see: [`Self::with_mii`]) + /// or [`Stm32Mii`] (see: [`Self::mii`]) + pub fn set_speed(&mut self, speed: Speed) { + self.eth_mac.maccr.modify(|_, w| match speed { + Speed::HalfDuplexBase10T => w.fes().clear_bit().dm().clear_bit(), + Speed::FullDuplexBase10T => w.fes().clear_bit().dm().set_bit(), + Speed::HalfDuplexBase100Tx => w.fes().set_bit().dm().clear_bit(), + Speed::FullDuplexBase100Tx => w.fes().set_bit().dm().set_bit(), + }); + } + + /// Get the Ethernet Speed at which the MAC communicates + pub fn get_speed(&self) -> Speed { + let cr = self.eth_mac.maccr.read(); + match (cr.fes().bit_is_set(), cr.dm().bit_is_set()) { + (false, false) => Speed::HalfDuplexBase10T, + (false, true) => Speed::FullDuplexBase10T, + (true, false) => Speed::HalfDuplexBase100Tx, + (true, true) => Speed::FullDuplexBase100Tx, + } + } + /// Borrow access to the MAC's SMI. /// /// Allows for controlling and monitoring any PHYs that may be accessible via the MDIO/MDC @@ -271,36 +284,7 @@ impl EthernetMAC { MDIO: MdioPin, MDC: MdcPin, { - EthernetMACWithMii { - eth_mac: self, - mdio, - mdc, - } - } - - /// Set the Ethernet Speed at which the MAC communicates - /// - /// Note that this does _not_ affect the PHY in any way. To - /// configure the PHY, use [`EthernetMACWithMii`] (see: [`Self::with_mii`]) - /// or [`Stm32Mii`] (see: [`Self::mii`]) - pub fn set_speed(&mut self, speed: Speed) { - self.eth_mac.maccr.modify(|_, w| match speed { - Speed::HalfDuplexBase10T => w.fes().clear_bit().dm().clear_bit(), - Speed::FullDuplexBase10T => w.fes().clear_bit().dm().set_bit(), - Speed::HalfDuplexBase100Tx => w.fes().set_bit().dm().clear_bit(), - Speed::FullDuplexBase100Tx => w.fes().set_bit().dm().set_bit(), - }); - } - - /// Get the Ethernet Speed at which the MAC communicates - pub fn get_speed(&self) -> Speed { - let cr = self.eth_mac.maccr.read(); - match (cr.fes().bit_is_set(), cr.dm().bit_is_set()) { - (false, false) => Speed::HalfDuplexBase10T, - (false, true) => Speed::FullDuplexBase10T, - (true, false) => Speed::HalfDuplexBase100Tx, - (true, true) => Speed::FullDuplexBase100Tx, - } + EthernetMACWithMii::new(self, mdio, mdc) } #[cfg(feature = "ptp")] diff --git a/src/peripherals.rs b/src/peripherals.rs index dbeef4e..cdd5dc7 100644 --- a/src/peripherals.rs +++ b/src/peripherals.rs @@ -196,6 +196,7 @@ mod pac_override_impl { #[doc = r"Return the pointer to the register block"] #[inline(always)] + #[cfg_attr(not(feature = "ptp"), allow(unused))] pub const fn ptr() -> *const MacRegisterBlock { Self::PTR } From 8e53e0d19e5aabf7e476690e7ba9d4fc4aebf058 Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Sat, 4 Feb 2023 16:19:42 +0100 Subject: [PATCH 09/35] Remove unnecessary assert --- src/dma/raw_descriptor.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/dma/raw_descriptor.rs b/src/dma/raw_descriptor.rs index ce847f1..0bbd624 100644 --- a/src/dma/raw_descriptor.rs +++ b/src/dma/raw_descriptor.rs @@ -65,9 +65,6 @@ pub struct DescriptorRing<'data, T> { impl<'data, T> DescriptorRing<'data, T> { pub fn new(descriptors: &'data mut [T], buffers: &'data mut [[u8; MTU + 2]]) -> Self { assert!(descriptors.len() == buffers.len()); - buffers - .iter() - .for_each(|b| assert!(b.len() <= super::MTU + 2)); Self { descriptors, From 96aa8442bb0bddeae1c7e25024349fac3280f15e Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Sat, 4 Feb 2023 16:57:32 +0100 Subject: [PATCH 10/35] This doesn't need to be pub --- src/dma/raw_descriptor.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dma/raw_descriptor.rs b/src/dma/raw_descriptor.rs index 0bbd624..4ba1c7f 100644 --- a/src/dma/raw_descriptor.rs +++ b/src/dma/raw_descriptor.rs @@ -58,7 +58,7 @@ impl RawDescriptor { } pub struct DescriptorRing<'data, T> { - pub descriptors: &'data mut [T], + descriptors: &'data mut [T], buffers: &'data mut [[u8; MTU + 2]], } From 489e80d93d313a37465a260c243f011fe8bce201 Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Sat, 4 Feb 2023 16:57:40 +0100 Subject: [PATCH 11/35] Move responsiblity for getting timestamps from a descriptor to the descriptor --- src/dma/rx/f_series_desc.rs | 17 ++++++++++++----- src/dma/tx/f_series_desc.rs | 8 +++++++- src/ptp/timestamp.rs | 24 ------------------------ 3 files changed, 19 insertions(+), 30 deletions(-) diff --git a/src/dma/rx/f_series_desc.rs b/src/dma/rx/f_series_desc.rs index 70cf85d..ae8e9e7 100644 --- a/src/dma/rx/f_series_desc.rs +++ b/src/dma/rx/f_series_desc.rs @@ -166,6 +166,12 @@ impl RxDescriptor { /// Get PTP timestamps if available pub(super) fn read_timestamp(&self) -> Option { + #[cfg(any(feature = "stm32f4xx-hal", feature = "stm32f7xx-hal"))] + let (high, low) = { (self.inner_raw.read(7), self.inner_raw.read(6)) }; + + #[cfg(feature = "stm32f1xx-hal")] + let (high, low) = { (self.inner_raw.read(3), self.inner_raw.read(2)) }; + #[cfg(not(feature = "stm32f1xx-hal"))] let is_valid = { /// RX timestamp @@ -174,14 +180,15 @@ impl RxDescriptor { }; #[cfg(feature = "stm32f1xx-hal")] - // There is no "timestamp valid" indicator bit - // on STM32F1XX - let is_valid = true; + // There is no direct "timestamp valid" indicator bit + // on STM32F1XX, but if it's invalid it will be written + // as all ones. + let is_valid = high != 0xFFFF_FFFF || low != 0xFFFF_FFFF; - let timestamp = Timestamp::from_descriptor(&self.inner_raw); + let timestamp = Timestamp::from_parts(high, low); if is_valid && self.is_last() { - timestamp + Some(timestamp) } else { None } diff --git a/src/dma/tx/f_series_desc.rs b/src/dma/tx/f_series_desc.rs index a201dc8..f787d9b 100644 --- a/src/dma/tx/f_series_desc.rs +++ b/src/dma/tx/f_series_desc.rs @@ -162,7 +162,13 @@ impl TxDescriptor { let contains_timestamp = (tdes0 & TXDESC_0_TIMESTAMP_STATUS) == TXDESC_0_TIMESTAMP_STATUS; if !self.is_owned() && contains_timestamp && Self::is_last(tdes0) { - Timestamp::from_descriptor(&self.inner_raw) + #[cfg(any(feature = "stm32f4xx-hal", feature = "stm32f7xx-hal"))] + let (high, low) = { (self.inner_raw.read(7), self.inner_raw.read(6)) }; + + #[cfg(feature = "stm32f1xx-hal")] + let (high, low) = { (self.inner_raw.read(3), self.inner_raw.read(2)) }; + + Some(Timestamp::from_parts(high, low)) } else { None } diff --git a/src/ptp/timestamp.rs b/src/ptp/timestamp.rs index bc91d0b..c9ec26c 100644 --- a/src/ptp/timestamp.rs +++ b/src/ptp/timestamp.rs @@ -1,5 +1,3 @@ -use crate::dma::raw_descriptor::RawDescriptor; - use super::Subseconds; /// A timestamp produced by the PTP periperhal @@ -96,28 +94,6 @@ impl Timestamp { Timestamp::new_unchecked(negative, high, subseconds) } - - /// Create a timestamp from the given descriptor - pub fn from_descriptor(desc: &RawDescriptor) -> Option { - #[cfg(not(feature = "stm32f1xx-hal"))] - { - let (high, low) = { (desc.read(7), desc.read(6)) }; - Some(Self::from_parts(high, low)) - } - - #[cfg(feature = "stm32f1xx-hal")] - { - let (high, low) = { (desc.read(3), desc.read(2)) }; - - // The timestamp registers are written to all-ones if - // timestamping was no succesfull - if high == 0xFFFF_FFFF && low == 0xFFFF_FFFF { - None - } else { - Some(Self::from_parts(high, low)) - } - } - } } impl core::ops::Add for Timestamp { From 64a44b1c7451f55dd3379026631cc365fe7561bb Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Sat, 4 Feb 2023 17:43:24 +0100 Subject: [PATCH 12/35] Tentative PTP support on H7 as well :D --- examples/common.rs | 2 +- examples/rtic-timestamp.rs | 35 ++++-- src/dma/rx/h_desc.rs | 20 +--- src/dma/tx/h_desc.rs | 13 ++- src/lib.rs | 7 +- src/mac/mod.rs | 9 +- src/ptp/mod.rs | 232 +++++++++++++++++++++++++++++-------- src/setup.rs | 6 +- 8 files changed, 230 insertions(+), 94 deletions(-) diff --git a/examples/common.rs b/examples/common.rs index 92d73b6..1169e06 100644 --- a/examples/common.rs +++ b/examples/common.rs @@ -30,7 +30,7 @@ pub fn setup_peripherals(p: stm32_eth::stm32::Peripherals) -> (Clocks, Gpio, Par mtl: p.ETHERNET_MTL, #[cfg(feature = "f-series")] mmc: p.ETHERNET_MMC, - #[cfg(feature = "ptp")] + #[cfg(all(feature = "ptp", feature = "f-series"))] ptp: p.ETHERNET_PTP, }; diff --git a/examples/rtic-timestamp.rs b/examples/rtic-timestamp.rs index 894c6d2..33dcddb 100644 --- a/examples/rtic-timestamp.rs +++ b/examples/rtic-timestamp.rs @@ -48,7 +48,7 @@ mod app { }, mac::Speed, ptp::{EthernetPTP, Timestamp}, - Parts, + Parts, MTU, }; #[local] @@ -65,19 +65,27 @@ mod app { #[monotonic(binds = SysTick, default = true)] type Monotonic = Systick<1000>; - #[init(local = [ - rx_desc: [RxDescriptor; 2] = [RxDescriptor::new(); 2], - tx_desc: [TxDescriptor; 2] = [TxDescriptor::new(); 2], - rx_buffers: [[u8; 1522]; 2] = [[0u8; stm32_eth::MTU]; 2], - tx_buffers: [[u8; 1522]; 2] = [[0u8; stm32_eth::MTU]; 2], - ])] + /// On H7s, the ethernet DMA does not have access to the normal ram + /// so we must explicitly put them in SRAM. + #[cfg_attr(feature = "stm32h7xx-hal", link_section = ".sram1.eth")] + static mut TX_DESCRIPTORS: [TxDescriptor; 4] = [TxDescriptor::new(); 4]; + #[cfg_attr(feature = "stm32h7xx-hal", link_section = ".sram1.eth")] + static mut TX_BUFFERS: [[u8; MTU + 2]; 4] = [[0u8; MTU + 2]; 4]; + #[cfg_attr(feature = "stm32h7xx-hal", link_section = ".sram1.eth2")] + static mut RX_DESCRIPTORS: [RxDescriptor; 4] = [RxDescriptor::new(); 4]; + #[cfg_attr(feature = "stm32h7xx-hal", link_section = ".sram1.eth2")] + static mut RX_BUFFERS: [[u8; MTU + 2]; 4] = [[0u8; MTU + 2]; 4]; + + #[init] fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { defmt::info!("Pre-init"); let core = cx.core; let p = cx.device; - let rx_ring = RxDescriptorRing::new(cx.local.rx_desc, cx.local.rx_buffers); - let tx_ring = TxDescriptorRing::new(cx.local.tx_desc, cx.local.tx_buffers); + let rx_ring = + RxDescriptorRing::new(unsafe { &mut RX_DESCRIPTORS }, unsafe { &mut RX_BUFFERS }); + let tx_ring = + TxDescriptorRing::new(unsafe { &mut TX_DESCRIPTORS }, unsafe { &mut TX_BUFFERS }); let (clocks, gpio, ethernet) = crate::common::setup_peripherals(p); let mono = Systick::new(core.SYST, clocks.hclk().raw()); @@ -88,13 +96,15 @@ mod app { defmt::info!("Configuring ethernet"); let Parts { dma, mac, mut ptp } = - stm32_eth::new_with_mii(ethernet, rx_ring, tx_ring, clocks, pins, mdio, mdc).unwrap(); + stm32_eth::new(ethernet, rx_ring, tx_ring, clocks, pins).unwrap(); + #[cfg(not(feature = "stm32h7xx-hal"))] ptp.enable_pps(pps); defmt::info!("Enabling interrupts"); dma.enable_interrupt(); + #[cfg(not(feature = "stm32h7xx-hal"))] match EthernetPhy::from_miim(mac, 0) { Ok(mut phy) => { defmt::info!( @@ -151,7 +161,7 @@ mod app { // incorrect, but works well enough in low-activity systems (such as this example). let now = (cx.shared.ptp, cx.shared.scheduled_time).lock(|ptp, sched_time| { let now = ptp.get_time(); - #[cfg(not(feature = "stm32f107"))] + #[cfg(not(any(feature = "stm32f107", feature = "stm32h7xx-hal")))] { let in_half_sec = now + Timestamp::new( @@ -159,6 +169,7 @@ mod app { 0, stm32_eth::ptp::Subseconds::new_from_nanos(500_000_000).unwrap(), ); + ptp.configure_target_time_interrupt(in_half_sec); } *sched_time = Some(now); @@ -201,7 +212,7 @@ mod app { .lock(|dma, tx_id, ptp, _sched_time| { dma.interrupt_handler(); - #[cfg(not(feature = "stm32f107"))] + #[cfg(not(any(feature = "stm32f107", feature = "stm32h7xx-hal")))] { if ptp.interrupt_handler() { if let Some(sched_time) = _sched_time.take() { diff --git a/src/dma/rx/h_desc.rs b/src/dma/rx/h_desc.rs index 567e17a..80e9a1a 100644 --- a/src/dma/rx/h_desc.rs +++ b/src/dma/rx/h_desc.rs @@ -216,25 +216,7 @@ impl RxDescriptor { impl RxDescriptor { /// Get PTP timestamps if available pub(super) fn read_timestamp(&self) -> Option { - #[cfg(not(feature = "stm32f1xx-hal"))] - let is_valid = { - /// RX timestamp - const RXDESC_0_TIMESTAMP_VALID: u32 = 1 << 7; - self.inner_raw.read(0) & RXDESC_0_TIMESTAMP_VALID == RXDESC_0_TIMESTAMP_VALID - }; - - #[cfg(feature = "stm32f1xx-hal")] - // There is no "timestamp valid" indicator bit - // on STM32F1XX - let is_valid = true; - - let timestamp = Timestamp::from_descriptor(&self.inner_raw); - - if is_valid && self.is_last() { - timestamp - } else { - None - } + todo!(); } fn attach_timestamp(&mut self) { diff --git a/src/dma/tx/h_desc.rs b/src/dma/tx/h_desc.rs index d62ce33..81da90b 100644 --- a/src/dma/tx/h_desc.rs +++ b/src/dma/tx/h_desc.rs @@ -144,6 +144,10 @@ impl TxDescriptor { } } + fn is_last(&self) -> bool { + (self.inner_raw.read(3) & TXDESC_3_LD) == TXDESC_3_LD + } + // Placeholder for API parity with f-series descriptor. pub(super) fn setup(&mut self, _: &[u8]) {} @@ -216,12 +220,11 @@ impl TxDescriptor { #[cfg(feature = "ptp")] impl TxDescriptor { fn read_timestamp(&mut self) -> Option { - let tdes0 = self.inner_raw.read(0); - - let contains_timestamp = (tdes0 & TXDESC_0_TIMESTAMP_STATUS) == TXDESC_0_TIMESTAMP_STATUS; + let contains_timestamp = (self.inner_raw.read(3) & TXDESC_3_TTSS) == TXDESC_3_TTSS; - if !self.is_owned() && contains_timestamp && Self::is_last(tdes0) { - Timestamp::from_descriptor(&self.inner_raw) + if !self.is_owned() && contains_timestamp && self.is_last() { + let (low, high) = (self.inner_raw.read(0), self.inner_raw.read(1)); + Some(Timestamp::from_parts(high, low)) } else { None } diff --git a/src/lib.rs b/src/lib.rs index 00eb90c..a3f7b71 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -125,7 +125,12 @@ where // Configure the ethernet PTP #[cfg(feature = "ptp")] - let ptp = EthernetPTP::new(parts.ptp.into(), clocks, &dma); + let ptp = EthernetPTP::new( + #[cfg(feature = "f-series")] + parts.ptp.into(), + clocks, + &dma, + ); // Configure the ethernet MAC let mac = EthernetMAC::new(mac_parts, clocks, Speed::FullDuplexBase100Tx, &dma)?; diff --git a/src/mac/mod.rs b/src/mac/mod.rs index 3a5d6bf..d30d957 100644 --- a/src/mac/mod.rs +++ b/src/mac/mod.rs @@ -287,7 +287,7 @@ impl EthernetMAC { EthernetMACWithMii::new(self, mdio, mdc) } - #[cfg(feature = "ptp")] + #[cfg(all(feature = "ptp", feature = "f-series"))] pub(crate) fn mask_timestamp_trigger_interrupt() { // SAFETY: MACIMR only receives atomic writes. let mac = &unsafe { &*ETHERNET_MAC::ptr() }; @@ -295,11 +295,10 @@ impl EthernetMAC { } // NOTE(allow): only used on F4 and F7 - #[cfg(feature = "ptp")] - #[allow(dead_code)] + #[cfg(all(feature = "ptp", feature = "f-series"))] pub(crate) fn unmask_timestamp_trigger_interrupt() { // SAFETY: MACIMR only receives atomic writes. - let macimr = &unsafe { &*ETHERNET_MAC::ptr() }.macimr; - macimr.write(|w| w.tstim().clear_bit()); + let mac = &unsafe { &*ETHERNET_MAC::ptr() }; + mac.macimr.write(|w| w.tstim().clear_bit()); } } diff --git a/src/ptp/mod.rs b/src/ptp/mod.rs index 6551e9f..f0b030d 100644 --- a/src/ptp/mod.rs +++ b/src/ptp/mod.rs @@ -2,7 +2,10 @@ //! //! See [`EthernetPTP`] for a more details. -use crate::{dma::EthernetDMA, hal::rcc::Clocks, mac::EthernetMAC, peripherals::ETHERNET_PTP}; +use crate::{dma::EthernetDMA, Clocks}; + +#[cfg(feature = "f-series")] +use crate::{mac::EthernetMAC, peripherals::ETHERNET_PTP}; mod timestamp; pub use timestamp::Timestamp; @@ -44,6 +47,7 @@ pub use pps_pin::PPSPin; /// /// [`NonZeroU8`]: core::num::NonZeroU8 pub struct EthernetPTP { + #[cfg(feature = "f-series")] eth_ptp: ETHERNET_PTP, } @@ -65,14 +69,23 @@ impl EthernetPTP { (stssi, tsa) } + #[cfg(feature = "stm32h7xx-hal")] + /// # Safety + /// The reference to the registerblock obtained using this function + /// must _only_ be used to change strictly PTP related registers. + unsafe fn mac(&self) -> &crate::stm32::ethernet_mac::RegisterBlock { + &*crate::peripherals::ETHERNET_MAC::ptr() + } + pub(crate) fn new( - eth_ptp: ETHERNET_PTP, + #[cfg(feature = "f-series")] eth_ptp: ETHERNET_PTP, clocks: Clocks, // Note(_dma): this field exists to ensure that the PTP is not // initialized before the DMA. If PTP is started before the DMA, // it doesn't work. _dma: &EthernetDMA, ) -> Self { + #[cfg(feature = "f-series")] // Mask timestamp interrupt register EthernetMAC::mask_timestamp_trigger_interrupt(); @@ -80,21 +93,51 @@ impl EthernetPTP { let (stssi, tsa) = Self::calculate_regs(hclk); - // Setup PTP timestamping in fine mode. - eth_ptp.ptptscr.write(|w| { - // Enable snapshots for all frames. - #[cfg(not(feature = "stm32f1xx-hal"))] - let w = w.tssarfe().set_bit(); + #[cfg(feature = "f-series")] + let mut me = { + // Setup PTP timestamping in fine mode. + eth_ptp.ptptscr.write(|w| { + // Enable snapshots for all frames. + #[cfg(not(feature = "stm32f1xx-hal"))] + let w = w.tssarfe().set_bit(); - w.tse().set_bit().tsfcu().set_bit() - }); + w.tse().set_bit().tsfcu().set_bit() + }); + + // Set up subsecond increment + eth_ptp + .ptpssir + .write(|w| unsafe { w.stssi().bits(stssi.raw() as u8) }); - // Set up subsecond increment - eth_ptp - .ptpssir - .write(|w| unsafe { w.stssi().bits(stssi.raw() as u8) }); + Self { eth_ptp } + }; - let mut me = Self { eth_ptp }; + #[cfg(feature = "stm32h7xx-hal")] + let mut me = { + let me = Self {}; + + // SAFETY: we only write to `mactscr` (timestamp control register) + let mac = unsafe { me.mac() }; + + mac.mactscr.modify(|_, w| { + w + // Enable timestamp snapshots for all frames + .tsenall() + .set_bit() + // Enable fine-grain update mode + .tscfupdt() + .set_bit() + // Enable all timestamps + .tsena() + .set_bit() + }); + + // Set up the subsecond increment + mac.macssir + .write(|w| unsafe { w.ssinc().bits(stssi.raw() as u8) }); + + Self {} + }; me.set_addend(tsa); me.set_time(Timestamp::new_unchecked(false, 0, 0)); @@ -104,49 +147,104 @@ impl EthernetPTP { /// Get the configured subsecond increment. pub fn subsecond_increment(&self) -> Subseconds { - Subseconds::new_unchecked(self.eth_ptp.ptpssir.read().stssi().bits() as u32) + #[cfg(feature = "f-series")] + return Subseconds::new_unchecked(self.eth_ptp.ptpssir.read().stssi().bits() as u32); + #[cfg(feature = "stm32h7xx-hal")] + // SAFETY: we only read `macssir` (subsecond register). + return Subseconds::new_unchecked(unsafe { + self.mac().macssir.read().ssinc().bits() as u32 + }); } /// Get the currently configured PTP clock addend. pub fn addend(&self) -> u32 { - self.eth_ptp.ptptsar.read().bits() + #[cfg(feature = "f-series")] + return self.eth_ptp.ptptsar.read().bits(); + #[cfg(feature = "stm32h7xx-hal")] + // SAFETY: we only read `mactsar` (timestamp addend register). + return unsafe { self.mac().mactsar.read().bits() }; } /// Set the PTP clock addend. #[inline(always)] pub fn set_addend(&mut self, rate: u32) { + #[cfg(feature = "f-series")] let ptp = &self.eth_ptp; + #[cfg(feature = "f-series")] ptp.ptptsar.write(|w| unsafe { w.bits(rate) }); - #[cfg(feature = "stm32f1xx-hal")] + #[cfg(feature = "f-series")] { - while ptp.ptptscr.read().tsaru().bit_is_set() {} - ptp.ptptscr.modify(|_, w| w.tsaru().set_bit()); - while ptp.ptptscr.read().tsaru().bit_is_set() {} + #[cfg(feature = "stm32f1xx-hal")] + { + while ptp.ptptscr.read().tsaru().bit_is_set() {} + ptp.ptptscr.modify(|_, w| w.tsaru().set_bit()); + while ptp.ptptscr.read().tsaru().bit_is_set() {} + } + + #[cfg(not(feature = "stm32f1xx-hal"))] + { + while ptp.ptptscr.read().ttsaru().bit_is_set() {} + ptp.ptptscr.modify(|_, w| w.ttsaru().set_bit()); + while ptp.ptptscr.read().ttsaru().bit_is_set() {} + } } - #[cfg(not(feature = "stm32f1xx-hal"))] + #[cfg(feature = "stm32h7xx-hal")] { - while ptp.ptptscr.read().ttsaru().bit_is_set() {} - ptp.ptptscr.modify(|_, w| w.ttsaru().set_bit()); - while ptp.ptptscr.read().ttsaru().bit_is_set() {} + // SAFETY: we only write to `mactsar` (timestamp addend register) + // and `mactscr` (timestamp control register) + let (mactsar, mactscr) = unsafe { + let mac = self.mac(); + (&mac.mactsar, &mac.mactscr) + }; + + mactsar.write(|w| unsafe { w.tsar().bits(rate) }); + + while mactscr.read().tsaddreg().bit_is_set() {} + mactscr.modify(|_, w| w.tsaddreg().set_bit()); + while mactscr.read().tsaddreg().bit_is_set() {} } } /// Set the current time. pub fn set_time(&mut self, time: Timestamp) { - let ptp = &self.eth_ptp; - let seconds = time.seconds(); + // TODO(stm32h7): figure out if the time being signed + // means that we have a two's complement number or not + // (the RM makes it read as though it may be). let subseconds = time.subseconds_signed(); - ptp.ptptshur.write(|w| unsafe { w.bits(seconds) }); - ptp.ptptslur.write(|w| unsafe { w.bits(subseconds) }); + #[cfg(feature = "f-series")] + { + let ptp = &self.eth_ptp; + + ptp.ptptshur.write(|w| unsafe { w.bits(seconds) }); + ptp.ptptslur.write(|w| unsafe { w.bits(subseconds) }); + + // Initialise timestamp + while ptp.ptptscr.read().tssti().bit_is_set() {} + ptp.ptptscr.modify(|_, w| w.tssti().set_bit()); + while ptp.ptptscr.read().tssti().bit_is_set() {} + } - // Initialise timestamp - while ptp.ptptscr.read().tssti().bit_is_set() {} - ptp.ptptscr.modify(|_, w| w.tssti().set_bit()); - while ptp.ptptscr.read().tssti().bit_is_set() {} + #[cfg(feature = "stm32h7xx-hal")] + { + // SAFETY: we only write to `mactscr` (timestamp control register), `macstsur` + // (timestamp update seconds register) and `macstnur` (timestmap update subsecond/nanosecond + // register) + let (mactscr, macstsur, macstnur) = unsafe { + let mac = self.mac(); + (&mac.mactscr, &mac.macstsur, &mac.macstnur) + }; + + macstsur.write(|w| unsafe { w.bits(seconds) }); + macstnur.write(|w| unsafe { w.bits(subseconds) }); + + while mactscr.read().tsinit().bit_is_set() {} + mactscr.modify(|_, w| w.tsinit().set_bit()); + while mactscr.read().tsinit().bit_is_set() {} + } } /// Add the provided time to the current time, atomically. @@ -154,32 +252,70 @@ impl EthernetPTP { /// If `time` is negative, it will instead be subtracted from the /// system time. pub fn update_time(&mut self, time: Timestamp) { - let ptp = &self.eth_ptp; - let seconds = time.seconds(); let subseconds = time.subseconds_signed(); - ptp.ptptshur.write(|w| unsafe { w.bits(seconds) }); - ptp.ptptslur.write(|w| unsafe { w.bits(subseconds) }); + #[cfg(feature = "f-series")] + { + let ptp = &self.eth_ptp; - // Add timestamp to global time + ptp.ptptshur.write(|w| unsafe { w.bits(seconds) }); + ptp.ptptslur.write(|w| unsafe { w.bits(subseconds) }); - let read_status = || { - let scr = ptp.ptptscr.read(); - scr.tsstu().bit_is_set() || scr.tssti().bit_is_set() - }; + // Add timestamp to global time - while read_status() {} - ptp.ptptscr.modify(|_, w| w.tsstu().set_bit()); - while ptp.ptptscr.read().tsstu().bit_is_set() {} + let read_status = || { + let scr = ptp.ptptscr.read(); + scr.tsstu().bit_is_set() || scr.tssti().bit_is_set() + }; + + while read_status() {} + ptp.ptptscr.modify(|_, w| w.tsstu().set_bit()); + while ptp.ptptscr.read().tsstu().bit_is_set() {} + } + + #[cfg(feature = "stm32h7xx-hal")] + { + // SAFETY: we only write to `mactscr` (timestamp control register), `macstsur` + // (timestamp update seconds register) and `macstnur` (timestmap update subsecond/nanosecond + // register) + let (mactscr, macstsur, macstnur) = unsafe { + let mac = self.mac(); + (&mac.mactscr, &mac.macstsur, &mac.macstnur) + }; + + macstsur.write(|w| unsafe { w.bits(seconds) }); + macstnur.write(|w| unsafe { w.bits(subseconds) }); + + while mactscr.read().tsupdt().bit_is_set() {} + mactscr.modify(|_, w| w.tsupdt().set_bit()); + while mactscr.read().tsupdt().bit_is_set() {} + } } /// Get the current time. pub fn get_time(&self) -> Timestamp { let try_read_time = || { - let seconds = self.eth_ptp.ptptshr.read().bits(); - let subseconds = self.eth_ptp.ptptslr.read().bits(); - let seconds_after = self.eth_ptp.ptptshr.read().bits(); + #[cfg(feature = "f-series")] + let (seconds, subseconds, seconds_after) = { + let seconds = self.eth_ptp.ptptshr.read().bits(); + let subseconds = self.eth_ptp.ptptslr.read().bits(); + let seconds2 = self.eth_ptp.ptptshr.read().bits(); + (seconds, subseconds, seconds2) + }; + + #[cfg(feature = "stm32h7xx-hal")] + let (seconds, subseconds, seconds_after) = { + let (macstsr, macstnr) = unsafe { + let mac = self.mac(); + (&mac.macstsr, &mac.macstnr) + }; + + let seconds = macstsr.read().bits(); + let subseconds = macstnr.read().bits(); + let seconds2 = macstsr.read().bits(); + (seconds, subseconds, seconds2) + }; if seconds == seconds_after { Ok(Timestamp::from_parts(seconds, subseconds)) @@ -207,7 +343,7 @@ impl EthernetPTP { /// Setting and configuring target time interrupts on the STM32F107 does not /// make any sense: we can generate the interrupt, but it is impossible to /// clear the flag as the register required to do so does not exist. -#[cfg(not(feature = "stm32f1xx-hal"))] +#[cfg(all(not(feature = "stm32f1xx-hal"), not(feature = "stm32h7xx-hal")))] impl EthernetPTP { /// Configure the target time. fn set_target_time(&mut self, timestamp: Timestamp) { diff --git a/src/setup.rs b/src/setup.rs index 20282d4..cfd0f5f 100644 --- a/src/setup.rs +++ b/src/setup.rs @@ -57,7 +57,7 @@ use crate::{ }; #[cfg(feature = "ptp")] -use crate::{ptp::EthernetPTP, stm32::ETHERNET_PTP}; +use crate::ptp::EthernetPTP; // Enable syscfg and ethernet clocks. Reset the Ethernet MAC. pub(crate) fn setup() { @@ -241,8 +241,8 @@ pub struct PartsIn { #[cfg(feature = "stm32h7xx-hal")] pub mtl: ETHERNET_MTL, - #[cfg(feature = "ptp")] - pub ptp: ETHERNET_PTP, + #[cfg(all(feature = "ptp", feature = "f-series"))] + pub ptp: crate::stm32::ETHERNET_PTP, } /// Access to all configured parts of the ethernet peripheral. From 9b7dc075ea5df32c7c808ca5898d00c470dbd1cc Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Sat, 4 Feb 2023 18:50:35 +0100 Subject: [PATCH 13/35] Also timestamp target time interrupt --- examples/rtic-timestamp.rs | 4 +-- src/mac/mod.rs | 7 +++++ src/ptp/mod.rs | 63 +++++++++++++++++++++++++++++--------- 3 files changed, 58 insertions(+), 16 deletions(-) diff --git a/examples/rtic-timestamp.rs b/examples/rtic-timestamp.rs index 33dcddb..fd7dcc3 100644 --- a/examples/rtic-timestamp.rs +++ b/examples/rtic-timestamp.rs @@ -161,7 +161,7 @@ mod app { // incorrect, but works well enough in low-activity systems (such as this example). let now = (cx.shared.ptp, cx.shared.scheduled_time).lock(|ptp, sched_time| { let now = ptp.get_time(); - #[cfg(not(any(feature = "stm32f107", feature = "stm32h7xx-hal")))] + #[cfg(not(feature = "stm32f107"))] { let in_half_sec = now + Timestamp::new( @@ -212,7 +212,7 @@ mod app { .lock(|dma, tx_id, ptp, _sched_time| { dma.interrupt_handler(); - #[cfg(not(any(feature = "stm32f107", feature = "stm32h7xx-hal")))] + #[cfg(not(feature = "stm32f107"))] { if ptp.interrupt_handler() { if let Some(sched_time) = _sched_time.take() { diff --git a/src/mac/mod.rs b/src/mac/mod.rs index d30d957..db7e681 100644 --- a/src/mac/mod.rs +++ b/src/mac/mod.rs @@ -223,6 +223,13 @@ impl EthernetMAC { parts.enable_promicious_mode(); parts.disable_mmc_interrupts(); + #[cfg(feature = "stm32h7xx-hal")] + // On H7 parts, the target timestamp interrupt + // is not broken, so we can enable it unconditionally. + { + parts.eth_mac.macier.modify(|_, w| w.tsie().set_bit()); + } + let mut me = Self { eth_mac: parts.eth_mac, }; diff --git a/src/ptp/mod.rs b/src/ptp/mod.rs index f0b030d..c325c1d 100644 --- a/src/ptp/mod.rs +++ b/src/ptp/mod.rs @@ -343,17 +343,34 @@ impl EthernetPTP { /// Setting and configuring target time interrupts on the STM32F107 does not /// make any sense: we can generate the interrupt, but it is impossible to /// clear the flag as the register required to do so does not exist. -#[cfg(all(not(feature = "stm32f1xx-hal"), not(feature = "stm32h7xx-hal")))] +#[cfg(all(not(feature = "stm32f1xx-hal")))] impl EthernetPTP { /// Configure the target time. fn set_target_time(&mut self, timestamp: Timestamp) { let (high, low) = (timestamp.seconds(), timestamp.subseconds_signed()); - self.eth_ptp - .ptptthr - .write(|w| unsafe { w.ttsh().bits(high) }); - self.eth_ptp - .ptpttlr - .write(|w| unsafe { w.ttsl().bits(low) }); + + #[cfg(feature = "f-series")] + { + self.eth_ptp + .ptptthr + .write(|w| unsafe { w.ttsh().bits(high) }); + self.eth_ptp + .ptpttlr + .write(|w| unsafe { w.ttsl().bits(low) }); + } + + #[cfg(feature = "stm32h7xx-hal")] + { + // SAFETY: we only write to `ppsttsr` (PPS target time seconds register) and + // `ppsttnr` (PPS target time subseconds register) + let (ppsttsr, ppsttnr) = unsafe { + let mac = self.mac(); + (&mac.macppsttsr, &mac.macppsttnr) + }; + + ppsttsr.write(|w| unsafe { w.bits(high) }); + ppsttnr.write(|w| unsafe { w.bits(low) }); + } } /// Configure the target time interrupt. @@ -362,22 +379,40 @@ impl EthernetPTP { /// interrupt to detect (and clear) the correct status bits. pub fn configure_target_time_interrupt(&mut self, timestamp: Timestamp) { self.set_target_time(timestamp); - self.eth_ptp.ptptscr.modify(|_, w| w.tsite().set_bit()); - EthernetMAC::unmask_timestamp_trigger_interrupt(); + #[cfg(feature = "f-series")] + { + self.eth_ptp.ptptscr.modify(|_, w| w.tsite().set_bit()); + EthernetMAC::unmask_timestamp_trigger_interrupt(); + } } /// Returns a boolean indicating whether or not the interrupt /// was caused by a Timestamp trigger and clears the interrupt /// flag. pub fn interrupt_handler(&mut self) -> bool { - let is_tsint = self.eth_ptp.ptptssr.read().tsttr().bit_is_set(); - if is_tsint { - self.eth_ptp.ptptscr.modify(|_, w| w.tsite().clear_bit()); - EthernetMAC::mask_timestamp_trigger_interrupt(); - } + #[cfg(feature = "f-series")] + let is_tsint = { + let is_tsint = self.eth_ptp.ptptssr.read().tsttr().bit_is_set(); + if is_tsint { + self.eth_ptp.ptptscr.modify(|_, w| w.tsite().clear_bit()); + EthernetMAC::mask_timestamp_trigger_interrupt(); + } + is_tsint + }; + + #[cfg(feature = "stm32h7xx-hal")] + let is_tsint = { + // SAFETY: we only write to `mactssr` (Timestamp Status register) + let mactssr = unsafe { &self.mac().mactssr }; + + // Reading the bit clears it, and deasserts the interrupt. + mactssr.read().tstargt0().bit_is_set() + }; + is_tsint } + #[cfg(feature = "f-series")] /// Configure the PPS output frequency. /// /// The PPS output frequency becomes `2 ^ pps_freq`. `pps_freq` is From 2066f70b4eef7c5e2920b06190db3644ccfa7915 Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Sat, 4 Feb 2023 19:15:47 +0100 Subject: [PATCH 14/35] Properly initialize rings (SRAM is not 0'd out by default!) and add some more checks --- examples/arp.rs | 14 +------------- examples/common.rs | 33 ++++++++++++++++++++++++++++++++- examples/rtic-timestamp.rs | 34 +++++++++++----------------------- 3 files changed, 44 insertions(+), 37 deletions(-) diff --git a/examples/arp.rs b/examples/arp.rs index 3043e24..c0b2745 100644 --- a/examples/arp.rs +++ b/examples/arp.rs @@ -33,17 +33,6 @@ const PHY_ADDR: u8 = 0; static TIME: Mutex> = Mutex::new(RefCell::new(0)); static ETH_PENDING: Mutex> = Mutex::new(RefCell::new(false)); -/// On H7s, the ethernet DMA does not have access to the normal ram -/// so we must explicitly put them in SRAM. -#[cfg_attr(feature = "stm32h7xx-hal", link_section = ".sram1.eth")] -static mut TX_DESCRIPTORS: [TxDescriptor; 4] = [TxDescriptor::new(); 4]; -#[cfg_attr(feature = "stm32h7xx-hal", link_section = ".sram1.eth")] -static mut TX_BUFFERS: [[u8; MTU + 2]; 4] = [[0u8; MTU + 2]; 4]; -#[cfg_attr(feature = "stm32h7xx-hal", link_section = ".sram1.eth2")] -static mut RX_DESCRIPTORS: [RxDescriptor; 4] = [RxDescriptor::new(); 4]; -#[cfg_attr(feature = "stm32h7xx-hal", link_section = ".sram1.eth2")] -static mut RX_BUFFERS: [[u8; MTU + 2]; 4] = [[0u8; MTU + 2]; 4]; - #[entry] fn main() -> ! { let p = Peripherals::take().unwrap(); @@ -57,8 +46,7 @@ fn main() -> ! { let (eth_pins, mdio, mdc, _) = common::setup_pins(gpio); - let rx_ring = RxDescriptorRing::new(unsafe { &mut RX_DESCRIPTORS }, unsafe { &mut RX_BUFFERS }); - let tx_ring = TxDescriptorRing::new(unsafe { &mut TX_DESCRIPTORS }, unsafe { &mut TX_BUFFERS }); + let (tx_ring, rx_ring) = crate::common::setup_rings(); let Parts { mut dma, diff --git a/examples/common.rs b/examples/common.rs index 1169e06..1f4bf28 100644 --- a/examples/common.rs +++ b/examples/common.rs @@ -4,7 +4,13 @@ //! //! Note that this module isn't an example by itself. -use stm32_eth::{hal::gpio::GpioExt, PartsIn}; +use core::mem::MaybeUninit; + +use stm32_eth::{ + dma::{RxDescriptor, RxDescriptorRing, TxDescriptor, TxDescriptorRing}, + hal::gpio::GpioExt, + PartsIn, MTU, +}; #[cfg(feature = "f-series")] use stm32_eth::hal::rcc::Clocks; @@ -17,6 +23,31 @@ pub use pins::{setup_pins, Gpio}; use fugit::RateExtU32; use stm32_eth::hal::rcc::RccExt; +/// On H7s, the ethernet DMA does not have access to the normal ram +/// so we must explicitly put them in SRAM. +#[cfg_attr(feature = "stm32h7xx-hal", link_section = ".sram1.eth")] +static mut TX_DESCRIPTORS: MaybeUninit<[TxDescriptor; 4]> = MaybeUninit::uninit(); +#[cfg_attr(feature = "stm32h7xx-hal", link_section = ".sram1.eth")] +static mut TX_BUFFERS: MaybeUninit<[[u8; MTU + 2]; 4]> = MaybeUninit::uninit(); +#[cfg_attr(feature = "stm32h7xx-hal", link_section = ".sram1.eth2")] +static mut RX_DESCRIPTORS: MaybeUninit<[RxDescriptor; 4]> = MaybeUninit::uninit(); +#[cfg_attr(feature = "stm32h7xx-hal", link_section = ".sram1.eth2")] +static mut RX_BUFFERS: MaybeUninit<[[u8; MTU + 2]; 4]> = MaybeUninit::uninit(); + +/// Set up the buffers to be used +pub fn setup_rings() -> (TxDescriptorRing<'static>, RxDescriptorRing<'static>) { + let tx_desc = unsafe { TX_DESCRIPTORS.write([TxDescriptor::new(); 4]) }; + let tx_buf = unsafe { TX_BUFFERS.write([[0u8; MTU + 2]; 4]) }; + + let rx_desc = unsafe { RX_DESCRIPTORS.write([RxDescriptor::new(); 4]) }; + let rx_buf = unsafe { RX_BUFFERS.write([[0u8; MTU + 2]; 4]) }; + + ( + TxDescriptorRing::new(tx_desc, tx_buf), + RxDescriptorRing::new(rx_desc, rx_buf), + ) +} + /// Setup the clocks and return clocks and a GPIO struct that /// can be used to set up all of the pins. /// diff --git a/examples/rtic-timestamp.rs b/examples/rtic-timestamp.rs index fd7dcc3..e2c645a 100644 --- a/examples/rtic-timestamp.rs +++ b/examples/rtic-timestamp.rs @@ -65,27 +65,13 @@ mod app { #[monotonic(binds = SysTick, default = true)] type Monotonic = Systick<1000>; - /// On H7s, the ethernet DMA does not have access to the normal ram - /// so we must explicitly put them in SRAM. - #[cfg_attr(feature = "stm32h7xx-hal", link_section = ".sram1.eth")] - static mut TX_DESCRIPTORS: [TxDescriptor; 4] = [TxDescriptor::new(); 4]; - #[cfg_attr(feature = "stm32h7xx-hal", link_section = ".sram1.eth")] - static mut TX_BUFFERS: [[u8; MTU + 2]; 4] = [[0u8; MTU + 2]; 4]; - #[cfg_attr(feature = "stm32h7xx-hal", link_section = ".sram1.eth2")] - static mut RX_DESCRIPTORS: [RxDescriptor; 4] = [RxDescriptor::new(); 4]; - #[cfg_attr(feature = "stm32h7xx-hal", link_section = ".sram1.eth2")] - static mut RX_BUFFERS: [[u8; MTU + 2]; 4] = [[0u8; MTU + 2]; 4]; - #[init] fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { defmt::info!("Pre-init"); let core = cx.core; let p = cx.device; - let rx_ring = - RxDescriptorRing::new(unsafe { &mut RX_DESCRIPTORS }, unsafe { &mut RX_BUFFERS }); - let tx_ring = - TxDescriptorRing::new(unsafe { &mut TX_DESCRIPTORS }, unsafe { &mut TX_BUFFERS }); + let (tx_ring, rx_ring) = crate::common::setup_rings(); let (clocks, gpio, ethernet) = crate::common::setup_peripherals(p); let mono = Systick::new(core.SYST, clocks.hclk().raw()); @@ -192,7 +178,7 @@ mod app { buf[12..14].copy_from_slice(Ð_TYPE); buf[14..22].copy_from_slice(&now.raw().to_be_bytes()); }) - .ok(); + .unwrap(); *tx_id = Some((tx_id_val, now)); *tx_id_ctr += 1; *tx_id_ctr |= 0x8000_0000; @@ -210,7 +196,7 @@ mod app { cx.shared.scheduled_time, ) .lock(|dma, tx_id, ptp, _sched_time| { - dma.interrupt_handler(); + let interrupt_summary = dma.interrupt_handler(); #[cfg(not(feature = "stm32f107"))] { @@ -302,15 +288,17 @@ mod app { } } - if let Some((tx_id, sent_time)) = tx_id.take() { - if let Ok(ts) = dma.get_timestamp_for_id(PacketId(tx_id)) { - defmt::info!("TX timestamp: {}", ts); - defmt::debug!( + if interrupt_summary.is_tx { + if let Some((tx_id, sent_time)) = tx_id.take() { + if let Ok(ts) = dma.get_timestamp_for_id(PacketId(tx_id)) { + defmt::info!("TX timestamp: {}", ts); + defmt::debug!( "Diff between TX timestamp and the time that was put into the packet: {}", ts - sent_time ); - } else { - defmt::warn!("Failed to retrieve TX timestamp"); + } else { + defmt::warn!("Failed to retrieve TX timestamp"); + } } } }); From bbb905d754715134a2c11526975d3861646828d8 Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Sat, 4 Feb 2023 22:47:29 +0100 Subject: [PATCH 15/35] Okay, so we really do need this: zeroing out the descriptors is important! --- src/dma/mod.rs | 2 +- src/dma/tx/h_desc.rs | 20 ++++++++++---------- src/dma/tx/mod.rs | 6 +++--- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/dma/mod.rs b/src/dma/mod.rs index d7a311b..aa7add9 100644 --- a/src/dma/mod.rs +++ b/src/dma/mod.rs @@ -39,7 +39,7 @@ const _TXDESC_SIZE: usize = core::mem::size_of::(); /// value which applies to both TX and RX descriptors. const _ASSERT_DESCRIPTOR_SIZES: () = assert!(_RXDESC_SIZE == _TXDESC_SIZE); -const DESC_WORD_SKIP: u8 = (core::mem::size_of::() / 4 - DESC_SIZE) as u8; +const DESC_WORD_SKIP: u8 = ((_RXDESC_SIZE / 4) - DESC_SIZE) as u8; const _ASSERT_DESC_WORD_SKIP_SIZE: () = assert!(DESC_WORD_SKIP <= 0b111); diff --git a/src/dma/tx/h_desc.rs b/src/dma/tx/h_desc.rs index 81da90b..9c340e9 100644 --- a/src/dma/tx/h_desc.rs +++ b/src/dma/tx/h_desc.rs @@ -144,17 +144,26 @@ impl TxDescriptor { } } + #[allow(unused)] fn is_last(&self) -> bool { (self.inner_raw.read(3) & TXDESC_3_LD) == TXDESC_3_LD } // Placeholder for API parity with f-series descriptor. - pub(super) fn setup(&mut self, _: &[u8]) {} + pub(super) fn setup(&mut self, _: &[u8]) { + // Zero-out all fields in the descriptor + (0..4).for_each(|n| unsafe { self.inner_raw.write(n, 0) }); + } pub(super) fn is_owned(&self) -> bool { (self.inner_raw.read(3) & TXDESC_3_OWN) == TXDESC_3_OWN } + #[allow(unused)] + pub(super) fn is_context(&self) -> bool { + (self.inner_raw.read(3) & TXDESC_3_CTXT) == TXDESC_3_CTXT + } + #[allow(unused)] pub(super) fn packet_id(&self) -> Option<&PacketId> { self.packet_id.as_ref() @@ -238,12 +247,3 @@ impl TxDescriptor { self.cached_timestamp.as_ref() } } - -impl TxDescriptor { - /// The initial value for a TxDescriptor - pub const TX_INIT: Self = Self::new(); - - pub(crate) fn prepare_packet(&mut self) -> bool { - !self.is_owned() - } -} diff --git a/src/dma/tx/mod.rs b/src/dma/tx/mod.rs index 5e0db78..ca47f8e 100644 --- a/src/dma/tx/mod.rs +++ b/src/dma/tx/mod.rs @@ -117,8 +117,8 @@ impl<'data> TxRing<'data, NotRunning> { // Assert that the descriptors are properly aligned. assert!(ring_ptr as u32 & !0b11 == ring_ptr as u32); assert!( - self.ring.last_descriptor_mut() as *const _ as u32 & !0b11 - == self.ring.last_descriptor_mut() as *const _ as u32 + self.ring.last_descriptor() as *const _ as u32 & !0b11 + == self.ring.last_descriptor() as *const _ as u32 ); // Set the start pointer. @@ -175,7 +175,7 @@ impl<'data> TxRing<'data, Running> { assert!(length <= buffer.len()); - if descriptor.prepare_packet() { + if !descriptor.is_owned() { let r = f(&mut buffer[0..length]); descriptor.send(packet_id, &buffer[0..length]); From 14a2f319d6a1a8746a1a2088f0c024eb20abc245 Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Sat, 4 Feb 2023 22:56:43 +0100 Subject: [PATCH 16/35] Faster clocks! --- examples/common.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/common.rs b/examples/common.rs index 1f4bf28..5ab89d1 100644 --- a/examples/common.rs +++ b/examples/common.rs @@ -148,7 +148,7 @@ pub fn setup_peripherals(p: stm32_eth::stm32::Peripherals) -> (Clocks, Gpio, Par let pwrcfg = pwr.vos0(&syscfg).freeze(); - let rcc = rcc.hclk(240.MHz()); + let rcc = rcc.hclk(240.MHz()).sys_ck(240.MHz()); let rcc = if cfg!(hse = "bypass") { rcc.bypass_hse().use_hse(8.MHz()) From 886c6baee4e83865b423d7ff8474777e3be5c1de Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Sat, 4 Feb 2023 22:59:15 +0100 Subject: [PATCH 17/35] Small fixes --- examples/common.rs | 22 ++++++++++++---------- src/ptp/mod.rs | 2 +- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/examples/common.rs b/examples/common.rs index 5ab89d1..e9d0a37 100644 --- a/examples/common.rs +++ b/examples/common.rs @@ -23,24 +23,26 @@ pub use pins::{setup_pins, Gpio}; use fugit::RateExtU32; use stm32_eth::hal::rcc::RccExt; +const NUM_DESCRIPTORS: usize = 4; + /// On H7s, the ethernet DMA does not have access to the normal ram /// so we must explicitly put them in SRAM. #[cfg_attr(feature = "stm32h7xx-hal", link_section = ".sram1.eth")] -static mut TX_DESCRIPTORS: MaybeUninit<[TxDescriptor; 4]> = MaybeUninit::uninit(); +static mut TX_DESCRIPTORS: MaybeUninit<[TxDescriptor; NUM_DESCRIPTORS]> = MaybeUninit::uninit(); +#[cfg_attr(feature = "stm32h7xx-hal", link_section = ".sram1.eth")] +static mut TX_BUFFERS: MaybeUninit<[[u8; MTU + 2]; NUM_DESCRIPTORS]> = MaybeUninit::uninit(); +#[cfg_attr(feature = "stm32h7xx-hal", link_section = ".sram1.eth")] +static mut RX_DESCRIPTORS: MaybeUninit<[RxDescriptor; NUM_DESCRIPTORS]> = MaybeUninit::uninit(); #[cfg_attr(feature = "stm32h7xx-hal", link_section = ".sram1.eth")] -static mut TX_BUFFERS: MaybeUninit<[[u8; MTU + 2]; 4]> = MaybeUninit::uninit(); -#[cfg_attr(feature = "stm32h7xx-hal", link_section = ".sram1.eth2")] -static mut RX_DESCRIPTORS: MaybeUninit<[RxDescriptor; 4]> = MaybeUninit::uninit(); -#[cfg_attr(feature = "stm32h7xx-hal", link_section = ".sram1.eth2")] -static mut RX_BUFFERS: MaybeUninit<[[u8; MTU + 2]; 4]> = MaybeUninit::uninit(); +static mut RX_BUFFERS: MaybeUninit<[[u8; MTU + 2]; NUM_DESCRIPTORS]> = MaybeUninit::uninit(); /// Set up the buffers to be used pub fn setup_rings() -> (TxDescriptorRing<'static>, RxDescriptorRing<'static>) { - let tx_desc = unsafe { TX_DESCRIPTORS.write([TxDescriptor::new(); 4]) }; - let tx_buf = unsafe { TX_BUFFERS.write([[0u8; MTU + 2]; 4]) }; + let tx_desc = unsafe { TX_DESCRIPTORS.write([TxDescriptor::new(); NUM_DESCRIPTORS]) }; + let tx_buf = unsafe { TX_BUFFERS.write([[0u8; MTU + 2]; NUM_DESCRIPTORS]) }; - let rx_desc = unsafe { RX_DESCRIPTORS.write([RxDescriptor::new(); 4]) }; - let rx_buf = unsafe { RX_BUFFERS.write([[0u8; MTU + 2]; 4]) }; + let rx_desc = unsafe { RX_DESCRIPTORS.write([RxDescriptor::new(); NUM_DESCRIPTORS]) }; + let rx_buf = unsafe { RX_BUFFERS.write([[0u8; MTU + 2]; NUM_DESCRIPTORS]) }; ( TxDescriptorRing::new(tx_desc, tx_buf), diff --git a/src/ptp/mod.rs b/src/ptp/mod.rs index c325c1d..6e3ce36 100644 --- a/src/ptp/mod.rs +++ b/src/ptp/mod.rs @@ -136,7 +136,7 @@ impl EthernetPTP { mac.macssir .write(|w| unsafe { w.ssinc().bits(stssi.raw() as u8) }); - Self {} + me }; me.set_addend(tsa); From accc60392a31d5257d0bf30167c6345ab82702ae Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Sat, 4 Feb 2023 23:30:52 +0100 Subject: [PATCH 18/35] Now with RX timestamps --- src/dma/rx/h_desc.rs | 25 +++++++++++++++++-------- src/dma/rx/mod.rs | 41 +++++++++++++++++++++++++++++++++++++++-- src/ptp/mod.rs | 3 +++ 3 files changed, 59 insertions(+), 10 deletions(-) diff --git a/src/dma/rx/h_desc.rs b/src/dma/rx/h_desc.rs index 80e9a1a..2e63a66 100644 --- a/src/dma/rx/h_desc.rs +++ b/src/dma/rx/h_desc.rs @@ -20,6 +20,10 @@ mod consts { pub const RXDESC_3_BUF1V: u32 = 1 << 24; // Write-back bits + /// Timestamp Dropped + pub const RXDESC_1_TD: u32 = 1 << 16; + /// Timestamp Avaialble + pub const RXDESC_1_TSA: u32 = 1 << 14; /// Context Descriptor pub const RXDESC_3_CTXT: u32 = 1 << 30; /// First Descriptor @@ -102,7 +106,7 @@ impl RxDescriptor { } /// Is owned by the DMA engine? - fn is_owned(&self) -> bool { + pub(super) fn is_owned(&self) -> bool { (self.inner_raw.read(3) & RXDESC_3_OWN) == RXDESC_3_OWN } @@ -122,6 +126,10 @@ impl RxDescriptor { self.inner_raw.read(3) & RXDESC_3_CTXT == RXDESC_3_CTXT } + pub(super) fn has_timestamp(&self) -> bool { + (self.inner_raw.read(1) & RXDESC_1_TSA) == RXDESC_1_TSA && self.is_last() + } + pub(super) fn frame_length(&self) -> usize { if self.is_owned() { 0 @@ -200,10 +208,6 @@ impl RxDescriptor { self.packet_id = packet_id; - // Cache the PTP timestamps if PTP is enabled. - #[cfg(feature = "ptp")] - self.attach_timestamp(); - Ok(()) } else { self.set_owned(buffer); @@ -216,11 +220,16 @@ impl RxDescriptor { impl RxDescriptor { /// Get PTP timestamps if available pub(super) fn read_timestamp(&self) -> Option { - todo!(); + if self.is_context() && !self.is_owned() { + let (high, low) = (self.inner_raw.read(1), self.inner_raw.read(0)); + Some(Timestamp::from_parts(high, low)) + } else { + None + } } - fn attach_timestamp(&mut self) { - self.cached_timestamp = self.read_timestamp(); + pub(super) fn attach_timestamp(&mut self, timestamp: Option) { + self.cached_timestamp = timestamp; } pub(super) fn timestamp(&self) -> Option<&Timestamp> { diff --git a/src/dma/rx/mod.rs b/src/dma/rx/mod.rs index 1cea58d..d352667 100644 --- a/src/dma/rx/mod.rs +++ b/src/dma/rx/mod.rs @@ -199,8 +199,9 @@ impl<'data> RxRing<'data, Running> { self.demand_poll(eth_dma); } + let entry = self.next_entry; let entries_len = self.ring.len(); - let (descriptor, buffer) = self.ring.get(self.next_entry); + let (descriptor, buffer) = self.ring.get(entry); let mut res = descriptor.take_received(packet_id, buffer); @@ -208,8 +209,44 @@ impl<'data> RxRing<'data, Running> { self.next_entry = (self.next_entry + 1) % entries_len; } + #[cfg(all(feature = "ptp", feature = "stm32h7xx-hal"))] + let (timestamp, descriptor, buffer) = { + if res.as_mut().err() != Some(&mut RxError::WouldBlock) { + let desc_has_timestamp = descriptor.has_timestamp(); + + drop(descriptor); + drop(buffer); + + // On H7's, the timestamp is stored in the next Context + // descriptor. + let timestamp = if desc_has_timestamp { + let (ctx_descriptor, ctx_des_buffer) = self.ring.get(self.next_entry); + if let Some(timestamp) = ctx_descriptor.read_timestamp() { + ctx_descriptor.set_owned(ctx_des_buffer); + // Advance over this buffer + self.next_entry = (self.next_entry + 1) % entries_len; + Some(timestamp) + } else { + None + } + } else { + None + }; + + let (descriptor, buffer) = self.ring.get(entry); + + descriptor.attach_timestamp(timestamp); + + (timestamp, descriptor, buffer) + } else { + let (descriptor, buffer) = self.ring.get(entry); + descriptor.attach_timestamp(None); + (None, descriptor, buffer) + } + }; + res.map(move |_| { - #[cfg(feature = "ptp")] + #[cfg(all(feature = "ptp", feature = "f-series"))] let timestamp = descriptor.read_timestamp(); RxPacket { diff --git a/src/ptp/mod.rs b/src/ptp/mod.rs index 6e3ce36..abea9b2 100644 --- a/src/ptp/mod.rs +++ b/src/ptp/mod.rs @@ -130,6 +130,9 @@ impl EthernetPTP { // Enable all timestamps .tsena() .set_bit() + // Tell MAC to overwrite non-read timestamps + .txtsstsm() + .set_bit() }); // Set up the subsecond increment From 3b4d04e2e5fef3fcd4fa5e5c046aee7d22718ee7 Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Sat, 4 Feb 2023 23:35:24 +0100 Subject: [PATCH 19/35] Remove these unnecessary things here --- src/dma/tx/f_series_desc.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/dma/tx/f_series_desc.rs b/src/dma/tx/f_series_desc.rs index f787d9b..328e882 100644 --- a/src/dma/tx/f_series_desc.rs +++ b/src/dma/tx/f_series_desc.rs @@ -182,12 +182,3 @@ impl TxDescriptor { self.cached_timestamp.as_ref() } } - -impl TxDescriptor { - /// The initial value for a TxDescriptor - pub const TX_INIT: Self = Self::new(); - - pub(crate) fn prepare_packet(&mut self) -> bool { - !self.is_owned() - } -} From cdd57b40cfe6dcc52fb7dd256dd8f321af454321 Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Sun, 5 Feb 2023 18:24:11 +0100 Subject: [PATCH 20/35] Add set_pps_freq to h7 as well --- src/ptp/mod.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/ptp/mod.rs b/src/ptp/mod.rs index abea9b2..c4ef7b7 100644 --- a/src/ptp/mod.rs +++ b/src/ptp/mod.rs @@ -415,7 +415,6 @@ impl EthernetPTP { is_tsint } - #[cfg(feature = "f-series")] /// Configure the PPS output frequency. /// /// The PPS output frequency becomes `2 ^ pps_freq`. `pps_freq` is @@ -426,10 +425,19 @@ impl EthernetPTP { // SAFETY: we atomically write to the PTPPPSCR register, which is // not read or written to anywhere else. The SVD files are incorrectly // saying that the bits in this register are read-only. + #[cfg(feature = "f-series")] unsafe { let ptpppscr = self.eth_ptp.ptpppscr.as_ptr() as *mut u32; core::ptr::write_volatile(ptpppscr, pps_freq as u32); } + + #[cfg(feature = "stm32h7xx-hal")] + { + // SAFETY: we only access and modify the `macppscr` (PPS Control register) + let macppscr = unsafe { &self.mac().macppscr }; + + macppscr.modify(|_, w| w.ppsctrl().variant(pps_freq)); + } } } @@ -442,7 +450,7 @@ mod test { // with the provided clock speeds. #[test] fn hclk_to_regs() { - for hclk_hz in (25..180).map(|v| v * 1_000_000) { + for hclk_hz in (25..480).map(|v| v * 1_000_000) { let (stssi, tsa) = EthernetPTP::calculate_regs(hclk_hz); let stssi = stssi.raw() as f64; From 04383525d62aa685bc7dc0a0d561cdddd0eb4c78 Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Wed, 8 Feb 2023 21:27:25 +0100 Subject: [PATCH 21/35] Update to smoltcp 0.9 and incorporate all changes that we added with that update --- Cargo.toml | 4 +- src/dma/mod.rs | 47 +++++++++---------- src/dma/rx/f_series_desc.rs | 2 +- src/dma/rx/mod.rs | 64 +++++++++++++++++--------- src/dma/smoltcp_phy.rs | 90 ++++++++++++++++++------------------- src/dma/tx/mod.rs | 36 ++++++++++++--- 6 files changed, 141 insertions(+), 102 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 537cd78..efeeaac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ siphasher = "*" paste = { version = "1.0", optional = true } [dependencies.smoltcp] -version = "0.8.2" +version = "0.9" default-features = false optional = true @@ -83,7 +83,7 @@ cortex-m-rtic = "1.0" defmt-rtt = "0.4" panic-probe = { version = "0.3", features = [ "print-defmt" ] } systick-monotonic = "1.0" -smoltcp = { version = "0.8", features = [ "medium-ethernet", "proto-ipv4", "socket-udp", "socket-tcp", "defmt" ], default-features = false } +smoltcp = { version = "0.9", features = [ "medium-ethernet", "proto-ipv4", "socket-udp", "socket-tcp", "defmt" ], default-features = false } [[example]] name = "pktgen" diff --git a/src/dma/mod.rs b/src/dma/mod.rs index aa7add9..72e27dc 100644 --- a/src/dma/mod.rs +++ b/src/dma/mod.rs @@ -282,44 +282,31 @@ impl<'rx, 'tx> EthernetDMA<'rx, 'tx> { /// It stops if the ring is full. Call `recv_next()` to free an /// entry and to demand poll from the hardware. pub fn rx_is_running(&self) -> bool { - self.rx_ring.running_state(self.eth_dma()).is_running() + self.rx_ring.running_state().is_running() } /// Get the current state of Tx DMA pub fn tx_state(&self) -> RunningState { - self.tx_ring.running_state(self.eth_dma()) - } - - fn recv_next_impl<'rx_borrow>( - eth_dma: ÐERNET_DMA, - rx_ring: &'rx_borrow mut RxRing, - rx_packet_id: Option, - ) -> Result, RxError> { - rx_ring.recv_next(eth_dma, rx_packet_id.map(|p| p.into())) + self.tx_ring.running_state() } /// Receive the next packet (if any is ready), or return `None` /// immediately. pub fn recv_next(&mut self, packet_id: Option) -> Result { - Self::recv_next_impl(&self.parts.eth_dma, &mut self.rx_ring, packet_id) + self.rx_ring.recv_next(packet_id.map(|p| p.into())) } - /// Is Tx DMA currently running? - pub fn tx_is_running(&self) -> bool { - self.tx_ring.is_running(self.eth_dma()) + /// Check whether a frame is available for reception. + /// + /// If this function returns `true`, the next [`TxRing::recv_next`] is + /// guaranteed to succeed. + pub fn rx_available(&mut self) -> bool { + self.rx_ring.available() } - pub(crate) fn send_impl R, R>( - eth_dma: ÐERNET_DMA, - tx_ring: &mut TxRing, - length: usize, - tx_packet_id: Option, - f: F, - ) -> Result { - let result = tx_ring.send(length, tx_packet_id.map(|p| p.into()), f); - tx_ring.demand_poll(eth_dma); - - result + /// Is Tx DMA currently running? + pub fn tx_is_running(&self) -> bool { + self.tx_ring.is_running() } /// Send a packet @@ -329,7 +316,15 @@ impl<'rx, 'tx> EthernetDMA<'rx, 'tx> { packet_id: Option, f: F, ) -> Result { - Self::send_impl(&self.parts.eth_dma, &mut self.tx_ring, length, packet_id, f) + self.tx_ring.send(length, packet_id.map(|p| p.into()), f) + } + + /// Check whether a descriptor is available for sending. + /// + /// If this function returns `true`, the next [`EthernetDMA::send`] is + /// guarantted to succeed. + pub fn tx_available(&mut self) -> bool { + self.tx_ring.available() } #[cfg(feature = "ptp")] diff --git a/src/dma/rx/f_series_desc.rs b/src/dma/rx/f_series_desc.rs index ae8e9e7..ba141dc 100644 --- a/src/dma/rx/f_series_desc.rs +++ b/src/dma/rx/f_series_desc.rs @@ -63,7 +63,7 @@ impl RxDescriptor { } /// Is owned by the DMA engine? - fn is_owned(&self) -> bool { + pub(super) fn is_owned(&self) -> bool { (self.inner_raw.read(0) & RXDESC_0_OWN) == RXDESC_0_OWN } diff --git a/src/dma/rx/mod.rs b/src/dma/rx/mod.rs index d352667..dc69fda 100644 --- a/src/dma/rx/mod.rs +++ b/src/dma/rx/mod.rs @@ -44,21 +44,11 @@ pub struct RxRing<'data, STATE> { } impl<'data, STATE> RxRing<'data, STATE> { - /// Demand that the DMA engine polls the current `RxDescriptor` - /// (when in `RunningState::Stopped`.) - pub fn demand_poll(&self, eth_dma: ÐERNET_DMA) { - #[cfg(feature = "f-series")] - eth_dma.dmarpdr.write(|w| unsafe { w.rpd().bits(1) }); - - // On H7, we poll by re-writing the tail pointer register. - #[cfg(feature = "stm32h7xx-hal")] - eth_dma - .dmacrx_dtpr - .modify(|r, w| unsafe { w.bits(r.bits()) }); - } - /// Get current `RunningState` - pub fn running_state(&self, eth_dma: ÐERNET_DMA) -> RunningState { + pub fn running_state(&self) -> RunningState { + // SAFETY: we only perform an atomic read of `dmasr` + let eth_dma = unsafe { &*ETHERNET_DMA::ptr() }; + #[cfg(feature = "f-series")] let rps = eth_dma.dmasr.read().rps().bits(); #[cfg(feature = "stm32h7xx-hal")] @@ -164,17 +154,43 @@ impl<'data> RxRing<'data, NotRunning> { }); } - self.demand_poll(eth_dma); - - RxRing { + let me = RxRing { ring: self.ring, next_entry: self.next_entry, state: Default::default(), - } + }; + + me.demand_poll(); + + me } } impl<'data> RxRing<'data, Running> { + /// Demand that the DMA engine polls the current `RxDescriptor` + /// (when in `RunningState::Stopped`.) + pub fn demand_poll(&self) { + // # SAFETY + // + // On F7, we only perform an atomic write to `damrpdr`. + // + // On H7, we only perform a Read-Write to `dmacrx_dtpr`, + // always with the same value. Running `demand_poll` concurrently + // with the other location in which this register is written ([`RxRing::start`]) + // is impossible, which is guaranteed the state transition from NotRunning to + // Running. + let eth_dma = unsafe { &*ETHERNET_DMA::ptr() }; + + #[cfg(feature = "f-series")] + eth_dma.dmarpdr.write(|w| unsafe { w.rpd().bits(1) }); + + // On H7, we poll by re-writing the tail pointer register. + #[cfg(feature = "stm32h7xx-hal")] + eth_dma + .dmacrx_dtpr + .modify(|r, w| unsafe { w.bits(r.bits()) }); + } + /// Stop the DMA engine. pub fn stop(&mut self, eth_dma: ÐERNET_DMA) { #[cfg(feature = "f-series")] @@ -185,18 +201,17 @@ impl<'data> RxRing<'data, Running> { start_reg.modify(|_, w| w.sr().clear_bit()); - while self.running_state(eth_dma) != RunningState::Stopped {} + while self.running_state() != RunningState::Stopped {} } /// Receive the next packet (if any is ready), or return `None` /// immediately. pub fn recv_next( &mut self, - eth_dma: ÐERNET_DMA, #[allow(unused_variables)] packet_id: Option, ) -> Result { - if !self.running_state(eth_dma).is_running() { - self.demand_poll(eth_dma); + if !self.running_state().is_running() { + self.demand_poll(); } let entry = self.next_entry; @@ -257,6 +272,11 @@ impl<'data> RxRing<'data, Running> { } }) } + + pub fn available(&mut self) -> bool { + let (desc, _) = self.ring.get(self.next_entry); + !desc.is_owned() + } } #[cfg(feature = "ptp")] diff --git a/src/dma/smoltcp_phy.rs b/src/dma/smoltcp_phy.rs index 089fba2..8a13b5b 100644 --- a/src/dma/smoltcp_phy.rs +++ b/src/dma/smoltcp_phy.rs @@ -1,13 +1,13 @@ -use super::{rx::RxPacket, tx::TxError, EthernetDMA}; -use core::intrinsics::transmute; +use super::rx::{self, RxRing}; +use super::tx::{self, TxRing}; +use super::EthernetDMA; use smoltcp::phy::{ChecksumCapabilities, Device, DeviceCapabilities, RxToken, TxToken}; use smoltcp::time::Instant; -use smoltcp::Error; /// Use this Ethernet driver with [smoltcp](https://github.com/smoltcp-rs/smoltcp) -impl<'a, 'rx, 'tx, 'b> Device<'a> for &'b mut EthernetDMA<'rx, 'tx> { - type RxToken = EthRxToken<'a>; - type TxToken = EthTxToken<'a>; +impl<'a, 'rx, 'tx> Device for &'a mut EthernetDMA<'rx, 'tx> { + type RxToken<'token> = EthRxToken<'token, 'rx> where Self: 'token; + type TxToken<'token> = EthTxToken<'token, 'tx> where Self: 'token; fn capabilities(&self) -> DeviceCapabilities { let mut caps = DeviceCapabilities::default(); @@ -17,65 +17,63 @@ impl<'a, 'rx, 'tx, 'b> Device<'a> for &'b mut EthernetDMA<'rx, 'tx> { caps } - fn receive(&mut self) -> Option<(Self::RxToken, Self::TxToken)> { - let self_ = unsafe { - // HACK: eliminate lifetimes - transmute::<&mut EthernetDMA<'rx, 'tx>, &mut EthernetDMA<'a, 'a>>(*self) - }; - let eth = self_ as *mut EthernetDMA<'a, 'a>; - match self_.recv_next(None) { - Ok(packet) => { - let rx = EthRxToken { packet }; - let tx = EthTxToken { eth }; - Some((rx, tx)) - } - Err(_) => None, + fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + if self.tx_available() && self.rx_available() { + let EthernetDMA { + rx_ring, tx_ring, .. + } = self; + + let rx = EthRxToken { rx_ring }; + + let tx = EthTxToken { tx_ring }; + Some((rx, tx)) + } else { + None } } - fn transmit(&mut self) -> Option { - let eth = unsafe { - transmute::<&mut EthernetDMA<'rx, 'tx>, &mut EthernetDMA<'a, 'a>>(*self) - as *mut EthernetDMA<'a, 'a> - }; - Some(EthTxToken { eth }) + fn transmit(&mut self, _timestamp: Instant) -> Option> { + if self.tx_available() { + let EthernetDMA { tx_ring, .. } = self; + Some(EthTxToken { tx_ring }) + } else { + None + } } } /// An Ethernet RX token that can be consumed in order to receive /// an ethernet packet. -pub struct EthRxToken<'a> { - packet: RxPacket<'a>, +pub struct EthRxToken<'a, 'rx> { + rx_ring: &'a mut RxRing<'rx, rx::Running>, } -impl<'a> RxToken for EthRxToken<'a> { - fn consume(mut self, _timestamp: Instant, f: F) -> Result +impl<'dma, 'rx> RxToken for EthRxToken<'dma, 'rx> { + fn consume(self, f: F) -> R where - F: FnOnce(&mut [u8]) -> Result, + F: FnOnce(&mut [u8]) -> R, { - let result = f(&mut self.packet); - self.packet.free(); + // NOTE(unwrap): an `EthRxToken` is only created when `eth.rx_available()` + let mut packet = self.rx_ring.recv_next(None).ok().unwrap(); + let result = f(&mut packet); + packet.free(); result } } -/// Just a reference to [`Eth`](../struct.EthernetDMA.html) for sending a -/// packet later with [`consume()`](#method.consume). -pub struct EthTxToken<'a> { - eth: *mut EthernetDMA<'a, 'a>, +/// Just a reference to [`EthernetDMA`] for sending a +/// packet later with [`TxToken::consume()`]. +pub struct EthTxToken<'a, 'tx> { + tx_ring: &'a mut TxRing<'tx, tx::Running>, } -impl<'a> TxToken for EthTxToken<'a> { - /// Allocate a [`Buffer`](../struct.Buffer.html), yield with - /// `f(buffer)`, and send it as an Ethernet packet. - fn consume(self, _timestamp: Instant, len: usize, f: F) -> Result +impl<'dma, 'tx> TxToken for EthTxToken<'dma, 'tx> { + fn consume(self, len: usize, f: F) -> R where - F: FnOnce(&mut [u8]) -> Result, + F: FnOnce(&mut [u8]) -> R, { - let eth = unsafe { &mut *self.eth }; - match eth.send(len, None, f) { - Err(TxError::WouldBlock) => Err(Error::Exhausted), - Ok(r) => r, - } + // NOTE(unwrap): an `EthTxToken` is only created if + // there is a descriptor available for sending. + self.tx_ring.send(len, None, f).ok().unwrap() } } diff --git a/src/dma/tx/mod.rs b/src/dma/tx/mod.rs index ca47f8e..cc010e5 100644 --- a/src/dma/tx/mod.rs +++ b/src/dma/tx/mod.rs @@ -39,7 +39,11 @@ pub(crate) struct TxRing<'data, STATE> { } impl<'data, STATE> TxRing<'data, STATE> { - pub fn running_state(&self, eth_dma: ÐERNET_DMA) -> RunningState { + pub fn running_state(&self) -> RunningState { + // SAFETY: we only perform an atomic read of `dmasr` or + // `dmadsr`. + let eth_dma = unsafe { &*ETHERNET_DMA::ptr() }; + #[cfg(feature = "f-series")] let tx_status = eth_dma.dmasr.read().tps().bits(); @@ -182,15 +186,37 @@ impl<'data> TxRing<'data, Running> { self.next_entry = (self.next_entry + 1) % entries_len; + self.demand_poll(); + Ok(r) } else { Err(TxError::WouldBlock) } } + /// Check whether a descriptor is available for sending. + /// + /// If this function returns `true`, the next [`TxRing::send`] is + /// guaranteed to succeed. + pub fn available(&mut self) -> bool { + let (desc, _) = self.ring.get(self.next_entry); + !desc.is_owned() + } + /// Demand that the DMA engine polls the current `TxDescriptor` /// (when we just transferred ownership to the hardware). - pub fn demand_poll(&self, eth_dma: ÐERNET_DMA) { + fn demand_poll(&self) { + // # SAFETY + // + // On F7, we only perform an atomic write to `damrpdr`. + // + // On H7, we only perform a Read-Write to `dmacrx_dtpr`, + // always with the same value. Running `demand_poll` concurrently + // with the other location in which this register is written ([`TxRing::start`]) + // is impossible, which is guaranteed the state transition from NotRunning to + // Running. + let eth_dma = unsafe { &*ETHERNET_DMA::ptr() }; + #[cfg(feature = "stm32h7xx-hal")] // To issue a poll demand, write a value to // the tail pointer. We just re-write the @@ -214,8 +240,8 @@ impl<'data> TxRing<'data, Running> { } /// Is the Tx DMA engine running? - pub fn is_running(&self, eth_dma: ÐERNET_DMA) -> bool { - self.running_state(eth_dma).is_running() + pub fn is_running(&self) -> bool { + self.running_state().is_running() } pub fn stop(&mut self, eth_dma: ÐERNET_DMA) { @@ -227,7 +253,7 @@ impl<'data> TxRing<'data, Running> { // Start transmission start_reg.modify(|_, w| w.st().clear_bit()); - while self.running_state(eth_dma) != RunningState::Stopped {} + while self.running_state() != RunningState::Stopped {} } } From df931cc4aec847b99805f9ac69714071d4720643 Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Wed, 8 Feb 2023 21:53:38 +0100 Subject: [PATCH 22/35] Add MIIM support to H7 --- examples/arp.rs | 3 -- examples/common.rs | 8 +-- examples/rtic-timestamp.rs | 3 +- src/lib.rs | 17 ++++--- src/mac/miim/f_series_miim.rs | 76 ++++++++++++++++++++++++++++ src/mac/miim/h_miim.rs | 59 ++++++++++++++++++++++ src/mac/{miim.rs => miim/mod.rs} | 86 ++++---------------------------- src/mac/mod.rs | 51 ++++++++++--------- 8 files changed, 188 insertions(+), 115 deletions(-) create mode 100644 src/mac/miim/f_series_miim.rs create mode 100644 src/mac/miim/h_miim.rs rename src/mac/{miim.rs => miim/mod.rs} (68%) diff --git a/examples/arp.rs b/examples/arp.rs index c0b2745..ab88b73 100644 --- a/examples/arp.rs +++ b/examples/arp.rs @@ -21,9 +21,6 @@ use stm32_eth::{ Parts, MTU, }; -#[cfg(feature = "f-series")] -use stm32_eth::mac::{phy::BarePhy, Phy}; - pub mod common; use stm32_eth::dma::{RxDescriptorRing, TxDescriptorRing, TxError}; diff --git a/examples/common.rs b/examples/common.rs index e9d0a37..ef74bf3 100644 --- a/examples/common.rs +++ b/examples/common.rs @@ -377,8 +377,8 @@ mod pins { pub type Pps = PB5>; - pub type Mdio = (); - pub type Mdc = (); + pub type Mdio = PA2>; + pub type Mdc = PC1>; pub fn setup_pins( gpio: Gpio, @@ -403,8 +403,8 @@ mod pins { let tx_d0 = gpiog.pg13.into_input(); let tx_d1 = gpiob.pb13.into_input(); - let mdc = (); - let mdio = (); + let mdio = gpioa.pa2.into_alternate(); + let mdc = gpioc.pc1.into_alternate(); let pps = gpiob.pb5.into_push_pull_output(); diff --git a/examples/rtic-timestamp.rs b/examples/rtic-timestamp.rs index e2c645a..e990f2c 100644 --- a/examples/rtic-timestamp.rs +++ b/examples/rtic-timestamp.rs @@ -82,7 +82,7 @@ mod app { defmt::info!("Configuring ethernet"); let Parts { dma, mac, mut ptp } = - stm32_eth::new(ethernet, rx_ring, tx_ring, clocks, pins).unwrap(); + stm32_eth::new_with_mii(ethernet, rx_ring, tx_ring, clocks, pins, mdio, mdc).unwrap(); #[cfg(not(feature = "stm32h7xx-hal"))] ptp.enable_pps(pps); @@ -90,7 +90,6 @@ mod app { defmt::info!("Enabling interrupts"); dma.enable_interrupt(); - #[cfg(not(feature = "stm32h7xx-hal"))] match EthernetPhy::from_miim(mac, 0) { Ok(mut phy) => { defmt::info!( diff --git a/src/lib.rs b/src/lib.rs index a3f7b71..6e0e00c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -56,13 +56,10 @@ pub use smoltcp; #[cfg(feature = "device-selected")] use { dma::{DmaParts, EthernetDMA, RxDescriptorRing, TxDescriptorRing}, - mac::{EthernetMAC, MacParts, Speed, WrongClock}, + mac::{EthernetMAC, EthernetMACWithMii, MacParts, MdcPin, MdioPin, Speed, WrongClock}, setup::*, }; -#[cfg(all(feature = "device-selected", feature = "f-series"))] -use mac::{EthernetMACWithMii, MdcPin, MdioPin}; - #[cfg(all(feature = "device-selected", feature = "ptp"))] use ptp::EthernetPTP; @@ -165,7 +162,7 @@ where /// accessible by the peripheral. Core-Coupled Memory (CCM) is /// usually not accessible. /// - HCLK must be at least 25 MHz. -#[cfg(all(feature = "device-selected", feature = "f-series"))] +#[cfg(all(feature = "device-selected"))] pub fn new_with_mii<'rx, 'tx, REFCLK, CRS, TXEN, TXD0, TXD1, RXD0, RXD1, MDIO, MDC>( parts: PartsIn, rx_buffer: RxDescriptorRing<'rx>, @@ -194,10 +191,13 @@ where let dma_parts = DmaParts { eth_dma: parts.dma.into(), + #[cfg(feature = "stm32h7xx-hal")] + eth_mtl: parts.mtl, }; let mac_parts = MacParts { eth_mac: parts.mac.into(), + #[cfg(feature = "f-series")] eth_mmc: parts.mmc.into(), }; @@ -206,7 +206,12 @@ where // Configure the ethernet PTP #[cfg(feature = "ptp")] - let ptp = EthernetPTP::new(parts.ptp.into(), clocks, &dma); + let ptp = EthernetPTP::new( + #[cfg(feature = "f-series")] + parts.ptp.into(), + clocks, + &dma, + ); // Configure the ethernet MAC let mac = diff --git a/src/mac/miim/f_series_miim.rs b/src/mac/miim/f_series_miim.rs new file mode 100644 index 0000000..9149da3 --- /dev/null +++ b/src/mac/miim/f_series_miim.rs @@ -0,0 +1,76 @@ +use crate::peripherals::ETHERNET_MAC; +use crate::stm32::ethernet_mac::MACMIIAR; + +#[inline(always)] +fn miim_wait_ready(iar: &MACMIIAR) { + while iar.read().mb().bit_is_set() {} +} + +#[inline(always)] +pub(crate) fn miim_write(eth_mac: &mut ETHERNET_MAC, phy: u8, reg: u8, data: u16) { + miim_wait_ready(ð_mac.macmiiar); + eth_mac.macmiidr.write(|w| w.md().bits(data)); + + miim_wait_ready(ð_mac.macmiiar); + + eth_mac.macmiiar.modify(|_, w| { + w.pa() + .bits(phy) + .mr() + .bits(reg) + /* Write operation MW=1*/ + .mw() + .set_bit() + .mb() + .set_bit() + }); + miim_wait_ready(ð_mac.macmiiar); +} + +#[inline(always)] +pub(crate) fn miim_read(eth_mac: &mut ETHERNET_MAC, phy: u8, reg: u8) -> u16 { + miim_wait_ready(ð_mac.macmiiar); + eth_mac.macmiiar.modify(|_, w| { + w.pa() + .bits(phy) + .mr() + .bits(reg) + /* Read operation MW=0 */ + .mw() + .clear_bit() + .mb() + .set_bit() + }); + miim_wait_ready(ð_mac.macmiiar); + + // Return value: + eth_mac.macmiidr.read().md().bits() +} + +#[cfg(feature = "stm32f4xx-hal")] +mod pin_impls { + use crate::hal::gpio::{gpioa::PA2, gpioc::PC1, Alternate}; + + const AF11: u8 = 11; + + unsafe impl crate::mac::MdioPin for PA2> {} + unsafe impl crate::mac::MdcPin for PC1> {} +} + +#[cfg(feature = "stm32f7xx-hal")] +mod pin_impls { + use crate::hal::gpio::{gpioa::PA2, gpioc::PC1, Alternate}; + + const AF11: u8 = 11; + + unsafe impl crate::mac::MdioPin for PA2> {} + unsafe impl crate::mac::MdcPin for PC1> {} +} + +#[cfg(feature = "stm32f1xx-hal")] +mod pin_impls { + use crate::hal::gpio::{gpioa::PA2, gpioc::PC1, Alternate, PushPull}; + + unsafe impl crate::mac::MdioPin for PA2> {} + unsafe impl crate::mac::MdcPin for PC1> {} +} diff --git a/src/mac/miim/h_miim.rs b/src/mac/miim/h_miim.rs new file mode 100644 index 0000000..7771e75 --- /dev/null +++ b/src/mac/miim/h_miim.rs @@ -0,0 +1,59 @@ +use crate::peripherals::ETHERNET_MAC; +use crate::stm32::ethernet_mac::MACMDIOAR; + +use super::{MdcPin, MdioPin}; + +use crate::hal::gpio::{Alternate, PA2, PC1}; + +#[inline(always)] +fn miim_wait_ready(iar: &MACMDIOAR) { + while iar.read().mb().bit_is_set() {} +} + +#[inline(always)] +pub(crate) fn miim_write(eth_mac: &mut ETHERNET_MAC, phy: u8, reg: u8, data: u16) { + miim_wait_ready(ð_mac.macmdioar); + + eth_mac.macmdiodr.write(|w| unsafe { w.md().bits(data) }); + + miim_wait_ready(ð_mac.macmdioar); + + eth_mac.macmdioar.modify(|_, w| unsafe { + w.pa() + .bits(phy) + .rda() + .bits(reg) + /* Write operation GOC=01*/ + .goc() + .variant(0b01) + .mb() + .set_bit() + }); + + miim_wait_ready(ð_mac.macmdioar); +} + +#[inline(always)] +pub(crate) fn miim_read(eth_mac: &mut ETHERNET_MAC, phy: u8, reg: u8) -> u16 { + miim_wait_ready(ð_mac.macmdioar); + + eth_mac.macmdioar.modify(|_, w| unsafe { + w.pa() + .bits(phy) + .rda() + .bits(reg) + /* Write operation GOC=11*/ + .goc() + .variant(0b11) + .mb() + .set_bit() + }); + + miim_wait_ready(ð_mac.macmdioar); + + // Return value: + eth_mac.macmdiodr.read().md().bits() +} + +unsafe impl MdcPin for PC1> {} +unsafe impl MdioPin for PA2> {} diff --git a/src/mac/miim.rs b/src/mac/miim/mod.rs similarity index 68% rename from src/mac/miim.rs rename to src/mac/miim/mod.rs index fdf3c59..e30ce5c 100644 --- a/src/mac/miim.rs +++ b/src/mac/miim/mod.rs @@ -3,10 +3,18 @@ pub use ieee802_3_miim::*; use core::ops::{Deref, DerefMut}; -use crate::{peripherals::ETHERNET_MAC, stm32::ethernet_mac::MACMIIAR}; - use super::EthernetMAC; +#[cfg(feature = "f-series")] +mod f_series_miim; +#[cfg(feature = "f-series")] +use f_series_miim::{miim_read, miim_write}; + +#[cfg(feature = "stm32h7xx-hal")] +mod h_miim; +#[cfg(feature = "stm32h7xx-hal")] +use h_miim::{miim_read, miim_write}; + /// MDIO pin types. /// /// # Safety @@ -21,52 +29,6 @@ pub unsafe trait MdioPin {} /// may implement this trait pub unsafe trait MdcPin {} -#[inline(always)] -fn miim_wait_ready(iar: &MACMIIAR) { - while iar.read().mb().bit_is_set() {} -} - -#[inline(always)] -fn miim_write(eth_mac: &mut ETHERNET_MAC, phy: u8, reg: u8, data: u16) { - miim_wait_ready(ð_mac.macmiiar); - eth_mac.macmiidr.write(|w| w.md().bits(data)); - - miim_wait_ready(ð_mac.macmiiar); - - eth_mac.macmiiar.modify(|_, w| { - w.pa() - .bits(phy) - .mr() - .bits(reg) - /* Write operation MW=1*/ - .mw() - .set_bit() - .mb() - .set_bit() - }); - miim_wait_ready(ð_mac.macmiiar); -} - -#[inline(always)] -fn miim_read(eth_mac: &mut ETHERNET_MAC, phy: u8, reg: u8) -> u16 { - miim_wait_ready(ð_mac.macmiiar); - eth_mac.macmiiar.modify(|_, w| { - w.pa() - .bits(phy) - .mr() - .bits(reg) - /* Read operation MW=0 */ - .mw() - .clear_bit() - .mb() - .set_bit() - }); - miim_wait_ready(ð_mac.macmiiar); - - // Return value: - eth_mac.macmiidr.read().md().bits() -} - /// Serial Management Interface /// /// Borrows an [`EthernetMAC`] and holds a mutable borrow to the SMI pins. @@ -120,34 +82,6 @@ where } } -#[cfg(feature = "stm32f4xx-hal")] -mod pin_impls { - use crate::hal::gpio::{gpioa::PA2, gpioc::PC1, Alternate}; - - const AF11: u8 = 11; - - unsafe impl super::MdioPin for PA2> {} - unsafe impl super::MdcPin for PC1> {} -} - -#[cfg(feature = "stm32f7xx-hal")] -mod pin_impls { - use crate::hal::gpio::{gpioa::PA2, gpioc::PC1, Alternate}; - - const AF11: u8 = 11; - - unsafe impl super::MdioPin for PA2> {} - unsafe impl super::MdcPin for PC1> {} -} - -#[cfg(feature = "stm32f1xx-hal")] -mod pin_impls { - use crate::hal::gpio::{gpioa::PA2, gpioc::PC1, Alternate, PushPull}; - - unsafe impl super::MdioPin for PA2> {} - unsafe impl super::MdcPin for PC1> {} -} - /// Ethernet media access control (MAC) with owned MII /// /// This version of the struct owns it's MII pins, diff --git a/src/mac/mod.rs b/src/mac/mod.rs index db7e681..0fb78f8 100644 --- a/src/mac/mod.rs +++ b/src/mac/mod.rs @@ -2,9 +2,7 @@ use crate::{dma::EthernetDMA, peripherals::ETHERNET_MAC, Clocks}; -#[cfg(feature = "f-series")] mod miim; -#[cfg(feature = "f-series")] pub use miim::*; pub(crate) struct MacParts { @@ -111,9 +109,7 @@ pub enum Speed { FullDuplexBase100Tx, } -#[cfg(feature = "f-series")] use self::consts::*; -#[cfg(feature = "f-series")] mod consts { /* For HCLK 60-100 MHz */ pub const ETH_MACMIIAR_CR_HCLK_DIV_42: u8 = 0; @@ -123,8 +119,11 @@ mod consts { pub const ETH_MACMIIAR_CR_HCLK_DIV_16: u8 = 2; /* For HCLK 35-60 MHz */ pub const ETH_MACMIIAR_CR_HCLK_DIV_26: u8 = 3; - /* For HCLK over 150 MHz */ + /* For HCLK 150-250 MHz */ pub const ETH_MACMIIAR_CR_HCLK_DIV_102: u8 = 4; + /* For HCLK over 250 MHz */ + #[cfg(feature = "stm32h7xx-hal")] + pub const ETH_MACMIIAR_CR_HCLK_DIV_124: u8 = 5; } /// HCLK must be at least 25MHz to use the ethernet peripheral. @@ -162,24 +161,30 @@ impl EthernetMAC { ) -> Result { let eth_mac = &parts.eth_mac; - // TODO: configure MDIOS + let clock_frequency = clocks.hclk().to_Hz(); + + let clock_range = match clock_frequency { + 0..=24_999_999 => return Err(WrongClock), + 25_000_000..=34_999_999 => ETH_MACMIIAR_CR_HCLK_DIV_16, + 35_000_000..=59_999_999 => ETH_MACMIIAR_CR_HCLK_DIV_26, + 60_000_000..=99_999_999 => ETH_MACMIIAR_CR_HCLK_DIV_42, + 100_000_000..=149_999_999 => ETH_MACMIIAR_CR_HCLK_DIV_62, + #[cfg(feature = "stm32h7xx-hal")] + 150_000_000..=250_000_000 => ETH_MACMIIAR_CR_HCLK_DIV_102, + #[cfg(feature = "stm32h7xx-hal")] + _ => ETH_MACMIIAR_CR_HCLK_DIV_124, + #[cfg(feature = "f-series")] + _ => ETH_MACMIIAR_CR_HCLK_DIV_102, + }; + #[cfg(feature = "f-series")] - { - let clock_frequency = clocks.hclk().to_Hz(); - let clock_range = match clock_frequency { - 0..=24_999_999 => return Err(WrongClock), - 25_000_000..=34_999_999 => ETH_MACMIIAR_CR_HCLK_DIV_16, - 35_000_000..=59_999_999 => ETH_MACMIIAR_CR_HCLK_DIV_26, - 60_000_000..=99_999_999 => ETH_MACMIIAR_CR_HCLK_DIV_42, - 100_000_000..=149_999_999 => ETH_MACMIIAR_CR_HCLK_DIV_62, - _ => ETH_MACMIIAR_CR_HCLK_DIV_102, - }; - - // Set clock range in MAC MII address register - eth_mac - .macmiiar - .modify(|_, w| unsafe { w.cr().bits(clock_range) }); - } + // Set clock range in MAC MII address register + eth_mac + .macmiiar + .modify(|_, w| unsafe { w.cr().bits(clock_range) }); + + #[cfg(feature = "stm32h7xx-hal")] + eth_mac.macmdioar.modify(|_, w| w.cr().variant(clock_range)); // Configuration Register eth_mac.maccr.modify(|_, w| { @@ -271,7 +276,6 @@ impl EthernetMAC { /// /// Exclusive access to the `MDIO` and `MDC` is required to ensure that are not used elsewhere /// for the duration of Mii communication. - #[cfg(feature = "f-series")] pub fn mii<'eth, 'pins, Mdio, Mdc>( &'eth mut self, mdio: &'pins mut Mdio, @@ -285,7 +289,6 @@ impl EthernetMAC { } /// Turn this [`EthernetMAC`] into an [`EthernetMACWithMii`] - #[cfg(feature = "f-series")] pub fn with_mii(self, mdio: MDIO, mdc: MDC) -> EthernetMACWithMii where MDIO: MdioPin, From 690a9d1f0863737f29757d38df56bd1ebb7ac82c Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Wed, 8 Feb 2023 21:57:43 +0100 Subject: [PATCH 23/35] Add PPS pin implementations for H7 Fix up MII examples --- examples/arp.rs | 13 ++++--------- examples/rtic-timestamp.rs | 7 ++----- src/ptp/pps_pin.rs | 7 +++++++ 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/examples/arp.rs b/examples/arp.rs index ab88b73..129044a 100644 --- a/examples/arp.rs +++ b/examples/arp.rs @@ -8,6 +8,7 @@ #![no_main] use defmt_rtt as _; +use ieee802_3_miim::{phy::BarePhy, Phy}; use panic_probe as _; use core::cell::RefCell; @@ -16,14 +17,13 @@ use cortex_m_rt::{entry, exception}; use cortex_m::interrupt::Mutex; use stm32_eth::{ - dma::{RxDescriptor, TxDescriptor}, stm32::{interrupt, CorePeripherals, Peripherals, SYST}, - Parts, MTU, + Parts, }; pub mod common; -use stm32_eth::dma::{RxDescriptorRing, TxDescriptorRing, TxError}; +use stm32_eth::dma::TxError; const PHY_ADDR: u8 = 0; @@ -55,16 +55,11 @@ fn main() -> ! { let mut last_link_up = false; - #[cfg(feature = "f-series")] - let mut bare_phy = BarePhy::new(_mac.with_mii(mdio, mdc), PHY_ADDR, Default::default()); + let mut bare_phy = BarePhy::new(mac.with_mii(mdio, mdc), PHY_ADDR, Default::default()); loop { - #[cfg(feature = "f-series")] let link_up = bare_phy.phy_link_up(); - #[cfg(feature = "stm32h7xx-hal")] - let link_up = true; - if link_up != last_link_up { if link_up { defmt::info!("Ethernet: link detected"); diff --git a/examples/rtic-timestamp.rs b/examples/rtic-timestamp.rs index e990f2c..30b86fa 100644 --- a/examples/rtic-timestamp.rs +++ b/examples/rtic-timestamp.rs @@ -43,12 +43,10 @@ mod app { use systick_monotonic::Systick; use stm32_eth::{ - dma::{ - EthernetDMA, PacketId, RxDescriptor, RxDescriptorRing, TxDescriptor, TxDescriptorRing, - }, + dma::{EthernetDMA, PacketId}, mac::Speed, ptp::{EthernetPTP, Timestamp}, - Parts, MTU, + Parts, }; #[local] @@ -84,7 +82,6 @@ mod app { let Parts { dma, mac, mut ptp } = stm32_eth::new_with_mii(ethernet, rx_ring, tx_ring, clocks, pins, mdio, mdc).unwrap(); - #[cfg(not(feature = "stm32h7xx-hal"))] ptp.enable_pps(pps); defmt::info!("Enabling interrupts"); diff --git a/src/ptp/pps_pin.rs b/src/ptp/pps_pin.rs index 99a05b9..bc68810 100644 --- a/src/ptp/pps_pin.rs +++ b/src/ptp/pps_pin.rs @@ -63,3 +63,10 @@ mod impl_pps_pin { } } } + +#[cfg(feature = "stm32h7xx-hal")] +mod impl_pps_pin { + use crate::hal::gpio::{Alternate, Output, PushPull, PB5, PG8}; + + impl_pps_pin!([PG8>, PG8>], [PB5>, PB5>]); +} From 261a47680485b3cd3e5f6d151d45e6c54d278e99 Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Wed, 8 Feb 2023 21:58:36 +0100 Subject: [PATCH 24/35] Update rtic-echo from branch master --- examples/rtic-echo.rs | 148 ++++++++++++++++-------------------------- 1 file changed, 55 insertions(+), 93 deletions(-) diff --git a/examples/rtic-echo.rs b/examples/rtic-echo.rs index ea836c2..cc636b9 100644 --- a/examples/rtic-echo.rs +++ b/examples/rtic-echo.rs @@ -10,14 +10,12 @@ use defmt_rtt as _; use panic_probe as _; -use smoltcp::{ - iface::{self, SocketStorage}, - wire::{self, IpAddress, Ipv4Address}, -}; +use smoltcp::wire::{IpAddress, Ipv4Address}; mod common; -const ADDRESS: (IpAddress, u16) = (IpAddress::Ipv4(Ipv4Address::new(10, 0, 0, 1)), 1337); +const IP_ADDRESS: Ipv4Address = Ipv4Address::new(10, 0, 0, 1); +const SOCKET_ADDRESS: (IpAddress, u16) = (IpAddress::Ipv4(IP_ADDRESS), 1337); const MAC: [u8; 6] = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05]; #[rtic::app(device = stm32_eth::stm32, dispatchers = [SPI1])] @@ -28,25 +26,20 @@ mod app { use ieee802_3_miim::{phy::PhySpeed, Phy}; use systick_monotonic::Systick; - use stm32_eth::{ - dma::{EthernetDMA, RxDescriptor, RxDescriptorRing, TxDescriptor, TxDescriptorRing}, - mac::Speed, - Parts, - }; + use stm32_eth::{dma::EthernetDMA, mac::Speed, Parts}; use smoltcp::{ - iface::{self, Interface, SocketHandle}, - socket::TcpSocket, - socket::{TcpSocketBuffer, TcpState}, - wire::EthernetAddress, + iface::{self, Interface, SocketHandle, SocketSet, SocketStorage}, + socket::tcp::{Socket as TcpSocket, SocketBuffer as TcpSocketBuffer, State as TcpState}, + wire::{EthernetAddress, IpCidr, Ipv4Cidr}, }; - use crate::NetworkStorage; - #[local] struct Local { - interface: Interface<'static, &'static mut EthernetDMA<'static, 'static>>, + interface: Interface, tcp_handle: SocketHandle, + dma: EthernetDMA<'static, 'static>, + sockets: SocketSet<'static>, } #[shared] @@ -61,69 +54,70 @@ mod app { } #[init(local = [ - rx_desc: [RxDescriptor; 2] = [RxDescriptor::new(); 2], - tx_desc: [TxDescriptor; 2] = [TxDescriptor::new(); 2], - rx_buffers: [[u8; 1522]; 2] = [[0u8; stm32_eth::MTU]; 2], - tx_buffers: [[u8; 1522]; 2] = [[0u8; stm32_eth::MTU]; 2], - storage: NetworkStorage = NetworkStorage::new(), - dma: core::mem::MaybeUninit> = core::mem::MaybeUninit::uninit(), + rx_storage: [u8; 512] = [0u8; 512], + tx_storage: [u8; 512] = [0u8; 512], + socket_storage: [SocketStorage<'static>; 1] = [SocketStorage::EMPTY; 1], ])] fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { defmt::info!("Pre-init"); let core = cx.core; let p = cx.device; - let rx_ring = RxDescriptorRing::new(cx.local.rx_desc, cx.local.rx_buffers); - let tx_ring = TxDescriptorRing::new(cx.local.tx_desc, cx.local.tx_buffers); + let (tx_ring, rx_ring) = crate::common::setup_rings(); let (clocks, gpio, ethernet) = crate::common::setup_peripherals(p); let mono = Systick::new(core.SYST, clocks.hclk().raw()); + let (rx_storage, tx_storage, socket_storage) = ( + cx.local.rx_storage, + cx.local.tx_storage, + cx.local.socket_storage, + ); + defmt::info!("Setting up pins"); let (pins, mdio, mdc, _) = crate::common::setup_pins(gpio); defmt::info!("Configuring ethernet"); let Parts { - dma, + mut dma, mac, #[cfg(feature = "ptp")] ptp: _, } = stm32_eth::new_with_mii(ethernet, rx_ring, tx_ring, clocks, pins, mdio, mdc).unwrap(); - let dma = cx.local.dma.write(dma); - defmt::info!("Enabling interrupts"); dma.enable_interrupt(); defmt::info!("Setting up smoltcp"); - let store = cx.local.storage; - let mut routes = smoltcp::iface::Routes::new(&mut store.routes_cache[..]); + let mut routes = smoltcp::iface::Routes::new(); routes .add_default_ipv4_route(smoltcp::wire::Ipv4Address::UNSPECIFIED) .ok(); - let neighbor_cache = smoltcp::iface::NeighborCache::new(&mut store.neighbor_cache[..]); - - let rx_buffer = TcpSocketBuffer::new(&mut store.tcp_socket_storage.rx_storage[..]); - let tx_buffer = TcpSocketBuffer::new(&mut store.tcp_socket_storage.tx_storage[..]); + let rx_buffer = TcpSocketBuffer::new(&mut rx_storage[..]); + let tx_buffer = TcpSocketBuffer::new(&mut tx_storage[..]); let socket = TcpSocket::new(rx_buffer, tx_buffer); - let mut interface = iface::InterfaceBuilder::new(dma, &mut store.sockets[..]) - .hardware_addr(EthernetAddress::from_bytes(&crate::MAC).into()) - .neighbor_cache(neighbor_cache) - .ip_addrs(&mut store.ip_addrs[..]) - .routes(routes) - .finalize(); + let mut config = iface::Config::new(); + config.hardware_addr = Some(EthernetAddress::from_bytes(&crate::MAC).into()); - let tcp_handle = interface.add_socket(socket); + let mut interface = Interface::new(config, &mut &mut dma); + interface.update_ip_addrs(|addr| { + addr.push(IpCidr::Ipv4(Ipv4Cidr::new(crate::IP_ADDRESS, 24))) + .ok(); + }); - let socket = interface.get_socket::(tcp_handle); - socket.listen(crate::ADDRESS).ok(); + let mut sockets = SocketSet::new(&mut socket_storage[..]); - interface.poll(now_fn()).unwrap(); + let tcp_handle = sockets.add(socket); + + let socket = sockets.get_mut::(tcp_handle); + socket.listen(crate::SOCKET_ADDRESS).ok(); + + interface.poll(now_fn(), &mut &mut dma, &mut sockets); if let Ok(mut phy) = EthernetPhy::from_miim(mac, 0) { defmt::info!( @@ -154,28 +148,36 @@ mod app { defmt::info!("Not resetting unsupported PHY. Cannot detect link speed."); } - defmt::info!("Setup done. Listening at {}", crate::ADDRESS); + defmt::info!("Setup done. Listening at {}", crate::SOCKET_ADDRESS); ( Shared {}, Local { interface, tcp_handle, + dma, + sockets, }, init::Monotonics(mono), ) } - #[task(binds = ETH, local = [interface, tcp_handle, data: [u8; 512] = [0u8; 512]], priority = 2)] + #[task(binds = ETH, local = [interface, tcp_handle, dma, sockets, data: [u8; 512] = [0u8; 512]], priority = 2)] fn eth_interrupt(cx: eth_interrupt::Context) { - let (iface, tcp_handle, buffer) = (cx.local.interface, cx.local.tcp_handle, cx.local.data); - - let interrupt_reason = iface.device_mut().interrupt_handler(); + let (iface, tcp_handle, buffer, sockets, mut dma) = ( + cx.local.interface, + cx.local.tcp_handle, + cx.local.data, + cx.local.sockets, + cx.local.dma, + ); + + let interrupt_reason = dma.interrupt_handler(); defmt::debug!("Got an ethernet interrupt! Reason: {}", interrupt_reason); - iface.poll(now_fn()).ok(); + iface.poll(now_fn(), &mut dma, sockets); - let socket = iface.get_socket::(*tcp_handle); + let socket = sockets.get_mut::(*tcp_handle); if let Ok(recv_bytes) = socket.recv_slice(buffer) { if recv_bytes > 0 { socket.send_slice(&buffer[..recv_bytes]).ok(); @@ -185,50 +187,10 @@ mod app { if !socket.is_listening() && !socket.is_open() || socket.state() == TcpState::CloseWait { socket.abort(); - socket.listen(crate::ADDRESS).ok(); + socket.listen(crate::SOCKET_ADDRESS).ok(); defmt::warn!("Disconnected... Reopening listening socket."); } - iface.poll(now_fn()).ok(); - } -} - -/// All storage required for networking -pub struct NetworkStorage { - pub ip_addrs: [wire::IpCidr; 1], - pub sockets: [iface::SocketStorage<'static>; 1], - pub tcp_socket_storage: TcpSocketStorage, - pub neighbor_cache: [Option<(wire::IpAddress, iface::Neighbor)>; 8], - pub routes_cache: [Option<(wire::IpCidr, iface::Route)>; 8], -} - -impl NetworkStorage { - const IP_INIT: wire::IpCidr = - wire::IpCidr::Ipv4(wire::Ipv4Cidr::new(wire::Ipv4Address::new(10, 0, 0, 1), 24)); - - pub const fn new() -> Self { - NetworkStorage { - ip_addrs: [Self::IP_INIT], - neighbor_cache: [None; 8], - routes_cache: [None; 8], - sockets: [SocketStorage::EMPTY; 1], - tcp_socket_storage: TcpSocketStorage::new(), - } - } -} - -/// Storage of TCP sockets -#[derive(Copy, Clone)] -pub struct TcpSocketStorage { - rx_storage: [u8; 512], - tx_storage: [u8; 512], -} - -impl TcpSocketStorage { - const fn new() -> Self { - Self { - rx_storage: [0; 512], - tx_storage: [0; 512], - } + iface.poll(now_fn(), &mut dma, sockets); } } From 81f80e409ca1b7f147794d97a0be9240830e0914 Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Wed, 8 Feb 2023 22:55:50 +0100 Subject: [PATCH 25/35] Fix CIC bits in TX descriptor & enable more interrupts --- src/dma/mod.rs | 13 +++++++++++++ src/dma/rx/h_desc.rs | 22 +++++++++------------- src/dma/rx/mod.rs | 15 ++++++++++----- src/dma/tx/h_desc.rs | 10 +++++----- 4 files changed, 37 insertions(+), 23 deletions(-) diff --git a/src/dma/mod.rs b/src/dma/mod.rs index 72e27dc..45cbc1d 100644 --- a/src/dma/mod.rs +++ b/src/dma/mod.rs @@ -257,6 +257,15 @@ impl<'rx, 'tx> EthernetDMA<'rx, 'tx> { // Transmit Interrupt Enable .tie() .set_bit() + // Abnormal Interrupt Summary enable + .aie() + .set_bit() + // Receive Buffer Unavailable + .rbue() + .set_bit() + // Transmit Buffer Unavailable + .tbue() + .set_bit() }); // Enable ethernet interrupts @@ -429,6 +438,10 @@ fn eth_interrupt_handler_impl(eth_dma: ÐERNET_DMA) -> InterruptReasonSummary .set_bit() .ri() .set_bit() + .rbu() + .set_bit() + .tbu() + .set_bit() }); ( diff --git a/src/dma/rx/h_desc.rs b/src/dma/rx/h_desc.rs index 2e63a66..2fb982a 100644 --- a/src/dma/rx/h_desc.rs +++ b/src/dma/rx/h_desc.rs @@ -55,16 +55,16 @@ mod consts { pub const RXDESC_3_LT_MASK: u32 = 0b111 << RXDESC_3_LT_SHIFT; /// Length/Type Field #[allow(non_camel_case_types)] - #[repr(u8)] + #[repr(u32)] pub enum RXDESC_3_LT { - Length = 0b000, - Type = 0b001, - Reserved = 0b010, - ArpRequest = 0b011, - TypeWithVlan = 0b100, - TypeWIthDoubleVlan = 0b101, - MacControl = 0b110, - Oam = 0b111, + Length = 0b000 << RXDESC_3_LT_SHIFT, + Type = 0b001 << RXDESC_3_LT_SHIFT, + Reserved = 0b010 << RXDESC_3_LT_SHIFT, + ArpRequest = 0b011 << RXDESC_3_LT_SHIFT, + TypeWithVlan = 0b100 << RXDESC_3_LT_SHIFT, + TypeWIthDoubleVlan = 0b101 << RXDESC_3_LT_SHIFT, + MacControl = 0b110 << RXDESC_3_LT_SHIFT, + Oam = 0b111 << RXDESC_3_LT_SHIFT, } /// Error Summary @@ -162,10 +162,6 @@ impl RxDescriptor { .modify(3, |w| w | RXDESC_3_OWN | RXDESC_3_IOC); } - cortex_m::asm::dsb(); - - assert!(self.is_owned()); - // Used to flush the store buffer as fast as possible to make the buffer available for the // DMA. #[cfg(feature = "fence")] diff --git a/src/dma/rx/mod.rs b/src/dma/rx/mod.rs index dc69fda..efcdd90 100644 --- a/src/dma/rx/mod.rs +++ b/src/dma/rx/mod.rs @@ -216,6 +216,7 @@ impl<'data> RxRing<'data, Running> { let entry = self.next_entry; let entries_len = self.ring.len(); + let (descriptor, buffer) = self.ring.get(entry); let mut res = descriptor.take_received(packet_id, buffer); @@ -226,7 +227,7 @@ impl<'data> RxRing<'data, Running> { #[cfg(all(feature = "ptp", feature = "stm32h7xx-hal"))] let (timestamp, descriptor, buffer) = { - if res.as_mut().err() != Some(&mut RxError::WouldBlock) { + if res.is_ok() { let desc_has_timestamp = descriptor.has_timestamp(); drop(descriptor); @@ -234,12 +235,10 @@ impl<'data> RxRing<'data, Running> { // On H7's, the timestamp is stored in the next Context // descriptor. + let (ctx_descriptor, ctx_des_buffer) = self.ring.get(self.next_entry); + let timestamp = if desc_has_timestamp { - let (ctx_descriptor, ctx_des_buffer) = self.ring.get(self.next_entry); if let Some(timestamp) = ctx_descriptor.read_timestamp() { - ctx_descriptor.set_owned(ctx_des_buffer); - // Advance over this buffer - self.next_entry = (self.next_entry + 1) % entries_len; Some(timestamp) } else { None @@ -248,6 +247,12 @@ impl<'data> RxRing<'data, Running> { None }; + if !ctx_descriptor.is_owned() { + // Advance over this buffer + self.next_entry = (self.next_entry + 1) % entries_len; + ctx_descriptor.set_owned(&ctx_des_buffer); + } + let (descriptor, buffer) = self.ring.get(entry); descriptor.attach_timestamp(timestamp); diff --git a/src/dma/tx/h_desc.rs b/src/dma/tx/h_desc.rs index 9c340e9..c080032 100644 --- a/src/dma/tx/h_desc.rs +++ b/src/dma/tx/h_desc.rs @@ -68,10 +68,10 @@ mod consts { #[repr(u32)] #[allow(non_camel_case_types)] pub enum TXDESC_3_CIC { - Disabled = 0b00, - IpHeaderOnly = 0b01, - IpHeaderAndPayloadOnly = 0b10, - IpHeaderAndPayloadAndPseudoHeader = 0b11, + Disabled = 0b00 << TXDESC_3_CIC_SHIFT, + IpHeaderOnly = 0b01 << TXDESC_3_CIC_SHIFT, + IpHeaderAndPayloadOnly = 0b10 << TXDESC_3_CIC_SHIFT, + IpHeaderAndPayloadAndPseudoHeader = 0b11 << TXDESC_3_CIC_SHIFT, } /// Checksum Insertion Control mask pub const TXDESC_3_CIC_MASK: u32 = 0b11 << TXDESC_3_CIC_SHIFT; @@ -192,7 +192,7 @@ impl TxDescriptor { self.inner_raw.modify(3, |w| { w | TXDESC_3_OWN - | TXDESC_3_CIC::IpHeaderAndPayloadOnly as u32 + | TXDESC_3_CIC::IpHeaderAndPayloadAndPseudoHeader as u32 | TXDESC_3_FD | TXDESC_3_LD | tx_len From b2fd80193650870fd77c8bf7f1bd7e5c97a07096 Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Wed, 8 Feb 2023 23:09:12 +0100 Subject: [PATCH 26/35] Make hclk/sysclk divisible with 8 --- examples/common.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/common.rs b/examples/common.rs index ef74bf3..4be3954 100644 --- a/examples/common.rs +++ b/examples/common.rs @@ -71,7 +71,7 @@ pub fn setup_peripherals(p: stm32_eth::stm32::Peripherals) -> (Clocks, Gpio, Par { let rcc = p.RCC.constrain(); - let clocks = rcc.cfgr.sysclk(100.MHz()).hclk(100.MHz()); + let clocks = rcc.cfgr.sysclk(96.MHz()).hclk(96.MHz()); #[cfg(feature = "stm32f4xx-hal")] let clocks = { From f50db4ab2b79b327a5f2c08935460782e6a2889a Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Thu, 9 Feb 2023 10:54:31 +0100 Subject: [PATCH 27/35] Clean up TX descriptors a little --- src/dma/raw_descriptor.rs | 8 ++++---- src/dma/tx/f_series_desc.rs | 5 +++-- src/dma/tx/h_desc.rs | 4 ++-- src/dma/tx/mod.rs | 9 +++------ 4 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/dma/raw_descriptor.rs b/src/dma/raw_descriptor.rs index 4ba1c7f..724dc43 100644 --- a/src/dma/raw_descriptor.rs +++ b/src/dma/raw_descriptor.rs @@ -80,6 +80,10 @@ impl<'data, T> DescriptorRing<'data, T> { (&mut self.descriptors[index], &mut self.buffers[index]) } + pub fn descriptors_mut(&mut self) -> impl Iterator { + self.descriptors.iter_mut() + } + pub fn descriptors(&self) -> impl Iterator { self.descriptors.iter() } @@ -100,10 +104,6 @@ impl<'data, T> DescriptorRing<'data, T> { &self.buffers[self.buffers.len() - 1] } - pub fn descriptors_mut(&mut self) -> impl Iterator { - self.descriptors.iter_mut() - } - pub fn descriptors_and_buffers( &mut self, ) -> impl Iterator { diff --git a/src/dma/tx/f_series_desc.rs b/src/dma/tx/f_series_desc.rs index 328e882..152c9f7 100644 --- a/src/dma/tx/f_series_desc.rs +++ b/src/dma/tx/f_series_desc.rs @@ -65,8 +65,9 @@ impl TxDescriptor { } } - pub(super) fn setup(&mut self, buffer: &mut [u8]) { - self.set_buffer(buffer); + pub(super) fn setup(&mut self) { + (0..crate::dma::raw_descriptor::DESC_SIZE) + .for_each(|i| unsafe { self.inner_raw.write(i, 0) }); } #[allow(unused)] diff --git a/src/dma/tx/h_desc.rs b/src/dma/tx/h_desc.rs index c080032..48270c2 100644 --- a/src/dma/tx/h_desc.rs +++ b/src/dma/tx/h_desc.rs @@ -149,10 +149,10 @@ impl TxDescriptor { (self.inner_raw.read(3) & TXDESC_3_LD) == TXDESC_3_LD } - // Placeholder for API parity with f-series descriptor. - pub(super) fn setup(&mut self, _: &[u8]) { + pub(super) fn setup(&mut self) { // Zero-out all fields in the descriptor (0..4).for_each(|n| unsafe { self.inner_raw.write(n, 0) }); + self.packet_id.take(); } pub(super) fn is_owned(&self) -> bool { diff --git a/src/dma/tx/mod.rs b/src/dma/tx/mod.rs index cc010e5..8bcc8aa 100644 --- a/src/dma/tx/mod.rs +++ b/src/dma/tx/mod.rs @@ -95,8 +95,8 @@ impl<'data> TxRing<'data, NotRunning> { /// Start the Tx DMA engine pub fn start(mut self, eth_dma: ÐERNET_DMA) -> TxRing<'data, Running> { // Setup ring - for (descriptor, buffer) in self.ring.descriptors_and_buffers() { - descriptor.setup(buffer); + for descriptor in self.ring.descriptors_mut() { + descriptor.setup(); } #[cfg(feature = "f-series")] @@ -120,10 +120,7 @@ impl<'data> TxRing<'data, NotRunning> { // Assert that the descriptors are properly aligned. assert!(ring_ptr as u32 & !0b11 == ring_ptr as u32); - assert!( - self.ring.last_descriptor() as *const _ as u32 & !0b11 - == self.ring.last_descriptor() as *const _ as u32 - ); + assert!(self.ring.last_descriptor() as *const _ as u32 % 4 == 0); // Set the start pointer. eth_dma From 486c2ad0e80600acb2738381cbb5744139d7d5df Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Thu, 9 Feb 2023 21:37:40 +0100 Subject: [PATCH 28/35] This is a PTP-only function --- src/dma/rx/h_desc.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/dma/rx/h_desc.rs b/src/dma/rx/h_desc.rs index 2fb982a..2a0bfd6 100644 --- a/src/dma/rx/h_desc.rs +++ b/src/dma/rx/h_desc.rs @@ -126,10 +126,6 @@ impl RxDescriptor { self.inner_raw.read(3) & RXDESC_3_CTXT == RXDESC_3_CTXT } - pub(super) fn has_timestamp(&self) -> bool { - (self.inner_raw.read(1) & RXDESC_1_TSA) == RXDESC_1_TSA && self.is_last() - } - pub(super) fn frame_length(&self) -> usize { if self.is_owned() { 0 @@ -214,6 +210,10 @@ impl RxDescriptor { #[cfg(feature = "ptp")] impl RxDescriptor { + pub(super) fn has_timestamp(&self) -> bool { + (self.inner_raw.read(1) & RXDESC_1_TSA) == RXDESC_1_TSA && self.is_last() + } + /// Get PTP timestamps if available pub(super) fn read_timestamp(&self) -> Option { if self.is_context() && !self.is_owned() { From 7f79c40e78126ca389eba91a8029ecff68512c43 Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Thu, 9 Feb 2023 21:42:18 +0100 Subject: [PATCH 29/35] Update Cargo.toml --- Cargo.toml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index efeeaac..b98e52e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,8 +30,6 @@ ieee802_3_miim = "0.8" cortex-m = "0.7" log = { version = "0.4", optional = true } defmt = { version = "0.3", optional = true } -siphasher = "*" -paste = { version = "1.0", optional = true } [dependencies.smoltcp] version = "0.9" @@ -48,7 +46,7 @@ f-series = [ ] stm32f1xx-hal = [ "dep:stm32f1xx-hal", "f-series" ] stm32f4xx-hal = [ "dep:stm32f4xx-hal", "f-series" ] stm32f7xx-hal = [ "dep:stm32f7xx-hal", "f-series" ] -stm32h7xx-hal = [ "dep:stm32h7xx-hal", "paste" ] +stm32h7xx-hal = [ "dep:stm32h7xx-hal" ] stm32f107 = ["stm32f1xx-hal/stm32f107", "device-selected"] @@ -84,6 +82,7 @@ defmt-rtt = "0.4" panic-probe = { version = "0.3", features = [ "print-defmt" ] } systick-monotonic = "1.0" smoltcp = { version = "0.9", features = [ "medium-ethernet", "proto-ipv4", "socket-udp", "socket-tcp", "defmt" ], default-features = false } +siphasher = "0.3" [[example]] name = "pktgen" From 900536cf0e9f211bc179fd34075c22f16292d831 Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Thu, 9 Feb 2023 21:51:47 +0100 Subject: [PATCH 30/35] Fix compilation of examples --- examples/ip.rs | 18 ++++-------------- examples/pktgen.rs | 15 ++++----------- 2 files changed, 8 insertions(+), 25 deletions(-) diff --git a/examples/ip.rs b/examples/ip.rs index d6e6908..9a5c55c 100644 --- a/examples/ip.rs +++ b/examples/ip.rs @@ -22,10 +22,7 @@ use smoltcp::wire::{EthernetAddress, IpCidr, Ipv4Address, Ipv4Cidr}; pub mod common; -use stm32_eth::{ - dma::{RxRingEntry, TxRingEntry}, - Parts, -}; +use stm32_eth::Parts; const IP_ADDRESS: Ipv4Address = Ipv4Address::new(10, 0, 0, 1); const SRC_MAC: [u8; 6] = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; @@ -46,21 +43,14 @@ fn main() -> ! { let (eth_pins, _mdio, _mdc, _) = common::setup_pins(gpio); - let mut rx_ring: [RxRingEntry; 2] = Default::default(); - let mut tx_ring: [TxRingEntry; 2] = Default::default(); + let (tx_ring, rx_ring) = common::setup_rings(); + let Parts { mut dma, mac: _, #[cfg(feature = "ptp")] ptp: _, - } = stm32_eth::new( - ethernet, - &mut rx_ring[..], - &mut tx_ring[..], - clocks, - eth_pins, - ) - .unwrap(); + } = stm32_eth::new(ethernet, rx_ring, tx_ring, clocks, eth_pins).unwrap(); dma.enable_interrupt(); let ethernet_addr = EthernetAddress(SRC_MAC); diff --git a/examples/pktgen.rs b/examples/pktgen.rs index e9278ee..6060c0a 100644 --- a/examples/pktgen.rs +++ b/examples/pktgen.rs @@ -18,7 +18,7 @@ use stm32_eth::{ Parts, }; -use stm32_eth::dma::{RxRingEntry, TxError, TxRingEntry}; +use stm32_eth::dma::TxError; pub mod common; @@ -42,21 +42,14 @@ fn main() -> ! { defmt::info!("Enabling ethernet..."); let (eth_pins, mdio, mdc, _) = common::setup_pins(gpio); - let mut rx_ring: [RxRingEntry; 2] = Default::default(); - let mut tx_ring: [TxRingEntry; 2] = Default::default(); + let (tx_ring, rx_ring) = common::setup_rings(); + let Parts { mut dma, mac, #[cfg(feature = "ptp")] ptp: _, - } = stm32_eth::new( - ethernet, - &mut rx_ring[..], - &mut tx_ring[..], - clocks, - eth_pins, - ) - .unwrap(); + } = stm32_eth::new(ethernet, rx_ring, tx_ring, clocks, eth_pins).unwrap(); dma.enable_interrupt(); // Main loop From 079941f8218153b61801050edda71e3abfd36030 Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Thu, 9 Feb 2023 21:58:58 +0100 Subject: [PATCH 31/35] Kind of fix pktgen example --- examples/pktgen.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/pktgen.rs b/examples/pktgen.rs index 6060c0a..7453a1d 100644 --- a/examples/pktgen.rs +++ b/examples/pktgen.rs @@ -37,7 +37,7 @@ fn main() -> ! { let (clocks, gpio, ethernet) = common::setup_peripherals(p); - setup_systick(&mut cp.SYST); + setup_systick(&mut cp.SYST, clocks.hclk().to_Hz()); defmt::info!("Enabling ethernet..."); let (eth_pins, mdio, mdc, _) = common::setup_pins(gpio); @@ -68,6 +68,7 @@ fn main() -> ! { // print stats every 30 seconds if time >= last_stats_time + 30 { let t = time - last_stats_time; + defmt::info!( "T={}\tRx:\t{} KB/s\t{} pps\tTx:\t{} KB/s\t{} pps", time, @@ -142,8 +143,8 @@ fn main() -> ! { } } -fn setup_systick(syst: &mut SYST) { - syst.set_reload(100 * SYST::get_ticks_per_10ms()); +fn setup_systick(syst: &mut SYST, hclk: u32) { + syst.set_reload(hclk.min(0x00FF_FFFF)); syst.enable_counter(); syst.enable_interrupt(); From c813db0773c887edbe9442db2e72579471c6826b Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Thu, 9 Feb 2023 22:00:24 +0100 Subject: [PATCH 32/35] Update Cargo.toml with all H7 features and add github workflows --- .github/workflows/build.yml | 12 ++++++++++++ Cargo.toml | 11 ++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4128040..06fe857 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -76,6 +76,16 @@ jobs: - stm32f777 - stm32f778 - stm32f779 + - stm32h735 + - stm32h742 + - stm32h742v + - stm32h743 + - stm32h743v + - stm32h747cm7 + - stm32h750 + - stm32h750v + - stm32h753 + - stm32h753v steps: - name: Checkout uses: actions/checkout@v3 @@ -103,6 +113,7 @@ jobs: - stm32f107 - stm32f407 - stm32f745 + - stm32h735 steps: - name: Checkout uses: actions/checkout@v3 @@ -138,6 +149,7 @@ jobs: - stm32f107 - stm32f429 - stm32f745 + - stm32h735 toolchain: - stable target: diff --git a/Cargo.toml b/Cargo.toml index b98e52e..c530d96 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -69,7 +69,16 @@ stm32f777 = ["stm32f7xx-hal/stm32f777", "device-selected", "fence"] stm32f778 = ["stm32f7xx-hal/stm32f778", "device-selected", "fence"] stm32f779 = ["stm32f7xx-hal/stm32f779", "device-selected", "fence"] -stm32h735 = ["stm32h7xx-hal/stm32h735", "device-selected", "fence"] +stm32h735 = ["device-selected", "fence", "stm32h7xx-hal/stm32h735", ] +stm32h742 = ["device-selected", "fence", "stm32h7xx-hal/stm32h742",] +stm32h742v = ["device-selected", "fence", "stm32h7xx-hal/stm32h742v"] +stm32h743 = ["device-selected", "fence", "stm32h7xx-hal/stm32h743"] +stm32h743v = ["device-selected", "fence", "stm32h7xx-hal/stm32h743v"] +stm32h747cm7 = ["device-selected", "fence", "stm32h7xx-hal/stm32h747cm7"] +stm32h750 = ["device-selected", "fence", "stm32h7xx-hal/stm32h750"] +stm32h750v = ["device-selected", "fence", "stm32h7xx-hal/stm32h750v"] +stm32h753 = ["device-selected", "fence", "stm32h7xx-hal/stm32h753"] +stm32h753v = ["device-selected", "fence", "stm32h7xx-hal/stm32h753v"] smoltcp-phy = ["smoltcp"] From 7de9731cb188f7f33b322d1b638e43b8a9a246a3 Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Thu, 9 Feb 2023 22:07:36 +0100 Subject: [PATCH 33/35] Add nucleo/default pin setups for H7 --- examples/common.rs | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/examples/common.rs b/examples/common.rs index 4be3954..af687a9 100644 --- a/examples/common.rs +++ b/examples/common.rs @@ -369,13 +369,25 @@ mod pins { pub type RefClk = PA1; pub type Crs = PA7; + + #[cfg(pins = "nucleo")] pub type TxEn = PG11; + #[cfg(pins = "nucleo")] pub type TxD0 = PG13; + + #[cfg(not(pins = "nucleo"))] + pub type TxEn = PB11; + #[cfg(not(pins = "nucleo"))] + pub type TxD0 = PB12; + pub type TxD1 = PB13; pub type RxD0 = PC4; pub type RxD1 = PC5; + #[cfg(not(pps = "alternate"))] pub type Pps = PB5>; + #[cfg(pps = "alternate")] + pub type Pps = PG5>; pub type Mdio = PA2>; pub type Mdc = PC1>; @@ -388,6 +400,7 @@ mod pins { Mdc, Pps, ) { + #[allow(unused_variables)] let Gpio { gpioa, gpiob, @@ -399,15 +412,23 @@ mod pins { let crs = gpioa.pa7.into_input(); let rx_d0 = gpioc.pc4.into_input(); let rx_d1 = gpioc.pc5.into_input(); - let tx_en = gpiog.pg11.into_input(); - let tx_d0 = gpiog.pg13.into_input(); let tx_d1 = gpiob.pb13.into_input(); + #[cfg(not(pins = "nucleo"))] + let (tx_en, tx_d0) = { (gpiob.pb11.into_input(), gpiob.pb12.into_input()) }; + + #[cfg(pins = "nucleo")] + let (tx_en, tx_d0) = { (gpiog.pg11.into_input(), gpiog.pg13.into_input()) }; + let mdio = gpioa.pa2.into_alternate(); let mdc = gpioc.pc1.into_alternate(); + #[cfg(not(pps = "alternate"))] let pps = gpiob.pb5.into_push_pull_output(); + #[cfg(pps = "alternate")] + let pps = gpiog.pg5.into_push_pull_output(); + let pins = EthPins { ref_clk, crs, From 947859704a86fbfc51fcb3a73acb2df27ddabaae Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Fri, 10 Feb 2023 10:20:55 +0100 Subject: [PATCH 34/35] Add some more TODOs Panic on FBE --- src/dma/mod.rs | 6 ++++++ src/dma/rx/mod.rs | 15 +++++++-------- src/dma/tx/mod.rs | 10 ++++++---- src/setup.rs | 1 - 4 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/dma/mod.rs b/src/dma/mod.rs index adc8cea..9d2142b 100644 --- a/src/dma/mod.rs +++ b/src/dma/mod.rs @@ -444,6 +444,12 @@ fn eth_interrupt_handler_impl(eth_dma: ÐERNET_DMA) -> InterruptReasonSummary .set_bit() }); + if status.fbe().bit_is_set() { + // TODO: add a link to a/the github issue describing this problem, + // and how to solve it. + panic!("Fatal bus error! Is the descriptor and buffer memory accessible by the Ethernet MAC/DMA?"); + } + ( status.ri().bit_is_set(), status.ti().bit_is_set(), diff --git a/src/dma/rx/mod.rs b/src/dma/rx/mod.rs index efcdd90..68c1d9c 100644 --- a/src/dma/rx/mod.rs +++ b/src/dma/rx/mod.rs @@ -109,16 +109,15 @@ impl<'data> RxRing<'data, NotRunning> { #[cfg(feature = "stm32h7xx-hal")] { - // TODO: assert that ethernet DMA can access - // the memory in these rings - assert!(self.ring.descriptors().count() >= 4); + let rx_ring_descriptors = self.ring.descriptors().count(); + assert!(rx_ring_descriptors >= 4); // Assert that the descriptors are properly aligned. - assert!(ring_ptr as u32 & !0b11 == ring_ptr as u32); - assert!( - self.ring.last_descriptor_mut() as *const _ as u32 & !0b11 - == self.ring.last_descriptor_mut() as *const _ as u32 - ); + // + // FIXME: these require different alignment if the data is stored + // in AXI SRAM + assert!(ring_ptr as u32 % 4 == 0); + assert!(self.ring.last_descriptor_mut() as *const _ as u32 % 4 == 0); // Set the start pointer. eth_dma diff --git a/src/dma/tx/mod.rs b/src/dma/tx/mod.rs index 8bcc8aa..2fe436e 100644 --- a/src/dma/tx/mod.rs +++ b/src/dma/tx/mod.rs @@ -114,12 +114,14 @@ impl<'data> TxRing<'data, NotRunning> { #[cfg(feature = "stm32h7xx-hal")] { - // TODO: assert that ethernet DMA can access - // the memory in these rings - assert!(self.ring.descriptors().count() >= 4); + let tx_descriptor_count = self.ring.descriptors().count(); + assert!(tx_descriptor_count >= 4); // Assert that the descriptors are properly aligned. - assert!(ring_ptr as u32 & !0b11 == ring_ptr as u32); + // + // FIXME: these require different alignment if the data is stored + // in AXI SRAM + assert!(ring_ptr as u32 % 4 == 0); assert!(self.ring.last_descriptor() as *const _ as u32 % 4 == 0); // Set the start pointer. diff --git a/src/setup.rs b/src/setup.rs index cfd0f5f..459abac 100644 --- a/src/setup.rs +++ b/src/setup.rs @@ -41,7 +41,6 @@ use crate::{ }; #[cfg(feature = "stm32h7xx-hal")] -// TODO: implement all allowed GPIO pins. #[allow(unused_imports)] use crate::{ dma::EthernetDMA, From f2119563f61f7e68503a1da15eb06ad33d000e2c Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Fri, 10 Feb 2023 14:59:30 +0100 Subject: [PATCH 35/35] Fix README example --- README.md | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index bc7bd67..6dbebad 100644 --- a/README.md +++ b/README.md @@ -25,14 +25,14 @@ stm32-eth = { version = "0.3.0", features = ["stm32f107"] } # For stm32f107 In `src/main.rs` add: ```rust,no_run +use fugit::RateExtU32; use stm32_eth::{ + dma::{RxDescriptor, RxDescriptorRing, TxDescriptor, TxDescriptorRing}, hal::gpio::GpioExt, hal::rcc::RccExt, stm32::Peripherals, - dma::{RxRingEntry, TxRingEntry}, - EthPins, + EthPins, MTU, }; -use fugit::RateExtU32; fn main() { let p = Peripherals::take().unwrap(); @@ -56,8 +56,13 @@ fn main() { rx_d1: gpioc.pc5, }; - let mut rx_ring: [RxRingEntry; 16] = Default::default(); - let mut tx_ring: [TxRingEntry; 8] = Default::default(); + let mut rx_ring: [RxDescriptor; 16] = Default::default(); + let mut rx_buffers: [[u8; MTU + 2]; 16] = [[0u8; MTU + 2]; 16]; + let rx_ring = RxDescriptorRing::new(&mut rx_ring[..], &mut rx_buffers[..]); + + let mut tx_ring: [TxDescriptor; 8] = Default::default(); + let mut tx_buffers: [[u8; MTU + 2]; 8] = [[0u8; MTU + 2]; 8]; + let tx_ring = TxDescriptorRing::new(&mut tx_ring[..], &mut tx_buffers[..]); let parts = stm32_eth::PartsIn { mac: p.ETHERNET_MAC, @@ -66,14 +71,11 @@ fn main() { ptp: p.ETHERNET_PTP, }; - let stm32_eth::Parts { dma: mut eth_dma, mac: _, ptp: _ } = stm32_eth::new( - parts, - &mut rx_ring[..], - &mut tx_ring[..], - clocks, - eth_pins, - ) - .unwrap(); + let stm32_eth::Parts { + dma: mut eth_dma, + mac: _, + ptp: _, + } = stm32_eth::new(parts, rx_ring, tx_ring, clocks, eth_pins).unwrap(); eth_dma.enable_interrupt(); if let Ok(pkt) = eth_dma.recv_next(None) { @@ -81,9 +83,11 @@ fn main() { } let size = 42; - eth_dma.send(size, None, |buf| { - // write up to `size` bytes into buf before it is being sent - }).expect("send"); + eth_dma + .send(size, None, |buf| { + // write up to `size` bytes into buf before it is being sent + }) + .expect("send"); } ```