Skip to content

Commit 7a7dbad

Browse files
committed
Rework Ring layout and optimize a little
1 parent f87108b commit 7a7dbad

File tree

13 files changed

+1328
-1047
lines changed

13 files changed

+1328
-1047
lines changed

Cargo.toml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ ieee802_3_miim = "0.8"
2929
cortex-m = "0.7"
3030
log = { version = "0.4", optional = true }
3131
defmt = { version = "0.3", optional = true }
32+
futures = { version = "0.3", default-features = false, features = ["async-await"] }
3233

3334
[dependencies.smoltcp]
3435
version = "0.9"
@@ -68,12 +69,17 @@ smoltcp-phy = ["smoltcp"]
6869
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
6970
cortex-m-rt = "0.7"
7071
fugit = "0.3"
71-
cortex-m-rtic = "1.0"
7272
defmt-rtt = "0.4"
7373
panic-probe = { version = "0.3", features = [ "print-defmt" ] }
7474
systick-monotonic = "1.0"
7575
smoltcp = { version = "0.9", features = [ "medium-ethernet", "proto-ipv4", "socket-udp", "socket-tcp", "defmt" ], default-features = false }
7676

77+
[dev-dependencies.rtic]
78+
package = "cortex-m-rtic"
79+
version = "1.0"
80+
# git = "https://github.com/rtic-rs/cortex-m-rtic.git"
81+
# rev = "613b3c59fc841caa3ca8c4d7ac440fbfa2f71fd1"
82+
7783
# This isn't an actual example. It just exists so we can easily
7884
# test the common items :)
7985
[[example]]

src/dma/mod.rs

Lines changed: 134 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
//! Ethernet DMA access and configuration.
22
3-
use core::borrow::Borrow;
3+
use core::task::Poll;
44

55
use cortex_m::peripheral::NVIC;
6+
use futures::task::AtomicWaker;
67

78
use crate::{peripherals::ETHERNET_DMA, stm32::Interrupt};
89

@@ -16,12 +17,10 @@ pub(crate) mod desc;
1617
pub(crate) mod ring;
1718

1819
mod rx;
19-
use rx::RxRing;
20-
pub use rx::{RxError, RxPacket, RxRingEntry};
20+
pub use rx::{RunningState as RxRunningState, RxError, RxPacket, RxRing, RxRingEntry};
2121

2222
mod tx;
23-
use tx::TxRing;
24-
pub use tx::{TxError, TxRingEntry};
23+
pub use tx::{RunningState as TxRunningState, TxError, TxPacket, TxRing, TxRingEntry};
2524

2625
#[cfg(feature = "ptp")]
2726
use crate::ptp::Timestamp;
@@ -32,17 +31,11 @@ pub use packet_id::PacketId;
3231
/// From the datasheet: *VLAN Frame maxsize = 1522*
3332
pub(crate) const MTU: usize = 1522;
3433

35-
/// An error that can occur when retrieving a timestamp from an
36-
/// RX or TX descriptor handled by the DMA.
37-
#[cfg(feature = "ptp")]
3834
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
39-
pub enum TimestampError {
40-
/// The descriptor with the given packet ID has not been
41-
/// timestamped yet.
42-
NotYetTimestamped,
43-
/// No active descriptors have the given packet ID.
44-
IdNotFound,
45-
}
35+
#[derive(Clone, Copy, Debug, PartialEq)]
36+
/// This struct is returned if a packet ID is not associated
37+
/// with any TX or RX descriptors.
38+
pub struct PacketIdNotFound;
4639

4740
/// Ethernet DMA.
4841
pub struct EthernetDMA<'rx, 'tx> {
@@ -122,8 +115,8 @@ impl<'rx, 'tx> EthernetDMA<'rx, 'tx> {
122115

123116
let mut dma = EthernetDMA {
124117
eth_dma,
125-
rx_ring: RxRing::new(rx_buffer),
126-
tx_ring: TxRing::new(tx_buffer),
118+
rx_ring: RxRing::new(rx_buffer, EthernetDMA::rx_waker()),
119+
tx_ring: TxRing::new(tx_buffer, EthernetDMA::tx_waker()),
127120
};
128121

129122
dma.rx_ring.start(&dma.eth_dma);
@@ -132,10 +125,26 @@ impl<'rx, 'tx> EthernetDMA<'rx, 'tx> {
132125
dma
133126
}
134127

128+
pub(crate) fn rx_waker() -> &'static AtomicWaker {
129+
static WAKER: AtomicWaker = AtomicWaker::new();
130+
&WAKER
131+
}
132+
133+
pub(crate) fn tx_waker() -> &'static AtomicWaker {
134+
static WAKER: AtomicWaker = AtomicWaker::new();
135+
&WAKER
136+
}
137+
138+
/// Split the [`EthernetDMA`] into concurrently operating send and
139+
/// receive parts.
140+
pub fn split(&mut self) -> (&mut RxRing<'rx>, &mut TxRing<'tx>) {
141+
(&mut self.rx_ring, &mut self.tx_ring)
142+
}
143+
135144
/// Enable RX and TX interrupts
136145
///
137146
/// In your handler you must call
138-
/// [`eth_interrupt_handler()`](fn.eth_interrupt_handler.html) to
147+
/// [`EthernetDMA::interrupt_handler()`] to
139148
/// clear interrupt pending bits. Otherwise the interrupt will
140149
/// reoccur immediately.
141150
pub fn enable_interrupt(&self) {
@@ -158,79 +167,78 @@ impl<'rx, 'tx> EthernetDMA<'rx, 'tx> {
158167
}
159168
}
160169

161-
/// Calls [`eth_interrupt_handler()`]
162-
#[cfg_attr(
163-
feature = "ptp",
164-
doc = " and collects/caches TX timestamps. (See [`EthernetDMA::get_timestamp_for_id`] for retrieval)"
165-
)]
166-
pub fn interrupt_handler(&mut self) -> InterruptReasonSummary {
167-
let eth_dma = &self.eth_dma;
168-
let status = eth_interrupt_handler_impl(eth_dma);
169-
#[cfg(feature = "ptp")]
170-
self.collect_timestamps();
170+
/// Handle the DMA parts of the ETH interrupt.
171+
pub fn interrupt_handler() -> InterruptReasonSummary {
172+
// SAFETY: we only perform atomic reads/writes through `eth_dma`.
173+
let eth_dma = unsafe { &*ETHERNET_DMA::ptr() };
174+
175+
let status = eth_dma.dmasr.read();
176+
177+
let status = InterruptReasonSummary {
178+
is_rx: status.rs().bit_is_set(),
179+
is_tx: status.ts().bit_is_set(),
180+
is_error: status.ais().bit_is_set(),
181+
};
182+
183+
eth_dma
184+
.dmasr
185+
.write(|w| w.nis().set_bit().ts().set_bit().rs().set_bit());
186+
187+
if status.is_tx {
188+
EthernetDMA::tx_waker().wake();
189+
}
190+
191+
if status.is_rx {
192+
EthernetDMA::rx_waker().wake();
193+
}
194+
171195
status
172196
}
173197

198+
/// Try to receive a packet.
199+
///
200+
/// If no packet is available, this function returns [`Err(RxError::WouldBlock)`](RxError::WouldBlock).
201+
///
202+
/// It may also return another kind of [`RxError`].
203+
pub fn recv_next(&mut self, packet_id: Option<PacketId>) -> Result<RxPacket, RxError> {
204+
self.rx_ring.recv_next(packet_id.map(Into::into))
205+
}
206+
207+
/// Receive a packet.
208+
pub async fn recv(&mut self) -> RxPacket {
209+
self.rx_ring.recv().await
210+
}
211+
174212
/// Is Rx DMA currently running?
175213
///
176-
/// It stops if the ring is full. Call `recv_next()` to free an
214+
/// It stops if the ring is full. Call [`EthernetDMA::recv_next()`] to free an
177215
/// entry and to demand poll from the hardware.
178216
pub fn rx_is_running(&self) -> bool {
179217
self.rx_ring.running_state().is_running()
180218
}
181219

182-
/// Receive the next packet (if any is ready), or return [`Err`]
183-
/// immediately.
184-
pub fn recv_next(&mut self, packet_id: Option<PacketId>) -> Result<RxPacket, RxError> {
185-
self.rx_ring.recv_next(packet_id.map(|p| p.into()))
186-
}
187-
188220
/// Is Tx DMA currently running?
189221
pub fn tx_is_running(&self) -> bool {
190222
self.tx_ring.is_running()
191223
}
192224

193-
/// Send a packet
194-
pub fn send<F: FnOnce(&mut [u8]) -> R, R>(
225+
/// Try to send a packet with data.
226+
///
227+
/// If there are no free TX slots, this function will
228+
/// return [`Err(TxError::WouldBlock)`](TxError::WouldBlock).
229+
pub fn send<F>(
195230
&mut self,
196231
length: usize,
197232
packet_id: Option<PacketId>,
198233
f: F,
199-
) -> Result<R, TxError> {
200-
self.tx_ring.send(length, packet_id.map(|p| p.into()), f)
201-
}
202-
203-
#[cfg(feature = "ptp")]
204-
/// Get a timestamp for the given ID
205-
///
206-
/// Both RX and TX timestamps can be obtained reliably as follows:
207-
/// 1. When an ethernet interrupt occurs, call [`EthernetDMA::interrupt_handler`] (_not_ [`eth_interrupt_handler`]).
208-
/// 2. Before calling [`interrupt_handler`](EthernetDMA::interrupt_handler) again, retrieve timestamps of sent and received frames using this function.
209-
///
210-
/// Retrieving RX timestamps can also be done using [`RxPacket::timestamp`].
211-
pub fn get_timestamp_for_id<'a, PKT>(
212-
&mut self,
213-
packet_id: PKT,
214-
) -> Result<Timestamp, TimestampError>
234+
) -> Result<(), TxError>
215235
where
216-
PKT: Into<PacketId>,
236+
F: FnOnce(&mut [u8]),
217237
{
218-
let Self {
219-
tx_ring, rx_ring, ..
220-
} = self;
221-
222-
let internal_packet_id = packet_id.into();
223-
224-
tx_ring
225-
.get_timestamp_for_id(internal_packet_id.clone())
226-
.or_else(|_| rx_ring.get_timestamp_for_id(internal_packet_id))
227-
}
228-
229-
/// Collect the timestamps from the TX descriptor
230-
/// ring
231-
#[cfg(feature = "ptp")]
232-
fn collect_timestamps(&mut self) {
233-
self.tx_ring.collect_timestamps();
238+
let mut tx_packet = self.tx_ring.send_next(length, packet_id)?;
239+
f(&mut tx_packet);
240+
tx_packet.send();
241+
Ok(())
234242
}
235243

236244
/// Check if there is a packet available for reading.
@@ -250,6 +258,62 @@ impl<'rx, 'tx> EthernetDMA<'rx, 'tx> {
250258
}
251259
}
252260

261+
#[cfg(feature = "ptp")]
262+
impl EthernetDMA<'_, '_> {
263+
/// Try to get the timestamp for the given packet ID.
264+
///
265+
/// This function will attempt to find both RX and TX timestamps,
266+
/// so make sure that the provided packet ID is unique between the two.
267+
pub fn poll_timestamp(
268+
&self,
269+
packet_id: &PacketId,
270+
) -> Poll<Result<Option<Timestamp>, PacketIdNotFound>> {
271+
// Check if it's a TX packet
272+
let tx = self.poll_tx_timestamp(packet_id);
273+
274+
if tx != Poll::Ready(Err(PacketIdNotFound)) {
275+
return tx;
276+
}
277+
278+
// It's not a TX packet, check if it's an RX packet
279+
Poll::Ready(self.rx_timestamp(packet_id))
280+
}
281+
282+
/// Get the RX timestamp for the given packet ID.
283+
pub fn rx_timestamp(
284+
&self,
285+
packet_id: &PacketId,
286+
) -> Result<Option<Timestamp>, PacketIdNotFound> {
287+
self.rx_ring.timestamp(packet_id)
288+
}
289+
290+
/// Blockingly wait until the TX timestamp for
291+
/// the given ID is available.
292+
pub fn wait_for_tx_timestamp(
293+
&self,
294+
packet_id: &PacketId,
295+
) -> Result<Option<Timestamp>, PacketIdNotFound> {
296+
self.tx_ring.wait_for_timestamp(packet_id)
297+
}
298+
299+
/// Poll to check if the TX timestamp for the given
300+
/// ID is available.
301+
pub fn poll_tx_timestamp(
302+
&self,
303+
packet_id: &PacketId,
304+
) -> Poll<Result<Option<Timestamp>, PacketIdNotFound>> {
305+
self.tx_ring.poll_timestamp(packet_id)
306+
}
307+
308+
/// Get the TX timestamp for the given ID.
309+
pub async fn tx_timestamp(
310+
&mut self,
311+
packet_id: &PacketId,
312+
) -> Result<Option<Timestamp>, PacketIdNotFound> {
313+
self.tx_ring.timestamp(packet_id).await
314+
}
315+
}
316+
253317
/// A summary of the reasons for the interrupt
254318
/// that occured
255319
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -262,30 +326,3 @@ pub struct InterruptReasonSummary {
262326
/// The interrupt was caused by an error event.
263327
pub is_error: bool,
264328
}
265-
266-
/// The handler for `ETH` interrupts.
267-
///
268-
/// There are two ways to call this:
269-
///
270-
/// * Indirectly by using [`EthernetDMA::interrupt_handler`] driver instance that your interrupt handler has access to.
271-
/// * By unsafely getting `Peripherals`.
272-
pub fn eth_interrupt_handler(eth_dma: &crate::hal::pac::ETHERNET_DMA) -> InterruptReasonSummary {
273-
let eth_dma: &ETHERNET_DMA = eth_dma.borrow();
274-
eth_interrupt_handler_impl(eth_dma)
275-
}
276-
277-
fn eth_interrupt_handler_impl(eth_dma: &ETHERNET_DMA) -> InterruptReasonSummary {
278-
let status = eth_dma.dmasr.read();
279-
280-
let status = InterruptReasonSummary {
281-
is_rx: status.rs().bit_is_set(),
282-
is_tx: status.ts().bit_is_set(),
283-
is_error: status.ais().bit_is_set(),
284-
};
285-
286-
eth_dma
287-
.dmasr
288-
.write(|w| w.nis().set_bit().ts().set_bit().rs().set_bit());
289-
290-
status
291-
}

src/dma/packet_id.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
#[cfg_attr(
77
feature = "ptp",
88
doc = "
9-
The main use is obtaining timestamps for frames using [`EthernetDMA::get_timestamp_for_id`](crate::EthernetDMA::get_timestamp_for_id)
9+
The main use is obtaining timestamps for frames using [`EthernetDMA::poll_timestamp`](crate::EthernetDMA::poll_timestamp)
1010
"
1111
)]
1212
#[cfg_attr(feature = "defmt", derive(defmt::Format))]

0 commit comments

Comments
 (0)