From 9ab5ffd5c22690c3929dfe0feeb8383565223e7e Mon Sep 17 00:00:00 2001 From: ZachParallel <134434083+ZachParallel@users.noreply.github.com> Date: Fri, 28 Jul 2023 13:09:22 -0400 Subject: [PATCH 01/77] Added ptp changes from stm32-eth h7-take2 branch --- Cargo.toml | 114 ++- src/ethernet/cache.rs | 85 ++ src/ethernet/eth.rs | 1388 ++++++++++++++++++++++--------- src/ethernet/mod.rs | 21 +- src/ethernet/packet_id.rs | 41 + src/ethernet/raw_descriptor.rs | 147 ++++ src/ethernet/rx/h_descriptor.rs | 238 ++++++ src/ethernet/rx/mod.rs | 364 ++++++++ src/ethernet/tx/h_descriptor.rs | 250 ++++++ src/ethernet/tx/mod.rs | 385 +++++++++ src/lib.rs | 3 + src/ptp/mod.rs | 7 + src/ptp/subseconds.rs | 165 ++++ src/ptp/timestamp.rs | 229 +++++ 14 files changed, 3005 insertions(+), 432 deletions(-) create mode 100644 src/ethernet/cache.rs create mode 100644 src/ethernet/packet_id.rs create mode 100644 src/ethernet/raw_descriptor.rs create mode 100644 src/ethernet/rx/h_descriptor.rs create mode 100644 src/ethernet/rx/mod.rs create mode 100644 src/ethernet/tx/h_descriptor.rs create mode 100644 src/ethernet/tx/mod.rs create mode 100644 src/ptp/mod.rs create mode 100644 src/ptp/subseconds.rs create mode 100644 src/ptp/timestamp.rs diff --git a/Cargo.toml b/Cargo.toml index 5e00ce4c..e112ceef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,15 +1,17 @@ [package] name = "stm32h7xx-hal" version = "0.14.0" -authors = ["Andrew Straw ", - "Richard Meadows ", - "Henrik Böving ", - "Jan Adä ", - "Robert Jördens ", - "Ryan Summers ", - "Matthew Meyer ", - "Florian Jung ", - "Matt Ickstadt "] +authors = [ + "Andrew Straw ", + "Richard Meadows ", + "Henrik Böving ", + "Jan Adä ", + "Robert Jördens ", + "Ryan Summers ", + "Matthew Meyer ", + "Florian Jung ", + "Matt Ickstadt ", +] edition = "2021" rust-version = "1.65" categories = ["embedded", "hardware-support", "no-std"] @@ -22,7 +24,24 @@ readme = "README.md" exclude = [".gitignore"] [package.metadata.docs.rs] -features = ["stm32h743v", "rt", "xspi", "sdmmc", "sdmmc-fatfs", "fmc", "usb_hs", "rtc", "ethernet", "ltdc", "crc", "rand", "can", "defmt", "log", "fugit/defmt"] +features = [ + "stm32h743v", + "rt", + "xspi", + "sdmmc", + "sdmmc-fatfs", + "fmc", + "usb_hs", + "rtc", + "ethernet", + "ltdc", + "crc", + "rand", + "can", + "defmt", + "log", + "fugit/defmt", +] targets = ["thumbv7em-none-eabihf"] rustdoc-args = ["--cfg", "docsrs"] @@ -41,9 +60,12 @@ bare-metal = "1.0.0" sdio-host = { version = "0.9", optional = true } embedded-sdmmc = { version = "0.5", optional = true } stm32-fmc = { version = "0.3", optional = true } -synopsys-usb-otg = { version = "^0.3.0", features = ["cortex-m"], optional = true } +volatile-register = "0.2" +synopsys-usb-otg = { version = "^0.3.0", features = [ + "cortex-m", +], optional = true } embedded-display-controller = { version = "^0.1.0", optional = true } -log = { version = "0.4.14", optional = true} # see also the dev-dependencies section +log = { version = "0.4.14", optional = true } # see also the dev-dependencies section fdcan = { version = "0.2", optional = true } bitflags = { version = "2.0.0" } embedded-storage = "0.3" @@ -73,7 +95,11 @@ panic-rtt-target = { version = "0.1.0", features = ["cortex-m"] } cfg-if = "1.0.0" rtt-target = "0.4.0" lazy_static = { version = "1.4.0", features = ["spin_no_std"] } -cortex-m-log = { version = "0.8.0", features = ["itm", "semihosting", "log-integration"] } +cortex-m-log = { version = "0.8.0", features = [ + "itm", + "semihosting", + "log-integration", +] } cortex-m-semihosting = "0.5.0" panic-itm = { version = "~0.4.1" } panic-semihosting = "0.6" @@ -89,13 +115,13 @@ default-features = false features = ["medium-ethernet", "proto-ipv4", "proto-ipv6", "socket-raw"] [features] -default = ["rt"] +default = ["rt", "device-selected", "stm32h743v", "ethernet"] device-selected = [] revision_v = [] -rm0433 = ["gpio-h747"] # aka. "single core" devices -rm0399 = ["gpio-h747"] # TODO: fix gpio # aka. "dual core" devices -rm0455 = ["gpio-h7a2"] # aka. "high memory integration" devices -rm0468 = ["gpio-h72"] # aka. "high speed" devices +rm0433 = ["gpio-h747"] # aka. "single core" devices +rm0399 = ["gpio-h747"] # TODO: fix gpio # aka. "dual core" devices +rm0455 = ["gpio-h7a2"] # aka. "high memory integration" devices +rm0468 = ["gpio-h72"] # aka. "high speed" devices gpio-h72 = [] gpio-h747 = [] @@ -125,11 +151,43 @@ stm32h742v = ["stm32h7/stm32h743v", "device-selected", "revision_v", "rm0433"] stm32h743v = ["stm32h7/stm32h743v", "device-selected", "revision_v", "rm0433"] stm32h753v = ["stm32h7/stm32h753v", "device-selected", "revision_v", "rm0433"] stm32h750v = ["stm32h7/stm32h743v", "device-selected", "revision_v", "rm0433"] -stm32h747cm7 = ["stm32h7/stm32h747cm7", "device-selected", "revision_v", "rm0399", "cm7", "dsi", "smps"] -stm32h7b3 = ["stm32h7/stm32h7b3", "device-selected", "revision_v", "rm0455", "smps"] -stm32h7b0 = ["stm32h7/stm32h7b3", "device-selected", "revision_v", "rm0455", "smps"] -stm32h7a3 = ["stm32h7/stm32h7b3", "device-selected", "revision_v", "rm0455", "smps"] -stm32h735 = ["stm32h7/stm32h735", "device-selected", "revision_v", "rm0468", "smps"] # Also applies to 723,725,730,733 +stm32h747cm7 = [ + "stm32h7/stm32h747cm7", + "device-selected", + "revision_v", + "rm0399", + "cm7", + "dsi", + "smps", +] +stm32h7b3 = [ + "stm32h7/stm32h7b3", + "device-selected", + "revision_v", + "rm0455", + "smps", +] +stm32h7b0 = [ + "stm32h7/stm32h7b3", + "device-selected", + "revision_v", + "rm0455", + "smps", +] +stm32h7a3 = [ + "stm32h7/stm32h7b3", + "device-selected", + "revision_v", + "rm0455", + "smps", +] +stm32h735 = [ + "stm32h7/stm32h735", + "device-selected", + "revision_v", + "rm0468", + "smps", +] # Also applies to 723,725,730,733 # Flags for examples log-itm = [] log-rtt = [] @@ -138,15 +196,15 @@ example-smps = [] example-ldo = [] [profile.dev] -codegen-units = 1 # better optimizations -debug = true # symbols are nice and they don't increase the size in flash +codegen-units = 1 # better optimizations +debug = true # symbols are nice and they don't increase the size in flash incremental = false [profile.release] codegen-units = 1 # better optimizations -debug = true # symbols are nice and they don't increase the size in flash -lto = true # better optimizations -opt-level = "s" # optimize for binary size +debug = true # symbols are nice and they don't increase the size in flash +lto = true # better optimizations +opt-level = "s" # optimize for binary size # The following examples do not build for all feature flag combinations. The # `required-features` field specifies the hal features and/or the hardware diff --git a/src/ethernet/cache.rs b/src/ethernet/cache.rs new file mode 100644 index 00000000..e05da507 --- /dev/null +++ b/src/ethernet/cache.rs @@ -0,0 +1,85 @@ +#[cfg(feature = "ptp")] +use crate::ptp::Timestamp; + +use super::PacketId; + +/// A cache for timestamping information, +/// used to keep the size of Descriptors down. +#[derive(Clone, Copy)] +#[repr(C, packed)] +pub struct Cache { + has_packet_id: bool, + #[cfg(feature = "ptp")] + has_timestamp: bool, + + packet_id: PacketId, + #[cfg(feature = "ptp")] + timestamp: Timestamp, +} + +impl Default for Cache { + fn default() -> Self { + Self::new() + } +} + +impl Cache { + pub const fn new() -> Self { + Self { + has_packet_id: false, + packet_id: PacketId(0), + + #[cfg(feature = "ptp")] + has_timestamp: false, + #[cfg(feature = "ptp")] + timestamp: Timestamp::new_raw(0), + } + } + + /// Set the packet ID of this [`Cache`] + /// + /// Removes the timestamp id if `ptp` feature is enabled. + pub fn set_id_and_clear_ts(&mut self, packet_id: Option) { + #[cfg(feature = "ptp")] + { + self.has_timestamp = false; + } + + if let Some(id) = packet_id { + self.packet_id = id; + self.has_packet_id = true + } else { + self.has_packet_id = false; + } + } + + // NOTE(unused): this function is not used if not(feature = "ptp") + #[allow(unused)] + pub fn id(&self) -> Option { + if self.has_packet_id { + Some(self.packet_id) + } else { + None + } + } +} + +#[cfg(feature = "ptp")] +impl Cache { + pub fn set_ts(&mut self, timestamp: Option) { + if let Some(ts) = timestamp { + self.timestamp = ts; + self.has_timestamp = true; + } else { + self.has_timestamp = false; + } + } + + pub fn ts(&self) -> Option { + if self.has_timestamp { + Some(self.timestamp) + } else { + None + } + } +} diff --git a/src/ethernet/eth.rs b/src/ethernet/eth.rs index d786dac0..b54f9d33 100644 --- a/src/ethernet/eth.rs +++ b/src/ethernet/eth.rs @@ -22,23 +22,51 @@ //! [quartiq/stabilizer]: https://github.com/quartiq/stabilizer //! [notes]: https://github.com/quartiq/stabilizer/commit/ab1735950b2108eaa8d51eb63efadcd2e25c35c4 -use core::ptr; - use crate::rcc::{rec, CoreClocks, ResetEnable}; use crate::stm32; +use crate::stm32::{Interrupt, ETHERNET_DMA, ETHERNET_MTL, NVIC}; use smoltcp::{ self, - phy::{self, DeviceCapabilities}, + phy::{ + self, ChecksumCapabilities, Device, DeviceCapabilities, PacketMeta, + RxToken, TxToken, + }, time::Instant, wire::EthernetAddress, }; +use super::rx::{ + self, RxDescriptor, RxDescriptorRing, RxError, RxPacket, RxRing, +}; +use super::tx::{ + self, RunningState, TxDescriptor, TxDescriptorRing, TxError, TxRing, +}; + +use super::packet_id::PacketId; + +use crate::ethernet::raw_descriptor::DESC_SIZE; use crate::{ - ethernet::{PinsRMII, StationManagement}, + ethernet::{PinsRMII, StationManagement, MTU}, gpio::Speed, }; +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 _ASSERT_DESCRIPTOR_ALIGN: () = assert!(_RXDESC_SIZE % 4 == 0); + +const DESC_WORD_SKIP: u8 = + ((_RXDESC_SIZE / 4) - super::raw_descriptor::DESC_SIZE) as u8; + +const _ASSERT_DESC_WORD_SKIP_SIZE: () = assert!(DESC_WORD_SKIP <= 0b111); + // 6 DMAC, 6 SMAC, 4 q tag, 2 ethernet type II, 1500 ip MTU, 4 CRC, 2 // padding const ETH_BUF_SIZE: usize = 1536; @@ -60,316 +88,333 @@ mod emac_consts { } use self::emac_consts::*; -/// Transmit Descriptor representation -/// -/// * tdes0: transmit buffer address -/// * tdes1: -/// * tdes2: buffer lengths -/// * tdes3: control and payload/frame length -/// -/// Note that Copy and Clone are derived to support initialising an -/// array of TDes, but you may not move a TDes after its address has -/// been given to the ETH_DMA engine. -#[derive(Copy, Clone)] -#[repr(C, packed)] -struct TDes { - tdes0: u32, - tdes1: u32, - tdes2: u32, - tdes3: u32, -} - -impl TDes { - /// Initialises this TDes to point at the given buffer. - pub fn init(&mut self) { - self.tdes0 = 0; - self.tdes1 = 0; - self.tdes2 = 0; - self.tdes3 = 0; // Owned by us - } - - /// Return true if this TDes is not currently owned by the DMA - pub fn available(&self) -> bool { - self.tdes3 & EMAC_DES3_OWN == 0 - } -} - -/// Store a ring of TDes and associated buffers -#[repr(C, packed)] -struct TDesRing { - td: [TDes; TD], - tbuf: [[u32; ETH_BUF_SIZE / 4]; TD], - tdidx: usize, -} - -impl TDesRing { - const fn new() -> Self { - Self { - td: [TDes { - tdes0: 0, - tdes1: 0, - tdes2: 0, - tdes3: 0, - }; TD], - tbuf: [[0; ETH_BUF_SIZE / 4]; TD], - tdidx: 0, - } - } - - /// Initialise this TDesRing. Assume TDesRing is corrupt - /// - /// The current memory address of the buffers inside this TDesRing - /// will be stored in the descriptors, so ensure the TDesRing is - /// not moved after initialisation. - pub fn init(&mut self) { - for x in 0..TD { - self.td[x].init(); - } - self.tdidx = 0; - - // Initialise pointers in the DMA engine. (There will be a memory barrier later - // before the DMA engine is enabled.) - unsafe { - let dma = &*stm32::ETHERNET_DMA::ptr(); - dma.dmactx_dlar - .write(|w| w.bits(&self.td[0] as *const _ as u32)); - dma.dmactx_rlr.write(|w| w.tdrl().bits(TD as u16 - 1)); - dma.dmactx_dtpr - .write(|w| w.bits(&self.td[0] as *const _ as u32)); - } - } - - /// Return true if a TDes is available for use - pub fn available(&self) -> bool { - self.td[self.tdidx].available() - } - - /// Release the next TDes to the DMA engine for transmission - pub fn release(&mut self) { - let x = self.tdidx; - assert!(self.td[x].tdes3 & EMAC_DES3_OWN == 0); // Owned by us - - let address = ptr::addr_of!(self.tbuf[x]) as u32; - - // Read format - self.td[x].tdes0 = address; // Buffer 1 - self.td[x].tdes1 = 0; // Not used - assert!(self.td[x].tdes2 & !EMAC_TDES2_B1L == 0); // Not used - assert!(self.td[x].tdes2 & EMAC_TDES2_B1L > 0); // Length must be valid - self.td[x].tdes3 = 0; - self.td[x].tdes3 |= EMAC_DES3_FD; // FD: Contains first buffer of packet - self.td[x].tdes3 |= EMAC_DES3_LD; // LD: Contains last buffer of packet - self.td[x].tdes3 |= EMAC_DES3_OWN; // Give the DMA engine ownership - - // Ensure changes to the descriptor are committed before - // DMA engine sees tail pointer store - cortex_m::asm::dsb(); - - // Move the tail pointer (TPR) to the next descriptor - let x = (x + 1) % TD; - unsafe { - let dma = &*stm32::ETHERNET_DMA::ptr(); - dma.dmactx_dtpr - .write(|w| w.bits(&(self.td[x]) as *const _ as u32)); - } - - self.tdidx = x; - } - - /// Access the buffer pointed to by the next TDes - pub unsafe fn buf_as_slice_mut(&mut self, length: usize) -> &mut [u8] { - let x = self.tdidx; - - // Set address in descriptor - self.td[x].tdes0 = ptr::addr_of!(self.tbuf[x]) as u32; // Buffer 1 - - // Set length in descriptor - let len = core::cmp::min(length, ETH_BUF_SIZE); - self.td[x].tdes2 = (length as u32) & EMAC_TDES2_B1L; - - // Create a raw pointer in place without an intermediate reference. Use - // this to return a slice from the packed buffer - let addr = ptr::addr_of_mut!(self.tbuf[x]) as *mut _; - core::slice::from_raw_parts_mut(addr, len) - } -} - -/// Receive Descriptor representation -/// -/// * rdes0: recieve buffer address -/// * rdes1: -/// * rdes2: -/// * rdes3: OWN and Status -/// -/// Note that Copy and Clone are derived to support initialising an -/// array of RDes, but you may not move a RDes after its address has -/// been given to the ETH_DMA engine. -#[derive(Copy, Clone)] -#[repr(C, packed)] -struct RDes { - rdes0: u32, - rdes1: u32, - rdes2: u32, - rdes3: u32, -} - -impl RDes { - /// Initialises RDes - pub fn init(&mut self) { - self.rdes0 = 0; - self.rdes1 = 0; - self.rdes2 = 0; - self.rdes3 = 0; // Owned by us - } - - /// Return true if this RDes is acceptable to us - pub fn valid(&self) -> bool { - // Write-back descriptor is valid if: - // - // Contains first buffer of packet AND contains last buf of - // packet AND no errors AND not a contex descriptor - self.rdes3 - & (EMAC_DES3_FD | EMAC_DES3_LD | EMAC_DES3_ES | EMAC_DES3_CTXT) - == (EMAC_DES3_FD | EMAC_DES3_LD) - } - - /// Return true if this RDes is not currently owned by the DMA - pub fn available(&self) -> bool { - self.rdes3 & EMAC_DES3_OWN == 0 // Owned by us - } -} - -/// Store a ring of RDes and associated buffers -#[repr(C, packed)] -struct RDesRing { - rd: [RDes; RD], - rbuf: [[u32; ETH_BUF_SIZE / 4]; RD], - rdidx: usize, -} - -impl RDesRing { - const fn new() -> Self { - Self { - rd: [RDes { - rdes0: 0, - rdes1: 0, - rdes2: 0, - rdes3: 0, - }; RD], - rbuf: [[0; ETH_BUF_SIZE / 4]; RD], - rdidx: 0, - } - } - - /// Initialise this RDesRing. Assume RDesRing is corrupt - /// - /// The current memory address of the buffers inside this RDesRing - /// will be stored in the descriptors, so ensure the RDesRing is - /// not moved after initialisation. - pub fn init(&mut self) { - for x in 0..RD { - self.rd[x].init(); - } - self.rdidx = 0; - - // Initialise pointers in the DMA engine - unsafe { - let dma = &*stm32::ETHERNET_DMA::ptr(); - dma.dmacrx_dlar - .write(|w| w.bits(&self.rd[0] as *const _ as u32)); - dma.dmacrx_rlr.write(|w| w.rdrl().bits(RD as u16 - 1)); - } - - // Release descriptors to the DMA engine - while self.available() { - self.release() - } - } - - /// Return true if a RDes is available for use - pub fn available(&self) -> bool { - self.rd[self.rdidx].available() - } - - /// Return true if current RDes is valid - pub fn valid(&self) -> bool { - self.rd[self.rdidx].valid() - } - - /// Release the next RDes to the DMA engine - pub fn release(&mut self) { - let x = self.rdidx; - assert!(self.rd[x].rdes3 & EMAC_DES3_OWN == 0); // Owned by us - - let address = ptr::addr_of!(self.rbuf[x]) as u32; - - // Read format - self.rd[x].rdes0 = address; // Buffer 1 - self.rd[x].rdes1 = 0; // Reserved - self.rd[x].rdes2 = 0; // Marked as invalid - self.rd[x].rdes3 = 0; - self.rd[x].rdes3 |= EMAC_DES3_OWN; // Give the DMA engine ownership - self.rd[x].rdes3 |= EMAC_RDES3_BUF1V; // BUF1V: 1st buffer address is valid - self.rd[x].rdes3 |= EMAC_RDES3_IOC; // IOC: Interrupt on complete - - // Ensure changes to the descriptor are committed before - // DMA engine sees tail pointer store - cortex_m::asm::dsb(); - - // Move the tail pointer (TPR) to this descriptor - unsafe { - let dma = &*stm32::ETHERNET_DMA::ptr(); - dma.dmacrx_dtpr - .write(|w| w.bits(&(self.rd[x]) as *const _ as u32)); - } - - // Update active descriptor - self.rdidx = (x + 1) % RD; - } - - /// Access the buffer pointed to by the next RDes - /// - /// # Safety - /// - /// Ensure that release() is called between subsequent calls to this - /// function. - #[allow(clippy::mut_from_ref)] - pub unsafe fn buf_as_slice_mut(&self) -> &mut [u8] { - let x = self.rdidx; - - // Write-back format - let addr = ptr::addr_of!(self.rbuf[x]) as *mut u8; - let len = (self.rd[x].rdes3 & EMAC_RDES3_PL) as usize; - - let len = core::cmp::min(len, ETH_BUF_SIZE); - core::slice::from_raw_parts_mut(addr, len) - } -} - -pub struct DesRing { - tx: TDesRing, - rx: RDesRing, -} -impl DesRing { - pub const fn new() -> Self { - DesRing { - tx: TDesRing::new(), - rx: RDesRing::new(), - } - } -} -impl Default for DesRing { - fn default() -> Self { - Self::new() - } -} +// /// Transmit Descriptor representation +// /// +// /// * tdes0: transmit buffer address +// /// * tdes1: +// /// * tdes2: buffer lengths +// /// * tdes3: control and payload/frame length +// /// +// /// Note that Copy and Clone are derived to support initialising an +// /// array of TDes, but you may not move a TDes after its address has +// /// been given to the ETH_DMA engine. +// #[derive(Copy, Clone)] +// #[repr(C, packed)] +// struct TDes { +// tdes0: u32, +// tdes1: u32, +// tdes2: u32, +// tdes3: u32, +// } + +// impl TDes { +// /// Initialises this TDes to point at the given buffer. +// pub fn init(&mut self) { +// self.tdes0 = 0; +// self.tdes1 = 0; +// self.tdes2 = 0; +// self.tdes3 = 0; // Owned by us +// } + +// /// Return true if this TDes is not currently owned by the DMA +// pub fn available(&self) -> bool { +// self.tdes3 & EMAC_DES3_OWN == 0 +// } +// } + +// /// Store a ring of TDes and associated buffers +// #[repr(C, packed)] +// struct TDesRing { +// td: [TDes; TD], +// tbuf: [[u32; ETH_BUF_SIZE / 4]; TD], +// tdidx: usize, +// } + +// impl TDesRing { +// const fn new() -> Self { +// Self { +// td: [TDes { +// tdes0: 0, +// tdes1: 0, +// tdes2: 0, +// tdes3: 0, +// }; TD], +// tbuf: [[0; ETH_BUF_SIZE / 4]; TD], +// tdidx: 0, +// } +// } + +// /// Initialise this TDesRing. Assume TDesRing is corrupt +// /// +// /// The current memory address of the buffers inside this TDesRing +// /// will be stored in the descriptors, so ensure the TDesRing is +// /// not moved after initialisation. +// pub fn init(&mut self) { +// for x in 0..TD { +// self.td[x].init(); +// } +// self.tdidx = 0; + +// // Initialise pointers in the DMA engine. (There will be a memory barrier later +// // before the DMA engine is enabled.) +// unsafe { +// let dma = &*stm32::ETHERNET_DMA::ptr(); +// dma.dmactx_dlar +// .write(|w| w.bits(&self.td[0] as *const _ as u32)); +// dma.dmactx_rlr.write(|w| w.tdrl().bits(TD as u16 - 1)); +// dma.dmactx_dtpr +// .write(|w| w.bits(&self.td[0] as *const _ as u32)); +// } +// } + +// /// Return true if a TDes is available for use +// pub fn available(&self) -> bool { +// self.td[self.tdidx].available() +// } + +// /// Release the next TDes to the DMA engine for transmission +// pub fn release(&mut self) { +// let x = self.tdidx; +// assert!(self.td[x].tdes3 & EMAC_DES3_OWN == 0); // Owned by us + +// let address = ptr::addr_of!(self.tbuf[x]) as u32; + +// // Read format +// self.td[x].tdes0 = address; // Buffer 1 +// self.td[x].tdes1 = 0; // Not used +// assert!(self.td[x].tdes2 & !EMAC_TDES2_B1L == 0); // Not used +// assert!(self.td[x].tdes2 & EMAC_TDES2_B1L > 0); // Length must be valid +// self.td[x].tdes3 = 0; +// self.td[x].tdes3 |= EMAC_DES3_FD; // FD: Contains first buffer of packet +// self.td[x].tdes3 |= EMAC_DES3_LD; // LD: Contains last buffer of packet +// self.td[x].tdes3 |= EMAC_DES3_OWN; // Give the DMA engine ownership + +// // Ensure changes to the descriptor are committed before +// // DMA engine sees tail pointer store +// cortex_m::asm::dsb(); + +// // Move the tail pointer (TPR) to the next descriptor +// let x = (x + 1) % TD; +// unsafe { +// let dma = &*stm32::ETHERNET_DMA::ptr(); +// dma.dmactx_dtpr +// .write(|w| w.bits(&(self.td[x]) as *const _ as u32)); +// } + +// self.tdidx = x; +// } + +// /// Access the buffer pointed to by the next TDes +// pub unsafe fn buf_as_slice_mut(&mut self, length: usize) -> &mut [u8] { +// let x = self.tdidx; + +// // Set address in descriptor +// self.td[x].tdes0 = ptr::addr_of!(self.tbuf[x]) as u32; // Buffer 1 + +// // Set length in descriptor +// let len = core::cmp::min(length, ETH_BUF_SIZE); +// self.td[x].tdes2 = (length as u32) & EMAC_TDES2_B1L; + +// // Create a raw pointer in place without an intermediate reference. Use +// // this to return a slice from the packed buffer +// let addr = ptr::addr_of_mut!(self.tbuf[x]) as *mut _; +// core::slice::from_raw_parts_mut(addr, len) +// } +// } + +// /// Receive Descriptor representation +// /// +// /// * rdes0: recieve buffer address +// /// * rdes1: +// /// * rdes2: +// /// * rdes3: OWN and Status +// /// +// /// Note that Copy and Clone are derived to support initialising an +// /// array of RDes, but you may not move a RDes after its address has +// /// been given to the ETH_DMA engine. +// #[derive(Copy, Clone)] +// #[repr(C, packed)] +// struct RDes { +// rdes0: u32, +// rdes1: u32, +// rdes2: u32, +// rdes3: u32, +// } + +// impl RDes { +// /// Initialises RDes +// pub fn init(&mut self) { +// self.rdes0 = 0; +// self.rdes1 = 0; +// self.rdes2 = 0; +// self.rdes3 = 0; // Owned by us +// } + +// /// Return true if this RDes is acceptable to us +// pub fn valid(&self) -> bool { +// // Write-back descriptor is valid if: +// // +// // Contains first buffer of packet AND contains last buf of +// // packet AND no errors AND not a contex descriptor +// self.rdes3 +// & (EMAC_DES3_FD | EMAC_DES3_LD | EMAC_DES3_ES | EMAC_DES3_CTXT) +// == (EMAC_DES3_FD | EMAC_DES3_LD) +// } + +// /// Return true if this RDes is not currently owned by the DMA +// pub fn available(&self) -> bool { +// self.rdes3 & EMAC_DES3_OWN == 0 // Owned by us +// } +// } + +// /// Store a ring of RDes and associated buffers +// #[repr(C, packed)] +// struct RDesRing { +// rd: [RDes; RD], +// rbuf: [[u32; ETH_BUF_SIZE / 4]; RD], +// rdidx: usize, +// } + +// impl RDesRing { +// const fn new() -> Self { +// Self { +// rd: [RDes { +// rdes0: 0, +// rdes1: 0, +// rdes2: 0, +// rdes3: 0, +// }; RD], +// rbuf: [[0; ETH_BUF_SIZE / 4]; RD], +// rdidx: 0, +// } +// } + +// /// Initialise this RDesRing. Assume RDesRing is corrupt +// /// +// /// The current memory address of the buffers inside this RDesRing +// /// will be stored in the descriptors, so ensure the RDesRing is +// /// not moved after initialisation. +// pub fn init(&mut self) { +// for x in 0..RD { +// self.rd[x].init(); +// } +// self.rdidx = 0; + +// // Initialise pointers in the DMA engine +// unsafe { +// let dma = &*stm32::ETHERNET_DMA::ptr(); +// dma.dmacrx_dlar +// .write(|w| w.bits(&self.rd[0] as *const _ as u32)); +// dma.dmacrx_rlr.write(|w| w.rdrl().bits(RD as u16 - 1)); +// } + +// // Release descriptors to the DMA engine +// while self.available() { +// self.release() +// } +// } + +// /// Return true if a RDes is available for use +// pub fn available(&self) -> bool { +// self.rd[self.rdidx].available() +// } + +// /// Return true if current RDes is valid +// pub fn valid(&self) -> bool { +// self.rd[self.rdidx].valid() +// } + +// /// Release the next RDes to the DMA engine +// pub fn release(&mut self) { +// let x = self.rdidx; +// assert!(self.rd[x].rdes3 & EMAC_DES3_OWN == 0); // Owned by us + +// let address = ptr::addr_of!(self.rbuf[x]) as u32; + +// // Read format +// self.rd[x].rdes0 = address; // Buffer 1 +// self.rd[x].rdes1 = 0; // Reserved +// self.rd[x].rdes2 = 0; // Marked as invalid +// self.rd[x].rdes3 = 0; +// self.rd[x].rdes3 |= EMAC_DES3_OWN; // Give the DMA engine ownership +// self.rd[x].rdes3 |= EMAC_RDES3_BUF1V; // BUF1V: 1st buffer address is valid +// self.rd[x].rdes3 |= EMAC_RDES3_IOC; // IOC: Interrupt on complete + +// // Ensure changes to the descriptor are committed before +// // DMA engine sees tail pointer store +// cortex_m::asm::dsb(); + +// // Move the tail pointer (TPR) to this descriptor +// unsafe { +// let dma = &*stm32::ETHERNET_DMA::ptr(); +// dma.dmacrx_dtpr +// .write(|w| w.bits(&(self.rd[x]) as *const _ as u32)); +// } + +// // Update active descriptor +// self.rdidx = (x + 1) % RD; +// } + +// /// Access the buffer pointed to by the next RDes +// /// +// /// # Safety +// /// +// /// Ensure that release() is called between subsequent calls to this +// /// function. +// #[allow(clippy::mut_from_ref)] +// pub unsafe fn buf_as_slice_mut(&self) -> &mut [u8] { +// let x = self.rdidx; + +// // Write-back format +// let addr = ptr::addr_of!(self.rbuf[x]) as *mut u8; +// let len = (self.rd[x].rdes3 & EMAC_RDES3_PL) as usize; + +// let len = core::cmp::min(len, ETH_BUF_SIZE); +// core::slice::from_raw_parts_mut(addr, len) +// } +// } + +//TODO Check this +// pub struct DesRing { +// tx: TDesRing, +// rx: RDesRing, +// } +// impl DesRing { +// pub const fn new() -> Self { +// DesRing { +// tx: TDesRing::new(), +// rx: RDesRing::new(), +// } +// } +// } +// impl Default for DesRing { +// fn default() -> Self { +// Self::new() +// } +// } + +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Clone, Copy, Debug, PartialEq)] +/// This struct is returned if a packet ID is not associated +/// with any TX or RX descriptors. +pub struct PacketIdNotFound; /// /// Ethernet DMA /// -pub struct EthernetDMA { - ring: &'static mut DesRing, - eth_dma: stm32::ETHERNET_DMA, +// pub struct EthernetDMA { +// ring: &'static mut DesRing, +// eth_dma: stm32::ETHERNET_DMA, +// } +/// Ethernet DMA. +pub struct EthernetDMA<'rx, 'tx> { + eth_dma: ETHERNET_DMA, + eth_mtl: crate::stm32::ETHERNET_MTL, + rx_ring: RxRing<'rx>, + tx_ring: TxRing<'tx>, + + #[cfg(feature = "ptp")] + packet_id_counter: u32, } /// @@ -400,19 +445,23 @@ pub struct EthernetMAC { /// /// `EthernetDMA` shall not be moved as it is initialised here #[allow(clippy::too_many_arguments)] -pub fn new( +pub fn new<'rx, 'tx>( eth_mac: stm32::ETHERNET_MAC, eth_mtl: stm32::ETHERNET_MTL, eth_dma: stm32::ETHERNET_DMA, mut pins: impl PinsRMII, - ring: &'static mut DesRing, + rx_buffer: RxDescriptorRing<'rx>, + tx_buffer: TxDescriptorRing<'tx>, mac_addr: EthernetAddress, prec: rec::Eth1Mac, clocks: &CoreClocks, -) -> (EthernetDMA, EthernetMAC) { +) -> (EthernetDMA<'rx, 'tx>, EthernetMAC) { pins.set_speed(Speed::VeryHigh); unsafe { - new_unchecked(eth_mac, eth_mtl, eth_dma, ring, mac_addr, prec, clocks) + new_unchecked( + eth_mac, eth_mtl, eth_dma, rx_buffer, tx_buffer, mac_addr, prec, + clocks, + ) } } @@ -438,15 +487,16 @@ pub fn new( /// # Safety /// /// `EthernetDMA` shall not be moved as it is initialised here -pub unsafe fn new_unchecked( +pub unsafe fn new_unchecked<'rx, 'tx>( eth_mac: stm32::ETHERNET_MAC, eth_mtl: stm32::ETHERNET_MTL, eth_dma: stm32::ETHERNET_DMA, - ring: &'static mut DesRing, + rx_buffer: RxDescriptorRing<'rx>, + tx_buffer: TxDescriptorRing<'tx>, mac_addr: EthernetAddress, prec: rec::Eth1Mac, clocks: &CoreClocks, -) -> (EthernetDMA, EthernetMAC) { +) -> (EthernetDMA<'rx, 'tx>, EthernetMAC) { // RCC { let rcc = &*stm32::RCC::ptr(); @@ -473,8 +523,8 @@ pub unsafe fn new_unchecked( cortex_m::interrupt::free(|_cs| { // reset ETH_DMA - write 1 and wait for 0 - eth_dma.dmamr.modify(|_, w| w.swr().set_bit()); - while eth_dma.dmamr.read().swr().bit_is_set() {} + eth_dma.dmamr.modify(|_, w| w.swr().set_bit()); //Match + while eth_dma.dmamr.read().swr().bit_is_set() {} //Match // 200 MHz eth_mac @@ -513,6 +563,7 @@ pub unsafe fn new_unchecked( .dr() .set_bit() }); + eth_mac.macecr.modify(|_, w| { w.eipgen() .clear_bit() @@ -642,55 +693,56 @@ pub unsafe fn new_unchecked( // operation mode register eth_dma.dmamr.modify(|_, w| { w.intm() - .bits(0b00) - // Rx Tx priority ratio 1:1 + .bits(0b00) //May not need + // Rx Tx priority ratio 2:1 .pr() - .bits(0b000) + .variant(0b001) // Modified .txpr() - .clear_bit() + .clear_bit() //May not need .da() - .clear_bit() + .clear_bit() //May not need }); // bus mode register eth_dma.dmasbmr.modify(|_, w| { // Address-aligned beats - w.aal() + w.aal() //Match .set_bit() // Fixed burst - .fb() + .fb() //May not need .set_bit() }); - eth_dma - .dmaccr - .modify(|_, w| w.dsl().bits(0).pblx8().clear_bit().mss().bits(536)); + eth_dma.dmaccr.modify(|_, w| { + w.dsl() //Modified + .variant(DESC_WORD_SKIP) + }); eth_dma.dmactx_cr.modify(|_, w| { w // Tx DMA PBL - .txpbl() + .txpbl() //Match .bits(32) - .tse() + .tse() //May not need .clear_bit() // Operate on second frame - .osf() + .osf() //Match .clear_bit() }); eth_dma.dmacrx_cr.modify(|_, w| { w // receive buffer size - .rbsz() - .bits(ETH_BUF_SIZE as u16) + .rbsz() //Modified + .variant(rx_buffer.first_buffer().len() as u16) // Rx DMA PBL - .rxpbl() + .rxpbl() //Match .bits(32) // Disable flushing of received frames .rpf() - .clear_bit() + .clear_bit() //May not need }); // Initialise DMA descriptors - ring.tx.init(); - ring.rx.init(); + // ring.tx.init(); + // ring.rx.init(); // Ensure the DMA descriptors are committed cortex_m::asm::dsb(); @@ -705,12 +757,12 @@ pub unsafe fn new_unchecked( eth_mtl.mtltx_qomr.modify(|_, w| w.ftq().set_bit()); // Manage DMA transmission and reception - eth_dma.dmactx_cr.modify(|_, w| w.st().set_bit()); - eth_dma.dmacrx_cr.modify(|_, w| w.sr().set_bit()); + eth_dma.dmactx_cr.modify(|_, w| w.st().set_bit()); //May not need + eth_dma.dmacrx_cr.modify(|_, w| w.sr().set_bit()); //May not need eth_dma .dmacsr - .modify(|_, w| w.tps().set_bit().rps().set_bit()); + .modify(|_, w| w.tps().set_bit().rps().set_bit()); //These bits are set if transmission is stopped. }); // MAC layer @@ -736,11 +788,55 @@ pub unsafe fn new_unchecked( clock_range: csr_clock_range, }; - let dma = EthernetDMA { ring, eth_dma }; + let mut dma = EthernetDMA { + eth_dma, + eth_mtl, + rx_ring: RxRing::new(rx_buffer), + tx_ring: TxRing::new(tx_buffer), + + #[cfg(feature = "ptp")] + packet_id_counter: 0, + }; + dma.rx_ring.start(&dma.eth_dma); + dma.tx_ring.start(&dma.eth_dma); (dma, mac) } +/// A summary of the reasons for the occurence of an +/// interrupt +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct InterruptReason { + /// A packet has arrived and is ready for processing. + pub rx: bool, + /// A packet was sent, and a TX slot has freed up. + pub tx: bool, + /// A DMA error occured. + pub dma_error: bool, + #[cfg(all(feature = "ptp"))] + /// The target time configured for PTP has + /// passed. + pub time_passed: bool, +} + +/// Handle the `ETH` interrupt. +/// +/// This function wakes wakers and resets +/// interrupt bits relevant in that interrupt. +#[cfg(feature = "device-selected")] +pub fn eth_interrupt_handler() -> InterruptReason { + let dma = EthernetDMA::interrupt_handler(); + + InterruptReason { + rx: dma.is_rx, + tx: dma.is_tx, + dma_error: dma.is_error, + #[cfg(all(feature = "ptp"))] + time_passed: is_time_trigger, + } +} + impl EthernetMAC { /// Sets the SMI address to use for the PHY pub fn set_phy_addr(self, eth_phy_addr: u8) -> Self { @@ -796,110 +892,598 @@ impl StationManagement for EthernetMAC { } /// Define TxToken type and implement consume method -pub struct TxToken<'a, const TD: usize>(&'a mut TDesRing); +// pub struct TxToken<'a, const TD: usize>(&'a mut TDesRing); + +// impl<'a, const TD: usize> phy::TxToken for TxToken<'a, TD> { +// fn consume(self, len: usize, f: F) -> R +// where +// F: FnOnce(&mut [u8]) -> R, +// { +// assert!(len <= ETH_BUF_SIZE); + +// let result = f(unsafe { self.0.buf_as_slice_mut(len) }); +// self.0.release(); +// result +// } +// } + +// /// Define RxToken type and implement consume method +// pub struct RxToken<'a, const RD: usize>(&'a mut RDesRing); + +// impl<'a, const RD: usize> phy::RxToken for RxToken<'a, RD> { +// fn consume(self, f: F) -> R +// where +// F: FnOnce(&mut [u8]) -> R, +// { +// let result = f(unsafe { self.0.buf_as_slice_mut() }); +// self.0.release(); +// result +// } +// } + +/// An Ethernet RX token that can be consumed in order to receive +/// an ethernet packet. +pub struct EthRxToken<'a, 'rx> { + rx_ring: &'a mut RxRing<'rx>, + #[cfg(feature = "ptp")] + meta: PacketId, +} -impl<'a, const TD: usize> phy::TxToken for TxToken<'a, TD> { - fn consume(self, len: usize, f: F) -> R +impl<'dma, 'rx> RxToken for EthRxToken<'dma, 'rx> { + fn consume(self, f: F) -> R where F: FnOnce(&mut [u8]) -> R, { - assert!(len <= ETH_BUF_SIZE); + #[cfg(feature = "ptp")] + let meta = Some(self.meta.into()); - let result = f(unsafe { self.0.buf_as_slice_mut(len) }); - self.0.release(); + #[cfg(not(feature = "ptp"))] + let meta = None; + + // NOTE(unwrap): an `EthRxToken` is only created when `eth.rx_available()` + let mut packet = self.rx_ring.recv_next(meta).ok().unwrap(); + let result = f(&mut packet); + packet.free(); result } + + #[cfg(feature = "ptp")] + fn meta(&self) -> smoltcp::phy::PacketMeta { + self.meta.clone().into() + } } -/// Define RxToken type and implement consume method -pub struct RxToken<'a, const RD: usize>(&'a mut RDesRing); +/// Just a reference to [`EthernetDMA`] for sending a +/// packet later with [`TxToken::consume()`]. +pub struct EthTxToken<'a, 'tx> { + tx_ring: &'a mut TxRing<'tx>, + #[cfg(feature = "ptp")] + meta: Option, +} -impl<'a, const RD: usize> phy::RxToken for RxToken<'a, RD> { - fn consume(self, f: F) -> R +impl<'dma, 'tx> TxToken for EthTxToken<'dma, 'tx> { + fn consume(self, len: usize, f: F) -> R where F: FnOnce(&mut [u8]) -> R, { - let result = f(unsafe { self.0.buf_as_slice_mut() }); - self.0.release(); - result + #[cfg(feature = "ptp")] + let meta = self.meta.map(Into::into); + #[cfg(not(feature = "ptp"))] + let meta = None; + + // NOTE(unwrap): an `EthTxToken` is only created if + // there is a descriptor available for sending. + let mut tx_packet = self.tx_ring.send_next(len, meta).ok().unwrap(); + let res = f(&mut tx_packet); + tx_packet.send(); + res + } + + #[cfg(feature = "ptp")] + fn set_meta(&mut self, meta: smoltcp::phy::PacketMeta) { + self.meta = Some(meta.into()); } } /// Implement the smoltcp Device interface -impl phy::Device for EthernetDMA { - type RxToken<'a> = RxToken<'a, RD>; - type TxToken<'a> = TxToken<'a, TD>; +// impl phy::Device for EthernetDMA { +// type RxToken<'a> = RxToken<'a, RD>; +// type TxToken<'a> = TxToken<'a, TD>; + +// // Clippy false positive because DeviceCapabilities is non-exhaustive +// #[allow(clippy::field_reassign_with_default)] +// fn capabilities(&self) -> DeviceCapabilities { +// let mut caps = DeviceCapabilities::default(); +// // ethernet frame type II (6 smac, 6 dmac, 2 ethertype), +// // sans CRC (4), 1500 IP MTU +// caps.max_transmission_unit = 1514; +// caps.max_burst_size = Some(core::cmp::min(TD, RD)); +// caps +// } + +// fn receive( +// &mut self, +// _timestamp: Instant, +// ) -> Option<(RxToken, TxToken)> { +// // Skip all queued packets with errors. +// while self.ring.rx.available() && !self.ring.rx.valid() { +// self.ring.rx.release() +// } + +// if self.ring.rx.available() && self.ring.tx.available() { +// Some((RxToken(&mut self.ring.rx), TxToken(&mut self.ring.tx))) +// } else { +// None +// } +// } + +// fn transmit(&mut self, _timestamp: Instant) -> Option> { +// if self.ring.tx.available() { +// Some(TxToken(&mut self.ring.tx)) +// } else { +// None +// } +// } +// } + +/// Use this Ethernet driver with [smoltcp](https://github.com/smoltcp-rs/smoltcp) +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; - // Clippy false positive because DeviceCapabilities is non-exhaustive - #[allow(clippy::field_reassign_with_default)] fn capabilities(&self) -> DeviceCapabilities { let mut caps = DeviceCapabilities::default(); - // ethernet frame type II (6 smac, 6 dmac, 2 ethertype), - // sans CRC (4), 1500 IP MTU - caps.max_transmission_unit = 1514; - caps.max_burst_size = Some(core::cmp::min(TD, RD)); + caps.max_transmission_unit = crate::ethernet::MTU; + caps.max_burst_size = Some(1); + caps.checksum = ChecksumCapabilities::ignored(); caps } fn receive( &mut self, _timestamp: Instant, - ) -> Option<(RxToken, TxToken)> { - // Skip all queued packets with errors. - while self.ring.rx.available() && !self.ring.rx.valid() { - self.ring.rx.release() - } - - if self.ring.rx.available() && self.ring.tx.available() { - Some((RxToken(&mut self.ring.rx), TxToken(&mut self.ring.tx))) + ) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + if self.tx_available() && self.rx_available() { + #[cfg(feature = "ptp")] + let rx_packet_id = self.next_packet_id(); + + let EthernetDMA { + rx_ring, tx_ring, .. + } = self; + + let rx = EthRxToken { + rx_ring, + #[cfg(feature = "ptp")] + meta: rx_packet_id, + }; + + let tx = EthTxToken { + tx_ring, + #[cfg(feature = "ptp")] + meta: None, + }; + Some((rx, tx)) } else { None } } - fn transmit(&mut self, _timestamp: Instant) -> Option> { - if self.ring.tx.available() { - Some(TxToken(&mut self.ring.tx)) + fn transmit(&mut self, _timestamp: Instant) -> Option> { + if self.tx_available() { + let EthernetDMA { tx_ring, .. } = self; + Some(EthTxToken { + tx_ring, + #[cfg(feature = "ptp")] + meta: None, + }) } else { None } } } -impl EthernetDMA { +impl<'rx, 'tx> EthernetDMA<'rx, 'tx> { /// Return the number of packets dropped since this method was /// last called pub fn number_packets_dropped(&self) -> u32 { self.eth_dma.dmacmfcr.read().mfc().bits() as u32 } + + fn eth_dma(&self) -> ÐERNET_DMA { + &self.eth_dma + } + + /// Split the [`EthernetDMA`] into concurrently operating send and + /// receive parts. + pub fn split(&mut self) -> (&mut RxRing<'rx>, &mut TxRing<'tx>) { + (&mut self.rx_ring, &mut self.tx_ring) + } + + /// Enable RX and TX interrupts + /// + /// In your handler you must call + /// [`EthernetDMA::interrupt_handler()`] or [`stm32_eth::eth_interrupt_handler`](crate::eth_interrupt_handler) + /// to clear interrupt pending bits. Otherwise the interrupt will reoccur immediately. + /// + /// [`EthernetPTP::interrupt_handler()`]: crate::ptp::EthernetPTP::interrupt_handler + #[cfg_attr( + feature = "ptp", + doc = "If you have PTP enabled, you must also call [`EthernetPTP::interrupt_handler()`] if you wish to make use of the PTP timestamp trigger feature." + )] + pub fn enable_interrupt(&self) { + 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() + // Abnormal Interrupt Summary enable + .aie() + .set_bit() + // Receive Buffer Unavailable + .rbue() + .set_bit() + // Transmit Buffer Unavailable + .tbue() + .set_bit() + }); + + // Enable ethernet interrupts + unsafe { + NVIC::unmask(Interrupt::ETH); + } + } + + pub fn panic_fbe() -> ! { + // SAFETY: we only perform atomic reads/writes through `eth_dma`. + let eth_dma = unsafe { &*ETHERNET_DMA::ptr() }; + + let tx_descriptor_addr = eth_dma.dmaccatx_dr.read().bits(); + let tx_buffer_addr = eth_dma.dmaccatx_br.read().bits(); + + let rx_descriptor_addr = eth_dma.dmaccarx_dr.read().bits(); + let rx_buffer_addr = eth_dma.dmaccarx_br.read().bits(); + + // 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? TXDESC: {:08X}, TXBUF: {:08X}, RXDESC: {:08X}, TXDESC: {:08X}", tx_descriptor_addr, tx_buffer_addr, rx_descriptor_addr, rx_buffer_addr); + } + + /// Handle the DMA parts of the `ETH` interrupt. + pub(crate) fn interrupt_handler() -> InterruptReasonSummary { + // SAFETY: we only perform atomic reads/writes through `eth_dma`. + let eth_dma = unsafe { &*ETHERNET_DMA::ptr() }; + + 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() + .rbu() + .set_bit() + .tbu() + .set_bit() + }); + + if status.fbe().bit_is_set() { + EthernetDMA::panic_fbe(); + } + + ( + status.ri().bit_is_set() || status.rbu().bit_is_set(), + status.ti().bit_is_set() || status.tbu().bit_is_set(), + status.ais().bit_is_set(), + ) + }; + + let status = InterruptReasonSummary { + is_rx, + is_tx, + is_error, + }; + + #[cfg(feature = "async-await")] + { + if status.is_tx { + EthernetDMA::tx_waker().wake(); + } + + if status.is_rx { + EthernetDMA::rx_waker().wake(); + } + } + + status + } + + /// Try to receive a packet. + /// + /// If no packet is available, this function returns [`Err(RxError::WouldBlock)`](RxError::WouldBlock). + /// + /// It may also return another kind of [`RxError`]. + pub fn recv_next( + &mut self, + packet_id: Option, + ) -> Result { + self.rx_ring.recv_next(packet_id.map(Into::into)) + } + + /// Is Rx DMA currently running? + /// + /// It stops if the ring is full. Call [`EthernetDMA::recv_next()`] to free an + /// entry and to demand poll from the hardware. + pub fn rx_is_running(&self) -> bool { + RxRing::running_state().is_running() + } + + /// Is Tx DMA currently running? + pub fn tx_is_running(&self) -> bool { + TxRing::is_running() + } + + /// Try to send a packet with data. + /// + /// If there are no free TX slots, this function will + /// return [`Err(TxError::WouldBlock)`](TxError::WouldBlock). + pub fn send( + &mut self, + length: usize, + packet_id: Option, + f: F, + ) -> Result<(), TxError> + where + F: FnOnce(&mut [u8]), + { + let mut tx_packet = self.tx_ring.send_next(length, packet_id)?; + f(&mut tx_packet); + tx_packet.send(); + + Ok(()) + } + + /// Check if there is a packet available for reading. + /// + /// If this function returns true, it is guaranteed that the + /// next call to [`EthernetDMA::recv_next`] will return [`Ok`]. + pub fn rx_available(&mut self) -> bool { + self.rx_ring.next_entry_available() + } + + /// Check if sending a packet now would succeed. + /// + /// If this function returns true, it is guaranteed that + /// the next call to [`EthernetDMA::send`] will return [`Ok`] + pub fn tx_available(&mut self) -> bool { + self.tx_ring.next_entry_available() + } } -/// Clears the Ethernet interrupt flag -/// -/// # Safety -/// -/// This method implements a single register write to DMACSR -pub unsafe fn interrupt_handler() { - let eth_dma = &*stm32::ETHERNET_DMA::ptr(); - eth_dma - .dmacsr - .write(|w| w.nis().set_bit().ri().set_bit().ti().set_bit()); - let _ = eth_dma.dmacsr.read(); - let _ = eth_dma.dmacsr.read(); // Delay 2 peripheral clocks +impl Drop for EthernetDMA<'_, '_> { + // On drop, stop all DMA actions. + fn drop(&mut self) { + self.tx_ring.stop(self.eth_dma()); + self.rx_ring.stop(self.eth_dma()); + } } -/// Enables the Ethernet Interrupt. The following interrupts are enabled: -/// -/// * Normal Interrupt `NIE` -/// * Receive Interrupt `RIE` -/// * Transmit Interript `TIE` -/// -/// # Safety -/// -/// This method implements a single RMW to DMACIER -pub unsafe fn enable_interrupt() { - let eth_dma = &*stm32::ETHERNET_DMA::ptr(); - eth_dma - .dmacier - .modify(|_, w| w.nie().set_bit().rie().set_bit().tie().set_bit()); +#[cfg(feature = "async-await")] +impl<'rx, 'tx> EthernetDMA<'rx, 'tx> { + pub(crate) fn rx_waker() -> &'static AtomicWaker { + static WAKER: AtomicWaker = AtomicWaker::new(); + &WAKER + } + + pub(crate) fn tx_waker() -> &'static AtomicWaker { + static WAKER: AtomicWaker = AtomicWaker::new(); + &WAKER + } + + /// Receive a packet. + /// + /// See [`RxRing::recv`]. + pub async fn recv(&mut self, packet_id: Option) -> RxPacket { + self.rx_ring.recv(packet_id).await + } + + /// Prepare a packet for sending. + /// + /// See [`TxRing::prepare_packet`]. + pub async fn prepare_packet( + &mut self, + length: usize, + packet_id: Option, + ) -> TxPacket { + self.tx_ring.prepare_packet(length, packet_id).await + } + + /// Wait for an RX or TX interrupt to have + /// occured. + pub async fn rx_or_tx(&mut self) { + let mut polled_once = false; + core::future::poll_fn(|ctx| { + if polled_once { + Poll::Ready(()) + } else { + polled_once = true; + EthernetDMA::rx_waker().register(ctx.waker()); + EthernetDMA::tx_waker().register(ctx.waker()); + Poll::Pending + } + }) + .await; + } +} + +#[cfg(feature = "ptp")] +/// PTP methods. +impl EthernetDMA<'_, '_> { + /// Try to get the timestamp for the given packet ID. + /// + /// This function will attempt to find both RX and TX timestamps, + /// so make sure that the provided packet ID is unique between the two. + pub fn poll_timestamp( + &self, + packet_id: &PacketId, + ) -> Poll, PacketIdNotFound>> { + // Check if it's a TX packet + let tx = self.poll_tx_timestamp(packet_id); + + if tx != Poll::Ready(Err(PacketIdNotFound)) { + return tx; + } + + // It's not a TX packet, check if it's an RX packet + Poll::Ready(self.rx_timestamp(packet_id)) + } + + /// Get the RX timestamp for the given packet ID. + pub fn rx_timestamp( + &self, + packet_id: &PacketId, + ) -> Result, PacketIdNotFound> { + self.rx_ring.timestamp(packet_id) + } + + /// Blockingly wait until the TX timestamp for + /// the given ID is available. + pub fn wait_for_tx_timestamp( + &self, + packet_id: &PacketId, + ) -> Result, PacketIdNotFound> { + self.tx_ring.wait_for_timestamp(packet_id) + } + + /// Poll to check if the TX timestamp for the given + /// ID is available. + pub fn poll_tx_timestamp( + &self, + packet_id: &PacketId, + ) -> Poll, PacketIdNotFound>> { + self.tx_ring.poll_timestamp(packet_id) + } + + /// Get the TX timestamp for the given ID. + #[cfg(feature = "async-await")] + pub async fn tx_timestamp( + &mut self, + packet_id: &PacketId, + ) -> Result, PacketIdNotFound> { + self.tx_ring.timestamp(packet_id).await + } + + /// Get the next packet ID. + pub fn next_packet_id(&mut self) -> PacketId { + let id = PacketId(self.packet_id_counter); + self.packet_id_counter += 1; + id + } } + +/// A summary of the reasons for the interrupt +/// that occured +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Debug, Clone, Copy)] +pub struct InterruptReasonSummary { + /// The interrupt was caused by an RX event. + pub is_rx: bool, + /// The interrupt was caused by an TX event. + pub is_tx: bool, + /// The interrupt was caused by an error event. + pub is_error: bool, +} + +// /// Clears the Ethernet interrupt flag +// /// +// /// # Safety +// /// +// /// This method implements a single register write to DMACSR +// pub fn eth_interrupt_handler_impl( +// eth_dma: ÐERNET_DMA, +// ) -> InterruptReasonSummary { +// 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() +// .rbu() +// .set_bit() +// .tbu() +// .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 +// } + +// / Enables the Ethernet Interrupt. The following interrupts are enabled: +// / +// / * Normal Interrupt `NIE` +// / * Receive Interrupt `RIE` +// / * Transmit Interript `TIE` +// / +// / # Safety +// / +// / This method implements a single RMW to DMACIER +// pub unsafe fn enable_interrupt() { +// let eth_dma = &*stm32::ETHERNET_DMA::ptr(); +// 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() +// // Abnormal Interrupt Summary enable +// .aie() +// .set_bit() +// // Receive Buffer Unavailable +// .rbue() +// .set_bit() +// // Transmit Buffer Unavailable +// .tbue() +// .set_bit() +// }); +// // Enable ethernet interrupts +// unsafe { +// NVIC::unmask(Interrupt::ETH); +// } +// } diff --git a/src/ethernet/mod.rs b/src/ethernet/mod.rs index 12240d5f..641e7242 100644 --- a/src/ethernet/mod.rs +++ b/src/ethernet/mod.rs @@ -32,6 +32,13 @@ pub trait PHY { mod ksz8081r; mod lan8742a; +pub(crate) mod raw_descriptor; + +mod rx; +pub use rx::{RxDescriptor, RxDescriptorRing, RxError, RxPacket}; + +mod tx; +pub use tx::{TxDescriptor, TxDescriptorRing, TxError}; /// Some common implementations of the [PHY trait](PHY) pub mod phy { @@ -39,9 +46,15 @@ pub mod phy { pub use super::lan8742a::*; } +mod packet_id; +pub use packet_id::PacketId; + +mod cache; +pub(crate) use cache::Cache; + mod eth; -pub use eth::{enable_interrupt, interrupt_handler, new, new_unchecked}; -pub use eth::{DesRing, EthernetDMA, EthernetMAC}; +pub use eth::{eth_interrupt_handler, new, new_unchecked}; +pub use eth::{EthernetDMA, EthernetMAC}; /// Marks a set of pins used to communciate to a PHY with a Reduced Media /// Independent Interface (RMII) @@ -49,6 +62,10 @@ pub trait PinsRMII { fn set_speed(&mut self, speed: Speed); } +// ***VERIFY THIS!!!!!*** +/// From the datasheet: *VLAN Frame maxsize = 1522* +pub const MTU: usize = 1522; + // Two lanes impl PinsRMII for (REF_CLK, MDIO, MDC, CRS_DV, RXD0, RXD1, TX_EN, TXD0, TXD1) diff --git a/src/ethernet/packet_id.rs b/src/ethernet/packet_id.rs new file mode 100644 index 00000000..01283f4d --- /dev/null +++ b/src/ethernet/packet_id.rs @@ -0,0 +1,41 @@ +/// A packet ID. +/// +/// This packet ID can be used to obtain information about a specific +/// ethernet frame (either sent or received) from the DMA. +/// +#[cfg_attr( + feature = "ptp", + doc = " +The main use is obtaining timestamps for frames using [`EthernetDMA::poll_timestamp`](crate::EthernetDMA::poll_timestamp) +" +)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Debug, PartialEq, Clone, Copy)] +pub struct PacketId(pub u32); + +impl PacketId { + /// The initial value for an [`Option`] + pub const INIT: Option = None; +} + +impl From for PacketId { + fn from(value: u32) -> Self { + Self(value) + } +} + +#[cfg(all(feature = "ptp", feature = "smoltcp-phy"))] +impl From for PacketId { + fn from(value: smoltcp::phy::PacketMeta) -> Self { + Self(value.id) + } +} + +#[cfg(all(feature = "ptp", feature = "smoltcp-phy"))] +impl From for smoltcp::phy::PacketMeta { + fn from(value: PacketId) -> Self { + let mut meta = smoltcp::phy::PacketMeta::default(); + meta.id = value.0; + meta + } +} diff --git a/src/ethernet/raw_descriptor.rs b/src/ethernet/raw_descriptor.rs new file mode 100644 index 00000000..38e3411e --- /dev/null +++ b/src/ethernet/raw_descriptor.rs @@ -0,0 +1,147 @@ +//TODO remove +// this is raw_descriptor +use volatile_register::{RO, RW}; + +use crate::ethernet::MTU; + +pub(crate) const DESC_SIZE: usize = 4; + +#[repr(C)] +#[repr(align(4))] +#[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 + 2]], +} + +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()); + + Self { + descriptors, + buffers, + } + } + + pub fn len(&self) -> usize { + self.descriptors.len() + } + + pub fn descriptor(&self, index: usize) -> &T { + &self.descriptors[index] + } + + pub fn get(&self, index: usize) -> (&T, &[u8]) { + (&self.descriptors[index], &self.buffers[index]) + } + + pub fn get_mut(&mut self, index: usize) -> (&mut T, &mut [u8]) { + (&mut self.descriptors[index], &mut self.buffers[index]) + } + + pub fn get_mut_and_next( + &mut self, + index: usize, + ) -> (&mut T, &mut [u8], &mut T, &mut [u8]) { + let next = (index + 1) % self.len(); + + macro_rules! mut_and_next { + ($array:expr) => {{ + let (index_slice, next_slice) = if next == 0 { + let (next, index) = $array.split_at_mut(1); + (index, next) + } else { + $array.split_at_mut(next) + }; + + (&mut index_slice[index_slice.len() - 1], &mut next_slice[0]) + }}; + } + + let (desc_index, desc_next) = mut_and_next!(self.descriptors); + let (buf_index, buf_next) = mut_and_next!(self.buffers); + + (desc_index, buf_index, desc_next, buf_next) + } + + pub fn descriptors_mut(&mut self) -> impl Iterator { + self.descriptors.iter_mut() + } + + pub fn descriptors(&self) -> impl Iterator { + 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_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/ethernet/rx/h_descriptor.rs b/src/ethernet/rx/h_descriptor.rs new file mode 100644 index 00000000..f6bde26f --- /dev/null +++ b/src/ethernet/rx/h_descriptor.rs @@ -0,0 +1,238 @@ +use core::sync::atomic::{self, Ordering}; + +use crate::ethernet::{raw_descriptor::RawDescriptor, Cache, PacketId}; + +#[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 + /// 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 + 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(u32)] + pub enum RXDESC_3_LT { + 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 + 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::*; + +use super::RxDescriptorError; + +#[repr(C)] +#[repr(align(4))] +#[derive(Clone, Copy)] +/// An RX DMA Descriptor. +pub struct RxDescriptor { + inner_raw: RawDescriptor, + cache: Cache, +} + +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(), + cache: Cache::new(), + } + } + + /// Is owned by the DMA engine? + pub(super) fn is_owned(&self) -> bool { + (self.inner_raw.read(3) & RXDESC_3_OWN) == RXDESC_3_OWN + } + + pub(super) fn is_available(&self) -> bool { + !self.is_owned() + } + + 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 + } + + pub(super) fn is_context(&self) -> bool { + self.inner_raw.read(3) & RXDESC_3_CTXT == RXDESC_3_CTXT + && self.is_available() + } + + pub(super) fn frame_len(&self) -> usize { + if self.is_owned() { + 0 + } else { + ((self.inner_raw.read(3) & RXDESC_3_PL_MASK) >> RXDESC_3_PL_SHIFT) + as usize + } + } + + // We ignore the end_of_ring bool, but need it for compatibility with the F-series + // descriptor. + pub(super) fn setup(&mut self, _end_of_ring: bool, buffer: &[u8]) { + self.set_owned(buffer); + } + + /// Pass ownership to the DMA engine + 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." + #[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); + } + + // 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 [`RxRing`](super::RxRing) + + self.inner_raw.write(3, RXDESC_3_BUF1V); + } + } + + pub(super) fn recv( + &mut self, + packet_id: Option, + buffer: &mut [u8], + ) -> Result<(), RxDescriptorError> { + if self.has_error() { + Err(RxDescriptorError::DmaError) + } 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.cache.set_id_and_clear_ts(packet_id); + + Ok(()) + } else { + self.set_owned(buffer); + Err(RxDescriptorError::Truncated) + } + } +} + +#[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() { + let (high, low) = (self.inner_raw.read(1), self.inner_raw.read(0)); + Some(Timestamp::from_parts(high, low)) + } else { + None + } + } + + pub(super) fn has_packet_id(&self, packet_id: &PacketId) -> bool { + self.cache.id().as_ref() == Some(packet_id) + } + + pub(super) fn attach_timestamp(&mut self, timestamp: Option) { + self.cache.set_ts(timestamp); + } + + pub(super) fn timestamp(&self) -> Option { + self.cache.ts() + } +} diff --git a/src/ethernet/rx/mod.rs b/src/ethernet/rx/mod.rs new file mode 100644 index 00000000..218b77d5 --- /dev/null +++ b/src/ethernet/rx/mod.rs @@ -0,0 +1,364 @@ +pub use descriptor::RxDescriptor; + +use super::{raw_descriptor::DescriptorRing, PacketId}; +use crate::stm32::ETHERNET_DMA; + +#[path = "./h_descriptor.rs"] +mod descriptor; + +#[cfg(feature = "ptp")] +use crate::{dma::PacketIdNotFound, ptp::Timestamp}; + +#[cfg(feature = "async-await")] +use core::task::Poll; + +/// Errors that can occur during RX +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Debug, PartialEq)] +pub(crate) enum RxDescriptorError { + /// The received packet was truncated + Truncated, + /// An error occured with the DMA + DmaError, +} + +/// Errors that can occur during RX +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Debug, PartialEq)] +pub enum RxError { + /// The received packet was truncated + Truncated, + /// An error occured with the DMA + DmaError, + /// Receiving would block + WouldBlock, +} + +impl From for RxError { + fn from(value: RxDescriptorError) -> Self { + match value { + RxDescriptorError::Truncated => Self::Truncated, + RxDescriptorError::DmaError => Self::DmaError, + } + } +} + +/// An RX descriptor ring. +pub type RxDescriptorRing<'rx> = DescriptorRing<'rx, RxDescriptor>; + +/// Rx DMA state +pub struct RxRing<'a> { + ring: RxDescriptorRing<'a>, + next_entry: usize, +} + +impl<'a> RxRing<'a> { + /// Allocate + pub(crate) fn new(ring: RxDescriptorRing<'a>) -> Self { + RxRing { + ring, + next_entry: 0, + } + } + + /// Setup the DMA engine (**required**) + pub(crate) fn start(&mut self, eth_dma: ÐERNET_DMA) { + // Setup ring + let ring_len = self.ring.len(); + for (idx, (entry, buffer)) in + self.ring.descriptors_and_buffers().enumerate() + { + entry.setup(idx == ring_len - 1, buffer); + } + + self.next_entry = 0; + let ring_ptr = self.ring.descriptors_start_address(); + + { + let rx_ring_descriptors = self.ring.descriptors().count(); + assert!(rx_ring_descriptors >= 4); + + // Assert that the descriptors are properly aligned. + // + // 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 + .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(); + } + + /// Stop the RX DMA + pub(crate) fn stop(&self, eth_dma: ÐERNET_DMA) { + let start_reg = ð_dma.dmacrx_cr; + + start_reg.modify(|_, w| w.sr().clear_bit()); + // DMA accesses do not stop before the running state + // of the DMA has changed to something other than + // running. + while Self::running_state().is_running() {} + } + + /// Demand that the DMA engine polls the current `RxDescriptor` + /// (when in [`RunningState::Stopped`].) + fn demand_poll() { + // # 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() }; + + // On H7, we poll by re-writing the tail pointer register. + eth_dma + .dmacrx_dtpr + .modify(|r, w| unsafe { w.bits(r.bits()) }); + } + + /// Get current state of the RxDMA + pub fn running_state() -> RunningState { + // SAFETY: we only perform an atomic read of `dmasr`. + let eth_dma = unsafe { &*ETHERNET_DMA::ptr() }; + + if eth_dma.dmacsr.read().fbe().bit_is_set() { + super::EthernetDMA::panic_fbe(); + } + + let rps = eth_dma.dmadsr.read().rps0().bits(); + + match rps { + // 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, + // Timestamp write state + 0b110 => RunningState::Running, + _ => RunningState::Unknown, + } + } + + /// Check if we can receive a new packet + pub fn next_entry_available(&self) -> bool { + self.ring.descriptor(self.next_entry).is_available() + } + + /// Obtain the index of the packet to receive (if any is ready). + /// + /// This function returns a tuple of `Ok(entry_index)` on + /// success. Whoever receives the `Ok` must ensure that `set_owned` + /// is eventually called on the entry with that index. + /// + /// Actually obtaining the relevant RxPacket is done using + /// [`RxRing::recv_and_timestamp`]. + fn recv_next_impl( + &mut self, + // NOTE(allow): packet_id is unused if ptp is disabled. + #[allow(unused_variables)] packet_id: Option, + ) -> Result { + if !Self::running_state().is_running() { + Self::demand_poll(); + } + + if self.next_entry_available() { + let entries_len = self.ring.len(); + let (desc, buffer) = self.ring.get_mut(self.next_entry); + + desc.recv(packet_id, buffer)?; + + let entry_num = self.next_entry; + self.next_entry = (self.next_entry + 1) % entries_len; + + Ok(entry_num) + } else { + Err(RxError::WouldBlock) + } + } + + fn recv_and_timestamp(&mut self, entry: usize) -> RxPacket { + let entries_len = self.ring.len(); + + let (desc, buffer) = { + let (desc, buffer, next_desc, next_buffer) = + self.ring.get_mut_and_next(entry); + + // Read the timestamp from the next context descriptor, if it's available. + if next_desc.is_context() { + #[cfg(feature = "ptp")] + if desc.has_timestamp() { + let timestamp = next_desc.read_timestamp(); + desc.attach_timestamp(timestamp); + } + next_desc.set_owned(next_buffer); + self.next_entry = (self.next_entry + 1) % entries_len; + } + + (desc, buffer) + }; + + let length = desc.frame_len(); + + RxPacket { + entry: desc, + buffer, + length, + } + } + + /// Receive the next packet (if any is ready), or return [`Err`] + /// immediately. + pub fn recv_next( + &mut self, + packet_id: Option, + ) -> Result { + let entry = self.recv_next_impl(packet_id)?; + + Ok(self.recv_and_timestamp(entry)) + } + + /// Receive the next packet. + /// + /// The returned [`RxPacket`] can be used as a slice, and + /// will contain the ethernet data. + #[cfg(feature = "async-await")] + pub async fn recv(&mut self, packet_id: Option) -> RxPacket { + let entry = core::future::poll_fn(|ctx| { + let res = self.recv_next_impl(packet_id.clone()); + + match res { + Ok(value) => Poll::Ready(value), + Err(_) => { + crate::dma::EthernetDMA::rx_waker().register(ctx.waker()); + Poll::Pending + } + } + }) + .await; + + self.recv_and_timestamp(entry) + } +} + +#[cfg(feature = "ptp")] +impl<'a> RxRing<'a> { + /// Get the timestamp for a specific ID + pub fn timestamp( + &self, + id: &PacketId, + ) -> Result, PacketIdNotFound> { + let entry = self.ring.descriptors().find(|e| e.has_packet_id(id)); + + let entry = entry.ok_or(PacketIdNotFound)?; + + Ok(entry.timestamp()) + } +} + +/// Running state of the `RxRing` +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(PartialEq, Eq, Debug)] +pub enum RunningState { + /// Running state is unknown. + Unknown, + /// The RX DMA is stopped. + Stopped, + /// The RX DMA is running. + 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, 'buf> { + entry: &'a mut RxDescriptor, + buffer: &'buf mut [u8], + length: usize, +} + +impl core::ops::Deref for RxPacket<'_, '_> { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + &self.buffer[..self.length] + } +} + +impl<'a> core::ops::DerefMut for RxPacket<'_, '_> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.buffer[..self.length] + } +} + +impl Drop for RxPacket<'_, '_> { + fn drop(&mut self) { + self.entry.set_owned(self.buffer); + } +} + +impl RxPacket<'_, '_> { + /// 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.entry.timestamp() + } +} diff --git a/src/ethernet/tx/h_descriptor.rs b/src/ethernet/tx/h_descriptor.rs new file mode 100644 index 00000000..9f37cd9c --- /dev/null +++ b/src/ethernet/tx/h_descriptor.rs @@ -0,0 +1,250 @@ +use core::sync::atomic::{self, Ordering}; + +use crate::ethernet::{raw_descriptor::RawDescriptor, Cache, 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 << 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; + + /// 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, + cache: Cache, +} + +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(), + cache: Cache::new(), + } + } + + #[allow(unused)] + fn is_last(&self) -> bool { + (self.inner_raw.read(3) & TXDESC_3_LD) == TXDESC_3_LD + } + + 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.cache.set_id_and_clear_ts(None); + } + + pub(super) fn is_owned(&self) -> bool { + (self.inner_raw.read(3) & TXDESC_3_OWN) == TXDESC_3_OWN + } + + pub(super) fn is_available(&self) -> bool { + !self.is_owned() + } + + #[allow(unused)] + pub(super) fn is_context(&self) -> bool { + (self.inner_raw.read(3) & TXDESC_3_CTXT) == TXDESC_3_CTXT + } + + /// 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.cache.set_id_and_clear_ts(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::IpHeaderAndPayloadAndPseudoHeader 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); + // Set buffer pointer 2 to NULL + self.inner_raw.write(1, 0); + + 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 + }); + } + } +} + +#[cfg(feature = "ptp")] +impl TxDescriptor { + pub(super) fn has_packet_id(&self, packet_id: &PacketId) -> bool { + self.cache.id().as_ref() == Some(packet_id) + } + + /// For the TxDescriptor we ignore [`Cache::ts`] because: + /// * We're only really using the cache so that the size of RxDescriptor and TxDescriptor + /// is the same. + /// * We want to be able to retrieve the timestamp immutably. + /// * The Timestamp in the TX descriptor is valid until we perform another transmission. + pub(super) fn timestamp(&self) -> Option { + let contains_timestamp = + (self.inner_raw.read(3) & TXDESC_3_TTSS) == TXDESC_3_TTSS; + + 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/ethernet/tx/mod.rs b/src/ethernet/tx/mod.rs new file mode 100644 index 00000000..15b7f9d1 --- /dev/null +++ b/src/ethernet/tx/mod.rs @@ -0,0 +1,385 @@ +use super::{raw_descriptor::DescriptorRing, PacketId}; +use crate::stm32::ETHERNET_DMA; + +#[cfg(feature = "ptp")] +use super::{PacketIdNotFound, Timestamp}; + +#[path = "./h_descriptor.rs"] +mod descriptor; + +pub use descriptor::TxDescriptor; + +#[cfg(any(feature = "ptp", feature = "async-await"))] +use core::task::Poll; + +/// 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 struct TxRing<'a> { + ring: TxDescriptorRing<'a>, + next_entry: usize, +} + +impl<'ring> TxRing<'ring> { + /// Allocate + /// + /// `start()` will be needed before `send()` + pub(crate) fn new(ring: TxDescriptorRing<'ring>) -> Self { + TxRing { + ring, + next_entry: 0, + } + } + + /// Start the Tx DMA engine + pub(crate) fn start(&mut self, eth_dma: ÐERNET_DMA) { + for descriptor in self.ring.descriptors_mut() { + descriptor.setup(); + } + + let ring_ptr = self.ring.descriptors_start_address(); + + { + let tx_descriptor_count = self.ring.descriptors().count(); + assert!(tx_descriptor_count >= 4); + + // Assert that the descriptors are properly aligned. + // + // 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. + 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); + + // We don't need a compiler fence here because all interactions with `Descriptor` are + // volatiles + + let start_reg = ð_dma.dmactx_cr; + + // Start transmission + start_reg.modify(|_, w| w.st().set_bit()); + } + + /// Stop the TX DMA + pub(crate) fn stop(&self, eth_dma: ÐERNET_DMA) { + let start_reg = ð_dma.dmactx_cr; + + start_reg.modify(|_, w| w.st().clear_bit()); + + // DMA accesses do not stop before the running state + // of the DMA has changed to something other than + // running. + while Self::is_running() {} + } + + fn entry_available(&self, index: usize) -> bool { + self.ring.descriptor(index).is_available() + } + + /// If this returns `true`, the next `send` will succeed. + pub fn next_entry_available(&self) -> bool { + self.entry_available(self.next_entry % self.ring.len()) + } + + /// Check if we can send the next TX entry. + /// + /// If [`Ok(res)`] is returned, the caller of must ensure + /// that [`self.entries[res].send()`](TxRingEntry::send) is called + /// before a new invocation of `send_next_impl`. + fn send_next_impl(&mut self) -> Result { + if self.next_entry_available() { + let entry_num = self.next_entry; + + self.next_entry = (self.next_entry + 1) % self.ring.len(); + Ok(entry_num) + } else { + Err(TxError::WouldBlock) + } + } + + /// Prepare a packet for sending. + /// + /// Write the data that you wish to send to the buffer + /// represented by the returned [`TxPacket`] by using it + /// as a slice. + /// + /// When all data is copied into the TX buffer, use [`TxPacket::send()`] + /// to transmit it. + pub fn send_next( + &mut self, + length: usize, + packet_id: Option, + ) -> Result { + let entry = self.send_next_impl()?; + + let (desc, tx_buffer) = self.ring.get_mut(entry); + + assert!(length <= tx_buffer.len(), "Not enough space in TX buffer"); + + Ok(TxPacket { + desc, + buffer: tx_buffer, + length, + packet_id, + }) + } + + /// Prepare a packet for sending. + /// + /// Write the data that you wish to send to the buffer + /// represented by the returned [`TxPacket`] by using it + /// as a slice. + /// + /// When all data is copied into the TX buffer, use [`TxPacket::send()`] + /// to transmit it. + #[cfg(feature = "async-await")] + pub async fn prepare_packet<'tx>( + &'tx mut self, + length: usize, + packet_id: Option, + ) -> TxPacket { + let entry = core::future::poll_fn(|ctx| match self.send_next_impl() { + Ok(packet) => Poll::Ready(packet), + Err(_) => { + crate::dma::EthernetDMA::tx_waker().register(ctx.waker()); + Poll::Pending + } + }) + .await; + + let (desc, tx_buffer) = self.ring.get_mut(entry); + + assert!(length <= tx_buffer.len(), "Not enough space in TX buffer"); + + TxPacket { + desc, + buffer: tx_buffer, + length, + packet_id, + } + } + + /// Demand that the DMA engine polls the current `TxDescriptor` + /// (when we just transferred ownership to the hardware). + pub(crate) fn demand_poll() { + // # SAFETY + // + // On F-series, 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() }; + + // 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()) }); + } + + /// Is the Tx DMA engine running? + pub fn is_running() -> bool { + Self::running_state().is_running() + } + + /// Get the current state of the TxDMA + pub fn running_state() -> RunningState { + // SAFETY: we only perform an atomic read of `dmasr` or + // `dmadsr`. + let eth_dma = unsafe { &*ETHERNET_DMA::ptr() }; + + if eth_dma.dmacsr.read().fbe().bit_is_set() { + super::EthernetDMA::panic_fbe(); + } + + 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 + 0b001 => RunningState::Running, + // Waiting for status + 0b010 => RunningState::Running, + // Reading Data from host memory buffer and queuing it to transmit buffer + 0b011 => RunningState::Running, + + 0b101 => RunningState::Reserved, + + // Transmit descriptor unavailable + 0b110 => RunningState::Suspended, + // Timestamp write + 0b100 => RunningState::Running, + // Closing Tx descriptor + 0b111 => RunningState::Running, + + _ => RunningState::Unknown, + } + } +} + +#[cfg(feature = "ptp")] +impl TxRing<'_> { + fn entry_for_id(&self, id: &PacketId) -> Option { + self.ring.descriptors().enumerate().find_map(|(idx, e)| { + if e.has_packet_id(id) { + Some(idx) + } else { + None + } + }) + } + + fn entry_timestamp(&self, index: usize) -> Option { + self.ring.descriptor(index).timestamp() + } + + /// Blockingly wait untill the timestamp for the + /// given ID is available. + pub fn wait_for_timestamp( + &self, + packet_id: &PacketId, + ) -> Result, PacketIdNotFound> { + loop { + if let Poll::Ready(res) = self.poll_timestamp(packet_id) { + return res; + } + } + } + + /// Poll to check if the timestamp for the given ID is already + /// available. + pub fn poll_timestamp( + &self, + packet_id: &PacketId, + ) -> Poll, PacketIdNotFound>> { + let entry = if let Some(entry) = self.entry_for_id(packet_id) { + entry + } else { + return Poll::Ready(Err(PacketIdNotFound)); + }; + + if self.entry_available(entry) { + Poll::Ready(Ok(self.entry_timestamp(entry))) + } else { + Poll::Pending + } + } + + /// Wait until the timestamp for the given ID is available. + #[cfg(feature = "async-await")] + pub async fn timestamp( + &mut self, + packet_id: &PacketId, + ) -> Result, PacketIdNotFound> { + core::future::poll_fn(move |ctx| { + let res = self.poll_timestamp(packet_id); + if res.is_pending() { + crate::dma::EthernetDMA::tx_waker().register(ctx.waker()); + } + res + }) + .await + } +} + +#[derive(Debug, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// The run state of the TX DMA. +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 { + /// Check whether this state represents that the + /// TX DMA is running + pub fn is_running(&self) -> bool { + *self == RunningState::Running + } +} + +/// A struct that represents a soon-to-be-sent packet. +/// +/// Implements [`Deref`] and [`DerefMut`] with `[u8]` as a target +/// so it can be used as a slice. +/// +/// [`Deref`]: core::ops::Deref +/// [`DerefMut`]: core::ops::DerefMut +pub struct TxPacket<'borrow> { + desc: &'borrow mut TxDescriptor, + buffer: &'borrow mut [u8], + length: usize, + packet_id: Option, +} + +impl core::ops::Deref for TxPacket<'_> { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + &self.buffer[..self.length] + } +} + +impl core::ops::DerefMut for TxPacket<'_> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.buffer[..self.length] + } +} + +impl TxPacket<'_> { + /// Send this packet! + pub fn send(self) { + drop(self); + } +} + +impl Drop for TxPacket<'_> { + fn drop(&mut self) { + self.desc + .send(self.packet_id.clone(), &self.buffer[..self.length]); + TxRing::demand_poll(); + } +} diff --git a/src/lib.rs b/src/lib.rs index 054a7f65..bca57f9f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -142,6 +142,9 @@ pub use crate::stm32 as device; #[cfg_attr(docsrs, doc(cfg(feature = "rt")))] pub use crate::stm32::interrupt; +#[cfg(feature = "device-selected")] +pub mod ptp; + #[cfg(feature = "device-selected")] pub mod adc; #[cfg(all(feature = "device-selected", feature = "can"))] diff --git a/src/ptp/mod.rs b/src/ptp/mod.rs new file mode 100644 index 00000000..f79c4223 --- /dev/null +++ b/src/ptp/mod.rs @@ -0,0 +1,7 @@ +mod timestamp; +pub use timestamp::Timestamp; + +mod subseconds; +pub use subseconds::{ + Subseconds, NANOS_PER_SECOND, SUBSECONDS_PER_SECOND, SUBSECONDS_TO_SECONDS, +}; diff --git a/src/ptp/subseconds.rs b/src/ptp/subseconds.rs new file mode 100644 index 00000000..0d168405 --- /dev/null +++ b/src/ptp/subseconds.rs @@ -0,0 +1,165 @@ +/// The amount of nanoseconds per second. +pub const NANOS_PER_SECOND: u32 = 1_000_000_000; + +/// The amount of subseconds per second. +pub const SUBSECONDS_PER_SECOND: u32 = 0x7FFF_FFFF; + +/// The ratio to use to convert subseconds to seconds. +pub const SUBSECONDS_TO_SECONDS: f32 = 1.0 / (SUBSECONDS_PER_SECOND as f32); + +const NS_PER_S: u64 = NANOS_PER_SECOND as u64; +const SUBS_PER_S: u64 = SUBSECONDS_PER_SECOND as u64; + +/// A subsecond value as produced by the PTP peripheral +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct Subseconds(u32); + +impl Subseconds { + /// The maximum possible value for [`Subseconds`] + pub const MAX_VALUE: u32 = SUBSECONDS_PER_SECOND; + + /// The maximum possible [`Subseconds`] + pub const MAX: Self = Self(SUBSECONDS_PER_SECOND); + + /// Zero [`Subseconds`] + pub const ZERO: Self = Self(0); + + /// Create a new [`Subseconds`] from the provided value. + /// + /// The returned [`Subseconds`] represents a time of `value / 2^31` seconds. + /// + /// To obtain that representation in nanoseconds, see [`Subseconds::nanos`]. + /// + /// To approximate a [`Subseconds`] from nanoseconds, see [`Subseconds::new_from_nanos`]. + /// + /// Returns `None` if `value > SUBSECONDS_PER_SECOND`. (See [`SUBSECONDS_PER_SECOND`]). + pub const fn new(value: u32) -> Option { + if value > SUBSECONDS_PER_SECOND as u32 { + None + } else { + Some(Self(value)) + } + } + + /// Create a new [`Subseconds`] from the provided value, without verifying that `value` + /// is less than or equal to [`Self::MAX_VALUE`]). + /// + /// The returned [`Subseconds`] represents a time of `value / 2^31` seconds. + /// + /// To obtain that representation in nanoseconds, see [`Subseconds::nanos`]. + /// + /// To approximate a [`Subseconds`] from nanoseconds, see [`Subseconds::new_from_nanos`] + pub(crate) const fn new_unchecked(value: u32) -> Self { + Self(value) + } + + /// Create a new [`Subseconds`] from the given amount of nanoseconds, + /// using a round-to-nearest method. + /// + /// Returns [`None`] if `nanos >= NANOS_PER_SECOND`. (See [`NANOS_PER_SECOND`]) + pub const fn new_from_nanos(nanos: u32) -> Option { + if nanos >= NANOS_PER_SECOND as u32 { + return None; + } + + let subseconds = ((nanos as u64 * SUBS_PER_S) + (NS_PER_S / 2)) / NS_PER_S; + + Some(Subseconds::new_unchecked(subseconds as u32)) + } + + /// Convert this [`Subseconds`] to nanoseconds, using a round-to-nearest method. + pub const fn nanos(&self) -> u32 { + let nanos = ((self.0 as u64 * NS_PER_S) + (SUBS_PER_S / 2)) / SUBS_PER_S; + + nanos as u32 + } + + /// Get the raw value of this [`Subseconds`] + pub const fn raw(&self) -> u32 { + self.0 + } + + /// Convert this [`Subseconds`] to Hertz + pub(crate) const fn hertz(&self) -> u32 { + SUBSECONDS_PER_SECOND as u32 / self.0 + } + + pub(crate) const fn nearest_increment(input_clk_hz: u32) -> Subseconds { + let hclk_half_subs = (SUBSECONDS_PER_SECOND as u32 + (input_clk_hz / 2)) / input_clk_hz; + + Self::new_unchecked(hclk_half_subs) + } +} + +impl core::ops::Add for Subseconds { + type Output = Self; + + fn add(self, rhs: Subseconds) -> Self::Output { + Self(self.0.wrapping_add(rhs.0) % (SUBSECONDS_PER_SECOND + 1)) + } +} + +impl core::ops::AddAssign for Subseconds { + fn add_assign(&mut self, rhs: Subseconds) { + *self = *self + rhs; + } +} + +impl core::ops::Sub for Subseconds { + type Output = Self; + + fn sub(self, rhs: Subseconds) -> Self::Output { + Self(self.0.wrapping_sub(rhs.0) % (SUBSECONDS_PER_SECOND + 1)) + } +} + +impl core::ops::SubAssign for Subseconds { + fn sub_assign(&mut self, rhs: Subseconds) { + *self = *self - rhs; + } +} + +#[cfg(all(test, not(target_os = "none")))] +mod test { + + use super::*; + + // Assert that values produced by [`Subseconds::nearest_increment`] for some + // valid frequencies are within the correct span for `stssi` + #[test] + fn correct_subsecond_increment() { + for i in (25_000..180_000).map(|v| v * 1_000) { + let subs = Subseconds::nearest_increment(i).raw(); + assert!(subs > 0 && subs <= 255); + } + } + + #[test] + fn from_nanos() { + for i in [0, 1, 2, 3, NANOS_PER_SECOND - 1] { + let subseconds = Subseconds::new_from_nanos(i).unwrap(); + assert!(subseconds.raw() < SUBSECONDS_PER_SECOND); + } + + assert!(Subseconds::new_from_nanos(NANOS_PER_SECOND).is_none()); + assert!(Subseconds::new_from_nanos(u32::MAX).is_none()); + } + + #[test] + fn subsecond_math() { + let one = Subseconds::new(1).unwrap(); + let two = Subseconds::new(2).unwrap(); + let three = Subseconds::new(3).unwrap(); + let max = Subseconds::new(SUBSECONDS_PER_SECOND).unwrap(); + let zero = Subseconds::new(0).unwrap(); + + assert_eq!(one + two, three); + assert_eq!(two - one, one); + + assert_eq!(one - max + max, one); + assert_eq!(one - two, max); + assert_eq!(one + max, zero); + assert_eq!(two + max, one); + } +} diff --git a/src/ptp/timestamp.rs b/src/ptp/timestamp.rs new file mode 100644 index 00000000..db378639 --- /dev/null +++ b/src/ptp/timestamp.rs @@ -0,0 +1,229 @@ +use super::{Subseconds, NANOS_PER_SECOND}; + +/// A timestamp produced by the PTP periperhal +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Timestamp(i64); + +#[cfg(feature = "defmt")] +impl defmt::Format for Timestamp { + fn format(&self, fmt: defmt::Formatter) { + if self.is_positive() { + defmt::write!(fmt, "{}.{:09}", self.seconds(), self.nanos()); + } else { + defmt::write!(fmt, "-{}.{:09}", self.seconds(), self.nanos()); + } + } +} + +impl Timestamp { + // The bit that represents the signedness of the timestamp in the + // subseconds value. + const SIGN_BIT: u32 = 0x8000_0000; + + /// Create a new [`Timestamp`] + pub const fn new(negative: bool, seconds: u32, subseconds: Subseconds) -> Self { + Self::new_unchecked(negative, seconds, subseconds.raw()) + } + + /// Create a new [`Timestamp`] from the given raw value. + pub const fn new_raw(value: i64) -> Self { + Self(value) + } + + /// Get the raw value of this [`Timestamp`] + pub const fn raw(&self) -> i64 { + self.0 + } + + /// Check whether this timestamp is negative or not. + pub const fn is_negative(&self) -> bool { + self.0.is_negative() + } + + /// Check whether this timestamp is positive or not. + pub const fn is_positive(&self) -> bool { + !self.is_negative() + } + + pub(crate) const fn new_unchecked(negative: bool, seconds: u32, subseconds: u32) -> Self { + let seconds: i64 = (seconds as i64) << 31; + let subseconds: i64 = subseconds as i64; + + let mut total = seconds + subseconds; + + if negative { + total = -total; + }; + + Self(total) + } + + /// Get the second component of this timestamp + pub const fn seconds(&self) -> u32 { + (self.0.abs() >> 31) as u32 + } + + /// Get the raw subsecond value of this timestamp. + pub const fn subseconds(&self) -> Subseconds { + Subseconds::new_unchecked(self.0.abs() as u32 & Subseconds::MAX_VALUE) + } + + /// Get the signed subsecond value of this timestamp. + /// + /// Note that this is _not_ an i32: it is, technically, + /// a u31 with a leading sign bit. + pub const fn subseconds_signed(&self) -> u32 { + let mut subseconds = self.subseconds().raw(); + + if self.0.is_negative() { + subseconds |= Self::SIGN_BIT; + } + + subseconds + } + + /// Get the nanosecond component of this timestamp + pub const fn nanos(&self) -> u32 { + self.subseconds().nanos() + } + + /// Get the total amount of nanoseconds in this [`Timestamp`]. + /// + /// Example: + /// ```rust + /// # use stm32_eth::ptp::{Subseconds, Timestamp}; + /// let timestamp = Timestamp::new(false, 500, Subseconds::new_from_nanos(500_000).unwrap()); + /// assert_eq!(timestamp.total_nanos(), 500 * 1_000_000_000 + 500_000); + /// + /// + /// let timestamp_neg = Timestamp::new(true, 500, Subseconds::new_from_nanos(500_000).unwrap()); + /// assert_eq!(timestamp_neg.total_nanos(), -1 * (500 * 1_000_000_000 + 500_000)); + /// ``` + pub const fn total_nanos(&self) -> i64 { + let nanos = self.seconds() as i64 * NANOS_PER_SECOND as i64 + self.nanos() as i64; + + if self.is_positive() { + nanos + } else { + -nanos + } + } + + /// Create a new timestamp from the provided register values. + pub const fn from_parts(high: u32, low: u32) -> Timestamp { + let negative = (low & Self::SIGN_BIT) == Self::SIGN_BIT; + let subseconds = low & !(Self::SIGN_BIT); + + Timestamp::new_unchecked(negative, high, subseconds) + } +} + +impl core::ops::Add for Timestamp { + type Output = Self; + + fn add(self, rhs: Timestamp) -> Self::Output { + Self(self.0 + rhs.0) + } +} + +impl core::ops::AddAssign for Timestamp { + fn add_assign(&mut self, rhs: Timestamp) { + self.0 += rhs.0; + } +} + +impl core::ops::Sub for Timestamp { + type Output = Self; + + fn sub(self, rhs: Timestamp) -> Self::Output { + Self(self.0 - rhs.0) + } +} + +impl core::ops::SubAssign for Timestamp { + fn sub_assign(&mut self, rhs: Timestamp) { + self.0 -= rhs.0 + } +} + +#[cfg(all(test, not(target_os = "none")))] +mod test { + use crate::ptp::SUBSECONDS_PER_SECOND; + + use super::{Subseconds, Timestamp}; + + fn subs(val: u32) -> Subseconds { + Subseconds::new(val).unwrap() + } + + #[test] + fn timestamp_add() { + let one = Timestamp::new(false, 1, subs(1)); + let one_big = Timestamp::new(false, 1, subs(SUBSECONDS_PER_SECOND - 1)); + let two = Timestamp::new(false, 2, subs(2)); + let three = Timestamp::new(false, 3, subs(3)); + + let one_neg = Timestamp::new(true, 1, subs(1)); + let one_big_neg = Timestamp::new(true, 1, subs(SUBSECONDS_PER_SECOND - 1)); + let two_neg = Timestamp::new(true, 2, subs(2)); + let three_neg = Timestamp::new(true, 3, subs(3)); + + let one_minus_two = Timestamp::new(true, 1, subs(1)); + let one_big_plus_two = Timestamp::new(false, 4, subs(0)); + let two_minus_one_big = Timestamp::new(false, 0, subs(4)); + let one_big_neg_plus_two_neg = Timestamp::new(true, 4, subs(0)); + + // +self + +rhs + assert_eq!(one + two, three); + assert_eq!(two + one, three); + assert_eq!(one_big + two, one_big_plus_two); + assert_eq!(two + one_big, one_big_plus_two); + + // +self + -rhs + assert_eq!(one + two_neg, one_minus_two); + assert_eq!(two + one_big_neg, two_minus_one_big); + + // -self + rhs + assert_eq!(one_neg + two, one); + assert_eq!(two + one_neg, one); + + // -self + -rhs + assert_eq!(one_neg + two_neg, three_neg); + assert_eq!(two_neg + one_neg, three_neg); + assert_eq!(one_big_neg + two_neg, one_big_neg_plus_two_neg); + assert_eq!(two_neg + one_big_neg, one_big_neg_plus_two_neg); + } + + #[test] + fn timestamp_sub() { + let one = Timestamp::new(false, 1, subs(1)); + let one_big = Timestamp::new(false, 1, subs(SUBSECONDS_PER_SECOND - 1)); + let two = Timestamp::new(false, 2, subs(2)); + let three = Timestamp::new(false, 3, subs(3)); + + let one_neg = Timestamp::new(true, 1, subs(1)); + let two_neg = Timestamp::new(true, 2, subs(2)); + let three_neg = Timestamp::new(true, 3, subs(3)); + + let one_minus_two = Timestamp::new(true, 1, subs(1)); + let one_minus_one_big = Timestamp::new(true, 0, subs(SUBSECONDS_PER_SECOND - 2)); + + assert_eq!(one - one_big, one_minus_one_big); + + // +self - +rhs + assert_eq!(two - one, one); + assert_eq!(one - two, one_minus_two); + + // +self - -rhs + assert_eq!(two - one_neg, three); + assert_eq!(one_neg - two, three_neg); + + // -self - +rhs + assert_eq!(one_neg - two, three_neg); + assert_eq!(two - one_neg, three); + + // -self - -rhs + assert_eq!(one_neg - two_neg, one); + assert_eq!(two_neg - one_neg, one_minus_two); + } +} From 313cefc36f7add136f8054edcb42140dbec7d789 Mon Sep 17 00:00:00 2001 From: ZachParallel <134434083+ZachParallel@users.noreply.github.com> Date: Fri, 28 Jul 2023 13:14:36 -0400 Subject: [PATCH 02/77] Removed default features --- Cargo.toml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e112ceef..d7229f91 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -115,13 +115,13 @@ default-features = false features = ["medium-ethernet", "proto-ipv4", "proto-ipv6", "socket-raw"] [features] -default = ["rt", "device-selected", "stm32h743v", "ethernet"] +default = ["rt"] device-selected = [] revision_v = [] -rm0433 = ["gpio-h747"] # aka. "single core" devices -rm0399 = ["gpio-h747"] # TODO: fix gpio # aka. "dual core" devices -rm0455 = ["gpio-h7a2"] # aka. "high memory integration" devices -rm0468 = ["gpio-h72"] # aka. "high speed" devices +rm0433 = ["gpio-h747"] # aka. "single core" devices +rm0399 = ["gpio-h747"] # TODO: fix gpio # aka. "dual core" devices +rm0455 = ["gpio-h7a2"] # aka. "high memory integration" devices +rm0468 = ["gpio-h72"] # aka. "high speed" devices gpio-h72 = [] gpio-h747 = [] From a2d53a66b86f810facafcadc0a0837690108fb67 Mon Sep 17 00:00:00 2001 From: ZachParallel <134434083+ZachParallel@users.noreply.github.com> Date: Fri, 28 Jul 2023 15:27:25 -0400 Subject: [PATCH 03/77] Removed unused lifetime from Devive impl --- src/ethernet/eth.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ethernet/eth.rs b/src/ethernet/eth.rs index b54f9d33..d52984e5 100644 --- a/src/ethernet/eth.rs +++ b/src/ethernet/eth.rs @@ -1027,7 +1027,7 @@ impl<'dma, 'tx> TxToken for EthTxToken<'dma, 'tx> { // } /// Use this Ethernet driver with [smoltcp](https://github.com/smoltcp-rs/smoltcp) -impl<'a, 'rx, 'tx> Device for &'a mut EthernetDMA<'rx, 'tx> { +impl<'rx, 'tx> Device for EthernetDMA<'rx, 'tx> { type RxToken<'token> = EthRxToken<'token, 'rx> where Self: 'token; type TxToken<'token> = EthTxToken<'token, 'tx> where Self: 'token; From f9ee69913416381d9b1e95fd3eedcd33067790d7 Mon Sep 17 00:00:00 2001 From: ZachParallel <134434083+ZachParallel@users.noreply.github.com> Date: Fri, 28 Jul 2023 15:34:00 -0400 Subject: [PATCH 04/77] Added the a lifetime back --- src/ethernet/eth.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ethernet/eth.rs b/src/ethernet/eth.rs index d52984e5..b54f9d33 100644 --- a/src/ethernet/eth.rs +++ b/src/ethernet/eth.rs @@ -1027,7 +1027,7 @@ impl<'dma, 'tx> TxToken for EthTxToken<'dma, 'tx> { // } /// Use this Ethernet driver with [smoltcp](https://github.com/smoltcp-rs/smoltcp) -impl<'rx, 'tx> Device for EthernetDMA<'rx, 'tx> { +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; From 5fe9187e409746fe24b3d6217b8ae3e5aa597615 Mon Sep 17 00:00:00 2001 From: ZachParallel <134434083+ZachParallel@users.noreply.github.com> Date: Fri, 28 Jul 2023 15:36:39 -0400 Subject: [PATCH 05/77] Removed a --- src/ethernet/eth.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ethernet/eth.rs b/src/ethernet/eth.rs index b54f9d33..d52984e5 100644 --- a/src/ethernet/eth.rs +++ b/src/ethernet/eth.rs @@ -1027,7 +1027,7 @@ impl<'dma, 'tx> TxToken for EthTxToken<'dma, 'tx> { // } /// Use this Ethernet driver with [smoltcp](https://github.com/smoltcp-rs/smoltcp) -impl<'a, 'rx, 'tx> Device for &'a mut EthernetDMA<'rx, 'tx> { +impl<'rx, 'tx> Device for EthernetDMA<'rx, 'tx> { type RxToken<'token> = EthRxToken<'token, 'rx> where Self: 'token; type TxToken<'token> = EthTxToken<'token, 'tx> where Self: 'token; From d7eae1fc6718bde7948cd50d9d67bc81a8993cb8 Mon Sep 17 00:00:00 2001 From: ZachParallel <134434083+ZachParallel@users.noreply.github.com> Date: Fri, 28 Jul 2023 16:06:30 -0400 Subject: [PATCH 06/77] Muttable reference DMA --- src/ethernet/eth.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ethernet/eth.rs b/src/ethernet/eth.rs index d52984e5..0d293b11 100644 --- a/src/ethernet/eth.rs +++ b/src/ethernet/eth.rs @@ -1027,7 +1027,7 @@ impl<'dma, 'tx> TxToken for EthTxToken<'dma, 'tx> { // } /// Use this Ethernet driver with [smoltcp](https://github.com/smoltcp-rs/smoltcp) -impl<'rx, 'tx> Device for EthernetDMA<'rx, 'tx> { +impl<'rx, 'tx> Device for &mut EthernetDMA<'rx, 'tx> { type RxToken<'token> = EthRxToken<'token, 'rx> where Self: 'token; type TxToken<'token> = EthTxToken<'token, 'tx> where Self: 'token; From 051785f7c6927fb09be33093520c4785cd098cb9 Mon Sep 17 00:00:00 2001 From: ZachParallel <134434083+ZachParallel@users.noreply.github.com> Date: Mon, 31 Jul 2023 10:29:29 -0700 Subject: [PATCH 07/77] Added a --- src/ethernet/eth.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ethernet/eth.rs b/src/ethernet/eth.rs index 0d293b11..b54f9d33 100644 --- a/src/ethernet/eth.rs +++ b/src/ethernet/eth.rs @@ -1027,7 +1027,7 @@ impl<'dma, 'tx> TxToken for EthTxToken<'dma, 'tx> { // } /// Use this Ethernet driver with [smoltcp](https://github.com/smoltcp-rs/smoltcp) -impl<'rx, 'tx> Device for &mut EthernetDMA<'rx, 'tx> { +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; From 3541cabc0c6c54d1542e503b14edff14caaadb26 Mon Sep 17 00:00:00 2001 From: ZachParallel <134434083+ZachParallel@users.noreply.github.com> Date: Mon, 31 Jul 2023 10:41:48 -0700 Subject: [PATCH 08/77] Explicit Device --- src/ethernet/eth.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ethernet/eth.rs b/src/ethernet/eth.rs index b54f9d33..727c213e 100644 --- a/src/ethernet/eth.rs +++ b/src/ethernet/eth.rs @@ -1027,7 +1027,7 @@ impl<'dma, 'tx> TxToken for EthTxToken<'dma, 'tx> { // } /// Use this Ethernet driver with [smoltcp](https://github.com/smoltcp-rs/smoltcp) -impl<'a, 'rx, 'tx> Device for &'a mut EthernetDMA<'rx, 'tx> { +impl<'a, 'rx, 'tx> phy::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; From 5012a2d4d0a88445ef1ce3e19f4686929f4070a4 Mon Sep 17 00:00:00 2001 From: ZachParallel <134434083+ZachParallel@users.noreply.github.com> Date: Mon, 31 Jul 2023 11:12:11 -0700 Subject: [PATCH 09/77] Added ptp feature --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index d7229f91..7c19f8c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -127,6 +127,7 @@ gpio-h72 = [] gpio-h747 = [] gpio-h7a2 = [] +ptp = ["smoltcp/packetmeta-id"] dsi = [] cm4 = [] cm7 = [] From b4bea4dd03d49b829f447e4a3043af776a8fc710 Mon Sep 17 00:00:00 2001 From: ZachParallel <134434083+ZachParallel@users.noreply.github.com> Date: Mon, 31 Jul 2023 12:38:00 -0700 Subject: [PATCH 10/77] PTP error correction --- src/ethernet/eth.rs | 6 + src/ethernet/packet_id.rs | 4 +- src/ethernet/rx/mod.rs | 2 +- src/ethernet/tx/mod.rs | 3 +- src/ptp/mod.rs | 385 ++++++++++++++++++++++++++++++++++++++ src/ptp/subseconds.rs | 11 +- 6 files changed, 404 insertions(+), 7 deletions(-) diff --git a/src/ethernet/eth.rs b/src/ethernet/eth.rs index 727c213e..57afc5fc 100644 --- a/src/ethernet/eth.rs +++ b/src/ethernet/eth.rs @@ -22,6 +22,9 @@ //! [quartiq/stabilizer]: https://github.com/quartiq/stabilizer //! [notes]: https://github.com/quartiq/stabilizer/commit/ab1735950b2108eaa8d51eb63efadcd2e25c35c4 +use core::task::Poll; + +use crate::ptp::{EthernetPTP, Timestamp}; use crate::rcc::{rec, CoreClocks, ResetEnable}; use crate::stm32; use crate::stm32::{Interrupt, ETHERNET_DMA, ETHERNET_MTL, NVIC}; @@ -828,6 +831,9 @@ pub struct InterruptReason { pub fn eth_interrupt_handler() -> InterruptReason { let dma = EthernetDMA::interrupt_handler(); + #[cfg(feature = "ptp")] + let is_time_trigger = EthernetPTP::interrupt_handler(); + InterruptReason { rx: dma.is_rx, tx: dma.is_tx, diff --git a/src/ethernet/packet_id.rs b/src/ethernet/packet_id.rs index 01283f4d..da07d2ee 100644 --- a/src/ethernet/packet_id.rs +++ b/src/ethernet/packet_id.rs @@ -24,14 +24,14 @@ impl From for PacketId { } } -#[cfg(all(feature = "ptp", feature = "smoltcp-phy"))] +#[cfg(feature = "ptp")] impl From for PacketId { fn from(value: smoltcp::phy::PacketMeta) -> Self { Self(value.id) } } -#[cfg(all(feature = "ptp", feature = "smoltcp-phy"))] +#[cfg(feature = "ptp")] impl From for smoltcp::phy::PacketMeta { fn from(value: PacketId) -> Self { let mut meta = smoltcp::phy::PacketMeta::default(); diff --git a/src/ethernet/rx/mod.rs b/src/ethernet/rx/mod.rs index 218b77d5..a5d9719f 100644 --- a/src/ethernet/rx/mod.rs +++ b/src/ethernet/rx/mod.rs @@ -7,7 +7,7 @@ use crate::stm32::ETHERNET_DMA; mod descriptor; #[cfg(feature = "ptp")] -use crate::{dma::PacketIdNotFound, ptp::Timestamp}; +use crate::{ethernet::eth::PacketIdNotFound, ptp::Timestamp}; #[cfg(feature = "async-await")] use core::task::Poll; diff --git a/src/ethernet/tx/mod.rs b/src/ethernet/tx/mod.rs index 15b7f9d1..9c95063b 100644 --- a/src/ethernet/tx/mod.rs +++ b/src/ethernet/tx/mod.rs @@ -2,7 +2,8 @@ use super::{raw_descriptor::DescriptorRing, PacketId}; use crate::stm32::ETHERNET_DMA; #[cfg(feature = "ptp")] -use super::{PacketIdNotFound, Timestamp}; +use super::eth::PacketIdNotFound; +use crate::ptp::Timestamp; #[path = "./h_descriptor.rs"] mod descriptor; diff --git a/src/ptp/mod.rs b/src/ptp/mod.rs index f79c4223..f01fe64a 100644 --- a/src/ptp/mod.rs +++ b/src/ptp/mod.rs @@ -1,7 +1,392 @@ +//! PTP access and configuration. +//! +//! See [`EthernetPTP`] for a more details. + +use crate::ethernet::EthernetDMA; +use crate::rcc::CoreClocks; + mod timestamp; pub use timestamp::Timestamp; +#[cfg(all(not(feature = "stm32f1xx-hal"), feature = "async-await"))] +use {core::task::Poll, futures::task::AtomicWaker}; + mod subseconds; pub use subseconds::{ Subseconds, NANOS_PER_SECOND, SUBSECONDS_PER_SECOND, SUBSECONDS_TO_SECONDS, }; + +/// Access to the IEEE 1508v2 PTP peripheral present on the ethernet peripheral. +/// +/// On STM32FXXX's, the PTP peripheral has/uses the following important parts: +/// * HCLK (the chip's high speed clock, configured externally). +/// * The global timestamp (`global_time`, a [`Timestamp`]). +/// * A subsecond increment register (`subsecond_increment`, a [`Subseconds`] with a value of 0 to 255, see [`EthernetPTP::subsecond_increment`]). +/// * An accumulator register (`accumulator`, an [`u32`]). +/// * An addend register (`addend`, an [`u32`], see [`EthernetPTP::addend`] and [`EthernetPTP::set_addend`]). +/// +/// To ensure that `global_time` advances at the correct rate, the system performs the following steps: +/// 1. On every clock of HCLK, `addend` is added to `accumulator`. +/// 2. If `accumulator` overflows during step 1, add `subsecond_increment` to `global_time`. +/// +/// When a new [`EthernetPTP`] is created, it is assumed that the frequency of HCLK is exactly correct. +/// Using HCLK, values for `subsecond_increment` and `addend` are calculated so that `global_time` represents +/// real-time. +/// +/// Subsequently, `addend` can be adjusted to compensate for possible errors in HCLK, using [`EthernetPTP::addend`] and [`EthernetPTP::set_addend`] +/// +/// To assess the correctness of the current speed at which `global_time` is running, one can use the +/// following equation: +/// +/// ```no_compile +/// clock_ratio = ((2^31 / subsecond_increment) / (HCLK_HZ * (addend / 2^32))) +/// ``` +/// Values greater than 1 indicate that the provided `HCLK_HZ` is less than the actual frequency of HCLK, which should +/// be compensated by increasing `addend`. Values less than 1 indicate that the provided `HCLK_HZ` is greater than the +/// actual frequency of HCLK, which should be compensated by decreasing `addend`. +/// +/// [`NonZeroU8`]: core::num::NonZeroU8 +pub struct EthernetPTP {} + +impl EthernetPTP { + /// # Safety + /// The reference to the registerblock obtained using this function + /// must _only_ be used to change strictly PTP related registers. + unsafe fn mac() -> &'static crate::stm32::ethernet_mac::RegisterBlock { + &*crate::stm32::ETHERNET_MAC::ptr() + } + + // Calculate the `addend` required for running `global_time` at + // the correct rate + const fn calculate_regs(hclk: u32) -> (Subseconds, u32) { + let half_hclk = hclk / 2; + + // Calculate the closest `subsecond_increment` we can use if we want to update at a + // frequency of `half_hclk` + let stssi = Subseconds::nearest_increment(half_hclk); + let half_rate_subsec_increment_hz = stssi.hertz(); + + // Calculate the `addend` required for running `global_time` at + // the correct rate, given that we increment `global_time` by `stssi` every + // time `accumulator` overflows. + let tsa = ((half_rate_subsec_increment_hz as u64 * u32::MAX as u64) + / hclk as u64) as u32; + (stssi, tsa) + } + + pub(crate) fn new( + clocks: CoreClocks, + // 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 { + let hclk = clocks.hclk().to_Hz(); + + let (stssi, tsa) = Self::calculate_regs(hclk); + + let mut me = { + let me = Self {}; + + // SAFETY: we only write to `mactscr` (timestamp control register) + let mac = unsafe { Self::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() + // Tell MAC to overwrite non-read timestamps + .txtsstsm() + .set_bit() + }); + + // Set up the subsecond increment + mac.macssir + .write(|w| unsafe { w.ssinc().bits(stssi.raw() as u8) }); + + me + }; + + me.set_addend(tsa); + me.set_time(Timestamp::new_unchecked(false, 0, 0)); + + me + } + + /// Get the configured subsecond increment. + pub fn subsecond_increment(&self) -> Subseconds { + // 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 { + // 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) { + { + // 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 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(); + + { + // 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. + /// + /// If `time` is negative, it will instead be subtracted from the + /// system time. + pub fn update_time(&mut self, time: Timestamp) { + let seconds = time.seconds(); + let subseconds = time.subseconds_signed(); + + { + // 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 now() -> Timestamp { + Self::get_time() + } + + /// Get the current time. + pub fn get_time() -> Timestamp { + let try_read_time = || { + let (seconds, subseconds, seconds_after) = { + // SAFETY: we only atomically read PTP registers. + 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)) + } else { + Err(()) + } + }; + + loop { + if let Ok(res) = try_read_time() { + return res; + } + } + } + + // /// Enable the PPS output on the provided pin. + // pub fn enable_pps

(&mut self, pin: P) -> P::Output + // where + // P: PPSPin, + // { + // pin.enable() + // } +} + +/// 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. +impl EthernetPTP { + #[cfg(feature = "async-await")] + fn waker() -> &'static AtomicWaker { + static WAKER: AtomicWaker = AtomicWaker::new(); + &WAKER + } + + /// Configure the target time. + fn set_target_time(&mut self, timestamp: Timestamp) { + let (high, low) = (timestamp.seconds(), timestamp.subseconds_signed()); + + { + // 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. + /// + /// You must call [`EthernetPTP::interrupt_handler`] in the `ETH` + /// interrupt to detect (and clear) the correct status bits. + pub fn configure_target_time_interrupt(&mut self, timestamp: Timestamp) { + self.set_target_time(timestamp); + } + + /// Wait until the specified time. + #[cfg(feature = "async-await")] + pub async fn wait_until(&mut self, timestamp: Timestamp) { + self.configure_target_time_interrupt(timestamp); + + core::future::poll_fn(|ctx| { + if EthernetPTP::read_and_clear_interrupt_flag() { + Poll::Ready(()) + } else if EthernetPTP::now().raw() >= timestamp.raw() { + Poll::Ready(()) + } else { + EthernetPTP::waker().register(ctx.waker()); + Poll::Pending + } + }) + .await; + } + + #[inline(always)] + fn read_and_clear_interrupt_flag() -> bool { + { + // SAFETY: we only read the ethernet ptp status register. + + let mac = unsafe { Self::mac() }; + + // Reading the register clears all of the bits in + // that register. + let tssr = mac.mactssr.read(); + let tstargt0 = tssr.tstargt0().bit_is_set(); + let tstrgterr0 = tssr.tstrgterr0().bit_is_set(); + + tstargt0 || tstrgterr0 + } + } + + /// Handle the PTP parts of the `ETH` 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() -> bool { + let is_tsint = { + // SAFETY: we only read from `macisr` (Interrupt status register) + let macisr = unsafe { &Self::mac().macisr }; + macisr.read().tsis().bit_is_set() + }; + + #[cfg(feature = "async-await")] + if is_tsint { + if let Some(waker) = EthernetPTP::waker().take() { + waker.wake(); + } else { + EthernetPTP::read_and_clear_interrupt_flag(); + } + } + + #[cfg(not(feature = "async-await"))] + EthernetPTP::read_and_clear_interrupt_flag(); + + is_tsint + } + + /// Configure the PPS output frequency. + /// + /// The PPS output frequency becomes `2 ^ pps_freq`. `pps_freq` is + /// clamped to `[0..31]`. + pub fn set_pps_freq(&mut self, pps_freq: u8) { + let pps_freq = pps_freq.max(31); + + // 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. + { + // 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)); + } + } +} + +#[cfg(all(test, not(target_os = "none")))] +mod test { + + use super::*; + + // Test that we get accurate addend and subsecond_increment values + // with the provided clock speeds. + #[test] + fn hclk_to_regs() { + for hclk_hz in (25..180).map(|v| v * 1_000_000) { + let (stssi, tsa) = EthernetPTP::calculate_regs(hclk_hz); + + let stssi = stssi.raw() as f64; + let tsa = tsa as f64; + + // calculate the clock ratio + let clock_ratio = (SUBSECONDS_PER_SECOND as f64 / stssi) + / (hclk_hz as f64 * (tsa / 0xFFFF_FFFFu32 as f64)); + + let ppm = (clock_ratio - 1f64) * 1_000_000f64; + + assert!(ppm <= 0.06, "{} at {}", ppm, hclk_hz); + } + } +} diff --git a/src/ptp/subseconds.rs b/src/ptp/subseconds.rs index 0d168405..c9d736d7 100644 --- a/src/ptp/subseconds.rs +++ b/src/ptp/subseconds.rs @@ -63,14 +63,16 @@ impl Subseconds { return None; } - let subseconds = ((nanos as u64 * SUBS_PER_S) + (NS_PER_S / 2)) / NS_PER_S; + let subseconds = + ((nanos as u64 * SUBS_PER_S) + (NS_PER_S / 2)) / NS_PER_S; Some(Subseconds::new_unchecked(subseconds as u32)) } /// Convert this [`Subseconds`] to nanoseconds, using a round-to-nearest method. pub const fn nanos(&self) -> u32 { - let nanos = ((self.0 as u64 * NS_PER_S) + (SUBS_PER_S / 2)) / SUBS_PER_S; + let nanos = + ((self.0 as u64 * NS_PER_S) + (SUBS_PER_S / 2)) / SUBS_PER_S; nanos as u32 } @@ -80,13 +82,16 @@ impl Subseconds { self.0 } + #[allow(unused)] /// Convert this [`Subseconds`] to Hertz pub(crate) const fn hertz(&self) -> u32 { SUBSECONDS_PER_SECOND as u32 / self.0 } + #[allow(unused)] pub(crate) const fn nearest_increment(input_clk_hz: u32) -> Subseconds { - let hclk_half_subs = (SUBSECONDS_PER_SECOND as u32 + (input_clk_hz / 2)) / input_clk_hz; + let hclk_half_subs = + (SUBSECONDS_PER_SECOND as u32 + (input_clk_hz / 2)) / input_clk_hz; Self::new_unchecked(hclk_half_subs) } From 29fbfc423884629aaffa859ce4d88fbfa28691db Mon Sep 17 00:00:00 2001 From: ZachParallel <134434083+ZachParallel@users.noreply.github.com> Date: Mon, 31 Jul 2023 18:13:10 -0700 Subject: [PATCH 11/77] Added defmt info comments --- src/ethernet/tx/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ethernet/tx/mod.rs b/src/ethernet/tx/mod.rs index 9c95063b..264d9ca8 100644 --- a/src/ethernet/tx/mod.rs +++ b/src/ethernet/tx/mod.rs @@ -256,8 +256,10 @@ impl TxRing<'_> { fn entry_for_id(&self, id: &PacketId) -> Option { self.ring.descriptors().enumerate().find_map(|(idx, e)| { if e.has_packet_id(id) { + defmt::info!("Entry: {} for packet id: {}", id.0, idx); Some(idx) } else { + defmt::info!("No entry for packet id: {}", id.0); None } }) From 3a200ec01f69eb61ef01656bcc46e61eec038708 Mon Sep 17 00:00:00 2001 From: ZachParallel <134434083+ZachParallel@users.noreply.github.com> Date: Mon, 31 Jul 2023 18:24:46 -0700 Subject: [PATCH 12/77] Removed info --- src/ethernet/tx/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ethernet/tx/mod.rs b/src/ethernet/tx/mod.rs index 264d9ca8..af2c20b5 100644 --- a/src/ethernet/tx/mod.rs +++ b/src/ethernet/tx/mod.rs @@ -259,7 +259,7 @@ impl TxRing<'_> { defmt::info!("Entry: {} for packet id: {}", id.0, idx); Some(idx) } else { - defmt::info!("No entry for packet id: {}", id.0); + // defmt::info!("No entry for packet id: {}", id.0); None } }) From 5aac15bcea390a0c8faa8e55ea0d8bad4b033b81 Mon Sep 17 00:00:00 2001 From: ZachParallel <134434083+ZachParallel@users.noreply.github.com> Date: Mon, 31 Jul 2023 18:44:51 -0700 Subject: [PATCH 13/77] Add defmt feature --- src/ethernet/tx/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ethernet/tx/mod.rs b/src/ethernet/tx/mod.rs index af2c20b5..12c1fbfa 100644 --- a/src/ethernet/tx/mod.rs +++ b/src/ethernet/tx/mod.rs @@ -256,9 +256,11 @@ impl TxRing<'_> { fn entry_for_id(&self, id: &PacketId) -> Option { self.ring.descriptors().enumerate().find_map(|(idx, e)| { if e.has_packet_id(id) { + #[cfg(feature = "defmt")] defmt::info!("Entry: {} for packet id: {}", id.0, idx); Some(idx) } else { + // #[cfg(feature = "defmt")] // defmt::info!("No entry for packet id: {}", id.0); None } From ee4cd73f5e75ca06e949fe84c2bbe6801994e791 Mon Sep 17 00:00:00 2001 From: ZachParallel <134434083+ZachParallel@users.noreply.github.com> Date: Mon, 31 Jul 2023 18:47:45 -0700 Subject: [PATCH 14/77] Change --- .vscode/settings.json | 8 +++++++- Cargo.toml | 10 +++++----- src/ethernet/tx/mod.rs | 2 +- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 5888de33..b5feddb4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,4 +6,10 @@ ], "rust-analyzer.check.allTargets": false, "rust-analyzer.check.targets": "thumbv7em-none-eabihf", -} + "rust-analyzer.linkedProjects": [ + "./Cargo.toml", + "./Cargo.toml", + "./Cargo.toml", + "./Cargo.toml" + ], +} \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 7c19f8c0..97f44785 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -115,13 +115,13 @@ default-features = false features = ["medium-ethernet", "proto-ipv4", "proto-ipv6", "socket-raw"] [features] -default = ["rt"] +default = ["rt", "device-selected", "stm32h743v", "ethernet", "ptp"] device-selected = [] revision_v = [] -rm0433 = ["gpio-h747"] # aka. "single core" devices -rm0399 = ["gpio-h747"] # TODO: fix gpio # aka. "dual core" devices -rm0455 = ["gpio-h7a2"] # aka. "high memory integration" devices -rm0468 = ["gpio-h72"] # aka. "high speed" devices +rm0433 = ["gpio-h747"] # aka. "single core" devices +rm0399 = ["gpio-h747"] # TODO: fix gpio # aka. "dual core" devices +rm0455 = ["gpio-h7a2"] # aka. "high memory integration" devices +rm0468 = ["gpio-h72"] # aka. "high speed" devices gpio-h72 = [] gpio-h747 = [] diff --git a/src/ethernet/tx/mod.rs b/src/ethernet/tx/mod.rs index 12c1fbfa..9dee8f30 100644 --- a/src/ethernet/tx/mod.rs +++ b/src/ethernet/tx/mod.rs @@ -257,7 +257,7 @@ impl TxRing<'_> { self.ring.descriptors().enumerate().find_map(|(idx, e)| { if e.has_packet_id(id) { #[cfg(feature = "defmt")] - defmt::info!("Entry: {} for packet id: {}", id.0, idx); + defmt::info!("Entry: {} for packet id: {}", idx, id.0); Some(idx) } else { // #[cfg(feature = "defmt")] From 3149dd3d7609ae87c2b0cf20ce373ecd76289e71 Mon Sep 17 00:00:00 2001 From: ZachParallel <134434083+ZachParallel@users.noreply.github.com> Date: Mon, 31 Jul 2023 19:25:26 -0700 Subject: [PATCH 15/77] Change --- Cargo.toml | 10 +++++----- src/ethernet/tx/mod.rs | 2 ++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 97f44785..7c19f8c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -115,13 +115,13 @@ default-features = false features = ["medium-ethernet", "proto-ipv4", "proto-ipv6", "socket-raw"] [features] -default = ["rt", "device-selected", "stm32h743v", "ethernet", "ptp"] +default = ["rt"] device-selected = [] revision_v = [] -rm0433 = ["gpio-h747"] # aka. "single core" devices -rm0399 = ["gpio-h747"] # TODO: fix gpio # aka. "dual core" devices -rm0455 = ["gpio-h7a2"] # aka. "high memory integration" devices -rm0468 = ["gpio-h72"] # aka. "high speed" devices +rm0433 = ["gpio-h747"] # aka. "single core" devices +rm0399 = ["gpio-h747"] # TODO: fix gpio # aka. "dual core" devices +rm0455 = ["gpio-h7a2"] # aka. "high memory integration" devices +rm0468 = ["gpio-h72"] # aka. "high speed" devices gpio-h72 = [] gpio-h747 = [] diff --git a/src/ethernet/tx/mod.rs b/src/ethernet/tx/mod.rs index 9dee8f30..162878de 100644 --- a/src/ethernet/tx/mod.rs +++ b/src/ethernet/tx/mod.rs @@ -297,8 +297,10 @@ impl TxRing<'_> { }; if self.entry_available(entry) { + defmt::info!("Entry: {} available", entry); Poll::Ready(Ok(self.entry_timestamp(entry))) } else { + defmt::info!("Entry: {} not available", entry); Poll::Pending } } From 7e5252111b00c3605a9f525b94cbc2922d13d379 Mon Sep 17 00:00:00 2001 From: ZachParallel <134434083+ZachParallel@users.noreply.github.com> Date: Mon, 31 Jul 2023 19:27:38 -0700 Subject: [PATCH 16/77] Change --- src/ethernet/tx/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ethernet/tx/mod.rs b/src/ethernet/tx/mod.rs index 162878de..cad4d937 100644 --- a/src/ethernet/tx/mod.rs +++ b/src/ethernet/tx/mod.rs @@ -297,9 +297,11 @@ impl TxRing<'_> { }; if self.entry_available(entry) { + #[cfg(feature = "defmt")] defmt::info!("Entry: {} available", entry); Poll::Ready(Ok(self.entry_timestamp(entry))) } else { + #[cfg(feature = "defmt")] defmt::info!("Entry: {} not available", entry); Poll::Pending } From 30bf9dd23cc0f1c6bcc8917ca38842b35b4ae64a Mon Sep 17 00:00:00 2001 From: ZachParallel <134434083+ZachParallel@users.noreply.github.com> Date: Mon, 31 Jul 2023 19:52:39 -0700 Subject: [PATCH 17/77] Fix defmt --- src/ethernet/tx/mod.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/ethernet/tx/mod.rs b/src/ethernet/tx/mod.rs index cad4d937..e096c01a 100644 --- a/src/ethernet/tx/mod.rs +++ b/src/ethernet/tx/mod.rs @@ -298,8 +298,15 @@ impl TxRing<'_> { if self.entry_available(entry) { #[cfg(feature = "defmt")] + let time = self.entry_timestamp(entry); defmt::info!("Entry: {} available", entry); - Poll::Ready(Ok(self.entry_timestamp(entry))) + if let Some(ts) = time { + #[cfg(feature = "defmt")] + defmt::info!("Timestamp: {}", ts.total_nanos()) + } else { + defmt::info!("No ts"); + } + Poll::Ready(Ok(ts)) } else { #[cfg(feature = "defmt")] defmt::info!("Entry: {} not available", entry); From d37828ae9ce1fc2dd13d2149f8aa505a03d5415c Mon Sep 17 00:00:00 2001 From: ZachParallel <134434083+ZachParallel@users.noreply.github.com> Date: Mon, 31 Jul 2023 19:53:11 -0700 Subject: [PATCH 18/77] Change --- src/ethernet/tx/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ethernet/tx/mod.rs b/src/ethernet/tx/mod.rs index e096c01a..690f7e8a 100644 --- a/src/ethernet/tx/mod.rs +++ b/src/ethernet/tx/mod.rs @@ -306,7 +306,7 @@ impl TxRing<'_> { } else { defmt::info!("No ts"); } - Poll::Ready(Ok(ts)) + Poll::Ready(Ok(time)) } else { #[cfg(feature = "defmt")] defmt::info!("Entry: {} not available", entry); From 73a631c3d7e9ab31d55fa7c7a24e77ea4bf3dff0 Mon Sep 17 00:00:00 2001 From: ZachParallel <134434083+ZachParallel@users.noreply.github.com> Date: Tue, 1 Aug 2023 08:35:25 -0700 Subject: [PATCH 19/77] Change --- src/ethernet/tx/mod.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ethernet/tx/mod.rs b/src/ethernet/tx/mod.rs index 690f7e8a..3a9235bf 100644 --- a/src/ethernet/tx/mod.rs +++ b/src/ethernet/tx/mod.rs @@ -268,6 +268,9 @@ impl TxRing<'_> { } fn entry_timestamp(&self, index: usize) -> Option { + let t = self.ring.descriptor(index); + #[cfg(feature = "defmt")] + defmt::info!("Descriptor for index: {}", t); self.ring.descriptor(index).timestamp() } @@ -304,7 +307,7 @@ impl TxRing<'_> { #[cfg(feature = "defmt")] defmt::info!("Timestamp: {}", ts.total_nanos()) } else { - defmt::info!("No ts"); + defmt::info!("No ts"); // THERE IS NO TIMESTAMPS APPLIED TO THE ENTRY!!! } Poll::Ready(Ok(time)) } else { From 417b719557dab4efea36ac9ec6309099d3b272e4 Mon Sep 17 00:00:00 2001 From: ZachParallel <134434083+ZachParallel@users.noreply.github.com> Date: Tue, 1 Aug 2023 08:37:58 -0700 Subject: [PATCH 20/77] Change --- src/ethernet/tx/h_descriptor.rs | 3 ++- src/ethernet/tx/mod.rs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ethernet/tx/h_descriptor.rs b/src/ethernet/tx/h_descriptor.rs index 9f37cd9c..7d9c239b 100644 --- a/src/ethernet/tx/h_descriptor.rs +++ b/src/ethernet/tx/h_descriptor.rs @@ -121,7 +121,8 @@ pub use consts::*; #[repr(C, align(4))] #[derive(Clone, Copy)] pub struct TxDescriptor { - inner_raw: RawDescriptor, + // TODO remove pub + pub inner_raw: RawDescriptor, cache: Cache, } diff --git a/src/ethernet/tx/mod.rs b/src/ethernet/tx/mod.rs index 3a9235bf..6055144e 100644 --- a/src/ethernet/tx/mod.rs +++ b/src/ethernet/tx/mod.rs @@ -270,7 +270,7 @@ impl TxRing<'_> { fn entry_timestamp(&self, index: usize) -> Option { let t = self.ring.descriptor(index); #[cfg(feature = "defmt")] - defmt::info!("Descriptor for index: {}", t); + defmt::info!("Descriptor for index: {}", t.inner_raw); self.ring.descriptor(index).timestamp() } From 91bf810f2235ebd24f876b3d64af7047ac3dac3c Mon Sep 17 00:00:00 2001 From: ZachParallel <134434083+ZachParallel@users.noreply.github.com> Date: Tue, 1 Aug 2023 08:38:49 -0700 Subject: [PATCH 21/77] Change --- src/ethernet/tx/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ethernet/tx/mod.rs b/src/ethernet/tx/mod.rs index 6055144e..296ed6c1 100644 --- a/src/ethernet/tx/mod.rs +++ b/src/ethernet/tx/mod.rs @@ -270,7 +270,7 @@ impl TxRing<'_> { fn entry_timestamp(&self, index: usize) -> Option { let t = self.ring.descriptor(index); #[cfg(feature = "defmt")] - defmt::info!("Descriptor for index: {}", t.inner_raw); + defmt::info!("Descriptor for index: {}", t.inner_raw.desc); self.ring.descriptor(index).timestamp() } From 6656b19a9911ab71dddbd1da8944904819665ec7 Mon Sep 17 00:00:00 2001 From: ZachParallel <134434083+ZachParallel@users.noreply.github.com> Date: Tue, 1 Aug 2023 08:42:29 -0700 Subject: [PATCH 22/77] Change --- src/ethernet/tx/mod.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/ethernet/tx/mod.rs b/src/ethernet/tx/mod.rs index 296ed6c1..a1bb2dae 100644 --- a/src/ethernet/tx/mod.rs +++ b/src/ethernet/tx/mod.rs @@ -300,19 +300,20 @@ impl TxRing<'_> { }; if self.entry_available(entry) { - #[cfg(feature = "defmt")] let time = self.entry_timestamp(entry); - defmt::info!("Entry: {} available", entry); + // #[cfg(feature = "defmt")] + // defmt::info!("Entry: {} available", entry); if let Some(ts) = time { #[cfg(feature = "defmt")] defmt::info!("Timestamp: {}", ts.total_nanos()) } else { + #[cfg(feature = "defmt")] defmt::info!("No ts"); // THERE IS NO TIMESTAMPS APPLIED TO THE ENTRY!!! } Poll::Ready(Ok(time)) } else { - #[cfg(feature = "defmt")] - defmt::info!("Entry: {} not available", entry); + // #[cfg(feature = "defmt")] + // defmt::info!("Entry: {} not available", entry); Poll::Pending } } From 767916e31cf42a16429496902a6e8caf6019195d Mon Sep 17 00:00:00 2001 From: ZachParallel <134434083+ZachParallel@users.noreply.github.com> Date: Tue, 1 Aug 2023 08:53:40 -0700 Subject: [PATCH 23/77] Change --- src/ethernet/tx/h_descriptor.rs | 8 +++++++- src/ethernet/tx/mod.rs | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/ethernet/tx/h_descriptor.rs b/src/ethernet/tx/h_descriptor.rs index 7d9c239b..a60580ea 100644 --- a/src/ethernet/tx/h_descriptor.rs +++ b/src/ethernet/tx/h_descriptor.rs @@ -240,7 +240,13 @@ impl TxDescriptor { pub(super) fn timestamp(&self) -> Option { let contains_timestamp = (self.inner_raw.read(3) & TXDESC_3_TTSS) == TXDESC_3_TTSS; - + #[cfg(feature = "defmt")] + defmt::info!( + "Is owned: {}, contains ts: {}, is last {}", + self.is_owned(), + contains_timestamp, + self.is_last() + ); 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)) diff --git a/src/ethernet/tx/mod.rs b/src/ethernet/tx/mod.rs index a1bb2dae..5347a4d2 100644 --- a/src/ethernet/tx/mod.rs +++ b/src/ethernet/tx/mod.rs @@ -307,8 +307,8 @@ impl TxRing<'_> { #[cfg(feature = "defmt")] defmt::info!("Timestamp: {}", ts.total_nanos()) } else { - #[cfg(feature = "defmt")] - defmt::info!("No ts"); // THERE IS NO TIMESTAMPS APPLIED TO THE ENTRY!!! + // #[cfg(feature = "defmt")] + // defmt::info!("No ts"); // THERE IS NO TIMESTAMPS APPLIED TO THE ENTRY!!! } Poll::Ready(Ok(time)) } else { From 6f6ff4c1a10dcd48bf95e76b235fe0d429f1a92f Mon Sep 17 00:00:00 2001 From: ZachParallel <134434083+ZachParallel@users.noreply.github.com> Date: Tue, 1 Aug 2023 08:59:38 -0700 Subject: [PATCH 24/77] Change --- src/ethernet/tx/h_descriptor.rs | 16 +++++++++------- src/ethernet/tx/mod.rs | 12 ++++++------ 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/ethernet/tx/h_descriptor.rs b/src/ethernet/tx/h_descriptor.rs index a60580ea..68f5e88e 100644 --- a/src/ethernet/tx/h_descriptor.rs +++ b/src/ethernet/tx/h_descriptor.rs @@ -240,13 +240,15 @@ impl TxDescriptor { pub(super) fn timestamp(&self) -> Option { let contains_timestamp = (self.inner_raw.read(3) & TXDESC_3_TTSS) == TXDESC_3_TTSS; - #[cfg(feature = "defmt")] - defmt::info!( - "Is owned: {}, contains ts: {}, is last {}", - self.is_owned(), - contains_timestamp, - self.is_last() - ); + if contains_timestamp { + #[cfg(feature = "defmt")] + defmt::info!( + "Is owned: {}, contains ts: {}, is last {}", + self.is_owned(), + contains_timestamp, + self.is_last() + ); + } 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)) diff --git a/src/ethernet/tx/mod.rs b/src/ethernet/tx/mod.rs index 5347a4d2..2f36d036 100644 --- a/src/ethernet/tx/mod.rs +++ b/src/ethernet/tx/mod.rs @@ -256,8 +256,8 @@ impl TxRing<'_> { fn entry_for_id(&self, id: &PacketId) -> Option { self.ring.descriptors().enumerate().find_map(|(idx, e)| { if e.has_packet_id(id) { - #[cfg(feature = "defmt")] - defmt::info!("Entry: {} for packet id: {}", idx, id.0); + // #[cfg(feature = "defmt")] + // defmt::info!("Entry: {} for packet id: {}", idx, id.0); Some(idx) } else { // #[cfg(feature = "defmt")] @@ -269,8 +269,8 @@ impl TxRing<'_> { fn entry_timestamp(&self, index: usize) -> Option { let t = self.ring.descriptor(index); - #[cfg(feature = "defmt")] - defmt::info!("Descriptor for index: {}", t.inner_raw.desc); + // #[cfg(feature = "defmt")] + // defmt::info!("Descriptor for index: {}", t.inner_raw.desc); self.ring.descriptor(index).timestamp() } @@ -304,8 +304,8 @@ impl TxRing<'_> { // #[cfg(feature = "defmt")] // defmt::info!("Entry: {} available", entry); if let Some(ts) = time { - #[cfg(feature = "defmt")] - defmt::info!("Timestamp: {}", ts.total_nanos()) + // #[cfg(feature = "defmt")] + // defmt::info!("Timestamp: {}", ts.total_nanos()) } else { // #[cfg(feature = "defmt")] // defmt::info!("No ts"); // THERE IS NO TIMESTAMPS APPLIED TO THE ENTRY!!! From c94dc1a077a52e2b201135cb08723de37116f8b9 Mon Sep 17 00:00:00 2001 From: ZachParallel <134434083+ZachParallel@users.noreply.github.com> Date: Tue, 1 Aug 2023 10:56:16 -0700 Subject: [PATCH 25/77] Removed register setting that did not exist in stm32-eth --- src/ethernet/eth.rs | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/ethernet/eth.rs b/src/ethernet/eth.rs index 57afc5fc..53a46a5b 100644 --- a/src/ethernet/eth.rs +++ b/src/ethernet/eth.rs @@ -695,24 +695,25 @@ pub unsafe fn new_unchecked<'rx, 'tx>( // operation mode register eth_dma.dmamr.modify(|_, w| { - w.intm() - .bits(0b00) //May not need + w + // .intm() + // .bits(0b00) //May not need // Rx Tx priority ratio 2:1 .pr() .variant(0b001) // Modified - .txpr() - .clear_bit() //May not need - .da() - .clear_bit() //May not need + // .txpr() + // .clear_bit() //May not need + // .da() + // .clear_bit() //May not need }); // bus mode register eth_dma.dmasbmr.modify(|_, w| { // Address-aligned beats w.aal() //Match .set_bit() - // Fixed burst - .fb() //May not need - .set_bit() + // Fixed burst + // .fb() //May not need + // .set_bit() }); eth_dma.dmaccr.modify(|_, w| { w.dsl() //Modified @@ -723,8 +724,8 @@ pub unsafe fn new_unchecked<'rx, 'tx>( // Tx DMA PBL .txpbl() //Match .bits(32) - .tse() //May not need - .clear_bit() + // .tse() //May not need + // .clear_bit() // Operate on second frame .osf() //Match .clear_bit() @@ -738,9 +739,9 @@ pub unsafe fn new_unchecked<'rx, 'tx>( // Rx DMA PBL .rxpbl() //Match .bits(32) - // Disable flushing of received frames - .rpf() - .clear_bit() //May not need + // Disable flushing of received frames + // .rpf() + // .clear_bit() //May not need }); // Initialise DMA descriptors @@ -760,8 +761,8 @@ pub unsafe fn new_unchecked<'rx, 'tx>( eth_mtl.mtltx_qomr.modify(|_, w| w.ftq().set_bit()); // Manage DMA transmission and reception - eth_dma.dmactx_cr.modify(|_, w| w.st().set_bit()); //May not need - eth_dma.dmacrx_cr.modify(|_, w| w.sr().set_bit()); //May not need + // eth_dma.dmactx_cr.modify(|_, w| w.st().set_bit()); //May not need + // eth_dma.dmacrx_cr.modify(|_, w| w.sr().set_bit()); //May not need eth_dma .dmacsr From b7b044535de1b9f34529bea092f481e4e1f834c3 Mon Sep 17 00:00:00 2001 From: ZachParallel <134434083+ZachParallel@users.noreply.github.com> Date: Tue, 1 Aug 2023 11:12:36 -0700 Subject: [PATCH 26/77] Change --- src/ethernet/tx/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ethernet/tx/mod.rs b/src/ethernet/tx/mod.rs index 2f36d036..dd1fcdfe 100644 --- a/src/ethernet/tx/mod.rs +++ b/src/ethernet/tx/mod.rs @@ -268,7 +268,7 @@ impl TxRing<'_> { } fn entry_timestamp(&self, index: usize) -> Option { - let t = self.ring.descriptor(index); + // let t = self.ring.descriptor(index); // #[cfg(feature = "defmt")] // defmt::info!("Descriptor for index: {}", t.inner_raw.desc); self.ring.descriptor(index).timestamp() From e49830634c6e8c657637c781adc88f9d159b1670 Mon Sep 17 00:00:00 2001 From: ZachParallel <134434083+ZachParallel@users.noreply.github.com> Date: Tue, 1 Aug 2023 14:05:21 -0700 Subject: [PATCH 27/77] Added PTP instance --- src/ethernet/eth.rs | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/ethernet/eth.rs b/src/ethernet/eth.rs index 53a46a5b..88baf3c6 100644 --- a/src/ethernet/eth.rs +++ b/src/ethernet/eth.rs @@ -429,6 +429,17 @@ pub struct EthernetMAC { clock_range: u8, } +/// Access to all configured parts of the ethernet peripheral. +pub struct Parts<'rx, 'tx> { + /// Access to and control over the ethernet MAC. + pub mac: EthernetMAC, + /// Access to and control over the ethernet DMA. + pub dma: EthernetDMA<'rx, 'tx>, + /// Access to and control over the ethernet PTP module. + #[cfg(feature = "ptp")] + pub ptp: EthernetPTP, +} + /// Create and initialise the ethernet driver. /// /// You must move in ETH_MAC, ETH_MTL, ETH_DMA. @@ -458,7 +469,7 @@ pub fn new<'rx, 'tx>( mac_addr: EthernetAddress, prec: rec::Eth1Mac, clocks: &CoreClocks, -) -> (EthernetDMA<'rx, 'tx>, EthernetMAC) { +) -> (Parts<'rx, 'tx>) { pins.set_speed(Speed::VeryHigh); unsafe { new_unchecked( @@ -499,7 +510,7 @@ pub unsafe fn new_unchecked<'rx, 'tx>( mac_addr: EthernetAddress, prec: rec::Eth1Mac, clocks: &CoreClocks, -) -> (EthernetDMA<'rx, 'tx>, EthernetMAC) { +) -> (Parts<'rx, 'tx>) { // RCC { let rcc = &*stm32::RCC::ptr(); @@ -804,7 +815,16 @@ pub unsafe fn new_unchecked<'rx, 'tx>( dma.rx_ring.start(&dma.eth_dma); dma.tx_ring.start(&dma.eth_dma); - (dma, mac) + // Configure the ethernet PTP + #[cfg(feature = "ptp")] + let ptp = EthernetPTP::new(*clocks, &dma); + + Parts { + mac, + dma, + #[cfg(feature = "ptp")] + ptp, + } } /// A summary of the reasons for the occurence of an From fe78ddc92cd8f21bef9de7295e2de38967be077c Mon Sep 17 00:00:00 2001 From: ZachParallel <134434083+ZachParallel@users.noreply.github.com> Date: Tue, 1 Aug 2023 14:09:44 -0700 Subject: [PATCH 28/77] Added Parts --- src/ethernet/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ethernet/mod.rs b/src/ethernet/mod.rs index 641e7242..7b5339db 100644 --- a/src/ethernet/mod.rs +++ b/src/ethernet/mod.rs @@ -54,7 +54,7 @@ pub(crate) use cache::Cache; mod eth; pub use eth::{eth_interrupt_handler, new, new_unchecked}; -pub use eth::{EthernetDMA, EthernetMAC}; +pub use eth::{EthernetDMA, EthernetMAC, Parts}; /// Marks a set of pins used to communciate to a PHY with a Reduced Media /// Independent Interface (RMII) From 7c9b04c97f3dac2e90466780164105900f08edc6 Mon Sep 17 00:00:00 2001 From: ZachParallel <134434083+ZachParallel@users.noreply.github.com> Date: Tue, 1 Aug 2023 15:42:00 -0700 Subject: [PATCH 29/77] Working timestamping hal --- src/ethernet/eth.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ethernet/eth.rs b/src/ethernet/eth.rs index 88baf3c6..e79a2a73 100644 --- a/src/ethernet/eth.rs +++ b/src/ethernet/eth.rs @@ -479,6 +479,8 @@ pub fn new<'rx, 'tx>( } } +//TODO remove EthernetPTP but keep the timestamping functionality + /// Create and initialise the ethernet driver. /// /// You must move in ETH_MAC, ETH_MTL, ETH_DMA. From 7e3d2057b3aba4b3259f32a541314d3a73a5a8bd Mon Sep 17 00:00:00 2001 From: ZachParallel <134434083+ZachParallel@users.noreply.github.com> Date: Wed, 2 Aug 2023 08:58:07 -0700 Subject: [PATCH 30/77] Removed uncessary defmt --- src/ethernet/tx/h_descriptor.rs | 9 --------- src/ethernet/tx/mod.rs | 18 ------------------ 2 files changed, 27 deletions(-) diff --git a/src/ethernet/tx/h_descriptor.rs b/src/ethernet/tx/h_descriptor.rs index 68f5e88e..86839222 100644 --- a/src/ethernet/tx/h_descriptor.rs +++ b/src/ethernet/tx/h_descriptor.rs @@ -240,15 +240,6 @@ impl TxDescriptor { pub(super) fn timestamp(&self) -> Option { let contains_timestamp = (self.inner_raw.read(3) & TXDESC_3_TTSS) == TXDESC_3_TTSS; - if contains_timestamp { - #[cfg(feature = "defmt")] - defmt::info!( - "Is owned: {}, contains ts: {}, is last {}", - self.is_owned(), - contains_timestamp, - self.is_last() - ); - } 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)) diff --git a/src/ethernet/tx/mod.rs b/src/ethernet/tx/mod.rs index dd1fcdfe..0de2228a 100644 --- a/src/ethernet/tx/mod.rs +++ b/src/ethernet/tx/mod.rs @@ -256,21 +256,14 @@ impl TxRing<'_> { fn entry_for_id(&self, id: &PacketId) -> Option { self.ring.descriptors().enumerate().find_map(|(idx, e)| { if e.has_packet_id(id) { - // #[cfg(feature = "defmt")] - // defmt::info!("Entry: {} for packet id: {}", idx, id.0); Some(idx) } else { - // #[cfg(feature = "defmt")] - // defmt::info!("No entry for packet id: {}", id.0); None } }) } fn entry_timestamp(&self, index: usize) -> Option { - // let t = self.ring.descriptor(index); - // #[cfg(feature = "defmt")] - // defmt::info!("Descriptor for index: {}", t.inner_raw.desc); self.ring.descriptor(index).timestamp() } @@ -301,19 +294,8 @@ impl TxRing<'_> { if self.entry_available(entry) { let time = self.entry_timestamp(entry); - // #[cfg(feature = "defmt")] - // defmt::info!("Entry: {} available", entry); - if let Some(ts) = time { - // #[cfg(feature = "defmt")] - // defmt::info!("Timestamp: {}", ts.total_nanos()) - } else { - // #[cfg(feature = "defmt")] - // defmt::info!("No ts"); // THERE IS NO TIMESTAMPS APPLIED TO THE ENTRY!!! - } Poll::Ready(Ok(time)) } else { - // #[cfg(feature = "defmt")] - // defmt::info!("Entry: {} not available", entry); Poll::Pending } } From 9d17c470414bc43ea4285a0446b9a02e6375870e Mon Sep 17 00:00:00 2001 From: ZachParallel <134434083+ZachParallel@users.noreply.github.com> Date: Mon, 7 Aug 2023 11:18:40 -0700 Subject: [PATCH 31/77] Converted totoal nanos to i128 --- src/ptp/timestamp.rs | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/ptp/timestamp.rs b/src/ptp/timestamp.rs index db378639..70d92879 100644 --- a/src/ptp/timestamp.rs +++ b/src/ptp/timestamp.rs @@ -21,7 +21,11 @@ impl Timestamp { const SIGN_BIT: u32 = 0x8000_0000; /// Create a new [`Timestamp`] - pub const fn new(negative: bool, seconds: u32, subseconds: Subseconds) -> Self { + pub const fn new( + negative: bool, + seconds: u32, + subseconds: Subseconds, + ) -> Self { Self::new_unchecked(negative, seconds, subseconds.raw()) } @@ -45,7 +49,11 @@ impl Timestamp { !self.is_negative() } - pub(crate) const fn new_unchecked(negative: bool, seconds: u32, subseconds: u32) -> Self { + pub(crate) const fn new_unchecked( + negative: bool, + seconds: u32, + subseconds: u32, + ) -> Self { let seconds: i64 = (seconds as i64) << 31; let subseconds: i64 = subseconds as i64; @@ -99,8 +107,9 @@ impl Timestamp { /// let timestamp_neg = Timestamp::new(true, 500, Subseconds::new_from_nanos(500_000).unwrap()); /// assert_eq!(timestamp_neg.total_nanos(), -1 * (500 * 1_000_000_000 + 500_000)); /// ``` - pub const fn total_nanos(&self) -> i64 { - let nanos = self.seconds() as i64 * NANOS_PER_SECOND as i64 + self.nanos() as i64; + pub const fn total_nanos(&self) -> i128 { + let nanos = self.seconds() as i128 * NANOS_PER_SECOND as i128 + + self.nanos() as i128; if self.is_positive() { nanos @@ -164,7 +173,8 @@ mod test { let three = Timestamp::new(false, 3, subs(3)); let one_neg = Timestamp::new(true, 1, subs(1)); - let one_big_neg = Timestamp::new(true, 1, subs(SUBSECONDS_PER_SECOND - 1)); + let one_big_neg = + Timestamp::new(true, 1, subs(SUBSECONDS_PER_SECOND - 1)); let two_neg = Timestamp::new(true, 2, subs(2)); let three_neg = Timestamp::new(true, 3, subs(3)); @@ -206,7 +216,8 @@ mod test { let three_neg = Timestamp::new(true, 3, subs(3)); let one_minus_two = Timestamp::new(true, 1, subs(1)); - let one_minus_one_big = Timestamp::new(true, 0, subs(SUBSECONDS_PER_SECOND - 2)); + let one_minus_one_big = + Timestamp::new(true, 0, subs(SUBSECONDS_PER_SECOND - 2)); assert_eq!(one - one_big, one_minus_one_big); From b1ca3a4d51d9ef0a7f08e8f33b65bde8df6e0532 Mon Sep 17 00:00:00 2001 From: ZachParallel <134434083+ZachParallel@users.noreply.github.com> Date: Mon, 7 Aug 2023 11:20:48 -0700 Subject: [PATCH 32/77] Converted totoal nanos to u64 --- src/ptp/timestamp.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ptp/timestamp.rs b/src/ptp/timestamp.rs index 70d92879..7d484c63 100644 --- a/src/ptp/timestamp.rs +++ b/src/ptp/timestamp.rs @@ -107,9 +107,9 @@ impl Timestamp { /// let timestamp_neg = Timestamp::new(true, 500, Subseconds::new_from_nanos(500_000).unwrap()); /// assert_eq!(timestamp_neg.total_nanos(), -1 * (500 * 1_000_000_000 + 500_000)); /// ``` - pub const fn total_nanos(&self) -> i128 { - let nanos = self.seconds() as i128 * NANOS_PER_SECOND as i128 - + self.nanos() as i128; + pub const fn total_nanos(&self) -> u64 { + let nanos = self.seconds() as u64 * NANOS_PER_SECOND as u64 + + self.nanos() as u64; if self.is_positive() { nanos From a7d9adb9855da04279d14dd0461db4e4b158d710 Mon Sep 17 00:00:00 2001 From: ZachParallel <134434083+ZachParallel@users.noreply.github.com> Date: Mon, 7 Aug 2023 11:36:19 -0700 Subject: [PATCH 33/77] Error corrected total_nanos --- src/ptp/timestamp.rs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/ptp/timestamp.rs b/src/ptp/timestamp.rs index 7d484c63..92716377 100644 --- a/src/ptp/timestamp.rs +++ b/src/ptp/timestamp.rs @@ -107,15 +107,8 @@ impl Timestamp { /// let timestamp_neg = Timestamp::new(true, 500, Subseconds::new_from_nanos(500_000).unwrap()); /// assert_eq!(timestamp_neg.total_nanos(), -1 * (500 * 1_000_000_000 + 500_000)); /// ``` - pub const fn total_nanos(&self) -> u64 { - let nanos = self.seconds() as u64 * NANOS_PER_SECOND as u64 - + self.nanos() as u64; - - if self.is_positive() { - nanos - } else { - -nanos - } + pub const fn total_abs_nanos(&self) -> u64 { + self.seconds() as u64 * NANOS_PER_SECOND as u64 + self.nanos() as u64 } /// Create a new timestamp from the provided register values. From 82eda19eaaa15969f75b47b8163196a41f4029d7 Mon Sep 17 00:00:00 2001 From: ZachParallel <134434083+ZachParallel@users.noreply.github.com> Date: Mon, 7 Aug 2023 13:34:30 -0700 Subject: [PATCH 34/77] Added safe add functionality to timestamp --- src/ptp/timestamp.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ptp/timestamp.rs b/src/ptp/timestamp.rs index 92716377..1c7f0a19 100644 --- a/src/ptp/timestamp.rs +++ b/src/ptp/timestamp.rs @@ -124,7 +124,7 @@ impl core::ops::Add for Timestamp { type Output = Self; fn add(self, rhs: Timestamp) -> Self::Output { - Self(self.0 + rhs.0) + Self(self.0.saturating_add(rhs.0)) } } @@ -138,7 +138,7 @@ impl core::ops::Sub for Timestamp { type Output = Self; fn sub(self, rhs: Timestamp) -> Self::Output { - Self(self.0 - rhs.0) + Self(self.0.saturating_sub(rhs.0)) } } From a51e08355ea15b5e5508c4f1c7b6c1d94af3469a Mon Sep 17 00:00:00 2001 From: ZachParallel <134434083+ZachParallel@users.noreply.github.com> Date: Mon, 7 Aug 2023 14:57:04 -0700 Subject: [PATCH 35/77] Changed how time was set --- src/ptp/mod.rs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/ptp/mod.rs b/src/ptp/mod.rs index f01fe64a..70a0a514 100644 --- a/src/ptp/mod.rs +++ b/src/ptp/mod.rs @@ -115,7 +115,7 @@ impl EthernetPTP { }; me.set_addend(tsa); - me.set_time(Timestamp::new_unchecked(false, 0, 0)); + me.set_time(0, 0); me } @@ -154,13 +154,7 @@ impl EthernetPTP { } /// Set the current time. - pub fn set_time(&mut self, time: Timestamp) { - 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(); - + pub fn set_time(&mut self, seconds: u32, nanoseconds: u32) { { // SAFETY: we only write to `mactscr` (timestamp control register), `macstsur` // (timestamp update seconds register) and `macstnur` (timestmap update subsecond/nanosecond @@ -171,7 +165,7 @@ impl EthernetPTP { }; macstsur.write(|w| unsafe { w.bits(seconds) }); - macstnur.write(|w| unsafe { w.bits(subseconds) }); + macstnur.write(|w| unsafe { w.bits(nanoseconds) }); while mactscr.read().tsinit().bit_is_set() {} mactscr.modify(|_, w| w.tsinit().set_bit()); From 25f98781054df9d80366f7d8a45575e26e502f85 Mon Sep 17 00:00:00 2001 From: ZachParallel <134434083+ZachParallel@users.noreply.github.com> Date: Fri, 18 Aug 2023 14:48:36 -0700 Subject: [PATCH 36/77] Test --- src/ethernet/cache.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ethernet/cache.rs b/src/ethernet/cache.rs index e05da507..4ad92b35 100644 --- a/src/ethernet/cache.rs +++ b/src/ethernet/cache.rs @@ -4,7 +4,7 @@ use crate::ptp::Timestamp; use super::PacketId; /// A cache for timestamping information, -/// used to keep the size of Descriptors down. +/// used to lessen the size of Descriptors. #[derive(Clone, Copy)] #[repr(C, packed)] pub struct Cache { From 8a7b44c102afd348781a07768a73f6893a14491c Mon Sep 17 00:00:00 2001 From: ZachParallel <134434083+ZachParallel@users.noreply.github.com> Date: Mon, 21 Aug 2023 15:42:23 -0700 Subject: [PATCH 37/77] Removed poor comments and unused imports as well as added the async feature --- Cargo.toml | 6 +- src/ethernet/eth.rs | 529 +----------------- .../rx/{h_descriptor.rs => descriptor.rs} | 0 src/ethernet/rx/mod.rs | 3 +- .../tx/{h_descriptor.rs => descriptor.rs} | 0 src/ethernet/tx/mod.rs | 5 +- 6 files changed, 26 insertions(+), 517 deletions(-) rename src/ethernet/rx/{h_descriptor.rs => descriptor.rs} (100%) rename src/ethernet/tx/{h_descriptor.rs => descriptor.rs} (100%) diff --git a/Cargo.toml b/Cargo.toml index 7c19f8c0..ce2e2f54 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -69,6 +69,7 @@ log = { version = "0.4.14", optional = true } # see also the dev-dependencies se fdcan = { version = "0.2", optional = true } bitflags = { version = "2.0.0" } embedded-storage = "0.3" +futures = { version = "0.3", default-features = false, features = ["async-await"], optional = true } [dependencies.smoltcp] version = "0.10.0" @@ -115,7 +116,9 @@ default-features = false features = ["medium-ethernet", "proto-ipv4", "proto-ipv6", "socket-raw"] [features] -default = ["rt"] +default = ["rt", "stm32h743v", + "ethernet", + "device-selected", "ptp", "async-await"] device-selected = [] revision_v = [] rm0433 = ["gpio-h747"] # aka. "single core" devices @@ -127,6 +130,7 @@ gpio-h72 = [] gpio-h747 = [] gpio-h7a2 = [] +async-await = ["dep:futures"] ptp = ["smoltcp/packetmeta-id"] dsi = [] cm4 = [] diff --git a/src/ethernet/eth.rs b/src/ethernet/eth.rs index e79a2a73..22e00949 100644 --- a/src/ethernet/eth.rs +++ b/src/ethernet/eth.rs @@ -28,11 +28,12 @@ use crate::ptp::{EthernetPTP, Timestamp}; use crate::rcc::{rec, CoreClocks, ResetEnable}; use crate::stm32; use crate::stm32::{Interrupt, ETHERNET_DMA, ETHERNET_MTL, NVIC}; +use futures::task::AtomicWaker; use smoltcp::{ self, phy::{ - self, ChecksumCapabilities, Device, DeviceCapabilities, PacketMeta, + self, ChecksumCapabilities, DeviceCapabilities, PacketMeta, RxToken, TxToken, }, time::Instant, @@ -43,14 +44,14 @@ use super::rx::{ self, RxDescriptor, RxDescriptorRing, RxError, RxPacket, RxRing, }; use super::tx::{ - self, RunningState, TxDescriptor, TxDescriptorRing, TxError, TxRing, + self, TxDescriptor, TxDescriptorRing, TxPacket, TxError, TxRing, }; use super::packet_id::PacketId; -use crate::ethernet::raw_descriptor::DESC_SIZE; +use crate::ethernet::raw_descriptor; use crate::{ - ethernet::{PinsRMII, StationManagement, MTU}, + ethernet::{self, PinsRMII, StationManagement}, gpio::Speed, }; @@ -66,7 +67,7 @@ const _ASSERT_DESCRIPTOR_SIZES: () = assert!(_RXDESC_SIZE == _TXDESC_SIZE); const _ASSERT_DESCRIPTOR_ALIGN: () = assert!(_RXDESC_SIZE % 4 == 0); const DESC_WORD_SKIP: u8 = - ((_RXDESC_SIZE / 4) - super::raw_descriptor::DESC_SIZE) as u8; + ((_RXDESC_SIZE / 4) - raw_descriptor::DESC_SIZE) as u8; const _ASSERT_DESC_WORD_SKIP_SIZE: () = assert!(DESC_WORD_SKIP <= 0b111); @@ -91,324 +92,12 @@ mod emac_consts { } use self::emac_consts::*; -// /// Transmit Descriptor representation -// /// -// /// * tdes0: transmit buffer address -// /// * tdes1: -// /// * tdes2: buffer lengths -// /// * tdes3: control and payload/frame length -// /// -// /// Note that Copy and Clone are derived to support initialising an -// /// array of TDes, but you may not move a TDes after its address has -// /// been given to the ETH_DMA engine. -// #[derive(Copy, Clone)] -// #[repr(C, packed)] -// struct TDes { -// tdes0: u32, -// tdes1: u32, -// tdes2: u32, -// tdes3: u32, -// } - -// impl TDes { -// /// Initialises this TDes to point at the given buffer. -// pub fn init(&mut self) { -// self.tdes0 = 0; -// self.tdes1 = 0; -// self.tdes2 = 0; -// self.tdes3 = 0; // Owned by us -// } - -// /// Return true if this TDes is not currently owned by the DMA -// pub fn available(&self) -> bool { -// self.tdes3 & EMAC_DES3_OWN == 0 -// } -// } - -// /// Store a ring of TDes and associated buffers -// #[repr(C, packed)] -// struct TDesRing { -// td: [TDes; TD], -// tbuf: [[u32; ETH_BUF_SIZE / 4]; TD], -// tdidx: usize, -// } - -// impl TDesRing { -// const fn new() -> Self { -// Self { -// td: [TDes { -// tdes0: 0, -// tdes1: 0, -// tdes2: 0, -// tdes3: 0, -// }; TD], -// tbuf: [[0; ETH_BUF_SIZE / 4]; TD], -// tdidx: 0, -// } -// } - -// /// Initialise this TDesRing. Assume TDesRing is corrupt -// /// -// /// The current memory address of the buffers inside this TDesRing -// /// will be stored in the descriptors, so ensure the TDesRing is -// /// not moved after initialisation. -// pub fn init(&mut self) { -// for x in 0..TD { -// self.td[x].init(); -// } -// self.tdidx = 0; - -// // Initialise pointers in the DMA engine. (There will be a memory barrier later -// // before the DMA engine is enabled.) -// unsafe { -// let dma = &*stm32::ETHERNET_DMA::ptr(); -// dma.dmactx_dlar -// .write(|w| w.bits(&self.td[0] as *const _ as u32)); -// dma.dmactx_rlr.write(|w| w.tdrl().bits(TD as u16 - 1)); -// dma.dmactx_dtpr -// .write(|w| w.bits(&self.td[0] as *const _ as u32)); -// } -// } - -// /// Return true if a TDes is available for use -// pub fn available(&self) -> bool { -// self.td[self.tdidx].available() -// } - -// /// Release the next TDes to the DMA engine for transmission -// pub fn release(&mut self) { -// let x = self.tdidx; -// assert!(self.td[x].tdes3 & EMAC_DES3_OWN == 0); // Owned by us - -// let address = ptr::addr_of!(self.tbuf[x]) as u32; - -// // Read format -// self.td[x].tdes0 = address; // Buffer 1 -// self.td[x].tdes1 = 0; // Not used -// assert!(self.td[x].tdes2 & !EMAC_TDES2_B1L == 0); // Not used -// assert!(self.td[x].tdes2 & EMAC_TDES2_B1L > 0); // Length must be valid -// self.td[x].tdes3 = 0; -// self.td[x].tdes3 |= EMAC_DES3_FD; // FD: Contains first buffer of packet -// self.td[x].tdes3 |= EMAC_DES3_LD; // LD: Contains last buffer of packet -// self.td[x].tdes3 |= EMAC_DES3_OWN; // Give the DMA engine ownership - -// // Ensure changes to the descriptor are committed before -// // DMA engine sees tail pointer store -// cortex_m::asm::dsb(); - -// // Move the tail pointer (TPR) to the next descriptor -// let x = (x + 1) % TD; -// unsafe { -// let dma = &*stm32::ETHERNET_DMA::ptr(); -// dma.dmactx_dtpr -// .write(|w| w.bits(&(self.td[x]) as *const _ as u32)); -// } - -// self.tdidx = x; -// } - -// /// Access the buffer pointed to by the next TDes -// pub unsafe fn buf_as_slice_mut(&mut self, length: usize) -> &mut [u8] { -// let x = self.tdidx; - -// // Set address in descriptor -// self.td[x].tdes0 = ptr::addr_of!(self.tbuf[x]) as u32; // Buffer 1 - -// // Set length in descriptor -// let len = core::cmp::min(length, ETH_BUF_SIZE); -// self.td[x].tdes2 = (length as u32) & EMAC_TDES2_B1L; - -// // Create a raw pointer in place without an intermediate reference. Use -// // this to return a slice from the packed buffer -// let addr = ptr::addr_of_mut!(self.tbuf[x]) as *mut _; -// core::slice::from_raw_parts_mut(addr, len) -// } -// } - -// /// Receive Descriptor representation -// /// -// /// * rdes0: recieve buffer address -// /// * rdes1: -// /// * rdes2: -// /// * rdes3: OWN and Status -// /// -// /// Note that Copy and Clone are derived to support initialising an -// /// array of RDes, but you may not move a RDes after its address has -// /// been given to the ETH_DMA engine. -// #[derive(Copy, Clone)] -// #[repr(C, packed)] -// struct RDes { -// rdes0: u32, -// rdes1: u32, -// rdes2: u32, -// rdes3: u32, -// } - -// impl RDes { -// /// Initialises RDes -// pub fn init(&mut self) { -// self.rdes0 = 0; -// self.rdes1 = 0; -// self.rdes2 = 0; -// self.rdes3 = 0; // Owned by us -// } - -// /// Return true if this RDes is acceptable to us -// pub fn valid(&self) -> bool { -// // Write-back descriptor is valid if: -// // -// // Contains first buffer of packet AND contains last buf of -// // packet AND no errors AND not a contex descriptor -// self.rdes3 -// & (EMAC_DES3_FD | EMAC_DES3_LD | EMAC_DES3_ES | EMAC_DES3_CTXT) -// == (EMAC_DES3_FD | EMAC_DES3_LD) -// } - -// /// Return true if this RDes is not currently owned by the DMA -// pub fn available(&self) -> bool { -// self.rdes3 & EMAC_DES3_OWN == 0 // Owned by us -// } -// } - -// /// Store a ring of RDes and associated buffers -// #[repr(C, packed)] -// struct RDesRing { -// rd: [RDes; RD], -// rbuf: [[u32; ETH_BUF_SIZE / 4]; RD], -// rdidx: usize, -// } - -// impl RDesRing { -// const fn new() -> Self { -// Self { -// rd: [RDes { -// rdes0: 0, -// rdes1: 0, -// rdes2: 0, -// rdes3: 0, -// }; RD], -// rbuf: [[0; ETH_BUF_SIZE / 4]; RD], -// rdidx: 0, -// } -// } - -// /// Initialise this RDesRing. Assume RDesRing is corrupt -// /// -// /// The current memory address of the buffers inside this RDesRing -// /// will be stored in the descriptors, so ensure the RDesRing is -// /// not moved after initialisation. -// pub fn init(&mut self) { -// for x in 0..RD { -// self.rd[x].init(); -// } -// self.rdidx = 0; - -// // Initialise pointers in the DMA engine -// unsafe { -// let dma = &*stm32::ETHERNET_DMA::ptr(); -// dma.dmacrx_dlar -// .write(|w| w.bits(&self.rd[0] as *const _ as u32)); -// dma.dmacrx_rlr.write(|w| w.rdrl().bits(RD as u16 - 1)); -// } - -// // Release descriptors to the DMA engine -// while self.available() { -// self.release() -// } -// } - -// /// Return true if a RDes is available for use -// pub fn available(&self) -> bool { -// self.rd[self.rdidx].available() -// } - -// /// Return true if current RDes is valid -// pub fn valid(&self) -> bool { -// self.rd[self.rdidx].valid() -// } - -// /// Release the next RDes to the DMA engine -// pub fn release(&mut self) { -// let x = self.rdidx; -// assert!(self.rd[x].rdes3 & EMAC_DES3_OWN == 0); // Owned by us - -// let address = ptr::addr_of!(self.rbuf[x]) as u32; - -// // Read format -// self.rd[x].rdes0 = address; // Buffer 1 -// self.rd[x].rdes1 = 0; // Reserved -// self.rd[x].rdes2 = 0; // Marked as invalid -// self.rd[x].rdes3 = 0; -// self.rd[x].rdes3 |= EMAC_DES3_OWN; // Give the DMA engine ownership -// self.rd[x].rdes3 |= EMAC_RDES3_BUF1V; // BUF1V: 1st buffer address is valid -// self.rd[x].rdes3 |= EMAC_RDES3_IOC; // IOC: Interrupt on complete - -// // Ensure changes to the descriptor are committed before -// // DMA engine sees tail pointer store -// cortex_m::asm::dsb(); - -// // Move the tail pointer (TPR) to this descriptor -// unsafe { -// let dma = &*stm32::ETHERNET_DMA::ptr(); -// dma.dmacrx_dtpr -// .write(|w| w.bits(&(self.rd[x]) as *const _ as u32)); -// } - -// // Update active descriptor -// self.rdidx = (x + 1) % RD; -// } - -// /// Access the buffer pointed to by the next RDes -// /// -// /// # Safety -// /// -// /// Ensure that release() is called between subsequent calls to this -// /// function. -// #[allow(clippy::mut_from_ref)] -// pub unsafe fn buf_as_slice_mut(&self) -> &mut [u8] { -// let x = self.rdidx; - -// // Write-back format -// let addr = ptr::addr_of!(self.rbuf[x]) as *mut u8; -// let len = (self.rd[x].rdes3 & EMAC_RDES3_PL) as usize; - -// let len = core::cmp::min(len, ETH_BUF_SIZE); -// core::slice::from_raw_parts_mut(addr, len) -// } -// } - -//TODO Check this -// pub struct DesRing { -// tx: TDesRing, -// rx: RDesRing, -// } -// impl DesRing { -// pub const fn new() -> Self { -// DesRing { -// tx: TDesRing::new(), -// rx: RDesRing::new(), -// } -// } -// } -// impl Default for DesRing { -// fn default() -> Self { -// Self::new() -// } -// } - #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[derive(Clone, Copy, Debug, PartialEq)] /// This struct is returned if a packet ID is not associated /// with any TX or RX descriptors. pub struct PacketIdNotFound; -/// -/// Ethernet DMA -/// -// pub struct EthernetDMA { -// ring: &'static mut DesRing, -// eth_dma: stm32::ETHERNET_DMA, -// } /// Ethernet DMA. pub struct EthernetDMA<'rx, 'tx> { eth_dma: ETHERNET_DMA, @@ -420,9 +109,7 @@ pub struct EthernetDMA<'rx, 'tx> { packet_id_counter: u32, } -/// /// Ethernet MAC -/// pub struct EthernetMAC { eth_mac: stm32::ETHERNET_MAC, eth_phy_addr: u8, @@ -479,8 +166,6 @@ pub fn new<'rx, 'tx>( } } -//TODO remove EthernetPTP but keep the timestamping functionality - /// Create and initialise the ethernet driver. /// /// You must move in ETH_MAC, ETH_MTL, ETH_DMA. @@ -512,7 +197,7 @@ pub unsafe fn new_unchecked<'rx, 'tx>( mac_addr: EthernetAddress, prec: rec::Eth1Mac, clocks: &CoreClocks, -) -> (Parts<'rx, 'tx>) { +) -> Parts<'rx, 'tx> { // RCC { let rcc = &*stm32::RCC::ptr(); @@ -533,14 +218,10 @@ pub unsafe fn new_unchecked<'rx, 'tx>( syscfg.pmcr.modify(|_, w| w.epis().bits(0b100)); // RMII } - // reset ETH_MAC - write 1 then 0 - //rcc.ahb1rstr.modify(|_, w| w.eth1macrst().set_bit()); - //rcc.ahb1rstr.modify(|_, w| w.eth1macrst().clear_bit()); - cortex_m::interrupt::free(|_cs| { // reset ETH_DMA - write 1 and wait for 0 - eth_dma.dmamr.modify(|_, w| w.swr().set_bit()); //Match - while eth_dma.dmamr.read().swr().bit_is_set() {} //Match + eth_dma.dmamr.modify(|_, w| w.swr().set_bit()); + while eth_dma.dmamr.read().swr().bit_is_set() {} // 200 MHz eth_mac @@ -709,58 +390,40 @@ pub unsafe fn new_unchecked<'rx, 'tx>( // operation mode register eth_dma.dmamr.modify(|_, w| { w - // .intm() - // .bits(0b00) //May not need // Rx Tx priority ratio 2:1 .pr() - .variant(0b001) // Modified - // .txpr() - // .clear_bit() //May not need - // .da() - // .clear_bit() //May not need + .variant(0b001) }); // bus mode register eth_dma.dmasbmr.modify(|_, w| { // Address-aligned beats - w.aal() //Match + w.aal() .set_bit() - // Fixed burst - // .fb() //May not need - // .set_bit() }); eth_dma.dmaccr.modify(|_, w| { - w.dsl() //Modified + w.dsl() .variant(DESC_WORD_SKIP) }); eth_dma.dmactx_cr.modify(|_, w| { w // Tx DMA PBL - .txpbl() //Match + .txpbl() .bits(32) - // .tse() //May not need - // .clear_bit() // Operate on second frame - .osf() //Match + .osf() .clear_bit() }); eth_dma.dmacrx_cr.modify(|_, w| { w // receive buffer size - .rbsz() //Modified + .rbsz() .variant(rx_buffer.first_buffer().len() as u16) // Rx DMA PBL - .rxpbl() //Match + .rxpbl() .bits(32) - // Disable flushing of received frames - // .rpf() - // .clear_bit() //May not need }); - // Initialise DMA descriptors - // ring.tx.init(); - // ring.rx.init(); - // Ensure the DMA descriptors are committed cortex_m::asm::dsb(); @@ -773,10 +436,6 @@ pub unsafe fn new_unchecked<'rx, 'tx>( }); eth_mtl.mtltx_qomr.modify(|_, w| w.ftq().set_bit()); - // Manage DMA transmission and reception - // eth_dma.dmactx_cr.modify(|_, w| w.st().set_bit()); //May not need - // eth_dma.dmacrx_cr.modify(|_, w| w.sr().set_bit()); //May not need - eth_dma .dmacsr .modify(|_, w| w.tps().set_bit().rps().set_bit()); //These bits are set if transmission is stopped. @@ -920,36 +579,6 @@ impl StationManagement for EthernetMAC { } } -/// Define TxToken type and implement consume method -// pub struct TxToken<'a, const TD: usize>(&'a mut TDesRing); - -// impl<'a, const TD: usize> phy::TxToken for TxToken<'a, TD> { -// fn consume(self, len: usize, f: F) -> R -// where -// F: FnOnce(&mut [u8]) -> R, -// { -// assert!(len <= ETH_BUF_SIZE); - -// let result = f(unsafe { self.0.buf_as_slice_mut(len) }); -// self.0.release(); -// result -// } -// } - -// /// Define RxToken type and implement consume method -// pub struct RxToken<'a, const RD: usize>(&'a mut RDesRing); - -// impl<'a, const RD: usize> phy::RxToken for RxToken<'a, RD> { -// fn consume(self, f: F) -> R -// where -// F: FnOnce(&mut [u8]) -> R, -// { -// let result = f(unsafe { self.0.buf_as_slice_mut() }); -// self.0.release(); -// result -// } -// } - /// An Ethernet RX token that can be consumed in order to receive /// an ethernet packet. pub struct EthRxToken<'a, 'rx> { @@ -1009,52 +638,11 @@ impl<'dma, 'tx> TxToken for EthTxToken<'dma, 'tx> { } #[cfg(feature = "ptp")] - fn set_meta(&mut self, meta: smoltcp::phy::PacketMeta) { + fn set_meta(&mut self, meta: PacketMeta) { self.meta = Some(meta.into()); } } -/// Implement the smoltcp Device interface -// impl phy::Device for EthernetDMA { -// type RxToken<'a> = RxToken<'a, RD>; -// type TxToken<'a> = TxToken<'a, TD>; - -// // Clippy false positive because DeviceCapabilities is non-exhaustive -// #[allow(clippy::field_reassign_with_default)] -// fn capabilities(&self) -> DeviceCapabilities { -// let mut caps = DeviceCapabilities::default(); -// // ethernet frame type II (6 smac, 6 dmac, 2 ethertype), -// // sans CRC (4), 1500 IP MTU -// caps.max_transmission_unit = 1514; -// caps.max_burst_size = Some(core::cmp::min(TD, RD)); -// caps -// } - -// fn receive( -// &mut self, -// _timestamp: Instant, -// ) -> Option<(RxToken, TxToken)> { -// // Skip all queued packets with errors. -// while self.ring.rx.available() && !self.ring.rx.valid() { -// self.ring.rx.release() -// } - -// if self.ring.rx.available() && self.ring.tx.available() { -// Some((RxToken(&mut self.ring.rx), TxToken(&mut self.ring.tx))) -// } else { -// None -// } -// } - -// fn transmit(&mut self, _timestamp: Instant) -> Option> { -// if self.ring.tx.available() { -// Some(TxToken(&mut self.ring.tx)) -// } else { -// None -// } -// } -// } - /// Use this Ethernet driver with [smoltcp](https://github.com/smoltcp-rs/smoltcp) impl<'a, 'rx, 'tx> phy::Device for &'a mut EthernetDMA<'rx, 'tx> { type RxToken<'token> = EthRxToken<'token, 'rx> where Self: 'token; @@ -1062,7 +650,7 @@ impl<'a, 'rx, 'tx> phy::Device for &'a mut EthernetDMA<'rx, 'tx> { fn capabilities(&self) -> DeviceCapabilities { let mut caps = DeviceCapabilities::default(); - caps.max_transmission_unit = crate::ethernet::MTU; + caps.max_transmission_unit = ethernet::MTU; caps.max_burst_size = Some(1); caps.checksum = ChecksumCapabilities::ignored(); caps @@ -1435,84 +1023,3 @@ pub struct InterruptReasonSummary { pub is_error: bool, } -// /// Clears the Ethernet interrupt flag -// /// -// /// # Safety -// /// -// /// This method implements a single register write to DMACSR -// pub fn eth_interrupt_handler_impl( -// eth_dma: ÐERNET_DMA, -// ) -> InterruptReasonSummary { -// 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() -// .rbu() -// .set_bit() -// .tbu() -// .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 -// } - -// / Enables the Ethernet Interrupt. The following interrupts are enabled: -// / -// / * Normal Interrupt `NIE` -// / * Receive Interrupt `RIE` -// / * Transmit Interript `TIE` -// / -// / # Safety -// / -// / This method implements a single RMW to DMACIER -// pub unsafe fn enable_interrupt() { -// let eth_dma = &*stm32::ETHERNET_DMA::ptr(); -// 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() -// // Abnormal Interrupt Summary enable -// .aie() -// .set_bit() -// // Receive Buffer Unavailable -// .rbue() -// .set_bit() -// // Transmit Buffer Unavailable -// .tbue() -// .set_bit() -// }); -// // Enable ethernet interrupts -// unsafe { -// NVIC::unmask(Interrupt::ETH); -// } -// } diff --git a/src/ethernet/rx/h_descriptor.rs b/src/ethernet/rx/descriptor.rs similarity index 100% rename from src/ethernet/rx/h_descriptor.rs rename to src/ethernet/rx/descriptor.rs diff --git a/src/ethernet/rx/mod.rs b/src/ethernet/rx/mod.rs index a5d9719f..e753a1a5 100644 --- a/src/ethernet/rx/mod.rs +++ b/src/ethernet/rx/mod.rs @@ -3,7 +3,6 @@ pub use descriptor::RxDescriptor; use super::{raw_descriptor::DescriptorRing, PacketId}; use crate::stm32::ETHERNET_DMA; -#[path = "./h_descriptor.rs"] mod descriptor; #[cfg(feature = "ptp")] @@ -275,7 +274,7 @@ impl<'a> RxRing<'a> { match res { Ok(value) => Poll::Ready(value), Err(_) => { - crate::dma::EthernetDMA::rx_waker().register(ctx.waker()); + super::EthernetDMA::rx_waker().register(ctx.waker()); Poll::Pending } } diff --git a/src/ethernet/tx/h_descriptor.rs b/src/ethernet/tx/descriptor.rs similarity index 100% rename from src/ethernet/tx/h_descriptor.rs rename to src/ethernet/tx/descriptor.rs diff --git a/src/ethernet/tx/mod.rs b/src/ethernet/tx/mod.rs index 0de2228a..4b207587 100644 --- a/src/ethernet/tx/mod.rs +++ b/src/ethernet/tx/mod.rs @@ -5,7 +5,6 @@ use crate::stm32::ETHERNET_DMA; use super::eth::PacketIdNotFound; use crate::ptp::Timestamp; -#[path = "./h_descriptor.rs"] mod descriptor; pub use descriptor::TxDescriptor; @@ -170,7 +169,7 @@ impl<'ring> TxRing<'ring> { let entry = core::future::poll_fn(|ctx| match self.send_next_impl() { Ok(packet) => Poll::Ready(packet), Err(_) => { - crate::dma::EthernetDMA::tx_waker().register(ctx.waker()); + super::EthernetDMA::tx_waker().register(ctx.waker()); Poll::Pending } }) @@ -309,7 +308,7 @@ impl TxRing<'_> { core::future::poll_fn(move |ctx| { let res = self.poll_timestamp(packet_id); if res.is_pending() { - crate::dma::EthernetDMA::tx_waker().register(ctx.waker()); + super::EthernetDMA::tx_waker().register(ctx.waker()); } res }) From 2dffefce5ec6a9c4f162be923a899df356884887 Mon Sep 17 00:00:00 2001 From: Harishguna Satgunarajah Date: Mon, 28 Aug 2023 16:01:33 -0700 Subject: [PATCH 38/77] adding device impl for ethernetdma value (not ref) as well --- src/ethernet/eth.rs | 42 +++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/src/ethernet/eth.rs b/src/ethernet/eth.rs index 22e00949..9c0a4169 100644 --- a/src/ethernet/eth.rs +++ b/src/ethernet/eth.rs @@ -33,8 +33,8 @@ use futures::task::AtomicWaker; use smoltcp::{ self, phy::{ - self, ChecksumCapabilities, DeviceCapabilities, PacketMeta, - RxToken, TxToken, + self, ChecksumCapabilities, DeviceCapabilities, PacketMeta, RxToken, + TxToken, }, time::Instant, wire::EthernetAddress, @@ -44,7 +44,7 @@ use super::rx::{ self, RxDescriptor, RxDescriptorRing, RxError, RxPacket, RxRing, }; use super::tx::{ - self, TxDescriptor, TxDescriptorRing, TxPacket, TxError, TxRing, + self, TxDescriptor, TxDescriptorRing, TxError, TxPacket, TxRing, }; use super::packet_id::PacketId; @@ -392,25 +392,23 @@ pub unsafe fn new_unchecked<'rx, 'tx>( w // Rx Tx priority ratio 2:1 .pr() - .variant(0b001) + .variant(0b001) }); // bus mode register eth_dma.dmasbmr.modify(|_, w| { // Address-aligned beats - w.aal() - .set_bit() - }); - eth_dma.dmaccr.modify(|_, w| { - w.dsl() - .variant(DESC_WORD_SKIP) + w.aal().set_bit() }); + eth_dma + .dmaccr + .modify(|_, w| w.dsl().variant(DESC_WORD_SKIP)); eth_dma.dmactx_cr.modify(|_, w| { w // Tx DMA PBL - .txpbl() + .txpbl() .bits(32) // Operate on second frame - .osf() + .osf() .clear_bit() }); @@ -420,7 +418,7 @@ pub unsafe fn new_unchecked<'rx, 'tx>( .rbsz() .variant(rx_buffer.first_buffer().len() as u16) // Rx DMA PBL - .rxpbl() + .rxpbl() .bits(32) }); @@ -643,8 +641,23 @@ impl<'dma, 'tx> TxToken for EthTxToken<'dma, 'tx> { } } -/// Use this Ethernet driver with [smoltcp](https://github.com/smoltcp-rs/smoltcp) impl<'a, 'rx, 'tx> phy::Device for &'a mut EthernetDMA<'rx, 'tx> { + fn capabilities(&self) -> DeviceCapabilities { + (*self).capabilities() + } + fn receive( + &mut self, + timestamp: Instant, + ) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + (*self).receive(timestamp) + } + fn transmit(&mut self, timestamp: Instant) -> Option> { + (*self).transmit(timestamp) + } +} + +/// Use this Ethernet driver with [smoltcp](https://github.com/smoltcp-rs/smoltcp) +impl<'rx, 'tx> phy::Device for EthernetDMA<'rx, 'tx> { type RxToken<'token> = EthRxToken<'token, 'rx> where Self: 'token; type TxToken<'token> = EthTxToken<'token, 'tx> where Self: 'token; @@ -1022,4 +1035,3 @@ pub struct InterruptReasonSummary { /// The interrupt was caused by an error event. pub is_error: bool, } - From 44f43ae240eed9f22fb82ed258b552af49f3ca32 Mon Sep 17 00:00:00 2001 From: Harishguna Satgunarajah Date: Mon, 28 Aug 2023 16:07:11 -0700 Subject: [PATCH 39/77] adding missing token impls --- src/ethernet/eth.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ethernet/eth.rs b/src/ethernet/eth.rs index 9c0a4169..8ce395ad 100644 --- a/src/ethernet/eth.rs +++ b/src/ethernet/eth.rs @@ -642,6 +642,9 @@ impl<'dma, 'tx> TxToken for EthTxToken<'dma, 'tx> { } impl<'a, 'rx, 'tx> phy::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 { (*self).capabilities() } From c02c47da23bf43889c10d4c9bc3fe3180349593b Mon Sep 17 00:00:00 2001 From: Harishguna Satgunarajah Date: Mon, 28 Aug 2023 16:37:28 -0700 Subject: [PATCH 40/77] setting packet_id in transmit --- src/ethernet/eth.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ethernet/eth.rs b/src/ethernet/eth.rs index 8ce395ad..0d7e40c0 100644 --- a/src/ethernet/eth.rs +++ b/src/ethernet/eth.rs @@ -703,11 +703,13 @@ impl<'rx, 'tx> phy::Device for EthernetDMA<'rx, 'tx> { fn transmit(&mut self, _timestamp: Instant) -> Option> { if self.tx_available() { + let tx_packet_id = self.next_packet_id(); + let EthernetDMA { tx_ring, .. } = self; Some(EthTxToken { tx_ring, #[cfg(feature = "ptp")] - meta: None, + meta: Some(tx_packet_id), }) } else { None From dfac2f24c5f804b5b51026a7529c69ba72622569 Mon Sep 17 00:00:00 2001 From: Harishguna Satgunarajah Date: Thu, 31 Aug 2023 15:37:06 -0700 Subject: [PATCH 41/77] fixing dma impls --- src/ethernet/eth.rs | 49 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/src/ethernet/eth.rs b/src/ethernet/eth.rs index 0d7e40c0..33228d01 100644 --- a/src/ethernet/eth.rs +++ b/src/ethernet/eth.rs @@ -646,16 +646,55 @@ impl<'a, 'rx, 'tx> phy::Device for &'a mut EthernetDMA<'rx, 'tx> { type TxToken<'token> = EthTxToken<'token, 'tx> where Self: 'token; fn capabilities(&self) -> DeviceCapabilities { - (*self).capabilities() + let mut caps = DeviceCapabilities::default(); + caps.max_transmission_unit = ethernet::MTU; + caps.max_burst_size = Some(1); + caps.checksum = ChecksumCapabilities::ignored(); + caps } + fn receive( &mut self, - timestamp: Instant, + _timestamp: Instant, ) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { - (*self).receive(timestamp) + if self.tx_available() && self.rx_available() { + #[cfg(feature = "ptp")] + let rx_packet_id = self.next_packet_id(); + + let EthernetDMA { + rx_ring, tx_ring, .. + } = self; + + let rx = EthRxToken { + rx_ring, + #[cfg(feature = "ptp")] + meta: rx_packet_id, + }; + + let tx = EthTxToken { + tx_ring, + #[cfg(feature = "ptp")] + meta: None, + }; + Some((rx, tx)) + } else { + None + } } - fn transmit(&mut self, timestamp: Instant) -> Option> { - (*self).transmit(timestamp) + + fn transmit(&mut self, _timestamp: Instant) -> Option> { + if self.tx_available() { + let tx_packet_id = self.next_packet_id(); + + let EthernetDMA { tx_ring, .. } = self; + Some(EthTxToken { + tx_ring, + #[cfg(feature = "ptp")] + meta: Some(tx_packet_id), + }) + } else { + None + } } } From 8bfee82ff049f969a021ba06960275b59ca1f744 Mon Sep 17 00:00:00 2001 From: Harishguna Satgunarajah Date: Thu, 7 Sep 2023 13:08:57 -0700 Subject: [PATCH 42/77] adding func to enable multicast --- src/ethernet/eth.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/ethernet/eth.rs b/src/ethernet/eth.rs index 33228d01..412a8412 100644 --- a/src/ethernet/eth.rs +++ b/src/ethernet/eth.rs @@ -532,6 +532,14 @@ impl EthernetMAC { clock_range: self.clock_range, } } + + pub fn set_all_multicast(&mut self, enable: bool) { + cortex_m::interrupt::free(|_cs| { + eth_mac.macpfr.modify(|_, w| { + w.pm().bit(enable) + }); + }); + } } /// PHY Operations From 01d0a7e454df68171e3177887f85888d55ea0df8 Mon Sep 17 00:00:00 2001 From: Harishguna Satgunarajah Date: Thu, 7 Sep 2023 13:21:49 -0700 Subject: [PATCH 43/77] fixing multicast bug --- src/ethernet/eth.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ethernet/eth.rs b/src/ethernet/eth.rs index 412a8412..ba3d78e3 100644 --- a/src/ethernet/eth.rs +++ b/src/ethernet/eth.rs @@ -535,7 +535,7 @@ impl EthernetMAC { pub fn set_all_multicast(&mut self, enable: bool) { cortex_m::interrupt::free(|_cs| { - eth_mac.macpfr.modify(|_, w| { + self.eth_mac.macpfr.modify(|_, w| { w.pm().bit(enable) }); }); From bff29fb5c71521e573dcf84829f1ff21fbb26caf Mon Sep 17 00:00:00 2001 From: Harishguna Satgunarajah Date: Thu, 14 Sep 2023 13:34:10 -0700 Subject: [PATCH 44/77] removing impl on dma ref --- src/ethernet/eth.rs | 57 --------------------------------------------- 1 file changed, 57 deletions(-) diff --git a/src/ethernet/eth.rs b/src/ethernet/eth.rs index ba3d78e3..d3911792 100644 --- a/src/ethernet/eth.rs +++ b/src/ethernet/eth.rs @@ -649,63 +649,6 @@ impl<'dma, 'tx> TxToken for EthTxToken<'dma, 'tx> { } } -impl<'a, 'rx, 'tx> phy::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(); - caps.max_transmission_unit = ethernet::MTU; - caps.max_burst_size = Some(1); - caps.checksum = ChecksumCapabilities::ignored(); - caps - } - - fn receive( - &mut self, - _timestamp: Instant, - ) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { - if self.tx_available() && self.rx_available() { - #[cfg(feature = "ptp")] - let rx_packet_id = self.next_packet_id(); - - let EthernetDMA { - rx_ring, tx_ring, .. - } = self; - - let rx = EthRxToken { - rx_ring, - #[cfg(feature = "ptp")] - meta: rx_packet_id, - }; - - let tx = EthTxToken { - tx_ring, - #[cfg(feature = "ptp")] - meta: None, - }; - Some((rx, tx)) - } else { - None - } - } - - fn transmit(&mut self, _timestamp: Instant) -> Option> { - if self.tx_available() { - let tx_packet_id = self.next_packet_id(); - - let EthernetDMA { tx_ring, .. } = self; - Some(EthTxToken { - tx_ring, - #[cfg(feature = "ptp")] - meta: Some(tx_packet_id), - }) - } else { - None - } - } -} - /// Use this Ethernet driver with [smoltcp](https://github.com/smoltcp-rs/smoltcp) impl<'rx, 'tx> phy::Device for EthernetDMA<'rx, 'tx> { type RxToken<'token> = EthRxToken<'token, 'rx> where Self: 'token; From 0eb4fce8a920483380ff73fb4c4ac9852c524c77 Mon Sep 17 00:00:00 2001 From: Harishguna Satgunarajah Date: Mon, 18 Sep 2023 16:44:41 -0700 Subject: [PATCH 45/77] capturing and saving ptp frames --- src/ethernet/eth.rs | 132 +++++++++++++++++++++++++++++++------------- 1 file changed, 95 insertions(+), 37 deletions(-) diff --git a/src/ethernet/eth.rs b/src/ethernet/eth.rs index d3911792..ab054909 100644 --- a/src/ethernet/eth.rs +++ b/src/ethernet/eth.rs @@ -75,6 +75,10 @@ const _ASSERT_DESC_WORD_SKIP_SIZE: () = assert!(DESC_WORD_SKIP <= 0b111); // padding const ETH_BUF_SIZE: usize = 1536; + +pub const PTP_MAX_SIZE: usize = 76 (0x4C); +pub const MAX_PTP_FOLLOWERS: usize = 16 (0x10); + /// Transmit and Receive Descriptor fields #[allow(dead_code)] mod emac_consts { @@ -107,6 +111,10 @@ pub struct EthernetDMA<'rx, 'tx> { #[cfg(feature = "ptp")] packet_id_counter: u32, + #[cfg(feature = "ptp")] + ptp_frame_buffer: [([u8; PTP_MAX_SIZE], Option); MAX_PTP_FOLLOWERS], + #[cfg(feature = "ptp")] + write_pos: usize, } /// Ethernet MAC @@ -289,36 +297,72 @@ pub unsafe fn new_unchecked<'rx, 'tx>( }); // frame filter register eth_mac.macpfr.modify(|_, w| { - w.dntu() - .clear_bit() - .ipfe() - .clear_bit() - .vtfe() - .clear_bit() - .hpf() - .clear_bit() - .saf() - .clear_bit() - .saif() - .clear_bit() - .pcf() - .bits(0b00) - .dbf() - .clear_bit() - .pm() - .clear_bit() - .daif() - .clear_bit() - .hmc() - .clear_bit() - .huc() - .clear_bit() - // Receive All - .ra() - .clear_bit() - // Promiscuous mode - .pr() - .clear_bit() + #[cfg(feature = "ptp")] + { + w.dntu() + .clear_bit() + .ipfe() + .clear_bit() + .vtfe() + .clear_bit() + .hpf() + .clear_bit() + .saf() + .clear_bit() + .saif() + .clear_bit() + .pcf() + .bits(0b00) + .dbf() + .clear_bit() + .pm() + .set_bit() + .daif() + .clear_bit() + .hmc() + .clear_bit() + .huc() + .clear_bit() + // Receive All + .ra() + .clear_bit() + // Promiscuous mode + .pr() + .clear_bit() + } + #[cfg(not(feature = "ptp"))] + { + w.dntu() + .clear_bit() + .ipfe() + .clear_bit() + .vtfe() + .clear_bit() + .hpf() + .clear_bit() + .saf() + .clear_bit() + .saif() + .clear_bit() + .pcf() + .bits(0b00) + .dbf() + .clear_bit() + .pm() + .clear_bit() + .daif() + .clear_bit() + .hmc() + .clear_bit() + .huc() + .clear_bit() + // Receive All + .ra() + .clear_bit() + // Promiscuous mode + .pr() + .clear_bit() + } }); eth_mac.macwtr.write(|w| w.pwe().clear_bit()); // Flow Control Register @@ -470,6 +514,11 @@ pub unsafe fn new_unchecked<'rx, 'tx>( #[cfg(feature = "ptp")] packet_id_counter: 0, + #[cfg(feature = "ptp")] + ptp_frame_buffer: [([0u8; PTP_MAX_SIZE], None); MAX_PTP_FOLLOWERS], + #[cfg(feature = "ptp")] + write_pos: 0, + }; dma.rx_ring.start(&dma.eth_dma); dma.tx_ring.start(&dma.eth_dma); @@ -533,13 +582,6 @@ impl EthernetMAC { } } - pub fn set_all_multicast(&mut self, enable: bool) { - cortex_m::interrupt::free(|_cs| { - self.eth_mac.macpfr.modify(|_, w| { - w.pm().bit(enable) - }); - }); - } } /// PHY Operations @@ -591,6 +633,8 @@ pub struct EthRxToken<'a, 'rx> { rx_ring: &'a mut RxRing<'rx>, #[cfg(feature = "ptp")] meta: PacketId, + #[cfg(feature = "ptp")] + buf: &'a mut ([u8; PTP_MAX_SIZE], Option), } impl<'dma, 'rx> RxToken for EthRxToken<'dma, 'rx> { @@ -606,6 +650,15 @@ impl<'dma, 'rx> RxToken for EthRxToken<'dma, 'rx> { // NOTE(unwrap): an `EthRxToken` is only created when `eth.rx_available()` let mut packet = self.rx_ring.recv_next(meta).ok().unwrap(); + #[cfg(feature = "ptp")] + { + let ethertype = NetworkEndian::read_u16(&buffer[12..14]); + if ethertype == 0x88F7 { + let packet_buf = &buffer[14..]; + ((self.buf.0)[0..packet_buf.len()]).copy_from_slice(packet_buf); + self.buf.1 = Some(meta); + } + } let result = f(&mut packet); packet.free(); result @@ -678,8 +731,13 @@ impl<'rx, 'tx> phy::Device for EthernetDMA<'rx, 'tx> { rx_ring, #[cfg(feature = "ptp")] meta: rx_packet_id, + #[cfg(feature = "ptp")] + buf: &mut self.ptp_frame_buffer[self.write_pos], }; + #[cfg(feature = "ptp")] + self.write_pos = (self.write_pos + 1) % MAX_PTP_FOLLOWERS; + let tx = EthTxToken { tx_ring, #[cfg(feature = "ptp")] From a8e67cbf83dc15137cb732aa86956400eaa73727 Mon Sep 17 00:00:00 2001 From: Harishguna Satgunarajah Date: Mon, 18 Sep 2023 16:46:09 -0700 Subject: [PATCH 46/77] importing networkendian --- src/ethernet/eth.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ethernet/eth.rs b/src/ethernet/eth.rs index ab054909..61d03341 100644 --- a/src/ethernet/eth.rs +++ b/src/ethernet/eth.rs @@ -28,6 +28,7 @@ use crate::ptp::{EthernetPTP, Timestamp}; use crate::rcc::{rec, CoreClocks, ResetEnable}; use crate::stm32; use crate::stm32::{Interrupt, ETHERNET_DMA, ETHERNET_MTL, NVIC}; +use byteorder::{ByteOrder, NetworkEndian}; use futures::task::AtomicWaker; use smoltcp::{ From e5f5e369e597fc6e78418d2b75b7b28f40f7acec Mon Sep 17 00:00:00 2001 From: Harishguna Satgunarajah Date: Wed, 20 Sep 2023 10:21:04 -0700 Subject: [PATCH 47/77] bug fixes --- src/ethernet/eth.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/ethernet/eth.rs b/src/ethernet/eth.rs index 61d03341..fb4985e9 100644 --- a/src/ethernet/eth.rs +++ b/src/ethernet/eth.rs @@ -28,7 +28,6 @@ use crate::ptp::{EthernetPTP, Timestamp}; use crate::rcc::{rec, CoreClocks, ResetEnable}; use crate::stm32; use crate::stm32::{Interrupt, ETHERNET_DMA, ETHERNET_MTL, NVIC}; -use byteorder::{ByteOrder, NetworkEndian}; use futures::task::AtomicWaker; use smoltcp::{ @@ -77,8 +76,8 @@ const _ASSERT_DESC_WORD_SKIP_SIZE: () = assert!(DESC_WORD_SKIP <= 0b111); const ETH_BUF_SIZE: usize = 1536; -pub const PTP_MAX_SIZE: usize = 76 (0x4C); -pub const MAX_PTP_FOLLOWERS: usize = 16 (0x10); +pub const PTP_MAX_SIZE: usize = 76; +pub const MAX_PTP_FOLLOWERS: usize = 16; /// Transmit and Receive Descriptor fields #[allow(dead_code)] @@ -653,9 +652,9 @@ impl<'dma, 'rx> RxToken for EthRxToken<'dma, 'rx> { let mut packet = self.rx_ring.recv_next(meta).ok().unwrap(); #[cfg(feature = "ptp")] { - let ethertype = NetworkEndian::read_u16(&buffer[12..14]); + let ethertype = u16::from_be_bytes(&packet[12..14].try_into().unwrap()); if ethertype == 0x88F7 { - let packet_buf = &buffer[14..]; + let packet_buf = &packet[14..]; ((self.buf.0)[0..packet_buf.len()]).copy_from_slice(packet_buf); self.buf.1 = Some(meta); } From 37b7f4ee83977b36883f30b85e4c3d1c0ad18918 Mon Sep 17 00:00:00 2001 From: Harishguna Satgunarajah Date: Wed, 20 Sep 2023 10:23:29 -0700 Subject: [PATCH 48/77] more bug fixes --- src/ethernet/eth.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ethernet/eth.rs b/src/ethernet/eth.rs index fb4985e9..6b0da880 100644 --- a/src/ethernet/eth.rs +++ b/src/ethernet/eth.rs @@ -652,11 +652,11 @@ impl<'dma, 'rx> RxToken for EthRxToken<'dma, 'rx> { let mut packet = self.rx_ring.recv_next(meta).ok().unwrap(); #[cfg(feature = "ptp")] { - let ethertype = u16::from_be_bytes(&packet[12..14].try_into().unwrap()); + let ethertype = u16::from_be_bytes(packet[12..14].try_into().unwrap()); if ethertype == 0x88F7 { let packet_buf = &packet[14..]; ((self.buf.0)[0..packet_buf.len()]).copy_from_slice(packet_buf); - self.buf.1 = Some(meta); + self.buf.1 = meta; } } let result = f(&mut packet); From 02a2fb95f532f8ef100a56ce947a1eeebe4d49d7 Mon Sep 17 00:00:00 2001 From: Harishguna Satgunarajah Date: Wed, 20 Sep 2023 10:24:44 -0700 Subject: [PATCH 49/77] more bug fixes --- src/ethernet/eth.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ethernet/eth.rs b/src/ethernet/eth.rs index 6b0da880..521b7b43 100644 --- a/src/ethernet/eth.rs +++ b/src/ethernet/eth.rs @@ -736,7 +736,9 @@ impl<'rx, 'tx> phy::Device for EthernetDMA<'rx, 'tx> { }; #[cfg(feature = "ptp")] - self.write_pos = (self.write_pos + 1) % MAX_PTP_FOLLOWERS; + { + self.write_pos = (self.write_pos + 1) % MAX_PTP_FOLLOWERS; + } let tx = EthTxToken { tx_ring, From ec574c8268f2f8317d3b993eebfe1a4c45c527d9 Mon Sep 17 00:00:00 2001 From: Harishguna Satgunarajah Date: Wed, 20 Sep 2023 10:36:00 -0700 Subject: [PATCH 50/77] adding functions to access ptp frames --- src/ethernet/eth.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/ethernet/eth.rs b/src/ethernet/eth.rs index 521b7b43..6ab9d41c 100644 --- a/src/ethernet/eth.rs +++ b/src/ethernet/eth.rs @@ -768,6 +768,24 @@ impl<'rx, 'tx> phy::Device for EthernetDMA<'rx, 'tx> { } impl<'rx, 'tx> EthernetDMA<'rx, 'tx> { + #[cfg(feature = "ptp")] + pub fn get_frame_from(&'a self, clock_identity: u64) -> Option<(&'a ([u8; PTP_MAX_SIZE], Option), usize)> { + for i in 0..MAX_PTP_FOLLOWERS { + if self.ptp_frame_buffer[i].1 != None { + // defmt::info!("buffer = {}", self.ptp_frame_buffer[i].0); + if NetworkEndian::read_u64(&self.ptp_frame_buffer[i].0[20..28]) == clock_identity { + return Some((&self.ptp_frame_buffer[i], i)); + } + } + } + None + } + #[cfg(feature = "ptp")] + pub fn invalidate_frame_at(&'a mut self, pos: usize) { + if pos < MAX_PTP_FOLLOWERS { + self.ptp_frame_buffer[pos].1 = None; + } + } /// Return the number of packets dropped since this method was /// last called pub fn number_packets_dropped(&self) -> u32 { From 1b10cf9d490ff865191da315fe64aa31387153a5 Mon Sep 17 00:00:00 2001 From: Harishguna Satgunarajah Date: Wed, 20 Sep 2023 10:44:13 -0700 Subject: [PATCH 51/77] adding lifetime removing networkendian usage --- src/ethernet/eth.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ethernet/eth.rs b/src/ethernet/eth.rs index 6ab9d41c..bcdd817d 100644 --- a/src/ethernet/eth.rs +++ b/src/ethernet/eth.rs @@ -767,13 +767,13 @@ impl<'rx, 'tx> phy::Device for EthernetDMA<'rx, 'tx> { } } -impl<'rx, 'tx> EthernetDMA<'rx, 'tx> { +impl<'a, 'rx, 'tx> EthernetDMA<'rx, 'tx> { #[cfg(feature = "ptp")] pub fn get_frame_from(&'a self, clock_identity: u64) -> Option<(&'a ([u8; PTP_MAX_SIZE], Option), usize)> { for i in 0..MAX_PTP_FOLLOWERS { if self.ptp_frame_buffer[i].1 != None { // defmt::info!("buffer = {}", self.ptp_frame_buffer[i].0); - if NetworkEndian::read_u64(&self.ptp_frame_buffer[i].0[20..28]) == clock_identity { + if u64::from_be_bytes(self.ptp_frame_buffer[i].0[20..28].try_into().unwrap()) == clock_identity { return Some((&self.ptp_frame_buffer[i], i)); } } From 2d9a212e626c0cb35fbe3a57a0fba65d4ae06f99 Mon Sep 17 00:00:00 2001 From: Harishguna Satgunarajah Date: Wed, 20 Sep 2023 14:23:27 -0700 Subject: [PATCH 52/77] adding send ptp frame func --- src/ethernet/eth.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/ethernet/eth.rs b/src/ethernet/eth.rs index bcdd817d..c06e6366 100644 --- a/src/ethernet/eth.rs +++ b/src/ethernet/eth.rs @@ -786,6 +786,19 @@ impl<'a, 'rx, 'tx> EthernetDMA<'rx, 'tx> { self.ptp_frame_buffer[pos].1 = None; } } + #[cfg(feature = "ptp")] + pub fn send_ptp_frame( + frame: &[u8], + tx_option: Option< as Device>::TxToken<'_>>, + meta: PacketId, + ) { + if let Some(mut tx_token) = tx_option { + tx_token.set_meta(meta.into()); + tx_token.consume(frame.len(), |buf| { + buf.copy_from_slice(&frame); + }); + } + } /// Return the number of packets dropped since this method was /// last called pub fn number_packets_dropped(&self) -> u32 { From e0083f6f0ceb55956b3eb6a97f0e4d8cc06974bc Mon Sep 17 00:00:00 2001 From: Harishguna Satgunarajah Date: Wed, 20 Sep 2023 14:33:36 -0700 Subject: [PATCH 53/77] devie import error fix --- src/ethernet/eth.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ethernet/eth.rs b/src/ethernet/eth.rs index c06e6366..78a1fc3a 100644 --- a/src/ethernet/eth.rs +++ b/src/ethernet/eth.rs @@ -789,7 +789,7 @@ impl<'a, 'rx, 'tx> EthernetDMA<'rx, 'tx> { #[cfg(feature = "ptp")] pub fn send_ptp_frame( frame: &[u8], - tx_option: Option< as Device>::TxToken<'_>>, + tx_option: Option< as phy::Device>::TxToken<'_>>, meta: PacketId, ) { if let Some(mut tx_token) = tx_option { From f8044737e8a273623391bc83de46db87c72a6f6a Mon Sep 17 00:00:00 2001 From: Harishguna Satgunarajah Date: Wed, 20 Sep 2023 14:39:59 -0700 Subject: [PATCH 54/77] Revert "removing impl on dma ref" This reverts commit bff29fb5c71521e573dcf84829f1ff21fbb26caf. --- src/ethernet/eth.rs | 57 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/src/ethernet/eth.rs b/src/ethernet/eth.rs index 78a1fc3a..10b81e79 100644 --- a/src/ethernet/eth.rs +++ b/src/ethernet/eth.rs @@ -702,6 +702,63 @@ impl<'dma, 'tx> TxToken for EthTxToken<'dma, 'tx> { } } +impl<'a, 'rx, 'tx> phy::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(); + caps.max_transmission_unit = ethernet::MTU; + caps.max_burst_size = Some(1); + caps.checksum = ChecksumCapabilities::ignored(); + caps + } + + fn receive( + &mut self, + _timestamp: Instant, + ) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + if self.tx_available() && self.rx_available() { + #[cfg(feature = "ptp")] + let rx_packet_id = self.next_packet_id(); + + let EthernetDMA { + rx_ring, tx_ring, .. + } = self; + + let rx = EthRxToken { + rx_ring, + #[cfg(feature = "ptp")] + meta: rx_packet_id, + }; + + let tx = EthTxToken { + tx_ring, + #[cfg(feature = "ptp")] + meta: None, + }; + Some((rx, tx)) + } else { + None + } + } + + fn transmit(&mut self, _timestamp: Instant) -> Option> { + if self.tx_available() { + let tx_packet_id = self.next_packet_id(); + + let EthernetDMA { tx_ring, .. } = self; + Some(EthTxToken { + tx_ring, + #[cfg(feature = "ptp")] + meta: Some(tx_packet_id), + }) + } else { + None + } + } +} + /// Use this Ethernet driver with [smoltcp](https://github.com/smoltcp-rs/smoltcp) impl<'rx, 'tx> phy::Device for EthernetDMA<'rx, 'tx> { type RxToken<'token> = EthRxToken<'token, 'rx> where Self: 'token; From f8c8f2e143f943909be6c8c2794f955b4bcde8e5 Mon Sep 17 00:00:00 2001 From: Harishguna Satgunarajah Date: Wed, 20 Sep 2023 14:48:58 -0700 Subject: [PATCH 55/77] adding ptp frame buf for dma ref impl --- src/ethernet/eth.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ethernet/eth.rs b/src/ethernet/eth.rs index 10b81e79..9083a5aa 100644 --- a/src/ethernet/eth.rs +++ b/src/ethernet/eth.rs @@ -730,6 +730,8 @@ impl<'a, 'rx, 'tx> phy::Device for &'a mut EthernetDMA<'rx, 'tx> { rx_ring, #[cfg(feature = "ptp")] meta: rx_packet_id, + #[cfg(feature = "ptp")] + buf: &mut self.ptp_frame_buffer[self.write_pos], }; let tx = EthTxToken { From 98597bedd8869d183b63179f1149188bcb5415d7 Mon Sep 17 00:00:00 2001 From: Harishguna Satgunarajah Date: Wed, 20 Sep 2023 15:14:11 -0700 Subject: [PATCH 56/77] ptp send uses frame length to copy --- src/ethernet/eth.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ethernet/eth.rs b/src/ethernet/eth.rs index 9083a5aa..71f0e53b 100644 --- a/src/ethernet/eth.rs +++ b/src/ethernet/eth.rs @@ -854,7 +854,7 @@ impl<'a, 'rx, 'tx> EthernetDMA<'rx, 'tx> { if let Some(mut tx_token) = tx_option { tx_token.set_meta(meta.into()); tx_token.consume(frame.len(), |buf| { - buf.copy_from_slice(&frame); + buf[..frame.len()].copy_from_slice(&frame); }); } } From 0f0c47c4d34bb2ae70d0ab3b07793c3d88817ff1 Mon Sep 17 00:00:00 2001 From: Harishguna Satgunarajah Date: Thu, 21 Sep 2023 11:07:18 -0700 Subject: [PATCH 57/77] adding credit to @datdenkikniet (Johannes Draaijer) --- src/ethernet/cache.rs | 4 ++++ src/ethernet/eth.rs | 4 ++++ src/ethernet/mod.rs | 4 ++++ src/ethernet/packet_id.rs | 4 ++++ src/ethernet/raw_descriptor.rs | 5 +++++ src/ethernet/rx/descriptor.rs | 4 ++++ src/ethernet/rx/mod.rs | 4 ++++ src/ethernet/tx/descriptor.rs | 4 ++++ src/ethernet/tx/mod.rs | 4 ++++ src/ptp/mod.rs | 3 +++ src/ptp/subseconds.rs | 4 ++++ src/ptp/timestamp.rs | 4 ++++ 12 files changed, 48 insertions(+) diff --git a/src/ethernet/cache.rs b/src/ethernet/cache.rs index 4ad92b35..f4c78770 100644 --- a/src/ethernet/cache.rs +++ b/src/ethernet/cache.rs @@ -1,3 +1,7 @@ +//! This implementation is derived from 0BSD-relicensed work done by +//! Johannes Draaijer for the +//! [`stm32-eth`](https://github.com/stm32-rs/stm32-eth) project + #[cfg(feature = "ptp")] use crate::ptp::Timestamp; diff --git a/src/ethernet/eth.rs b/src/ethernet/eth.rs index 71f0e53b..df613eff 100644 --- a/src/ethernet/eth.rs +++ b/src/ethernet/eth.rs @@ -19,6 +19,10 @@ //! > want to enable the cache, the simplest method would be to mark SRAM3 //! > as uncacheable via the MPU. //! +//! This implementation is derived from 0BSD-relicensed work done by +//! Johannes Draaijer for the +//! [`stm32-eth`](https://github.com/stm32-rs/stm32-eth) project +//! //! [quartiq/stabilizer]: https://github.com/quartiq/stabilizer //! [notes]: https://github.com/quartiq/stabilizer/commit/ab1735950b2108eaa8d51eb63efadcd2e25c35c4 diff --git a/src/ethernet/mod.rs b/src/ethernet/mod.rs index 7b5339db..ec61d761 100644 --- a/src/ethernet/mod.rs +++ b/src/ethernet/mod.rs @@ -5,6 +5,10 @@ //! - SMSC LAN8742a //! - Micrel KSZ8081R //! +//! This implementation is derived from 0BSD-relicensed work done by +//! Johannes Draaijer for the +//! [`stm32-eth`](https://github.com/stm32-rs/stm32-eth) project +//! //! # Examples //! //! - [Simple link checker for the Nucleo-H743ZI2](https://github.com/stm32-rs/stm32h7xx-hal/blob/master/examples/ethernet-nucleo-h743zi2.rs) diff --git a/src/ethernet/packet_id.rs b/src/ethernet/packet_id.rs index da07d2ee..f6e90e54 100644 --- a/src/ethernet/packet_id.rs +++ b/src/ethernet/packet_id.rs @@ -3,6 +3,10 @@ /// This packet ID can be used to obtain information about a specific /// ethernet frame (either sent or received) from the DMA. /// +//! This implementation is derived from 0BSD-relicensed work done by +//! Johannes Draaijer for the +//! [`stm32-eth`](https://github.com/stm32-rs/stm32-eth) project + #[cfg_attr( feature = "ptp", doc = " diff --git a/src/ethernet/raw_descriptor.rs b/src/ethernet/raw_descriptor.rs index 38e3411e..3baeb52c 100644 --- a/src/ethernet/raw_descriptor.rs +++ b/src/ethernet/raw_descriptor.rs @@ -1,3 +1,8 @@ +//! This implementation is derived from 0BSD-relicensed work done by +//! Johannes Draaijer for the +//! [`stm32-eth`](https://github.com/stm32-rs/stm32-eth) project + + //TODO remove // this is raw_descriptor use volatile_register::{RO, RW}; diff --git a/src/ethernet/rx/descriptor.rs b/src/ethernet/rx/descriptor.rs index f6bde26f..dfa4ed26 100644 --- a/src/ethernet/rx/descriptor.rs +++ b/src/ethernet/rx/descriptor.rs @@ -1,3 +1,7 @@ +//! This implementation is derived from 0BSD-relicensed work done by +//! Johannes Draaijer for the +//! [`stm32-eth`](https://github.com/stm32-rs/stm32-eth) project + use core::sync::atomic::{self, Ordering}; use crate::ethernet::{raw_descriptor::RawDescriptor, Cache, PacketId}; diff --git a/src/ethernet/rx/mod.rs b/src/ethernet/rx/mod.rs index e753a1a5..d3742fa1 100644 --- a/src/ethernet/rx/mod.rs +++ b/src/ethernet/rx/mod.rs @@ -1,3 +1,7 @@ +//! This implementation is derived from 0BSD-relicensed work done by +//! Johannes Draaijer for the +//! [`stm32-eth`](https://github.com/stm32-rs/stm32-eth) project + pub use descriptor::RxDescriptor; use super::{raw_descriptor::DescriptorRing, PacketId}; diff --git a/src/ethernet/tx/descriptor.rs b/src/ethernet/tx/descriptor.rs index 86839222..da79e5a8 100644 --- a/src/ethernet/tx/descriptor.rs +++ b/src/ethernet/tx/descriptor.rs @@ -1,3 +1,7 @@ +//! This implementation is derived from 0BSD-relicensed work done by +//! Johannes Draaijer for the +//! [`stm32-eth`](https://github.com/stm32-rs/stm32-eth) project + use core::sync::atomic::{self, Ordering}; use crate::ethernet::{raw_descriptor::RawDescriptor, Cache, PacketId}; diff --git a/src/ethernet/tx/mod.rs b/src/ethernet/tx/mod.rs index 4b207587..546214e6 100644 --- a/src/ethernet/tx/mod.rs +++ b/src/ethernet/tx/mod.rs @@ -1,3 +1,7 @@ +//! This implementation is derived from 0BSD-relicensed work done by +//! Johannes Draaijer for the +//! [`stm32-eth`](https://github.com/stm32-rs/stm32-eth) project + use super::{raw_descriptor::DescriptorRing, PacketId}; use crate::stm32::ETHERNET_DMA; diff --git a/src/ptp/mod.rs b/src/ptp/mod.rs index 70a0a514..74834533 100644 --- a/src/ptp/mod.rs +++ b/src/ptp/mod.rs @@ -1,6 +1,9 @@ //! PTP access and configuration. //! //! See [`EthernetPTP`] for a more details. +//! This implementation is derived from 0BSD-relicensed work done by +//! Johannes Draaijer for the +//! [`stm32-eth`](https://github.com/stm32-rs/stm32-eth) project use crate::ethernet::EthernetDMA; use crate::rcc::CoreClocks; diff --git a/src/ptp/subseconds.rs b/src/ptp/subseconds.rs index c9d736d7..b0df629d 100644 --- a/src/ptp/subseconds.rs +++ b/src/ptp/subseconds.rs @@ -1,3 +1,7 @@ +//! This implementation is derived from 0BSD-relicensed work done by +//! Johannes Draaijer for the +//! [`stm32-eth`](https://github.com/stm32-rs/stm32-eth) project + /// The amount of nanoseconds per second. pub const NANOS_PER_SECOND: u32 = 1_000_000_000; diff --git a/src/ptp/timestamp.rs b/src/ptp/timestamp.rs index 1c7f0a19..2c1d8e25 100644 --- a/src/ptp/timestamp.rs +++ b/src/ptp/timestamp.rs @@ -1,3 +1,7 @@ +//! This implementation is derived from 0BSD-relicensed work done by +//! Johannes Draaijer for the +//! [`stm32-eth`](https://github.com/stm32-rs/stm32-eth) project + use super::{Subseconds, NANOS_PER_SECOND}; /// A timestamp produced by the PTP periperhal From b64d97a390f62a2a0de984a4ebdd8e4f48bbee24 Mon Sep 17 00:00:00 2001 From: Harishguna Satgunarajah Date: Thu, 21 Sep 2023 11:37:06 -0700 Subject: [PATCH 58/77] moving comment to prevent error --- src/ethernet/packet_id.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ethernet/packet_id.rs b/src/ethernet/packet_id.rs index f6e90e54..9d965f08 100644 --- a/src/ethernet/packet_id.rs +++ b/src/ethernet/packet_id.rs @@ -1,11 +1,12 @@ +//! This implementation is derived from 0BSD-relicensed work done by +//! Johannes Draaijer for the +//! [`stm32-eth`](https://github.com/stm32-rs/stm32-eth) project + /// A packet ID. /// /// This packet ID can be used to obtain information about a specific /// ethernet frame (either sent or received) from the DMA. /// -//! This implementation is derived from 0BSD-relicensed work done by -//! Johannes Draaijer for the -//! [`stm32-eth`](https://github.com/stm32-rs/stm32-eth) project #[cfg_attr( feature = "ptp", From d324c1e28c27ce246892133d98be95d37030f476 Mon Sep 17 00:00:00 2001 From: Harishguna Satgunarajah Date: Mon, 2 Oct 2023 09:56:42 -0700 Subject: [PATCH 59/77] some clippy fixes --- src/ethernet/eth.rs | 44 +++++++++++++++++++---------------- src/ethernet/rx/descriptor.rs | 7 +++--- src/ethernet/rx/mod.rs | 4 ++-- src/ethernet/tx/mod.rs | 2 +- src/ptp/mod.rs | 4 +--- src/ptp/subseconds.rs | 8 +++---- src/ptp/timestamp.rs | 2 +- 7 files changed, 36 insertions(+), 35 deletions(-) diff --git a/src/ethernet/eth.rs b/src/ethernet/eth.rs index df613eff..3960eaaa 100644 --- a/src/ethernet/eth.rs +++ b/src/ethernet/eth.rs @@ -31,7 +31,7 @@ use core::task::Poll; use crate::ptp::{EthernetPTP, Timestamp}; use crate::rcc::{rec, CoreClocks, ResetEnable}; use crate::stm32; -use crate::stm32::{Interrupt, ETHERNET_DMA, ETHERNET_MTL, NVIC}; +use crate::stm32::{Interrupt, ETHERNET_DMA, NVIC}; use futures::task::AtomicWaker; use smoltcp::{ @@ -45,10 +45,10 @@ use smoltcp::{ }; use super::rx::{ - self, RxDescriptor, RxDescriptorRing, RxError, RxPacket, RxRing, + RxDescriptor, RxDescriptorRing, RxError, RxPacket, RxRing, }; use super::tx::{ - self, TxDescriptor, TxDescriptorRing, TxError, TxPacket, TxRing, + TxDescriptor, TxDescriptorRing, TxError, TxPacket, TxRing, }; use super::packet_id::PacketId; @@ -77,7 +77,7 @@ const _ASSERT_DESC_WORD_SKIP_SIZE: () = assert!(DESC_WORD_SKIP <= 0b111); // 6 DMAC, 6 SMAC, 4 q tag, 2 ethernet type II, 1500 ip MTU, 4 CRC, 2 // padding -const ETH_BUF_SIZE: usize = 1536; +// const ETH_BUF_SIZE: usize = 1536; pub const PTP_MAX_SIZE: usize = 76; @@ -98,7 +98,6 @@ mod emac_consts { pub const EMAC_TDES2_B1L: u32 = 0x0000_3FFF; pub const EMAC_DES0_BUF1AP: u32 = 0xFFFF_FFFF; } -use self::emac_consts::*; #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[derive(Clone, Copy, Debug, PartialEq)] @@ -106,17 +105,22 @@ use self::emac_consts::*; /// with any TX or RX descriptors. pub struct PacketIdNotFound; +pub struct PtpFrameWithId { + ptp_frame: [u8; PTP_MAX_SIZE], + packet_id: Option, +} + /// Ethernet DMA. pub struct EthernetDMA<'rx, 'tx> { eth_dma: ETHERNET_DMA, - eth_mtl: crate::stm32::ETHERNET_MTL, + // eth_mtl: crate::stm32::ETHERNET_MTL, rx_ring: RxRing<'rx>, tx_ring: TxRing<'tx>, #[cfg(feature = "ptp")] packet_id_counter: u32, #[cfg(feature = "ptp")] - ptp_frame_buffer: [([u8; PTP_MAX_SIZE], Option); MAX_PTP_FOLLOWERS], + ptp_frame_buffer: [PtpFrameWithId; MAX_PTP_FOLLOWERS], #[cfg(feature = "ptp")] write_pos: usize, } @@ -168,7 +172,7 @@ pub fn new<'rx, 'tx>( mac_addr: EthernetAddress, prec: rec::Eth1Mac, clocks: &CoreClocks, -) -> (Parts<'rx, 'tx>) { +) -> Parts<'rx, 'tx> { pins.set_speed(Speed::VeryHigh); unsafe { new_unchecked( @@ -512,14 +516,14 @@ pub unsafe fn new_unchecked<'rx, 'tx>( let mut dma = EthernetDMA { eth_dma, - eth_mtl, + // eth_mtl, rx_ring: RxRing::new(rx_buffer), tx_ring: TxRing::new(tx_buffer), #[cfg(feature = "ptp")] packet_id_counter: 0, #[cfg(feature = "ptp")] - ptp_frame_buffer: [([0u8; PTP_MAX_SIZE], None); MAX_PTP_FOLLOWERS], + ptp_frame_buffer: [PtpFrameWithId{ ptp_frame: [0u8; PTP_MAX_SIZE], packet_id: None}; MAX_PTP_FOLLOWERS], #[cfg(feature = "ptp")] write_pos: 0, @@ -638,7 +642,7 @@ pub struct EthRxToken<'a, 'rx> { #[cfg(feature = "ptp")] meta: PacketId, #[cfg(feature = "ptp")] - buf: &'a mut ([u8; PTP_MAX_SIZE], Option), + buf: &'a mut PtpFrameWithId, } impl<'dma, 'rx> RxToken for EthRxToken<'dma, 'rx> { @@ -647,7 +651,7 @@ impl<'dma, 'rx> RxToken for EthRxToken<'dma, 'rx> { F: FnOnce(&mut [u8]) -> R, { #[cfg(feature = "ptp")] - let meta = Some(self.meta.into()); + let meta = Some(self.meta); #[cfg(not(feature = "ptp"))] let meta = None; @@ -659,8 +663,8 @@ impl<'dma, 'rx> RxToken for EthRxToken<'dma, 'rx> { let ethertype = u16::from_be_bytes(packet[12..14].try_into().unwrap()); if ethertype == 0x88F7 { let packet_buf = &packet[14..]; - ((self.buf.0)[0..packet_buf.len()]).copy_from_slice(packet_buf); - self.buf.1 = meta; + ((self.buf.ptp_frame)[0..packet_buf.len()]).copy_from_slice(packet_buf); + self.buf.packet_id = meta; } } let result = f(&mut packet); @@ -670,7 +674,7 @@ impl<'dma, 'rx> RxToken for EthRxToken<'dma, 'rx> { #[cfg(feature = "ptp")] fn meta(&self) -> smoltcp::phy::PacketMeta { - self.meta.clone().into() + self.meta.into() } } @@ -832,11 +836,11 @@ impl<'rx, 'tx> phy::Device for EthernetDMA<'rx, 'tx> { impl<'a, 'rx, 'tx> EthernetDMA<'rx, 'tx> { #[cfg(feature = "ptp")] - pub fn get_frame_from(&'a self, clock_identity: u64) -> Option<(&'a ([u8; PTP_MAX_SIZE], Option), usize)> { + pub fn get_frame_from(&'a self, clock_identity: u64) -> Option<(&'a PtpFrameWithId, usize)> { for i in 0..MAX_PTP_FOLLOWERS { - if self.ptp_frame_buffer[i].1 != None { + if self.ptp_frame_buffer[i].packet_id.is_some() { // defmt::info!("buffer = {}", self.ptp_frame_buffer[i].0); - if u64::from_be_bytes(self.ptp_frame_buffer[i].0[20..28].try_into().unwrap()) == clock_identity { + if u64::from_be_bytes(self.ptp_frame_buffer[i].ptp_frame[20..28].try_into().unwrap()) == clock_identity { return Some((&self.ptp_frame_buffer[i], i)); } } @@ -846,7 +850,7 @@ impl<'a, 'rx, 'tx> EthernetDMA<'rx, 'tx> { #[cfg(feature = "ptp")] pub fn invalidate_frame_at(&'a mut self, pos: usize) { if pos < MAX_PTP_FOLLOWERS { - self.ptp_frame_buffer[pos].1 = None; + self.ptp_frame_buffer[pos].packet_id = None; } } #[cfg(feature = "ptp")] @@ -858,7 +862,7 @@ impl<'a, 'rx, 'tx> EthernetDMA<'rx, 'tx> { if let Some(mut tx_token) = tx_option { tx_token.set_meta(meta.into()); tx_token.consume(frame.len(), |buf| { - buf[..frame.len()].copy_from_slice(&frame); + buf[..frame.len()].copy_from_slice(frame); }); } } diff --git a/src/ethernet/rx/descriptor.rs b/src/ethernet/rx/descriptor.rs index dfa4ed26..2ad3f4e7 100644 --- a/src/ethernet/rx/descriptor.rs +++ b/src/ethernet/rx/descriptor.rs @@ -188,12 +188,11 @@ impl RxDescriptor { packet_id: Option, buffer: &mut [u8], ) -> Result<(), RxDescriptorError> { - if self.has_error() { - Err(RxDescriptorError::DmaError) - } else // Only single-frame descriptors and non-context descriptors are supported // for now. - if self.is_first() + if self.has_error() { + Err(RxDescriptorError::DmaError) + } else if self.is_first() && self.is_last() && !self.has_error() && !self.is_context() diff --git a/src/ethernet/rx/mod.rs b/src/ethernet/rx/mod.rs index d3742fa1..bd6dbc0c 100644 --- a/src/ethernet/rx/mod.rs +++ b/src/ethernet/rx/mod.rs @@ -273,7 +273,7 @@ impl<'a> RxRing<'a> { #[cfg(feature = "async-await")] pub async fn recv(&mut self, packet_id: Option) -> RxPacket { let entry = core::future::poll_fn(|ctx| { - let res = self.recv_next_impl(packet_id.clone()); + let res = self.recv_next_impl(packet_id); match res { Ok(value) => Poll::Ready(value), @@ -341,7 +341,7 @@ impl core::ops::Deref for RxPacket<'_, '_> { } } -impl<'a> core::ops::DerefMut for RxPacket<'_, '_> { +impl core::ops::DerefMut for RxPacket<'_, '_> { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.buffer[..self.length] } diff --git a/src/ethernet/tx/mod.rs b/src/ethernet/tx/mod.rs index 546214e6..8e37442b 100644 --- a/src/ethernet/tx/mod.rs +++ b/src/ethernet/tx/mod.rs @@ -384,7 +384,7 @@ impl TxPacket<'_> { impl Drop for TxPacket<'_> { fn drop(&mut self) { self.desc - .send(self.packet_id.clone(), &self.buffer[..self.length]); + .send(self.packet_id, &self.buffer[..self.length]); TxRing::demand_poll(); } } diff --git a/src/ptp/mod.rs b/src/ptp/mod.rs index 74834533..d16cbda1 100644 --- a/src/ptp/mod.rs +++ b/src/ptp/mod.rs @@ -287,9 +287,7 @@ impl EthernetPTP { self.configure_target_time_interrupt(timestamp); core::future::poll_fn(|ctx| { - if EthernetPTP::read_and_clear_interrupt_flag() { - Poll::Ready(()) - } else if EthernetPTP::now().raw() >= timestamp.raw() { + if (EthernetPTP::read_and_clear_interrupt_flag()) || (EthernetPTP::now().raw() >= timestamp.raw()) { Poll::Ready(()) } else { EthernetPTP::waker().register(ctx.waker()); diff --git a/src/ptp/subseconds.rs b/src/ptp/subseconds.rs index b0df629d..a5c013f5 100644 --- a/src/ptp/subseconds.rs +++ b/src/ptp/subseconds.rs @@ -39,7 +39,7 @@ impl Subseconds { /// /// Returns `None` if `value > SUBSECONDS_PER_SECOND`. (See [`SUBSECONDS_PER_SECOND`]). pub const fn new(value: u32) -> Option { - if value > SUBSECONDS_PER_SECOND as u32 { + if value > SUBSECONDS_PER_SECOND { None } else { Some(Self(value)) @@ -63,7 +63,7 @@ impl Subseconds { /// /// Returns [`None`] if `nanos >= NANOS_PER_SECOND`. (See [`NANOS_PER_SECOND`]) pub const fn new_from_nanos(nanos: u32) -> Option { - if nanos >= NANOS_PER_SECOND as u32 { + if nanos >= NANOS_PER_SECOND { return None; } @@ -89,13 +89,13 @@ impl Subseconds { #[allow(unused)] /// Convert this [`Subseconds`] to Hertz pub(crate) const fn hertz(&self) -> u32 { - SUBSECONDS_PER_SECOND as u32 / self.0 + SUBSECONDS_PER_SECOND / self.0 } #[allow(unused)] pub(crate) const fn nearest_increment(input_clk_hz: u32) -> Subseconds { let hclk_half_subs = - (SUBSECONDS_PER_SECOND as u32 + (input_clk_hz / 2)) / input_clk_hz; + (SUBSECONDS_PER_SECOND + (input_clk_hz / 2)) / input_clk_hz; Self::new_unchecked(hclk_half_subs) } diff --git a/src/ptp/timestamp.rs b/src/ptp/timestamp.rs index 2c1d8e25..fda6145f 100644 --- a/src/ptp/timestamp.rs +++ b/src/ptp/timestamp.rs @@ -77,7 +77,7 @@ impl Timestamp { /// Get the raw subsecond value of this timestamp. pub const fn subseconds(&self) -> Subseconds { - Subseconds::new_unchecked(self.0.abs() as u32 & Subseconds::MAX_VALUE) + Subseconds::new_unchecked(self.0.unsigned_abs() as u32 & Subseconds::MAX_VALUE) } /// Get the signed subsecond value of this timestamp. From 342217709b89e1efe1764e3ce426bb7d70ee1c51 Mon Sep 17 00:00:00 2001 From: Harishguna Satgunarajah Date: Mon, 2 Oct 2023 10:01:40 -0700 Subject: [PATCH 60/77] cargo fmt --- src/ethernet/cache.rs | 4 ++-- src/ethernet/eth.rs | 42 ++++++++++++++++++++-------------- src/ethernet/mod.rs | 4 ++-- src/ethernet/packet_id.rs | 4 ++-- src/ethernet/raw_descriptor.rs | 5 ++-- src/ethernet/rx/descriptor.rs | 4 ++-- src/ethernet/rx/mod.rs | 4 ++-- src/ethernet/tx/descriptor.rs | 4 ++-- src/ethernet/tx/mod.rs | 7 +++--- src/ptp/mod.rs | 8 ++++--- src/ptp/subseconds.rs | 4 ++-- src/ptp/timestamp.rs | 8 ++++--- 12 files changed, 54 insertions(+), 44 deletions(-) diff --git a/src/ethernet/cache.rs b/src/ethernet/cache.rs index f4c78770..aa9ed036 100644 --- a/src/ethernet/cache.rs +++ b/src/ethernet/cache.rs @@ -1,5 +1,5 @@ -//! This implementation is derived from 0BSD-relicensed work done by -//! Johannes Draaijer for the +//! This implementation is derived from 0BSD-relicensed work done by +//! Johannes Draaijer for the //! [`stm32-eth`](https://github.com/stm32-rs/stm32-eth) project #[cfg(feature = "ptp")] diff --git a/src/ethernet/eth.rs b/src/ethernet/eth.rs index 3960eaaa..a6cf8866 100644 --- a/src/ethernet/eth.rs +++ b/src/ethernet/eth.rs @@ -19,8 +19,8 @@ //! > want to enable the cache, the simplest method would be to mark SRAM3 //! > as uncacheable via the MPU. //! -//! This implementation is derived from 0BSD-relicensed work done by -//! Johannes Draaijer for the +//! This implementation is derived from 0BSD-relicensed work done by +//! Johannes Draaijer for the //! [`stm32-eth`](https://github.com/stm32-rs/stm32-eth) project //! //! [quartiq/stabilizer]: https://github.com/quartiq/stabilizer @@ -44,12 +44,8 @@ use smoltcp::{ wire::EthernetAddress, }; -use super::rx::{ - RxDescriptor, RxDescriptorRing, RxError, RxPacket, RxRing, -}; -use super::tx::{ - TxDescriptor, TxDescriptorRing, TxError, TxPacket, TxRing, -}; +use super::rx::{RxDescriptor, RxDescriptorRing, RxError, RxPacket, RxRing}; +use super::tx::{TxDescriptor, TxDescriptorRing, TxError, TxPacket, TxRing}; use super::packet_id::PacketId; @@ -79,7 +75,6 @@ const _ASSERT_DESC_WORD_SKIP_SIZE: () = assert!(DESC_WORD_SKIP <= 0b111); // padding // const ETH_BUF_SIZE: usize = 1536; - pub const PTP_MAX_SIZE: usize = 76; pub const MAX_PTP_FOLLOWERS: usize = 16; @@ -523,10 +518,12 @@ pub unsafe fn new_unchecked<'rx, 'tx>( #[cfg(feature = "ptp")] packet_id_counter: 0, #[cfg(feature = "ptp")] - ptp_frame_buffer: [PtpFrameWithId{ ptp_frame: [0u8; PTP_MAX_SIZE], packet_id: None}; MAX_PTP_FOLLOWERS], + ptp_frame_buffer: [PtpFrameWithId { + ptp_frame: [0u8; PTP_MAX_SIZE], + packet_id: None, + }; MAX_PTP_FOLLOWERS], #[cfg(feature = "ptp")] write_pos: 0, - }; dma.rx_ring.start(&dma.eth_dma); dma.tx_ring.start(&dma.eth_dma); @@ -589,7 +586,6 @@ impl EthernetMAC { clock_range: self.clock_range, } } - } /// PHY Operations @@ -660,10 +656,12 @@ impl<'dma, 'rx> RxToken for EthRxToken<'dma, 'rx> { let mut packet = self.rx_ring.recv_next(meta).ok().unwrap(); #[cfg(feature = "ptp")] { - let ethertype = u16::from_be_bytes(packet[12..14].try_into().unwrap()); + let ethertype = + u16::from_be_bytes(packet[12..14].try_into().unwrap()); if ethertype == 0x88F7 { let packet_buf = &packet[14..]; - ((self.buf.ptp_frame)[0..packet_buf.len()]).copy_from_slice(packet_buf); + ((self.buf.ptp_frame)[0..packet_buf.len()]) + .copy_from_slice(packet_buf); self.buf.packet_id = meta; } } @@ -836,11 +834,19 @@ impl<'rx, 'tx> phy::Device for EthernetDMA<'rx, 'tx> { impl<'a, 'rx, 'tx> EthernetDMA<'rx, 'tx> { #[cfg(feature = "ptp")] - pub fn get_frame_from(&'a self, clock_identity: u64) -> Option<(&'a PtpFrameWithId, usize)> { + pub fn get_frame_from( + &'a self, + clock_identity: u64, + ) -> Option<(&'a PtpFrameWithId, usize)> { for i in 0..MAX_PTP_FOLLOWERS { if self.ptp_frame_buffer[i].packet_id.is_some() { // defmt::info!("buffer = {}", self.ptp_frame_buffer[i].0); - if u64::from_be_bytes(self.ptp_frame_buffer[i].ptp_frame[20..28].try_into().unwrap()) == clock_identity { + if u64::from_be_bytes( + self.ptp_frame_buffer[i].ptp_frame[20..28] + .try_into() + .unwrap(), + ) == clock_identity + { return Some((&self.ptp_frame_buffer[i], i)); } } @@ -856,7 +862,9 @@ impl<'a, 'rx, 'tx> EthernetDMA<'rx, 'tx> { #[cfg(feature = "ptp")] pub fn send_ptp_frame( frame: &[u8], - tx_option: Option< as phy::Device>::TxToken<'_>>, + tx_option: Option< + as phy::Device>::TxToken<'_>, + >, meta: PacketId, ) { if let Some(mut tx_token) = tx_option { diff --git a/src/ethernet/mod.rs b/src/ethernet/mod.rs index ec61d761..6ed2b75d 100644 --- a/src/ethernet/mod.rs +++ b/src/ethernet/mod.rs @@ -5,8 +5,8 @@ //! - SMSC LAN8742a //! - Micrel KSZ8081R //! -//! This implementation is derived from 0BSD-relicensed work done by -//! Johannes Draaijer for the +//! This implementation is derived from 0BSD-relicensed work done by +//! Johannes Draaijer for the //! [`stm32-eth`](https://github.com/stm32-rs/stm32-eth) project //! //! # Examples diff --git a/src/ethernet/packet_id.rs b/src/ethernet/packet_id.rs index 9d965f08..99419f1c 100644 --- a/src/ethernet/packet_id.rs +++ b/src/ethernet/packet_id.rs @@ -1,5 +1,5 @@ -//! This implementation is derived from 0BSD-relicensed work done by -//! Johannes Draaijer for the +//! This implementation is derived from 0BSD-relicensed work done by +//! Johannes Draaijer for the //! [`stm32-eth`](https://github.com/stm32-rs/stm32-eth) project /// A packet ID. diff --git a/src/ethernet/raw_descriptor.rs b/src/ethernet/raw_descriptor.rs index 3baeb52c..eb9bab15 100644 --- a/src/ethernet/raw_descriptor.rs +++ b/src/ethernet/raw_descriptor.rs @@ -1,8 +1,7 @@ -//! This implementation is derived from 0BSD-relicensed work done by -//! Johannes Draaijer for the +//! This implementation is derived from 0BSD-relicensed work done by +//! Johannes Draaijer for the //! [`stm32-eth`](https://github.com/stm32-rs/stm32-eth) project - //TODO remove // this is raw_descriptor use volatile_register::{RO, RW}; diff --git a/src/ethernet/rx/descriptor.rs b/src/ethernet/rx/descriptor.rs index 2ad3f4e7..8df578ab 100644 --- a/src/ethernet/rx/descriptor.rs +++ b/src/ethernet/rx/descriptor.rs @@ -1,5 +1,5 @@ -//! This implementation is derived from 0BSD-relicensed work done by -//! Johannes Draaijer for the +//! This implementation is derived from 0BSD-relicensed work done by +//! Johannes Draaijer for the //! [`stm32-eth`](https://github.com/stm32-rs/stm32-eth) project use core::sync::atomic::{self, Ordering}; diff --git a/src/ethernet/rx/mod.rs b/src/ethernet/rx/mod.rs index bd6dbc0c..bb40d116 100644 --- a/src/ethernet/rx/mod.rs +++ b/src/ethernet/rx/mod.rs @@ -1,5 +1,5 @@ -//! This implementation is derived from 0BSD-relicensed work done by -//! Johannes Draaijer for the +//! This implementation is derived from 0BSD-relicensed work done by +//! Johannes Draaijer for the //! [`stm32-eth`](https://github.com/stm32-rs/stm32-eth) project pub use descriptor::RxDescriptor; diff --git a/src/ethernet/tx/descriptor.rs b/src/ethernet/tx/descriptor.rs index da79e5a8..3b4cece1 100644 --- a/src/ethernet/tx/descriptor.rs +++ b/src/ethernet/tx/descriptor.rs @@ -1,5 +1,5 @@ -//! This implementation is derived from 0BSD-relicensed work done by -//! Johannes Draaijer for the +//! This implementation is derived from 0BSD-relicensed work done by +//! Johannes Draaijer for the //! [`stm32-eth`](https://github.com/stm32-rs/stm32-eth) project use core::sync::atomic::{self, Ordering}; diff --git a/src/ethernet/tx/mod.rs b/src/ethernet/tx/mod.rs index 8e37442b..036e375a 100644 --- a/src/ethernet/tx/mod.rs +++ b/src/ethernet/tx/mod.rs @@ -1,5 +1,5 @@ -//! This implementation is derived from 0BSD-relicensed work done by -//! Johannes Draaijer for the +//! This implementation is derived from 0BSD-relicensed work done by +//! Johannes Draaijer for the //! [`stm32-eth`](https://github.com/stm32-rs/stm32-eth) project use super::{raw_descriptor::DescriptorRing, PacketId}; @@ -383,8 +383,7 @@ impl TxPacket<'_> { impl Drop for TxPacket<'_> { fn drop(&mut self) { - self.desc - .send(self.packet_id, &self.buffer[..self.length]); + self.desc.send(self.packet_id, &self.buffer[..self.length]); TxRing::demand_poll(); } } diff --git a/src/ptp/mod.rs b/src/ptp/mod.rs index d16cbda1..34f90fdf 100644 --- a/src/ptp/mod.rs +++ b/src/ptp/mod.rs @@ -1,8 +1,8 @@ //! PTP access and configuration. //! //! See [`EthernetPTP`] for a more details. -//! This implementation is derived from 0BSD-relicensed work done by -//! Johannes Draaijer for the +//! This implementation is derived from 0BSD-relicensed work done by +//! Johannes Draaijer for the //! [`stm32-eth`](https://github.com/stm32-rs/stm32-eth) project use crate::ethernet::EthernetDMA; @@ -287,7 +287,9 @@ impl EthernetPTP { self.configure_target_time_interrupt(timestamp); core::future::poll_fn(|ctx| { - if (EthernetPTP::read_and_clear_interrupt_flag()) || (EthernetPTP::now().raw() >= timestamp.raw()) { + if (EthernetPTP::read_and_clear_interrupt_flag()) + || (EthernetPTP::now().raw() >= timestamp.raw()) + { Poll::Ready(()) } else { EthernetPTP::waker().register(ctx.waker()); diff --git a/src/ptp/subseconds.rs b/src/ptp/subseconds.rs index a5c013f5..74df5e93 100644 --- a/src/ptp/subseconds.rs +++ b/src/ptp/subseconds.rs @@ -1,5 +1,5 @@ -//! This implementation is derived from 0BSD-relicensed work done by -//! Johannes Draaijer for the +//! This implementation is derived from 0BSD-relicensed work done by +//! Johannes Draaijer for the //! [`stm32-eth`](https://github.com/stm32-rs/stm32-eth) project /// The amount of nanoseconds per second. diff --git a/src/ptp/timestamp.rs b/src/ptp/timestamp.rs index fda6145f..0bca05c5 100644 --- a/src/ptp/timestamp.rs +++ b/src/ptp/timestamp.rs @@ -1,5 +1,5 @@ -//! This implementation is derived from 0BSD-relicensed work done by -//! Johannes Draaijer for the +//! This implementation is derived from 0BSD-relicensed work done by +//! Johannes Draaijer for the //! [`stm32-eth`](https://github.com/stm32-rs/stm32-eth) project use super::{Subseconds, NANOS_PER_SECOND}; @@ -77,7 +77,9 @@ impl Timestamp { /// Get the raw subsecond value of this timestamp. pub const fn subseconds(&self) -> Subseconds { - Subseconds::new_unchecked(self.0.unsigned_abs() as u32 & Subseconds::MAX_VALUE) + Subseconds::new_unchecked( + self.0.unsigned_abs() as u32 & Subseconds::MAX_VALUE, + ) } /// Get the signed subsecond value of this timestamp. From 0d74740c38a52de8559aeedd2d15fb17200d6adc Mon Sep 17 00:00:00 2001 From: Harishguna Satgunarajah Date: Mon, 2 Oct 2023 10:03:35 -0700 Subject: [PATCH 61/77] adding copy for ptpframewithid --- src/ethernet/eth.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ethernet/eth.rs b/src/ethernet/eth.rs index a6cf8866..a797157e 100644 --- a/src/ethernet/eth.rs +++ b/src/ethernet/eth.rs @@ -100,6 +100,7 @@ mod emac_consts { /// with any TX or RX descriptors. pub struct PacketIdNotFound; +#[derive(Copy)] pub struct PtpFrameWithId { ptp_frame: [u8; PTP_MAX_SIZE], packet_id: Option, From c093c47668c75bc0df777e5fe5ae99867da4d484 Mon Sep 17 00:00:00 2001 From: Harishguna Satgunarajah Date: Mon, 2 Oct 2023 10:08:18 -0700 Subject: [PATCH 62/77] adding clone --- src/ethernet/eth.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ethernet/eth.rs b/src/ethernet/eth.rs index a797157e..c9960ea5 100644 --- a/src/ethernet/eth.rs +++ b/src/ethernet/eth.rs @@ -100,7 +100,7 @@ mod emac_consts { /// with any TX or RX descriptors. pub struct PacketIdNotFound; -#[derive(Copy)] +#[derive(Clone, Copy)] pub struct PtpFrameWithId { ptp_frame: [u8; PTP_MAX_SIZE], packet_id: Option, From e7b79259859549f787d835134672425cb9038879 Mon Sep 17 00:00:00 2001 From: Harishguna Satgunarajah Date: Mon, 2 Oct 2023 10:41:12 -0700 Subject: [PATCH 63/77] adding clippy allows --- src/ethernet/eth.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ethernet/eth.rs b/src/ethernet/eth.rs index c9960ea5..2d9302c3 100644 --- a/src/ethernet/eth.rs +++ b/src/ethernet/eth.rs @@ -62,13 +62,16 @@ const _TXDESC_SIZE: usize = core::mem::size_of::(); /// /// This is necessary as we only have a single Descriptor Skip Length /// value which applies to both TX and RX descriptors. +#[allow(clippy::assertions_on_constants)] const _ASSERT_DESCRIPTOR_SIZES: () = assert!(_RXDESC_SIZE == _TXDESC_SIZE); +#[allow(clippy::assertions_on_constants)] const _ASSERT_DESCRIPTOR_ALIGN: () = assert!(_RXDESC_SIZE % 4 == 0); const DESC_WORD_SKIP: u8 = ((_RXDESC_SIZE / 4) - raw_descriptor::DESC_SIZE) as u8; +#[allow(clippy::assertions_on_constants)] const _ASSERT_DESC_WORD_SKIP_SIZE: () = assert!(DESC_WORD_SKIP <= 0b111); // 6 DMAC, 6 SMAC, 4 q tag, 2 ethernet type II, 1500 ip MTU, 4 CRC, 2 @@ -200,6 +203,7 @@ pub fn new<'rx, 'tx>( /// # Safety /// /// `EthernetDMA` shall not be moved as it is initialised here +#[allow(clippy::too_many_arguments)] pub unsafe fn new_unchecked<'rx, 'tx>( eth_mac: stm32::ETHERNET_MAC, eth_mtl: stm32::ETHERNET_MTL, From ba4fd99c4076b78eb00d80570f26a8c64e2c4def Mon Sep 17 00:00:00 2001 From: Harishguna Satgunarajah Date: Mon, 2 Oct 2023 10:59:37 -0700 Subject: [PATCH 64/77] nucleo example fix --- examples/ethernet-nucleo-h743zi2.rs | 82 +++++++++++++++++++++-------- 1 file changed, 59 insertions(+), 23 deletions(-) diff --git a/examples/ethernet-nucleo-h743zi2.rs b/examples/ethernet-nucleo-h743zi2.rs index 29fa24fc..38972144 100644 --- a/examples/ethernet-nucleo-h743zi2.rs +++ b/examples/ethernet-nucleo-h743zi2.rs @@ -49,9 +49,22 @@ static TIME: AtomicU32 = AtomicU32::new(0); /// Locally administered MAC address const MAC_ADDRESS: [u8; 6] = [0x02, 0x00, 0x11, 0x22, 0x33, 0x44]; -/// Ethernet descriptor rings are a global singleton #[link_section = ".sram3.eth"] -static mut DES_RING: ethernet::DesRing<4, 4> = ethernet::DesRing::new(); +/// Doc +static mut TX_DESCRIPTORS: [TxDescriptor; NUM_DESCRIPTORS] = + [TxDescriptor::new(); NUM_DESCRIPTORS]; +#[link_section = ".sram3.eth"] +/// Doc +static mut TX_BUFFERS: [[u8; MTU + 2]; NUM_DESCRIPTORS] = + [[0u8; MTU + 2]; NUM_DESCRIPTORS]; +#[link_section = ".sram3.eth"] +/// Doc +static mut RX_DESCRIPTORS: [RxDescriptor; NUM_DESCRIPTORS] = + [RxDescriptor::new(); NUM_DESCRIPTORS]; +#[link_section = ".sram3.eth"] +/// Doc +static mut RX_BUFFERS: [[u8; MTU + 2]; NUM_DESCRIPTORS] = + [[0u8; MTU + 2]; NUM_DESCRIPTORS]; // the program entry point #[entry] @@ -104,6 +117,17 @@ fn main() -> ! { let rmii_txd0 = gpiog.pg13.into_alternate(); let rmii_txd1 = gpiob.pb13.into_alternate(); + let rmii_pins = ( + rmii_ref_clk, + rmii_mdio, + rmii_mdc, + rmii_crs_dv, + rmii_rxd0, + rmii_rxd1, + rmii_tx_en, + rmii_txd0, + rmii_txd1, + ); // Initialise ethernet... assert_eq!(ccdr.clocks.hclk().raw(), 200_000_000); // HCLK 200MHz assert_eq!(ccdr.clocks.pclk1().raw(), 100_000_000); // PCLK 100MHz @@ -111,36 +135,48 @@ fn main() -> ! { assert_eq!(ccdr.clocks.pclk4().raw(), 100_000_000); // PCLK 100MHz let mac_addr = smoltcp::wire::EthernetAddress::from_bytes(&MAC_ADDRESS); - let (_eth_dma, eth_mac) = unsafe { - ethernet::new( - dp.ETHERNET_MAC, - dp.ETHERNET_MTL, - dp.ETHERNET_DMA, - ( - rmii_ref_clk, - rmii_mdio, - rmii_mdc, - rmii_crs_dv, - rmii_rxd0, - rmii_rxd1, - rmii_tx_en, - rmii_txd0, - rmii_txd1, - ), - &mut DES_RING, - mac_addr, - ccdr.peripheral.ETH1MAC, - &ccdr.clocks, + let (rx_ring, tx_ring) = { + // 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(); NUM_DESCRIPTORS]) }; + // let rx_buf = unsafe { RX_BUFFERS.write([[0u8; MTU + 2]; NUM_DESCRIPTORS]) }; + + ( + RxDescriptorRing::new(unsafe { &mut RX_DESCRIPTORS }, unsafe { + &mut RX_BUFFERS + }), + TxDescriptorRing::new(unsafe { &mut TX_DESCRIPTORS }, unsafe { + &mut TX_BUFFERS + }), ) }; + let ethernet::Parts { + dma: mut eth_dma, + mac: mut eth_mac, + ptp, + } = ethernet::new( + dp.ETHERNET_MAC, + dp.ETHERNET_MTL, + dp.ETHERNET_DMA, + rmii_pins, + rx_ring, + tx_ring, + mac_addr, + ccdr.peripheral.ETH1MAC, + &ccdr.clocks, + ); + + let start_addend = ptp.addend(); + eth_dma.enable_interrupt(); + // Initialise ethernet PHY... let mut lan8742a = ethernet::phy::LAN8742A::new(eth_mac.set_phy_addr(0)); lan8742a.phy_reset(); lan8742a.phy_init(); unsafe { - ethernet::enable_interrupt(); cp.NVIC.set_priority(stm32::Interrupt::ETH, 196); // Mid prio cortex_m::peripheral::NVIC::unmask(stm32::Interrupt::ETH); } From 79fedc635070d19427d69b4863dde2d0c6017eca Mon Sep 17 00:00:00 2001 From: Harishguna Satgunarajah Date: Mon, 2 Oct 2023 11:10:24 -0700 Subject: [PATCH 65/77] 735 fix --- examples/ethernet-rtic-stm32h735g-dk.rs | 81 ++++++++++++++++++------- 1 file changed, 58 insertions(+), 23 deletions(-) diff --git a/examples/ethernet-rtic-stm32h735g-dk.rs b/examples/ethernet-rtic-stm32h735g-dk.rs index 033a8204..dbe4e93e 100644 --- a/examples/ethernet-rtic-stm32h735g-dk.rs +++ b/examples/ethernet-rtic-stm32h735g-dk.rs @@ -44,8 +44,22 @@ static TIME: AtomicU32 = AtomicU32::new(0); const MAC_ADDRESS: [u8; 6] = [0x02, 0x00, 0x11, 0x22, 0x33, 0x44]; /// Ethernet descriptor rings are a global singleton -#[link_section = ".axisram.eth"] -static mut DES_RING: ethernet::DesRing<4, 4> = ethernet::DesRing::new(); +#[link_section = ".sram3.eth"] +/// Doc +static mut TX_DESCRIPTORS: [TxDescriptor; NUM_DESCRIPTORS] = + [TxDescriptor::new(); NUM_DESCRIPTORS]; +#[link_section = ".sram3.eth"] +/// Doc +static mut TX_BUFFERS: [[u8; MTU + 2]; NUM_DESCRIPTORS] = + [[0u8; MTU + 2]; NUM_DESCRIPTORS]; +#[link_section = ".sram3.eth"] +/// Doc +static mut RX_DESCRIPTORS: [RxDescriptor; NUM_DESCRIPTORS] = + [RxDescriptor::new(); NUM_DESCRIPTORS]; +#[link_section = ".sram3.eth"] +/// Doc +static mut RX_BUFFERS: [[u8; MTU + 2]; NUM_DESCRIPTORS] = + [[0u8; MTU + 2]; NUM_DESCRIPTORS]; // This data will be held by Net through a mutable reference pub struct NetStorageStatic<'a> { @@ -156,36 +170,57 @@ mod app { assert_eq!(ccdr.clocks.pclk4().raw(), 100_000_000); // PCLK 100MHz let mac_addr = smoltcp::wire::EthernetAddress::from_bytes(&MAC_ADDRESS); - let (eth_dma, eth_mac) = unsafe { - ethernet::new( - ctx.device.ETHERNET_MAC, - ctx.device.ETHERNET_MTL, - ctx.device.ETHERNET_DMA, - ( - rmii_ref_clk, - rmii_mdio, - rmii_mdc, - rmii_crs_dv, - rmii_rxd0, - rmii_rxd1, - rmii_tx_en, - rmii_txd0, - rmii_txd1, - ), - &mut DES_RING, - mac_addr, - ccdr.peripheral.ETH1MAC, - &ccdr.clocks, + let (rx_ring, tx_ring) = { + // 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(); NUM_DESCRIPTORS]) }; + // let rx_buf = unsafe { RX_BUFFERS.write([[0u8; MTU + 2]; NUM_DESCRIPTORS]) }; + + ( + RxDescriptorRing::new(unsafe { &mut RX_DESCRIPTORS }, unsafe { + &mut RX_BUFFERS + }), + TxDescriptorRing::new(unsafe { &mut TX_DESCRIPTORS }, unsafe { + &mut TX_BUFFERS + }), ) }; + let ethernet::Parts { + dma: mut eth_dma, + mac: mut eth_mac, + ptp, + } = ethernet::new( + dp.ETHERNET_MAC, + dp.ETHERNET_MTL, + dp.ETHERNET_DMA, + ( + rmii_ref_clk, + rmii_mdio, + rmii_mdc, + rmii_crs_dv, + rmii_rxd0, + rmii_rxd1, + rmii_tx_en, + rmii_txd0, + rmii_txd1, + ), + rx_ring, + tx_ring, + mac_addr, + ccdr.peripheral.ETH1MAC, + &ccdr.clocks, + ); + let start_addend = ptp.addend(); + eth_dma.enable_interrupt(); + // Initialise ethernet PHY... let mut lan8742a = ethernet::phy::LAN8742A::new(eth_mac); lan8742a.phy_reset(); lan8742a.phy_init(); // The eth_dma should not be used until the PHY reports the link is up - unsafe { ethernet::enable_interrupt() }; // unsafe: mutable reference to static storage, we only do this once let store = unsafe { From c00cebc5fc8a42b8ab74329a00ae8d368ca8dd18 Mon Sep 17 00:00:00 2001 From: Harishguna Satgunarajah Date: Mon, 2 Oct 2023 11:14:54 -0700 Subject: [PATCH 66/77] cargo fmt --- examples/ethernet-rtic-stm32h735g-dk.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/ethernet-rtic-stm32h735g-dk.rs b/examples/ethernet-rtic-stm32h735g-dk.rs index dbe4e93e..366c7749 100644 --- a/examples/ethernet-rtic-stm32h735g-dk.rs +++ b/examples/ethernet-rtic-stm32h735g-dk.rs @@ -194,7 +194,7 @@ mod app { } = ethernet::new( dp.ETHERNET_MAC, dp.ETHERNET_MTL, - dp.ETHERNET_DMA, + dp.ETHERNET_DMA, ( rmii_ref_clk, rmii_mdio, @@ -221,7 +221,6 @@ mod app { lan8742a.phy_init(); // The eth_dma should not be used until the PHY reports the link is up - // unsafe: mutable reference to static storage, we only do this once let store = unsafe { let store_ptr = STORE.as_mut_ptr(); From 986170e4fe0ff51bd94e94e05e1518b302429e3e Mon Sep 17 00:00:00 2001 From: Harishguna Satgunarajah Date: Mon, 2 Oct 2023 11:25:12 -0700 Subject: [PATCH 67/77] adding desc num --- examples/ethernet-nucleo-h743zi2.rs | 2 ++ examples/ethernet-rtic-stm32h735g-dk.rs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/examples/ethernet-nucleo-h743zi2.rs b/examples/ethernet-nucleo-h743zi2.rs index 38972144..4c7ef802 100644 --- a/examples/ethernet-nucleo-h743zi2.rs +++ b/examples/ethernet-nucleo-h743zi2.rs @@ -49,6 +49,8 @@ static TIME: AtomicU32 = AtomicU32::new(0); /// Locally administered MAC address const MAC_ADDRESS: [u8; 6] = [0x02, 0x00, 0x11, 0x22, 0x33, 0x44]; +/// DesRing TD +const NUM_DESCRIPTORS: usize = 8; #[link_section = ".sram3.eth"] /// Doc static mut TX_DESCRIPTORS: [TxDescriptor; NUM_DESCRIPTORS] = diff --git a/examples/ethernet-rtic-stm32h735g-dk.rs b/examples/ethernet-rtic-stm32h735g-dk.rs index 366c7749..b5124383 100644 --- a/examples/ethernet-rtic-stm32h735g-dk.rs +++ b/examples/ethernet-rtic-stm32h735g-dk.rs @@ -43,6 +43,8 @@ static TIME: AtomicU32 = AtomicU32::new(0); /// Locally administered MAC address const MAC_ADDRESS: [u8; 6] = [0x02, 0x00, 0x11, 0x22, 0x33, 0x44]; +/// DesRing TD +const NUM_DESCRIPTORS: usize = 8; /// Ethernet descriptor rings are a global singleton #[link_section = ".sram3.eth"] /// Doc From ad717ef92e175b537151f4dd3658e62038227a0a Mon Sep 17 00:00:00 2001 From: Harishguna Satgunarajah Date: Mon, 2 Oct 2023 12:13:59 -0700 Subject: [PATCH 68/77] clippy passing --- examples/ethernet-nucleo-h743zi2.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/ethernet-nucleo-h743zi2.rs b/examples/ethernet-nucleo-h743zi2.rs index 4c7ef802..ffb7d02a 100644 --- a/examples/ethernet-nucleo-h743zi2.rs +++ b/examples/ethernet-nucleo-h743zi2.rs @@ -23,7 +23,7 @@ mod utilities; use log::info; use stm32h7xx_hal::rcc::CoreClocks; -use stm32h7xx_hal::{ethernet, ethernet::PHY}; +use stm32h7xx_hal::{ethernet, ethernet::{PHY, MTU, RxDescriptor, RxDescriptorRing, TxDescriptor, TxDescriptorRing}}; use stm32h7xx_hal::{prelude::*, stm32, stm32::interrupt}; /// Configure SYSTICK for 1ms timebase @@ -155,9 +155,9 @@ fn main() -> ! { }; let ethernet::Parts { - dma: mut eth_dma, - mac: mut eth_mac, - ptp, + dma: eth_dma, + mac: eth_mac, + ptp: _ptp, } = ethernet::new( dp.ETHERNET_MAC, dp.ETHERNET_MTL, @@ -170,7 +170,7 @@ fn main() -> ! { &ccdr.clocks, ); - let start_addend = ptp.addend(); + // let start_addend = ptp.addend(); eth_dma.enable_interrupt(); // Initialise ethernet PHY... @@ -218,7 +218,7 @@ fn main() -> ! { #[interrupt] fn ETH() { - unsafe { ethernet::interrupt_handler() } + ethernet::eth_interrupt_handler(); } #[exception] From ad8f8b411be6cd80ffa9238caa88033a4b18ac64 Mon Sep 17 00:00:00 2001 From: Harishguna Satgunarajah Date: Mon, 2 Oct 2023 12:14:47 -0700 Subject: [PATCH 69/77] fmt --- examples/ethernet-nucleo-h743zi2.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/examples/ethernet-nucleo-h743zi2.rs b/examples/ethernet-nucleo-h743zi2.rs index ffb7d02a..c0b52fd9 100644 --- a/examples/ethernet-nucleo-h743zi2.rs +++ b/examples/ethernet-nucleo-h743zi2.rs @@ -23,7 +23,13 @@ mod utilities; use log::info; use stm32h7xx_hal::rcc::CoreClocks; -use stm32h7xx_hal::{ethernet, ethernet::{PHY, MTU, RxDescriptor, RxDescriptorRing, TxDescriptor, TxDescriptorRing}}; +use stm32h7xx_hal::{ + ethernet, + ethernet::{ + RxDescriptor, RxDescriptorRing, TxDescriptor, TxDescriptorRing, MTU, + PHY, + }, +}; use stm32h7xx_hal::{prelude::*, stm32, stm32::interrupt}; /// Configure SYSTICK for 1ms timebase @@ -218,7 +224,7 @@ fn main() -> ! { #[interrupt] fn ETH() { - ethernet::eth_interrupt_handler(); + ethernet::eth_interrupt_handler(); } #[exception] From e687a30f9e217027558c45139047b57722e99242 Mon Sep 17 00:00:00 2001 From: Harishguna Satgunarajah Date: Mon, 2 Oct 2023 12:22:47 -0700 Subject: [PATCH 70/77] removing bad defaults --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ce2e2f54..e57c270a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -116,9 +116,9 @@ default-features = false features = ["medium-ethernet", "proto-ipv4", "proto-ipv6", "socket-raw"] [features] -default = ["rt", "stm32h743v", +default = ["rt", "ethernet", - "device-selected", "ptp", "async-await"] + "device-selected", "async-await"] device-selected = [] revision_v = [] rm0433 = ["gpio-h747"] # aka. "single core" devices From 66389129ea29cdc587416eb59407c65e245bf57b Mon Sep 17 00:00:00 2001 From: Harishguna Satgunarajah Date: Mon, 2 Oct 2023 14:00:54 -0700 Subject: [PATCH 71/77] fixing all ci micro checks/examples --- examples/ethernet-nucleo-h743zi2.rs | 16 +++ examples/ethernet-rtic-stm32h735g-dk.rs | 68 ++++++++---- examples/ethernet-rtic-stm32h747i-disco.rs | 114 ++++++++++++++++----- examples/ethernet-stm32h747i-disco.rs | 109 +++++++++++++++----- src/ethernet/eth.rs | 2 + src/lib.rs | 7 +- 6 files changed, 242 insertions(+), 74 deletions(-) diff --git a/examples/ethernet-nucleo-h743zi2.rs b/examples/ethernet-nucleo-h743zi2.rs index c0b52fd9..e2db4852 100644 --- a/examples/ethernet-nucleo-h743zi2.rs +++ b/examples/ethernet-nucleo-h743zi2.rs @@ -160,6 +160,7 @@ fn main() -> ! { ) }; + #[cfg(feature = "ptp")] let ethernet::Parts { dma: eth_dma, mac: eth_mac, @@ -176,6 +177,21 @@ fn main() -> ! { &ccdr.clocks, ); + #[cfg(not(feature = "ptp"))] + let ethernet::Parts { + dma: eth_dma, + mac: eth_mac, + } = ethernet::new( + dp.ETHERNET_MAC, + dp.ETHERNET_MTL, + dp.ETHERNET_DMA, + rmii_pins, + rx_ring, + tx_ring, + mac_addr, + ccdr.peripheral.ETH1MAC, + &ccdr.clocks, + ); // let start_addend = ptp.addend(); eth_dma.enable_interrupt(); diff --git a/examples/ethernet-rtic-stm32h735g-dk.rs b/examples/ethernet-rtic-stm32h735g-dk.rs index b5124383..8814c385 100644 --- a/examples/ethernet-rtic-stm32h735g-dk.rs +++ b/examples/ethernet-rtic-stm32h735g-dk.rs @@ -23,7 +23,12 @@ use smoltcp::iface::{Config, Interface, SocketSet, SocketStorage}; use smoltcp::time::Instant; use smoltcp::wire::{HardwareAddress, IpAddress, IpCidr}; -use stm32h7xx_hal::{ethernet, rcc::CoreClocks, stm32}; +use stm32h7xx_hal::{rcc::CoreClocks, stm32}; +use stm32h7xx_hal::{ethernet, + ethernet::{ + RxDescriptor, RxDescriptorRing, TxDescriptor, TxDescriptorRing, MTU, + }, +}; /// Configure SYSTICK for 1ms timebase fn systick_init(mut syst: stm32::SYST, clocks: CoreClocks) { @@ -73,13 +78,13 @@ static mut STORE: MaybeUninit = MaybeUninit::uninit(); pub struct Net<'a> { iface: Interface, - ethdev: ethernet::EthernetDMA<4, 4>, + ethdev: ethernet::EthernetDMA<'a, 'a>, sockets: SocketSet<'a>, } impl<'a> Net<'a> { pub fn new( store: &'a mut NetStorageStatic<'a>, - mut ethdev: ethernet::EthernetDMA<4, 4>, + mut ethdev: ethernet::EthernetDMA<'a, 'a>, ethernet_addr: HardwareAddress, now: Instant, ) -> Self { @@ -165,6 +170,18 @@ mod app { let rmii_txd0 = gpiob.pb12.into_alternate(); let rmii_txd1 = gpiob.pb13.into_alternate(); + let rmii_pins = ( + rmii_ref_clk, + rmii_mdio, + rmii_mdc, + rmii_crs_dv, + rmii_rxd0, + rmii_rxd1, + rmii_tx_en, + rmii_txd0, + rmii_txd1, + ); + // Initialise ethernet... assert_eq!(ccdr.clocks.hclk().raw(), 200_000_000); // HCLK 200MHz assert_eq!(ccdr.clocks.pclk1().raw(), 100_000_000); // PCLK 100MHz @@ -189,32 +206,39 @@ mod app { ) }; + #[cfg(feature = "ptp")] let ethernet::Parts { - dma: mut eth_dma, - mac: mut eth_mac, - ptp, + dma: eth_dma, + mac: eth_mac, + ptp: _ptp, } = ethernet::new( - dp.ETHERNET_MAC, - dp.ETHERNET_MTL, - dp.ETHERNET_DMA, - ( - rmii_ref_clk, - rmii_mdio, - rmii_mdc, - rmii_crs_dv, - rmii_rxd0, - rmii_rxd1, - rmii_tx_en, - rmii_txd0, - rmii_txd1, - ), + ctx.device.ETHERNET_MAC, + ctx.device.ETHERNET_MTL, + ctx.device.ETHERNET_DMA, + rmii_pins, + rx_ring, + tx_ring, + mac_addr, + ccdr.peripheral.ETH1MAC, + &ccdr.clocks, + ); + + #[cfg(not(feature = "ptp"))] + let ethernet::Parts { + dma: eth_dma, + mac: eth_mac, + } = ethernet::new( + ctx.device.ETHERNET_MAC, + ctx.device.ETHERNET_MTL, + ctx.device.ETHERNET_DMA, + rmii_pins, rx_ring, tx_ring, mac_addr, ccdr.peripheral.ETH1MAC, &ccdr.clocks, ); - let start_addend = ptp.addend(); + // let start_addend = ptp.addend(); eth_dma.enable_interrupt(); // Initialise ethernet PHY... @@ -267,7 +291,7 @@ mod app { #[task(binds = ETH, local = [net])] fn ethernet_event(ctx: ethernet_event::Context) { - unsafe { ethernet::interrupt_handler() } + ethernet::eth_interrupt_handler(); let time = TIME.load(Ordering::Relaxed); ctx.local.net.poll(time as i64); diff --git a/examples/ethernet-rtic-stm32h747i-disco.rs b/examples/ethernet-rtic-stm32h747i-disco.rs index 7d6525d7..afa34ed8 100644 --- a/examples/ethernet-rtic-stm32h747i-disco.rs +++ b/examples/ethernet-rtic-stm32h747i-disco.rs @@ -30,7 +30,13 @@ use smoltcp::iface::{Config, Interface, SocketSet, SocketStorage}; use smoltcp::time::Instant; use smoltcp::wire::{HardwareAddress, IpAddress, IpCidr}; -use stm32h7xx_hal::{ethernet, rcc::CoreClocks, stm32}; +use stm32h7xx_hal::{rcc::CoreClocks, stm32}; + +use stm32h7xx_hal::{ethernet, + ethernet::{ + RxDescriptor, RxDescriptorRing, TxDescriptor, TxDescriptorRing, MTU, + }, +}; /// Configure SYSTICK for 1ms timebase fn systick_init(mut syst: stm32::SYST, clocks: CoreClocks) { @@ -50,9 +56,25 @@ static TIME: AtomicU32 = AtomicU32::new(0); /// Locally administered MAC address const MAC_ADDRESS: [u8; 6] = [0x02, 0x00, 0x11, 0x22, 0x33, 0x44]; +/// DesRing TD +const NUM_DESCRIPTORS: usize = 8; /// Ethernet descriptor rings are a global singleton #[link_section = ".sram3.eth"] -static mut DES_RING: ethernet::DesRing<4, 4> = ethernet::DesRing::new(); +/// Doc +static mut TX_DESCRIPTORS: [TxDescriptor; NUM_DESCRIPTORS] = + [TxDescriptor::new(); NUM_DESCRIPTORS]; +#[link_section = ".sram3.eth"] +/// Doc +static mut TX_BUFFERS: [[u8; MTU + 2]; NUM_DESCRIPTORS] = + [[0u8; MTU + 2]; NUM_DESCRIPTORS]; +#[link_section = ".sram3.eth"] +/// Doc +static mut RX_DESCRIPTORS: [RxDescriptor; NUM_DESCRIPTORS] = + [RxDescriptor::new(); NUM_DESCRIPTORS]; +#[link_section = ".sram3.eth"] +/// Doc +static mut RX_BUFFERS: [[u8; MTU + 2]; NUM_DESCRIPTORS] = + [[0u8; MTU + 2]; NUM_DESCRIPTORS]; // This data will be held by Net through a mutable reference pub struct NetStorageStatic<'a> { @@ -64,13 +86,13 @@ static mut STORE: MaybeUninit = MaybeUninit::uninit(); pub struct Net<'a> { iface: Interface, - ethdev: ethernet::EthernetDMA<4, 4>, + ethdev: ethernet::EthernetDMA<'a, 'a>, sockets: SocketSet<'a>, } impl<'a> Net<'a> { pub fn new( store: &'a mut NetStorageStatic<'a>, - mut ethdev: ethernet::EthernetDMA<4, 4>, + mut ethdev: ethernet::EthernetDMA<'a, 'a>, ethernet_addr: HardwareAddress, now: Instant, ) -> Self { @@ -159,6 +181,17 @@ mod app { let rmii_txd0 = gpiog.pg13.into_alternate(); let rmii_txd1 = gpiog.pg12.into_alternate(); // STM32H747I-DISCO + let rmii_pins = ( + rmii_ref_clk, + rmii_mdio, + rmii_mdc, + rmii_crs_dv, + rmii_rxd0, + rmii_rxd1, + rmii_tx_en, + rmii_txd0, + rmii_txd1, + ); // Initialise ethernet... assert_eq!(ccdr.clocks.hclk().raw(), 200_000_000); // HCLK 200MHz assert_eq!(ccdr.clocks.pclk1().raw(), 100_000_000); // PCLK 100MHz @@ -166,37 +199,64 @@ mod app { assert_eq!(ccdr.clocks.pclk4().raw(), 100_000_000); // PCLK 100MHz let mac_addr = smoltcp::wire::EthernetAddress::from_bytes(&MAC_ADDRESS); - let (eth_dma, eth_mac) = unsafe { - ethernet::new( - ctx.device.ETHERNET_MAC, - ctx.device.ETHERNET_MTL, - ctx.device.ETHERNET_DMA, - ( - rmii_ref_clk, - rmii_mdio, - rmii_mdc, - rmii_crs_dv, - rmii_rxd0, - rmii_rxd1, - rmii_tx_en, - rmii_txd0, - rmii_txd1, - ), - &mut DES_RING, - mac_addr, - ccdr.peripheral.ETH1MAC, - &ccdr.clocks, + let (rx_ring, tx_ring) = { + // 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(); NUM_DESCRIPTORS]) }; + // let rx_buf = unsafe { RX_BUFFERS.write([[0u8; MTU + 2]; NUM_DESCRIPTORS]) }; + + ( + RxDescriptorRing::new(unsafe { &mut RX_DESCRIPTORS }, unsafe { + &mut RX_BUFFERS + }), + TxDescriptorRing::new(unsafe { &mut TX_DESCRIPTORS }, unsafe { + &mut TX_BUFFERS + }), ) }; + #[cfg(feature = "ptp")] + let ethernet::Parts { + dma: eth_dma, + mac: eth_mac, + ptp: _ptp, + } = ethernet::new( + ctx.device.ETHERNET_MAC, + ctx.device.ETHERNET_MTL, + ctx.device.ETHERNET_DMA, + rmii_pins, + rx_ring, + tx_ring, + mac_addr, + ccdr.peripheral.ETH1MAC, + &ccdr.clocks, + ); + + #[cfg(not(feature = "ptp"))] + let ethernet::Parts { + dma: eth_dma, + mac: eth_mac, + } = ethernet::new( + ctx.device.ETHERNET_MAC, + ctx.device.ETHERNET_MTL, + ctx.device.ETHERNET_DMA, + rmii_pins, + rx_ring, + tx_ring, + mac_addr, + ccdr.peripheral.ETH1MAC, + &ccdr.clocks, + ); + // let start_addend = ptp.addend(); + eth_dma.enable_interrupt(); + // Initialise ethernet PHY... let mut lan8742a = ethernet::phy::LAN8742A::new(eth_mac); lan8742a.phy_reset(); lan8742a.phy_init(); // The eth_dma should not be used until the PHY reports the link is up - unsafe { ethernet::enable_interrupt() }; - // unsafe: mutable reference to static storage, we only do this once let store = unsafe { let store_ptr = STORE.as_mut_ptr(); @@ -241,7 +301,7 @@ mod app { #[task(binds = ETH, local = [net])] fn ethernet_event(ctx: ethernet_event::Context) { - unsafe { ethernet::interrupt_handler() } + ethernet::eth_interrupt_handler(); let time = TIME.load(Ordering::Relaxed); ctx.local.net.poll(time as i64); diff --git a/examples/ethernet-stm32h747i-disco.rs b/examples/ethernet-stm32h747i-disco.rs index 206c4e43..06159f86 100644 --- a/examples/ethernet-stm32h747i-disco.rs +++ b/examples/ethernet-stm32h747i-disco.rs @@ -20,15 +20,37 @@ mod utilities; use log::info; -use stm32h7xx_hal::{ethernet, ethernet::PHY}; +use stm32h7xx_hal::{ethernet, + ethernet::{ + RxDescriptor, RxDescriptorRing, TxDescriptor, TxDescriptorRing, MTU, + PHY, + }, +}; use stm32h7xx_hal::{prelude::*, stm32, stm32::interrupt}; /// Locally administered MAC address const MAC_ADDRESS: [u8; 6] = [0x02, 0x00, 0x11, 0x22, 0x33, 0x44]; +/// DesRing TD +const NUM_DESCRIPTORS: usize = 8; /// Ethernet descriptor rings are a global singleton #[link_section = ".sram3.eth"] -static mut DES_RING: ethernet::DesRing<4, 4> = ethernet::DesRing::new(); +/// Doc +static mut TX_DESCRIPTORS: [TxDescriptor; NUM_DESCRIPTORS] = + [TxDescriptor::new(); NUM_DESCRIPTORS]; +#[link_section = ".sram3.eth"] +/// Doc +static mut TX_BUFFERS: [[u8; MTU + 2]; NUM_DESCRIPTORS] = + [[0u8; MTU + 2]; NUM_DESCRIPTORS]; +#[link_section = ".sram3.eth"] +/// Doc +static mut RX_DESCRIPTORS: [RxDescriptor; NUM_DESCRIPTORS] = + [RxDescriptor::new(); NUM_DESCRIPTORS]; +#[link_section = ".sram3.eth"] +/// Doc +static mut RX_BUFFERS: [[u8; MTU + 2]; NUM_DESCRIPTORS] = + [[0u8; MTU + 2]; NUM_DESCRIPTORS]; + // the program entry point #[entry] @@ -77,6 +99,17 @@ fn main() -> ! { let rmii_txd0 = gpiog.pg13.into_alternate(); let rmii_txd1 = gpiog.pg12.into_alternate(); + let rmii_pins = ( + rmii_ref_clk, + rmii_mdio, + rmii_mdc, + rmii_crs_dv, + rmii_rxd0, + rmii_rxd1, + rmii_tx_en, + rmii_txd0, + rmii_txd1, + ); // Initialise ethernet... assert_eq!(ccdr.clocks.hclk().raw(), 200_000_000); // HCLK 200MHz assert_eq!(ccdr.clocks.pclk1().raw(), 100_000_000); // PCLK 100MHz @@ -84,36 +117,64 @@ fn main() -> ! { assert_eq!(ccdr.clocks.pclk4().raw(), 100_000_000); // PCLK 100MHz let mac_addr = smoltcp::wire::EthernetAddress::from_bytes(&MAC_ADDRESS); - let (_eth_dma, eth_mac) = unsafe { - ethernet::new( - dp.ETHERNET_MAC, - dp.ETHERNET_MTL, - dp.ETHERNET_DMA, - ( - rmii_ref_clk, - rmii_mdio, - rmii_mdc, - rmii_crs_dv, - rmii_rxd0, - rmii_rxd1, - rmii_tx_en, - rmii_txd0, - rmii_txd1, - ), - &mut DES_RING, - mac_addr, - ccdr.peripheral.ETH1MAC, - &ccdr.clocks, + let (rx_ring, tx_ring) = { + // 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(); NUM_DESCRIPTORS]) }; + // let rx_buf = unsafe { RX_BUFFERS.write([[0u8; MTU + 2]; NUM_DESCRIPTORS]) }; + + ( + RxDescriptorRing::new(unsafe { &mut RX_DESCRIPTORS }, unsafe { + &mut RX_BUFFERS + }), + TxDescriptorRing::new(unsafe { &mut TX_DESCRIPTORS }, unsafe { + &mut TX_BUFFERS + }), ) }; + #[cfg(feature = "ptp")] + let ethernet::Parts { + dma: eth_dma, + mac: eth_mac, + ptp: _ptp, + } = ethernet::new( + dp.ETHERNET_MAC, + dp.ETHERNET_MTL, + dp.ETHERNET_DMA, + rmii_pins, + rx_ring, + tx_ring, + mac_addr, + ccdr.peripheral.ETH1MAC, + &ccdr.clocks, + ); + + #[cfg(not(feature = "ptp"))] + let ethernet::Parts { + dma: eth_dma, + mac: eth_mac, + } = ethernet::new( + dp.ETHERNET_MAC, + dp.ETHERNET_MTL, + dp.ETHERNET_DMA, + rmii_pins, + rx_ring, + tx_ring, + mac_addr, + ccdr.peripheral.ETH1MAC, + &ccdr.clocks, + ); + // let start_addend = ptp.addend(); + eth_dma.enable_interrupt(); + // Initialise ethernet PHY... let mut lan8742a = ethernet::phy::LAN8742A::new(eth_mac.set_phy_addr(0)); lan8742a.phy_reset(); lan8742a.phy_init(); unsafe { - ethernet::enable_interrupt(); cp.NVIC.set_priority(stm32::Interrupt::ETH, 196); // Mid prio cortex_m::peripheral::NVIC::unmask(stm32::Interrupt::ETH); } @@ -143,7 +204,7 @@ fn main() -> ! { #[interrupt] fn ETH() { - unsafe { ethernet::interrupt_handler() } + ethernet::eth_interrupt_handler(); } #[exception] diff --git a/src/ethernet/eth.rs b/src/ethernet/eth.rs index 2d9302c3..5a3e1c71 100644 --- a/src/ethernet/eth.rs +++ b/src/ethernet/eth.rs @@ -758,6 +758,7 @@ impl<'a, 'rx, 'tx> phy::Device for &'a mut EthernetDMA<'rx, 'tx> { fn transmit(&mut self, _timestamp: Instant) -> Option> { if self.tx_available() { + #[cfg(feature = "ptp")] let tx_packet_id = self.next_packet_id(); let EthernetDMA { tx_ring, .. } = self; @@ -823,6 +824,7 @@ impl<'rx, 'tx> phy::Device for EthernetDMA<'rx, 'tx> { fn transmit(&mut self, _timestamp: Instant) -> Option> { if self.tx_available() { + #[cfg(feature = "ptp")] let tx_packet_id = self.next_packet_id(); let EthernetDMA { tx_ring, .. } = self; diff --git a/src/lib.rs b/src/lib.rs index bca57f9f..08cc68f3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -142,7 +142,12 @@ pub use crate::stm32 as device; #[cfg_attr(docsrs, doc(cfg(feature = "rt")))] pub use crate::stm32::interrupt; -#[cfg(feature = "device-selected")] +#[cfg(all( + feature = "device-selected", + feature = "ethernet", + not(feature = "rm0455") +))] +#[cfg_attr(docsrs, doc(cfg(feature = "ethernet")))] pub mod ptp; #[cfg(feature = "device-selected")] From ec810901f52fc0be22ca15c0cc61c02a65c6a295 Mon Sep 17 00:00:00 2001 From: Harishguna Satgunarajah Date: Mon, 2 Oct 2023 14:20:00 -0700 Subject: [PATCH 72/77] clippy check fix --- examples/ethernet-rtic-stm32h735g-dk.rs | 5 +++-- examples/ethernet-rtic-stm32h747i-disco.rs | 5 +++-- examples/ethernet-stm32h747i-disco.rs | 6 +++--- src/ethernet/eth.rs | 12 ++++++++---- src/ethernet/tx/mod.rs | 1 + src/lib.rs | 3 ++- 6 files changed, 20 insertions(+), 12 deletions(-) diff --git a/examples/ethernet-rtic-stm32h735g-dk.rs b/examples/ethernet-rtic-stm32h735g-dk.rs index 8814c385..a4e78c4b 100644 --- a/examples/ethernet-rtic-stm32h735g-dk.rs +++ b/examples/ethernet-rtic-stm32h735g-dk.rs @@ -23,12 +23,13 @@ use smoltcp::iface::{Config, Interface, SocketSet, SocketStorage}; use smoltcp::time::Instant; use smoltcp::wire::{HardwareAddress, IpAddress, IpCidr}; -use stm32h7xx_hal::{rcc::CoreClocks, stm32}; -use stm32h7xx_hal::{ethernet, +use stm32h7xx_hal::{ + ethernet, ethernet::{ RxDescriptor, RxDescriptorRing, TxDescriptor, TxDescriptorRing, MTU, }, }; +use stm32h7xx_hal::{rcc::CoreClocks, stm32}; /// Configure SYSTICK for 1ms timebase fn systick_init(mut syst: stm32::SYST, clocks: CoreClocks) { diff --git a/examples/ethernet-rtic-stm32h747i-disco.rs b/examples/ethernet-rtic-stm32h747i-disco.rs index afa34ed8..59437594 100644 --- a/examples/ethernet-rtic-stm32h747i-disco.rs +++ b/examples/ethernet-rtic-stm32h747i-disco.rs @@ -32,7 +32,8 @@ use smoltcp::wire::{HardwareAddress, IpAddress, IpCidr}; use stm32h7xx_hal::{rcc::CoreClocks, stm32}; -use stm32h7xx_hal::{ethernet, +use stm32h7xx_hal::{ + ethernet, ethernet::{ RxDescriptor, RxDescriptorRing, TxDescriptor, TxDescriptorRing, MTU, }, @@ -301,7 +302,7 @@ mod app { #[task(binds = ETH, local = [net])] fn ethernet_event(ctx: ethernet_event::Context) { - ethernet::eth_interrupt_handler(); + ethernet::eth_interrupt_handler(); let time = TIME.load(Ordering::Relaxed); ctx.local.net.poll(time as i64); diff --git a/examples/ethernet-stm32h747i-disco.rs b/examples/ethernet-stm32h747i-disco.rs index 06159f86..d851136d 100644 --- a/examples/ethernet-stm32h747i-disco.rs +++ b/examples/ethernet-stm32h747i-disco.rs @@ -20,7 +20,8 @@ mod utilities; use log::info; -use stm32h7xx_hal::{ethernet, +use stm32h7xx_hal::{ + ethernet, ethernet::{ RxDescriptor, RxDescriptorRing, TxDescriptor, TxDescriptorRing, MTU, PHY, @@ -51,7 +52,6 @@ static mut RX_DESCRIPTORS: [RxDescriptor; NUM_DESCRIPTORS] = static mut RX_BUFFERS: [[u8; MTU + 2]; NUM_DESCRIPTORS] = [[0u8; MTU + 2]; NUM_DESCRIPTORS]; - // the program entry point #[entry] fn main() -> ! { @@ -204,7 +204,7 @@ fn main() -> ! { #[interrupt] fn ETH() { - ethernet::eth_interrupt_handler(); + ethernet::eth_interrupt_handler(); } #[exception] diff --git a/src/ethernet/eth.rs b/src/ethernet/eth.rs index 5a3e1c71..e5767376 100644 --- a/src/ethernet/eth.rs +++ b/src/ethernet/eth.rs @@ -28,18 +28,18 @@ use core::task::Poll; +#[cfg(feature = "ptp")] use crate::ptp::{EthernetPTP, Timestamp}; use crate::rcc::{rec, CoreClocks, ResetEnable}; use crate::stm32; use crate::stm32::{Interrupt, ETHERNET_DMA, NVIC}; use futures::task::AtomicWaker; +#[cfg(feature = "ptp")] +use smoltcp::phy::PacketMeta; use smoltcp::{ self, - phy::{ - self, ChecksumCapabilities, DeviceCapabilities, PacketMeta, RxToken, - TxToken, - }, + phy::{self, ChecksumCapabilities, DeviceCapabilities, RxToken, TxToken}, time::Instant, wire::EthernetAddress, }; @@ -78,7 +78,9 @@ const _ASSERT_DESC_WORD_SKIP_SIZE: () = assert!(DESC_WORD_SKIP <= 0b111); // padding // const ETH_BUF_SIZE: usize = 1536; +#[cfg(feature = "ptp")] pub const PTP_MAX_SIZE: usize = 76; +#[cfg(feature = "ptp")] pub const MAX_PTP_FOLLOWERS: usize = 16; /// Transmit and Receive Descriptor fields @@ -103,6 +105,7 @@ mod emac_consts { /// with any TX or RX descriptors. pub struct PacketIdNotFound; +#[cfg(feature = "ptp")] #[derive(Clone, Copy)] pub struct PtpFrameWithId { ptp_frame: [u8; PTP_MAX_SIZE], @@ -839,6 +842,7 @@ impl<'rx, 'tx> phy::Device for EthernetDMA<'rx, 'tx> { } } +#[allow(clippy::extra_unused_lifetimes)] impl<'a, 'rx, 'tx> EthernetDMA<'rx, 'tx> { #[cfg(feature = "ptp")] pub fn get_frame_from( diff --git a/src/ethernet/tx/mod.rs b/src/ethernet/tx/mod.rs index 036e375a..df1a2116 100644 --- a/src/ethernet/tx/mod.rs +++ b/src/ethernet/tx/mod.rs @@ -7,6 +7,7 @@ use crate::stm32::ETHERNET_DMA; #[cfg(feature = "ptp")] use super::eth::PacketIdNotFound; +#[cfg(feature = "ptp")] use crate::ptp::Timestamp; mod descriptor; diff --git a/src/lib.rs b/src/lib.rs index 08cc68f3..1904f8b7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -145,7 +145,8 @@ pub use crate::stm32::interrupt; #[cfg(all( feature = "device-selected", feature = "ethernet", - not(feature = "rm0455") + not(feature = "rm0455"), + feature = "ptp" ))] #[cfg_attr(docsrs, doc(cfg(feature = "ethernet")))] pub mod ptp; From cca418f66fa45958c3083fbe501e93841ce175b9 Mon Sep 17 00:00:00 2001 From: Harishguna Satgunarajah Date: Mon, 2 Oct 2023 15:50:18 -0700 Subject: [PATCH 73/77] fixing 735 example --- examples/ethernet-rtic-nucleo-h723zg.rs | 117 ++++++++++++++++++------ 1 file changed, 88 insertions(+), 29 deletions(-) diff --git a/examples/ethernet-rtic-nucleo-h723zg.rs b/examples/ethernet-rtic-nucleo-h723zg.rs index fb13fb8d..77013ef7 100644 --- a/examples/ethernet-rtic-nucleo-h723zg.rs +++ b/examples/ethernet-rtic-nucleo-h723zg.rs @@ -25,8 +25,13 @@ use smoltcp::iface::{Config, Interface, SocketSet, SocketStorage}; use smoltcp::time::Instant; use smoltcp::wire::{HardwareAddress, IpAddress, IpCidr}; -use stm32h7xx_hal::{ethernet, rcc::CoreClocks, stm32}; - +use stm32h7xx_hal::{rcc::CoreClocks, stm32}; +use stm32h7xx_hal::{ + ethernet, + ethernet::{ + RxDescriptor, RxDescriptorRing, TxDescriptor, TxDescriptorRing, MTU, + }, +}; /// Configure SYSTICK for 1ms timebase fn systick_init(mut syst: stm32::SYST, clocks: CoreClocks) { let c_ck_mhz = clocks.c_ck().to_MHz(); @@ -45,9 +50,25 @@ static TIME: AtomicU32 = AtomicU32::new(0); /// Locally administered MAC address const MAC_ADDRESS: [u8; 6] = [0x02, 0x00, 0x11, 0x22, 0x33, 0x44]; +/// DesRing TD +const NUM_DESCRIPTORS: usize = 8; /// Ethernet descriptor rings are a global singleton -#[link_section = ".axisram.eth"] -static mut DES_RING: ethernet::DesRing<4, 4> = ethernet::DesRing::new(); +#[link_section = ".sram3.eth"] +/// Doc +static mut TX_DESCRIPTORS: [TxDescriptor; NUM_DESCRIPTORS] = + [TxDescriptor::new(); NUM_DESCRIPTORS]; +#[link_section = ".sram3.eth"] +/// Doc +static mut TX_BUFFERS: [[u8; MTU + 2]; NUM_DESCRIPTORS] = + [[0u8; MTU + 2]; NUM_DESCRIPTORS]; +#[link_section = ".sram3.eth"] +/// Doc +static mut RX_DESCRIPTORS: [RxDescriptor; NUM_DESCRIPTORS] = + [RxDescriptor::new(); NUM_DESCRIPTORS]; +#[link_section = ".sram3.eth"] +/// Doc +static mut RX_BUFFERS: [[u8; MTU + 2]; NUM_DESCRIPTORS] = + [[0u8; MTU + 2]; NUM_DESCRIPTORS]; /// Net storage with static initialisation - another global singleton pub struct NetStorageStatic<'a> { @@ -60,13 +81,13 @@ static mut STORE: NetStorageStatic = NetStorageStatic { pub struct Net<'a> { iface: Interface, - ethdev: ethernet::EthernetDMA<4, 4>, + ethdev: ethernet::EthernetDMA<'a, 'a>, sockets: SocketSet<'a>, } impl<'a> Net<'a> { pub fn new( store: &'a mut NetStorageStatic<'a>, - mut ethdev: ethernet::EthernetDMA<4, 4>, + mut ethdev: ethernet::EthernetDMA<'a, 'a>, ethernet_addr: HardwareAddress, ) -> Self { let config = Config::new(ethernet_addr); @@ -153,6 +174,17 @@ mod app { let rmii_txd0 = gpiob.pb12.into_alternate(); let rmii_txd1 = gpiob.pb13.into_alternate(); + let rmii_pins = ( + rmii_ref_clk, + rmii_mdio, + rmii_mdc, + rmii_crs_dv, + rmii_rxd0, + rmii_rxd1, + rmii_tx_en, + rmii_txd0, + rmii_txd1, + ); // Initialise ethernet... assert_eq!(ccdr.clocks.hclk().raw(), 200_000_000); // HCLK 200MHz assert_eq!(ccdr.clocks.pclk1().raw(), 100_000_000); // PCLK 100MHz @@ -160,37 +192,64 @@ mod app { assert_eq!(ccdr.clocks.pclk4().raw(), 100_000_000); // PCLK 100MHz let mac_addr = smoltcp::wire::EthernetAddress::from_bytes(&MAC_ADDRESS); - let (eth_dma, eth_mac) = unsafe { - ethernet::new( - ctx.device.ETHERNET_MAC, - ctx.device.ETHERNET_MTL, - ctx.device.ETHERNET_DMA, - ( - rmii_ref_clk, - rmii_mdio, - rmii_mdc, - rmii_crs_dv, - rmii_rxd0, - rmii_rxd1, - rmii_tx_en, - rmii_txd0, - rmii_txd1, - ), - &mut DES_RING, - mac_addr, - ccdr.peripheral.ETH1MAC, - &ccdr.clocks, + let (rx_ring, tx_ring) = { + // 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(); NUM_DESCRIPTORS]) }; + // let rx_buf = unsafe { RX_BUFFERS.write([[0u8; MTU + 2]; NUM_DESCRIPTORS]) }; + + ( + RxDescriptorRing::new(unsafe { &mut RX_DESCRIPTORS }, unsafe { + &mut RX_BUFFERS + }), + TxDescriptorRing::new(unsafe { &mut TX_DESCRIPTORS }, unsafe { + &mut TX_BUFFERS + }), ) }; + #[cfg(feature = "ptp")] + let ethernet::Parts { + dma: eth_dma, + mac: eth_mac, + ptp: _ptp, + } = ethernet::new( + ctx.device.ETHERNET_MAC, + ctx.device.ETHERNET_MTL, + ctx.device.ETHERNET_DMA, + rmii_pins, + rx_ring, + tx_ring, + mac_addr, + ccdr.peripheral.ETH1MAC, + &ccdr.clocks, + ); + + #[cfg(not(feature = "ptp"))] + let ethernet::Parts { + dma: eth_dma, + mac: eth_mac, + } = ethernet::new( + ctx.device.ETHERNET_MAC, + ctx.device.ETHERNET_MTL, + ctx.device.ETHERNET_DMA, + rmii_pins, + rx_ring, + tx_ring, + mac_addr, + ccdr.peripheral.ETH1MAC, + &ccdr.clocks, + ); + // let start_addend = ptp.addend(); + eth_dma.enable_interrupt(); + // Initialise ethernet PHY... let mut lan8742a = ethernet::phy::LAN8742A::new(eth_mac); lan8742a.phy_reset(); lan8742a.phy_init(); // The eth_dma should not be used until the PHY reports the link is up - unsafe { ethernet::enable_interrupt() }; - // unsafe: mutable reference to static storage, we only do this once let store = unsafe { &mut STORE }; let net = Net::new(store, eth_dma, mac_addr.into()); @@ -222,7 +281,7 @@ mod app { #[task(binds = ETH, local = [net])] fn ethernet_event(ctx: ethernet_event::Context) { - unsafe { ethernet::interrupt_handler() } + ethernet::eth_interrupt_handler(); let time = TIME.load(Ordering::Relaxed); ctx.local.net.poll(time as i64); From bb7b50cb83c9c8f8bbcb668bebe0e6b09dad10a5 Mon Sep 17 00:00:00 2001 From: Harishguna Satgunarajah Date: Mon, 2 Oct 2023 15:50:34 -0700 Subject: [PATCH 74/77] fmt --- examples/ethernet-rtic-nucleo-h723zg.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/ethernet-rtic-nucleo-h723zg.rs b/examples/ethernet-rtic-nucleo-h723zg.rs index 77013ef7..89f9e07e 100644 --- a/examples/ethernet-rtic-nucleo-h723zg.rs +++ b/examples/ethernet-rtic-nucleo-h723zg.rs @@ -25,13 +25,13 @@ use smoltcp::iface::{Config, Interface, SocketSet, SocketStorage}; use smoltcp::time::Instant; use smoltcp::wire::{HardwareAddress, IpAddress, IpCidr}; -use stm32h7xx_hal::{rcc::CoreClocks, stm32}; use stm32h7xx_hal::{ ethernet, ethernet::{ RxDescriptor, RxDescriptorRing, TxDescriptor, TxDescriptorRing, MTU, }, }; +use stm32h7xx_hal::{rcc::CoreClocks, stm32}; /// Configure SYSTICK for 1ms timebase fn systick_init(mut syst: stm32::SYST, clocks: CoreClocks) { let c_ck_mhz = clocks.c_ck().to_MHz(); @@ -281,7 +281,7 @@ mod app { #[task(binds = ETH, local = [net])] fn ethernet_event(ctx: ethernet_event::Context) { - ethernet::eth_interrupt_handler(); + ethernet::eth_interrupt_handler(); let time = TIME.load(Ordering::Relaxed); ctx.local.net.poll(time as i64); From 43e28544326d01fa7c7ddd5f6872e0df74c7cef5 Mon Sep 17 00:00:00 2001 From: Harishguna Satgunarajah Date: Tue, 3 Oct 2023 09:26:29 -0700 Subject: [PATCH 75/77] export ptpframewithid --- src/ethernet/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ethernet/mod.rs b/src/ethernet/mod.rs index 6ed2b75d..00bc3909 100644 --- a/src/ethernet/mod.rs +++ b/src/ethernet/mod.rs @@ -59,6 +59,8 @@ pub(crate) use cache::Cache; mod eth; pub use eth::{eth_interrupt_handler, new, new_unchecked}; pub use eth::{EthernetDMA, EthernetMAC, Parts}; +#[cfg(feature = "ptp")] +pub use eth::PtpFrameWithId; /// Marks a set of pins used to communciate to a PHY with a Reduced Media /// Independent Interface (RMII) From 3fbfee429642688d4fd4bd679d2ce6898ed5fc5c Mon Sep 17 00:00:00 2001 From: Harishguna Satgunarajah Date: Tue, 3 Oct 2023 09:29:09 -0700 Subject: [PATCH 76/77] making struct fields pub --- src/ethernet/eth.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ethernet/eth.rs b/src/ethernet/eth.rs index e5767376..5454cb60 100644 --- a/src/ethernet/eth.rs +++ b/src/ethernet/eth.rs @@ -108,8 +108,8 @@ pub struct PacketIdNotFound; #[cfg(feature = "ptp")] #[derive(Clone, Copy)] pub struct PtpFrameWithId { - ptp_frame: [u8; PTP_MAX_SIZE], - packet_id: Option, + pub ptp_frame: [u8; PTP_MAX_SIZE], + pub packet_id: Option, } /// Ethernet DMA. From d6d268f1b6854462a9cb34b5cd47cb417b2d8772 Mon Sep 17 00:00:00 2001 From: Harishguna Satgunarajah Date: Tue, 3 Oct 2023 09:54:32 -0700 Subject: [PATCH 77/77] fmt --- src/ethernet/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ethernet/mod.rs b/src/ethernet/mod.rs index 00bc3909..9a5ea111 100644 --- a/src/ethernet/mod.rs +++ b/src/ethernet/mod.rs @@ -57,10 +57,10 @@ mod cache; pub(crate) use cache::Cache; mod eth; -pub use eth::{eth_interrupt_handler, new, new_unchecked}; -pub use eth::{EthernetDMA, EthernetMAC, Parts}; #[cfg(feature = "ptp")] pub use eth::PtpFrameWithId; +pub use eth::{eth_interrupt_handler, new, new_unchecked}; +pub use eth::{EthernetDMA, EthernetMAC, Parts}; /// Marks a set of pins used to communciate to a PHY with a Reduced Media /// Independent Interface (RMII)