diff --git a/firmware/zynq7000-hal/Cargo.toml b/firmware/zynq7000-hal/Cargo.toml index d967dd9..7883c56 100644 --- a/firmware/zynq7000-hal/Cargo.toml +++ b/firmware/zynq7000-hal/Cargo.toml @@ -49,14 +49,22 @@ serde = { version = "1", optional = true, features = ["derive"] } defmt = { version = "1", optional = true } [features] -# Activates the embassy-time support using the global timer counter. -time-driver-gtc = ["dep:embassy-time-driver", "dep:embassy-time-queue-utils", "dep:once_cell"] +default = ["eth-cache-maintenance"] alloc = [] defmt = ["dep:defmt", "fugit/defmt", "zynq7000/defmt"] std = ["thiserror/std", "alloc"] + +# Activates the embassy-time support using the global timer counter. +time-driver-gtc = ["dep:embassy-time-driver", "dep:embassy-time-queue-utils", "dep:once_cell"] + # These devices have a lower pin count. 7z010-7z007s-clg225 = [] +# Enabled by default to run cache maintenance operations on Ethernet DMA buffers. +# Only disable if the Ethernet DMA buffers are placed in a memory region where +# caching is disabled. +eth-cache-maintenance = [] + [dev-dependencies] approx = "0.5" diff --git a/firmware/zynq7000-hal/src/eth/smoltcp.rs b/firmware/zynq7000-hal/src/eth/smoltcp.rs index 7a494e6..df65a8c 100644 --- a/firmware/zynq7000-hal/src/eth/smoltcp.rs +++ b/firmware/zynq7000-hal/src/eth/smoltcp.rs @@ -1,11 +1,12 @@ //! smoltcp driver for the Zynq 7000 ethernet peripheral. use arbitrary_int::u14; -pub use crate::eth::{EthernetId, InterruptResult}; -use crate::{ - cache::{CACHE_LINE_SIZE, clean_and_invalidate_data_cache_range, invalidate_data_cache_range}, - eth::{rx_descr, tx_descr}, +#[cfg(feature = "eth-cache-maintenance")] +use crate::cache::{ + clean_and_invalidate_data_cache_range, invalidate_data_cache_range, CACHE_LINE_SIZE, }; +use crate::eth::{rx_descr, tx_descr}; +pub use crate::eth::{EthernetId, InterruptResult}; /// This interrupt handler should be called when a Gigabit Ethernet interrupt occurs. pub fn on_interrupt(eth_id: EthernetId) -> InterruptResult { @@ -46,16 +47,21 @@ impl SmoltcpRxToken<'_> { // The DMA will write the received frame into DDR. The L1 and L2 cache lines for the // particular reception address need to be invalidated, to avoid fetching stale data from // the cache instead of the DDR. + #[cfg(feature = "eth-cache-maintenance")] let clean_invalidate_len = (self.rx_size + CACHE_LINE_SIZE - 1) & !(CACHE_LINE_SIZE - 1); + #[cfg(feature = "eth-cache-maintenance")] invalidate_data_cache_range(self.rx_buf.0.as_ptr() as u32, clean_invalidate_len) .expect("RX buffer or buffer size not aligned to cache line size"); + log::debug!("eth rx {} bytes", self.rx_size); log::trace!("rx data: {:x?}", &self.rx_buf.0[0..self.rx_size]); let result = f(&mut self.rx_buf.0[0..self.rx_size]); self.descr_list.clear_slot(self.slot_index); + // Okay, this is weird, but we have to do this. I encountered this bug where ICMP replies // were duplicated after the descriptor rings wrapped. My theory is that there is // some data in the cache after the embassy reception function which needs to be cleaned. + #[cfg(feature = "eth-cache-maintenance")] clean_and_invalidate_data_cache_range(self.rx_buf.0.as_ptr() as u32, clean_invalidate_len) .expect("RX buffer or buffer size not aligned to cache line size"); result @@ -97,11 +103,15 @@ impl SmoltcpTxToken<'_> { let buffer = self.tx_bufs.get_mut(tx_idx).unwrap(); let addr = buffer.0.as_ptr() as u32; let result = f(&mut buffer.0[0..len]); - let clean_invalidate_len = (len + CACHE_LINE_SIZE - 1) & !(CACHE_LINE_SIZE - 1); + // DMA accesses the DDR memory directly, so we need to flush everything that might // still be in the L1 or L2 cache to the DDR. + #[cfg(feature = "eth-cache-maintenance")] + let clean_invalidate_len = (len + CACHE_LINE_SIZE - 1) & !(CACHE_LINE_SIZE - 1); + #[cfg(feature = "eth-cache-maintenance")] clean_and_invalidate_data_cache_range(buffer.0.as_ptr() as u32, clean_invalidate_len) .expect("TX buffer or buffer size not aligned to cache line size"); + log::debug!("eth tx {len} bytes"); log::trace!("tx data: {:x?}", &buffer.0[0..len]); self.descr_list