From 4b6538c8a86947c64a0cf22fadeac847923f16e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20van=20Dorst?= Date: Fri, 25 Aug 2023 23:39:32 +0200 Subject: [PATCH 01/11] Fix read_fifo() better readout and more checks read_fifo() used part of the frame buffer to readout non-frame data. This results in incorrect readout of the fifo buffer but also the full MTU could not be used. Also added some more tests to check this and that the readout is a multipule of 4 bytes. --- embassy-net-adin1110/src/lib.rs | 187 ++++++++++++++++++++++++++++---- 1 file changed, 165 insertions(+), 22 deletions(-) diff --git a/embassy-net-adin1110/src/lib.rs b/embassy-net-adin1110/src/lib.rs index c0a9b44ee..8e5fef701 100644 --- a/embassy-net-adin1110/src/lib.rs +++ b/embassy-net-adin1110/src/lib.rs @@ -65,14 +65,16 @@ const FSC_LEN: usize = 4; const SPI_HEADER_LEN: usize = 2; /// SPI Header CRC length const SPI_HEADER_CRC_LEN: usize = 1; -/// Frame Header, +/// SPI Header Trun Around length +const SPI_HEADER_TA_LEN: usize = 1; +/// Frame Header length const FRAME_HEADER_LEN: usize = 2; +/// Space for last bytes to create multipule 4 bytes on the end of a FIFO read/write. +const SPI_SPACE_MULTIPULE: usize = 3; // P1 = 0x00, P2 = 0x01 const PORT_ID_BYTE: u8 = 0x00; -pub type Packet = Vec; - /// Type alias for the embassy-net driver for ADIN1110 pub type Device<'d> = embassy_net_driver_channel::Device<'d, MTU>; @@ -187,22 +189,24 @@ impl ADIN1110 { } /// Read out fifo ethernet packet memory received via the wire. - pub async fn read_fifo(&mut self, packet: &mut [u8]) -> AEResult { - let mut tx_buf = Vec::::new(); + pub async fn read_fifo(&mut self, frame: &mut [u8]) -> AEResult { + const HEAD_LEN: usize = SPI_HEADER_LEN + SPI_HEADER_CRC_LEN + SPI_HEADER_TA_LEN; + const TAIL_LEN: usize = FSC_LEN + SPI_SPACE_MULTIPULE; - // Size of the frame, also includes the appednded header. - let packet_size = self.read_reg(sr::RX_FSIZE).await? as usize; + let mut tx_buf = Vec::::new(); - // Packet read of write to the MAC packet buffer must be a multipul of 4! - let read_size = packet_size.next_multiple_of(4); + // Size of the frame, also includes the `frame header` and `FSC`. + let fifo_frame_size = self.read_reg(sr::RX_FSIZE).await? as usize; - if packet_size < (SPI_HEADER_LEN + FSC_LEN) { + if fifo_frame_size < ETH_MIN_LEN + FRAME_HEADER_LEN { return Err(AdinError::PACKET_TOO_SMALL); } - if read_size > packet.len() { + let packet_size = fifo_frame_size - FRAME_HEADER_LEN - FSC_LEN; + + if packet_size > frame.len() { #[cfg(feature = "defmt")] - defmt::trace!("MAX: {} WANT: {}", packet.len(), read_size); + defmt::trace!("MAX: {} WANT: {}", frame.len(), packet_size); return Err(AdinError::PACKET_TOO_BIG); } @@ -219,29 +223,28 @@ impl ADIN1110 { // Turn around byte, TODO: Unknown that this is. let _ = tx_buf.push(TURN_AROUND_BYTE); - let spi_packet = &mut packet[0..read_size]; + let mut frame_header = [0, 0]; + let mut fsc_and_extra = [0; TAIL_LEN]; - assert_eq!(spi_packet.len() & 0x03, 0x00); - - let mut pkt_header = [0, 0]; - let mut fsc = [0, 0, 0, 0]; + // Packet read of write to the MAC packet buffer must be a multipul of 4! + let tail_size = (fifo_frame_size & 0x03) + FSC_LEN; let mut spi_op = [ Operation::Write(&tx_buf), - Operation::Read(&mut pkt_header), - Operation::Read(spi_packet), - Operation::Read(&mut fsc), + Operation::Read(&mut frame_header), + Operation::Read(&mut frame[0..packet_size]), + Operation::Read(&mut fsc_and_extra[0..tail_size]), ]; self.spi.transaction(&mut spi_op).await.map_err(AdinError::Spi)?; - Ok(packet_size as usize) + Ok(packet_size) } /// Write to fifo ethernet packet memory send over the wire. pub async fn write_fifo(&mut self, frame: &[u8]) -> AEResult<(), SPI::Error> { const HEAD_LEN: usize = SPI_HEADER_LEN + SPI_HEADER_CRC_LEN + FRAME_HEADER_LEN; - const TAIL_LEN: usize = ETH_MIN_LEN - FSC_LEN + FSC_LEN + 1; + const TAIL_LEN: usize = ETH_MIN_LEN - FSC_LEN + FSC_LEN + SPI_SPACE_MULTIPULE; if frame.len() < (6 + 6 + 2) { return Err(AdinError::PACKET_TOO_SMALL); @@ -1043,4 +1046,144 @@ mod tests { spi.done(); } + + #[futures_test::test] + async fn read_packet_from_fifo_packet_too_big_for_frame_buffer() { + // Configure expectations + let mut expectations = vec![]; + + // Read RX_SIZE reg + let rx_size: u32 = u32::try_from(ETH_MIN_LEN + FRAME_HEADER_LEN + FSC_LEN).unwrap(); + let mut rx_size_vec = rx_size.to_be_bytes().to_vec(); + rx_size_vec.push(crc8(&rx_size_vec)); + + expectations.push(SpiTransaction::write_vec(vec![128, 144, 79, TURN_AROUND_BYTE])); + expectations.push(SpiTransaction::read_vec(rx_size_vec)); + expectations.push(SpiTransaction::flush()); + + let mut spi = SpiMock::new(&expectations); + + let cs = CsPinMock::default(); + let delay = MockDelay {}; + let spi_dev = ExclusiveDevice::new(spi.clone(), cs, delay); + + let mut spe = ADIN1110::new(spi_dev, true); + + let mut frame = [0; MTU]; + + let ret = spe.read_fifo(&mut frame[0..ETH_MIN_LEN - 1]).await; + assert!(matches!(dbg!(ret), Err(AdinError::PACKET_TOO_BIG))); + + spi.done(); + } + + #[futures_test::test] + async fn read_packet_from_fifo_packet_too_small() { + // Configure expectations + let mut expectations = vec![]; + + // This value is importen for this test! + assert_eq!(ETH_MIN_LEN, 64); + + // Packet data, size = `ETH_MIN_LEN` - `FSC_LEN` - 1 + let packet = [0; 64 - FSC_LEN - 1]; + + // Read RX_SIZE reg + let rx_size: u32 = u32::try_from(packet.len() + FRAME_HEADER_LEN + FSC_LEN).unwrap(); + let mut rx_size_vec = rx_size.to_be_bytes().to_vec(); + rx_size_vec.push(crc8(&rx_size_vec)); + + expectations.push(SpiTransaction::write_vec(vec![128, 144, 79, TURN_AROUND_BYTE])); + expectations.push(SpiTransaction::read_vec(rx_size_vec)); + expectations.push(SpiTransaction::flush()); + + let mut spi = SpiMock::new(&expectations); + + let cs = CsPinMock::default(); + let delay = MockDelay {}; + let spi_dev = ExclusiveDevice::new(spi.clone(), cs, delay); + + let mut spe = ADIN1110::new(spi_dev, true); + + let mut frame = [0; MTU]; + + let ret = spe.read_fifo(&mut frame).await; + assert!(matches!(dbg!(ret), Err(AdinError::PACKET_TOO_SMALL))); + + spi.done(); + } + + #[futures_test::test] + async fn read_packet_to_fifo_check_spi_read_multipule_of_u32_valid_lengths() { + let packet_buffer = [0; MTU]; + let mut frame = [0; MTU]; + let mut expectations = std::vec::Vec::with_capacity(16); + + // Packet data, size = `ETH_MIN_LEN` - `FSC_LEN` + for packet_size in [60, 61, 62, 63, 64, MTU - 4, MTU - 3, MTU - 2, MTU - 1, MTU] { + for crc_en in [false, true] { + expectations.clear(); + + let packet = &packet_buffer[0..packet_size]; + + // Read RX_SIZE reg + let rx_size: u32 = u32::try_from(packet.len() + FRAME_HEADER_LEN + FSC_LEN).unwrap(); + let mut rx_size_vec = rx_size.to_be_bytes().to_vec(); + if crc_en { + rx_size_vec.push(crc8(&rx_size_vec)); + } + + // SPI Header with CRC + let mut rx_fsize = vec![128, 144, 79, TURN_AROUND_BYTE]; + if !crc_en { + // remove the CRC on idx 2 + rx_fsize.swap_remove(2); + } + expectations.push(SpiTransaction::write_vec(rx_fsize)); + expectations.push(SpiTransaction::read_vec(rx_size_vec)); + expectations.push(SpiTransaction::flush()); + + // Read RX reg, SPI Header with CRC + let mut rx_reg = vec![128, 145, 72, TURN_AROUND_BYTE]; + if !crc_en { + // remove the CRC on idx 2 + rx_reg.swap_remove(2); + } + expectations.push(SpiTransaction::write_vec(rx_reg)); + // Frame Header + expectations.push(SpiTransaction::read_vec(vec![0, 0])); + // Packet data + expectations.push(SpiTransaction::read_vec(packet.to_vec())); + + let packet_crc = ETH_FSC::new(packet); + + let mut tail = std::vec::Vec::::with_capacity(100); + + tail.extend_from_slice(&packet_crc.hton_bytes()); + + // Need extra bytes? + let pad = (packet_size + FSC_LEN + FRAME_HEADER_LEN) & 0x03; + if pad != 0 { + // Packet FCS + optinal padding + tail.resize(tail.len() + pad, DONT_CARE_BYTE); + } + + expectations.push(SpiTransaction::read_vec(tail)); + expectations.push(SpiTransaction::flush()); + + let mut spi = SpiMock::new(&expectations); + + let cs = CsPinMock::default(); + let delay = MockDelay {}; + let spi_dev = ExclusiveDevice::new(spi.clone(), cs, delay); + + let mut spe = ADIN1110::new(spi_dev, crc_en); + + let ret = spe.read_fifo(&mut frame).await.expect("Error!"); + assert_eq!(ret, packet_size); + + spi.done(); + } + } + } } From 7f7256050cd8a4f1cb1bb270ee39cb9c8dfdafb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20van=20Dorst?= Date: Sat, 26 Aug 2023 00:21:01 +0200 Subject: [PATCH 02/11] Don't let the MAC add FCS when it is done by app MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The application can append FSC to outgoing packets and the MAC can detect and report when a bitflip has occurred. But the MAC can also add FSC if we want, but we can´t do both. When adding FSC by the application and MAC results in the packet drop by the MAC when the TX packet size > (MTU - 4). Having the application append the FSC is preferred. So set the right config bits. --- embassy-net-adin1110/src/lib.rs | 41 +++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/embassy-net-adin1110/src/lib.rs b/embassy-net-adin1110/src/lib.rs index 8e5fef701..4042bd73c 100644 --- a/embassy-net-adin1110/src/lib.rs +++ b/embassy-net-adin1110/src/lib.rs @@ -58,9 +58,11 @@ const TURN_AROUND_BYTE: u8 = 0x00; /// Packet minimal frame/packet length const ETH_MIN_LEN: usize = 64; - /// Ethernet `Frame Check Sequence` length const FSC_LEN: usize = 4; +/// Packet minimal frame/packet length without `Frame Check Sequence` length +const ETH_MIN_WITHOUT_FSC_LEN: usize = ETH_MIN_LEN - FSC_LEN; + /// SPI Header, contains SPI action and register id. const SPI_HEADER_LEN: usize = 2; /// SPI Header CRC length @@ -283,8 +285,8 @@ impl ADIN1110 { // ADIN1110 MAC and PHY don´t accept ethernet packet smaller than 64 bytes. // So padded the data minus the FCS, FCS is automatilly added to by the MAC. - if let Some(pad_len) = (ETH_MIN_LEN - FSC_LEN).checked_sub(frame.len()) { - let _ = tail_data.resize(pad_len, 0x00); + if frame.len() < ETH_MIN_WITHOUT_FSC_LEN { + let _ = tail_data.resize(ETH_MIN_WITHOUT_FSC_LEN - frame.len(), 0x00); frame_fcs = frame_fcs.update(&tail_data); } @@ -294,15 +296,18 @@ impl ADIN1110 { // len = frame_size + optional padding + 2 bytes Frame header let send_len_orig = frame.len() + tail_data.len() + FRAME_HEADER_LEN; - let spi_pad_len = send_len_orig.next_multiple_of(4); + let send_len = u32::try_from(send_len_orig).map_err(|_| AdinError::PACKET_TOO_BIG)?; // Packet read of write to the MAC packet buffer must be a multipul of 4 bytes! - if spi_pad_len != send_len_orig { - let spi_pad_len = spi_pad_len - send_len_orig; - let _ = tail_data.extend_from_slice(&[DONT_CARE_BYTE, DONT_CARE_BYTE, DONT_CARE_BYTE][..spi_pad_len]); + let pad_len = send_len_orig & 0x03; + if pad_len != 0 { + let spi_pad_len = 4 - pad_len + tail_data.len(); + let _ = tail_data.resize(spi_pad_len, DONT_CARE_BYTE); } + self.write_reg(sr::TX_FSIZE, send_len).await?; + #[cfg(feature = "defmt")] defmt::trace!( "TX: hdr {} [{}] {:02x}-{:02x}-{:02x} SIZE: {}", @@ -314,8 +319,6 @@ impl ADIN1110 { send_len, ); - self.write_reg(sr::TX_FSIZE, send_len).await?; - let mut transaction = [ Operation::Write(head_data.as_slice()), Operation::Write(frame), @@ -439,7 +442,12 @@ impl<'d, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, SPI, INT, RST> { Err(e) => match e { AdinError::PACKET_TOO_BIG => { #[cfg(feature = "defmt")] - defmt::error!("RX Packet to big, DROP"); + defmt::error!("RX Packet too big, DROP"); + self.mac.write_reg(sr::FIFO_CLR, 1).await.unwrap(); + } + AdinError::PACKET_TOO_SMALL => { + #[cfg(feature = "defmt")] + defmt::error!("RX Packet too small, DROP"); self.mac.write_reg(sr::FIFO_CLR, 1).await.unwrap(); } AdinError::Spi(_) => { @@ -622,9 +630,18 @@ pub async fn new Date: Sat, 26 Aug 2023 01:29:06 +0200 Subject: [PATCH 03/11] Validate FCS in fifo_read() and refactor tests. Adding TestHarnass to declutter the tests. Also added a test for FCS and SPI_CRC. --- embassy-net-adin1110/src/crc32.rs | 29 +- embassy-net-adin1110/src/lib.rs | 451 +++++++++++++++++++----------- embassy-net-adin1110/src/regs.rs | 2 +- 3 files changed, 307 insertions(+), 175 deletions(-) diff --git a/embassy-net-adin1110/src/crc32.rs b/embassy-net-adin1110/src/crc32.rs index a3474f70a..ec020b70c 100644 --- a/embassy-net-adin1110/src/crc32.rs +++ b/embassy-net-adin1110/src/crc32.rs @@ -257,29 +257,30 @@ pub const CRC32R_LOOKUP_TABLE: [u32; 256] = [ 0x2D02_EF8D, ]; +/// Generate Ethernet Frame Check Sequence #[allow(non_camel_case_types)] #[derive(Debug)] -pub struct ETH_FSC(pub u32); +pub struct ETH_FCS(pub u32); -impl ETH_FSC { +impl ETH_FCS { pub const CRC32_OK: u32 = 0x2144_df1c; #[must_use] pub fn new(data: &[u8]) -> Self { - let fsc = data.iter().fold(u32::MAX, |crc, byte| { + let fcs = data.iter().fold(u32::MAX, |crc, byte| { let idx = u8::try_from(crc & 0xFF).unwrap() ^ byte; CRC32R_LOOKUP_TABLE[usize::from(idx)] ^ (crc >> 8) }) ^ u32::MAX; - Self(fsc) + Self(fcs) } #[must_use] pub fn update(self, data: &[u8]) -> Self { - let fsc = data.iter().fold(self.0 ^ u32::MAX, |crc, byte| { + let fcs = data.iter().fold(self.0 ^ u32::MAX, |crc, byte| { let idx = u8::try_from(crc & 0xFF).unwrap() ^ byte; CRC32R_LOOKUP_TABLE[usize::from(idx)] ^ (crc >> 8) }) ^ u32::MAX; - Self(fsc) + Self(fcs) } #[must_use] @@ -319,24 +320,24 @@ mod tests { ]; // Packet A - let own_crc = ETH_FSC::new(&packet_a[0..60]); + let own_crc = ETH_FCS::new(&packet_a[0..60]); let crc_bytes = own_crc.hton_bytes(); println!("{:08x} {:02x?}", own_crc.0, crc_bytes); assert_eq!(&crc_bytes, &packet_a[60..64]); - let own_crc = ETH_FSC::new(packet_a); + let own_crc = ETH_FCS::new(packet_a); println!("{:08x}", own_crc.0); - assert_eq!(own_crc.0, ETH_FSC::CRC32_OK); + assert_eq!(own_crc.0, ETH_FCS::CRC32_OK); // Packet B - let own_crc = ETH_FSC::new(&packet_b[0..60]); + let own_crc = ETH_FCS::new(&packet_b[0..60]); let crc_bytes = own_crc.hton_bytes(); println!("{:08x} {:02x?}", own_crc.0, crc_bytes); assert_eq!(&crc_bytes, &packet_b[60..64]); - let own_crc = ETH_FSC::new(packet_b); + let own_crc = ETH_FCS::new(packet_b); println!("{:08x}", own_crc.0); - assert_eq!(own_crc.0, ETH_FSC::CRC32_OK); + assert_eq!(own_crc.0, ETH_FCS::CRC32_OK); } #[test] @@ -349,9 +350,9 @@ mod tests { ]; let (part_a, part_b) = full_data.split_at(16); - let crc_partially = ETH_FSC::new(part_a).update(part_b); + let crc_partially = ETH_FCS::new(part_a).update(part_b); - let crc_full = ETH_FSC::new(full_data); + let crc_full = ETH_FCS::new(full_data); assert_eq!(crc_full.0, crc_partially.0); } diff --git a/embassy-net-adin1110/src/lib.rs b/embassy-net-adin1110/src/lib.rs index 4042bd73c..8d73e024f 100644 --- a/embassy-net-adin1110/src/lib.rs +++ b/embassy-net-adin1110/src/lib.rs @@ -13,7 +13,7 @@ mod phy; mod regs; use ch::driver::LinkState; -pub use crc32::ETH_FSC; +pub use crc32::ETH_FCS; use crc8::crc8; use embassy_futures::select::{select, Either}; use embassy_net_driver_channel as ch; @@ -35,16 +35,22 @@ pub const PHYID: u32 = 0x0283_BC91; #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[allow(non_camel_case_types)] pub enum AdinError { + /// SPI-BUS Error Spi(E), - SENDERROR, - READERROR, - CRC, + /// Ethernet FCS error + FCS, + /// SPI Header CRC error + SPI_CRC, + /// Received or sended ethernet packet is too big PACKET_TOO_BIG, + /// Received or sended ethernet packet is too small PACKET_TOO_SMALL, + /// MDIO transaction timeout MDIO_ACC_TIMEOUT, } pub type AEResult = core::result::Result>; +/// Internet PHY address pub const MDIO_PHY_ADDR: u8 = 0x01; /// Maximum Transmission Unit @@ -59,9 +65,9 @@ const TURN_AROUND_BYTE: u8 = 0x00; /// Packet minimal frame/packet length const ETH_MIN_LEN: usize = 64; /// Ethernet `Frame Check Sequence` length -const FSC_LEN: usize = 4; +const FCS_LEN: usize = 4; /// Packet minimal frame/packet length without `Frame Check Sequence` length -const ETH_MIN_WITHOUT_FSC_LEN: usize = ETH_MIN_LEN - FSC_LEN; +const ETH_MIN_WITHOUT_FCS_LEN: usize = ETH_MIN_LEN - FCS_LEN; /// SPI Header, contains SPI action and register id. const SPI_HEADER_LEN: usize = 2; @@ -74,7 +80,7 @@ const FRAME_HEADER_LEN: usize = 2; /// Space for last bytes to create multipule 4 bytes on the end of a FIFO read/write. const SPI_SPACE_MULTIPULE: usize = 3; -// P1 = 0x00, P2 = 0x01 +/// P1 = 0x00, P2 = 0x01 const PORT_ID_BYTE: u8 = 0x00; /// Type alias for the embassy-net driver for ADIN1110 @@ -100,12 +106,18 @@ pub struct ADIN1110 { spi: SPI, /// Enable CRC on SPI transfer. /// This must match with the hardware pin `SPI_CFG0` were low = CRC enable, high = CRC disabled. - crc: bool, + spi_crc: bool, + /// Append FCS by the application of transmit packet, false = FCS is appended by the MAC, true = FCS appended by the application. + append_fcs_on_tx: bool, } impl ADIN1110 { - pub fn new(spi: SPI, crc: bool) -> Self { - Self { spi, crc } + pub fn new(spi: SPI, spi_crc: bool, append_fcs_on_tx: bool) -> Self { + Self { + spi, + spi_crc, + append_fcs_on_tx, + } } pub async fn read_reg(&mut self, reg: sr) -> AEResult { @@ -116,7 +128,7 @@ impl ADIN1110 { spi_hdr.set_addr(reg); let _ = tx_buf.extend_from_slice(spi_hdr.0.to_be_bytes().as_slice()); - if self.crc { + if self.spi_crc { // Add CRC for header data let _ = tx_buf.push(crc8(&tx_buf)); } @@ -126,16 +138,16 @@ impl ADIN1110 { let mut rx_buf = [0; 5]; - let spi_read_len = if self.crc { rx_buf.len() } else { rx_buf.len() - 1 }; + let spi_read_len = if self.spi_crc { rx_buf.len() } else { rx_buf.len() - 1 }; let mut spi_op = [Operation::Write(&tx_buf), Operation::Read(&mut rx_buf[0..spi_read_len])]; self.spi.transaction(&mut spi_op).await.map_err(AdinError::Spi)?; - if self.crc { + if self.spi_crc { let crc = crc8(&rx_buf[0..4]); if crc != rx_buf[4] { - return Err(AdinError::CRC); + return Err(AdinError::SPI_CRC); } } @@ -156,7 +168,7 @@ impl ADIN1110 { spi_hdr.set_addr(reg); let _ = tx_buf.extend_from_slice(spi_hdr.0.to_be_bytes().as_slice()); - if self.crc { + if self.spi_crc { // Add CRC for header data let _ = tx_buf.push(crc8(&tx_buf)); } @@ -164,7 +176,7 @@ impl ADIN1110 { let val = value.to_be_bytes(); let _ = tx_buf.extend_from_slice(val.as_slice()); - if self.crc { + if self.spi_crc { // Add CRC for header data let _ = tx_buf.push(crc8(val.as_slice())); } @@ -193,18 +205,18 @@ impl ADIN1110 { /// Read out fifo ethernet packet memory received via the wire. pub async fn read_fifo(&mut self, frame: &mut [u8]) -> AEResult { const HEAD_LEN: usize = SPI_HEADER_LEN + SPI_HEADER_CRC_LEN + SPI_HEADER_TA_LEN; - const TAIL_LEN: usize = FSC_LEN + SPI_SPACE_MULTIPULE; + const TAIL_LEN: usize = FCS_LEN + SPI_SPACE_MULTIPULE; let mut tx_buf = Vec::::new(); - // Size of the frame, also includes the `frame header` and `FSC`. + // Size of the frame, also includes the `frame header` and `FCS`. let fifo_frame_size = self.read_reg(sr::RX_FSIZE).await? as usize; if fifo_frame_size < ETH_MIN_LEN + FRAME_HEADER_LEN { return Err(AdinError::PACKET_TOO_SMALL); } - let packet_size = fifo_frame_size - FRAME_HEADER_LEN - FSC_LEN; + let packet_size = fifo_frame_size - FRAME_HEADER_LEN - FCS_LEN; if packet_size > frame.len() { #[cfg(feature = "defmt")] @@ -217,7 +229,7 @@ impl ADIN1110 { spi_hdr.set_addr(sr::RX); let _ = tx_buf.extend_from_slice(spi_hdr.0.to_be_bytes().as_slice()); - if self.crc { + if self.spi_crc { // Add CRC for header data let _ = tx_buf.push(crc8(&tx_buf)); } @@ -226,27 +238,36 @@ impl ADIN1110 { let _ = tx_buf.push(TURN_AROUND_BYTE); let mut frame_header = [0, 0]; - let mut fsc_and_extra = [0; TAIL_LEN]; + let mut fcs_and_extra = [0; TAIL_LEN]; // Packet read of write to the MAC packet buffer must be a multipul of 4! - let tail_size = (fifo_frame_size & 0x03) + FSC_LEN; + let tail_size = (fifo_frame_size & 0x03) + FCS_LEN; let mut spi_op = [ Operation::Write(&tx_buf), Operation::Read(&mut frame_header), Operation::Read(&mut frame[0..packet_size]), - Operation::Read(&mut fsc_and_extra[0..tail_size]), + Operation::Read(&mut fcs_and_extra[0..tail_size]), ]; self.spi.transaction(&mut spi_op).await.map_err(AdinError::Spi)?; - Ok(packet_size) + // According to register `CONFIG2`, bit 5 `CRC_APPEND` discription: + // "Similarly, on receive, the CRC32 is forwarded with the frame to the host where the host must verify it is correct." + // The application must allways check the FCS. It seems that the MAC/PHY has no option to handle this. + let fcs_calc = ETH_FCS::new(&frame[0..packet_size]); + + if fcs_calc.hton_bytes() == fcs_and_extra[0..4] { + Ok(packet_size) + } else { + Err(AdinError::FCS) + } } /// Write to fifo ethernet packet memory send over the wire. pub async fn write_fifo(&mut self, frame: &[u8]) -> AEResult<(), SPI::Error> { const HEAD_LEN: usize = SPI_HEADER_LEN + SPI_HEADER_CRC_LEN + FRAME_HEADER_LEN; - const TAIL_LEN: usize = ETH_MIN_LEN - FSC_LEN + FSC_LEN + SPI_SPACE_MULTIPULE; + const TAIL_LEN: usize = ETH_MIN_LEN - FCS_LEN + FCS_LEN + SPI_SPACE_MULTIPULE; if frame.len() < (6 + 6 + 2) { return Err(AdinError::PACKET_TOO_SMALL); @@ -269,7 +290,7 @@ impl ADIN1110 { .extend_from_slice(spi_hdr.0.to_be_bytes().as_slice()) .map_err(|_e| AdinError::PACKET_TOO_BIG)?; - if self.crc { + if self.spi_crc { // Add CRC for header data head_data .push(crc8(&head_data[0..2])) @@ -281,18 +302,22 @@ impl ADIN1110 { .extend_from_slice(u16::from(PORT_ID_BYTE).to_be_bytes().as_slice()) .map_err(|_e| AdinError::PACKET_TOO_BIG)?; - let mut frame_fcs = ETH_FSC::new(frame); - // ADIN1110 MAC and PHY don´t accept ethernet packet smaller than 64 bytes. // So padded the data minus the FCS, FCS is automatilly added to by the MAC. - if frame.len() < ETH_MIN_WITHOUT_FSC_LEN { - let _ = tail_data.resize(ETH_MIN_WITHOUT_FSC_LEN - frame.len(), 0x00); - frame_fcs = frame_fcs.update(&tail_data); + if frame.len() < ETH_MIN_WITHOUT_FCS_LEN { + let _ = tail_data.resize(ETH_MIN_WITHOUT_FCS_LEN - frame.len(), 0x00); } - // Add ethernet FCS only over the ethernet packet. - // Only usefull when `CONFIG0`, `Transmit Frame Check Sequence Validation Enable` bit is enabled. - let _ = tail_data.extend_from_slice(frame_fcs.hton_bytes().as_slice()); + // Append FCS by the application + if self.append_fcs_on_tx { + let mut frame_fcs = ETH_FCS::new(frame); + + if !tail_data.is_empty() { + frame_fcs = frame_fcs.update(&tail_data); + } + + let _ = tail_data.extend_from_slice(frame_fcs.hton_bytes().as_slice()); + } // len = frame_size + optional padding + 2 bytes Frame header let send_len_orig = frame.len() + tail_data.len() + FRAME_HEADER_LEN; @@ -583,7 +608,8 @@ pub async fn new (Device<'_>, Runner<'_, SPI, INT, RST>) { use crate::regs::{IMask0, IMask1}; @@ -602,7 +628,7 @@ pub async fn new, CsPinMock, MockDelay>>, + spi: Generic, + } + + impl TestHarnass { + pub fn new(expectations: &[SpiTransaction], spi_crc: bool, append_fcs_on_tx: bool) -> Self { + let cs = CsPinMock::default(); + let delay = MockDelay {}; + let spi = SpiMock::new(expectations); + let spi_dev: ExclusiveDevice, CsPinMock, MockDelay> = + ExclusiveDevice::new(spi.clone(), cs, delay); + let spe: ADIN1110< + ExclusiveDevice, CsPinMock, MockDelay>, + > = ADIN1110::new(spi_dev, spi_crc, append_fcs_on_tx); + + Self { spe, spi } + } + + pub fn done(&mut self) { + self.spi.done(); + } + } + #[futures_test::test] async fn mac_read_registers_without_crc() { // Configure expectations @@ -772,22 +820,20 @@ mod tests { SpiTransaction::read_vec(vec![0x00, 0x00, 0x06, 0xC3]), SpiTransaction::flush(), ]; - let mut spi = SpiMock::new(&expectations); - let cs = CsPinMock::default(); - let delay = MockDelay {}; - let spi_dev = ExclusiveDevice::new(spi.clone(), cs, delay); - let mut spe = ADIN1110::new(spi_dev, false); + // Create TestHarnass + let mut th = TestHarnass::new(&expectations, false, true); // Read PHIID - let val = spe.read_reg(sr::PHYID).await.expect("Error"); + let val = th.spe.read_reg(sr::PHYID).await.expect("Error"); assert_eq!(val, 0x0283_BC91); // Read CAPAVILITY - let val = spe.read_reg(sr::CAPABILITY).await.expect("Error"); + let val = th.spe.read_reg(sr::CAPABILITY).await.expect("Error"); assert_eq!(val, 0x0000_06C3); - spi.done(); + // Mark end of the SPI test. + th.done(); } #[futures_test::test] @@ -803,26 +849,23 @@ mod tests { SpiTransaction::read_vec(vec![0x00, 0x00, 0x06, 0xC3, 57]), SpiTransaction::flush(), ]; - let mut spi = SpiMock::new(&expectations); - let cs = CsPinMock::default(); - let delay = MockDelay {}; - let spi_dev = ExclusiveDevice::new(spi.clone(), cs, delay); - - let mut spe = ADIN1110::new(spi_dev, true); + // Create TestHarnass + let mut th = TestHarnass::new(&expectations, true, true); assert_eq!(crc8(0x0283_BC91_u32.to_be_bytes().as_slice()), 215); assert_eq!(crc8(0x0000_06C3_u32.to_be_bytes().as_slice()), 57); // Read PHIID - let val = spe.read_reg(sr::PHYID).await.expect("Error"); + let val = th.spe.read_reg(sr::PHYID).await.expect("Error"); assert_eq!(val, 0x0283_BC91); // Read CAPAVILITY - let val = spe.read_reg(sr::CAPABILITY).await.expect("Error"); + let val = th.spe.read_reg(sr::CAPABILITY).await.expect("Error"); assert_eq!(val, 0x0000_06C3); - spi.done(); + // Mark end of the SPI test. + th.done(); } #[futures_test::test] @@ -832,18 +875,15 @@ mod tests { SpiTransaction::write_vec(vec![0xA0, 0x09, 0x12, 0x34, 0x56, 0x78]), SpiTransaction::flush(), ]; - let mut spi = SpiMock::new(&expectations); - let cs = CsPinMock::default(); - let delay = MockDelay {}; - let spi_dev = ExclusiveDevice::new(spi.clone(), cs, delay); - - let mut spe = ADIN1110::new(spi_dev, false); + // Create TestHarnass + let mut th = TestHarnass::new(&expectations, false, true); // Write reg: 0x1FFF - assert!(spe.write_reg(sr::STATUS1, 0x1234_5678).await.is_ok()); + assert!(th.spe.write_reg(sr::STATUS1, 0x1234_5678).await.is_ok()); - spi.done(); + // Mark end of the SPI test. + th.done(); } #[futures_test::test] @@ -854,17 +894,14 @@ mod tests { SpiTransaction::flush(), ]; - // Basic test init block - let mut spi = SpiMock::new(&expectations); - let cs = CsPinMock::default(); - let delay = MockDelay {}; - let spi_dev = ExclusiveDevice::new(spi.clone(), cs, delay); - let mut spe = ADIN1110::new(spi_dev, true); + // Create TestHarnass + let mut th = TestHarnass::new(&expectations, true, true); // Write reg: 0x1FFF - assert!(spe.write_reg(sr::STATUS1, 0x1234_5678).await.is_ok()); + assert!(th.spe.write_reg(sr::STATUS1, 0x1234_5678).await.is_ok()); - spi.done(); + // Mark end of the SPI test. + th.done(); } #[futures_test::test] @@ -885,7 +922,7 @@ mod tests { let mut tail = std::vec::Vec::::with_capacity(100); // Padding - if let Some(padding_len) = (ETH_MIN_LEN - FSC_LEN).checked_sub(packet.len()) { + if let Some(padding_len) = (ETH_MIN_LEN - FCS_LEN).checked_sub(packet.len()) { tail.resize(padding_len, 0x00); } // Packet FCS + optinal padding @@ -894,17 +931,49 @@ mod tests { expectations.push(SpiTransaction::write_vec(tail)); expectations.push(SpiTransaction::flush()); - let mut spi = SpiMock::new(&expectations); + // Create TestHarnass + let mut th = TestHarnass::new(&expectations, true, true); - let cs = CsPinMock::default(); - let delay = MockDelay {}; - let spi_dev = ExclusiveDevice::new(spi.clone(), cs, delay); + assert!(th.spe.write_fifo(&packet).await.is_ok()); - let mut spe = ADIN1110::new(spi_dev, true); + // Mark end of the SPI test. + th.done(); + } - assert!(spe.write_fifo(&packet).await.is_ok()); + #[futures_test::test] + async fn write_packet_to_fifo_minimal_with_crc_without_fcs() { + // Configure expectations + let mut expectations = vec![]; - spi.done(); + // Write TX_SIZE reg + expectations.push(SpiTransaction::write_vec(vec![160, 48, 136, 0, 0, 0, 62, 186])); + expectations.push(SpiTransaction::flush()); + + // Write TX reg. + // SPI Header + optional CRC + Frame Header + expectations.push(SpiTransaction::write_vec(vec![160, 49, 143, 0, 0])); + // Packet data + let packet = [0xFF_u8; 60]; + expectations.push(SpiTransaction::write_vec(packet.to_vec())); + + let mut tail = std::vec::Vec::::with_capacity(100); + // Padding + if let Some(padding_len) = (ETH_MIN_LEN - FCS_LEN).checked_sub(packet.len()) { + tail.resize(padding_len, 0x00); + } + // Packet FCS + optinal padding + tail.extend_from_slice(&[DONT_CARE_BYTE, DONT_CARE_BYTE]); + + expectations.push(SpiTransaction::write_vec(tail)); + expectations.push(SpiTransaction::flush()); + + // Create TestHarnass + let mut th = TestHarnass::new(&expectations, true, false); + + assert!(th.spe.write_fifo(&packet).await.is_ok()); + + // Mark end of the SPI test. + th.done(); } #[futures_test::test] @@ -926,7 +995,7 @@ mod tests { let mut tail = std::vec::Vec::::with_capacity(100); // Padding - if let Some(padding_len) = (ETH_MIN_LEN - FSC_LEN).checked_sub(packet.len()) { + if let Some(padding_len) = (ETH_MIN_LEN - FCS_LEN).checked_sub(packet.len()) { tail.resize(padding_len, 0x00); } // Packet FCS + optinal padding @@ -935,17 +1004,13 @@ mod tests { expectations.push(SpiTransaction::write_vec(tail)); expectations.push(SpiTransaction::flush()); - let mut spi = SpiMock::new(&expectations); + // Create TestHarnass + let mut th = TestHarnass::new(&expectations, true, true); - let cs = CsPinMock::default(); - let delay = MockDelay {}; - let spi_dev = ExclusiveDevice::new(spi.clone(), cs, delay); + assert!(th.spe.write_fifo(&packet).await.is_ok()); - let mut spe = ADIN1110::new(spi_dev, true); - - assert!(spe.write_fifo(&packet).await.is_ok()); - - spi.done(); + // Mark end of the SPI test. + th.done(); } #[futures_test::test] @@ -958,24 +1023,23 @@ mod tests { // Max packet size = MAX_BUFF - FRAME_HEADER_LEN let packet = [0xAA_u8; MAX_BUFF - FRAME_HEADER_LEN + 1]; - let mut spi = SpiMock::new(&expectations); - - let cs = CsPinMock::default(); - let delay = MockDelay {}; - let spi_dev = ExclusiveDevice::new(spi.clone(), cs, delay); - - let mut spe = ADIN1110::new(spi_dev, true); + // Create TestHarnass + let mut th = TestHarnass::new(&expectations, true, true); // minimal assert!(matches!( - spe.write_fifo(&packet[0..(6 + 6 + 2 - 1)]).await, + th.spe.write_fifo(&packet[0..(6 + 6 + 2 - 1)]).await, Err(AdinError::PACKET_TOO_SMALL) )); // max + 1 - assert!(matches!(spe.write_fifo(&packet).await, Err(AdinError::PACKET_TOO_BIG))); + assert!(matches!( + th.spe.write_fifo(&packet).await, + Err(AdinError::PACKET_TOO_BIG) + )); - spi.done(); + // Mark end of the SPI test. + th.done(); } #[futures_test::test] @@ -999,7 +1063,7 @@ mod tests { let mut tail = std::vec::Vec::::with_capacity(100); // Padding - if let Some(padding_len) = (ETH_MIN_LEN - FSC_LEN).checked_sub(packet.len()) { + if let Some(padding_len) = (ETH_MIN_LEN - FCS_LEN).checked_sub(packet.len()) { tail.resize(padding_len, 0x00); } // Packet FCS + optinal padding @@ -1008,17 +1072,13 @@ mod tests { expectations.push(SpiTransaction::write_vec(tail)); expectations.push(SpiTransaction::flush()); - let mut spi = SpiMock::new(&expectations); + // Create TestHarnass + let mut th = TestHarnass::new(&expectations, true, true); - let cs = CsPinMock::default(); - let delay = MockDelay {}; - let spi_dev = ExclusiveDevice::new(spi.clone(), cs, delay); + assert!(th.spe.write_fifo(&packet).await.is_ok()); - let mut spe = ADIN1110::new(spi_dev, true); - - assert!(spe.write_fifo(&packet).await.is_ok()); - - spi.done(); + // Mark end of the SPI test. + th.done(); } #[futures_test::test] @@ -1042,7 +1102,7 @@ mod tests { let mut tail = std::vec::Vec::::with_capacity(100); // Padding - if let Some(padding_len) = (ETH_MIN_LEN - FSC_LEN).checked_sub(packet.len()) { + if let Some(padding_len) = (ETH_MIN_LEN - FCS_LEN).checked_sub(packet.len()) { tail.resize(padding_len, 0x00); } // Packet FCS + optinal padding @@ -1051,17 +1111,13 @@ mod tests { expectations.push(SpiTransaction::write_vec(tail)); expectations.push(SpiTransaction::flush()); - let mut spi = SpiMock::new(&expectations); + // Create TestHarnass + let mut th = TestHarnass::new(&expectations, false, true); - let cs = CsPinMock::default(); - let delay = MockDelay {}; - let spi_dev = ExclusiveDevice::new(spi.clone(), cs, delay); + assert!(th.spe.write_fifo(&packet).await.is_ok()); - let mut spe = ADIN1110::new(spi_dev, false); - - assert!(spe.write_fifo(&packet).await.is_ok()); - - spi.done(); + // Mark end of the SPI test. + th.done(); } #[futures_test::test] @@ -1070,7 +1126,7 @@ mod tests { let mut expectations = vec![]; // Read RX_SIZE reg - let rx_size: u32 = u32::try_from(ETH_MIN_LEN + FRAME_HEADER_LEN + FSC_LEN).unwrap(); + let rx_size: u32 = u32::try_from(ETH_MIN_LEN + FRAME_HEADER_LEN + FCS_LEN).unwrap(); let mut rx_size_vec = rx_size.to_be_bytes().to_vec(); rx_size_vec.push(crc8(&rx_size_vec)); @@ -1078,20 +1134,16 @@ mod tests { expectations.push(SpiTransaction::read_vec(rx_size_vec)); expectations.push(SpiTransaction::flush()); - let mut spi = SpiMock::new(&expectations); - - let cs = CsPinMock::default(); - let delay = MockDelay {}; - let spi_dev = ExclusiveDevice::new(spi.clone(), cs, delay); - - let mut spe = ADIN1110::new(spi_dev, true); - let mut frame = [0; MTU]; - let ret = spe.read_fifo(&mut frame[0..ETH_MIN_LEN - 1]).await; + // Create TestHarnass + let mut th = TestHarnass::new(&expectations, true, true); + + let ret = th.spe.read_fifo(&mut frame[0..ETH_MIN_LEN - 1]).await; assert!(matches!(dbg!(ret), Err(AdinError::PACKET_TOO_BIG))); - spi.done(); + // Mark end of the SPI test. + th.done(); } #[futures_test::test] @@ -1102,11 +1154,11 @@ mod tests { // This value is importen for this test! assert_eq!(ETH_MIN_LEN, 64); - // Packet data, size = `ETH_MIN_LEN` - `FSC_LEN` - 1 - let packet = [0; 64 - FSC_LEN - 1]; + // Packet data, size = `ETH_MIN_LEN` - `FCS_LEN` - 1 + let packet = [0; 64 - FCS_LEN - 1]; // Read RX_SIZE reg - let rx_size: u32 = u32::try_from(packet.len() + FRAME_HEADER_LEN + FSC_LEN).unwrap(); + let rx_size: u32 = u32::try_from(packet.len() + FRAME_HEADER_LEN + FCS_LEN).unwrap(); let mut rx_size_vec = rx_size.to_be_bytes().to_vec(); rx_size_vec.push(crc8(&rx_size_vec)); @@ -1114,20 +1166,84 @@ mod tests { expectations.push(SpiTransaction::read_vec(rx_size_vec)); expectations.push(SpiTransaction::flush()); - let mut spi = SpiMock::new(&expectations); - - let cs = CsPinMock::default(); - let delay = MockDelay {}; - let spi_dev = ExclusiveDevice::new(spi.clone(), cs, delay); - - let mut spe = ADIN1110::new(spi_dev, true); - let mut frame = [0; MTU]; - let ret = spe.read_fifo(&mut frame).await; + // Create TestHarnass + let mut th = TestHarnass::new(&expectations, true, true); + + let ret = th.spe.read_fifo(&mut frame).await; assert!(matches!(dbg!(ret), Err(AdinError::PACKET_TOO_SMALL))); - spi.done(); + // Mark end of the SPI test. + th.done(); + } + + #[futures_test::test] + async fn read_packet_from_fifo_packet_corrupted_fcs() { + let mut frame = [0; MTU]; + // Configure expectations + let mut expectations = vec![]; + + let packet = [0xDE; 60]; + let crc_en = true; + + // Read RX_SIZE reg + let rx_size: u32 = u32::try_from(packet.len() + FRAME_HEADER_LEN + FCS_LEN).unwrap(); + let mut rx_size_vec = rx_size.to_be_bytes().to_vec(); + if crc_en { + rx_size_vec.push(crc8(&rx_size_vec)); + } + + // SPI Header with CRC + let mut rx_fsize = vec![128, 144, 79, TURN_AROUND_BYTE]; + if !crc_en { + // remove the CRC on idx 2 + rx_fsize.swap_remove(2); + } + expectations.push(SpiTransaction::write_vec(rx_fsize)); + expectations.push(SpiTransaction::read_vec(rx_size_vec)); + expectations.push(SpiTransaction::flush()); + + // Read RX reg, SPI Header with CRC + let mut rx_reg = vec![128, 145, 72, TURN_AROUND_BYTE]; + if !crc_en { + // remove the CRC on idx 2 + rx_reg.swap_remove(2); + } + expectations.push(SpiTransaction::write_vec(rx_reg)); + // Frame Header + expectations.push(SpiTransaction::read_vec(vec![0, 0])); + // Packet data + expectations.push(SpiTransaction::read_vec(packet.to_vec())); + + let packet_crc = ETH_FCS::new(&packet); + + let mut tail = std::vec::Vec::::with_capacity(100); + + tail.extend_from_slice(&packet_crc.hton_bytes()); + // increase last byte with 1. + if let Some(crc) = tail.last_mut() { + *crc = crc.wrapping_add(1); + } + + // Need extra bytes? + let pad = (packet.len() + FCS_LEN + FRAME_HEADER_LEN) & 0x03; + if pad != 0 { + // Packet FCS + optinal padding + tail.resize(tail.len() + pad, DONT_CARE_BYTE); + } + + expectations.push(SpiTransaction::read_vec(tail)); + expectations.push(SpiTransaction::flush()); + + // Create TestHarnass + let mut th = TestHarnass::new(&expectations, crc_en, false); + + let ret = th.spe.read_fifo(&mut frame).await.expect_err("Error!"); + assert!(matches!(ret, AdinError::FCS)); + + // Mark end of the SPI test. + th.done(); } #[futures_test::test] @@ -1136,7 +1252,7 @@ mod tests { let mut frame = [0; MTU]; let mut expectations = std::vec::Vec::with_capacity(16); - // Packet data, size = `ETH_MIN_LEN` - `FSC_LEN` + // Packet data, size = `ETH_MIN_LEN` - `FCS_LEN` for packet_size in [60, 61, 62, 63, 64, MTU - 4, MTU - 3, MTU - 2, MTU - 1, MTU] { for crc_en in [false, true] { expectations.clear(); @@ -1144,7 +1260,7 @@ mod tests { let packet = &packet_buffer[0..packet_size]; // Read RX_SIZE reg - let rx_size: u32 = u32::try_from(packet.len() + FRAME_HEADER_LEN + FSC_LEN).unwrap(); + let rx_size: u32 = u32::try_from(packet.len() + FRAME_HEADER_LEN + FCS_LEN).unwrap(); let mut rx_size_vec = rx_size.to_be_bytes().to_vec(); if crc_en { rx_size_vec.push(crc8(&rx_size_vec)); @@ -1172,14 +1288,14 @@ mod tests { // Packet data expectations.push(SpiTransaction::read_vec(packet.to_vec())); - let packet_crc = ETH_FSC::new(packet); + let packet_crc = ETH_FCS::new(packet); let mut tail = std::vec::Vec::::with_capacity(100); tail.extend_from_slice(&packet_crc.hton_bytes()); // Need extra bytes? - let pad = (packet_size + FSC_LEN + FRAME_HEADER_LEN) & 0x03; + let pad = (packet_size + FCS_LEN + FRAME_HEADER_LEN) & 0x03; if pad != 0 { // Packet FCS + optinal padding tail.resize(tail.len() + pad, DONT_CARE_BYTE); @@ -1188,19 +1304,34 @@ mod tests { expectations.push(SpiTransaction::read_vec(tail)); expectations.push(SpiTransaction::flush()); - let mut spi = SpiMock::new(&expectations); + // Create TestHarnass + let mut th = TestHarnass::new(&expectations, crc_en, false); - let cs = CsPinMock::default(); - let delay = MockDelay {}; - let spi_dev = ExclusiveDevice::new(spi.clone(), cs, delay); - - let mut spe = ADIN1110::new(spi_dev, crc_en); - - let ret = spe.read_fifo(&mut frame).await.expect("Error!"); + let ret = th.spe.read_fifo(&mut frame).await.expect("Error!"); assert_eq!(ret, packet_size); - spi.done(); + // Mark end of the SPI test. + th.done(); } } } + + #[futures_test::test] + async fn spi_crc_error() { + // Configure expectations + let expectations = vec![ + SpiTransaction::write_vec(vec![128, 144, 79, TURN_AROUND_BYTE]), + SpiTransaction::read_vec(vec![0x00, 0x00, 0x00, 0x00, 0xDD]), + SpiTransaction::flush(), + ]; + + // Create TestHarnass + let mut th = TestHarnass::new(&expectations, true, false); + + let ret = th.spe.read_reg(sr::RX_FSIZE).await; + assert!(matches!(dbg!(ret), Err(AdinError::SPI_CRC))); + + // Mark end of the SPI test. + th.done(); + } } diff --git a/embassy-net-adin1110/src/regs.rs b/embassy-net-adin1110/src/regs.rs index 4557929f0..46466c7d1 100644 --- a/embassy-net-adin1110/src/regs.rs +++ b/embassy-net-adin1110/src/regs.rs @@ -174,7 +174,7 @@ bitfield! { pub sdf_detect_src, set_sdf_detect_src : 7; /// Statistics Clear on Reading pub stats_clr_on_rd, set_stats_clr_on_rd : 6; - /// Enable CRC Append + /// Enable SPI CRC pub crc_append, set_crc_append : 5; /// Admit Frames with IFG Errors on Port 1 (P1) pub p1_rcv_ifg_err_frm, set_p1_rcv_ifg_err_frm : 4; From 2c36199dea8230f261bc1ee210f96f47272b8b11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20van=20Dorst?= Date: Sun, 27 Aug 2023 13:47:02 +0200 Subject: [PATCH 04/11] stm32l4: Update adin1110 example add FCS option --- .../src/bin/spe_adin1110_http_server.rs | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs index 148c58771..baaa9dfad 100644 --- a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs +++ b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs @@ -11,7 +11,9 @@ // Settings switch S201 "HW CFG": // - Without SPI CRC: OFF-ON-OFF-OFF-OFF // - With SPI CRC: ON -ON-OFF-OFF-OFF -// Settings switch S303 "uC CFG": CFG0: On = static ip, Off = Dhcp +// Settings switch S303 "uC CFG": +// - CFG0: On = static ip, Off = Dhcp +// - CFG1: Ethernet `FCS` on TX path: On, Off // The webserver shows the actual temperature of the onboard i2c temp sensor. use core::marker::PhantomData; @@ -107,7 +109,7 @@ async fn main(spawner: Spawner) { // Read the uc_cfg switches let uc_cfg0 = Input::new(dp.PB2, Pull::None); - let _uc_cfg1 = Input::new(dp.PF11, Pull::None); + let uc_cfg1 = Input::new(dp.PF11, Pull::None); let _uc_cfg2 = Input::new(dp.PG6, Pull::None); let _uc_cfg3 = Input::new(dp.PG11, Pull::None); @@ -154,11 +156,13 @@ async fn main(spawner: Spawner) { let cfg0_without_crc = spe_cfg0.is_high(); let cfg1_spi_mode = spe_cfg1.is_high(); + let uc_cfg1_fcs_en = uc_cfg1.is_low(); defmt::println!( - "ADIN1110: CFG SPI-MODE 1-{}, CRC-bit 0-{}", + "ADIN1110: CFG SPI-MODE 1-{}, CRC-bit 0-{} FCS-{}", cfg1_spi_mode, - cfg0_without_crc + cfg0_without_crc, + uc_cfg1_fcs_en ); // Check the SPI mode selected with the "HW CFG" dip-switch @@ -172,8 +176,16 @@ async fn main(spawner: Spawner) { let state = make_static!(embassy_net_adin1110::State::<8, 8>::new()); - let (device, runner) = - embassy_net_adin1110::new(MAC, state, spe_spi, spe_int, spe_reset_n, !cfg0_without_crc).await; + let (device, runner) = embassy_net_adin1110::new( + MAC, + state, + spe_spi, + spe_int, + spe_reset_n, + !cfg0_without_crc, + uc_cfg1_fcs_en, + ) + .await; // Start task blink_led unwrap!(spawner.spawn(heartbeat_led(led_uc3_yellow))); From 5c27265a21681938819230fb3d5df1b732bd2470 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20van=20Dorst?= Date: Sun, 27 Aug 2023 23:36:16 +0200 Subject: [PATCH 05/11] Add fmt.rs to improve log/debug and embbed and PC Also add `defmt` to the features list. --- embassy-net-adin1110/Cargo.toml | 5 +- embassy-net-adin1110/src/fmt.rs | 254 +++++++++++++++++++++++++++++++ embassy-net-adin1110/src/lib.rs | 101 +++++------- embassy-net-adin1110/src/regs.rs | 14 +- 4 files changed, 308 insertions(+), 66 deletions(-) create mode 100644 embassy-net-adin1110/src/fmt.rs diff --git a/embassy-net-adin1110/Cargo.toml b/embassy-net-adin1110/Cargo.toml index e74fb7cd4..c13f10187 100644 --- a/embassy-net-adin1110/Cargo.toml +++ b/embassy-net-adin1110/Cargo.toml @@ -21,7 +21,6 @@ embassy-time = { version = "0.1.0" } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } bitfield = "0.14.0" - [dev-dependencies] # reenable when https://github.com/dbrgn/embedded-hal-mock/pull/86 is merged. #embedded-hal-mock = { git = "https://github.com/dbrgn/embedded-hal-mock", branch = "1-alpha", features = ["embedded-hal-async", "eh1"] }] } @@ -33,9 +32,11 @@ futures-test = "0.3.17" [features] default = [ ] -defmt = [ "dep:defmt" ] +defmt = [ "dep:defmt", "embedded-hal-1/defmt-03" ] +log = ["dep:log"] [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-adin1110-v$VERSION/embassy-net-adin1110/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-adin1110/src/" target = "thumbv7em-none-eabi" +features = ["defmt"] diff --git a/embassy-net-adin1110/src/fmt.rs b/embassy-net-adin1110/src/fmt.rs new file mode 100644 index 000000000..12737c690 --- /dev/null +++ b/embassy-net-adin1110/src/fmt.rs @@ -0,0 +1,254 @@ +#![macro_use] +#![allow(unused_macros)] + +use core::fmt::{Debug, Display, LowerHex}; + +#[cfg(all(feature = "defmt", feature = "log"))] +compile_error!("You may not enable both `defmt` and `log` features."); + +macro_rules! assert { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::assert!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::assert!($($x)*); + } + }; +} + +macro_rules! assert_eq { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::assert_eq!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::assert_eq!($($x)*); + } + }; +} + +macro_rules! assert_ne { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::assert_ne!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::assert_ne!($($x)*); + } + }; +} + +macro_rules! debug_assert { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::debug_assert!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::debug_assert!($($x)*); + } + }; +} + +macro_rules! debug_assert_eq { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::debug_assert_eq!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::debug_assert_eq!($($x)*); + } + }; +} + +macro_rules! debug_assert_ne { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::debug_assert_ne!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::debug_assert_ne!($($x)*); + } + }; +} + +macro_rules! todo { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::todo!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::todo!($($x)*); + } + }; +} + +macro_rules! unreachable { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::unreachable!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::unreachable!($($x)*); + } + }; +} + +macro_rules! panic { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::panic!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::panic!($($x)*); + } + }; +} + +macro_rules! trace { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::trace!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::trace!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ignored = ($( & $x ),*); + } + }; +} + +macro_rules! debug { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::debug!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::debug!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ignored = ($( & $x ),*); + } + }; +} + +macro_rules! info { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::info!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::info!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ignored = ($( & $x ),*); + } + }; +} + +macro_rules! warn { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::warn!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::warn!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ignored = ($( & $x ),*); + } + }; +} + +macro_rules! error { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::error!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::error!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ignored = ($( & $x ),*); + } + }; +} + +#[cfg(feature = "defmt")] +macro_rules! unwrap { + ($($x:tt)*) => { + ::defmt::unwrap!($($x)*) + }; +} + +#[cfg(not(feature = "defmt"))] +macro_rules! unwrap { + ($arg:expr) => { + match $crate::fmt::Try::into_result($arg) { + ::core::result::Result::Ok(t) => t, + ::core::result::Result::Err(e) => { + ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e); + } + } + }; + ($arg:expr, $($msg:expr),+ $(,)? ) => { + match $crate::fmt::Try::into_result($arg) { + ::core::result::Result::Ok(t) => t, + ::core::result::Result::Err(e) => { + ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e); + } + } + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct NoneError; + +pub trait Try { + type Ok; + type Error; + fn into_result(self) -> Result; +} + +impl Try for Option { + type Ok = T; + type Error = NoneError; + + #[inline] + fn into_result(self) -> Result { + self.ok_or(NoneError) + } +} + +impl Try for Result { + type Ok = T; + type Error = E; + + #[inline] + fn into_result(self) -> Self { + self + } +} + +pub struct Bytes<'a>(pub &'a [u8]); + +impl<'a> Debug for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> Display for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> LowerHex for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +#[cfg(feature = "defmt")] +impl<'a> defmt::Format for Bytes<'a> { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!(fmt, "{:02x}", self.0) + } +} diff --git a/embassy-net-adin1110/src/lib.rs b/embassy-net-adin1110/src/lib.rs index 8d73e024f..4af054aea 100644 --- a/embassy-net-adin1110/src/lib.rs +++ b/embassy-net-adin1110/src/lib.rs @@ -6,6 +6,9 @@ #![allow(clippy::missing_panics_doc)] #![doc = include_str!("../README.md")] +// must go first! +mod fmt; + mod crc32; mod crc8; mod mdio; @@ -20,12 +23,13 @@ use embassy_net_driver_channel as ch; use embassy_time::{Duration, Timer}; use embedded_hal_1::digital::OutputPin; use embedded_hal_async::digital::Wait; -use embedded_hal_async::spi::{Operation, SpiDevice}; +use embedded_hal_async::spi::{Error, Operation, SpiDevice}; use heapless::Vec; pub use mdio::MdioBus; pub use phy::{Phy10BaseT1x, RegsC22, RegsC45}; pub use regs::{Config0, Config2, SpiRegisters as sr, Status0, Status1}; +use crate::fmt::Bytes; use crate::regs::{LedCntrl, LedFunc, LedPol, LedPolarity, SpiHeader}; pub const PHYID: u32 = 0x0283_BC91; @@ -153,8 +157,7 @@ impl ADIN1110 { let value = u32::from_be_bytes(rx_buf[0..4].try_into().unwrap()); - #[cfg(feature = "defmt")] - defmt::trace!("REG Read {} = {:08x} SPI {:02x}", reg, value, &tx_buf); + trace!("REG Read {} = {:08x} SPI {}", reg, value, Bytes(&tx_buf)); Ok(value) } @@ -181,8 +184,7 @@ impl ADIN1110 { let _ = tx_buf.push(crc8(val.as_slice())); } - #[cfg(feature = "defmt")] - defmt::trace!("REG Write {} = {:08x} SPI {:02x}", reg, value, &tx_buf); + trace!("REG Write {} = {:08x} SPI {}", reg, value, Bytes(&tx_buf)); self.spi.write(&tx_buf).await.map_err(AdinError::Spi) } @@ -219,8 +221,7 @@ impl ADIN1110 { let packet_size = fifo_frame_size - FRAME_HEADER_LEN - FCS_LEN; if packet_size > frame.len() { - #[cfg(feature = "defmt")] - defmt::trace!("MAX: {} WANT: {}", frame.len(), packet_size); + trace!("MAX: {} WANT: {}", frame.len(), packet_size); return Err(AdinError::PACKET_TOO_BIG); } @@ -333,14 +334,13 @@ impl ADIN1110 { self.write_reg(sr::TX_FSIZE, send_len).await?; - #[cfg(feature = "defmt")] - defmt::trace!( - "TX: hdr {} [{}] {:02x}-{:02x}-{:02x} SIZE: {}", + trace!( + "TX: hdr {} [{}] {}-{}-{} SIZE: {}", head_data.len(), frame.len(), - head_data.as_slice(), - frame, - tail_data.as_slice(), + Bytes(head_data.as_slice()), + Bytes(frame), + Bytes(tail_data.as_slice()), send_len, ); @@ -445,16 +445,14 @@ impl<'d, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, SPI, INT, RST> { let (state_chan, mut rx_chan, mut tx_chan) = self.ch.split(); loop { - #[cfg(feature = "defmt")] - defmt::debug!("Waiting for interrupts"); + debug!("Waiting for interrupts"); match select(self.int.wait_for_low(), tx_chan.tx_buf()).await { Either::First(_) => { let mut status1_clr = Status1(0); let mut status1 = Status1(self.mac.read_reg(sr::STATUS1).await.unwrap()); while status1.p1_rx_rdy() { - #[cfg(feature = "defmt")] - defmt::debug!("alloc RX packet buffer"); + debug!("alloc RX packet buffer"); match select(rx_chan.rx_buf(), tx_chan.tx_buf()).await { // Handle frames that needs to transmit from the wire. // Note: rx_chan.rx_buf() channel don´t accept new request @@ -466,22 +464,18 @@ impl<'d, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, SPI, INT, RST> { } Err(e) => match e { AdinError::PACKET_TOO_BIG => { - #[cfg(feature = "defmt")] - defmt::error!("RX Packet too big, DROP"); + error!("RX Packet too big, DROP"); self.mac.write_reg(sr::FIFO_CLR, 1).await.unwrap(); } AdinError::PACKET_TOO_SMALL => { - #[cfg(feature = "defmt")] - defmt::error!("RX Packet too small, DROP"); + error!("RX Packet too small, DROP"); self.mac.write_reg(sr::FIFO_CLR, 1).await.unwrap(); } - AdinError::Spi(_) => { - #[cfg(feature = "defmt")] - defmt::error!("RX Spi error") + AdinError::Spi(e) => { + error!("RX Spi error {}", e.kind()); } _ => { - #[cfg(feature = "defmt")] - defmt::error!("RX Error") + error!("RX Error"); } }, }, @@ -496,21 +490,18 @@ impl<'d, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, SPI, INT, RST> { let status0 = Status0(self.mac.read_reg(sr::STATUS0).await.unwrap()); if status1.0 & !0x1b != 0 { - #[cfg(feature = "defmt")] - defmt::error!("SPE CHIP STATUS 0:{:08x} 1:{:08x}", status0.0, status1.0); + error!("SPE CHIP STATUS 0:{:08x} 1:{:08x}", status0.0, status1.0); } if status1.tx_rdy() { status1_clr.set_tx_rdy(true); - #[cfg(feature = "defmt")] - defmt::info!("TX_DONE"); + trace!("TX_DONE"); } if status1.link_change() { let link = status1.p1_link_status(); self.is_link_up = link; - #[cfg(feature = "defmt")] if link { let link_status = self .mac @@ -530,9 +521,9 @@ impl<'d, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, SPI, INT, RST> { .await .unwrap(); - defmt::info!("LINK Changed: Link Up, Volt: {} V p-p, MSE: {:0004}", volt, mse); + info!("LINK Changed: Link Up, Volt: {} V p-p, MSE: {:0004}", volt, mse); } else { - defmt::info!("LINK Changed: Link Down"); + info!("LINK Changed: Link Down"); } state_chan.set_link_state(if link { LinkState::Up } else { LinkState::Down }); @@ -540,50 +531,42 @@ impl<'d, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, SPI, INT, RST> { } if status1.tx_ecc_err() { - #[cfg(feature = "defmt")] - defmt::error!("SPI TX_ECC_ERR error, CLEAR TX FIFO"); + error!("SPI TX_ECC_ERR error, CLEAR TX FIFO"); self.mac.write_reg(sr::FIFO_CLR, 2).await.unwrap(); status1_clr.set_tx_ecc_err(true); } if status1.rx_ecc_err() { - #[cfg(feature = "defmt")] - defmt::error!("SPI RX_ECC_ERR error"); + error!("SPI RX_ECC_ERR error"); status1_clr.set_rx_ecc_err(true); } if status1.spi_err() { - #[cfg(feature = "defmt")] - defmt::error!("SPI SPI_ERR CRC error"); + error!("SPI SPI_ERR CRC error"); status1_clr.set_spi_err(true); } if status0.phyint() { - #[cfg_attr(not(feature = "defmt"), allow(unused_variables))] let crsm_irq_st = self .mac .read_cl45(MDIO_PHY_ADDR, RegsC45::DA1E::CRSM_IRQ_STATUS.into()) .await .unwrap(); - #[cfg_attr(not(feature = "defmt"), allow(unused_variables))] let phy_irq_st = self .mac .read_cl45(MDIO_PHY_ADDR, RegsC45::DA1F::PHY_SYBSYS_IRQ_STATUS.into()) .await .unwrap(); - #[cfg(feature = "defmt")] - defmt::warn!( + warn!( "SPE CHIP PHY CRSM_IRQ_STATUS {:04x} PHY_SUBSYS_IRQ_STATUS {:04x}", - crsm_irq_st, - phy_irq_st + crsm_irq_st, phy_irq_st ); } if status0.txfcse() { - #[cfg(feature = "defmt")] - defmt::error!("SPE CHIP PHY TX Frame CRC error"); + error!("Ethernet Frame FCS and calc FCS don't match!"); } // Clear status0 @@ -613,8 +596,7 @@ pub async fn new (Device<'_>, Runner<'_, SPI, INT, RST>) { use crate::regs::{IMask0, IMask1}; - #[cfg(feature = "defmt")] - defmt::info!("INIT ADIN1110"); + info!("INIT ADIN1110"); // Reset sequence reset.set_low().unwrap(); @@ -634,23 +616,20 @@ pub async fn new) -> core::fmt::Result { + write!(f, "{self:?}") + } +} + impl From for u16 { fn from(val: SpiRegisters) -> Self { val as u16 @@ -68,7 +76,7 @@ impl From for SpiRegisters { 0x73 => Self::ADDR_MSK_UPR1, 0x90 => Self::RX_FSIZE, 0x91 => Self::RX, - e => panic!("Unknown value {e}"), + e => panic!("Unknown value {}", e), } } } @@ -313,7 +321,7 @@ impl From for LedFunc { 26 => LedFunc::Clk25Ref, 27 => LedFunc::TxTCLK, 28 => LedFunc::Clk120MHz, - e => panic!("Invalid value {e}"), + e => panic!("Invalid value {}", e), } } } @@ -369,7 +377,7 @@ impl From for LedPol { 0 => LedPol::AutoSense, 1 => LedPol::ActiveHigh, 2 => LedPol::ActiveLow, - e => panic!("Invalid value {e}"), + e => panic!("Invalid value {}", e), } } } From e08dbcd027feb9bad232caa2adc4433b6f4885d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20van=20Dorst?= Date: Mon, 28 Aug 2023 00:02:09 +0200 Subject: [PATCH 06/11] embassy-net-adin1110: bump version v0.2.0 --- embassy-net-adin1110/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-net-adin1110/Cargo.toml b/embassy-net-adin1110/Cargo.toml index c13f10187..26111c095 100644 --- a/embassy-net-adin1110/Cargo.toml +++ b/embassy-net-adin1110/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embassy-net-adin1110" -version = "0.1.0" +version = "0.2.0" description = "embassy-net driver for the ADIN1110 ethernet chip" keywords = ["embedded", "ADIN1110", "embassy-net", "embedded-hal-async", "ethernet", "async"] categories = ["embedded", "hardware-support", "no-std", "network-programming", "async"] From 91bb3aae3fdb81f8aafceb6540c5af128b05e718 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20van=20Dorst?= Date: Mon, 28 Aug 2023 00:03:50 +0200 Subject: [PATCH 07/11] stm32l4: bump embassy-net-adin1110 to v0.2.0 --- examples/stm32l4/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index e5be94eda..e864759b0 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -1,7 +1,7 @@ [package] edition = "2021" name = "embassy-stm32l4-examples" -version = "0.1.0" +version = "0.1.1" license = "MIT OR Apache-2.0" [dependencies] @@ -12,7 +12,7 @@ embassy-executor = { version = "0.3.0", path = "../../embassy-executor", feature embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768", "unstable-traits", "nightly"] } embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } -embassy-net-adin1110 = { version = "0.1.0", path = "../../embassy-net-adin1110", default-features = false } +embassy-net-adin1110 = { version = "0.2.0", path = "../../embassy-net-adin1110" } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "udp", "tcp", "dhcpv4", "medium-ethernet"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embedded-io-async = { version = "0.5.0", features = ["defmt-03"] } From d02886786e4c496dc5a2c8851a7a01764905182b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20van=20Dorst?= Date: Mon, 28 Aug 2023 19:00:00 +0200 Subject: [PATCH 08/11] Show the error type --- embassy-net-adin1110/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-net-adin1110/src/lib.rs b/embassy-net-adin1110/src/lib.rs index 4af054aea..9eca801f3 100644 --- a/embassy-net-adin1110/src/lib.rs +++ b/embassy-net-adin1110/src/lib.rs @@ -474,8 +474,8 @@ impl<'d, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, SPI, INT, RST> { AdinError::Spi(e) => { error!("RX Spi error {}", e.kind()); } - _ => { - error!("RX Error"); + e => { + error!("RX Error {:?}", e); } }, }, From 6e616a6fe60f1ff9355888724af7c201ae70ed1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20van=20Dorst?= Date: Mon, 28 Aug 2023 19:23:15 +0200 Subject: [PATCH 09/11] Update comment about turn around byte. --- embassy-net-adin1110/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-net-adin1110/src/lib.rs b/embassy-net-adin1110/src/lib.rs index 9eca801f3..e0af7bde9 100644 --- a/embassy-net-adin1110/src/lib.rs +++ b/embassy-net-adin1110/src/lib.rs @@ -77,7 +77,7 @@ const ETH_MIN_WITHOUT_FCS_LEN: usize = ETH_MIN_LEN - FCS_LEN; const SPI_HEADER_LEN: usize = 2; /// SPI Header CRC length const SPI_HEADER_CRC_LEN: usize = 1; -/// SPI Header Trun Around length +/// SPI Header Turn Around length const SPI_HEADER_TA_LEN: usize = 1; /// Frame Header length const FRAME_HEADER_LEN: usize = 2; @@ -137,7 +137,7 @@ impl ADIN1110 { let _ = tx_buf.push(crc8(&tx_buf)); } - // Turn around byte, TODO: Unknown that this is. + // Turn around byte, give the chip the time to access/setup the answer data. let _ = tx_buf.push(TURN_AROUND_BYTE); let mut rx_buf = [0; 5]; From 7fc17bc15001449ea427cb0bff3557922ae595ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20van=20Dorst?= Date: Mon, 28 Aug 2023 19:29:32 +0200 Subject: [PATCH 10/11] embassy-net-adin1110 bump deps. --- embassy-net-adin1110/Cargo.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/embassy-net-adin1110/Cargo.toml b/embassy-net-adin1110/Cargo.toml index 26111c095..8de8eadea 100644 --- a/embassy-net-adin1110/Cargo.toml +++ b/embassy-net-adin1110/Cargo.toml @@ -12,12 +12,12 @@ edition = "2021" [dependencies] heapless = "0.7.16" defmt = { version = "0.3", optional = true } -log = { version = "0.4.4", default-features = false, optional = true } +log = { version = "0.4", default-features = false, optional = true } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" } embedded-hal-async = { version = "=1.0.0-rc.1" } embedded-hal-bus = { version = "=0.1.0-rc.1", features = ["async"] } embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel" } -embassy-time = { version = "0.1.0" } +embassy-time = { version = "0.1.3" } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } bitfield = "0.14.0" @@ -27,8 +27,8 @@ bitfield = "0.14.0" embedded-hal-mock = { git = "https://github.com/newAM/embedded-hal-mock", branch = "eh1-rc.1", features = ["embedded-hal-async", "eh1"] } crc = "3.0.1" env_logger = "0.10" -critical-section = { version = "1.1.1", features = ["std"] } -futures-test = "0.3.17" +critical-section = { version = "1.1.2", features = ["std"] } +futures-test = "0.3.28" [features] default = [ ] From 1db00f54399307e4ec922965f5ca49547ccd14a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20van=20Dorst?= Date: Mon, 28 Aug 2023 20:00:59 +0200 Subject: [PATCH 11/11] embassy-net-adin1110: Add basic benchmark results. --- embassy-net-adin1110/README.md | 37 ++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/embassy-net-adin1110/README.md b/embassy-net-adin1110/README.md index 3c2804183..8ea10b714 100644 --- a/embassy-net-adin1110/README.md +++ b/embassy-net-adin1110/README.md @@ -30,8 +30,8 @@ Currently only `Generic` SPI with or without CRC is supported. ## Hardware -- Tested on [`Analog Devices EVAL-ADIN1110EBZ`](https://www.analog.com/en/design-center/evaluation-hardware-and-software/evaluation-boards-kits/eval-adin1110.html) with an `STM32L4S5QII3P`, see [`spe_adin1110_http_server`](../examples/stm32l4/src/bin/spe_adin1110_http_server.rs) dor an example. -- [`SparkFun MicroMod Single Pair Ethernet Function Board`](https://www.sparkfun.com/products/19038) or [`SparkFun MicroMod Single Pair Ethernet Kit`](https://www.sparkfun.com/products/19628), supporting multiple microcontrollers. **Make sure to check if it's a microcontroller that is supported by Embassy!** +- Tested on [`Analog Devices EVAL-ADIN1110EBZ`](https://www.analog.com/en/design-center/evaluation-hardware-and-software/evaluation-boards-kits/eval-adin1110.html) with an `STM32L4S5QII3P`, see [`spe_adin1110_http_server`](../examples/stm32l4/src/bin/spe_adin1110_http_server.rs) for an example. +- [`SparkFun MicroMod Single Pair Ethernet Function Board`](https://www.sparkfun.com/products/19038) or [`SparkFun MicroMod Single Pair Ethernet Kit (End Of Life)`](https://www.sparkfun.com/products/19628), supporting multiple microcontrollers. **Make sure to check if it's a microcontroller that is supported by Embassy!** ## Other SPE chips @@ -44,6 +44,39 @@ ADIN1110 library can tested on the host with a mock SPI driver. $ `cargo test --target x86_64-unknown-linux-gnu` +## Benchmark + +- Benchmarked on [`Analog Devices EVAL-ADIN1110EBZ`](https://www.analog.com/en/design-center/evaluation-hardware-and-software/evaluation-boards-kits/eval-adin1110.html), with [`spe_adin1110_http_server`](../examples/stm32l4/src/bin/spe_adin1110_http_server.rs) example. + +Basic `ping` benchmark +```rust,ignore +# ping -c 60 + +60 packets transmitted, 60 received, 0% packet loss, time 59066ms +rtt min/avg/max/mdev = 1.089/1.161/1.237/0.018 ms + +# ping -s 1472 -M do -c 60 + +60 packets transmitted, 60 received, 0% packet loss, time 59066ms +rtt min/avg/max/mdev = 5.122/5.162/6.177/0.133 ms +``` + +HTTP load generator benchmark with [`oha`](https://github.com/hatoo/oha) +```rust,ignore +# oha -c 1 http:// -z 60s +Summary: + Success rate: 50.00% + Total: 60.0005 secs + Slowest: 0.0055 secs + Fastest: 0.0033 secs + Average: 0.0034 secs + Requests/sec: 362.1971 + + Total data: 2.99 MiB + Size/request: 289 B + Size/sec: 51.11 KiB +``` + ## License This work is licensed under either of