From 7e269f6f1721ef5ca07c6e7354f2dd8b164644b7 Mon Sep 17 00:00:00 2001 From: xoviat Date: Thu, 3 Aug 2023 21:12:34 -0500 Subject: [PATCH 01/77] stm32/dma: consolidate ringbuf --- embassy-stm32/src/dma/bdma.rs | 70 +++++-------------- embassy-stm32/src/dma/dma.rs | 66 ++++-------------- embassy-stm32/src/dma/ringbuffer.rs | 103 +++++++++++++++++++++++----- 3 files changed, 114 insertions(+), 125 deletions(-) diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index 60f4fbd09..e79a159ef 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -393,6 +393,10 @@ impl<'a, C: Channel> DmaCtrl for DmaCtrlImpl<'a, C> { fn reset_complete_count(&mut self) -> usize { STATE.complete_count[self.0.index()].swap(0, Ordering::AcqRel) } + + fn set_waker(&mut self, waker: &Waker) { + STATE.ch_wakers[self.0.index()].register(waker); + } } pub struct ReadableRingBuffer<'a, C: Channel, W: Word> { @@ -463,7 +467,7 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> { } pub fn clear(&mut self) { - self.ringbuf.clear(DmaCtrlImpl(self.channel.reborrow())); + self.ringbuf.clear(&mut DmaCtrlImpl(self.channel.reborrow())); } /// Read elements from the ring buffer @@ -472,7 +476,7 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> { /// The length remaining is the capacity, ring_buf.len(), less the elements remaining after the read /// OverrunError is returned if the portion to be read was overwritten by the DMA controller. pub fn read(&mut self, buf: &mut [W]) -> Result<(usize, usize), OverrunError> { - self.ringbuf.read(DmaCtrlImpl(self.channel.reborrow()), buf) + self.ringbuf.read(&mut DmaCtrlImpl(self.channel.reborrow()), buf) } /// Read an exact number of elements from the ringbuffer. @@ -487,30 +491,9 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> { /// - If M equals N/2 or N/2 divides evenly into M, this function will return every N/2 elements read on the DMA source. /// - Otherwise, this function may need up to N/2 extra elements to arrive before returning. pub async fn read_exact(&mut self, buffer: &mut [W]) -> Result { - use core::future::poll_fn; - use core::sync::atomic::compiler_fence; - - let mut read_data = 0; - let buffer_len = buffer.len(); - - poll_fn(|cx| { - self.set_waker(cx.waker()); - - compiler_fence(Ordering::SeqCst); - - match self.read(&mut buffer[read_data..buffer_len]) { - Ok((len, remaining)) => { - read_data += len; - if read_data == buffer_len { - Poll::Ready(Ok(remaining)) - } else { - Poll::Pending - } - } - Err(e) => Poll::Ready(Err(e)), - } - }) - .await + self.ringbuf + .read_exact(&mut DmaCtrlImpl(self.channel.reborrow()), buffer) + .await } /// The capacity of the ringbuffer. @@ -519,7 +502,7 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> { } pub fn set_waker(&mut self, waker: &Waker) { - STATE.ch_wakers[self.channel.index()].register(waker); + DmaCtrlImpl(self.channel.reborrow()).set_waker(waker); } fn clear_irqs(&mut self) { @@ -628,41 +611,20 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> { } pub fn clear(&mut self) { - self.ringbuf.clear(DmaCtrlImpl(self.channel.reborrow())); + self.ringbuf.clear(&mut DmaCtrlImpl(self.channel.reborrow())); } /// Write elements to the ring buffer /// Return a tuple of the length written and the length remaining in the buffer pub fn write(&mut self, buf: &[W]) -> Result<(usize, usize), OverrunError> { - self.ringbuf.write(DmaCtrlImpl(self.channel.reborrow()), buf) + self.ringbuf.write(&mut DmaCtrlImpl(self.channel.reborrow()), buf) } /// Write an exact number of elements to the ringbuffer. pub async fn write_exact(&mut self, buffer: &[W]) -> Result { - use core::future::poll_fn; - use core::sync::atomic::compiler_fence; - - let mut written_data = 0; - let buffer_len = buffer.len(); - - poll_fn(|cx| { - self.set_waker(cx.waker()); - - compiler_fence(Ordering::SeqCst); - - match self.write(&buffer[written_data..buffer_len]) { - Ok((len, remaining)) => { - written_data += len; - if written_data == buffer_len { - Poll::Ready(Ok(remaining)) - } else { - Poll::Pending - } - } - Err(e) => Poll::Ready(Err(e)), - } - }) - .await + self.ringbuf + .write_exact(&mut DmaCtrlImpl(self.channel.reborrow()), buffer) + .await } /// The capacity of the ringbuffer. @@ -671,7 +633,7 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> { } pub fn set_waker(&mut self, waker: &Waker) { - STATE.ch_wakers[self.channel.index()].register(waker); + DmaCtrlImpl(self.channel.reborrow()).set_waker(waker); } fn clear_irqs(&mut self) { diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 9cd7aa8d5..91d8b63b0 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -623,6 +623,10 @@ impl<'a, C: Channel> DmaCtrl for DmaCtrlImpl<'a, C> { fn reset_complete_count(&mut self) -> usize { STATE.complete_count[self.0.index()].swap(0, Ordering::AcqRel) } + + fn set_waker(&mut self, waker: &Waker) { + STATE.ch_wakers[self.0.index()].register(waker); + } } pub struct ReadableRingBuffer<'a, C: Channel, W: Word> { @@ -708,7 +712,7 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> { } pub fn clear(&mut self) { - self.ringbuf.clear(DmaCtrlImpl(self.channel.reborrow())); + self.ringbuf.clear(&mut DmaCtrlImpl(self.channel.reborrow())); } /// Read elements from the ring buffer @@ -717,7 +721,7 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> { /// The length remaining is the capacity, ring_buf.len(), less the elements remaining after the read /// OverrunError is returned if the portion to be read was overwritten by the DMA controller. pub fn read(&mut self, buf: &mut [W]) -> Result<(usize, usize), OverrunError> { - self.ringbuf.read(DmaCtrlImpl(self.channel.reborrow()), buf) + self.ringbuf.read(&mut DmaCtrlImpl(self.channel.reborrow()), buf) } /// Read an exact number of elements from the ringbuffer. @@ -732,30 +736,9 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> { /// - If M equals N/2 or N/2 divides evenly into M, this function will return every N/2 elements read on the DMA source. /// - Otherwise, this function may need up to N/2 extra elements to arrive before returning. pub async fn read_exact(&mut self, buffer: &mut [W]) -> Result { - use core::future::poll_fn; - use core::sync::atomic::compiler_fence; - - let mut read_data = 0; - let buffer_len = buffer.len(); - - poll_fn(|cx| { - self.set_waker(cx.waker()); - - compiler_fence(Ordering::SeqCst); - - match self.read(&mut buffer[read_data..buffer_len]) { - Ok((len, remaining)) => { - read_data += len; - if read_data == buffer_len { - Poll::Ready(Ok(remaining)) - } else { - Poll::Pending - } - } - Err(e) => Poll::Ready(Err(e)), - } - }) - .await + self.ringbuf + .read_exact(&mut DmaCtrlImpl(self.channel.reborrow()), buffer) + .await } // The capacity of the ringbuffer @@ -890,41 +873,20 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> { } pub fn clear(&mut self) { - self.ringbuf.clear(DmaCtrlImpl(self.channel.reborrow())); + self.ringbuf.clear(&mut DmaCtrlImpl(self.channel.reborrow())); } /// Write elements from the ring buffer /// Return a tuple of the length written and the length remaining in the buffer pub fn write(&mut self, buf: &[W]) -> Result<(usize, usize), OverrunError> { - self.ringbuf.write(DmaCtrlImpl(self.channel.reborrow()), buf) + self.ringbuf.write(&mut DmaCtrlImpl(self.channel.reborrow()), buf) } /// Write an exact number of elements to the ringbuffer. pub async fn write_exact(&mut self, buffer: &[W]) -> Result { - use core::future::poll_fn; - use core::sync::atomic::compiler_fence; - - let mut written_data = 0; - let buffer_len = buffer.len(); - - poll_fn(|cx| { - self.set_waker(cx.waker()); - - compiler_fence(Ordering::SeqCst); - - match self.write(&buffer[written_data..buffer_len]) { - Ok((len, remaining)) => { - written_data += len; - if written_data == buffer_len { - Poll::Ready(Ok(remaining)) - } else { - Poll::Pending - } - } - Err(e) => Poll::Ready(Err(e)), - } - }) - .await + self.ringbuf + .write_exact(&mut DmaCtrlImpl(self.channel.reborrow()), buffer) + .await } // The capacity of the ringbuffer diff --git a/embassy-stm32/src/dma/ringbuffer.rs b/embassy-stm32/src/dma/ringbuffer.rs index 945c7508c..5a97152e3 100644 --- a/embassy-stm32/src/dma/ringbuffer.rs +++ b/embassy-stm32/src/dma/ringbuffer.rs @@ -1,7 +1,9 @@ #![cfg_attr(gpdma, allow(unused))] +use core::future::poll_fn; use core::ops::Range; use core::sync::atomic::{compiler_fence, Ordering}; +use core::task::{Poll, Waker}; use super::word::Word; @@ -49,6 +51,9 @@ pub trait DmaCtrl { /// Reset the transfer completed counter to 0 and return the value just prior to the reset. fn reset_complete_count(&mut self) -> usize; + + /// Set the waker for a running poll_fn + fn set_waker(&mut self, waker: &Waker); } impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { @@ -57,7 +62,7 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { } /// Reset the ring buffer to its initial state - pub fn clear(&mut self, mut dma: impl DmaCtrl) { + pub fn clear(&mut self, dma: &mut impl DmaCtrl) { self.start = 0; dma.reset_complete_count(); } @@ -68,8 +73,43 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { } /// The current position of the ringbuffer - fn pos(&self, remaining_transfers: usize) -> usize { - self.cap() - remaining_transfers + fn pos(&self, dma: &mut impl DmaCtrl) -> usize { + self.cap() - dma.get_remaining_transfers() + } + + /// Read an exact number of elements from the ringbuffer. + /// + /// Returns the remaining number of elements available for immediate reading. + /// OverrunError is returned if the portion to be read was overwritten by the DMA controller. + /// + /// Async/Wake Behavior: + /// The underlying DMA peripheral only can wake us when its buffer pointer has reached the halfway point, + /// and when it wraps around. This means that when called with a buffer of length 'M', when this + /// ring buffer was created with a buffer of size 'N': + /// - If M equals N/2 or N/2 divides evenly into M, this function will return every N/2 elements read on the DMA source. + /// - Otherwise, this function may need up to N/2 extra elements to arrive before returning. + pub async fn read_exact(&mut self, dma: &mut impl DmaCtrl, buffer: &mut [W]) -> Result { + let mut read_data = 0; + let buffer_len = buffer.len(); + + poll_fn(|cx| { + dma.set_waker(cx.waker()); + + compiler_fence(Ordering::SeqCst); + + match self.read(dma, &mut buffer[read_data..buffer_len]) { + Ok((len, remaining)) => { + read_data += len; + if read_data == buffer_len { + Poll::Ready(Ok(remaining)) + } else { + Poll::Pending + } + } + Err(e) => Poll::Ready(Err(e)), + } + }) + .await } /// Read elements from the ring buffer @@ -77,7 +117,7 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { /// If not all of the elements were read, then there will be some elements in the buffer remaining /// The length remaining is the capacity, ring_buf.len(), less the elements remaining after the read /// OverrunError is returned if the portion to be read was overwritten by the DMA controller. - pub fn read(&mut self, mut dma: impl DmaCtrl, buf: &mut [W]) -> Result<(usize, usize), OverrunError> { + pub fn read(&mut self, dma: &mut impl DmaCtrl, buf: &mut [W]) -> Result<(usize, usize), OverrunError> { /* This algorithm is optimistic: we assume we haven't overrun more than a full buffer and then check after we've done our work to see we have. This is because on stm32, an interrupt is not guaranteed @@ -93,7 +133,7 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { rather than the data we actually copied because it costs nothing and confirms an error condition earlier. */ - let end = self.pos(dma.get_remaining_transfers()); + let end = self.pos(dma); if self.start == end && dma.get_complete_count() == 0 { // No elements are available in the buffer Ok((0, self.cap())) @@ -114,8 +154,7 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { then, get the current position of of the dma write and check if it's inside data we could have copied */ - let (pos, complete_count) = - critical_section::with(|_| (self.pos(dma.get_remaining_transfers()), dma.get_complete_count())); + let (pos, complete_count) = critical_section::with(|_| (self.pos(dma), dma.get_complete_count())); if (pos >= self.start && pos < end) || (complete_count > 0 && pos >= end) || complete_count > 1 { Err(OverrunError) } else { @@ -141,7 +180,7 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { then, get the current position of of the dma write and check if it's inside data we could have copied */ - let pos = self.pos(dma.get_remaining_transfers()); + let pos = self.pos(dma); if pos > self.start || pos < end || dma.get_complete_count() > 1 { Err(OverrunError) } else { @@ -169,7 +208,7 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { then, get the current position of of the dma write and check if it's inside data we could have copied */ - let pos = self.pos(dma.get_remaining_transfers()); + let pos = self.pos(dma); if pos > self.start || pos < end || dma.reset_complete_count() > 1 { Err(OverrunError) } else { @@ -209,7 +248,7 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { } /// Reset the ring buffer to its initial state - pub fn clear(&mut self, mut dma: impl DmaCtrl) { + pub fn clear(&mut self, dma: &mut impl DmaCtrl) { self.end = 0; dma.reset_complete_count(); } @@ -220,14 +259,39 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { } /// The current position of the ringbuffer - fn pos(&self, remaining_transfers: usize) -> usize { - self.cap() - remaining_transfers + fn pos(&self, dma: &mut impl DmaCtrl) -> usize { + self.cap() - dma.get_remaining_transfers() + } + + /// Write an exact number of elements to the ringbuffer. + pub async fn write_exact(&mut self, dma: &mut impl DmaCtrl, buffer: &[W]) -> Result { + let mut written_data = 0; + let buffer_len = buffer.len(); + + poll_fn(|cx| { + dma.set_waker(cx.waker()); + + compiler_fence(Ordering::SeqCst); + + match self.write(dma, &buffer[written_data..buffer_len]) { + Ok((len, remaining)) => { + written_data += len; + if written_data == buffer_len { + Poll::Ready(Ok(remaining)) + } else { + Poll::Pending + } + } + Err(e) => Poll::Ready(Err(e)), + } + }) + .await } /// Write elements from the ring buffer /// Return a tuple of the length written and the capacity remaining to be written in the buffer - pub fn write(&mut self, mut dma: impl DmaCtrl, buf: &[W]) -> Result<(usize, usize), OverrunError> { - let start = self.pos(dma.get_remaining_transfers()); + pub fn write(&mut self, dma: &mut impl DmaCtrl, buf: &[W]) -> Result<(usize, usize), OverrunError> { + let start = self.pos(dma); if start > self.end { // The occupied portion in the ring buffer DOES wrap let len = self.copy_from(buf, self.end..start); @@ -235,8 +299,7 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { compiler_fence(Ordering::SeqCst); // Confirm that the DMA is not inside data we could have written - let (pos, complete_count) = - critical_section::with(|_| (self.pos(dma.get_remaining_transfers()), dma.get_complete_count())); + let (pos, complete_count) = critical_section::with(|_| (self.pos(dma), dma.get_complete_count())); if (pos >= self.end && pos < start) || (complete_count > 0 && pos >= start) || complete_count > 1 { Err(OverrunError) } else { @@ -256,7 +319,7 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { compiler_fence(Ordering::SeqCst); // Confirm that the DMA is not inside data we could have written - let pos = self.pos(dma.get_remaining_transfers()); + let pos = self.pos(dma); if pos > self.end || pos < start || dma.get_complete_count() > 1 { Err(OverrunError) } else { @@ -274,7 +337,7 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { compiler_fence(Ordering::SeqCst); // Confirm that the DMA is not inside data we could have written - let pos = self.pos(dma.get_remaining_transfers()); + let pos = self.pos(dma); if pos > self.end || pos < start || dma.reset_complete_count() > 1 { Err(OverrunError) } else { @@ -323,7 +386,7 @@ mod tests { requests: cell::RefCell>, } - impl DmaCtrl for &mut TestCircularTransfer { + impl DmaCtrl for TestCircularTransfer { fn get_remaining_transfers(&self) -> usize { match self.requests.borrow_mut().pop().unwrap() { TestCircularTransferRequest::PositionRequest(pos) => { @@ -350,6 +413,8 @@ mod tests { _ => unreachable!(), } } + + fn set_waker(&mut self, waker: &Waker) {} } impl TestCircularTransfer { From e80db420610cc1bf2619bb64688f87712c1eee1c Mon Sep 17 00:00:00 2001 From: xoviat Date: Fri, 4 Aug 2023 17:15:56 -0500 Subject: [PATCH 02/77] stm32/dma: minor cleanup, optmization --- embassy-stm32/src/dma/bdma.rs | 4 ++-- embassy-stm32/src/dma/dma.rs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index e79a159ef..20ff29bef 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -497,7 +497,7 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> { } /// The capacity of the ringbuffer. - pub fn cap(&self) -> usize { + pub const fn cap(&self) -> usize { self.ringbuf.cap() } @@ -628,7 +628,7 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> { } /// The capacity of the ringbuffer. - pub fn cap(&self) -> usize { + pub const fn cap(&self) -> usize { self.ringbuf.cap() } diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 91d8b63b0..5033ae477 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -742,12 +742,12 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> { } // The capacity of the ringbuffer - pub fn cap(&self) -> usize { + pub const fn cap(&self) -> usize { self.ringbuf.cap() } pub fn set_waker(&mut self, waker: &Waker) { - STATE.ch_wakers[self.channel.index()].register(waker); + DmaCtrlImpl(self.channel.reborrow()).set_waker(waker); } fn clear_irqs(&mut self) { @@ -890,12 +890,12 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> { } // The capacity of the ringbuffer - pub fn cap(&self) -> usize { + pub const fn cap(&self) -> usize { self.ringbuf.cap() } pub fn set_waker(&mut self, waker: &Waker) { - STATE.ch_wakers[self.channel.index()].register(waker); + DmaCtrlImpl(self.channel.reborrow()).set_waker(waker); } fn clear_irqs(&mut self) { From 66faba2df76bf76bcfcca30d159c5a39b64819e8 Mon Sep 17 00:00:00 2001 From: loris <76793979+lorislibralato@users.noreply.github.com> Date: Sat, 5 Aug 2023 21:04:51 +0200 Subject: [PATCH 03/77] add wake_task_no_pend for expired timer enqueue inside run_queue --- embassy-executor/src/raw/mod.rs | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index f3760f589..25c2ab0da 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -409,7 +409,7 @@ impl SyncExecutor { #[allow(clippy::never_loop)] loop { #[cfg(feature = "integrated-timers")] - self.timer_queue.dequeue_expired(Instant::now(), |task| wake_task(task)); + self.timer_queue.dequeue_expired(Instant::now(), wake_task_no_pend); self.run_queue.dequeue_all(|p| { let task = p.header(); @@ -573,6 +573,31 @@ pub fn wake_task(task: TaskRef) { } } +/// Wake a task by `TaskRef` without calling pend. +/// +/// You can obtain a `TaskRef` from a `Waker` using [`task_from_waker`]. +pub fn wake_task_no_pend(task: TaskRef) { + let header = task.header(); + + let res = header.state.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |state| { + // If already scheduled, or if not started, + if (state & STATE_RUN_QUEUED != 0) || (state & STATE_SPAWNED == 0) { + None + } else { + // Mark it as scheduled + Some(state | STATE_RUN_QUEUED) + } + }); + + if res.is_ok() { + // We have just marked the task as scheduled, so enqueue it. + unsafe { + let executor = header.executor.get().unwrap_unchecked(); + executor.run_queue.enqueue(task); + } + } +} + #[cfg(feature = "integrated-timers")] struct TimerQueue; From e31545860e2c44e67f45dd5132e85f18b6fab5d7 Mon Sep 17 00:00:00 2001 From: Don Reilly Date: Tue, 8 Aug 2023 16:46:30 -0500 Subject: [PATCH 04/77] don't generate adc peripheral for f3 series --- embassy-stm32/build.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 30e25aefd..743631b88 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -310,7 +310,10 @@ fn main() { for p in METADATA.peripherals { // generating RccPeripheral impl for H7 ADC3 would result in bad frequency - if !singletons.contains(&p.name.to_string()) || (p.name == "ADC3" && METADATA.line.starts_with("STM32H7")) { + if !singletons.contains(&p.name.to_string()) + || (p.name == "ADC3" && METADATA.line.starts_with("STM32H7")) + || (p.name.starts_with("ADC") && p.registers.as_ref().map_or(false, |r| r.version == "f3")) + { continue; } From 8a9622ec2ecb9960f17ea0bd5b662783ba522cbd Mon Sep 17 00:00:00 2001 From: Don Reilly Date: Thu, 10 Aug 2023 08:03:35 -0500 Subject: [PATCH 05/77] update metapac tag --- embassy-stm32/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 0d713f60d..8f765e9d3 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -57,7 +57,7 @@ sdio-host = "0.5.0" embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true } critical-section = "1.1" atomic-polyfill = "1.0.1" -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-82a24863823a3daf0ca664c7fdf008379d0a0d42" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-7eddb78e705905af4c1dd2359900db3e78a3c500" } vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -76,7 +76,7 @@ critical-section = { version = "1.1", features = ["std"] } [build-dependencies] proc-macro2 = "1.0.36" quote = "1.0.15" -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-82a24863823a3daf0ca664c7fdf008379d0a0d42", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-7eddb78e705905af4c1dd2359900db3e78a3c500", default-features = false, features = ["metadata"]} [features] default = ["rt"] From bc156afbb2950ae073d0f271f26517cf16b35263 Mon Sep 17 00:00:00 2001 From: Lucas Granberg Date: Thu, 10 Aug 2023 16:16:45 +0300 Subject: [PATCH 06/77] fix rng ced toggling sequence on reset. --- embassy-stm32/src/rng.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/rng.rs b/embassy-stm32/src/rng.rs index 2a4978ec5..30816e436 100644 --- a/embassy-stm32/src/rng.rs +++ b/embassy-stm32/src/rng.rs @@ -73,15 +73,20 @@ impl<'d, T: Instance> Rng<'d, T> { #[cfg(not(rng_v1))] pub fn reset(&mut self) { T::regs().cr().write(|reg| { - reg.set_rngen(false); reg.set_condrst(true); + reg.set_nistc(pac::rng::vals::Nistc::CUSTOM); // set RNG config "A" according to reference manual // this has to be written within the same write access as setting the CONDRST bit - reg.set_nistc(pac::rng::vals::Nistc::DEFAULT); reg.set_rng_config1(pac::rng::vals::RngConfig1::CONFIGA); + reg.set_clkdiv(pac::rng::vals::Clkdiv::NODIV); reg.set_rng_config2(pac::rng::vals::RngConfig2::CONFIGA_B); reg.set_rng_config3(pac::rng::vals::RngConfig3::CONFIGA); - reg.set_clkdiv(pac::rng::vals::Clkdiv::NODIV); + reg.set_ced(true); + reg.set_ie(false); + reg.set_rngen(true); + }); + T::regs().cr().write(|reg| { + reg.set_ced(false); }); // wait for CONDRST to be set while !T::regs().cr().read().condrst() {} From c3128846923718d94921617776008c1eadcd4fec Mon Sep 17 00:00:00 2001 From: Don Reilly Date: Thu, 10 Aug 2023 08:21:03 -0500 Subject: [PATCH 07/77] added exclusion for adc v4 as well --- .vscode/settings.json | 1 + embassy-stm32/build.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/.vscode/settings.json b/.vscode/settings.json index 29e8812e3..139b432f4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -41,4 +41,5 @@ // "examples/stm32wl/Cargo.toml", // "examples/wasm/Cargo.toml", ], + "rust-analyzer.showUnlinkedFileNotification": false, } \ No newline at end of file diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 743631b88..8a731620f 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -313,6 +313,7 @@ fn main() { if !singletons.contains(&p.name.to_string()) || (p.name == "ADC3" && METADATA.line.starts_with("STM32H7")) || (p.name.starts_with("ADC") && p.registers.as_ref().map_or(false, |r| r.version == "f3")) + || (p.name.starts_with("ADC") && p.registers.as_ref().map_or(false, |r| r.version == "v4")) { continue; } From 7d8e3951ba3ff3a89df1d21210c4a76686d4f142 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Thu, 10 Aug 2023 22:18:26 +0200 Subject: [PATCH 08/77] fix: ensure spi irq is disabled when dropped --- embassy-nrf/src/spim.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index 4673a0175..d131a43dd 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs @@ -378,6 +378,9 @@ impl<'d, T: Instance> Drop for Spim<'d, T> { gpio::deconfigure_pin(r.psel.miso.read().bits()); gpio::deconfigure_pin(r.psel.mosi.read().bits()); + // Disable all events interrupts + T::Interrupt::disable(); + trace!("spim drop: done"); } } From 858d520882df986758eac491fbde916071ebaceb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Thu, 10 Aug 2023 23:14:52 +0200 Subject: [PATCH 09/77] Executor: Add changelog --- embassy-executor/CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/embassy-executor/CHANGELOG.md b/embassy-executor/CHANGELOG.md index 4fd3dccf7..5aecbec16 100644 --- a/embassy-executor/CHANGELOG.md +++ b/embassy-executor/CHANGELOG.md @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +- Avoid calling `pend()` when waking expired timers +- Properly reset finished task state with `integrated-timers` enabled +- Introduce `InterruptExecutor::spawner()` +- Fix incorrect critical section in Xtensa executor + ## 0.2.0 - 2023-04-27 - Replace unnecessary atomics in runqueue From baef856206dd2cc6f0326914b6db68ed1ada1a7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Thu, 10 Aug 2023 23:15:29 +0200 Subject: [PATCH 10/77] Bump version --- embassy-executor/CHANGELOG.md | 2 +- embassy-executor/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-executor/CHANGELOG.md b/embassy-executor/CHANGELOG.md index 5aecbec16..86a477589 100644 --- a/embassy-executor/CHANGELOG.md +++ b/embassy-executor/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased +## 0.2.1 - 2023-08-10 - Avoid calling `pend()` when waking expired timers - Properly reset finished task state with `integrated-timers` enabled diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 590718e3e..ce5e2741f 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embassy-executor" -version = "0.2.0" +version = "0.2.1" edition = "2021" license = "MIT OR Apache-2.0" description = "async/await executor designed for embedded usage" From b658f10db9a963d85b8465759692b0aa7973a1d1 Mon Sep 17 00:00:00 2001 From: Ruben De Smet Date: Wed, 9 Aug 2023 11:50:26 +0200 Subject: [PATCH 11/77] Expose poll_ready_to_{send,receive} in Sender/Receiver --- embassy-sync/src/channel.rs | 39 +++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/embassy-sync/src/channel.rs b/embassy-sync/src/channel.rs index d6f36f53d..3896f70ac 100644 --- a/embassy-sync/src/channel.rs +++ b/embassy-sync/src/channel.rs @@ -65,6 +65,13 @@ where pub fn try_send(&self, message: T) -> Result<(), TrySendError> { self.channel.try_send(message) } + + /// Allows a poll_fn to poll until the channel is ready to send + /// + /// See [`Channel::poll_ready_to_send()`] + pub fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> bool { + self.channel.poll_ready_to_send(cx) + } } /// Send-only access to a [`Channel`] without knowing channel size. @@ -106,6 +113,13 @@ impl<'ch, T> DynamicSender<'ch, T> { pub fn try_send(&self, message: T) -> Result<(), TrySendError> { self.channel.try_send_with_context(message, None) } + + /// Allows a poll_fn to poll until the channel is ready to send + /// + /// See [`Channel::poll_ready_to_send()`] + pub fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> bool { + self.channel.poll_ready_to_send(cx) + } } /// Receive-only access to a [`Channel`]. @@ -144,6 +158,13 @@ where pub fn try_recv(&self) -> Result { self.channel.try_recv() } + + /// Allows a poll_fn to poll until the channel is ready to receive + /// + /// See [`Channel::poll_ready_to_receive()`] + pub fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> bool { + self.channel.poll_ready_to_receive(cx) + } } /// Receive-only access to a [`Channel`] without knowing channel size. @@ -173,6 +194,13 @@ impl<'ch, T> DynamicReceiver<'ch, T> { pub fn try_recv(&self) -> Result { self.channel.try_recv_with_context(None) } + + /// Allows a poll_fn to poll until the channel is ready to receive + /// + /// See [`Channel::poll_ready_to_receive()`] + pub fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> bool { + self.channel.poll_ready_to_receive(cx) + } } impl<'ch, M, T, const N: usize> From> for DynamicReceiver<'ch, T> @@ -286,6 +314,9 @@ trait DynamicChannel { fn try_send_with_context(&self, message: T, cx: Option<&mut Context<'_>>) -> Result<(), TrySendError>; fn try_recv_with_context(&self, cx: Option<&mut Context<'_>>) -> Result; + + fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> bool; + fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> bool; } /// Error returned by [`try_recv`](Channel::try_recv). @@ -492,6 +523,14 @@ where fn try_recv_with_context(&self, cx: Option<&mut Context<'_>>) -> Result { Channel::try_recv_with_context(self, cx) } + + fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> bool { + Channel::poll_ready_to_send(self, cx) + } + + fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> bool { + Channel::poll_ready_to_receive(self, cx) + } } #[cfg(test)] From f9d251cd5cd9c84718cd66e7697a8502c4d61a0a Mon Sep 17 00:00:00 2001 From: Ruben De Smet Date: Fri, 11 Aug 2023 11:15:17 +0200 Subject: [PATCH 12/77] Channel poll methods return Poll instead of bool --- embassy-stm32-wpan/src/mac/driver.rs | 4 +++- embassy-stm32/src/can/bxcan.rs | 9 +------ embassy-sync/src/channel.rs | 36 +++++++++++++++++----------- 3 files changed, 26 insertions(+), 23 deletions(-) diff --git a/embassy-stm32-wpan/src/mac/driver.rs b/embassy-stm32-wpan/src/mac/driver.rs index f8e3a2b08..a58ee231b 100644 --- a/embassy-stm32-wpan/src/mac/driver.rs +++ b/embassy-stm32-wpan/src/mac/driver.rs @@ -28,7 +28,9 @@ impl<'d> embassy_net_driver::Driver for Driver<'d> { type TxToken<'a> = TxToken<'d> where Self: 'a; fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { - if self.runner.rx_channel.poll_ready_to_receive(cx) && self.runner.tx_buf_channel.poll_ready_to_receive(cx) { + if self.runner.rx_channel.poll_ready_to_receive(cx).is_ready() + && self.runner.tx_buf_channel.poll_ready_to_receive(cx).is_ready() + { Some(( RxToken { rx: &self.runner.rx_channel, diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index fb223e4a9..e439207ef 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -506,14 +506,7 @@ impl<'c, 'd, T: Instance> CanRx<'c, 'd, T> { /// Waits while receive queue is empty. pub async fn wait_not_empty(&mut self) { - poll_fn(|cx| { - if T::state().rx_queue.poll_ready_to_receive(cx) { - Poll::Ready(()) - } else { - Poll::Pending - } - }) - .await + poll_fn(|cx| T::state().rx_queue.poll_ready_to_receive(cx)).await } fn curr_error(&self) -> Option { diff --git a/embassy-sync/src/channel.rs b/embassy-sync/src/channel.rs index 3896f70ac..e7224856c 100644 --- a/embassy-sync/src/channel.rs +++ b/embassy-sync/src/channel.rs @@ -69,7 +69,7 @@ where /// Allows a poll_fn to poll until the channel is ready to send /// /// See [`Channel::poll_ready_to_send()`] - pub fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> bool { + pub fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> Poll<()> { self.channel.poll_ready_to_send(cx) } } @@ -117,7 +117,7 @@ impl<'ch, T> DynamicSender<'ch, T> { /// Allows a poll_fn to poll until the channel is ready to send /// /// See [`Channel::poll_ready_to_send()`] - pub fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> bool { + pub fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> Poll<()> { self.channel.poll_ready_to_send(cx) } } @@ -162,7 +162,7 @@ where /// Allows a poll_fn to poll until the channel is ready to receive /// /// See [`Channel::poll_ready_to_receive()`] - pub fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> bool { + pub fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()> { self.channel.poll_ready_to_receive(cx) } } @@ -198,7 +198,7 @@ impl<'ch, T> DynamicReceiver<'ch, T> { /// Allows a poll_fn to poll until the channel is ready to receive /// /// See [`Channel::poll_ready_to_receive()`] - pub fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> bool { + pub fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()> { self.channel.poll_ready_to_receive(cx) } } @@ -315,8 +315,8 @@ trait DynamicChannel { fn try_recv_with_context(&self, cx: Option<&mut Context<'_>>) -> Result; - fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> bool; - fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> bool; + fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> Poll<()>; + fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()>; } /// Error returned by [`try_recv`](Channel::try_recv). @@ -370,10 +370,14 @@ impl ChannelState { } } - fn poll_ready_to_receive(&mut self, cx: &mut Context<'_>) -> bool { + fn poll_ready_to_receive(&mut self, cx: &mut Context<'_>) -> Poll<()> { self.receiver_waker.register(cx.waker()); - !self.queue.is_empty() + if !self.queue.is_empty() { + Poll::Ready(()) + } else { + Poll::Pending + } } fn try_send(&mut self, message: T) -> Result<(), TrySendError> { @@ -395,10 +399,14 @@ impl ChannelState { } } - fn poll_ready_to_send(&mut self, cx: &mut Context<'_>) -> bool { + fn poll_ready_to_send(&mut self, cx: &mut Context<'_>) -> Poll<()> { self.senders_waker.register(cx.waker()); - !self.queue.is_full() + if !self.queue.is_full() { + Poll::Ready(()) + } else { + Poll::Pending + } } } @@ -449,12 +457,12 @@ where } /// Allows a poll_fn to poll until the channel is ready to receive - pub fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> bool { + pub fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()> { self.lock(|c| c.poll_ready_to_receive(cx)) } /// Allows a poll_fn to poll until the channel is ready to send - pub fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> bool { + pub fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> Poll<()> { self.lock(|c| c.poll_ready_to_send(cx)) } @@ -524,11 +532,11 @@ where Channel::try_recv_with_context(self, cx) } - fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> bool { + fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> Poll<()> { Channel::poll_ready_to_send(self, cx) } - fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> bool { + fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()> { Channel::poll_ready_to_receive(self, cx) } } From b1ec460b9af131ef80fcafd79a7f63aa326aaf94 Mon Sep 17 00:00:00 2001 From: Ruben De Smet Date: Fri, 11 Aug 2023 11:30:29 +0200 Subject: [PATCH 13/77] Implement Channel::poll_receive(..) -> Poll --- embassy-stm32-wpan/src/mac/driver.rs | 2 +- embassy-sync/src/channel.rs | 43 +++++++++++++++++++++++++--- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/embassy-stm32-wpan/src/mac/driver.rs b/embassy-stm32-wpan/src/mac/driver.rs index a58ee231b..93898d888 100644 --- a/embassy-stm32-wpan/src/mac/driver.rs +++ b/embassy-stm32-wpan/src/mac/driver.rs @@ -46,7 +46,7 @@ impl<'d> embassy_net_driver::Driver for Driver<'d> { } fn transmit(&mut self, cx: &mut Context) -> Option> { - if self.runner.tx_buf_channel.poll_ready_to_receive(cx) { + if self.runner.tx_buf_channel.poll_ready_to_receive(cx).is_ready() { Some(TxToken { tx: &self.runner.tx_channel, tx_buf: &self.runner.tx_buf_channel, diff --git a/embassy-sync/src/channel.rs b/embassy-sync/src/channel.rs index e7224856c..dc727fb10 100644 --- a/embassy-sync/src/channel.rs +++ b/embassy-sync/src/channel.rs @@ -165,6 +165,13 @@ where pub fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()> { self.channel.poll_ready_to_receive(cx) } + + /// Poll the channel for the next item + /// + /// See [`Channel::poll_receive()`] + pub fn poll_receive(&self, cx: &mut Context<'_>) -> Poll { + self.channel.poll_receive(cx) + } } /// Receive-only access to a [`Channel`] without knowing channel size. @@ -201,6 +208,13 @@ impl<'ch, T> DynamicReceiver<'ch, T> { pub fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()> { self.channel.poll_ready_to_receive(cx) } + + /// Poll the channel for the next item + /// + /// See [`Channel::poll_receive()`] + pub fn poll_receive(&self, cx: &mut Context<'_>) -> Poll { + self.channel.poll_receive(cx) + } } impl<'ch, M, T, const N: usize> From> for DynamicReceiver<'ch, T> @@ -228,10 +242,7 @@ where type Output = T; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - match self.channel.try_recv_with_context(Some(cx)) { - Ok(v) => Poll::Ready(v), - Err(TryRecvError::Empty) => Poll::Pending, - } + self.channel.poll_receive(cx) } } @@ -317,6 +328,8 @@ trait DynamicChannel { fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> Poll<()>; fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()>; + + fn poll_receive(&self, cx: &mut Context<'_>) -> Poll; } /// Error returned by [`try_recv`](Channel::try_recv). @@ -370,6 +383,19 @@ impl ChannelState { } } + fn poll_receive(&mut self, cx: &mut Context<'_>) -> Poll { + if self.queue.is_full() { + self.senders_waker.wake(); + } + + if let Some(message) = self.queue.pop_front() { + Poll::Ready(message) + } else { + self.receiver_waker.register(cx.waker()); + Poll::Pending + } + } + fn poll_ready_to_receive(&mut self, cx: &mut Context<'_>) -> Poll<()> { self.receiver_waker.register(cx.waker()); @@ -452,6 +478,11 @@ where self.lock(|c| c.try_recv_with_context(cx)) } + /// Poll the channel for the next message + pub fn poll_receive(&self, cx: &mut Context<'_>) -> Poll { + self.lock(|c| c.poll_receive(cx)) + } + fn try_send_with_context(&self, m: T, cx: Option<&mut Context<'_>>) -> Result<(), TrySendError> { self.lock(|c| c.try_send_with_context(m, cx)) } @@ -539,6 +570,10 @@ where fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()> { Channel::poll_ready_to_receive(self, cx) } + + fn poll_receive(&self, cx: &mut Context<'_>) -> Poll { + Channel::poll_receive(self, cx) + } } #[cfg(test)] From 55ff397c0cde8a04c41cfc228645c3fd33383cd1 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Fri, 11 Aug 2023 19:47:24 +0200 Subject: [PATCH 14/77] boot: release flash after prepare and refactor api This refactoring of the chip specific bootloader creates the internal boot instance and aligned buffer in the prepare stage, so that they are automatically dropped after. This unlocks a use case where peripherals owning the flash need to be Drop'ed before load() happens. --- embassy-boot/nrf/src/lib.rs | 35 +++++++--------------- embassy-boot/rp/src/lib.rs | 33 +++++++------------- embassy-boot/stm32/src/lib.rs | 33 +++++++------------- examples/boot/bootloader/nrf/src/main.rs | 4 +-- examples/boot/bootloader/rp/src/main.rs | 4 +-- examples/boot/bootloader/stm32/src/main.rs | 4 +-- 6 files changed, 34 insertions(+), 79 deletions(-) diff --git a/embassy-boot/nrf/src/lib.rs b/embassy-boot/nrf/src/lib.rs index df94819fc..b9d86eb17 100644 --- a/embassy-boot/nrf/src/lib.rs +++ b/embassy-boot/nrf/src/lib.rs @@ -14,28 +14,17 @@ use embassy_nrf::wdt; use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; /// A bootloader for nRF devices. -pub struct BootLoader { - boot: embassy_boot::BootLoader, - aligned_buf: AlignedBuffer, -} +pub struct BootLoader; -impl - BootLoader -{ - /// Create a new bootloader instance using the supplied partitions for active, dfu and state. - pub fn new(config: BootLoaderConfig) -> Self { - Self { - boot: embassy_boot::BootLoader::new(config), - aligned_buf: AlignedBuffer([0; BUFFER_SIZE]), - } - } - - /// Inspect the bootloader state and perform actions required before booting, such as swapping - /// firmware. - pub fn prepare(&mut self) { - self.boot - .prepare_boot(&mut self.aligned_buf.0) - .expect("Boot prepare error"); +impl BootLoader { + /// Inspect the bootloader state and perform actions required before booting, such as swapping firmware. + pub fn prepare( + config: BootLoaderConfig, + ) -> Self { + let mut aligned_buf = AlignedBuffer([0; BUFFER_SIZE]); + let mut boot = embassy_boot::BootLoader::new(config); + boot.prepare_boot(&mut aligned_buf.0).expect("Boot prepare error"); + Self } /// Boots the application without softdevice mechanisms. @@ -45,8 +34,6 @@ impl /// This modifies the stack pointer and reset vector and will run code placed in the active partition. #[cfg(not(feature = "softdevice"))] pub unsafe fn load(self, start: u32) -> ! { - core::mem::drop(self.boot); - let mut p = cortex_m::Peripherals::steal(); p.SCB.invalidate_icache(); p.SCB.vtor.write(start); @@ -59,7 +46,7 @@ impl /// /// This modifies the stack pointer and reset vector and will run code placed in the active partition. #[cfg(feature = "softdevice")] - pub unsafe fn load(&mut self, _app: u32) -> ! { + pub unsafe fn load(self, _app: u32) -> ! { use nrf_softdevice_mbr as mbr; const NRF_SUCCESS: u32 = 0; diff --git a/embassy-boot/rp/src/lib.rs b/embassy-boot/rp/src/lib.rs index f5aefa416..96bcf3bf7 100644 --- a/embassy-boot/rp/src/lib.rs +++ b/embassy-boot/rp/src/lib.rs @@ -15,28 +15,17 @@ use embassy_time::Duration; use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; /// A bootloader for RP2040 devices. -pub struct BootLoader { - boot: embassy_boot::BootLoader, - aligned_buf: AlignedBuffer, -} +pub struct BootLoader; -impl - BootLoader -{ - /// Create a new bootloader instance using the supplied partitions for active, dfu and state. - pub fn new(config: BootLoaderConfig) -> Self { - Self { - boot: embassy_boot::BootLoader::new(config), - aligned_buf: AlignedBuffer([0; BUFFER_SIZE]), - } - } - - /// Inspect the bootloader state and perform actions required before booting, such as swapping - /// firmware. - pub fn prepare(&mut self) { - self.boot - .prepare_boot(self.aligned_buf.as_mut()) - .expect("Boot prepare error"); +impl BootLoader { + /// Inspect the bootloader state and perform actions required before booting, such as swapping firmware + pub fn prepare( + config: BootLoaderConfig, + ) -> Self { + let mut aligned_buf = AlignedBuffer([0; BUFFER_SIZE]); + let mut boot = embassy_boot::BootLoader::new(config); + boot.prepare_boot(aligned_buf.as_mut()).expect("Boot prepare error"); + Self } /// Boots the application. @@ -45,8 +34,6 @@ impl /// /// This modifies the stack pointer and reset vector and will run code placed in the active partition. pub unsafe fn load(self, start: u32) -> ! { - core::mem::drop(self.boot); - trace!("Loading app at 0x{:x}", start); #[allow(unused_mut)] let mut p = cortex_m::Peripherals::steal(); diff --git a/embassy-boot/stm32/src/lib.rs b/embassy-boot/stm32/src/lib.rs index 25f029423..c6350c495 100644 --- a/embassy-boot/stm32/src/lib.rs +++ b/embassy-boot/stm32/src/lib.rs @@ -11,28 +11,17 @@ pub use embassy_boot::{FirmwareState, FirmwareUpdater}; use embedded_storage::nor_flash::NorFlash; /// A bootloader for STM32 devices. -pub struct BootLoader { - boot: embassy_boot::BootLoader, - aligned_buf: AlignedBuffer, -} +pub struct BootLoader; -impl - BootLoader -{ - /// Create a new bootloader instance using the supplied partitions for active, dfu and state. - pub fn new(config: BootLoaderConfig) -> Self { - Self { - boot: embassy_boot::BootLoader::new(config), - aligned_buf: AlignedBuffer([0; BUFFER_SIZE]), - } - } - - /// Inspect the bootloader state and perform actions required before booting, such as swapping - /// firmware. - pub fn prepare(&mut self) { - self.boot - .prepare_boot(self.aligned_buf.as_mut()) - .expect("Boot prepare error"); +impl BootLoader { + /// Inspect the bootloader state and perform actions required before booting, such as swapping firmware + pub fn prepare( + config: BootLoaderConfig, + ) -> Self { + let mut aligned_buf = AlignedBuffer([0; BUFFER_SIZE]); + let mut boot = embassy_boot::BootLoader::new(config); + boot.prepare_boot(aligned_buf.as_mut()).expect("Boot prepare error"); + Self } /// Boots the application. @@ -41,8 +30,6 @@ impl /// /// This modifies the stack pointer and reset vector and will run code placed in the active partition. pub unsafe fn load(self, start: u32) -> ! { - core::mem::drop(self.boot); - trace!("Loading app at 0x{:x}", start); #[allow(unused_mut)] let mut p = cortex_m::Peripherals::steal(); diff --git a/examples/boot/bootloader/nrf/src/main.rs b/examples/boot/bootloader/nrf/src/main.rs index 72c95c02a..74e2e293f 100644 --- a/examples/boot/bootloader/nrf/src/main.rs +++ b/examples/boot/bootloader/nrf/src/main.rs @@ -33,9 +33,7 @@ fn main() -> ! { let config = BootLoaderConfig::from_linkerfile_blocking(&flash); let active_offset = config.active.offset(); - let mut bl: BootLoader<_, _, _> = BootLoader::new(config); - - bl.prepare(); + let bl: BootLoader = BootLoader::prepare(config); unsafe { bl.load(active_offset) } } diff --git a/examples/boot/bootloader/rp/src/main.rs b/examples/boot/bootloader/rp/src/main.rs index 6a81db804..c0e75d1ea 100644 --- a/examples/boot/bootloader/rp/src/main.rs +++ b/examples/boot/bootloader/rp/src/main.rs @@ -29,9 +29,7 @@ fn main() -> ! { let config = BootLoaderConfig::from_linkerfile_blocking(&flash); let active_offset = config.active.offset(); - let mut bl: BootLoader<_, _, _> = BootLoader::new(config); - - bl.prepare(); + let bl: BootLoader = BootLoader::prepare(config); unsafe { bl.load(embassy_rp::flash::FLASH_BASE as u32 + active_offset) } } diff --git a/examples/boot/bootloader/stm32/src/main.rs b/examples/boot/bootloader/stm32/src/main.rs index 262eed200..5fd9ea588 100644 --- a/examples/boot/bootloader/stm32/src/main.rs +++ b/examples/boot/bootloader/stm32/src/main.rs @@ -27,9 +27,7 @@ fn main() -> ! { let config = BootLoaderConfig::from_linkerfile_blocking(&flash); let active_offset = config.active.offset(); - let mut bl: BootLoader<_, _, _, 2048> = BootLoader::new(config); - - bl.prepare(); + let bl = BootLoader::prepare::<_, _, _, 2048>(config); unsafe { bl.load(BANK1_REGION.base + active_offset) } } From 675b7fb6056d8c3dfaca759b7cd373e2f4a0e111 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sat, 12 Aug 2023 16:00:18 +0200 Subject: [PATCH 15/77] POC: allow custom executors --- embassy-executor/Cargo.toml | 7 +- embassy-executor/src/arch/cortex_m.rs | 252 +++++++------------------- embassy-executor/src/arch/riscv32.rs | 100 ++++------ embassy-executor/src/arch/std.rs | 76 +++----- embassy-executor/src/arch/wasm.rs | 37 ++-- embassy-executor/src/arch/xtensa.rs | 117 +++++------- embassy-executor/src/interrupt.rs | 127 +++++++++++++ embassy-executor/src/lib.rs | 5 + embassy-executor/src/raw/mod.rs | 35 +++- embassy-executor/src/thread.rs | 80 ++++++++ 10 files changed, 448 insertions(+), 388 deletions(-) create mode 100644 embassy-executor/src/interrupt.rs create mode 100644 embassy-executor/src/thread.rs diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index ce5e2741f..182dd6937 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -31,11 +31,11 @@ features = ["nightly", "defmt", "pender-callback", "arch-cortex-m", "executor-th # Architecture _arch = [] # some arch was picked -arch-std = ["_arch", "critical-section/std"] +arch-std = ["_arch", "critical-section/std", "thread-context"] arch-cortex-m = ["_arch", "dep:cortex-m"] arch-xtensa = ["_arch"] arch-riscv32 = ["_arch"] -arch-wasm = ["_arch", "dep:wasm-bindgen", "dep:js-sys"] +arch-wasm = ["_arch", "dep:wasm-bindgen", "dep:js-sys", "thread-context"] # Enable creating a `Pender` from an arbitrary function pointer callback. pender-callback = [] @@ -45,6 +45,9 @@ executor-thread = [] # Enable the interrupt-mode executor (available in Cortex-M only) executor-interrupt = [] +# Pass a context to the thread-mode executor. +thread-context = [] + # Enable nightly-only features nightly = [] diff --git a/embassy-executor/src/arch/cortex_m.rs b/embassy-executor/src/arch/cortex_m.rs index 94c8134d6..ca1675c03 100644 --- a/embassy-executor/src/arch/cortex_m.rs +++ b/embassy-executor/src/arch/cortex_m.rs @@ -1,224 +1,98 @@ #[cfg(feature = "executor-thread")] pub use thread::*; + #[cfg(feature = "executor-thread")] mod thread { - use core::arch::asm; - use core::marker::PhantomData; #[cfg(feature = "nightly")] pub use embassy_macros::main_cortex_m as main; - use crate::raw::{Pender, PenderInner}; - use crate::{raw, Spawner}; + use crate::raw::OpaqueThreadContext; + use crate::thread::ThreadContext; - #[derive(Copy, Clone)] - pub(crate) struct ThreadPender; + #[export_name = "__thread_mode_pender"] + fn __thread_mode_pender(_core_id: OpaqueThreadContext) { + unsafe { core::arch::asm!("sev") } + } - impl ThreadPender { - pub(crate) fn pend(self) { - unsafe { core::arch::asm!("sev") } + /// TODO + // Name pending + #[derive(Default)] // Default enables Executor::new + pub struct CortexMThreadContext { + _not_send: core::marker::PhantomData<*mut ()>, + } + + impl ThreadContext for CortexMThreadContext { + #[cfg(feature = "thread-context")] + fn context(&self) -> OpaqueThreadContext { + // Enabling thread-context is not incorrect, just wasteful. + OpaqueThreadContext(0) + } + + #[cfg(not(feature = "thread-context"))] + fn context(&self) -> OpaqueThreadContext { + OpaqueThreadContext(()) + } + + fn wait(&mut self) { + unsafe { core::arch::asm!("wfe") } } } - /// Thread mode executor, using WFE/SEV. - /// - /// This is the simplest and most common kind of executor. It runs on - /// thread mode (at the lowest priority level), and uses the `WFE` ARM instruction - /// to sleep when it has no more work to do. When a task is woken, a `SEV` instruction - /// is executed, to make the `WFE` exit from sleep and poll the task. - /// - /// This executor allows for ultra low power consumption for chips where `WFE` - /// triggers low-power sleep without extra steps. If your chip requires extra steps, - /// you may use [`raw::Executor`] directly to program custom behavior. - pub struct Executor { - inner: raw::Executor, - not_send: PhantomData<*mut ()>, - } - - impl Executor { - /// Create a new Executor. - pub fn new() -> Self { - Self { - inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))), - not_send: PhantomData, - } - } - - /// Run the executor. - /// - /// The `init` closure is called with a [`Spawner`] that spawns tasks on - /// this executor. Use it to spawn the initial task(s). After `init` returns, - /// the executor starts running the tasks. - /// - /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), - /// for example by passing it as an argument to the initial tasks. - /// - /// This function requires `&'static mut self`. This means you have to store the - /// Executor instance in a place where it'll live forever and grants you mutable - /// access. There's a few ways to do this: - /// - /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) - /// - a `static mut` (unsafe) - /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) - /// - /// This function never returns. - pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { - init(self.inner.spawner()); - - loop { - unsafe { - self.inner.poll(); - asm!("wfe"); - }; - } - } - } + /// TODO + // Type alias for backwards compatibility + pub type Executor = crate::thread::ThreadModeExecutor; } +// None of this has to be public, I guess? #[cfg(feature = "executor-interrupt")] pub use interrupt::*; #[cfg(feature = "executor-interrupt")] mod interrupt { - use core::cell::UnsafeCell; - use core::mem::MaybeUninit; - - use atomic_polyfill::{AtomicBool, Ordering}; use cortex_m::interrupt::InterruptNumber; use cortex_m::peripheral::NVIC; - use crate::raw::{self, Pender, PenderInner}; + use crate::interrupt::InterruptContext; + use crate::raw::OpaqueInterruptContext; #[derive(Clone, Copy)] - pub(crate) struct InterruptPender(u16); + struct CortexMInterruptContext(u16); - impl InterruptPender { - pub(crate) fn pend(self) { - // STIR is faster, but is only available in v7 and higher. - #[cfg(not(armv6m))] - { - let mut nvic: cortex_m::peripheral::NVIC = unsafe { core::mem::transmute(()) }; - nvic.request(self); - } - - #[cfg(armv6m)] - cortex_m::peripheral::NVIC::pend(self); - } - } - - unsafe impl cortex_m::interrupt::InterruptNumber for InterruptPender { + unsafe impl cortex_m::interrupt::InterruptNumber for CortexMInterruptContext { fn number(self) -> u16 { self.0 } } - /// Interrupt mode executor. - /// - /// This executor runs tasks in interrupt mode. The interrupt handler is set up - /// to poll tasks, and when a task is woken the interrupt is pended from software. - /// - /// This allows running async tasks at a priority higher than thread mode. One - /// use case is to leave thread mode free for non-async tasks. Another use case is - /// to run multiple executors: one in thread mode for low priority tasks and another in - /// interrupt mode for higher priority tasks. Higher priority tasks will preempt lower - /// priority ones. - /// - /// It is even possible to run multiple interrupt mode executors at different priorities, - /// by assigning different priorities to the interrupts. For an example on how to do this, - /// See the 'multiprio' example for 'embassy-nrf'. - /// - /// To use it, you have to pick an interrupt that won't be used by the hardware. - /// Some chips reserve some interrupts for this purpose, sometimes named "software interrupts" (SWI). - /// If this is not the case, you may use an interrupt from any unused peripheral. - /// - /// It is somewhat more complex to use, it's recommended to use the thread-mode - /// [`Executor`] instead, if it works for your use case. - pub struct InterruptExecutor { - started: AtomicBool, - executor: UnsafeCell>, - } - - unsafe impl Send for InterruptExecutor {} - unsafe impl Sync for InterruptExecutor {} - - impl InterruptExecutor { - /// Create a new, not started `InterruptExecutor`. - #[inline] - pub const fn new() -> Self { - Self { - started: AtomicBool::new(false), - executor: UnsafeCell::new(MaybeUninit::uninit()), - } + impl InterruptContext for T + where + T: InterruptNumber, + { + fn context(&self) -> OpaqueInterruptContext { + OpaqueInterruptContext(self.number() as usize) } - /// Executor interrupt callback. - /// - /// # Safety - /// - /// You MUST call this from the interrupt handler, and from nowhere else. - pub unsafe fn on_interrupt(&'static self) { - let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; - executor.poll(); - } - - /// Start the executor. - /// - /// This initializes the executor, enables the interrupt, and returns. - /// The executor keeps running in the background through the interrupt. - /// - /// This returns a [`SendSpawner`] you can use to spawn tasks on it. A [`SendSpawner`] - /// is returned instead of a [`Spawner`](embassy_executor::Spawner) because the executor effectively runs in a - /// different "thread" (the interrupt), so spawning tasks on it is effectively - /// sending them. - /// - /// To obtain a [`Spawner`](embassy_executor::Spawner) for this executor, use [`Spawner::for_current_executor()`](embassy_executor::Spawner::for_current_executor()) from - /// a task running in it. - /// - /// # Interrupt requirements - /// - /// You must write the interrupt handler yourself, and make it call [`on_interrupt()`](Self::on_interrupt). - /// - /// This method already enables (unmasks) the interrupt, you must NOT do it yourself. - /// - /// You must set the interrupt priority before calling this method. You MUST NOT - /// do it after. - /// - pub fn start(&'static self, irq: impl InterruptNumber) -> crate::SendSpawner { - if self - .started - .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) - .is_err() - { - panic!("InterruptExecutor::start() called multiple times on the same executor."); - } - - unsafe { - (&mut *self.executor.get()) - .as_mut_ptr() - .write(raw::Executor::new(Pender(PenderInner::Interrupt(InterruptPender( - irq.number(), - ))))) - } - - let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; - - unsafe { NVIC::unmask(irq) } - - executor.spawner().make_send() - } - - /// Get a SendSpawner for this executor - /// - /// This returns a [`SendSpawner`] you can use to spawn tasks on this - /// executor. - /// - /// This MUST only be called on an executor that has already been spawned. - /// The function will panic otherwise. - pub fn spawner(&'static self) -> crate::SendSpawner { - if !self.started.load(Ordering::Acquire) { - panic!("InterruptExecutor::spawner() called on uninitialized executor."); - } - let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; - executor.spawner().make_send() + fn enable(&self) { + unsafe { NVIC::unmask(*self) } } } + + #[export_name = "__interrupt_mode_pender"] + fn __interrupt_mode_pender(interrupt: OpaqueInterruptContext) { + let interrupt = CortexMInterruptContext(unsafe { core::mem::transmute::<_, usize>(interrupt) as u16 }); + + // STIR is faster, but is only available in v7 and higher. + #[cfg(not(armv6m))] + { + let mut nvic: NVIC = unsafe { core::mem::transmute(()) }; + nvic.request(interrupt); + } + + #[cfg(armv6m)] + NVIC::pend(interrupt); + } + + /// TODO + // Type alias for backwards compatibility + pub type InterruptExecutor = crate::interrupt::InterruptModeExecutor; } diff --git a/embassy-executor/src/arch/riscv32.rs b/embassy-executor/src/arch/riscv32.rs index ff7ec1575..5f766442d 100644 --- a/embassy-executor/src/arch/riscv32.rs +++ b/embassy-executor/src/arch/riscv32.rs @@ -1,6 +1,9 @@ #[cfg(feature = "executor-interrupt")] compile_error!("`executor-interrupt` is not supported with `arch-riscv32`."); +#[cfg(feature = "thread-context")] +compile_error!("`thread-context` is not supported with `arch-riscv32`."); + #[cfg(feature = "executor-thread")] pub use thread::*; #[cfg(feature = "executor-thread")] @@ -11,77 +14,50 @@ mod thread { #[cfg(feature = "nightly")] pub use embassy_macros::main_riscv as main; - use crate::raw::{Pender, PenderInner}; - use crate::{raw, Spawner}; - - #[derive(Copy, Clone)] - pub(crate) struct ThreadPender; - - impl ThreadPender { - #[allow(unused)] - pub(crate) fn pend(self) { - SIGNAL_WORK_THREAD_MODE.store(true, core::sync::atomic::Ordering::SeqCst); - } - } + use crate::raw::OpaqueThreadContext; + use crate::thread::ThreadContext; /// global atomic used to keep track of whether there is work to do since sev() is not available on RISCV static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); - /// RISCV32 Executor - pub struct Executor { - inner: raw::Executor, - not_send: PhantomData<*mut ()>, + #[export_name = "__thread_mode_pender"] + fn __thread_mode_pender(_core_id: OpaqueThreadContext) { + SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst); } - impl Executor { - /// Create a new Executor. - pub fn new() -> Self { - Self { - inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))), - not_send: PhantomData, - } + /// TODO + // Name pending + #[derive(Default)] // Default enables Executor::new + pub struct RiscVThreadContext { + _not_send: PhantomData<*mut ()>, + } + + impl ThreadContext for RiscVThreadContext { + fn context(&self) -> OpaqueThreadContext { + OpaqueThreadContext(()) } - /// Run the executor. - /// - /// The `init` closure is called with a [`Spawner`] that spawns tasks on - /// this executor. Use it to spawn the initial task(s). After `init` returns, - /// the executor starts running the tasks. - /// - /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), - /// for example by passing it as an argument to the initial tasks. - /// - /// This function requires `&'static mut self`. This means you have to store the - /// Executor instance in a place where it'll live forever and grants you mutable - /// access. There's a few ways to do this: - /// - /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) - /// - a `static mut` (unsafe) - /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) - /// - /// This function never returns. - pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { - init(self.inner.spawner()); - - loop { - unsafe { - self.inner.poll(); - // we do not care about race conditions between the load and store operations, interrupts - //will only set this value to true. - critical_section::with(|_| { - // if there is work to do, loop back to polling - // TODO can we relax this? - if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { - SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); - } - // if not, wait for interrupt - else { - core::arch::asm!("wfi"); - } - }); - // if an interrupt occurred while waiting, it will be serviced here + fn wait(&mut self) { + // We do not care about race conditions between the load and store operations, + // interrupts will only set this value to true. + critical_section::with(|_| { + // if there is work to do, loop back to polling + // TODO can we relax this? + if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { + SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); } - } + // if not, wait for interrupt + else { + unsafe { + core::arch::asm!("wfi"); + } + } + }); + // if an interrupt occurred while waiting, it will be serviced here } } + + /// TODO + // Type alias for backwards compatibility + pub type Executor = crate::thread::ThreadModeExecutor; } diff --git a/embassy-executor/src/arch/std.rs b/embassy-executor/src/arch/std.rs index 4e4a178f0..28e25fbd0 100644 --- a/embassy-executor/src/arch/std.rs +++ b/embassy-executor/src/arch/std.rs @@ -1,6 +1,9 @@ #[cfg(feature = "executor-interrupt")] compile_error!("`executor-interrupt` is not supported with `arch-std`."); +#[cfg(not(feature = "thread-context"))] +compile_error!("`arch-std` requires `thread-context`."); + #[cfg(feature = "executor-thread")] pub use thread::*; #[cfg(feature = "executor-thread")] @@ -11,63 +14,40 @@ mod thread { #[cfg(feature = "nightly")] pub use embassy_macros::main_std as main; - use crate::raw::{Pender, PenderInner}; - use crate::{raw, Spawner}; + use crate::raw::OpaqueThreadContext; + use crate::thread::ThreadContext; - #[derive(Copy, Clone)] - pub(crate) struct ThreadPender(&'static Signaler); - - impl ThreadPender { - #[allow(unused)] - pub(crate) fn pend(self) { - self.0.signal() - } - } - - /// Single-threaded std-based executor. - pub struct Executor { - inner: raw::Executor, - not_send: PhantomData<*mut ()>, + /// TODO + // Name pending + pub struct StdThreadCtx { + _not_send: PhantomData<*mut ()>, signaler: &'static Signaler, } - impl Executor { - /// Create a new Executor. - pub fn new() -> Self { + impl Default for StdThreadCtx { + fn default() -> Self { let signaler = &*Box::leak(Box::new(Signaler::new())); Self { - inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender(signaler)))), - not_send: PhantomData, + _not_send: PhantomData, signaler, } } + } - /// Run the executor. - /// - /// The `init` closure is called with a [`Spawner`] that spawns tasks on - /// this executor. Use it to spawn the initial task(s). After `init` returns, - /// the executor starts running the tasks. - /// - /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), - /// for example by passing it as an argument to the initial tasks. - /// - /// This function requires `&'static mut self`. This means you have to store the - /// Executor instance in a place where it'll live forever and grants you mutable - /// access. There's a few ways to do this: - /// - /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) - /// - a `static mut` (unsafe) - /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) - /// - /// This function never returns. - pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { - init(self.inner.spawner()); - - loop { - unsafe { self.inner.poll() }; - self.signaler.wait() - } + impl ThreadContext for StdThreadCtx { + fn context(&self) -> OpaqueThreadContext { + OpaqueThreadContext(self.signaler as *const _ as usize) } + + fn wait(&mut self) { + self.signaler.wait() + } + } + + #[export_name = "__thread_mode_pender"] + fn __thread_mode_pender(core_id: OpaqueThreadContext) { + let signaler: &'static Signaler = unsafe { std::mem::transmute(core_id) }; + signaler.signal() } struct Signaler { @@ -97,4 +77,8 @@ mod thread { self.condvar.notify_one(); } } + + /// TODO + // Type alias for backwards compatibility + pub type Executor = crate::thread::ThreadModeExecutor; } diff --git a/embassy-executor/src/arch/wasm.rs b/embassy-executor/src/arch/wasm.rs index 08ab16b99..4f5ce9c90 100644 --- a/embassy-executor/src/arch/wasm.rs +++ b/embassy-executor/src/arch/wasm.rs @@ -1,6 +1,9 @@ #[cfg(feature = "executor-interrupt")] compile_error!("`executor-interrupt` is not supported with `arch-wasm`."); +#[cfg(not(feature = "thread-context"))] +compile_error!("`arch-wasm` requires `thread-context`."); + #[cfg(feature = "executor-thread")] pub use thread::*; #[cfg(feature = "executor-thread")] @@ -14,14 +17,13 @@ mod thread { use wasm_bindgen::prelude::*; use crate::raw::util::UninitCell; - use crate::raw::{Pender, PenderInner}; + use crate::raw::{OpaqueThreadContext, Pender, PenderInner}; use crate::{raw, Spawner}; - /// WASM executor, wasm_bindgen to schedule tasks on the JS event loop. - pub struct Executor { - inner: raw::Executor, - ctx: &'static WasmContext, - not_send: PhantomData<*mut ()>, + #[export_name = "__thread_mode_pender"] + fn __thread_mode_pender(context: OpaqueThreadContext) { + let signaler: &'static WasmContext = unsafe { std::mem::transmute(context) }; + let _ = signaler.promise.then(unsafe { signaler.closure.as_mut() }); } pub(crate) struct WasmContext { @@ -29,16 +31,6 @@ mod thread { closure: UninitCell>, } - #[derive(Copy, Clone)] - pub(crate) struct ThreadPender(&'static WasmContext); - - impl ThreadPender { - #[allow(unused)] - pub(crate) fn pend(self) { - let _ = self.0.promise.then(unsafe { self.0.closure.as_mut() }); - } - } - impl WasmContext { pub fn new() -> Self { Self { @@ -48,14 +40,23 @@ mod thread { } } + /// WASM executor, wasm_bindgen to schedule tasks on the JS event loop. + pub struct Executor { + inner: raw::Executor, + ctx: &'static WasmContext, + not_send: PhantomData<*mut ()>, + } + impl Executor { /// Create a new Executor. pub fn new() -> Self { let ctx = &*Box::leak(Box::new(WasmContext::new())); Self { - inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender(ctx)))), - not_send: PhantomData, + inner: raw::Executor::new(Pender(PenderInner::Thread(OpaqueThreadContext( + ctx as *const _ as usize, + )))), ctx, + not_send: PhantomData, } } diff --git a/embassy-executor/src/arch/xtensa.rs b/embassy-executor/src/arch/xtensa.rs index 017b2c52b..8e1b917de 100644 --- a/embassy-executor/src/arch/xtensa.rs +++ b/embassy-executor/src/arch/xtensa.rs @@ -1,6 +1,12 @@ #[cfg(feature = "executor-interrupt")] compile_error!("`executor-interrupt` is not supported with `arch-xtensa`."); +#[cfg(feature = "thread-context")] +compile_error!( + "`thread-context` is not supported with `arch-xtensa`.\ + Use a multicore-safe executor from esp-hal instead." // obviously, this is too specific to ESP32 +); + #[cfg(feature = "executor-thread")] pub use thread::*; #[cfg(feature = "executor-thread")] @@ -8,86 +14,63 @@ mod thread { use core::marker::PhantomData; use core::sync::atomic::{AtomicBool, Ordering}; - use crate::raw::{Pender, PenderInner}; - use crate::{raw, Spawner}; + use crate::raw::OpaqueThreadContext; + use crate::thread::ThreadContext; - #[derive(Copy, Clone)] - pub(crate) struct ThreadPender; - - impl ThreadPender { - #[allow(unused)] - pub(crate) fn pend(self) { - SIGNAL_WORK_THREAD_MODE.store(true, core::sync::atomic::Ordering::SeqCst); - } - } - - /// global atomic used to keep track of whether there is work to do since sev() is not available on Xtensa + /// global atomic used to keep track of whether there is work to do since sev() is not available on RISCV static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); - /// Xtensa Executor - pub struct Executor { - inner: raw::Executor, - not_send: PhantomData<*mut ()>, + #[export_name = "__thread_mode_pender"] + fn __thread_mode_pender(_core_id: OpaqueThreadContext) { + SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst); } - impl Executor { - /// Create a new Executor. - pub fn new() -> Self { - Self { - inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))), - not_send: PhantomData, - } + /// TODO + // Name pending + pub struct XtensaThreadContext { + _not_send: PhantomData<*mut ()>, + } + + impl Default for XtensaThreadContext { + fn default() -> Self { + Self { _not_send: PhantomData } + } + } + + impl ThreadContext for XtensaThreadContext { + fn context(&self) -> OpaqueThreadContext { + OpaqueThreadContext(()) } - /// Run the executor. - /// - /// The `init` closure is called with a [`Spawner`] that spawns tasks on - /// this executor. Use it to spawn the initial task(s). After `init` returns, - /// the executor starts running the tasks. - /// - /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), - /// for example by passing it as an argument to the initial tasks. - /// - /// This function requires `&'static mut self`. This means you have to store the - /// Executor instance in a place where it'll live forever and grants you mutable - /// access. There's a few ways to do this: - /// - /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) - /// - a `static mut` (unsafe) - /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) - /// - /// This function never returns. - pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { - init(self.inner.spawner()); + fn wait(&mut self) { + unsafe { + // Manual critical section implementation that only masks interrupts handlers. + // We must not acquire the cross-core on dual-core systems because that would + // prevent the other core from doing useful work while this core is sleeping. + let token: critical_section::RawRestoreState; + core::arch::asm!("rsil {0}, 5", out(reg) token); - loop { - unsafe { - self.inner.poll(); + // we do not care about race conditions between the load and store operations, interrupts + // will only set this value to true. + // if there is work to do, loop back to polling + if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { + SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); - // Manual critical section implementation that only masks interrupts handlers. - // We must not acquire the cross-core on dual-core systems because that would - // prevent the other core from doing useful work while this core is sleeping. - let token: critical_section::RawRestoreState; - core::arch::asm!("rsil {0}, 5", out(reg) token); - - // we do not care about race conditions between the load and store operations, interrupts - // will only set this value to true. - // if there is work to do, loop back to polling - if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { - SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); - - core::arch::asm!( + core::arch::asm!( "wsr.ps {0}", "rsync", in(reg) token) - } else { - // waiti sets the PS.INTLEVEL when slipping into sleep - // because critical sections in Xtensa are implemented via increasing - // PS.INTLEVEL the critical section ends here - // take care not add code after `waiti` if it needs to be inside the CS - core::arch::asm!("waiti 0"); // critical section ends here - } + } else { + // waiti sets the PS.INTLEVEL when slipping into sleep + // because critical sections in Xtensa are implemented via increasing + // PS.INTLEVEL the critical section ends here + // take care not add code after `waiti` if it needs to be inside the CS + core::arch::asm!("waiti 0"); // critical section ends here } } } } + + /// TODO + // Type alias for backwards compatibility + pub type Executor = crate::thread::ThreadModeExecutor; } diff --git a/embassy-executor/src/interrupt.rs b/embassy-executor/src/interrupt.rs new file mode 100644 index 000000000..f8b0809da --- /dev/null +++ b/embassy-executor/src/interrupt.rs @@ -0,0 +1,127 @@ +//! Interrupt-mode executor. + +use core::cell::UnsafeCell; +use core::mem::MaybeUninit; + +use atomic_polyfill::{AtomicBool, Ordering}; + +use crate::raw::{self, OpaqueInterruptContext, Pender, PenderInner}; + +/// An interrupt source that can be used to drive an [`InterruptExecutor`]. +// Name pending +pub trait InterruptContext { + /// Creates an opaque identifier for this interrupt. + fn context(&self) -> OpaqueInterruptContext; + + /// Sets up the interrupt request. + fn enable(&self); +} + +/// Interrupt mode executor. +/// +/// This executor runs tasks in interrupt mode. The interrupt handler is set up +/// to poll tasks, and when a task is woken the interrupt is pended from software. +/// +/// This allows running async tasks at a priority higher than thread mode. One +/// use case is to leave thread mode free for non-async tasks. Another use case is +/// to run multiple executors: one in thread mode for low priority tasks and another in +/// interrupt mode for higher priority tasks. Higher priority tasks will preempt lower +/// priority ones. +/// +/// It is even possible to run multiple interrupt mode executors at different priorities, +/// by assigning different priorities to the interrupts. For an example on how to do this, +/// See the 'multiprio' example for 'embassy-nrf'. +/// +/// To use it, you have to pick an interrupt that won't be used by the hardware. +/// Some chips reserve some interrupts for this purpose, sometimes named "software interrupts" (SWI). +/// If this is not the case, you may use an interrupt from any unused peripheral. +/// +/// It is somewhat more complex to use, it's recommended to use the thread-mode +/// [`Executor`] instead, if it works for your use case. +pub struct InterruptModeExecutor { + started: AtomicBool, + executor: UnsafeCell>, +} + +unsafe impl Send for InterruptModeExecutor {} +unsafe impl Sync for InterruptModeExecutor {} + +impl InterruptModeExecutor { + /// Create a new, not started `InterruptExecutor`. + #[inline] + pub const fn new() -> Self { + Self { + started: AtomicBool::new(false), + executor: UnsafeCell::new(MaybeUninit::uninit()), + } + } + + /// Executor interrupt callback. + /// + /// # Safety + /// + /// You MUST call this from the interrupt handler, and from nowhere else. + pub unsafe fn on_interrupt(&'static self) { + let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; + executor.poll(); + } + + /// Start the executor. + /// + /// This initializes the executor, enables the interrupt, and returns. + /// The executor keeps running in the background through the interrupt. + /// + /// This returns a [`SendSpawner`] you can use to spawn tasks on it. A [`SendSpawner`] + /// is returned instead of a [`Spawner`](embassy_executor::Spawner) because the executor effectively runs in a + /// different "thread" (the interrupt), so spawning tasks on it is effectively + /// sending them. + /// + /// To obtain a [`Spawner`](embassy_executor::Spawner) for this executor, use [`Spawner::for_current_executor()`](embassy_executor::Spawner::for_current_executor()) from + /// a task running in it. + /// + /// # Interrupt requirements + /// + /// You must write the interrupt handler yourself, and make it call [`on_interrupt()`](Self::on_interrupt). + /// + /// This method already enables (unmasks) the interrupt, you must NOT do it yourself. + /// + /// You must set the interrupt priority before calling this method. You MUST NOT + /// do it after. + /// + pub fn start(&'static self, irq: impl InterruptContext) -> crate::SendSpawner { + if self + .started + .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) + .is_err() + { + panic!("InterruptExecutor::start() called multiple times on the same executor."); + } + + unsafe { + (&mut *self.executor.get()) + .as_mut_ptr() + .write(raw::Executor::new(Pender(PenderInner::Interrupt(irq.context())))) + } + + let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; + + irq.enable(); + + executor.spawner().make_send() + } + + /// Get a SendSpawner for this executor + /// + /// This returns a [`SendSpawner`] you can use to spawn tasks on this + /// executor. + /// + /// This MUST only be called on an executor that has already been spawned. + /// The function will panic otherwise. + pub fn spawner(&'static self) -> crate::SendSpawner { + if !self.started.load(Ordering::Acquire) { + panic!("InterruptExecutor::spawner() called on uninitialized executor."); + } + let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; + executor.spawner().make_send() + } +} diff --git a/embassy-executor/src/lib.rs b/embassy-executor/src/lib.rs index 3ce687eb6..ca67c9484 100644 --- a/embassy-executor/src/lib.rs +++ b/embassy-executor/src/lib.rs @@ -37,6 +37,11 @@ pub use arch::*; pub mod raw; +#[cfg(feature = "executor-interrupt")] +pub mod interrupt; +#[cfg(feature = "executor-thread")] +pub mod thread; + mod spawner; pub use spawner::*; diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 25c2ab0da..b4d70b1e9 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -291,12 +291,29 @@ impl TaskPool { } } +/// Context given to the thread-mode executor's pender. +#[cfg(all(feature = "executor-thread", not(feature = "thread-context")))] +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct OpaqueThreadContext(pub(crate) ()); + +/// Context given to the thread-mode executor's pender. +#[cfg(all(feature = "executor-thread", feature = "thread-context"))] +#[repr(transparent)] +#[derive(Clone, Copy)] +pub struct OpaqueThreadContext(pub(crate) usize); + +/// Context given to the interrupt-mode executor's pender. +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct OpaqueInterruptContext(pub(crate) usize); + #[derive(Clone, Copy)] pub(crate) enum PenderInner { #[cfg(feature = "executor-thread")] - Thread(crate::arch::ThreadPender), + Thread(OpaqueThreadContext), #[cfg(feature = "executor-interrupt")] - Interrupt(crate::arch::InterruptPender), + Interrupt(OpaqueInterruptContext), #[cfg(feature = "pender-callback")] Callback { func: fn(*mut ()), context: *mut () }, } @@ -333,9 +350,19 @@ impl Pender { pub(crate) fn pend(&self) { match self.0 { #[cfg(feature = "executor-thread")] - PenderInner::Thread(x) => x.pend(), + PenderInner::Thread(core_id) => { + extern "Rust" { + fn __thread_mode_pender(core_id: OpaqueThreadContext); + } + unsafe { __thread_mode_pender(core_id) }; + } #[cfg(feature = "executor-interrupt")] - PenderInner::Interrupt(x) => x.pend(), + PenderInner::Interrupt(interrupt) => { + extern "Rust" { + fn __interrupt_mode_pender(interrupt: OpaqueInterruptContext); + } + unsafe { __interrupt_mode_pender(interrupt) }; + } #[cfg(feature = "pender-callback")] PenderInner::Callback { func, context } => func(context), } diff --git a/embassy-executor/src/thread.rs b/embassy-executor/src/thread.rs new file mode 100644 index 000000000..9bbe29500 --- /dev/null +++ b/embassy-executor/src/thread.rs @@ -0,0 +1,80 @@ +//! Thread-mode executor. + +use core::marker::PhantomData; + +use crate::raw::{OpaqueThreadContext, Pender, PenderInner}; +use crate::{raw, Spawner}; + +/// TODO +// Name pending +pub trait ThreadContext: Sized { + /// TODO + fn context(&self) -> OpaqueThreadContext; + + /// TODO + fn wait(&mut self); +} + +/// Thread mode executor, using WFE/SEV. +/// +/// This is the simplest and most common kind of executor. It runs on +/// thread mode (at the lowest priority level), and uses the `WFE` ARM instruction +/// to sleep when it has no more work to do. When a task is woken, a `SEV` instruction +/// is executed, to make the `WFE` exit from sleep and poll the task. +/// +/// This executor allows for ultra low power consumption for chips where `WFE` +/// triggers low-power sleep without extra steps. If your chip requires extra steps, +/// you may use [`raw::Executor`] directly to program custom behavior. +pub struct ThreadModeExecutor { + inner: raw::Executor, + context: C, + not_send: PhantomData<*mut ()>, +} + +impl ThreadModeExecutor { + /// Create a new Executor. + pub fn new() -> Self + where + C: Default, + { + Self::with_context(C::default()) + } + + /// Create a new Executor. + pub fn with_context(context: C) -> Self { + Self { + inner: raw::Executor::new(Pender(PenderInner::Thread(context.context()))), + context, + not_send: PhantomData, + } + } + + /// Run the executor. + /// + /// The `init` closure is called with a [`Spawner`] that spawns tasks on + /// this executor. Use it to spawn the initial task(s). After `init` returns, + /// the executor starts running the tasks. + /// + /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), + /// for example by passing it as an argument to the initial tasks. + /// + /// This function requires `&'static mut self`. This means you have to store the + /// Executor instance in a place where it'll live forever and grants you mutable + /// access. There's a few ways to do this: + /// + /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) + /// - a `static mut` (unsafe) + /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) + /// + /// This function never returns. + pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { + init(self.inner.spawner()); + + loop { + unsafe { + self.inner.poll(); + self.context.wait(); + }; + } + } +} From fbf50cdae899dc1cd2f232b880e096d0fc51f49c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sat, 12 Aug 2023 22:05:19 +0200 Subject: [PATCH 16/77] Remove Pender wrapper --- embassy-executor/src/arch/wasm.rs | 6 ++-- embassy-executor/src/interrupt.rs | 4 +-- embassy-executor/src/raw/mod.rs | 46 +++++++++++++++---------------- embassy-executor/src/thread.rs | 4 +-- 4 files changed, 29 insertions(+), 31 deletions(-) diff --git a/embassy-executor/src/arch/wasm.rs b/embassy-executor/src/arch/wasm.rs index 4f5ce9c90..e244c0b3f 100644 --- a/embassy-executor/src/arch/wasm.rs +++ b/embassy-executor/src/arch/wasm.rs @@ -17,7 +17,7 @@ mod thread { use wasm_bindgen::prelude::*; use crate::raw::util::UninitCell; - use crate::raw::{OpaqueThreadContext, Pender, PenderInner}; + use crate::raw::{OpaqueThreadContext, Pender}; use crate::{raw, Spawner}; #[export_name = "__thread_mode_pender"] @@ -52,9 +52,7 @@ mod thread { pub fn new() -> Self { let ctx = &*Box::leak(Box::new(WasmContext::new())); Self { - inner: raw::Executor::new(Pender(PenderInner::Thread(OpaqueThreadContext( - ctx as *const _ as usize, - )))), + inner: raw::Executor::new(Pender::Thread(OpaqueThreadContext(ctx as *const _ as usize))), ctx, not_send: PhantomData, } diff --git a/embassy-executor/src/interrupt.rs b/embassy-executor/src/interrupt.rs index f8b0809da..c1084ea7b 100644 --- a/embassy-executor/src/interrupt.rs +++ b/embassy-executor/src/interrupt.rs @@ -5,7 +5,7 @@ use core::mem::MaybeUninit; use atomic_polyfill::{AtomicBool, Ordering}; -use crate::raw::{self, OpaqueInterruptContext, Pender, PenderInner}; +use crate::raw::{self, OpaqueInterruptContext, Pender}; /// An interrupt source that can be used to drive an [`InterruptExecutor`]. // Name pending @@ -100,7 +100,7 @@ impl InterruptModeExecutor { unsafe { (&mut *self.executor.get()) .as_mut_ptr() - .write(raw::Executor::new(Pender(PenderInner::Interrupt(irq.context())))) + .write(raw::Executor::new(Pender::Interrupt(irq.context()))) } let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index b4d70b1e9..7fd29db40 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -308,19 +308,6 @@ pub struct OpaqueThreadContext(pub(crate) usize); #[repr(transparent)] pub struct OpaqueInterruptContext(pub(crate) usize); -#[derive(Clone, Copy)] -pub(crate) enum PenderInner { - #[cfg(feature = "executor-thread")] - Thread(OpaqueThreadContext), - #[cfg(feature = "executor-interrupt")] - Interrupt(OpaqueInterruptContext), - #[cfg(feature = "pender-callback")] - Callback { func: fn(*mut ()), context: *mut () }, -} - -unsafe impl Send for PenderInner {} -unsafe impl Sync for PenderInner {} - /// Platform/architecture-specific action executed when an executor has pending work. /// /// When a task within an executor is woken, the `Pender` is called. This does a @@ -328,7 +315,23 @@ unsafe impl Sync for PenderInner {} /// When this happens, you must arrange for [`Executor::poll`] to be called. /// /// You can think of it as a waker, but for the whole executor. -pub struct Pender(pub(crate) PenderInner); +#[derive(Clone, Copy)] +pub enum Pender { + /// Pender for a thread-mode executor. + #[cfg(feature = "executor-thread")] + Thread(OpaqueThreadContext), + + /// Pender for an interrupt-mode executor. + #[cfg(feature = "executor-interrupt")] + Interrupt(OpaqueInterruptContext), + + /// Arbitrary, dynamically dispatched pender. + #[cfg(feature = "pender-callback")] + Callback { func: fn(*mut ()), context: *mut () }, +} + +unsafe impl Send for Pender {} +unsafe impl Sync for Pender {} impl Pender { /// Create a `Pender` that will call an arbitrary function pointer. @@ -339,32 +342,29 @@ impl Pender { /// - `context`: Opaque context pointer, that will be passed to the function pointer. #[cfg(feature = "pender-callback")] pub fn new_from_callback(func: fn(*mut ()), context: *mut ()) -> Self { - Self(PenderInner::Callback { - func, - context: context.into(), - }) + Self::Callback { func, context } } } impl Pender { - pub(crate) fn pend(&self) { - match self.0 { + pub(crate) fn pend(self) { + match self { #[cfg(feature = "executor-thread")] - PenderInner::Thread(core_id) => { + Pender::Thread(core_id) => { extern "Rust" { fn __thread_mode_pender(core_id: OpaqueThreadContext); } unsafe { __thread_mode_pender(core_id) }; } #[cfg(feature = "executor-interrupt")] - PenderInner::Interrupt(interrupt) => { + Pender::Interrupt(interrupt) => { extern "Rust" { fn __interrupt_mode_pender(interrupt: OpaqueInterruptContext); } unsafe { __interrupt_mode_pender(interrupt) }; } #[cfg(feature = "pender-callback")] - PenderInner::Callback { func, context } => func(context), + Pender::Callback { func, context } => func(context), } } } diff --git a/embassy-executor/src/thread.rs b/embassy-executor/src/thread.rs index 9bbe29500..2d2c6daa5 100644 --- a/embassy-executor/src/thread.rs +++ b/embassy-executor/src/thread.rs @@ -2,7 +2,7 @@ use core::marker::PhantomData; -use crate::raw::{OpaqueThreadContext, Pender, PenderInner}; +use crate::raw::{OpaqueThreadContext, Pender}; use crate::{raw, Spawner}; /// TODO @@ -43,7 +43,7 @@ impl ThreadModeExecutor { /// Create a new Executor. pub fn with_context(context: C) -> Self { Self { - inner: raw::Executor::new(Pender(PenderInner::Thread(context.context()))), + inner: raw::Executor::new(Pender::Thread(context.context())), context, not_send: PhantomData, } From bce250bbdc18f025547f59c30a7bec24826b3aea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sat, 12 Aug 2023 22:08:46 +0200 Subject: [PATCH 17/77] Remove unnecessary !Send markers --- embassy-executor/src/arch/cortex_m.rs | 4 +--- embassy-executor/src/arch/riscv32.rs | 5 +---- embassy-executor/src/arch/std.rs | 6 +----- embassy-executor/src/arch/xtensa.rs | 11 ++--------- 4 files changed, 5 insertions(+), 21 deletions(-) diff --git a/embassy-executor/src/arch/cortex_m.rs b/embassy-executor/src/arch/cortex_m.rs index ca1675c03..355a0f086 100644 --- a/embassy-executor/src/arch/cortex_m.rs +++ b/embassy-executor/src/arch/cortex_m.rs @@ -18,9 +18,7 @@ mod thread { /// TODO // Name pending #[derive(Default)] // Default enables Executor::new - pub struct CortexMThreadContext { - _not_send: core::marker::PhantomData<*mut ()>, - } + pub struct CortexMThreadContext; impl ThreadContext for CortexMThreadContext { #[cfg(feature = "thread-context")] diff --git a/embassy-executor/src/arch/riscv32.rs b/embassy-executor/src/arch/riscv32.rs index 5f766442d..becc0245a 100644 --- a/embassy-executor/src/arch/riscv32.rs +++ b/embassy-executor/src/arch/riscv32.rs @@ -8,7 +8,6 @@ compile_error!("`thread-context` is not supported with `arch-riscv32`."); pub use thread::*; #[cfg(feature = "executor-thread")] mod thread { - use core::marker::PhantomData; use core::sync::atomic::{AtomicBool, Ordering}; #[cfg(feature = "nightly")] @@ -28,9 +27,7 @@ mod thread { /// TODO // Name pending #[derive(Default)] // Default enables Executor::new - pub struct RiscVThreadContext { - _not_send: PhantomData<*mut ()>, - } + pub struct RiscVThreadContext; impl ThreadContext for RiscVThreadContext { fn context(&self) -> OpaqueThreadContext { diff --git a/embassy-executor/src/arch/std.rs b/embassy-executor/src/arch/std.rs index 28e25fbd0..b08974a02 100644 --- a/embassy-executor/src/arch/std.rs +++ b/embassy-executor/src/arch/std.rs @@ -8,7 +8,6 @@ compile_error!("`arch-std` requires `thread-context`."); pub use thread::*; #[cfg(feature = "executor-thread")] mod thread { - use std::marker::PhantomData; use std::sync::{Condvar, Mutex}; #[cfg(feature = "nightly")] @@ -20,16 +19,13 @@ mod thread { /// TODO // Name pending pub struct StdThreadCtx { - _not_send: PhantomData<*mut ()>, signaler: &'static Signaler, } impl Default for StdThreadCtx { fn default() -> Self { - let signaler = &*Box::leak(Box::new(Signaler::new())); Self { - _not_send: PhantomData, - signaler, + signaler: &*Box::leak(Box::new(Signaler::new())), } } } diff --git a/embassy-executor/src/arch/xtensa.rs b/embassy-executor/src/arch/xtensa.rs index 8e1b917de..6357bfef2 100644 --- a/embassy-executor/src/arch/xtensa.rs +++ b/embassy-executor/src/arch/xtensa.rs @@ -27,15 +27,8 @@ mod thread { /// TODO // Name pending - pub struct XtensaThreadContext { - _not_send: PhantomData<*mut ()>, - } - - impl Default for XtensaThreadContext { - fn default() -> Self { - Self { _not_send: PhantomData } - } - } + #[derive(Default)] // Default enables Executor::new + pub struct XtensaThreadContext; impl ThreadContext for XtensaThreadContext { fn context(&self) -> OpaqueThreadContext { From d5e66f6f87222de65ac575c4b923b2fee5487388 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sat, 12 Aug 2023 22:20:11 +0200 Subject: [PATCH 18/77] Lift thread-context feature restrictions --- embassy-executor/src/arch/riscv32.rs | 10 +++++++--- embassy-executor/src/arch/xtensa.rs | 19 ++++++++++--------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/embassy-executor/src/arch/riscv32.rs b/embassy-executor/src/arch/riscv32.rs index becc0245a..c4e772e34 100644 --- a/embassy-executor/src/arch/riscv32.rs +++ b/embassy-executor/src/arch/riscv32.rs @@ -1,9 +1,6 @@ #[cfg(feature = "executor-interrupt")] compile_error!("`executor-interrupt` is not supported with `arch-riscv32`."); -#[cfg(feature = "thread-context")] -compile_error!("`thread-context` is not supported with `arch-riscv32`."); - #[cfg(feature = "executor-thread")] pub use thread::*; #[cfg(feature = "executor-thread")] @@ -30,6 +27,13 @@ mod thread { pub struct RiscVThreadContext; impl ThreadContext for RiscVThreadContext { + #[cfg(feature = "thread-context")] + fn context(&self) -> OpaqueThreadContext { + // Enabling thread-context is not incorrect, just wasteful. + OpaqueThreadContext(0) + } + + #[cfg(not(feature = "thread-context"))] fn context(&self) -> OpaqueThreadContext { OpaqueThreadContext(()) } diff --git a/embassy-executor/src/arch/xtensa.rs b/embassy-executor/src/arch/xtensa.rs index 6357bfef2..1097bff83 100644 --- a/embassy-executor/src/arch/xtensa.rs +++ b/embassy-executor/src/arch/xtensa.rs @@ -1,12 +1,6 @@ #[cfg(feature = "executor-interrupt")] compile_error!("`executor-interrupt` is not supported with `arch-xtensa`."); -#[cfg(feature = "thread-context")] -compile_error!( - "`thread-context` is not supported with `arch-xtensa`.\ - Use a multicore-safe executor from esp-hal instead." // obviously, this is too specific to ESP32 -); - #[cfg(feature = "executor-thread")] pub use thread::*; #[cfg(feature = "executor-thread")] @@ -17,7 +11,7 @@ mod thread { use crate::raw::OpaqueThreadContext; use crate::thread::ThreadContext; - /// global atomic used to keep track of whether there is work to do since sev() is not available on RISCV + /// global atomic used to keep track of whether there is work to do since sev() is not available on Xtensa static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); #[export_name = "__thread_mode_pender"] @@ -31,6 +25,13 @@ mod thread { pub struct XtensaThreadContext; impl ThreadContext for XtensaThreadContext { + #[cfg(feature = "thread-context")] + fn context(&self) -> OpaqueThreadContext { + // Enabling thread-context is not incorrect, just wasteful. + OpaqueThreadContext(0) + } + + #[cfg(not(feature = "thread-context"))] fn context(&self) -> OpaqueThreadContext { OpaqueThreadContext(()) } @@ -43,8 +44,8 @@ mod thread { let token: critical_section::RawRestoreState; core::arch::asm!("rsil {0}, 5", out(reg) token); - // we do not care about race conditions between the load and store operations, interrupts - // will only set this value to true. + // we do not care about race conditions between the load and store operations, + // interrupts will only set this value to true. // if there is work to do, loop back to polling if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); From 6ab0d71d9246cdc65f392212d03d639a51d21098 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sat, 12 Aug 2023 22:42:50 +0200 Subject: [PATCH 19/77] Tweak identifiers and comments --- embassy-executor/src/arch/cortex_m.rs | 13 ++++-------- embassy-executor/src/arch/riscv32.rs | 13 ++++-------- embassy-executor/src/arch/std.rs | 12 +++++------ embassy-executor/src/arch/xtensa.rs | 13 ++++-------- embassy-executor/src/interrupt.rs | 16 ++++++++------ embassy-executor/src/thread.rs | 30 ++++++++++++++++++++------- 6 files changed, 50 insertions(+), 47 deletions(-) diff --git a/embassy-executor/src/arch/cortex_m.rs b/embassy-executor/src/arch/cortex_m.rs index 355a0f086..6c1300ae5 100644 --- a/embassy-executor/src/arch/cortex_m.rs +++ b/embassy-executor/src/arch/cortex_m.rs @@ -11,27 +11,22 @@ mod thread { use crate::thread::ThreadContext; #[export_name = "__thread_mode_pender"] - fn __thread_mode_pender(_core_id: OpaqueThreadContext) { + fn __thread_mode_pender(_context: OpaqueThreadContext) { unsafe { core::arch::asm!("sev") } } /// TODO // Name pending #[derive(Default)] // Default enables Executor::new - pub struct CortexMThreadContext; + pub struct Context; - impl ThreadContext for CortexMThreadContext { + impl ThreadContext for Context { #[cfg(feature = "thread-context")] fn context(&self) -> OpaqueThreadContext { // Enabling thread-context is not incorrect, just wasteful. OpaqueThreadContext(0) } - #[cfg(not(feature = "thread-context"))] - fn context(&self) -> OpaqueThreadContext { - OpaqueThreadContext(()) - } - fn wait(&mut self) { unsafe { core::arch::asm!("wfe") } } @@ -39,7 +34,7 @@ mod thread { /// TODO // Type alias for backwards compatibility - pub type Executor = crate::thread::ThreadModeExecutor; + pub type Executor = crate::thread::ThreadModeExecutor; } // None of this has to be public, I guess? diff --git a/embassy-executor/src/arch/riscv32.rs b/embassy-executor/src/arch/riscv32.rs index c4e772e34..087204006 100644 --- a/embassy-executor/src/arch/riscv32.rs +++ b/embassy-executor/src/arch/riscv32.rs @@ -17,27 +17,22 @@ mod thread { static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); #[export_name = "__thread_mode_pender"] - fn __thread_mode_pender(_core_id: OpaqueThreadContext) { + fn __thread_mode_pender(_context: OpaqueThreadContext) { SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst); } /// TODO // Name pending #[derive(Default)] // Default enables Executor::new - pub struct RiscVThreadContext; + pub struct Context; - impl ThreadContext for RiscVThreadContext { + impl ThreadContext for Context { #[cfg(feature = "thread-context")] fn context(&self) -> OpaqueThreadContext { // Enabling thread-context is not incorrect, just wasteful. OpaqueThreadContext(0) } - #[cfg(not(feature = "thread-context"))] - fn context(&self) -> OpaqueThreadContext { - OpaqueThreadContext(()) - } - fn wait(&mut self) { // We do not care about race conditions between the load and store operations, // interrupts will only set this value to true. @@ -60,5 +55,5 @@ mod thread { /// TODO // Type alias for backwards compatibility - pub type Executor = crate::thread::ThreadModeExecutor; + pub type Executor = crate::thread::ThreadModeExecutor; } diff --git a/embassy-executor/src/arch/std.rs b/embassy-executor/src/arch/std.rs index b08974a02..2731e275e 100644 --- a/embassy-executor/src/arch/std.rs +++ b/embassy-executor/src/arch/std.rs @@ -18,11 +18,11 @@ mod thread { /// TODO // Name pending - pub struct StdThreadCtx { + pub struct Context { signaler: &'static Signaler, } - impl Default for StdThreadCtx { + impl Default for Context { fn default() -> Self { Self { signaler: &*Box::leak(Box::new(Signaler::new())), @@ -30,7 +30,7 @@ mod thread { } } - impl ThreadContext for StdThreadCtx { + impl ThreadContext for Context { fn context(&self) -> OpaqueThreadContext { OpaqueThreadContext(self.signaler as *const _ as usize) } @@ -41,8 +41,8 @@ mod thread { } #[export_name = "__thread_mode_pender"] - fn __thread_mode_pender(core_id: OpaqueThreadContext) { - let signaler: &'static Signaler = unsafe { std::mem::transmute(core_id) }; + fn __thread_mode_pender(context: OpaqueThreadContext) { + let signaler: &'static Signaler = unsafe { std::mem::transmute(context) }; signaler.signal() } @@ -76,5 +76,5 @@ mod thread { /// TODO // Type alias for backwards compatibility - pub type Executor = crate::thread::ThreadModeExecutor; + pub type Executor = crate::thread::ThreadModeExecutor; } diff --git a/embassy-executor/src/arch/xtensa.rs b/embassy-executor/src/arch/xtensa.rs index 1097bff83..54c842025 100644 --- a/embassy-executor/src/arch/xtensa.rs +++ b/embassy-executor/src/arch/xtensa.rs @@ -15,27 +15,22 @@ mod thread { static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); #[export_name = "__thread_mode_pender"] - fn __thread_mode_pender(_core_id: OpaqueThreadContext) { + fn __thread_mode_pender(_context: OpaqueThreadContext) { SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst); } /// TODO // Name pending #[derive(Default)] // Default enables Executor::new - pub struct XtensaThreadContext; + pub struct Context; - impl ThreadContext for XtensaThreadContext { + impl ThreadContext for Context { #[cfg(feature = "thread-context")] fn context(&self) -> OpaqueThreadContext { // Enabling thread-context is not incorrect, just wasteful. OpaqueThreadContext(0) } - #[cfg(not(feature = "thread-context"))] - fn context(&self) -> OpaqueThreadContext { - OpaqueThreadContext(()) - } - fn wait(&mut self) { unsafe { // Manual critical section implementation that only masks interrupts handlers. @@ -66,5 +61,5 @@ mod thread { /// TODO // Type alias for backwards compatibility - pub type Executor = crate::thread::ThreadModeExecutor; + pub type Executor = crate::thread::ThreadModeExecutor; } diff --git a/embassy-executor/src/interrupt.rs b/embassy-executor/src/interrupt.rs index c1084ea7b..6f310651b 100644 --- a/embassy-executor/src/interrupt.rs +++ b/embassy-executor/src/interrupt.rs @@ -7,13 +7,17 @@ use atomic_polyfill::{AtomicBool, Ordering}; use crate::raw::{self, OpaqueInterruptContext, Pender}; -/// An interrupt source that can be used to drive an [`InterruptExecutor`]. -// Name pending +/// Architecture-specific interface for an interrupt-mode executor. This trait describes what data +/// should be passed to the [`InterruptExecutor`]'s pender, and how to enable the interrupt that +/// triggers polling the executor. +// TODO: Name pending pub trait InterruptContext { - /// Creates an opaque identifier for this interrupt. + /// A pointer-sized piece of data that is passed to the pender function. + /// + /// Usually, the context contains the interrupt that should be used to wake the executor. fn context(&self) -> OpaqueInterruptContext; - /// Sets up the interrupt request. + /// Enabled the interrupt request. fn enable(&self); } @@ -36,8 +40,8 @@ pub trait InterruptContext { /// Some chips reserve some interrupts for this purpose, sometimes named "software interrupts" (SWI). /// If this is not the case, you may use an interrupt from any unused peripheral. /// -/// It is somewhat more complex to use, it's recommended to use the thread-mode -/// [`Executor`] instead, if it works for your use case. +/// It is somewhat more complex to use, it's recommended to use the +/// [`crate::thread::ThreadModeExecutor`] instead, if it works for your use case. pub struct InterruptModeExecutor { started: AtomicBool, executor: UnsafeCell>, diff --git a/embassy-executor/src/thread.rs b/embassy-executor/src/thread.rs index 2d2c6daa5..f977d41e7 100644 --- a/embassy-executor/src/thread.rs +++ b/embassy-executor/src/thread.rs @@ -5,13 +5,21 @@ use core::marker::PhantomData; use crate::raw::{OpaqueThreadContext, Pender}; use crate::{raw, Spawner}; -/// TODO -// Name pending +/// Architecture-specific interface for a thread-mode executor. This trait describes what the +/// executor should do when idle, and what data should be passed to its pender. +// TODO: Name pending pub trait ThreadContext: Sized { - /// TODO + /// A pointer-sized piece of data that is passed to the pender function. + /// + /// For example, on multi-core systems, this can be used to store the ID of the core that + /// should be woken up. + #[cfg(feature = "thread-context")] fn context(&self) -> OpaqueThreadContext; - /// TODO + /// Waits for the executor to be waken. + /// + /// While it is valid for this function can be empty, it is recommended to use a WFE instruction + /// or equivalent to let the CPU sleep. fn wait(&mut self); } @@ -40,11 +48,17 @@ impl ThreadModeExecutor { Self::with_context(C::default()) } - /// Create a new Executor. - pub fn with_context(context: C) -> Self { + /// Create a new Executor using the given thread context. + pub fn with_context(thread_context: C) -> Self { + #[cfg(not(feature = "thread-context"))] + let context = OpaqueThreadContext(()); + + #[cfg(feature = "thread-context")] + let context = thread_context.context(); + Self { - inner: raw::Executor::new(Pender::Thread(context.context())), - context, + inner: raw::Executor::new(Pender::Thread(context)), + context: thread_context, not_send: PhantomData, } } From ec6bd27df6101bc5f77fa4eace0e8963970231ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 14 Aug 2023 08:22:22 +0200 Subject: [PATCH 20/77] Remove thread-context feature --- embassy-executor/Cargo.toml | 7 ++----- embassy-executor/src/arch/cortex_m.rs | 2 -- embassy-executor/src/arch/riscv32.rs | 2 -- embassy-executor/src/arch/std.rs | 3 --- embassy-executor/src/arch/wasm.rs | 3 --- embassy-executor/src/arch/xtensa.rs | 2 -- embassy-executor/src/raw/mod.rs | 7 ------- embassy-executor/src/thread.rs | 13 +++---------- 8 files changed, 5 insertions(+), 34 deletions(-) diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 182dd6937..ce5e2741f 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -31,11 +31,11 @@ features = ["nightly", "defmt", "pender-callback", "arch-cortex-m", "executor-th # Architecture _arch = [] # some arch was picked -arch-std = ["_arch", "critical-section/std", "thread-context"] +arch-std = ["_arch", "critical-section/std"] arch-cortex-m = ["_arch", "dep:cortex-m"] arch-xtensa = ["_arch"] arch-riscv32 = ["_arch"] -arch-wasm = ["_arch", "dep:wasm-bindgen", "dep:js-sys", "thread-context"] +arch-wasm = ["_arch", "dep:wasm-bindgen", "dep:js-sys"] # Enable creating a `Pender` from an arbitrary function pointer callback. pender-callback = [] @@ -45,9 +45,6 @@ executor-thread = [] # Enable the interrupt-mode executor (available in Cortex-M only) executor-interrupt = [] -# Pass a context to the thread-mode executor. -thread-context = [] - # Enable nightly-only features nightly = [] diff --git a/embassy-executor/src/arch/cortex_m.rs b/embassy-executor/src/arch/cortex_m.rs index 6c1300ae5..a29e5b23e 100644 --- a/embassy-executor/src/arch/cortex_m.rs +++ b/embassy-executor/src/arch/cortex_m.rs @@ -21,9 +21,7 @@ mod thread { pub struct Context; impl ThreadContext for Context { - #[cfg(feature = "thread-context")] fn context(&self) -> OpaqueThreadContext { - // Enabling thread-context is not incorrect, just wasteful. OpaqueThreadContext(0) } diff --git a/embassy-executor/src/arch/riscv32.rs b/embassy-executor/src/arch/riscv32.rs index 087204006..976e7bcb3 100644 --- a/embassy-executor/src/arch/riscv32.rs +++ b/embassy-executor/src/arch/riscv32.rs @@ -27,9 +27,7 @@ mod thread { pub struct Context; impl ThreadContext for Context { - #[cfg(feature = "thread-context")] fn context(&self) -> OpaqueThreadContext { - // Enabling thread-context is not incorrect, just wasteful. OpaqueThreadContext(0) } diff --git a/embassy-executor/src/arch/std.rs b/embassy-executor/src/arch/std.rs index 2731e275e..ceaa5c7ab 100644 --- a/embassy-executor/src/arch/std.rs +++ b/embassy-executor/src/arch/std.rs @@ -1,9 +1,6 @@ #[cfg(feature = "executor-interrupt")] compile_error!("`executor-interrupt` is not supported with `arch-std`."); -#[cfg(not(feature = "thread-context"))] -compile_error!("`arch-std` requires `thread-context`."); - #[cfg(feature = "executor-thread")] pub use thread::*; #[cfg(feature = "executor-thread")] diff --git a/embassy-executor/src/arch/wasm.rs b/embassy-executor/src/arch/wasm.rs index e244c0b3f..c393722b2 100644 --- a/embassy-executor/src/arch/wasm.rs +++ b/embassy-executor/src/arch/wasm.rs @@ -1,9 +1,6 @@ #[cfg(feature = "executor-interrupt")] compile_error!("`executor-interrupt` is not supported with `arch-wasm`."); -#[cfg(not(feature = "thread-context"))] -compile_error!("`arch-wasm` requires `thread-context`."); - #[cfg(feature = "executor-thread")] pub use thread::*; #[cfg(feature = "executor-thread")] diff --git a/embassy-executor/src/arch/xtensa.rs b/embassy-executor/src/arch/xtensa.rs index 54c842025..28abf352c 100644 --- a/embassy-executor/src/arch/xtensa.rs +++ b/embassy-executor/src/arch/xtensa.rs @@ -25,9 +25,7 @@ mod thread { pub struct Context; impl ThreadContext for Context { - #[cfg(feature = "thread-context")] fn context(&self) -> OpaqueThreadContext { - // Enabling thread-context is not incorrect, just wasteful. OpaqueThreadContext(0) } diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 7fd29db40..7795f1e4a 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -292,13 +292,6 @@ impl TaskPool { } /// Context given to the thread-mode executor's pender. -#[cfg(all(feature = "executor-thread", not(feature = "thread-context")))] -#[derive(Clone, Copy)] -#[repr(transparent)] -pub struct OpaqueThreadContext(pub(crate) ()); - -/// Context given to the thread-mode executor's pender. -#[cfg(all(feature = "executor-thread", feature = "thread-context"))] #[repr(transparent)] #[derive(Clone, Copy)] pub struct OpaqueThreadContext(pub(crate) usize); diff --git a/embassy-executor/src/thread.rs b/embassy-executor/src/thread.rs index f977d41e7..ef703003d 100644 --- a/embassy-executor/src/thread.rs +++ b/embassy-executor/src/thread.rs @@ -13,7 +13,6 @@ pub trait ThreadContext: Sized { /// /// For example, on multi-core systems, this can be used to store the ID of the core that /// should be woken up. - #[cfg(feature = "thread-context")] fn context(&self) -> OpaqueThreadContext; /// Waits for the executor to be waken. @@ -49,16 +48,10 @@ impl ThreadModeExecutor { } /// Create a new Executor using the given thread context. - pub fn with_context(thread_context: C) -> Self { - #[cfg(not(feature = "thread-context"))] - let context = OpaqueThreadContext(()); - - #[cfg(feature = "thread-context")] - let context = thread_context.context(); - + pub fn with_context(context: C) -> Self { Self { - inner: raw::Executor::new(Pender::Thread(context)), - context: thread_context, + inner: raw::Executor::new(Pender::Thread(context.context())), + context, not_send: PhantomData, } } From 454a7cbf4c0eb3a4e651e7da5512ec49ff7d4050 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 14 Aug 2023 08:32:26 +0200 Subject: [PATCH 21/77] Remove pender-callback --- embassy-executor/Cargo.toml | 7 ++----- embassy-executor/src/raw/mod.rs | 19 ------------------- 2 files changed, 2 insertions(+), 24 deletions(-) diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index ce5e2741f..d190c95a3 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -14,7 +14,7 @@ categories = [ [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-executor-v$VERSION/embassy-executor/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-executor/src/" -features = ["nightly", "defmt", "pender-callback"] +features = ["nightly", "defmt"] flavors = [ { name = "std", target = "x86_64-unknown-linux-gnu", features = ["arch-std", "executor-thread"] }, { name = "wasm", target = "wasm32-unknown-unknown", features = ["arch-wasm", "executor-thread"] }, @@ -25,7 +25,7 @@ flavors = [ [package.metadata.docs.rs] default-target = "thumbv7em-none-eabi" targets = ["thumbv7em-none-eabi"] -features = ["nightly", "defmt", "pender-callback", "arch-cortex-m", "executor-thread", "executor-interrupt"] +features = ["nightly", "defmt", "arch-cortex-m", "executor-thread", "executor-interrupt"] [features] @@ -37,9 +37,6 @@ arch-xtensa = ["_arch"] arch-riscv32 = ["_arch"] arch-wasm = ["_arch", "dep:wasm-bindgen", "dep:js-sys"] -# Enable creating a `Pender` from an arbitrary function pointer callback. -pender-callback = [] - # Enable the thread-mode executor (using WFE/SEV in Cortex-M, WFI in other embedded archs) executor-thread = [] # Enable the interrupt-mode executor (available in Cortex-M only) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 7795f1e4a..81ad1e53d 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -317,28 +317,11 @@ pub enum Pender { /// Pender for an interrupt-mode executor. #[cfg(feature = "executor-interrupt")] Interrupt(OpaqueInterruptContext), - - /// Arbitrary, dynamically dispatched pender. - #[cfg(feature = "pender-callback")] - Callback { func: fn(*mut ()), context: *mut () }, } unsafe impl Send for Pender {} unsafe impl Sync for Pender {} -impl Pender { - /// Create a `Pender` that will call an arbitrary function pointer. - /// - /// # Arguments - /// - /// - `func`: The function pointer to call. - /// - `context`: Opaque context pointer, that will be passed to the function pointer. - #[cfg(feature = "pender-callback")] - pub fn new_from_callback(func: fn(*mut ()), context: *mut ()) -> Self { - Self::Callback { func, context } - } -} - impl Pender { pub(crate) fn pend(self) { match self { @@ -356,8 +339,6 @@ impl Pender { } unsafe { __interrupt_mode_pender(interrupt) }; } - #[cfg(feature = "pender-callback")] - Pender::Callback { func, context } => func(context), } } } From f6007869bffd3ed4f48e74222dc40d11c7c87ec0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 14 Aug 2023 08:57:14 +0200 Subject: [PATCH 22/77] Remove the Pender enum --- embassy-executor/src/arch/cortex_m.rs | 103 +++++++++++++++++--------- embassy-executor/src/arch/riscv32.rs | 10 +-- embassy-executor/src/arch/std.rs | 10 +-- embassy-executor/src/arch/wasm.rs | 6 +- embassy-executor/src/arch/xtensa.rs | 8 +- embassy-executor/src/interrupt.rs | 6 +- embassy-executor/src/raw/mod.rs | 48 ++---------- embassy-executor/src/thread.rs | 8 +- 8 files changed, 98 insertions(+), 101 deletions(-) diff --git a/embassy-executor/src/arch/cortex_m.rs b/embassy-executor/src/arch/cortex_m.rs index a29e5b23e..7f8a97ef1 100644 --- a/embassy-executor/src/arch/cortex_m.rs +++ b/embassy-executor/src/arch/cortex_m.rs @@ -1,28 +1,84 @@ #[cfg(feature = "executor-thread")] pub use thread::*; +use crate::raw::PenderContext; + +#[cfg(feature = "executor-interrupt")] + +/// # Safety +/// +/// `irq` must be a valid interrupt request number +unsafe fn nvic_pend(irq: u16) { + use cortex_m::interrupt::InterruptNumber; + + #[derive(Clone, Copy)] + struct Irq(u16); + unsafe impl InterruptNumber for Irq { + fn number(self) -> u16 { + self.0 + } + } + + let irq = Irq(irq); + + // STIR is faster, but is only available in v7 and higher. + #[cfg(not(armv6m))] + { + let mut nvic: cortex_m::peripheral::NVIC = unsafe { core::mem::transmute(()) }; + nvic.request(irq); + } + + #[cfg(armv6m)] + cortex_m::peripheral::NVIC::pend(irq); +} + +#[cfg(all(feature = "executor-thread", feature = "executor-interrupt"))] +#[export_name = "__pender"] +fn __pender(context: PenderContext) { + unsafe { + // Safety: `context` is either `usize::MAX` created by `Executor::run`, or a valid interrupt + // request number given to `InterruptExecutor::start`. + if context as usize == usize::MAX { + core::arch::asm!("sev") + } else { + nvic_pend(context as u16) + } + } +} + +#[cfg(all(feature = "executor-thread", not(feature = "executor-interrupt")))] +#[export_name = "__pender"] +fn __pender(_context: PenderContext) { + unsafe { core::arch::asm!("sev") } +} + +#[cfg(all(not(feature = "executor-thread"), feature = "executor-interrupt"))] +#[export_name = "__pender"] +fn __pender(context: PenderContext) { + unsafe { + // Safety: `context` is the same value we passed to `InterruptExecutor::start`, which must + // be a valid interrupt request number. + nvic_pend(context as u16) + } +} + #[cfg(feature = "executor-thread")] mod thread { #[cfg(feature = "nightly")] pub use embassy_macros::main_cortex_m as main; - use crate::raw::OpaqueThreadContext; + use crate::raw::PenderContext; use crate::thread::ThreadContext; - #[export_name = "__thread_mode_pender"] - fn __thread_mode_pender(_context: OpaqueThreadContext) { - unsafe { core::arch::asm!("sev") } - } - /// TODO // Name pending #[derive(Default)] // Default enables Executor::new pub struct Context; impl ThreadContext for Context { - fn context(&self) -> OpaqueThreadContext { - OpaqueThreadContext(0) + fn context(&self) -> PenderContext { + usize::MAX } fn wait(&mut self) { @@ -35,7 +91,6 @@ mod thread { pub type Executor = crate::thread::ThreadModeExecutor; } -// None of this has to be public, I guess? #[cfg(feature = "executor-interrupt")] pub use interrupt::*; #[cfg(feature = "executor-interrupt")] @@ -44,23 +99,14 @@ mod interrupt { use cortex_m::peripheral::NVIC; use crate::interrupt::InterruptContext; - use crate::raw::OpaqueInterruptContext; - - #[derive(Clone, Copy)] - struct CortexMInterruptContext(u16); - - unsafe impl cortex_m::interrupt::InterruptNumber for CortexMInterruptContext { - fn number(self) -> u16 { - self.0 - } - } + use crate::raw::PenderContext; impl InterruptContext for T where T: InterruptNumber, { - fn context(&self) -> OpaqueInterruptContext { - OpaqueInterruptContext(self.number() as usize) + fn context(&self) -> PenderContext { + self.number() as usize } fn enable(&self) { @@ -68,21 +114,6 @@ mod interrupt { } } - #[export_name = "__interrupt_mode_pender"] - fn __interrupt_mode_pender(interrupt: OpaqueInterruptContext) { - let interrupt = CortexMInterruptContext(unsafe { core::mem::transmute::<_, usize>(interrupt) as u16 }); - - // STIR is faster, but is only available in v7 and higher. - #[cfg(not(armv6m))] - { - let mut nvic: NVIC = unsafe { core::mem::transmute(()) }; - nvic.request(interrupt); - } - - #[cfg(armv6m)] - NVIC::pend(interrupt); - } - /// TODO // Type alias for backwards compatibility pub type InterruptExecutor = crate::interrupt::InterruptModeExecutor; diff --git a/embassy-executor/src/arch/riscv32.rs b/embassy-executor/src/arch/riscv32.rs index 976e7bcb3..886056e84 100644 --- a/embassy-executor/src/arch/riscv32.rs +++ b/embassy-executor/src/arch/riscv32.rs @@ -10,14 +10,14 @@ mod thread { #[cfg(feature = "nightly")] pub use embassy_macros::main_riscv as main; - use crate::raw::OpaqueThreadContext; + use crate::raw::PenderContext; use crate::thread::ThreadContext; /// global atomic used to keep track of whether there is work to do since sev() is not available on RISCV static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); - #[export_name = "__thread_mode_pender"] - fn __thread_mode_pender(_context: OpaqueThreadContext) { + #[export_name = "__pender"] + fn __thread_mode_pender(_context: PenderContext) { SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst); } @@ -27,8 +27,8 @@ mod thread { pub struct Context; impl ThreadContext for Context { - fn context(&self) -> OpaqueThreadContext { - OpaqueThreadContext(0) + fn context(&self) -> PenderContext { + 0 } fn wait(&mut self) { diff --git a/embassy-executor/src/arch/std.rs b/embassy-executor/src/arch/std.rs index ceaa5c7ab..d2a069d1c 100644 --- a/embassy-executor/src/arch/std.rs +++ b/embassy-executor/src/arch/std.rs @@ -10,7 +10,7 @@ mod thread { #[cfg(feature = "nightly")] pub use embassy_macros::main_std as main; - use crate::raw::OpaqueThreadContext; + use crate::raw::PenderContext; use crate::thread::ThreadContext; /// TODO @@ -28,8 +28,8 @@ mod thread { } impl ThreadContext for Context { - fn context(&self) -> OpaqueThreadContext { - OpaqueThreadContext(self.signaler as *const _ as usize) + fn context(&self) -> PenderContext { + self.signaler as *const _ as usize } fn wait(&mut self) { @@ -37,8 +37,8 @@ mod thread { } } - #[export_name = "__thread_mode_pender"] - fn __thread_mode_pender(context: OpaqueThreadContext) { + #[export_name = "__pender"] + fn __pender(context: PenderContext) { let signaler: &'static Signaler = unsafe { std::mem::transmute(context) }; signaler.signal() } diff --git a/embassy-executor/src/arch/wasm.rs b/embassy-executor/src/arch/wasm.rs index c393722b2..634f48d1a 100644 --- a/embassy-executor/src/arch/wasm.rs +++ b/embassy-executor/src/arch/wasm.rs @@ -14,11 +14,11 @@ mod thread { use wasm_bindgen::prelude::*; use crate::raw::util::UninitCell; - use crate::raw::{OpaqueThreadContext, Pender}; + use crate::raw::PenderContext; use crate::{raw, Spawner}; #[export_name = "__thread_mode_pender"] - fn __thread_mode_pender(context: OpaqueThreadContext) { + fn __thread_mode_pender(context: PenderContext) { let signaler: &'static WasmContext = unsafe { std::mem::transmute(context) }; let _ = signaler.promise.then(unsafe { signaler.closure.as_mut() }); } @@ -49,7 +49,7 @@ mod thread { pub fn new() -> Self { let ctx = &*Box::leak(Box::new(WasmContext::new())); Self { - inner: raw::Executor::new(Pender::Thread(OpaqueThreadContext(ctx as *const _ as usize))), + inner: raw::Executor::new(ctx as *const _ as usize), ctx, not_send: PhantomData, } diff --git a/embassy-executor/src/arch/xtensa.rs b/embassy-executor/src/arch/xtensa.rs index 28abf352c..3986c6c19 100644 --- a/embassy-executor/src/arch/xtensa.rs +++ b/embassy-executor/src/arch/xtensa.rs @@ -8,14 +8,14 @@ mod thread { use core::marker::PhantomData; use core::sync::atomic::{AtomicBool, Ordering}; - use crate::raw::OpaqueThreadContext; + use crate::raw::PenderContext; use crate::thread::ThreadContext; /// global atomic used to keep track of whether there is work to do since sev() is not available on Xtensa static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); #[export_name = "__thread_mode_pender"] - fn __thread_mode_pender(_context: OpaqueThreadContext) { + fn __thread_mode_pender(_context: PenderContext) { SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst); } @@ -25,8 +25,8 @@ mod thread { pub struct Context; impl ThreadContext for Context { - fn context(&self) -> OpaqueThreadContext { - OpaqueThreadContext(0) + fn context(&self) -> PenderContext { + 0 } fn wait(&mut self) { diff --git a/embassy-executor/src/interrupt.rs b/embassy-executor/src/interrupt.rs index 6f310651b..28a1cd525 100644 --- a/embassy-executor/src/interrupt.rs +++ b/embassy-executor/src/interrupt.rs @@ -5,7 +5,7 @@ use core::mem::MaybeUninit; use atomic_polyfill::{AtomicBool, Ordering}; -use crate::raw::{self, OpaqueInterruptContext, Pender}; +use crate::raw::{self, PenderContext}; /// Architecture-specific interface for an interrupt-mode executor. This trait describes what data /// should be passed to the [`InterruptExecutor`]'s pender, and how to enable the interrupt that @@ -15,7 +15,7 @@ pub trait InterruptContext { /// A pointer-sized piece of data that is passed to the pender function. /// /// Usually, the context contains the interrupt that should be used to wake the executor. - fn context(&self) -> OpaqueInterruptContext; + fn context(&self) -> PenderContext; /// Enabled the interrupt request. fn enable(&self); @@ -104,7 +104,7 @@ impl InterruptModeExecutor { unsafe { (&mut *self.executor.get()) .as_mut_ptr() - .write(raw::Executor::new(Pender::Interrupt(irq.context()))) + .write(raw::Executor::new(irq.context())) } let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 81ad1e53d..a0a940e25 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -292,54 +292,20 @@ impl TaskPool { } /// Context given to the thread-mode executor's pender. -#[repr(transparent)] -#[derive(Clone, Copy)] -pub struct OpaqueThreadContext(pub(crate) usize); +pub type PenderContext = usize; -/// Context given to the interrupt-mode executor's pender. #[derive(Clone, Copy)] -#[repr(transparent)] -pub struct OpaqueInterruptContext(pub(crate) usize); - -/// Platform/architecture-specific action executed when an executor has pending work. -/// -/// When a task within an executor is woken, the `Pender` is called. This does a -/// platform/architecture-specific action to signal there is pending work in the executor. -/// When this happens, you must arrange for [`Executor::poll`] to be called. -/// -/// You can think of it as a waker, but for the whole executor. -#[derive(Clone, Copy)] -pub enum Pender { - /// Pender for a thread-mode executor. - #[cfg(feature = "executor-thread")] - Thread(OpaqueThreadContext), - - /// Pender for an interrupt-mode executor. - #[cfg(feature = "executor-interrupt")] - Interrupt(OpaqueInterruptContext), -} +pub(crate) struct Pender(PenderContext); unsafe impl Send for Pender {} unsafe impl Sync for Pender {} impl Pender { pub(crate) fn pend(self) { - match self { - #[cfg(feature = "executor-thread")] - Pender::Thread(core_id) => { - extern "Rust" { - fn __thread_mode_pender(core_id: OpaqueThreadContext); - } - unsafe { __thread_mode_pender(core_id) }; - } - #[cfg(feature = "executor-interrupt")] - Pender::Interrupt(interrupt) => { - extern "Rust" { - fn __interrupt_mode_pender(interrupt: OpaqueInterruptContext); - } - unsafe { __interrupt_mode_pender(interrupt) }; - } + extern "Rust" { + fn __pender(context: PenderContext); } + unsafe { __pender(self.0) }; } } @@ -499,9 +465,9 @@ impl Executor { /// When the executor has work to do, it will call the [`Pender`]. /// /// See [`Executor`] docs for details on `Pender`. - pub fn new(pender: Pender) -> Self { + pub fn new(context: PenderContext) -> Self { Self { - inner: SyncExecutor::new(pender), + inner: SyncExecutor::new(Pender(context)), _not_sync: PhantomData, } } diff --git a/embassy-executor/src/thread.rs b/embassy-executor/src/thread.rs index ef703003d..8ff4071da 100644 --- a/embassy-executor/src/thread.rs +++ b/embassy-executor/src/thread.rs @@ -2,8 +2,8 @@ use core::marker::PhantomData; -use crate::raw::{OpaqueThreadContext, Pender}; -use crate::{raw, Spawner}; +use crate::raw::{self, PenderContext}; +use crate::Spawner; /// Architecture-specific interface for a thread-mode executor. This trait describes what the /// executor should do when idle, and what data should be passed to its pender. @@ -13,7 +13,7 @@ pub trait ThreadContext: Sized { /// /// For example, on multi-core systems, this can be used to store the ID of the core that /// should be woken up. - fn context(&self) -> OpaqueThreadContext; + fn context(&self) -> PenderContext; /// Waits for the executor to be waken. /// @@ -50,7 +50,7 @@ impl ThreadModeExecutor { /// Create a new Executor using the given thread context. pub fn with_context(context: C) -> Self { Self { - inner: raw::Executor::new(Pender::Thread(context.context())), + inner: raw::Executor::new(context.context()), context, not_send: PhantomData, } From 4c4b12c307bf77516299eb73f9da00ef777b9814 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 14 Aug 2023 15:16:40 +0200 Subject: [PATCH 23/77] Make PenderContext opaque --- embassy-executor/src/arch/cortex_m.rs | 6 ++++-- embassy-executor/src/arch/riscv32.rs | 2 +- embassy-executor/src/arch/std.rs | 2 +- embassy-executor/src/arch/wasm.rs | 2 +- embassy-executor/src/arch/xtensa.rs | 2 +- embassy-executor/src/raw/mod.rs | 25 ++++++++++++++++++++++--- 6 files changed, 30 insertions(+), 9 deletions(-) diff --git a/embassy-executor/src/arch/cortex_m.rs b/embassy-executor/src/arch/cortex_m.rs index 7f8a97ef1..439db0fc0 100644 --- a/embassy-executor/src/arch/cortex_m.rs +++ b/embassy-executor/src/arch/cortex_m.rs @@ -36,6 +36,7 @@ unsafe fn nvic_pend(irq: u16) { #[export_name = "__pender"] fn __pender(context: PenderContext) { unsafe { + let context: usize = core::mem::transmute(context); // Safety: `context` is either `usize::MAX` created by `Executor::run`, or a valid interrupt // request number given to `InterruptExecutor::start`. if context as usize == usize::MAX { @@ -56,6 +57,7 @@ fn __pender(_context: PenderContext) { #[export_name = "__pender"] fn __pender(context: PenderContext) { unsafe { + let context: usize = core::mem::transmute(context); // Safety: `context` is the same value we passed to `InterruptExecutor::start`, which must // be a valid interrupt request number. nvic_pend(context as u16) @@ -78,7 +80,7 @@ mod thread { impl ThreadContext for Context { fn context(&self) -> PenderContext { - usize::MAX + unsafe { core::mem::transmute(usize::MAX) } } fn wait(&mut self) { @@ -106,7 +108,7 @@ mod interrupt { T: InterruptNumber, { fn context(&self) -> PenderContext { - self.number() as usize + unsafe { core::mem::transmute(self.number() as usize) } } fn enable(&self) { diff --git a/embassy-executor/src/arch/riscv32.rs b/embassy-executor/src/arch/riscv32.rs index 886056e84..f76a4bcf6 100644 --- a/embassy-executor/src/arch/riscv32.rs +++ b/embassy-executor/src/arch/riscv32.rs @@ -28,7 +28,7 @@ mod thread { impl ThreadContext for Context { fn context(&self) -> PenderContext { - 0 + unsafe { core::mem::transmute(0) } } fn wait(&mut self) { diff --git a/embassy-executor/src/arch/std.rs b/embassy-executor/src/arch/std.rs index d2a069d1c..d55de118d 100644 --- a/embassy-executor/src/arch/std.rs +++ b/embassy-executor/src/arch/std.rs @@ -29,7 +29,7 @@ mod thread { impl ThreadContext for Context { fn context(&self) -> PenderContext { - self.signaler as *const _ as usize + unsafe { core::mem::transmute(self.signaler) } } fn wait(&mut self) { diff --git a/embassy-executor/src/arch/wasm.rs b/embassy-executor/src/arch/wasm.rs index 634f48d1a..452c3e394 100644 --- a/embassy-executor/src/arch/wasm.rs +++ b/embassy-executor/src/arch/wasm.rs @@ -49,7 +49,7 @@ mod thread { pub fn new() -> Self { let ctx = &*Box::leak(Box::new(WasmContext::new())); Self { - inner: raw::Executor::new(ctx as *const _ as usize), + inner: raw::Executor::new(unsafe { core::mem::transmute(ctx) }), ctx, not_send: PhantomData, } diff --git a/embassy-executor/src/arch/xtensa.rs b/embassy-executor/src/arch/xtensa.rs index 3986c6c19..1aea9f230 100644 --- a/embassy-executor/src/arch/xtensa.rs +++ b/embassy-executor/src/arch/xtensa.rs @@ -26,7 +26,7 @@ mod thread { impl ThreadContext for Context { fn context(&self) -> PenderContext { - 0 + unsafe { core::mem::transmute(0) } } fn wait(&mut self) { diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index a0a940e25..4a6e45535 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -292,10 +292,29 @@ impl TaskPool { } /// Context given to the thread-mode executor's pender. -pub type PenderContext = usize; - +#[repr(transparent)] #[derive(Clone, Copy)] -pub(crate) struct Pender(PenderContext); +pub struct PenderContext(usize); + +/// Platform/architecture-specific action executed when an executor has pending work. +/// +/// When a task within an executor is woken, the `Pender` is called. This does a +/// platform/architecture-specific action to signal there is pending work in the executor. +/// When this happens, you must arrange for [`Executor::poll`] to be called. +/// +/// You can think of it as a waker, but for the whole executor. +/// +/// Platform/architecture implementations must provide a function that can be referred to as: +/// +/// ```rust +/// use embassy_executor::raw::PenderContext; +/// +/// extern "Rust" { +/// fn __pender(context: PenderContext); +/// } +/// ``` +#[derive(Clone, Copy)] +pub struct Pender(PenderContext); unsafe impl Send for Pender {} unsafe impl Sync for Pender {} From 986a63ebb8611a4dc7c6b14e03146286942ec8e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 14 Aug 2023 15:35:22 +0200 Subject: [PATCH 24/77] Remove the non-specific thread-mode executor --- embassy-executor/src/arch/cortex_m.rs | 296 ++++++++++++++++++-------- embassy-executor/src/arch/riscv32.rs | 92 +++++--- embassy-executor/src/arch/std.rs | 70 +++--- embassy-executor/src/arch/xtensa.rs | 94 +++++--- embassy-executor/src/interrupt.rs | 2 +- embassy-executor/src/lib.rs | 4 +- embassy-executor/src/thread.rs | 87 -------- 7 files changed, 369 insertions(+), 276 deletions(-) delete mode 100644 embassy-executor/src/thread.rs diff --git a/embassy-executor/src/arch/cortex_m.rs b/embassy-executor/src/arch/cortex_m.rs index 439db0fc0..2ed70dd1e 100644 --- a/embassy-executor/src/arch/cortex_m.rs +++ b/embassy-executor/src/arch/cortex_m.rs @@ -1,122 +1,236 @@ -#[cfg(feature = "executor-thread")] -pub use thread::*; +const THREAD_PENDER: usize = usize::MAX; -use crate::raw::PenderContext; - -#[cfg(feature = "executor-interrupt")] - -/// # Safety -/// -/// `irq` must be a valid interrupt request number -unsafe fn nvic_pend(irq: u16) { - use cortex_m::interrupt::InterruptNumber; - - #[derive(Clone, Copy)] - struct Irq(u16); - unsafe impl InterruptNumber for Irq { - fn number(self) -> u16 { - self.0 - } - } - - let irq = Irq(irq); - - // STIR is faster, but is only available in v7 and higher. - #[cfg(not(armv6m))] - { - let mut nvic: cortex_m::peripheral::NVIC = unsafe { core::mem::transmute(()) }; - nvic.request(irq); - } - - #[cfg(armv6m)] - cortex_m::peripheral::NVIC::pend(irq); -} - -#[cfg(all(feature = "executor-thread", feature = "executor-interrupt"))] #[export_name = "__pender"] -fn __pender(context: PenderContext) { +#[cfg(any(feature = "executor-thread", feature = "executor-interrupt"))] +fn __pender(context: crate::raw::PenderContext) { unsafe { - let context: usize = core::mem::transmute(context); // Safety: `context` is either `usize::MAX` created by `Executor::run`, or a valid interrupt // request number given to `InterruptExecutor::start`. - if context as usize == usize::MAX { - core::arch::asm!("sev") - } else { - nvic_pend(context as u16) + + let context: usize = core::mem::transmute(context); + + #[cfg(feature = "executor-thread")] + if context == THREAD_PENDER { + core::arch::asm!("sev"); + return; + } + + #[cfg(feature = "executor-interrupt")] + { + use cortex_m::interrupt::InterruptNumber; + use cortex_m::peripheral::NVIC; + + #[derive(Clone, Copy)] + struct Irq(u16); + unsafe impl InterruptNumber for Irq { + fn number(self) -> u16 { + self.0 + } + } + + let irq = Irq(context as u16); + + // STIR is faster, but is only available in v7 and higher. + #[cfg(not(armv6m))] + { + let mut nvic: NVIC = core::mem::transmute(()); + nvic.request(irq); + } + + #[cfg(armv6m)] + NVIC::pend(irq); } } } -#[cfg(all(feature = "executor-thread", not(feature = "executor-interrupt")))] -#[export_name = "__pender"] -fn __pender(_context: PenderContext) { - unsafe { core::arch::asm!("sev") } -} - -#[cfg(all(not(feature = "executor-thread"), feature = "executor-interrupt"))] -#[export_name = "__pender"] -fn __pender(context: PenderContext) { - unsafe { - let context: usize = core::mem::transmute(context); - // Safety: `context` is the same value we passed to `InterruptExecutor::start`, which must - // be a valid interrupt request number. - nvic_pend(context as u16) - } -} - +#[cfg(feature = "executor-thread")] +pub use thread::*; #[cfg(feature = "executor-thread")] mod thread { + use core::arch::asm; + use core::marker::PhantomData; #[cfg(feature = "nightly")] pub use embassy_macros::main_cortex_m as main; - use crate::raw::PenderContext; - use crate::thread::ThreadContext; + use crate::arch::THREAD_PENDER; + use crate::{raw, Spawner}; - /// TODO - // Name pending - #[derive(Default)] // Default enables Executor::new - pub struct Context; - - impl ThreadContext for Context { - fn context(&self) -> PenderContext { - unsafe { core::mem::transmute(usize::MAX) } - } - - fn wait(&mut self) { - unsafe { core::arch::asm!("wfe") } - } + /// Thread mode executor, using WFE/SEV. + /// + /// This is the simplest and most common kind of executor. It runs on + /// thread mode (at the lowest priority level), and uses the `WFE` ARM instruction + /// to sleep when it has no more work to do. When a task is woken, a `SEV` instruction + /// is executed, to make the `WFE` exit from sleep and poll the task. + /// + /// This executor allows for ultra low power consumption for chips where `WFE` + /// triggers low-power sleep without extra steps. If your chip requires extra steps, + /// you may use [`raw::Executor`] directly to program custom behavior. + pub struct Executor { + inner: raw::Executor, + not_send: PhantomData<*mut ()>, } - /// TODO - // Type alias for backwards compatibility - pub type Executor = crate::thread::ThreadModeExecutor; + impl Executor { + /// Create a new Executor. + pub fn new() -> Self { + Self { + inner: raw::Executor::new(unsafe { core::mem::transmute(THREAD_PENDER) }), + not_send: PhantomData, + } + } + + /// Run the executor. + /// + /// The `init` closure is called with a [`Spawner`] that spawns tasks on + /// this executor. Use it to spawn the initial task(s). After `init` returns, + /// the executor starts running the tasks. + /// + /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), + /// for example by passing it as an argument to the initial tasks. + /// + /// This function requires `&'static mut self`. This means you have to store the + /// Executor instance in a place where it'll live forever and grants you mutable + /// access. There's a few ways to do this: + /// + /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) + /// - a `static mut` (unsafe) + /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) + /// + /// This function never returns. + pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { + init(self.inner.spawner()); + + loop { + unsafe { + self.inner.poll(); + asm!("wfe"); + }; + } + } + } } #[cfg(feature = "executor-interrupt")] pub use interrupt::*; #[cfg(feature = "executor-interrupt")] mod interrupt { + use core::cell::UnsafeCell; + use core::mem::MaybeUninit; + + use atomic_polyfill::{AtomicBool, Ordering}; use cortex_m::interrupt::InterruptNumber; use cortex_m::peripheral::NVIC; - use crate::interrupt::InterruptContext; - use crate::raw::PenderContext; + use crate::raw; - impl InterruptContext for T - where - T: InterruptNumber, - { - fn context(&self) -> PenderContext { - unsafe { core::mem::transmute(self.number() as usize) } - } - - fn enable(&self) { - unsafe { NVIC::unmask(*self) } - } + /// Interrupt mode executor. + /// + /// This executor runs tasks in interrupt mode. The interrupt handler is set up + /// to poll tasks, and when a task is woken the interrupt is pended from software. + /// + /// This allows running async tasks at a priority higher than thread mode. One + /// use case is to leave thread mode free for non-async tasks. Another use case is + /// to run multiple executors: one in thread mode for low priority tasks and another in + /// interrupt mode for higher priority tasks. Higher priority tasks will preempt lower + /// priority ones. + /// + /// It is even possible to run multiple interrupt mode executors at different priorities, + /// by assigning different priorities to the interrupts. For an example on how to do this, + /// See the 'multiprio' example for 'embassy-nrf'. + /// + /// To use it, you have to pick an interrupt that won't be used by the hardware. + /// Some chips reserve some interrupts for this purpose, sometimes named "software interrupts" (SWI). + /// If this is not the case, you may use an interrupt from any unused peripheral. + /// + /// It is somewhat more complex to use, it's recommended to use the thread-mode + /// [`Executor`] instead, if it works for your use case. + pub struct InterruptExecutor { + started: AtomicBool, + executor: UnsafeCell>, } - /// TODO - // Type alias for backwards compatibility - pub type InterruptExecutor = crate::interrupt::InterruptModeExecutor; + unsafe impl Send for InterruptExecutor {} + unsafe impl Sync for InterruptExecutor {} + + impl InterruptExecutor { + /// Create a new, not started `InterruptExecutor`. + #[inline] + pub const fn new() -> Self { + Self { + started: AtomicBool::new(false), + executor: UnsafeCell::new(MaybeUninit::uninit()), + } + } + + /// Executor interrupt callback. + /// + /// # Safety + /// + /// You MUST call this from the interrupt handler, and from nowhere else. + pub unsafe fn on_interrupt(&'static self) { + let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; + executor.poll(); + } + + /// Start the executor. + /// + /// This initializes the executor, enables the interrupt, and returns. + /// The executor keeps running in the background through the interrupt. + /// + /// This returns a [`SendSpawner`] you can use to spawn tasks on it. A [`SendSpawner`] + /// is returned instead of a [`Spawner`](embassy_executor::Spawner) because the executor effectively runs in a + /// different "thread" (the interrupt), so spawning tasks on it is effectively + /// sending them. + /// + /// To obtain a [`Spawner`](embassy_executor::Spawner) for this executor, use [`Spawner::for_current_executor()`](embassy_executor::Spawner::for_current_executor()) from + /// a task running in it. + /// + /// # Interrupt requirements + /// + /// You must write the interrupt handler yourself, and make it call [`on_interrupt()`](Self::on_interrupt). + /// + /// This method already enables (unmasks) the interrupt, you must NOT do it yourself. + /// + /// You must set the interrupt priority before calling this method. You MUST NOT + /// do it after. + /// + pub fn start(&'static self, irq: impl InterruptNumber) -> crate::SendSpawner { + if self + .started + .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) + .is_err() + { + panic!("InterruptExecutor::start() called multiple times on the same executor."); + } + + unsafe { + let context = core::mem::transmute(irq.number() as usize); + (&mut *self.executor.get()) + .as_mut_ptr() + .write(raw::Executor::new(context)) + } + + let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; + + unsafe { NVIC::unmask(irq) } + + executor.spawner().make_send() + } + + /// Get a SendSpawner for this executor + /// + /// This returns a [`SendSpawner`] you can use to spawn tasks on this + /// executor. + /// + /// This MUST only be called on an executor that has already been spawned. + /// The function will panic otherwise. + pub fn spawner(&'static self) -> crate::SendSpawner { + if !self.started.load(Ordering::Acquire) { + panic!("InterruptExecutor::spawner() called on uninitialized executor."); + } + let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; + executor.spawner().make_send() + } + } } diff --git a/embassy-executor/src/arch/riscv32.rs b/embassy-executor/src/arch/riscv32.rs index f76a4bcf6..551d7527f 100644 --- a/embassy-executor/src/arch/riscv32.rs +++ b/embassy-executor/src/arch/riscv32.rs @@ -5,53 +5,77 @@ compile_error!("`executor-interrupt` is not supported with `arch-riscv32`."); pub use thread::*; #[cfg(feature = "executor-thread")] mod thread { + use core::marker::PhantomData; use core::sync::atomic::{AtomicBool, Ordering}; #[cfg(feature = "nightly")] pub use embassy_macros::main_riscv as main; - use crate::raw::PenderContext; - use crate::thread::ThreadContext; + use crate::{raw, Spawner}; /// global atomic used to keep track of whether there is work to do since sev() is not available on RISCV static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); #[export_name = "__pender"] - fn __thread_mode_pender(_context: PenderContext) { + fn __thread_mode_pender(_context: crate::raw::PenderContext) { SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst); } - /// TODO - // Name pending - #[derive(Default)] // Default enables Executor::new - pub struct Context; - - impl ThreadContext for Context { - fn context(&self) -> PenderContext { - unsafe { core::mem::transmute(0) } - } - - fn wait(&mut self) { - // We do not care about race conditions between the load and store operations, - // interrupts will only set this value to true. - critical_section::with(|_| { - // if there is work to do, loop back to polling - // TODO can we relax this? - if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { - SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); - } - // if not, wait for interrupt - else { - unsafe { - core::arch::asm!("wfi"); - } - } - }); - // if an interrupt occurred while waiting, it will be serviced here - } + /// RISCV32 Executor + pub struct Executor { + inner: raw::Executor, + not_send: PhantomData<*mut ()>, } - /// TODO - // Type alias for backwards compatibility - pub type Executor = crate::thread::ThreadModeExecutor; + impl Executor { + /// Create a new Executor. + pub fn new() -> Self { + Self { + inner: raw::Executor::new(unsafe { core::mem::transmute(0) }), + not_send: PhantomData, + } + } + + /// Run the executor. + /// + /// The `init` closure is called with a [`Spawner`] that spawns tasks on + /// this executor. Use it to spawn the initial task(s). After `init` returns, + /// the executor starts running the tasks. + /// + /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), + /// for example by passing it as an argument to the initial tasks. + /// + /// This function requires `&'static mut self`. This means you have to store the + /// Executor instance in a place where it'll live forever and grants you mutable + /// access. There's a few ways to do this: + /// + /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) + /// - a `static mut` (unsafe) + /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) + /// + /// This function never returns. + pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { + init(self.inner.spawner()); + + loop { + unsafe { + self.inner.poll(); + // we do not care about race conditions between the load and store operations, interrupts + //will only set this value to true. + critical_section::with(|_| { + // if there is work to do, loop back to polling + // TODO can we relax this? + if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { + SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); + } + // if not, wait for interrupt + else { + core::arch::asm!("wfi"); + } + }); + // if an interrupt occurred while waiting, it will be serviced here + } + } + } + } } diff --git a/embassy-executor/src/arch/std.rs b/embassy-executor/src/arch/std.rs index d55de118d..f490084d6 100644 --- a/embassy-executor/src/arch/std.rs +++ b/embassy-executor/src/arch/std.rs @@ -5,42 +5,64 @@ compile_error!("`executor-interrupt` is not supported with `arch-std`."); pub use thread::*; #[cfg(feature = "executor-thread")] mod thread { + use std::marker::PhantomData; use std::sync::{Condvar, Mutex}; #[cfg(feature = "nightly")] pub use embassy_macros::main_std as main; - use crate::raw::PenderContext; - use crate::thread::ThreadContext; + use crate::{raw, Spawner}; - /// TODO - // Name pending - pub struct Context { + #[export_name = "__pender"] + fn __pender(context: crate::raw::PenderContext) { + let signaler: &'static Signaler = unsafe { std::mem::transmute(context) }; + signaler.signal() + } + + /// Single-threaded std-based executor. + pub struct Executor { + inner: raw::Executor, + not_send: PhantomData<*mut ()>, signaler: &'static Signaler, } - impl Default for Context { - fn default() -> Self { + impl Executor { + /// Create a new Executor. + pub fn new() -> Self { + let signaler = &*Box::leak(Box::new(Signaler::new())); Self { - signaler: &*Box::leak(Box::new(Signaler::new())), + inner: raw::Executor::new(unsafe { std::mem::transmute(signaler) }), + not_send: PhantomData, + signaler, } } - } - impl ThreadContext for Context { - fn context(&self) -> PenderContext { - unsafe { core::mem::transmute(self.signaler) } + /// Run the executor. + /// + /// The `init` closure is called with a [`Spawner`] that spawns tasks on + /// this executor. Use it to spawn the initial task(s). After `init` returns, + /// the executor starts running the tasks. + /// + /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), + /// for example by passing it as an argument to the initial tasks. + /// + /// This function requires `&'static mut self`. This means you have to store the + /// Executor instance in a place where it'll live forever and grants you mutable + /// access. There's a few ways to do this: + /// + /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) + /// - a `static mut` (unsafe) + /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) + /// + /// This function never returns. + pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { + init(self.inner.spawner()); + + loop { + unsafe { self.inner.poll() }; + self.signaler.wait() + } } - - fn wait(&mut self) { - self.signaler.wait() - } - } - - #[export_name = "__pender"] - fn __pender(context: PenderContext) { - let signaler: &'static Signaler = unsafe { std::mem::transmute(context) }; - signaler.signal() } struct Signaler { @@ -70,8 +92,4 @@ mod thread { self.condvar.notify_one(); } } - - /// TODO - // Type alias for backwards compatibility - pub type Executor = crate::thread::ThreadModeExecutor; } diff --git a/embassy-executor/src/arch/xtensa.rs b/embassy-executor/src/arch/xtensa.rs index 1aea9f230..8665a9cb6 100644 --- a/embassy-executor/src/arch/xtensa.rs +++ b/embassy-executor/src/arch/xtensa.rs @@ -8,56 +8,80 @@ mod thread { use core::marker::PhantomData; use core::sync::atomic::{AtomicBool, Ordering}; - use crate::raw::PenderContext; - use crate::thread::ThreadContext; + use crate::{raw, Spawner}; /// global atomic used to keep track of whether there is work to do since sev() is not available on Xtensa static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); #[export_name = "__thread_mode_pender"] - fn __thread_mode_pender(_context: PenderContext) { + fn __thread_mode_pender(_context: crate::raw::PenderContext) { SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst); } - /// TODO - // Name pending - #[derive(Default)] // Default enables Executor::new - pub struct Context; + /// Xtensa Executor + pub struct Executor { + inner: raw::Executor, + not_send: PhantomData<*mut ()>, + } - impl ThreadContext for Context { - fn context(&self) -> PenderContext { - unsafe { core::mem::transmute(0) } + impl Executor { + /// Create a new Executor. + pub fn new() -> Self { + Self { + inner: raw::Executor::new(unsafe { core::mem::transmute(0) }), + not_send: PhantomData, + } } - fn wait(&mut self) { - unsafe { - // Manual critical section implementation that only masks interrupts handlers. - // We must not acquire the cross-core on dual-core systems because that would - // prevent the other core from doing useful work while this core is sleeping. - let token: critical_section::RawRestoreState; - core::arch::asm!("rsil {0}, 5", out(reg) token); + /// Run the executor. + /// + /// The `init` closure is called with a [`Spawner`] that spawns tasks on + /// this executor. Use it to spawn the initial task(s). After `init` returns, + /// the executor starts running the tasks. + /// + /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), + /// for example by passing it as an argument to the initial tasks. + /// + /// This function requires `&'static mut self`. This means you have to store the + /// Executor instance in a place where it'll live forever and grants you mutable + /// access. There's a few ways to do this: + /// + /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) + /// - a `static mut` (unsafe) + /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) + /// + /// This function never returns. + pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { + init(self.inner.spawner()); - // we do not care about race conditions between the load and store operations, - // interrupts will only set this value to true. - // if there is work to do, loop back to polling - if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { - SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); + loop { + unsafe { + self.inner.poll(); - core::arch::asm!( - "wsr.ps {0}", - "rsync", in(reg) token) - } else { - // waiti sets the PS.INTLEVEL when slipping into sleep - // because critical sections in Xtensa are implemented via increasing - // PS.INTLEVEL the critical section ends here - // take care not add code after `waiti` if it needs to be inside the CS - core::arch::asm!("waiti 0"); // critical section ends here + // Manual critical section implementation that only masks interrupts handlers. + // We must not acquire the cross-core on dual-core systems because that would + // prevent the other core from doing useful work while this core is sleeping. + let token: critical_section::RawRestoreState; + core::arch::asm!("rsil {0}, 5", out(reg) token); + + // we do not care about race conditions between the load and store operations, interrupts + // will only set this value to true. + // if there is work to do, loop back to polling + if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { + SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); + + core::arch::asm!( + "wsr.ps {0}", + "rsync", in(reg) token) + } else { + // waiti sets the PS.INTLEVEL when slipping into sleep + // because critical sections in Xtensa are implemented via increasing + // PS.INTLEVEL the critical section ends here + // take care not add code after `waiti` if it needs to be inside the CS + core::arch::asm!("waiti 0"); // critical section ends here + } } } } } - - /// TODO - // Type alias for backwards compatibility - pub type Executor = crate::thread::ThreadModeExecutor; } diff --git a/embassy-executor/src/interrupt.rs b/embassy-executor/src/interrupt.rs index 28a1cd525..b68754ab2 100644 --- a/embassy-executor/src/interrupt.rs +++ b/embassy-executor/src/interrupt.rs @@ -41,7 +41,7 @@ pub trait InterruptContext { /// If this is not the case, you may use an interrupt from any unused peripheral. /// /// It is somewhat more complex to use, it's recommended to use the -/// [`crate::thread::ThreadModeExecutor`] instead, if it works for your use case. +/// thread-mode executor instead, if it works for your use case. pub struct InterruptModeExecutor { started: AtomicBool, executor: UnsafeCell>, diff --git a/embassy-executor/src/lib.rs b/embassy-executor/src/lib.rs index ca67c9484..3be32d9c5 100644 --- a/embassy-executor/src/lib.rs +++ b/embassy-executor/src/lib.rs @@ -39,8 +39,8 @@ pub mod raw; #[cfg(feature = "executor-interrupt")] pub mod interrupt; -#[cfg(feature = "executor-thread")] -pub mod thread; +#[cfg(feature = "executor-interrupt")] +pub use interrupt::*; mod spawner; pub use spawner::*; diff --git a/embassy-executor/src/thread.rs b/embassy-executor/src/thread.rs deleted file mode 100644 index 8ff4071da..000000000 --- a/embassy-executor/src/thread.rs +++ /dev/null @@ -1,87 +0,0 @@ -//! Thread-mode executor. - -use core::marker::PhantomData; - -use crate::raw::{self, PenderContext}; -use crate::Spawner; - -/// Architecture-specific interface for a thread-mode executor. This trait describes what the -/// executor should do when idle, and what data should be passed to its pender. -// TODO: Name pending -pub trait ThreadContext: Sized { - /// A pointer-sized piece of data that is passed to the pender function. - /// - /// For example, on multi-core systems, this can be used to store the ID of the core that - /// should be woken up. - fn context(&self) -> PenderContext; - - /// Waits for the executor to be waken. - /// - /// While it is valid for this function can be empty, it is recommended to use a WFE instruction - /// or equivalent to let the CPU sleep. - fn wait(&mut self); -} - -/// Thread mode executor, using WFE/SEV. -/// -/// This is the simplest and most common kind of executor. It runs on -/// thread mode (at the lowest priority level), and uses the `WFE` ARM instruction -/// to sleep when it has no more work to do. When a task is woken, a `SEV` instruction -/// is executed, to make the `WFE` exit from sleep and poll the task. -/// -/// This executor allows for ultra low power consumption for chips where `WFE` -/// triggers low-power sleep without extra steps. If your chip requires extra steps, -/// you may use [`raw::Executor`] directly to program custom behavior. -pub struct ThreadModeExecutor { - inner: raw::Executor, - context: C, - not_send: PhantomData<*mut ()>, -} - -impl ThreadModeExecutor { - /// Create a new Executor. - pub fn new() -> Self - where - C: Default, - { - Self::with_context(C::default()) - } - - /// Create a new Executor using the given thread context. - pub fn with_context(context: C) -> Self { - Self { - inner: raw::Executor::new(context.context()), - context, - not_send: PhantomData, - } - } - - /// Run the executor. - /// - /// The `init` closure is called with a [`Spawner`] that spawns tasks on - /// this executor. Use it to spawn the initial task(s). After `init` returns, - /// the executor starts running the tasks. - /// - /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), - /// for example by passing it as an argument to the initial tasks. - /// - /// This function requires `&'static mut self`. This means you have to store the - /// Executor instance in a place where it'll live forever and grants you mutable - /// access. There's a few ways to do this: - /// - /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) - /// - a `static mut` (unsafe) - /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) - /// - /// This function never returns. - pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { - init(self.inner.spawner()); - - loop { - unsafe { - self.inner.poll(); - self.context.wait(); - }; - } - } -} From 3a51e2d9cae6fad2fd903c07634b4a66de59b3bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 14 Aug 2023 15:45:43 +0200 Subject: [PATCH 25/77] Make PenderContext actually pointer-size --- embassy-executor/src/raw/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 4a6e45535..2bbbb132c 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -294,7 +294,7 @@ impl TaskPool { /// Context given to the thread-mode executor's pender. #[repr(transparent)] #[derive(Clone, Copy)] -pub struct PenderContext(usize); +pub struct PenderContext(*mut ()); /// Platform/architecture-specific action executed when an executor has pending work. /// From 995434614384bc5c218a16a026ce7c06737ca860 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 14 Aug 2023 15:59:47 +0200 Subject: [PATCH 26/77] Remove interrupt executor, remove PenderContext --- embassy-executor/src/arch/cortex_m.rs | 9 +- embassy-executor/src/arch/riscv32.rs | 4 +- embassy-executor/src/arch/std.rs | 6 +- embassy-executor/src/arch/wasm.rs | 7 +- embassy-executor/src/arch/xtensa.rs | 4 +- embassy-executor/src/interrupt.rs | 131 -------------------------- embassy-executor/src/lib.rs | 5 - embassy-executor/src/raw/mod.rs | 17 +--- 8 files changed, 19 insertions(+), 164 deletions(-) delete mode 100644 embassy-executor/src/interrupt.rs diff --git a/embassy-executor/src/arch/cortex_m.rs b/embassy-executor/src/arch/cortex_m.rs index 2ed70dd1e..8fe5644d7 100644 --- a/embassy-executor/src/arch/cortex_m.rs +++ b/embassy-executor/src/arch/cortex_m.rs @@ -2,12 +2,12 @@ const THREAD_PENDER: usize = usize::MAX; #[export_name = "__pender"] #[cfg(any(feature = "executor-thread", feature = "executor-interrupt"))] -fn __pender(context: crate::raw::PenderContext) { +fn __pender(context: *mut ()) { unsafe { // Safety: `context` is either `usize::MAX` created by `Executor::run`, or a valid interrupt // request number given to `InterruptExecutor::start`. - let context: usize = core::mem::transmute(context); + let context = context as usize; #[cfg(feature = "executor-thread")] if context == THREAD_PENDER { @@ -75,7 +75,7 @@ mod thread { /// Create a new Executor. pub fn new() -> Self { Self { - inner: raw::Executor::new(unsafe { core::mem::transmute(THREAD_PENDER) }), + inner: raw::Executor::new(THREAD_PENDER as *mut ()), not_send: PhantomData, } } @@ -205,10 +205,9 @@ mod interrupt { } unsafe { - let context = core::mem::transmute(irq.number() as usize); (&mut *self.executor.get()) .as_mut_ptr() - .write(raw::Executor::new(context)) + .write(raw::Executor::new(irq.number() as *mut ())) } let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; diff --git a/embassy-executor/src/arch/riscv32.rs b/embassy-executor/src/arch/riscv32.rs index 551d7527f..ce78bc258 100644 --- a/embassy-executor/src/arch/riscv32.rs +++ b/embassy-executor/src/arch/riscv32.rs @@ -17,7 +17,7 @@ mod thread { static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); #[export_name = "__pender"] - fn __thread_mode_pender(_context: crate::raw::PenderContext) { + fn __thread_mode_pender(_context: *mut ()) { SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst); } @@ -31,7 +31,7 @@ mod thread { /// Create a new Executor. pub fn new() -> Self { Self { - inner: raw::Executor::new(unsafe { core::mem::transmute(0) }), + inner: raw::Executor::new(core::ptr::null_mut()), not_send: PhantomData, } } diff --git a/embassy-executor/src/arch/std.rs b/embassy-executor/src/arch/std.rs index f490084d6..5b2f7e2e4 100644 --- a/embassy-executor/src/arch/std.rs +++ b/embassy-executor/src/arch/std.rs @@ -14,7 +14,7 @@ mod thread { use crate::{raw, Spawner}; #[export_name = "__pender"] - fn __pender(context: crate::raw::PenderContext) { + fn __pender(context: *mut ()) { let signaler: &'static Signaler = unsafe { std::mem::transmute(context) }; signaler.signal() } @@ -29,9 +29,9 @@ mod thread { impl Executor { /// Create a new Executor. pub fn new() -> Self { - let signaler = &*Box::leak(Box::new(Signaler::new())); + let signaler = Box::leak(Box::new(Signaler::new())); Self { - inner: raw::Executor::new(unsafe { std::mem::transmute(signaler) }), + inner: raw::Executor::new(signaler as *mut Signaler as *mut ()), not_send: PhantomData, signaler, } diff --git a/embassy-executor/src/arch/wasm.rs b/embassy-executor/src/arch/wasm.rs index 452c3e394..5f9b2e705 100644 --- a/embassy-executor/src/arch/wasm.rs +++ b/embassy-executor/src/arch/wasm.rs @@ -14,11 +14,10 @@ mod thread { use wasm_bindgen::prelude::*; use crate::raw::util::UninitCell; - use crate::raw::PenderContext; use crate::{raw, Spawner}; #[export_name = "__thread_mode_pender"] - fn __thread_mode_pender(context: PenderContext) { + fn __thread_mode_pender(context: *mut ()) { let signaler: &'static WasmContext = unsafe { std::mem::transmute(context) }; let _ = signaler.promise.then(unsafe { signaler.closure.as_mut() }); } @@ -47,9 +46,9 @@ mod thread { impl Executor { /// Create a new Executor. pub fn new() -> Self { - let ctx = &*Box::leak(Box::new(WasmContext::new())); + let ctx = Box::leak(Box::new(WasmContext::new())); Self { - inner: raw::Executor::new(unsafe { core::mem::transmute(ctx) }), + inner: raw::Executor::new(ctx as *mut WasmContext as *mut ()), ctx, not_send: PhantomData, } diff --git a/embassy-executor/src/arch/xtensa.rs b/embassy-executor/src/arch/xtensa.rs index 8665a9cb6..66b3351c5 100644 --- a/embassy-executor/src/arch/xtensa.rs +++ b/embassy-executor/src/arch/xtensa.rs @@ -14,7 +14,7 @@ mod thread { static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); #[export_name = "__thread_mode_pender"] - fn __thread_mode_pender(_context: crate::raw::PenderContext) { + fn __thread_mode_pender(_context: *mut ()) { SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst); } @@ -28,7 +28,7 @@ mod thread { /// Create a new Executor. pub fn new() -> Self { Self { - inner: raw::Executor::new(unsafe { core::mem::transmute(0) }), + inner: raw::Executor::new(core::ptr::null_mut()), not_send: PhantomData, } } diff --git a/embassy-executor/src/interrupt.rs b/embassy-executor/src/interrupt.rs deleted file mode 100644 index b68754ab2..000000000 --- a/embassy-executor/src/interrupt.rs +++ /dev/null @@ -1,131 +0,0 @@ -//! Interrupt-mode executor. - -use core::cell::UnsafeCell; -use core::mem::MaybeUninit; - -use atomic_polyfill::{AtomicBool, Ordering}; - -use crate::raw::{self, PenderContext}; - -/// Architecture-specific interface for an interrupt-mode executor. This trait describes what data -/// should be passed to the [`InterruptExecutor`]'s pender, and how to enable the interrupt that -/// triggers polling the executor. -// TODO: Name pending -pub trait InterruptContext { - /// A pointer-sized piece of data that is passed to the pender function. - /// - /// Usually, the context contains the interrupt that should be used to wake the executor. - fn context(&self) -> PenderContext; - - /// Enabled the interrupt request. - fn enable(&self); -} - -/// Interrupt mode executor. -/// -/// This executor runs tasks in interrupt mode. The interrupt handler is set up -/// to poll tasks, and when a task is woken the interrupt is pended from software. -/// -/// This allows running async tasks at a priority higher than thread mode. One -/// use case is to leave thread mode free for non-async tasks. Another use case is -/// to run multiple executors: one in thread mode for low priority tasks and another in -/// interrupt mode for higher priority tasks. Higher priority tasks will preempt lower -/// priority ones. -/// -/// It is even possible to run multiple interrupt mode executors at different priorities, -/// by assigning different priorities to the interrupts. For an example on how to do this, -/// See the 'multiprio' example for 'embassy-nrf'. -/// -/// To use it, you have to pick an interrupt that won't be used by the hardware. -/// Some chips reserve some interrupts for this purpose, sometimes named "software interrupts" (SWI). -/// If this is not the case, you may use an interrupt from any unused peripheral. -/// -/// It is somewhat more complex to use, it's recommended to use the -/// thread-mode executor instead, if it works for your use case. -pub struct InterruptModeExecutor { - started: AtomicBool, - executor: UnsafeCell>, -} - -unsafe impl Send for InterruptModeExecutor {} -unsafe impl Sync for InterruptModeExecutor {} - -impl InterruptModeExecutor { - /// Create a new, not started `InterruptExecutor`. - #[inline] - pub const fn new() -> Self { - Self { - started: AtomicBool::new(false), - executor: UnsafeCell::new(MaybeUninit::uninit()), - } - } - - /// Executor interrupt callback. - /// - /// # Safety - /// - /// You MUST call this from the interrupt handler, and from nowhere else. - pub unsafe fn on_interrupt(&'static self) { - let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; - executor.poll(); - } - - /// Start the executor. - /// - /// This initializes the executor, enables the interrupt, and returns. - /// The executor keeps running in the background through the interrupt. - /// - /// This returns a [`SendSpawner`] you can use to spawn tasks on it. A [`SendSpawner`] - /// is returned instead of a [`Spawner`](embassy_executor::Spawner) because the executor effectively runs in a - /// different "thread" (the interrupt), so spawning tasks on it is effectively - /// sending them. - /// - /// To obtain a [`Spawner`](embassy_executor::Spawner) for this executor, use [`Spawner::for_current_executor()`](embassy_executor::Spawner::for_current_executor()) from - /// a task running in it. - /// - /// # Interrupt requirements - /// - /// You must write the interrupt handler yourself, and make it call [`on_interrupt()`](Self::on_interrupt). - /// - /// This method already enables (unmasks) the interrupt, you must NOT do it yourself. - /// - /// You must set the interrupt priority before calling this method. You MUST NOT - /// do it after. - /// - pub fn start(&'static self, irq: impl InterruptContext) -> crate::SendSpawner { - if self - .started - .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) - .is_err() - { - panic!("InterruptExecutor::start() called multiple times on the same executor."); - } - - unsafe { - (&mut *self.executor.get()) - .as_mut_ptr() - .write(raw::Executor::new(irq.context())) - } - - let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; - - irq.enable(); - - executor.spawner().make_send() - } - - /// Get a SendSpawner for this executor - /// - /// This returns a [`SendSpawner`] you can use to spawn tasks on this - /// executor. - /// - /// This MUST only be called on an executor that has already been spawned. - /// The function will panic otherwise. - pub fn spawner(&'static self) -> crate::SendSpawner { - if !self.started.load(Ordering::Acquire) { - panic!("InterruptExecutor::spawner() called on uninitialized executor."); - } - let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; - executor.spawner().make_send() - } -} diff --git a/embassy-executor/src/lib.rs b/embassy-executor/src/lib.rs index 3be32d9c5..3ce687eb6 100644 --- a/embassy-executor/src/lib.rs +++ b/embassy-executor/src/lib.rs @@ -37,11 +37,6 @@ pub use arch::*; pub mod raw; -#[cfg(feature = "executor-interrupt")] -pub mod interrupt; -#[cfg(feature = "executor-interrupt")] -pub use interrupt::*; - mod spawner; pub use spawner::*; diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 2bbbb132c..aa99b4cf3 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -291,11 +291,6 @@ impl TaskPool { } } -/// Context given to the thread-mode executor's pender. -#[repr(transparent)] -#[derive(Clone, Copy)] -pub struct PenderContext(*mut ()); - /// Platform/architecture-specific action executed when an executor has pending work. /// /// When a task within an executor is woken, the `Pender` is called. This does a @@ -306,15 +301,13 @@ pub struct PenderContext(*mut ()); /// /// Platform/architecture implementations must provide a function that can be referred to as: /// -/// ```rust -/// use embassy_executor::raw::PenderContext; -/// +/// ```rust/// /// extern "Rust" { -/// fn __pender(context: PenderContext); +/// fn __pender(context: *mut ()); /// } /// ``` #[derive(Clone, Copy)] -pub struct Pender(PenderContext); +pub struct Pender(*mut ()); unsafe impl Send for Pender {} unsafe impl Sync for Pender {} @@ -322,7 +315,7 @@ unsafe impl Sync for Pender {} impl Pender { pub(crate) fn pend(self) { extern "Rust" { - fn __pender(context: PenderContext); + fn __pender(context: *mut ()); } unsafe { __pender(self.0) }; } @@ -484,7 +477,7 @@ impl Executor { /// When the executor has work to do, it will call the [`Pender`]. /// /// See [`Executor`] docs for details on `Pender`. - pub fn new(context: PenderContext) -> Self { + pub fn new(context: *mut ()) -> Self { Self { inner: SyncExecutor::new(Pender(context)), _not_sync: PhantomData, From fbec797d6403348f5c8c306c1d9a7c396b063f10 Mon Sep 17 00:00:00 2001 From: Scott Mabin Date: Sun, 13 Aug 2023 23:39:06 +0100 Subject: [PATCH 27/77] embassy-net:tcp:send/recv - Add async versions of smoltcp's `send` and `recv` closure based API. --- embassy-net/src/tcp.rs | 102 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index c903fb245..b4ce40945 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -82,6 +82,17 @@ impl<'a> TcpReader<'a> { pub async fn read(&mut self, buf: &mut [u8]) -> Result { self.io.read(buf).await } + + /// Call `f` with the largest contiguous slice of octets in the receive buffer, + /// and dequeue the amount of elements returned by `f`. + /// + /// If no data is available, it waits until there is at least one byte available. + pub async fn read_with(&mut self, f: F) -> Result + where + F: FnOnce(&mut [u8]) -> (usize, R), + { + self.io.read_with(f).await + } } impl<'a> TcpWriter<'a> { @@ -100,6 +111,17 @@ impl<'a> TcpWriter<'a> { pub async fn flush(&mut self) -> Result<(), Error> { self.io.flush().await } + + /// Call `f` with the largest contiguous slice of octets in the transmit buffer, + /// and enqueue the amount of elements returned by `f`. + /// + /// If the socket is not ready to accept data, it waits until it is. + pub async fn write_with(&mut self, f: F) -> Result + where + F: FnOnce(&mut [u8]) -> (usize, R), + { + self.io.write_with(f).await + } } impl<'a> TcpSocket<'a> { @@ -121,6 +143,28 @@ impl<'a> TcpSocket<'a> { } } + /// Call `f` with the largest contiguous slice of octets in the transmit buffer, + /// and enqueue the amount of elements returned by `f`. + /// + /// If the socket is not ready to accept data, it waits until it is. + pub async fn write_with(&mut self, f: F) -> Result + where + F: FnOnce(&mut [u8]) -> (usize, R), + { + self.io.write_with(f).await + } + + /// Call `f` with the largest contiguous slice of octets in the receive buffer, + /// and dequeue the amount of elements returned by `f`. + /// + /// If no data is available, it waits until there is at least one byte available. + pub async fn read_with(&mut self, f: F) -> Result + where + F: FnOnce(&mut [u8]) -> (usize, R), + { + self.io.read_with(f).await + } + /// Split the socket into reader and a writer halves. pub fn split(&mut self) -> (TcpReader<'_>, TcpWriter<'_>) { (TcpReader { io: self.io }, TcpWriter { io: self.io }) @@ -359,6 +403,64 @@ impl<'d> TcpIo<'d> { .await } + async fn write_with(&mut self, f: F) -> Result + where + F: FnOnce(&mut [u8]) -> (usize, R), + { + let mut f = Some(f); + poll_fn(move |cx| { + self.with_mut(|s, _| { + if !s.can_send() { + if s.may_send() { + // socket buffer is full wait until it has atleast one byte free + s.register_send_waker(cx.waker()); + Poll::Pending + } else { + // if we can't transmit because the transmit half of the duplex connection is closed then return an error + Poll::Ready(Err(Error::ConnectionReset)) + } + } else { + Poll::Ready(match s.send(f.take().unwrap()) { + // Connection reset. TODO: this can also be timeouts etc, investigate. + Err(tcp::SendError::InvalidState) => Err(Error::ConnectionReset), + Ok(r) => Ok(r), + }) + } + }) + }) + .await + } + + async fn read_with(&mut self, f: F) -> Result + where + F: FnOnce(&mut [u8]) -> (usize, R), + { + let mut f = Some(f); + poll_fn(move |cx| { + self.with_mut(|s, _| { + if !s.can_recv() { + if s.may_recv() { + // socket buffer is empty wait until it has atleast one byte has arrived + s.register_recv_waker(cx.waker()); + Poll::Pending + } else { + // if we can't receive because the recieve half of the duplex connection is closed then return an error + Poll::Ready(Err(Error::ConnectionReset)) + } + } else { + Poll::Ready(match s.recv(f.take().unwrap()) { + // Connection reset. TODO: this can also be timeouts etc, investigate. + Err(tcp::RecvError::Finished) | Err(tcp::RecvError::InvalidState) => { + Err(Error::ConnectionReset) + } + Ok(r) => Ok(r), + }) + } + }) + }) + .await + } + async fn flush(&mut self) -> Result<(), Error> { poll_fn(move |cx| { self.with_mut(|s, _| { From da4f15d94492667dd33087f95e28747b74b07811 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 14 Aug 2023 16:33:49 +0200 Subject: [PATCH 28/77] Fix fn name --- embassy-executor/src/arch/riscv32.rs | 2 +- embassy-executor/src/arch/wasm.rs | 4 ++-- embassy-executor/src/arch/xtensa.rs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/embassy-executor/src/arch/riscv32.rs b/embassy-executor/src/arch/riscv32.rs index ce78bc258..40c6877e2 100644 --- a/embassy-executor/src/arch/riscv32.rs +++ b/embassy-executor/src/arch/riscv32.rs @@ -17,7 +17,7 @@ mod thread { static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); #[export_name = "__pender"] - fn __thread_mode_pender(_context: *mut ()) { + fn __pender(_context: *mut ()) { SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst); } diff --git a/embassy-executor/src/arch/wasm.rs b/embassy-executor/src/arch/wasm.rs index 5f9b2e705..934fd69e5 100644 --- a/embassy-executor/src/arch/wasm.rs +++ b/embassy-executor/src/arch/wasm.rs @@ -16,8 +16,8 @@ mod thread { use crate::raw::util::UninitCell; use crate::{raw, Spawner}; - #[export_name = "__thread_mode_pender"] - fn __thread_mode_pender(context: *mut ()) { + #[export_name = "__pender"] + fn __pender(context: *mut ()) { let signaler: &'static WasmContext = unsafe { std::mem::transmute(context) }; let _ = signaler.promise.then(unsafe { signaler.closure.as_mut() }); } diff --git a/embassy-executor/src/arch/xtensa.rs b/embassy-executor/src/arch/xtensa.rs index 66b3351c5..601d85002 100644 --- a/embassy-executor/src/arch/xtensa.rs +++ b/embassy-executor/src/arch/xtensa.rs @@ -13,8 +13,8 @@ mod thread { /// global atomic used to keep track of whether there is work to do since sev() is not available on Xtensa static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); - #[export_name = "__thread_mode_pender"] - fn __thread_mode_pender(_context: *mut ()) { + #[export_name = "__pender"] + fn __pender(_context: *mut ()) { SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst); } From 07c36001271ab0a033a08a6535719729efb677c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 14 Aug 2023 16:35:12 +0200 Subject: [PATCH 29/77] Hide Pender --- embassy-executor/src/raw/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index aa99b4cf3..064831026 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -301,13 +301,13 @@ impl TaskPool { /// /// Platform/architecture implementations must provide a function that can be referred to as: /// -/// ```rust/// +/// ```rust /// extern "Rust" { /// fn __pender(context: *mut ()); /// } /// ``` #[derive(Clone, Copy)] -pub struct Pender(*mut ()); +pub(crate) struct Pender(*mut ()); unsafe impl Send for Pender {} unsafe impl Sync for Pender {} From e4f3979ec8dd12fef1f44e78733917980045def0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 14 Aug 2023 16:46:02 +0200 Subject: [PATCH 30/77] Don't check context if only thread-mode is enabled --- embassy-executor/src/arch/cortex_m.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/embassy-executor/src/arch/cortex_m.rs b/embassy-executor/src/arch/cortex_m.rs index 8fe5644d7..0806a22ab 100644 --- a/embassy-executor/src/arch/cortex_m.rs +++ b/embassy-executor/src/arch/cortex_m.rs @@ -10,7 +10,8 @@ fn __pender(context: *mut ()) { let context = context as usize; #[cfg(feature = "executor-thread")] - if context == THREAD_PENDER { + // Try to make Rust optimize the branching away if we only use thread mode. + if !cfg!(feature = "executor-interrupt") || context == THREAD_PENDER { core::arch::asm!("sev"); return; } From 890f29ccfe129f3205cf835c7131862c579d9349 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 14 Aug 2023 17:53:42 +0200 Subject: [PATCH 31/77] Update docs --- embassy-executor/CHANGELOG.md | 4 +++ embassy-executor/src/raw/mod.rs | 49 +++++++++++++++++---------------- 2 files changed, 29 insertions(+), 24 deletions(-) diff --git a/embassy-executor/CHANGELOG.md b/embassy-executor/CHANGELOG.md index 86a477589..e2e7bce3a 100644 --- a/embassy-executor/CHANGELOG.md +++ b/embassy-executor/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +- Replaced Pender. Implementations now must define an extern function called `__pender`. + ## 0.2.1 - 2023-08-10 - Avoid calling `pend()` when waking expired timers diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 064831026..7caa3302f 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -291,21 +291,6 @@ impl TaskPool { } } -/// Platform/architecture-specific action executed when an executor has pending work. -/// -/// When a task within an executor is woken, the `Pender` is called. This does a -/// platform/architecture-specific action to signal there is pending work in the executor. -/// When this happens, you must arrange for [`Executor::poll`] to be called. -/// -/// You can think of it as a waker, but for the whole executor. -/// -/// Platform/architecture implementations must provide a function that can be referred to as: -/// -/// ```rust -/// extern "Rust" { -/// fn __pender(context: *mut ()); -/// } -/// ``` #[derive(Clone, Copy)] pub(crate) struct Pender(*mut ()); @@ -451,15 +436,31 @@ impl SyncExecutor { /// /// - To get the executor to do work, call `poll()`. This will poll all queued tasks (all tasks /// that "want to run"). -/// - You must supply a [`Pender`]. The executor will call it to notify you it has work -/// to do. You must arrange for `poll()` to be called as soon as possible. +/// - You must supply a pender function, as shown below. The executor will call it to notify you +/// it has work to do. You must arrange for `poll()` to be called as soon as possible. +/// - Enabling `arch-xx` features will define a pender function for you. This means that you +/// are limited to using the executors provided to you by the architecture/platform +/// implementation. If you need a different executor, you must not enable `arch-xx` features. /// -/// The [`Pender`] can be called from *any* context: any thread, any interrupt priority +/// The pender can be called from *any* context: any thread, any interrupt priority /// level, etc. It may be called synchronously from any `Executor` method call as well. /// You must deal with this correctly. /// /// In particular, you must NOT call `poll` directly from the pender callback, as this violates /// the requirement for `poll` to not be called reentrantly. +/// +/// The pender function must be exported with the name `__pender` and have the following signature: +/// +/// ```rust +/// #[export_name = "__pender"] +/// fn pender(context: *mut ()) { +/// // schedule `poll()` to be called +/// } +/// ``` +/// +/// The `context` argument is a piece of arbitrary data the executor will pass to the pender. +/// You can set the `context` when calling [`Executor::new()`]. You can use it to, for example, +/// differentiate between executors, or to pass a pointer to a callback that should be called. #[repr(transparent)] pub struct Executor { pub(crate) inner: SyncExecutor, @@ -474,9 +475,9 @@ impl Executor { /// Create a new executor. /// - /// When the executor has work to do, it will call the [`Pender`]. + /// When the executor has work to do, it will call the pender function and pass `context` to it. /// - /// See [`Executor`] docs for details on `Pender`. + /// See [`Executor`] docs for details on the pender. pub fn new(context: *mut ()) -> Self { Self { inner: SyncExecutor::new(Pender(context)), @@ -502,16 +503,16 @@ impl Executor { /// This loops over all tasks that are queued to be polled (i.e. they're /// freshly spawned or they've been woken). Other tasks are not polled. /// - /// You must call `poll` after receiving a call to the [`Pender`]. It is OK - /// to call `poll` even when not requested by the `Pender`, but it wastes + /// You must call `poll` after receiving a call to the pender. It is OK + /// to call `poll` even when not requested by the pender, but it wastes /// energy. /// /// # Safety /// /// You must NOT call `poll` reentrantly on the same executor. /// - /// In particular, note that `poll` may call the `Pender` synchronously. Therefore, you - /// must NOT directly call `poll()` from the `Pender` callback. Instead, the callback has to + /// In particular, note that `poll` may call the pender synchronously. Therefore, you + /// must NOT directly call `poll()` from the pender callback. Instead, the callback has to /// somehow schedule for `poll()` to be called later, at a time you know for sure there's /// no `poll()` already running. pub unsafe fn poll(&'static self) { From 0fd9d7400b47f42d1043347cc2982862de7cffc7 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 15 Aug 2023 00:18:23 +0200 Subject: [PATCH 32/77] Build stm32 docs last. --- .github/ci/doc.sh | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/ci/doc.sh b/.github/ci/doc.sh index 06c6fa00b..e410ffa35 100755 --- a/.github/ci/doc.sh +++ b/.github/ci/doc.sh @@ -15,7 +15,6 @@ export BUILDER_COMPRESS=true # which makes rustup very sad rustc --version > /dev/null -docserver-builder -i ./embassy-stm32 -o webroot/crates/embassy-stm32/git.zup docserver-builder -i ./embassy-boot/boot -o webroot/crates/embassy-boot/git.zup docserver-builder -i ./embassy-boot/nrf -o webroot/crates/embassy-boot-nrf/git.zup docserver-builder -i ./embassy-boot/rp -o webroot/crates/embassy-boot-rp/git.zup @@ -43,4 +42,12 @@ docserver-builder -i ./embassy-stm32-wpan -o webroot/crates/embassy-stm32-wpan/g export KUBECONFIG=/ci/secrets/kubeconfig.yml POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name}) kubectl cp webroot/crates $POD:/data -kubectl cp webroot/static $POD:/data \ No newline at end of file +kubectl cp webroot/static $POD:/data + +# build and upload stm32 last +# so that it doesn't prevent other crates from getting docs updates when it breaks. +rm -rf webroot +docserver-builder -i ./embassy-stm32 -o webroot/crates/embassy-stm32/git.zup + +POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name}) +kubectl cp webroot/crates $POD:/data From 2c1402843ae8d4f07db3670bd7153e794a1553ae Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 5 Aug 2023 00:10:23 +0200 Subject: [PATCH 33/77] wip: enc28j60 driver. --- embassy-net-enc28j60/Cargo.toml | 23 + embassy-net-enc28j60/README.md | 1 + embassy-net-enc28j60/src/bank0.rs | 69 +++ embassy-net-enc28j60/src/bank1.rs | 84 ++++ embassy-net-enc28j60/src/bank2.rs | 86 ++++ embassy-net-enc28j60/src/bank3.rs | 53 +++ embassy-net-enc28j60/src/common.rs | 106 +++++ embassy-net-enc28j60/src/fmt.rs | 225 ++++++++++ embassy-net-enc28j60/src/header.rs | 30 ++ embassy-net-enc28j60/src/lib.rs | 693 +++++++++++++++++++++++++++++ embassy-net-enc28j60/src/macros.rs | 89 ++++ embassy-net-enc28j60/src/phy.rs | 36 ++ embassy-net-enc28j60/src/traits.rs | 57 +++ 13 files changed, 1552 insertions(+) create mode 100644 embassy-net-enc28j60/Cargo.toml create mode 100644 embassy-net-enc28j60/README.md create mode 100644 embassy-net-enc28j60/src/bank0.rs create mode 100644 embassy-net-enc28j60/src/bank1.rs create mode 100644 embassy-net-enc28j60/src/bank2.rs create mode 100644 embassy-net-enc28j60/src/bank3.rs create mode 100644 embassy-net-enc28j60/src/common.rs create mode 100644 embassy-net-enc28j60/src/fmt.rs create mode 100644 embassy-net-enc28j60/src/header.rs create mode 100644 embassy-net-enc28j60/src/lib.rs create mode 100644 embassy-net-enc28j60/src/macros.rs create mode 100644 embassy-net-enc28j60/src/phy.rs create mode 100644 embassy-net-enc28j60/src/traits.rs diff --git a/embassy-net-enc28j60/Cargo.toml b/embassy-net-enc28j60/Cargo.toml new file mode 100644 index 000000000..c502ed04b --- /dev/null +++ b/embassy-net-enc28j60/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "embassy-net-enc28j60" +version = "0.1.0" +description = "embassy-net driver for the ENC28J60 ethernet chip" +keywords = ["embedded", "enc28j60", "embassy-net", "embedded-hal-async", "ethernet", "async"] +categories = ["embedded", "hardware-support", "no-std", "network-programming", "async"] +license = "MIT OR Apache-2.0" +edition = "2021" + +[dependencies] +embedded-hal = { version = "1.0.0-alpha.11" } +embedded-hal-async = { version = "=0.2.0-alpha.2" } +embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } +embassy-time = { version = "0.1.2", path = "../embassy-time" } +embassy-futures = { version = "0.1.0", path = "../embassy-futures" } + +defmt = { version = "0.3", optional = true } +log = { version = "0.4.14", optional = true } + +[package.metadata.embassy_docs] +src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-enc28j60-v$VERSION/embassy-net-enc28j60/src/" +src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-enc28j60/src/" +target = "thumbv7em-none-eabi" \ No newline at end of file diff --git a/embassy-net-enc28j60/README.md b/embassy-net-enc28j60/README.md new file mode 100644 index 000000000..e12d240c3 --- /dev/null +++ b/embassy-net-enc28j60/README.md @@ -0,0 +1 @@ +# `embassy-net-enc28j60` diff --git a/embassy-net-enc28j60/src/bank0.rs b/embassy-net-enc28j60/src/bank0.rs new file mode 100644 index 000000000..1c1b3a7f6 --- /dev/null +++ b/embassy-net-enc28j60/src/bank0.rs @@ -0,0 +1,69 @@ +#[allow(dead_code)] +#[derive(Clone, Copy)] +pub enum Register { + ERDPTL = 0x00, + ERDPTH = 0x01, + EWRPTL = 0x02, + EWRPTH = 0x03, + ETXSTL = 0x04, + ETXSTH = 0x05, + ETXNDL = 0x06, + ETXNDH = 0x07, + ERXSTL = 0x08, + ERXSTH = 0x09, + ERXNDL = 0x0a, + ERXNDH = 0x0b, + ERXRDPTL = 0x0c, + ERXRDPTH = 0x0d, + ERXWRPTL = 0x0e, + ERXWRPTH = 0x0f, + EDMASTL = 0x10, + EDMASTH = 0x11, + EDMANDL = 0x12, + EDMANDH = 0x13, + EDMADSTL = 0x14, + EDMADSTH = 0x15, + EDMACSL = 0x16, + EDMACSH = 0x17, +} + +impl Register { + pub(crate) fn addr(&self) -> u8 { + *self as u8 + } + + pub(crate) fn is_eth_register(&self) -> bool { + match *self { + Register::ERDPTL => true, + Register::ERDPTH => true, + Register::EWRPTL => true, + Register::EWRPTH => true, + Register::ETXSTL => true, + Register::ETXSTH => true, + Register::ETXNDL => true, + Register::ETXNDH => true, + Register::ERXSTL => true, + Register::ERXSTH => true, + Register::ERXNDL => true, + Register::ERXNDH => true, + Register::ERXRDPTL => true, + Register::ERXRDPTH => true, + Register::ERXWRPTL => true, + Register::ERXWRPTH => true, + Register::EDMASTL => true, + Register::EDMASTH => true, + Register::EDMANDL => true, + Register::EDMANDH => true, + Register::EDMADSTL => true, + Register::EDMADSTH => true, + Register::EDMACSL => true, + Register::EDMACSH => true, + } + } +} + +impl Into for Register { + fn into(self) -> super::Register { + super::Register::Bank0(self) + } +} diff --git a/embassy-net-enc28j60/src/bank1.rs b/embassy-net-enc28j60/src/bank1.rs new file mode 100644 index 000000000..30560edf6 --- /dev/null +++ b/embassy-net-enc28j60/src/bank1.rs @@ -0,0 +1,84 @@ +#[allow(dead_code)] +#[derive(Clone, Copy)] +pub enum Register { + EHT0 = 0x00, + EHT1 = 0x01, + EHT2 = 0x02, + EHT3 = 0x03, + EHT4 = 0x04, + EHT5 = 0x05, + EHT6 = 0x06, + EHT7 = 0x07, + EPMM0 = 0x08, + EPMM1 = 0x09, + EPMM2 = 0x0a, + EPMM3 = 0x0b, + EPMM4 = 0x0c, + EPMM5 = 0x0d, + EPMM6 = 0x0e, + EPMM7 = 0x0f, + EPMCSL = 0x10, + EPMCSH = 0x11, + EPMOL = 0x14, + EPMOH = 0x15, + ERXFCON = 0x18, + EPKTCNT = 0x19, +} + +impl Register { + pub(crate) fn addr(&self) -> u8 { + *self as u8 + } + + pub(crate) fn is_eth_register(&self) -> bool { + match *self { + Register::EHT0 => true, + Register::EHT1 => true, + Register::EHT2 => true, + Register::EHT3 => true, + Register::EHT4 => true, + Register::EHT5 => true, + Register::EHT6 => true, + Register::EHT7 => true, + Register::EPMM0 => true, + Register::EPMM1 => true, + Register::EPMM2 => true, + Register::EPMM3 => true, + Register::EPMM4 => true, + Register::EPMM5 => true, + Register::EPMM6 => true, + Register::EPMM7 => true, + Register::EPMCSL => true, + Register::EPMCSH => true, + Register::EPMOL => true, + Register::EPMOH => true, + Register::ERXFCON => true, + Register::EPKTCNT => true, + } + } +} + +impl Into for Register { + fn into(self) -> super::Register { + super::Register::Bank1(self) + } +} + +register!(ERXFCON, 0b1010_0001, u8, { + #[doc = "Broadcast Filter Enable bit"] + bcen @ 0, + #[doc = "Multicast Filter Enable bit"] + mcen @ 1, + #[doc = "Hash Table Filter Enable bit"] + hten @ 2, + #[doc = "Magic Packetâ„¢ Filter Enable bit"] + mpen @ 3, + #[doc = "Pattern Match Filter Enable bit"] + pmen @ 4, + #[doc = "Post-Filter CRC Check Enable bit"] + crcen @ 5, + #[doc = "AND/OR Filter Select bit"] + andor @ 6, + #[doc = "Unicast Filter Enable bit"] + ucen @ 7, +}); diff --git a/embassy-net-enc28j60/src/bank2.rs b/embassy-net-enc28j60/src/bank2.rs new file mode 100644 index 000000000..74a1d245f --- /dev/null +++ b/embassy-net-enc28j60/src/bank2.rs @@ -0,0 +1,86 @@ +#[allow(dead_code)] +#[derive(Clone, Copy)] +pub enum Register { + MACON1 = 0x00, + MACON3 = 0x02, + MACON4 = 0x03, + MABBIPG = 0x04, + MAIPGL = 0x06, + MAIPGH = 0x07, + MACLCON1 = 0x08, + MACLCON2 = 0x09, + MAMXFLL = 0x0a, + MAMXFLH = 0x0b, + MICMD = 0x12, + MIREGADR = 0x14, + MIWRL = 0x16, + MIWRH = 0x17, + MIRDL = 0x18, + MIRDH = 0x19, +} + +impl Register { + pub(crate) fn addr(&self) -> u8 { + *self as u8 + } + + pub(crate) fn is_eth_register(&self) -> bool { + match *self { + Register::MACON1 => false, + Register::MACON3 => false, + Register::MACON4 => false, + Register::MABBIPG => false, + Register::MAIPGL => false, + Register::MAIPGH => false, + Register::MACLCON1 => false, + Register::MACLCON2 => false, + Register::MAMXFLL => false, + Register::MAMXFLH => false, + Register::MICMD => false, + Register::MIREGADR => false, + Register::MIWRL => false, + Register::MIWRH => false, + Register::MIRDL => false, + Register::MIRDH => false, + } + } +} + +impl Into for Register { + fn into(self) -> super::Register { + super::Register::Bank2(self) + } +} + +register!(MACON1, 0, u8, { + #[doc = "Enable packets to be received by the MAC"] + marxen @ 0, + #[doc = "Control frames will be discarded after being processed by the MAC"] + passall @ 1, + #[doc = "Inhibit transmissions when pause control frames are received"] + rxpaus @ 2, + #[doc = "Allow the MAC to transmit pause control frames"] + txpaus @ 3, +}); + +register!(MACON3, 0, u8, { + #[doc = "MAC will operate in Full-Duplex mode"] + fuldpx @ 0, + #[doc = "The type/length field of transmitted and received frames will be checked"] + frmlnen @ 1, + #[doc = "Frames bigger than MAMXFL will be aborted when transmitted or received"] + hfrmen @ 2, + #[doc = "No proprietary header is present"] + phdren @ 3, + #[doc = "MAC will append a valid CRC to all frames transmitted regardless of PADCFG bit"] + txcrcen @ 4, + #[doc = "All short frames will be zero-padded to 64 bytes and a valid CRC will then be appended"] + padcfg @ 5..7, +}); + +register!(MICMD, 0, u8, { + #[doc = "MII Read Enable bit"] + miird @ 0, + #[doc = "MII Scan Enable bit"] + miiscan @ 1, +}); diff --git a/embassy-net-enc28j60/src/bank3.rs b/embassy-net-enc28j60/src/bank3.rs new file mode 100644 index 000000000..4f7eb9406 --- /dev/null +++ b/embassy-net-enc28j60/src/bank3.rs @@ -0,0 +1,53 @@ +#[allow(dead_code)] +#[derive(Clone, Copy)] +pub enum Register { + MAADR5 = 0x00, + MAADR6 = 0x01, + MAADR3 = 0x02, + MAADR4 = 0x03, + MAADR1 = 0x04, + MAADR2 = 0x05, + EBSTSD = 0x06, + EBSTCON = 0x07, + EBSTCSL = 0x08, + EBSTCSH = 0x09, + MISTAT = 0x0a, + EREVID = 0x12, + ECOCON = 0x15, + EFLOCON = 0x17, + EPAUSL = 0x18, + EPAUSH = 0x19, +} + +impl Register { + pub(crate) fn addr(&self) -> u8 { + *self as u8 + } + + pub(crate) fn is_eth_register(&self) -> bool { + match *self { + Register::MAADR5 => false, + Register::MAADR6 => false, + Register::MAADR3 => false, + Register::MAADR4 => false, + Register::MAADR1 => false, + Register::MAADR2 => false, + Register::EBSTSD => true, + Register::EBSTCON => true, + Register::EBSTCSL => true, + Register::EBSTCSH => true, + Register::MISTAT => false, + Register::EREVID => true, + Register::ECOCON => true, + Register::EFLOCON => true, + Register::EPAUSL => true, + Register::EPAUSH => true, + } + } +} + +impl Into for Register { + fn into(self) -> super::Register { + super::Register::Bank3(self) + } +} diff --git a/embassy-net-enc28j60/src/common.rs b/embassy-net-enc28j60/src/common.rs new file mode 100644 index 000000000..ef339dd2a --- /dev/null +++ b/embassy-net-enc28j60/src/common.rs @@ -0,0 +1,106 @@ +#[allow(dead_code)] +#[derive(Clone, Copy)] +pub enum Register { + ECON1 = 0x1f, + ECON2 = 0x1e, + EIE = 0x1b, + EIR = 0x1c, + ESTAT = 0x1d, +} + +impl Register { + pub(crate) fn addr(&self) -> u8 { + *self as u8 + } + + pub(crate) fn is_eth_register(&self) -> bool { + match *self { + Register::ECON1 => true, + Register::ECON2 => true, + Register::EIE => true, + Register::EIR => true, + Register::ESTAT => true, + } + } +} + +impl Into for Register { + fn into(self) -> super::Register { + super::Register::Common(self) + } +} + +register!(EIE, 0, u8, { + #[doc = "Receive Error Interrupt Enable bit"] + rxerie @ 0, + #[doc = "Transmit Error Interrupt Enable bit"] + txerie @ 1, + #[doc = "Transmit Enable bit"] + txie @ 3, + #[doc = "Link Status Change Interrupt Enable bit"] + linkie @ 4, + #[doc = "DMA Interrupt Enable bit"] + dmaie @ 5, + #[doc = "Receive Packet Pending Interrupt Enable bit"] + pktie @ 6, + #[doc = "Global INT Interrupt Enable bit"] + intie @ 7, +}); + +register!(EIR, 0, u8, { + #[doc = "Receive Error Interrupt Flag bit"] + rxerif @ 0, + #[doc = "Transmit Error Interrupt Flag bit"] + txerif @ 1, + #[doc = "Transmit Interrupt Flag bit"] + txif @ 3, + #[doc = "Link Change Interrupt Flag bit"] + linkif @ 4, + #[doc = "DMA Interrupt Flag bit"] + dmaif @ 5, + #[doc = "Receive Packet Pending Interrupt Flag bit"] + pktif @ 6, +}); + +register!(ESTAT, 0, u8, { + #[doc = "Clock Ready bit"] + clkrdy @ 0, + #[doc = "Transmit Abort Error bit"] + txabrt @ 1, + #[doc = "Receive Busy bit"] + rxbusy @ 2, + #[doc = "Late Collision Error bit"] + latecol @ 4, + #[doc = "Ethernet Buffer Error Status bit"] + bufer @ 6, + #[doc = "INT Interrupt Flag bit"] + int @ 7, +}); + +register!(ECON2, 0b1000_0000, u8, { + #[doc = "Voltage Regulator Power Save Enable bit"] + vrps @ 3, + #[doc = "Power Save Enable bit"] + pwrsv @ 5, + #[doc = "Packet Decrement bit"] + pktdec @ 6, + #[doc = "Automatic Buffer Pointer Increment Enable bit"] + autoinc @ 7, +}); + +register!(ECON1, 0, u8, { + #[doc = "Bank Select bits"] + bsel @ 0..1, + #[doc = "Receive Enable bi"] + rxen @ 2, + #[doc = "Transmit Request to Send bit"] + txrts @ 3, + #[doc = "DMA Checksum Enable bit"] + csumen @ 4, + #[doc = "DMA Start and Busy Status bit"] + dmast @ 5, + #[doc = "Receive Logic Reset bit"] + rxrst @ 6, + #[doc = "Transmit Logic Reset bit"] + txrst @ 7, +}); diff --git a/embassy-net-enc28j60/src/fmt.rs b/embassy-net-enc28j60/src/fmt.rs new file mode 100644 index 000000000..066970813 --- /dev/null +++ b/embassy-net-enc28j60/src/fmt.rs @@ -0,0 +1,225 @@ +#![macro_use] +#![allow(unused_macros)] + +#[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 _ = ($( & $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 _ = ($( & $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 _ = ($( & $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 _ = ($( & $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 _ = ($( & $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 + } +} diff --git a/embassy-net-enc28j60/src/header.rs b/embassy-net-enc28j60/src/header.rs new file mode 100644 index 000000000..c2d4e468f --- /dev/null +++ b/embassy-net-enc28j60/src/header.rs @@ -0,0 +1,30 @@ +register!(RxStatus, 0, u32, { + #[doc = "Indicates length of the received frame"] + byte_count @ 0..15, + #[doc = "Indicates a packet over 50,000 bit times occurred or that a packet was dropped since the last receive"] + long_event @ 16, + #[doc = "Indicates that at some time since the last receive, a carrier event was detected"] + carrier_event @ 18, + #[doc = "Indicates that frame CRC field value does not match the CRC calculated by the MAC"] + crc_error @ 20, + #[doc = "Indicates that frame length field value in the packet does not match the actual data byte length and specifies a valid length"] + length_check_error @ 21, + #[doc = "Indicates that frame type/length field was larger than 1500 bytes (type field)"] + length_out_of_range @ 22, + #[doc = "Indicates that at the packet had a valid CRC and no symbol errors"] + received_ok @ 23, + #[doc = "Indicates packet received had a valid Multicast address"] + multicast @ 24, + #[doc = "Indicates packet received had a valid Broadcast address."] + broadcast @ 25, + #[doc = "Indicates that after the end of this packet, an additional 1 to 7 bits were received"] + dribble_nibble @ 26, + #[doc = "Current frame was recognized as a control frame for having a valid type/length designating it as a control frame"] + receive_control_frame @ 27, + #[doc = "Current frame was recognized as a control frame containing a valid pause frame opcode and a valid destination address"] + receive_pause_control_frame @ 28, + #[doc = "Current frame was recognized as a control frame but it contained an unknown opcode"] + receive_unknown_opcode @ 29, + #[doc = "Current frame was recognized as a VLAN tagged frame"] + receive_vlan_type_detected @ 30, +}); diff --git a/embassy-net-enc28j60/src/lib.rs b/embassy-net-enc28j60/src/lib.rs new file mode 100644 index 000000000..4f129b6b2 --- /dev/null +++ b/embassy-net-enc28j60/src/lib.rs @@ -0,0 +1,693 @@ +#![no_std] +#![doc = include_str!("../README.md")] + +// must go first. +mod fmt; + +#[macro_use] +mod macros; +mod bank0; +mod bank1; +mod bank2; +mod bank3; +mod common; +mod header; +mod phy; +mod traits; + +use core::cmp; +use core::convert::TryInto; + +use embassy_net_driver::{Capabilities, HardwareAddress, LinkState, Medium}; +use embassy_time::Duration; +use embedded_hal::digital::OutputPin; +use embedded_hal::spi::{Operation, SpiDevice}; +use traits::U16Ext; + +// Total buffer size (see section 3.2) +const BUF_SZ: u16 = 8 * 1024; + +// Maximum frame length +const MAX_FRAME_LENGTH: u16 = 1518; // value recommended in the data sheet + +// Size of the Frame check sequence (32-bit CRC) +const CRC_SZ: u16 = 4; + +// define the boundaries of the TX and RX buffers +// to workaround errata #5 we do the opposite of what section 6.1 of the data sheet +// says: we place the RX buffer at address 0 and the TX buffer after it +const RXST: u16 = 0x0000; +const RXND: u16 = 0x19ff; +const TXST: u16 = 0x1a00; +const _TXND: u16 = 0x1fff; + +const MTU: usize = 1514; // 1500 IP + 14 ethernet header + +pub struct Enc28j60 { + mac_addr: [u8; 6], + + spi: S, + rst: Option, + + bank: Bank, + + // address of the next packet in buffer memory + next_packet: u16, +} + +impl Enc28j60 +where + S: SpiDevice, + O: OutputPin, +{ + pub fn new(spi: S, rst: Option, mac_addr: [u8; 6]) -> Self { + let mut res = Self { + mac_addr, + spi, + rst, + + bank: Bank::Bank0, + next_packet: RXST, + }; + res.init(); + res + } + + fn init(&mut self) { + if let Some(rst) = &mut self.rst { + rst.set_low().unwrap(); + embassy_time::block_for(Duration::from_millis(5)); + rst.set_high().unwrap(); + embassy_time::block_for(Duration::from_millis(5)); + } else { + embassy_time::block_for(Duration::from_millis(5)); + self.soft_reset(); + embassy_time::block_for(Duration::from_millis(5)); + } + + debug!( + "enc28j60: erevid {=u8:x}", + self.read_control_register(bank3::Register::EREVID) + ); + debug!("enc28j60: waiting for clk"); + while common::ESTAT(self.read_control_register(common::Register::ESTAT)).clkrdy() == 0 {} + debug!("enc28j60: clk ok"); + + if self.read_control_register(bank3::Register::EREVID) == 0 { + panic!("ErevidIsZero"); + } + + // disable CLKOUT output + self.write_control_register(bank3::Register::ECOCON, 0); + + // RX start + // "It is recommended that the ERXST Pointer be programmed with an even address" + self.write_control_register(bank0::Register::ERXSTL, RXST.low()); + self.write_control_register(bank0::Register::ERXSTH, RXST.high()); + + // RX read pointer + // NOTE Errata #14 so we are using an *odd* address here instead of ERXST + self.write_control_register(bank0::Register::ERXRDPTL, RXND.low()); + self.write_control_register(bank0::Register::ERXRDPTH, RXND.high()); + + // RX end + self.write_control_register(bank0::Register::ERXNDL, RXND.low()); + self.write_control_register(bank0::Register::ERXNDH, RXND.high()); + + // TX start + // "It is recommended that an even address be used for ETXST" + debug_assert_eq!(TXST % 2, 0); + self.write_control_register(bank0::Register::ETXSTL, TXST.low()); + self.write_control_register(bank0::Register::ETXSTH, TXST.high()); + + // TX end is set in `transmit` + + // MAC initialization (see section 6.5) + // 1. Set the MARXEN bit in MACON1 to enable the MAC to receive frames. + self.write_control_register( + bank2::Register::MACON1, + bank2::MACON1::default().marxen(1).passall(0).rxpaus(1).txpaus(1).bits(), + ); + + // 2. Configure the PADCFG, TXCRCEN and FULDPX bits of MACON3. + self.write_control_register( + bank2::Register::MACON3, + bank2::MACON3::default().frmlnen(1).txcrcen(1).padcfg(0b001).bits(), + ); + + // 4. Program the MAMXFL registers with the maximum frame length to be permitted to be + // received or transmitted + self.write_control_register(bank2::Register::MAMXFLL, MAX_FRAME_LENGTH.low()); + self.write_control_register(bank2::Register::MAMXFLH, MAX_FRAME_LENGTH.high()); + + // 5. Configure the Back-to-Back Inter-Packet Gap register, MABBIPG. + // Use recommended value of 0x12 + self.write_control_register(bank2::Register::MABBIPG, 0x12); + + // 6. Configure the Non-Back-to-Back Inter-Packet Gap register low byte, MAIPGL. + // Use recommended value of 0x12 + self.write_control_register(bank2::Register::MAIPGL, 0x12); + self.write_control_register(bank2::Register::MAIPGH, 0x0c); + + // 9. Program the local MAC address into the MAADR1:MAADR6 registers + self.write_control_register(bank3::Register::MAADR1, self.mac_addr[0]); + self.write_control_register(bank3::Register::MAADR2, self.mac_addr[1]); + self.write_control_register(bank3::Register::MAADR3, self.mac_addr[2]); + self.write_control_register(bank3::Register::MAADR4, self.mac_addr[3]); + self.write_control_register(bank3::Register::MAADR5, self.mac_addr[4]); + self.write_control_register(bank3::Register::MAADR6, self.mac_addr[5]); + + // Set the PHCON2.HDLDIS bit to prevent automatic loopback of the data which is transmitted + self.write_phy_register(phy::Register::PHCON2, phy::PHCON2::default().hdldis(1).bits()); + + // Globally enable interrupts + //self.bit_field_set(common::Register::EIE, common::EIE::mask().intie()); + + // Set the per packet control byte; we'll always use the value 0 + self.write_buffer_memory(Some(TXST), &mut [0]); + + // decrease the packet count to 0 + while self.read_control_register(bank1::Register::EPKTCNT) != 0 { + self.bit_field_set(common::Register::ECON2, common::ECON2::mask().pktdec()); + } + + // Enable reception + self.bit_field_set(common::Register::ECON1, common::ECON1::mask().rxen()); + } + + /// Flushes the transmit buffer, ensuring all pending transmissions have completed + /// NOTE: The returned packet *must* be `read` or `ignore`-d, otherwise this method will always + /// return `None` on subsequent invocations + pub fn receive<'a>(&mut self, buf: &'a mut [u8]) -> Option<&'a mut [u8]> { + if self.pending_packets() == 0 { + // Errata #6: we can't rely on PKTIF so we check PKTCNT + return None; + } + + let curr_packet = self.next_packet; + + // read out the first 6 bytes + let mut temp_buf = [0; 6]; + self.read_buffer_memory(Some(curr_packet), &mut temp_buf); + + // next packet pointer + let next_packet = u16::from_parts(temp_buf[0], temp_buf[1]); + if next_packet > RXND { + panic!("CorruptRxBuffer"); + } + + // status vector + let status = header::RxStatus(u32::from_le_bytes(temp_buf[2..].try_into().unwrap())); + let len = status.byte_count() as u16 - CRC_SZ; + + if len > RXND { + panic!("CorruptRxBuffer 2"); + } + + self.read_buffer_memory(None, &mut buf[..len as usize]); + + // update ERXRDPT + // due to Errata #14 we must write an odd address to ERXRDPT + // we know that ERXST = 0, that ERXND is odd and that next_packet is even + let rxrdpt = if self.next_packet < 1 || self.next_packet > RXND + 1 { + RXND + } else { + self.next_packet - 1 + }; + // "To move ERXRDPT, the host controller must write to ERXRDPTL first." + self.write_control_register(bank0::Register::ERXRDPTL, rxrdpt.low()); + self.write_control_register(bank0::Register::ERXRDPTH, rxrdpt.high()); + + // decrease the packet count + self.bit_field_set(common::Register::ECON2, common::ECON2::mask().pktdec()); + + self.next_packet = next_packet; + + Some(&mut buf[..len as usize]) + } + + fn wait_tx_ready(&mut self) { + for _ in 0u32..10000 { + if common::ECON1(self.read_control_register(common::Register::ECON1)).txrts() == 0 { + return; + } + } + + // work around errata #12 by resetting the transmit logic before every new + // transmission + self.bit_field_set(common::Register::ECON1, common::ECON1::mask().txrst()); + self.bit_field_clear(common::Register::ECON1, common::ECON1::mask().txrst()); + //self.bit_field_clear(common::Register::EIR, { + // let mask = common::EIR::mask(); + // mask.txerif() | mask.txif() + //}); + } + + /// Starts the transmission of `bytes` + /// + /// It's up to the caller to ensure that `bytes` is a valid Ethernet frame. The interface will + /// take care of appending a (4 byte) CRC to the frame and of padding the frame to the minimum + /// size allowed by the Ethernet specification (64 bytes, or 46 bytes of payload). + /// + /// NOTE This method will flush any previous transmission that's in progress + /// + /// # Panics + /// + /// If `bytes` length is greater than 1514, the maximum frame length allowed by the interface, + /// or greater than the transmit buffer + pub fn transmit(&mut self, bytes: &[u8]) { + assert!(bytes.len() <= self.mtu() as usize); + + self.wait_tx_ready(); + + // NOTE the plus one is to not overwrite the per packet control byte + let wrpt = TXST + 1; + + // 1. ETXST was set during initialization + + // 2. write the frame to the IC memory + self.write_buffer_memory(Some(wrpt), bytes); + + let txnd = wrpt + bytes.len() as u16 - 1; + + // 3. Set the end address of the transmit buffer + self.write_control_register(bank0::Register::ETXNDL, txnd.low()); + self.write_control_register(bank0::Register::ETXNDH, txnd.high()); + + // 4. reset interrupt flag + //self.bit_field_clear(common::Register::EIR, common::EIR::mask().txif()); + + // 5. start transmission + self.bit_field_set(common::Register::ECON1, common::ECON1::mask().txrts()); + + // Wait until transmission finishes + //while common::ECON1(self.read_control_register(common::Register::ECON1)).txrts() == 1 {} + + /* + // read the transmit status vector + let mut tx_stat = [0; 7]; + self.read_buffer_memory(None, &mut tx_stat); + + let stat = common::ESTAT(self.read_control_register(common::Register::ESTAT)); + + if stat.txabrt() == 1 { + // work around errata #12 by reading the transmit status vector + if stat.latecol() == 1 || (tx_stat[2] & (1 << 5)) != 0 { + panic!("LateCollision") + } else { + panic!("TransmitAbort") + } + }*/ + } + + pub fn is_link_up(&mut self) -> bool { + let bits = self.read_phy_register(phy::Register::PHSTAT2); + phy::PHSTAT2(bits).lstat() == 1 + } + + /// Returns the interface Maximum Transmission Unit (MTU) + /// + /// The value returned by this function will never exceed 1514 bytes. The actual value depends + /// on the memory assigned to the transmission buffer when initializing the device + pub fn mtu(&self) -> u16 { + cmp::min(BUF_SZ - RXND - 1, MAX_FRAME_LENGTH - CRC_SZ) + } + + /* Miscellaneous */ + /// Returns the number of packets that have been received but have not been processed yet + pub fn pending_packets(&mut self) -> u8 { + self.read_control_register(bank1::Register::EPKTCNT) + } + + /// Adjusts the receive filter to *accept* these packet types + pub fn accept(&mut self, packets: &[Packet]) { + let mask = bank1::ERXFCON::mask(); + let mut val = 0; + for packet in packets { + match packet { + Packet::Broadcast => val |= mask.bcen(), + Packet::Multicast => val |= mask.mcen(), + Packet::Unicast => val |= mask.ucen(), + } + } + + self.bit_field_set(bank1::Register::ERXFCON, val) + } + + /// Adjusts the receive filter to *ignore* these packet types + pub fn ignore(&mut self, packets: &[Packet]) { + let mask = bank1::ERXFCON::mask(); + let mut val = 0; + for packet in packets { + match packet { + Packet::Broadcast => val |= mask.bcen(), + Packet::Multicast => val |= mask.mcen(), + Packet::Unicast => val |= mask.ucen(), + } + } + + self.bit_field_clear(bank1::Register::ERXFCON, val) + } + + /* Private */ + /* Read */ + fn read_control_register(&mut self, register: R) -> u8 + where + R: Into, + { + self._read_control_register(register.into()) + } + + fn _read_control_register(&mut self, register: Register) -> u8 { + self.change_bank(register); + + let mut buffer = [Instruction::RCR.opcode() | register.addr(), 0]; + self.spi.transfer_in_place(&mut buffer).unwrap(); + + buffer[1] + } + + fn read_phy_register(&mut self, register: phy::Register) -> u16 { + embassy_time::block_for(Duration::from_millis(1)); + + // set PHY register address + self.write_control_register(bank2::Register::MIREGADR, register.addr()); + + // start read operation + self.write_control_register(bank2::Register::MICMD, bank2::MICMD::default().miird(1).bits()); + + // wait until the read operation finishes + while self.read_control_register(bank3::Register::MISTAT) & 0b1 != 0 {} + + let h = self.read_control_register(bank2::Register::MIRDH); + let l = self.read_control_register(bank2::Register::MIRDL); + + self.write_control_register(bank2::Register::MICMD, bank2::MICMD::default().miird(0).bits()); + + (l as u16) | (h as u16) << 8 + } + + /* Write */ + fn _write_control_register(&mut self, register: Register, value: u8) { + self.change_bank(register); + + let buffer = [Instruction::WCR.opcode() | register.addr(), value]; + self.spi.write(&buffer).unwrap(); + } + + fn write_control_register(&mut self, register: R, value: u8) + where + R: Into, + { + self._write_control_register(register.into(), value) + } + + fn write_phy_register(&mut self, register: phy::Register, value: u16) { + // set PHY register address + self.write_control_register(bank2::Register::MIREGADR, register.addr()); + + self.write_control_register(bank2::Register::MIWRL, (value & 0xff) as u8); + // this starts the write operation + self.write_control_register(bank2::Register::MIWRH, (value >> 8) as u8); + + // wait until the write operation finishes + while self.read_control_register(bank3::Register::MISTAT) & 0b1 != 0 {} + } + + /* RMW */ + fn modify_control_register(&mut self, register: R, f: F) + where + F: FnOnce(u8) -> u8, + R: Into, + { + self._modify_control_register(register.into(), f) + } + + fn _modify_control_register(&mut self, register: Register, f: F) + where + F: FnOnce(u8) -> u8, + { + let r = self._read_control_register(register); + self._write_control_register(register, f(r)) + } + + /* Auxiliary */ + fn change_bank(&mut self, register: Register) { + let bank = register.bank(); + + if let Some(bank) = bank { + if self.bank == bank { + // already on the register bank + return; + } + + // change bank + self.bank = bank; + match bank { + Bank::Bank0 => self.bit_field_clear(common::Register::ECON1, 0b11), + Bank::Bank1 => self.modify_control_register(common::Register::ECON1, |r| (r & !0b11) | 0b01), + Bank::Bank2 => self.modify_control_register(common::Register::ECON1, |r| (r & !0b11) | 0b10), + Bank::Bank3 => self.bit_field_set(common::Register::ECON1, 0b11), + } + } else { + // common register + } + } + + /* Primitive operations */ + fn bit_field_clear(&mut self, register: R, mask: u8) + where + R: Into, + { + self._bit_field_clear(register.into(), mask) + } + + fn _bit_field_clear(&mut self, register: Register, mask: u8) { + debug_assert!(register.is_eth_register()); + + self.change_bank(register); + + self.spi + .write(&[Instruction::BFC.opcode() | register.addr(), mask]) + .unwrap(); + } + + fn bit_field_set(&mut self, register: R, mask: u8) + where + R: Into, + { + self._bit_field_set(register.into(), mask) + } + + fn _bit_field_set(&mut self, register: Register, mask: u8) { + debug_assert!(register.is_eth_register()); + + self.change_bank(register); + + self.spi + .write(&[Instruction::BFS.opcode() | register.addr(), mask]) + .unwrap(); + } + + fn read_buffer_memory(&mut self, addr: Option, buf: &mut [u8]) { + if let Some(addr) = addr { + self.write_control_register(bank0::Register::ERDPTL, addr.low()); + self.write_control_register(bank0::Register::ERDPTH, addr.high()); + } + + self.spi + .transaction(&mut [Operation::Write(&[Instruction::RBM.opcode()]), Operation::Read(buf)]) + .unwrap(); + } + + fn soft_reset(&mut self) { + self.spi.write(&[Instruction::SRC.opcode()]).unwrap(); + } + + fn write_buffer_memory(&mut self, addr: Option, buffer: &[u8]) { + if let Some(addr) = addr { + self.write_control_register(bank0::Register::EWRPTL, addr.low()); + self.write_control_register(bank0::Register::EWRPTH, addr.high()); + } + + self.spi + .transaction(&mut [Operation::Write(&[Instruction::WBM.opcode()]), Operation::Write(buffer)]) + .unwrap(); + } +} + +#[derive(Clone, Copy, PartialEq)] +enum Bank { + Bank0, + Bank1, + Bank2, + Bank3, +} + +#[derive(Clone, Copy)] +enum Instruction { + /// Read Control Register + RCR = 0b000_00000, + /// Read Buffer Memory + RBM = 0b001_11010, + /// Write Control Register + WCR = 0b010_00000, + /// Write Buffer Memory + WBM = 0b011_11010, + /// Bit Field Set + BFS = 0b100_00000, + /// Bit Field Clear + BFC = 0b101_00000, + /// System Reset Command + SRC = 0b111_11111, +} + +impl Instruction { + fn opcode(&self) -> u8 { + *self as u8 + } +} + +#[derive(Clone, Copy)] +enum Register { + Bank0(bank0::Register), + Bank1(bank1::Register), + Bank2(bank2::Register), + Bank3(bank3::Register), + Common(common::Register), +} + +impl Register { + fn addr(&self) -> u8 { + match *self { + Register::Bank0(r) => r.addr(), + Register::Bank1(r) => r.addr(), + Register::Bank2(r) => r.addr(), + Register::Bank3(r) => r.addr(), + Register::Common(r) => r.addr(), + } + } + + fn bank(&self) -> Option { + Some(match *self { + Register::Bank0(_) => Bank::Bank0, + Register::Bank1(_) => Bank::Bank1, + Register::Bank2(_) => Bank::Bank2, + Register::Bank3(_) => Bank::Bank3, + Register::Common(_) => return None, + }) + } + + fn is_eth_register(&self) -> bool { + match *self { + Register::Bank0(r) => r.is_eth_register(), + Register::Bank1(r) => r.is_eth_register(), + Register::Bank2(r) => r.is_eth_register(), + Register::Bank3(r) => r.is_eth_register(), + Register::Common(r) => r.is_eth_register(), + } + } +} + +/// Packet type, used to configure receive filters +#[non_exhaustive] +#[derive(Clone, Copy, Eq, PartialEq)] +pub enum Packet { + /// Broadcast packets + Broadcast, + /// Multicast packets + Multicast, + /// Unicast packets + Unicast, +} + +static mut TX_BUF: [u8; MTU] = [0; MTU]; +static mut RX_BUF: [u8; MTU] = [0; MTU]; + +impl embassy_net_driver::Driver for Enc28j60 +where + S: SpiDevice, + O: OutputPin, +{ + type RxToken<'a> = RxToken<'a> + where + Self: 'a; + + type TxToken<'a> = TxToken<'a, S, O> + where + Self: 'a; + + fn receive(&mut self, cx: &mut core::task::Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + let rx_buf = unsafe { &mut RX_BUF }; + let tx_buf = unsafe { &mut TX_BUF }; + if let Some(pkt) = self.receive(rx_buf) { + let n = pkt.len(); + Some((RxToken { buf: &mut pkt[..n] }, TxToken { buf: tx_buf, eth: self })) + } else { + cx.waker().wake_by_ref(); + None + } + } + + fn transmit(&mut self, _cx: &mut core::task::Context) -> Option> { + let tx_buf = unsafe { &mut TX_BUF }; + Some(TxToken { buf: tx_buf, eth: self }) + } + + fn link_state(&mut self, cx: &mut core::task::Context) -> LinkState { + cx.waker().wake_by_ref(); + match self.is_link_up() { + true => LinkState::Up, + false => LinkState::Down, + } + } + + fn capabilities(&self) -> Capabilities { + let mut caps = Capabilities::default(); + caps.max_transmission_unit = MTU; + caps.medium = Medium::Ethernet; + caps + } + + fn hardware_address(&self) -> HardwareAddress { + HardwareAddress::Ethernet(self.mac_addr) + } +} + +pub struct RxToken<'a> { + buf: &'a mut [u8], +} + +impl<'a> embassy_net_driver::RxToken for RxToken<'a> { + fn consume(self, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + f(self.buf) + } +} + +pub struct TxToken<'a, S, O> +where + S: SpiDevice, + O: OutputPin, +{ + eth: &'a mut Enc28j60, + buf: &'a mut [u8], +} + +impl<'a, S, O> embassy_net_driver::TxToken for TxToken<'a, S, O> +where + S: SpiDevice, + O: OutputPin, +{ + fn consume(self, len: usize, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + assert!(len <= self.buf.len()); + let r = f(&mut self.buf[..len]); + self.eth.transmit(&self.buf[..len]); + r + } +} diff --git a/embassy-net-enc28j60/src/macros.rs b/embassy-net-enc28j60/src/macros.rs new file mode 100644 index 000000000..8d0649572 --- /dev/null +++ b/embassy-net-enc28j60/src/macros.rs @@ -0,0 +1,89 @@ +macro_rules! register { + ($REGISTER:ident, $reset_value:expr, $uxx:ty, { + $(#[$($attr:tt)*] $bitfield:ident @ $range:expr,)+ + }) => { + #[derive(Clone, Copy)] + pub(crate) struct $REGISTER { + bits: $uxx, + _mode: ::core::marker::PhantomData, + } + + impl $REGISTER { + #[allow(dead_code)] + pub(crate) fn mask() -> $REGISTER { + $REGISTER { bits: 0, _mode: ::core::marker::PhantomData } + } + + $( + #[allow(dead_code)] + pub(crate) fn $bitfield(&self) -> $uxx { + use super::traits::OffsetSize; + + let size = $range.size(); + let offset = $range.offset(); + ((1 << size) - 1) << offset + } + )+ + } + + impl ::core::default::Default for $REGISTER { + fn default() -> Self { + $REGISTER { bits: $reset_value, _mode: ::core::marker::PhantomData } + } + } + + #[allow(non_snake_case)] + #[allow(dead_code)] + pub(crate) fn $REGISTER(bits: $uxx) -> $REGISTER { + $REGISTER { bits, _mode: ::core::marker::PhantomData } + } + + impl $REGISTER { + #[allow(dead_code)] + pub(crate) fn modify(self) -> $REGISTER { + $REGISTER { bits: self.bits, _mode: ::core::marker::PhantomData } + } + + $( + #[$($attr)*] + #[allow(dead_code)] + pub(crate) fn $bitfield(&self) -> $uxx { + use super::traits::OffsetSize; + + let offset = $range.offset(); + let size = $range.size(); + let mask = (1 << size) - 1; + + (self.bits >> offset) & mask + } + )+ + } + + impl $REGISTER { + #[allow(dead_code)] + pub(crate) fn bits(self) -> $uxx { + self.bits + } + + $( + #[$($attr)*] + #[allow(dead_code)] + pub(crate) fn $bitfield(&mut self, mut bits: $uxx) -> &mut Self { + use super::traits::OffsetSize; + + let offset = $range.offset(); + let size = $range.size(); + let mask = (1 << size) - 1; + + debug_assert!(bits <= mask); + bits &= mask; + + self.bits &= !(mask << offset); + self.bits |= bits << offset; + + self + } + )+ + } + } +} diff --git a/embassy-net-enc28j60/src/phy.rs b/embassy-net-enc28j60/src/phy.rs new file mode 100644 index 000000000..7f62b5f27 --- /dev/null +++ b/embassy-net-enc28j60/src/phy.rs @@ -0,0 +1,36 @@ +#[allow(dead_code)] +#[derive(Clone, Copy)] +pub enum Register { + PHCON1 = 0x00, + PHSTAT1 = 0x01, + PHID1 = 0x02, + PHID2 = 0x03, + PHCON2 = 0x10, + PHSTAT2 = 0x11, + PHIE = 0x12, + PHIR = 0x13, + PHLCON = 0x14, +} + +impl Register { + pub(crate) fn addr(&self) -> u8 { + *self as u8 + } +} + +register!(PHCON2, 0, u16, { + #[doc = "PHY Half-Duplex Loopback Disable bit"] + hdldis @ 8, + #[doc = "Jabber Correction Disable bit"] + jabber @ 10, + #[doc = "Twisted-Pair Transmitter Disable bit"] + txdis @ 13, + #[doc = "PHY Force Linkup bit"] + frclnk @ 14, +}); + +register!(PHSTAT2, 0, u16, { + // Datasheet says it's bit 10, but it's actually bit 2 ?!?! + #[doc = "Link Status bit"] + lstat @ 2, +}); diff --git a/embassy-net-enc28j60/src/traits.rs b/embassy-net-enc28j60/src/traits.rs new file mode 100644 index 000000000..08f94045a --- /dev/null +++ b/embassy-net-enc28j60/src/traits.rs @@ -0,0 +1,57 @@ +use core::ops::Range; + +pub(crate) trait OffsetSize { + fn offset(self) -> u8; + fn size(self) -> u8; +} + +impl OffsetSize for u8 { + fn offset(self) -> u8 { + self + } + + fn size(self) -> u8 { + 1 + } +} + +impl OffsetSize for Range { + fn offset(self) -> u8 { + self.start + } + + fn size(self) -> u8 { + self.end - self.start + } +} + +pub(crate) trait U16Ext { + fn from_parts(low: u8, high: u8) -> Self; + + fn low(self) -> u8; + + fn high(self) -> u8; +} + +impl U16Ext for u16 { + fn from_parts(low: u8, high: u8) -> u16 { + ((high as u16) << 8) + low as u16 + } + + fn low(self) -> u8 { + (self & 0xff) as u8 + } + + fn high(self) -> u8 { + (self >> 8) as u8 + } +} + +#[derive(Clone, Copy)] +pub struct Mask; + +#[derive(Clone, Copy)] +pub struct R; + +#[derive(Clone, Copy)] +pub struct W; From 4af1cf88d29f29df52f8c9e7928409e60a91ffbc Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 5 Aug 2023 19:10:45 +0200 Subject: [PATCH 34/77] net-enc28j60: add example. --- examples/nrf52840/Cargo.toml | 4 + .../nrf52840/src/bin/ethernet_enc28j60.rs | 124 ++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 examples/nrf52840/src/bin/ethernet_enc28j60.rs diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 15fe22d3a..8c6f6bccf 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -12,6 +12,7 @@ nightly = [ "embassy-nrf/nightly", "embassy-net/nightly", "embassy-net-esp-hosted", + "embassy-net-enc28j60", "embassy-nrf/unstable-traits", "embassy-time/nightly", "embassy-time/unstable-traits", @@ -40,6 +41,7 @@ lora-phy = { version = "1", optional = true } lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"], optional = true } lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"], optional = true } embassy-net-esp-hosted = { version = "0.1.0", path = "../../embassy-net-esp-hosted", features = ["defmt"], optional = true } +embassy-net-enc28j60 = { version = "0.1.0", path = "../../embassy-net-enc28j60", features = ["defmt"], optional = true } defmt = "0.3" defmt-rtt = "0.4" @@ -54,7 +56,9 @@ rand = { version = "0.8.4", default-features = false } embedded-storage = "0.3.0" usbd-hid = "0.6.0" serde = { version = "1.0.136", default-features = false } +embedded-hal = { version = "1.0.0-alpha.11" } embedded-hal-async = { version = "0.2.0-alpha.2", optional = true } +embedded-hal-bus = { version = "0.1.0-alpha.3" } num-integer = { version = "0.1.45", default-features = false } microfft = "0.5.0" diff --git a/examples/nrf52840/src/bin/ethernet_enc28j60.rs b/examples/nrf52840/src/bin/ethernet_enc28j60.rs new file mode 100644 index 000000000..d1b796fab --- /dev/null +++ b/examples/nrf52840/src/bin/ethernet_enc28j60.rs @@ -0,0 +1,124 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_net::tcp::TcpSocket; +use embassy_net::{Stack, StackResources}; +use embassy_net_enc28j60::Enc28j60; +use embassy_nrf::gpio::{Level, Output, OutputDrive}; +use embassy_nrf::rng::Rng; +use embassy_nrf::spim::Spim; +use embassy_nrf::{bind_interrupts, peripherals, spim}; +use embassy_time::Delay; +use embedded_hal_bus::spi::ExclusiveDevice; +use embedded_io_async::Write; +use static_cell::make_static; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + SPIM3 => spim::InterruptHandler; + RNG => embassy_nrf::rng::InterruptHandler; +}); + +#[embassy_executor::task] +async fn net_task( + stack: &'static Stack< + Enc28j60< + ExclusiveDevice, Output<'static, peripherals::P0_15>, Delay>, + Output<'static, peripherals::P0_13>, + >, + >, +) -> ! { + stack.run().await +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + info!("running!"); + + let eth_sck = p.P0_20; + let eth_mosi = p.P0_22; + let eth_miso = p.P0_24; + let eth_cs = p.P0_15; + let eth_rst = p.P0_13; + let _eth_irq = p.P0_12; + + let mut config = spim::Config::default(); + config.frequency = spim::Frequency::M16; + let spi = spim::Spim::new(p.SPI3, Irqs, eth_sck, eth_miso, eth_mosi, config); + let cs = Output::new(eth_cs, Level::High, OutputDrive::Standard); + let spi = ExclusiveDevice::new(spi, cs, Delay); + + let rst = Output::new(eth_rst, Level::High, OutputDrive::Standard); + let mac_addr = [2, 3, 4, 5, 6, 7]; + let device = Enc28j60::new(spi, Some(rst), mac_addr); + + let config = embassy_net::Config::dhcpv4(Default::default()); + // let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 { + // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), + // dns_servers: Vec::new(), + // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), + // }); + + // Generate random seed + let mut rng = Rng::new(p.RNG, Irqs); + let mut seed = [0; 8]; + rng.blocking_fill_bytes(&mut seed); + let seed = u64::from_le_bytes(seed); + + // Init network stack + let stack = &*make_static!(Stack::new( + device, + config, + make_static!(StackResources::<2>::new()), + seed + )); + + unwrap!(spawner.spawn(net_task(stack))); + + // And now we can use it! + + let mut rx_buffer = [0; 4096]; + let mut tx_buffer = [0; 4096]; + let mut buf = [0; 4096]; + + loop { + let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + socket.set_timeout(Some(embassy_time::Duration::from_secs(10))); + + info!("Listening on TCP:1234..."); + if let Err(e) = socket.accept(1234).await { + warn!("accept error: {:?}", e); + continue; + } + + info!("Received connection from {:?}", socket.remote_endpoint()); + + loop { + let n = match socket.read(&mut buf).await { + Ok(0) => { + warn!("read EOF"); + break; + } + Ok(n) => n, + Err(e) => { + warn!("read error: {:?}", e); + break; + } + }; + + info!("rxd {:02x}", &buf[..n]); + + match socket.write_all(&buf[..n]).await { + Ok(()) => {} + Err(e) => { + warn!("write error: {:?}", e); + break; + } + }; + } + } +} From 253b28debaf13d839087a2b9160f416f4be22438 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 12 Aug 2023 03:07:13 +0200 Subject: [PATCH 35/77] net-enc28j60: fix PHY read unreliable due to missing dummy byte. --- embassy-net-enc28j60/src/lib.rs | 19 ++++++++++++------- embassy-net-enc28j60/src/phy.rs | 3 +-- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/embassy-net-enc28j60/src/lib.rs b/embassy-net-enc28j60/src/lib.rs index 4f129b6b2..d77dc2c5e 100644 --- a/embassy-net-enc28j60/src/lib.rs +++ b/embassy-net-enc28j60/src/lib.rs @@ -361,10 +361,16 @@ where fn _read_control_register(&mut self, register: Register) -> u8 { self.change_bank(register); - let mut buffer = [Instruction::RCR.opcode() | register.addr(), 0]; - self.spi.transfer_in_place(&mut buffer).unwrap(); - - buffer[1] + if register.is_eth_register() { + let mut buffer = [Instruction::RCR.opcode() | register.addr(), 0]; + self.spi.transfer_in_place(&mut buffer).unwrap(); + buffer[1] + } else { + // MAC, MII regs need a dummy byte. + let mut buffer = [Instruction::RCR.opcode() | register.addr(), 0, 0]; + self.spi.transfer_in_place(&mut buffer).unwrap(); + buffer[2] + } } fn read_phy_register(&mut self, register: phy::Register) -> u16 { @@ -379,11 +385,10 @@ where // wait until the read operation finishes while self.read_control_register(bank3::Register::MISTAT) & 0b1 != 0 {} - let h = self.read_control_register(bank2::Register::MIRDH); - let l = self.read_control_register(bank2::Register::MIRDL); - self.write_control_register(bank2::Register::MICMD, bank2::MICMD::default().miird(0).bits()); + let l = self.read_control_register(bank2::Register::MIRDL); + let h = self.read_control_register(bank2::Register::MIRDH); (l as u16) | (h as u16) << 8 } diff --git a/embassy-net-enc28j60/src/phy.rs b/embassy-net-enc28j60/src/phy.rs index 7f62b5f27..89144ada3 100644 --- a/embassy-net-enc28j60/src/phy.rs +++ b/embassy-net-enc28j60/src/phy.rs @@ -30,7 +30,6 @@ register!(PHCON2, 0, u16, { }); register!(PHSTAT2, 0, u16, { - // Datasheet says it's bit 10, but it's actually bit 2 ?!?! #[doc = "Link Status bit"] - lstat @ 2, + lstat @ 10, }); From f7f75167accd1fb91238b05f30a280bf115baddf Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 15 Aug 2023 14:15:29 +0200 Subject: [PATCH 36/77] cleanup vscode settings. --- .vscode/settings.json | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 139b432f4..d48f7ba1e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,16 +6,21 @@ "rust-analyzer.check.allTargets": false, "rust-analyzer.check.noDefaultFeatures": true, "rust-analyzer.cargo.noDefaultFeatures": true, - "rust-analyzer.cargo.target": "thumbv7m-none-eabi", + "rust-analyzer.showUnlinkedFileNotification": false, + // uncomment the target of your chip. + //"rust-analyzer.cargo.target": "thumbv6m-none-eabi", + //"rust-analyzer.cargo.target": "thumbv7m-none-eabi", + "rust-analyzer.cargo.target": "thumbv7em-none-eabi", //"rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf", "rust-analyzer.cargo.features": [ - ///"nightly", + // Uncomment if the example has a "nightly" feature. + "nightly", ], "rust-analyzer.linkedProjects": [ - // Declare for the target you wish to develop - // "embassy-executor/Cargo.toml", - // "embassy-sync/Cargo.toml", - "examples/stm32wl/Cargo.toml", + // Uncomment ONE line for the chip you want to work on. + // This makes rust-analyzer work on the example crate and all its dependencies. + "examples/nrf52840/Cargo.toml", + // "examples/nrf52840-rtic/Cargo.toml", // "examples/nrf5340/Cargo.toml", // "examples/nrf-rtos-trace/Cargo.toml", // "examples/rp/Cargo.toml", @@ -41,5 +46,4 @@ // "examples/stm32wl/Cargo.toml", // "examples/wasm/Cargo.toml", ], - "rust-analyzer.showUnlinkedFileNotification": false, } \ No newline at end of file From ea9f887ee168aab502ab595aa460c7c0910ff6b9 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 15 Aug 2023 14:27:31 +0200 Subject: [PATCH 37/77] net-enc28j60: add docs, readme. --- embassy-net-enc28j60/README.md | 18 ++++++++++++++++++ embassy-net-enc28j60/src/lib.rs | 9 +++++++++ 2 files changed, 27 insertions(+) diff --git a/embassy-net-enc28j60/README.md b/embassy-net-enc28j60/README.md index e12d240c3..39011ca13 100644 --- a/embassy-net-enc28j60/README.md +++ b/embassy-net-enc28j60/README.md @@ -1 +1,19 @@ # `embassy-net-enc28j60` + +[`embassy-net`](https://crates.io/crates/embassy-net) integration for the Microchip ENC28J60 Ethernet chip. + +Based on [@japaric](https://github.com/japaric)'s [`enc28j60`](https://github.com/japaric/enc28j60) crate. + +## Interoperability + +This crate can run on any executor. + +## License + +This work is licensed under either of + +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or + http://www.apache.org/licenses/LICENSE-2.0) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. diff --git a/embassy-net-enc28j60/src/lib.rs b/embassy-net-enc28j60/src/lib.rs index d77dc2c5e..09e77bafd 100644 --- a/embassy-net-enc28j60/src/lib.rs +++ b/embassy-net-enc28j60/src/lib.rs @@ -1,5 +1,6 @@ #![no_std] #![doc = include_str!("../README.md")] +#![warn(missing_docs)] // must go first. mod fmt; @@ -43,6 +44,7 @@ const _TXND: u16 = 0x1fff; const MTU: usize = 1514; // 1500 IP + 14 ethernet header +/// ENC28J60 embassy-net driver pub struct Enc28j60 { mac_addr: [u8; 6], @@ -60,6 +62,10 @@ where S: SpiDevice, O: OutputPin, { + /// Create a new ENC28J60 driver instance. + /// + /// The RST pin is optional. If None, reset will be done with a SPI + /// soft reset command, instead of via the RST pin. pub fn new(spi: S, rst: Option, mac_addr: [u8; 6]) -> Self { let mut res = Self { mac_addr, @@ -300,6 +306,7 @@ where }*/ } + /// Get whether the link is up pub fn is_link_up(&mut self) -> bool { let bits = self.read_phy_register(phy::Register::PHSTAT2); phy::PHSTAT2(bits).lstat() == 1 @@ -659,6 +666,7 @@ where } } +/// embassy-net RX token. pub struct RxToken<'a> { buf: &'a mut [u8], } @@ -672,6 +680,7 @@ impl<'a> embassy_net_driver::RxToken for RxToken<'a> { } } +/// embassy-net TX token. pub struct TxToken<'a, S, O> where S: SpiDevice, From b6b44480457d272aace977e885d5dba252fd2bed Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 15 Aug 2023 15:13:03 +0200 Subject: [PATCH 38/77] ci/docs: build embassy-net-enc28j60. --- .github/ci/doc.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/ci/doc.sh b/.github/ci/doc.sh index e410ffa35..f829582ab 100755 --- a/.github/ci/doc.sh +++ b/.github/ci/doc.sh @@ -36,6 +36,7 @@ docserver-builder -i ./embassy-usb-logger -o webroot/crates/embassy-usb-logger/g docserver-builder -i ./cyw43 -o webroot/crates/cyw43/git.zup docserver-builder -i ./cyw43-pio -o webroot/crates/cyw43-pio/git.zup docserver-builder -i ./embassy-net-w5500 -o webroot/crates/embassy-net-w5500/git.zup +docserver-builder -i ./embassy-net-enc28j60 -o webroot/crates/embassy-net-enc28j60/git.zup docserver-builder -i ./embassy-net-esp-hosted -o webroot/crates/embassy-net-esp-hosted/git.zup docserver-builder -i ./embassy-stm32-wpan -o webroot/crates/embassy-stm32-wpan/git.zup --output-static webroot/static From b1ef009c6b08ae54595ca558f4571d430e99cb0b Mon Sep 17 00:00:00 2001 From: Scott Mabin Date: Mon, 14 Aug 2023 20:58:44 +0100 Subject: [PATCH 39/77] Add tcp capacity impls --- embassy-net/src/tcp.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index b4ce40945..ab22ffb70 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -93,6 +93,11 @@ impl<'a> TcpReader<'a> { { self.io.read_with(f).await } + + /// Return the maximum number of bytes inside the transmit buffer. + pub fn recv_capacity(&self) -> usize { + self.io.recv_capacity() + } } impl<'a> TcpWriter<'a> { @@ -122,6 +127,11 @@ impl<'a> TcpWriter<'a> { { self.io.write_with(f).await } + + /// Return the maximum number of bytes inside the transmit buffer. + pub fn send_capacity(&self) -> usize { + self.io.send_capacity() + } } impl<'a> TcpSocket<'a> { @@ -143,6 +153,16 @@ impl<'a> TcpSocket<'a> { } } + /// Return the maximum number of bytes inside the recv buffer. + pub fn recv_capacity(&self) -> usize { + self.io.recv_capacity() + } + + /// Return the maximum number of bytes inside the transmit buffer. + pub fn send_capacity(&self) -> usize { + self.io.send_capacity() + } + /// Call `f` with the largest contiguous slice of octets in the transmit buffer, /// and enqueue the amount of elements returned by `f`. /// @@ -478,6 +498,14 @@ impl<'d> TcpIo<'d> { }) .await } + + fn recv_capacity(&self) -> usize { + self.with(|s, _| s.recv_capacity()) + } + + fn send_capacity(&self) -> usize { + self.with(|s, _| s.send_capacity()) + } } #[cfg(feature = "nightly")] From c114ea024a2b74e3e220e6af1d8a494dd598b130 Mon Sep 17 00:00:00 2001 From: Scott Mabin Date: Tue, 15 Aug 2023 15:24:58 +0100 Subject: [PATCH 40/77] Add udp capacity impls --- embassy-net/src/udp.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs index 0d97b6db1..0a5a7b8f6 100644 --- a/embassy-net/src/udp.rs +++ b/embassy-net/src/udp.rs @@ -184,6 +184,26 @@ impl<'a> UdpSocket<'a> { pub fn may_recv(&self) -> bool { self.with(|s, _| s.can_recv()) } + + /// Return the maximum number packets the socket can receive. + pub fn packet_recv_capacity(&self) -> usize { + self.with(|s, _| s.packet_recv_capacity()) + } + + /// Return the maximum number packets the socket can receive. + pub fn packet_send_capacity(&self) -> usize { + self.with(|s, _| s.packet_send_capacity()) + } + + /// Return the maximum number of bytes inside the recv buffer. + pub fn payload_recv_capacity(&self) -> usize { + self.with(|s, _| s.payload_recv_capacity()) + } + + /// Return the maximum number of bytes inside the transmit buffer. + pub fn payload_send_capacity(&self) -> usize { + self.with(|s, _| s.payload_send_capacity()) + } } impl Drop for UdpSocket<'_> { From 098fcb14b504dea617c38e90410c639ecbcb6aa7 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 15 Aug 2023 16:47:45 +0200 Subject: [PATCH 41/77] net-w5500: simplify rx logic. --- embassy-net-w5500/src/device.rs | 38 ++++++++++----------------------- 1 file changed, 11 insertions(+), 27 deletions(-) diff --git a/embassy-net-w5500/src/device.rs b/embassy-net-w5500/src/device.rs index 9874df0d6..4933625e2 100644 --- a/embassy-net-w5500/src/device.rs +++ b/embassy-net-w5500/src/device.rs @@ -58,20 +58,11 @@ impl W5500 { } /// Read bytes from the RX buffer. Returns the number of bytes read. - async fn read_bytes(&mut self, buffer: &mut [u8], offset: u16) -> Result { - let rx_size = socket::get_rx_size(&mut self.bus).await? as usize; + async fn read_bytes(&mut self, read_ptr: &mut u16, buffer: &mut [u8]) -> Result<(), SPI::Error> { + self.bus.read_frame(RegisterBlock::RxBuf, *read_ptr, buffer).await?; + *read_ptr = (*read_ptr).wrapping_add(buffer.len() as u16); - let read_buffer = if rx_size > buffer.len() + offset as usize { - buffer - } else { - &mut buffer[..rx_size - offset as usize] - }; - - let read_ptr = socket::get_rx_read_ptr(&mut self.bus).await?.wrapping_add(offset); - self.bus.read_frame(RegisterBlock::RxBuf, read_ptr, read_buffer).await?; - socket::set_rx_read_ptr(&mut self.bus, read_ptr.wrapping_add(read_buffer.len() as u16)).await?; - - Ok(read_buffer.len()) + Ok(()) } /// Read an ethernet frame from the device. Returns the number of bytes read. @@ -83,31 +74,24 @@ impl W5500 { socket::reset_interrupt(&mut self.bus, socket::Interrupt::Receive).await?; + let mut read_ptr = socket::get_rx_read_ptr(&mut self.bus).await?; + // First two bytes gives the size of the received ethernet frame let expected_frame_size: usize = { let mut frame_bytes = [0u8; 2]; - assert!(self.read_bytes(&mut frame_bytes[..], 0).await? == 2); + self.read_bytes(&mut read_ptr, &mut frame_bytes).await?; u16::from_be_bytes(frame_bytes) as usize - 2 }; // Read the ethernet frame - let read_buffer = if frame.len() > expected_frame_size { - &mut frame[..expected_frame_size] - } else { - frame - }; - - let recvd_frame_size = self.read_bytes(read_buffer, 2).await?; + self.read_bytes(&mut read_ptr, &mut frame[..expected_frame_size]) + .await?; // Register RX as completed + socket::set_rx_read_ptr(&mut self.bus, read_ptr).await?; socket::command(&mut self.bus, socket::Command::Receive).await?; - // If the whole frame wasn't read, drop it - if recvd_frame_size < expected_frame_size { - Ok(0) - } else { - Ok(recvd_frame_size) - } + Ok(expected_frame_size) } /// Write an ethernet frame to the device. Returns number of bytes written From 8655ba110c95777c3edab3aabef34ae71d64957a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Tue, 15 Aug 2023 19:13:36 +0200 Subject: [PATCH 42/77] Fix typo --- embassy-sync/src/blocking_mutex/raw.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-sync/src/blocking_mutex/raw.rs b/embassy-sync/src/blocking_mutex/raw.rs index 15796f1b2..a8afcad34 100644 --- a/embassy-sync/src/blocking_mutex/raw.rs +++ b/embassy-sync/src/blocking_mutex/raw.rs @@ -11,7 +11,7 @@ use core::marker::PhantomData; /// /// Note that, unlike other mutexes, implementations only guarantee no /// concurrent access from other threads: concurrent access from the current -/// thread is allwed. For example, it's possible to lock the same mutex multiple times reentrantly. +/// thread is allowed. For example, it's possible to lock the same mutex multiple times reentrantly. /// /// Therefore, locking a `RawMutex` is only enough to guarantee safe shared (`&`) access /// to the data, it is not enough to guarantee exclusive (`&mut`) access. From 46f671ae420f27b527edf03756d1da76d86216c7 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 15 Aug 2023 22:47:03 +0200 Subject: [PATCH 43/77] rp: fix async spi read sometimes hanging. --- embassy-rp/src/dma.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index c8f741804..45ca21a75 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -76,7 +76,8 @@ pub unsafe fn write<'a, C: Channel, W: Word>( ) } -static DUMMY: u32 = 0; +// static mut so that this is allocated in RAM. +static mut DUMMY: u32 = 0; pub unsafe fn write_repeated<'a, C: Channel, W: Word>( ch: impl Peripheral

+ 'a, @@ -86,7 +87,7 @@ pub unsafe fn write_repeated<'a, C: Channel, W: Word>( ) -> Transfer<'a, C> { copy_inner( ch, - &DUMMY as *const u32, + &mut DUMMY as *const u32, to as *mut u32, len, W::size(), From c367b84ee53b9d7a31f442f962c939f822aadb0d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 15 Aug 2023 17:11:24 +0200 Subject: [PATCH 44/77] net-w5500: add Address type. --- embassy-net-w5500/src/device.rs | 41 +++++++++++++------------------- embassy-net-w5500/src/socket.rs | 42 ++++++++++++++++----------------- embassy-net-w5500/src/spi.rs | 14 ++++++----- 3 files changed, 44 insertions(+), 53 deletions(-) diff --git a/embassy-net-w5500/src/device.rs b/embassy-net-w5500/src/device.rs index 4933625e2..1793baaa3 100644 --- a/embassy-net-w5500/src/device.rs +++ b/embassy-net-w5500/src/device.rs @@ -1,12 +1,12 @@ use embedded_hal_async::spi::SpiDevice; use crate::socket; -use crate::spi::SpiInterface; +use crate::spi::{Address, SpiInterface}; -pub const MODE: u16 = 0x00; -pub const MAC: u16 = 0x09; -pub const SOCKET_INTR: u16 = 0x18; -pub const PHY_CFG: u16 = 0x2E; +pub const MODE: Address = (RegisterBlock::Common, 0x00); +pub const MAC: Address = (RegisterBlock::Common, 0x09); +pub const SOCKET_INTR: Address = (RegisterBlock::Common, 0x18); +pub const PHY_CFG: Address = (RegisterBlock::Common, 0x2E); #[repr(u8)] pub enum RegisterBlock { @@ -28,30 +28,24 @@ impl W5500 { pub async fn new(spi: SPI, mac_addr: [u8; 6]) -> Result, SPI::Error> { let mut bus = SpiInterface(spi); // Reset device - bus.write_frame(RegisterBlock::Common, MODE, &[0x80]).await?; + bus.write_frame(MODE, &[0x80]).await?; // Enable interrupt pin - bus.write_frame(RegisterBlock::Common, SOCKET_INTR, &[0x01]).await?; + bus.write_frame(SOCKET_INTR, &[0x01]).await?; // Enable receive interrupt - bus.write_frame( - RegisterBlock::Socket0, - socket::SOCKET_INTR_MASK, - &[socket::Interrupt::Receive as u8], - ) - .await?; + bus.write_frame(socket::SOCKET_INTR_MASK, &[socket::Interrupt::Receive as u8]) + .await?; // Set MAC address - bus.write_frame(RegisterBlock::Common, MAC, &mac_addr).await?; + bus.write_frame(MAC, &mac_addr).await?; // Set the raw socket RX/TX buffer sizes to 16KB - bus.write_frame(RegisterBlock::Socket0, socket::TXBUF_SIZE, &[16]) - .await?; - bus.write_frame(RegisterBlock::Socket0, socket::RXBUF_SIZE, &[16]) - .await?; + bus.write_frame(socket::TXBUF_SIZE, &[16]).await?; + bus.write_frame(socket::RXBUF_SIZE, &[16]).await?; // MACRAW mode with MAC filtering. let mode: u8 = (1 << 2) | (1 << 7); - bus.write_frame(RegisterBlock::Socket0, socket::MODE, &[mode]).await?; + bus.write_frame(socket::MODE, &[mode]).await?; socket::command(&mut bus, socket::Command::Open).await?; Ok(Self { bus }) @@ -59,7 +53,7 @@ impl W5500 { /// Read bytes from the RX buffer. Returns the number of bytes read. async fn read_bytes(&mut self, read_ptr: &mut u16, buffer: &mut [u8]) -> Result<(), SPI::Error> { - self.bus.read_frame(RegisterBlock::RxBuf, *read_ptr, buffer).await?; + self.bus.read_frame((RegisterBlock::RxBuf, *read_ptr), buffer).await?; *read_ptr = (*read_ptr).wrapping_add(buffer.len() as u16); Ok(()) @@ -98,7 +92,7 @@ impl W5500 { pub async fn write_frame(&mut self, frame: &[u8]) -> Result { while socket::get_tx_free_size(&mut self.bus).await? < frame.len() as u16 {} let write_ptr = socket::get_tx_write_ptr(&mut self.bus).await?; - self.bus.write_frame(RegisterBlock::TxBuf, write_ptr, frame).await?; + self.bus.write_frame((RegisterBlock::TxBuf, write_ptr), frame).await?; socket::set_tx_write_ptr(&mut self.bus, write_ptr.wrapping_add(frame.len() as u16)).await?; socket::command(&mut self.bus, socket::Command::Send).await?; Ok(frame.len()) @@ -106,10 +100,7 @@ impl W5500 { pub async fn is_link_up(&mut self) -> bool { let mut link = [0]; - self.bus - .read_frame(RegisterBlock::Common, PHY_CFG, &mut link) - .await - .ok(); + self.bus.read_frame(PHY_CFG, &mut link).await.ok(); link[0] & 1 == 1 } } diff --git a/embassy-net-w5500/src/socket.rs b/embassy-net-w5500/src/socket.rs index 3d65583c1..1c96cca1f 100644 --- a/embassy-net-w5500/src/socket.rs +++ b/embassy-net-w5500/src/socket.rs @@ -1,17 +1,18 @@ use embedded_hal_async::spi::SpiDevice; use crate::device::RegisterBlock; -use crate::spi::SpiInterface; +use crate::spi::{Address, SpiInterface}; -pub const MODE: u16 = 0x00; -pub const COMMAND: u16 = 0x01; -pub const RXBUF_SIZE: u16 = 0x1E; -pub const TXBUF_SIZE: u16 = 0x1F; -pub const TX_FREE_SIZE: u16 = 0x20; -pub const TX_DATA_WRITE_PTR: u16 = 0x24; -pub const RECVD_SIZE: u16 = 0x26; -pub const RX_DATA_READ_PTR: u16 = 0x28; -pub const SOCKET_INTR_MASK: u16 = 0x2C; +pub const MODE: Address = (RegisterBlock::Socket0, 0x00); +pub const COMMAND: Address = (RegisterBlock::Socket0, 0x01); +pub const RXBUF_SIZE: Address = (RegisterBlock::Socket0, 0x1E); +pub const TXBUF_SIZE: Address = (RegisterBlock::Socket0, 0x1F); +pub const TX_FREE_SIZE: Address = (RegisterBlock::Socket0, 0x20); +pub const TX_DATA_WRITE_PTR: Address = (RegisterBlock::Socket0, 0x24); +pub const RECVD_SIZE: Address = (RegisterBlock::Socket0, 0x26); +pub const RX_DATA_READ_PTR: Address = (RegisterBlock::Socket0, 0x28); +pub const SOCKET_INTR_MASK: Address = (RegisterBlock::Socket0, 0x2C); +pub const INTR: Address = (RegisterBlock::Socket0, 0x02); #[repr(u8)] pub enum Command { @@ -20,7 +21,6 @@ pub enum Command { Receive = 0x40, } -pub const INTR: u16 = 0x02; #[repr(u8)] pub enum Interrupt { Receive = 0b00100_u8, @@ -28,45 +28,43 @@ pub enum Interrupt { pub async fn reset_interrupt(bus: &mut SpiInterface, code: Interrupt) -> Result<(), SPI::Error> { let data = [code as u8]; - bus.write_frame(RegisterBlock::Socket0, INTR, &data).await + bus.write_frame(INTR, &data).await } pub async fn get_tx_write_ptr(bus: &mut SpiInterface) -> Result { let mut data = [0u8; 2]; - bus.read_frame(RegisterBlock::Socket0, TX_DATA_WRITE_PTR, &mut data) - .await?; + bus.read_frame(TX_DATA_WRITE_PTR, &mut data).await?; Ok(u16::from_be_bytes(data)) } pub async fn set_tx_write_ptr(bus: &mut SpiInterface, ptr: u16) -> Result<(), SPI::Error> { let data = ptr.to_be_bytes(); - bus.write_frame(RegisterBlock::Socket0, TX_DATA_WRITE_PTR, &data).await + bus.write_frame(TX_DATA_WRITE_PTR, &data).await } pub async fn get_rx_read_ptr(bus: &mut SpiInterface) -> Result { let mut data = [0u8; 2]; - bus.read_frame(RegisterBlock::Socket0, RX_DATA_READ_PTR, &mut data) - .await?; + bus.read_frame(RX_DATA_READ_PTR, &mut data).await?; Ok(u16::from_be_bytes(data)) } pub async fn set_rx_read_ptr(bus: &mut SpiInterface, ptr: u16) -> Result<(), SPI::Error> { let data = ptr.to_be_bytes(); - bus.write_frame(RegisterBlock::Socket0, RX_DATA_READ_PTR, &data).await + bus.write_frame(RX_DATA_READ_PTR, &data).await } pub async fn command(bus: &mut SpiInterface, command: Command) -> Result<(), SPI::Error> { let data = [command as u8]; - bus.write_frame(RegisterBlock::Socket0, COMMAND, &data).await + bus.write_frame(COMMAND, &data).await } pub async fn get_rx_size(bus: &mut SpiInterface) -> Result { loop { // Wait until two sequential reads are equal let mut res0 = [0u8; 2]; - bus.read_frame(RegisterBlock::Socket0, RECVD_SIZE, &mut res0).await?; + bus.read_frame(RECVD_SIZE, &mut res0).await?; let mut res1 = [0u8; 2]; - bus.read_frame(RegisterBlock::Socket0, RECVD_SIZE, &mut res1).await?; + bus.read_frame(RECVD_SIZE, &mut res1).await?; if res0 == res1 { break Ok(u16::from_be_bytes(res0)); } @@ -75,6 +73,6 @@ pub async fn get_rx_size(bus: &mut SpiInterface) -> Result< pub async fn get_tx_free_size(bus: &mut SpiInterface) -> Result { let mut data = [0; 2]; - bus.read_frame(RegisterBlock::Socket0, TX_FREE_SIZE, &mut data).await?; + bus.read_frame(TX_FREE_SIZE, &mut data).await?; Ok(u16::from_be_bytes(data)) } diff --git a/embassy-net-w5500/src/spi.rs b/embassy-net-w5500/src/spi.rs index 07749d6be..316c6521e 100644 --- a/embassy-net-w5500/src/spi.rs +++ b/embassy-net-w5500/src/spi.rs @@ -2,14 +2,16 @@ use embedded_hal_async::spi::{Operation, SpiDevice}; use crate::device::RegisterBlock; +pub type Address = (RegisterBlock, u16); + #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct SpiInterface(pub SPI); impl SpiInterface { - pub async fn read_frame(&mut self, block: RegisterBlock, address: u16, data: &mut [u8]) -> Result<(), SPI::Error> { - let address_phase = address.to_be_bytes(); - let control_phase = [(block as u8) << 3]; + pub async fn read_frame(&mut self, address: Address, data: &mut [u8]) -> Result<(), SPI::Error> { + let address_phase = address.1.to_be_bytes(); + let control_phase = [(address.0 as u8) << 3]; let operations = &mut [ Operation::Write(&address_phase), Operation::Write(&control_phase), @@ -18,9 +20,9 @@ impl SpiInterface { self.0.transaction(operations).await } - pub async fn write_frame(&mut self, block: RegisterBlock, address: u16, data: &[u8]) -> Result<(), SPI::Error> { - let address_phase = address.to_be_bytes(); - let control_phase = [(block as u8) << 3 | 0b0000_0100]; + pub async fn write_frame(&mut self, address: Address, data: &[u8]) -> Result<(), SPI::Error> { + let address_phase = address.1.to_be_bytes(); + let control_phase = [(address.0 as u8) << 3 | 0b0000_0100]; let data_phase = data; let operations = &mut [ Operation::Write(&address_phase[..]), From a436bd068f6059fa073a9f1f246a77e1ce38dd31 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 15 Aug 2023 17:20:41 +0200 Subject: [PATCH 45/77] net-w5500: inline socket into device. --- embassy-net-w5500/src/device.rs | 124 +++++++++++++++++++++++++------- embassy-net-w5500/src/lib.rs | 1 - embassy-net-w5500/src/socket.rs | 78 -------------------- 3 files changed, 99 insertions(+), 104 deletions(-) delete mode 100644 embassy-net-w5500/src/socket.rs diff --git a/embassy-net-w5500/src/device.rs b/embassy-net-w5500/src/device.rs index 1793baaa3..cf451b334 100644 --- a/embassy-net-w5500/src/device.rs +++ b/embassy-net-w5500/src/device.rs @@ -1,12 +1,34 @@ use embedded_hal_async::spi::SpiDevice; -use crate::socket; use crate::spi::{Address, SpiInterface}; -pub const MODE: Address = (RegisterBlock::Common, 0x00); -pub const MAC: Address = (RegisterBlock::Common, 0x09); -pub const SOCKET_INTR: Address = (RegisterBlock::Common, 0x18); -pub const PHY_CFG: Address = (RegisterBlock::Common, 0x2E); +pub const COMMON_MODE: Address = (RegisterBlock::Common, 0x00); +pub const COMMON_MAC: Address = (RegisterBlock::Common, 0x09); +pub const COMMON_SOCKET_INTR: Address = (RegisterBlock::Common, 0x18); +pub const COMMON_PHY_CFG: Address = (RegisterBlock::Common, 0x2E); + +pub const SOCKET_MODE: Address = (RegisterBlock::Socket0, 0x00); +pub const SOCKET_COMMAND: Address = (RegisterBlock::Socket0, 0x01); +pub const SOCKET_RXBUF_SIZE: Address = (RegisterBlock::Socket0, 0x1E); +pub const SOCKET_TXBUF_SIZE: Address = (RegisterBlock::Socket0, 0x1F); +pub const SOCKET_TX_FREE_SIZE: Address = (RegisterBlock::Socket0, 0x20); +pub const SOCKET_TX_DATA_WRITE_PTR: Address = (RegisterBlock::Socket0, 0x24); +pub const SOCKET_RECVD_SIZE: Address = (RegisterBlock::Socket0, 0x26); +pub const SOCKET_RX_DATA_READ_PTR: Address = (RegisterBlock::Socket0, 0x28); +pub const SOCKET_INTR_MASK: Address = (RegisterBlock::Socket0, 0x2C); +pub const SOCKET_INTR: Address = (RegisterBlock::Socket0, 0x02); + +#[repr(u8)] +pub enum Command { + Open = 0x01, + Send = 0x20, + Receive = 0x40, +} + +#[repr(u8)] +pub enum Interrupt { + Receive = 0b00100_u8, +} #[repr(u8)] pub enum RegisterBlock { @@ -28,27 +50,78 @@ impl W5500 { pub async fn new(spi: SPI, mac_addr: [u8; 6]) -> Result, SPI::Error> { let mut bus = SpiInterface(spi); // Reset device - bus.write_frame(MODE, &[0x80]).await?; + bus.write_frame(COMMON_MODE, &[0x80]).await?; // Enable interrupt pin - bus.write_frame(SOCKET_INTR, &[0x01]).await?; + bus.write_frame(COMMON_SOCKET_INTR, &[0x01]).await?; // Enable receive interrupt - bus.write_frame(socket::SOCKET_INTR_MASK, &[socket::Interrupt::Receive as u8]) - .await?; + bus.write_frame(SOCKET_INTR_MASK, &[Interrupt::Receive as u8]).await?; // Set MAC address - bus.write_frame(MAC, &mac_addr).await?; + bus.write_frame(COMMON_MAC, &mac_addr).await?; // Set the raw socket RX/TX buffer sizes to 16KB - bus.write_frame(socket::TXBUF_SIZE, &[16]).await?; - bus.write_frame(socket::RXBUF_SIZE, &[16]).await?; + bus.write_frame(SOCKET_TXBUF_SIZE, &[16]).await?; + bus.write_frame(SOCKET_RXBUF_SIZE, &[16]).await?; // MACRAW mode with MAC filtering. let mode: u8 = (1 << 2) | (1 << 7); - bus.write_frame(socket::MODE, &[mode]).await?; - socket::command(&mut bus, socket::Command::Open).await?; + bus.write_frame(SOCKET_MODE, &[mode]).await?; + let mut this = Self { bus }; + this.command(Command::Open).await?; - Ok(Self { bus }) + Ok(this) + } + + async fn reset_interrupt(&mut self, code: Interrupt) -> Result<(), SPI::Error> { + let data = [code as u8]; + self.bus.write_frame(SOCKET_INTR, &data).await + } + + async fn get_tx_write_ptr(&mut self) -> Result { + let mut data = [0u8; 2]; + self.bus.read_frame(SOCKET_TX_DATA_WRITE_PTR, &mut data).await?; + Ok(u16::from_be_bytes(data)) + } + + async fn set_tx_write_ptr(&mut self, ptr: u16) -> Result<(), SPI::Error> { + let data = ptr.to_be_bytes(); + self.bus.write_frame(SOCKET_TX_DATA_WRITE_PTR, &data).await + } + + async fn get_rx_read_ptr(&mut self) -> Result { + let mut data = [0u8; 2]; + self.bus.read_frame(SOCKET_RX_DATA_READ_PTR, &mut data).await?; + Ok(u16::from_be_bytes(data)) + } + + async fn set_rx_read_ptr(&mut self, ptr: u16) -> Result<(), SPI::Error> { + let data = ptr.to_be_bytes(); + self.bus.write_frame(SOCKET_RX_DATA_READ_PTR, &data).await + } + + async fn command(&mut self, command: Command) -> Result<(), SPI::Error> { + let data = [command as u8]; + self.bus.write_frame(SOCKET_COMMAND, &data).await + } + + async fn get_rx_size(&mut self) -> Result { + loop { + // Wait until two sequential reads are equal + let mut res0 = [0u8; 2]; + self.bus.read_frame(SOCKET_RECVD_SIZE, &mut res0).await?; + let mut res1 = [0u8; 2]; + self.bus.read_frame(SOCKET_RECVD_SIZE, &mut res1).await?; + if res0 == res1 { + break Ok(u16::from_be_bytes(res0)); + } + } + } + + async fn get_tx_free_size(&mut self) -> Result { + let mut data = [0; 2]; + self.bus.read_frame(SOCKET_TX_FREE_SIZE, &mut data).await?; + Ok(u16::from_be_bytes(data)) } /// Read bytes from the RX buffer. Returns the number of bytes read. @@ -61,14 +134,14 @@ impl W5500 { /// Read an ethernet frame from the device. Returns the number of bytes read. pub async fn read_frame(&mut self, frame: &mut [u8]) -> Result { - let rx_size = socket::get_rx_size(&mut self.bus).await? as usize; + let rx_size = self.get_rx_size().await? as usize; if rx_size == 0 { return Ok(0); } - socket::reset_interrupt(&mut self.bus, socket::Interrupt::Receive).await?; + self.reset_interrupt(Interrupt::Receive).await?; - let mut read_ptr = socket::get_rx_read_ptr(&mut self.bus).await?; + let mut read_ptr = self.get_rx_read_ptr().await?; // First two bytes gives the size of the received ethernet frame let expected_frame_size: usize = { @@ -82,25 +155,26 @@ impl W5500 { .await?; // Register RX as completed - socket::set_rx_read_ptr(&mut self.bus, read_ptr).await?; - socket::command(&mut self.bus, socket::Command::Receive).await?; + self.set_rx_read_ptr(read_ptr).await?; + self.command(Command::Receive).await?; Ok(expected_frame_size) } /// Write an ethernet frame to the device. Returns number of bytes written pub async fn write_frame(&mut self, frame: &[u8]) -> Result { - while socket::get_tx_free_size(&mut self.bus).await? < frame.len() as u16 {} - let write_ptr = socket::get_tx_write_ptr(&mut self.bus).await?; + while self.get_tx_free_size().await? < frame.len() as u16 {} + let write_ptr = self.get_tx_write_ptr().await?; self.bus.write_frame((RegisterBlock::TxBuf, write_ptr), frame).await?; - socket::set_tx_write_ptr(&mut self.bus, write_ptr.wrapping_add(frame.len() as u16)).await?; - socket::command(&mut self.bus, socket::Command::Send).await?; + self.set_tx_write_ptr(write_ptr.wrapping_add(frame.len() as u16)) + .await?; + self.command(Command::Send).await?; Ok(frame.len()) } pub async fn is_link_up(&mut self) -> bool { let mut link = [0]; - self.bus.read_frame(PHY_CFG, &mut link).await.ok(); + self.bus.read_frame(COMMON_PHY_CFG, &mut link).await.ok(); link[0] & 1 == 1 } } diff --git a/embassy-net-w5500/src/lib.rs b/embassy-net-w5500/src/lib.rs index 52494b443..3c54777d8 100644 --- a/embassy-net-w5500/src/lib.rs +++ b/embassy-net-w5500/src/lib.rs @@ -2,7 +2,6 @@ #![no_std] mod device; -mod socket; mod spi; use embassy_futures::select::{select, Either}; diff --git a/embassy-net-w5500/src/socket.rs b/embassy-net-w5500/src/socket.rs deleted file mode 100644 index 1c96cca1f..000000000 --- a/embassy-net-w5500/src/socket.rs +++ /dev/null @@ -1,78 +0,0 @@ -use embedded_hal_async::spi::SpiDevice; - -use crate::device::RegisterBlock; -use crate::spi::{Address, SpiInterface}; - -pub const MODE: Address = (RegisterBlock::Socket0, 0x00); -pub const COMMAND: Address = (RegisterBlock::Socket0, 0x01); -pub const RXBUF_SIZE: Address = (RegisterBlock::Socket0, 0x1E); -pub const TXBUF_SIZE: Address = (RegisterBlock::Socket0, 0x1F); -pub const TX_FREE_SIZE: Address = (RegisterBlock::Socket0, 0x20); -pub const TX_DATA_WRITE_PTR: Address = (RegisterBlock::Socket0, 0x24); -pub const RECVD_SIZE: Address = (RegisterBlock::Socket0, 0x26); -pub const RX_DATA_READ_PTR: Address = (RegisterBlock::Socket0, 0x28); -pub const SOCKET_INTR_MASK: Address = (RegisterBlock::Socket0, 0x2C); -pub const INTR: Address = (RegisterBlock::Socket0, 0x02); - -#[repr(u8)] -pub enum Command { - Open = 0x01, - Send = 0x20, - Receive = 0x40, -} - -#[repr(u8)] -pub enum Interrupt { - Receive = 0b00100_u8, -} - -pub async fn reset_interrupt(bus: &mut SpiInterface, code: Interrupt) -> Result<(), SPI::Error> { - let data = [code as u8]; - bus.write_frame(INTR, &data).await -} - -pub async fn get_tx_write_ptr(bus: &mut SpiInterface) -> Result { - let mut data = [0u8; 2]; - bus.read_frame(TX_DATA_WRITE_PTR, &mut data).await?; - Ok(u16::from_be_bytes(data)) -} - -pub async fn set_tx_write_ptr(bus: &mut SpiInterface, ptr: u16) -> Result<(), SPI::Error> { - let data = ptr.to_be_bytes(); - bus.write_frame(TX_DATA_WRITE_PTR, &data).await -} - -pub async fn get_rx_read_ptr(bus: &mut SpiInterface) -> Result { - let mut data = [0u8; 2]; - bus.read_frame(RX_DATA_READ_PTR, &mut data).await?; - Ok(u16::from_be_bytes(data)) -} - -pub async fn set_rx_read_ptr(bus: &mut SpiInterface, ptr: u16) -> Result<(), SPI::Error> { - let data = ptr.to_be_bytes(); - bus.write_frame(RX_DATA_READ_PTR, &data).await -} - -pub async fn command(bus: &mut SpiInterface, command: Command) -> Result<(), SPI::Error> { - let data = [command as u8]; - bus.write_frame(COMMAND, &data).await -} - -pub async fn get_rx_size(bus: &mut SpiInterface) -> Result { - loop { - // Wait until two sequential reads are equal - let mut res0 = [0u8; 2]; - bus.read_frame(RECVD_SIZE, &mut res0).await?; - let mut res1 = [0u8; 2]; - bus.read_frame(RECVD_SIZE, &mut res1).await?; - if res0 == res1 { - break Ok(u16::from_be_bytes(res0)); - } - } -} - -pub async fn get_tx_free_size(bus: &mut SpiInterface) -> Result { - let mut data = [0; 2]; - bus.read_frame(TX_FREE_SIZE, &mut data).await?; - Ok(u16::from_be_bytes(data)) -} From 76276c326aaa4fd64a73253a480e2ea22f5ff740 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 15 Aug 2023 17:50:56 +0200 Subject: [PATCH 46/77] net-w5500: extract chip-specific stuff to a trait. --- embassy-net-w5500/src/chip/mod.rs | 41 ++++++++ embassy-net-w5500/src/chip/w5500.rs | 67 +++++++++++++ embassy-net-w5500/src/device.rs | 94 +++++++++---------- embassy-net-w5500/src/lib.rs | 19 ++-- embassy-net-w5500/src/spi.rs | 34 ------- .../rp/src/bin/ethernet_w5500_multisocket.rs | 2 + .../rp/src/bin/ethernet_w5500_tcp_client.rs | 2 + .../rp/src/bin/ethernet_w5500_tcp_server.rs | 2 + examples/rp/src/bin/ethernet_w5500_udp.rs | 2 + 9 files changed, 169 insertions(+), 94 deletions(-) create mode 100644 embassy-net-w5500/src/chip/mod.rs create mode 100644 embassy-net-w5500/src/chip/w5500.rs delete mode 100644 embassy-net-w5500/src/spi.rs diff --git a/embassy-net-w5500/src/chip/mod.rs b/embassy-net-w5500/src/chip/mod.rs new file mode 100644 index 000000000..b7d550e26 --- /dev/null +++ b/embassy-net-w5500/src/chip/mod.rs @@ -0,0 +1,41 @@ +mod w5500; +pub use w5500::W5500; + +pub(crate) mod sealed { + use embedded_hal_async::spi::SpiDevice; + + pub trait Chip { + type Address; + + const COMMON_MODE: Self::Address; + const COMMON_MAC: Self::Address; + const COMMON_SOCKET_INTR: Self::Address; + const COMMON_PHY_CFG: Self::Address; + const SOCKET_MODE: Self::Address; + const SOCKET_COMMAND: Self::Address; + const SOCKET_RXBUF_SIZE: Self::Address; + const SOCKET_TXBUF_SIZE: Self::Address; + const SOCKET_TX_FREE_SIZE: Self::Address; + const SOCKET_TX_DATA_WRITE_PTR: Self::Address; + const SOCKET_RECVD_SIZE: Self::Address; + const SOCKET_RX_DATA_READ_PTR: Self::Address; + const SOCKET_INTR_MASK: Self::Address; + const SOCKET_INTR: Self::Address; + + fn rx_addr(addr: u16) -> Self::Address; + fn tx_addr(addr: u16) -> Self::Address; + + async fn bus_read( + spi: &mut SPI, + address: Self::Address, + data: &mut [u8], + ) -> Result<(), SPI::Error>; + async fn bus_write( + spi: &mut SPI, + address: Self::Address, + data: &[u8], + ) -> Result<(), SPI::Error>; + } +} + +pub trait Chip: sealed::Chip {} diff --git a/embassy-net-w5500/src/chip/w5500.rs b/embassy-net-w5500/src/chip/w5500.rs new file mode 100644 index 000000000..f514e12a7 --- /dev/null +++ b/embassy-net-w5500/src/chip/w5500.rs @@ -0,0 +1,67 @@ +use embedded_hal_async::spi::{Operation, SpiDevice}; + +#[repr(u8)] +pub enum RegisterBlock { + Common = 0x00, + Socket0 = 0x01, + TxBuf = 0x02, + RxBuf = 0x03, +} + +pub enum W5500 {} + +impl super::Chip for W5500 {} +impl super::sealed::Chip for W5500 { + type Address = (RegisterBlock, u16); + + const COMMON_MODE: Self::Address = (RegisterBlock::Common, 0x00); + const COMMON_MAC: Self::Address = (RegisterBlock::Common, 0x09); + const COMMON_SOCKET_INTR: Self::Address = (RegisterBlock::Common, 0x18); + const COMMON_PHY_CFG: Self::Address = (RegisterBlock::Common, 0x2E); + + const SOCKET_MODE: Self::Address = (RegisterBlock::Socket0, 0x00); + const SOCKET_COMMAND: Self::Address = (RegisterBlock::Socket0, 0x01); + const SOCKET_RXBUF_SIZE: Self::Address = (RegisterBlock::Socket0, 0x1E); + const SOCKET_TXBUF_SIZE: Self::Address = (RegisterBlock::Socket0, 0x1F); + const SOCKET_TX_FREE_SIZE: Self::Address = (RegisterBlock::Socket0, 0x20); + const SOCKET_TX_DATA_WRITE_PTR: Self::Address = (RegisterBlock::Socket0, 0x24); + const SOCKET_RECVD_SIZE: Self::Address = (RegisterBlock::Socket0, 0x26); + const SOCKET_RX_DATA_READ_PTR: Self::Address = (RegisterBlock::Socket0, 0x28); + const SOCKET_INTR_MASK: Self::Address = (RegisterBlock::Socket0, 0x2C); + const SOCKET_INTR: Self::Address = (RegisterBlock::Socket0, 0x02); + + fn rx_addr(addr: u16) -> Self::Address { + (RegisterBlock::RxBuf, addr) + } + + fn tx_addr(addr: u16) -> Self::Address { + (RegisterBlock::TxBuf, addr) + } + + async fn bus_read( + spi: &mut SPI, + address: Self::Address, + data: &mut [u8], + ) -> Result<(), SPI::Error> { + let address_phase = address.1.to_be_bytes(); + let control_phase = [(address.0 as u8) << 3]; + let operations = &mut [ + Operation::Write(&address_phase), + Operation::Write(&control_phase), + Operation::TransferInPlace(data), + ]; + spi.transaction(operations).await + } + + async fn bus_write(spi: &mut SPI, address: Self::Address, data: &[u8]) -> Result<(), SPI::Error> { + let address_phase = address.1.to_be_bytes(); + let control_phase = [(address.0 as u8) << 3 | 0b0000_0100]; + let data_phase = data; + let operations = &mut [ + Operation::Write(&address_phase[..]), + Operation::Write(&control_phase), + Operation::Write(&data_phase), + ]; + spi.transaction(operations).await + } +} diff --git a/embassy-net-w5500/src/device.rs b/embassy-net-w5500/src/device.rs index cf451b334..a6ee8f8f7 100644 --- a/embassy-net-w5500/src/device.rs +++ b/embassy-net-w5500/src/device.rs @@ -1,117 +1,107 @@ +use core::marker::PhantomData; + use embedded_hal_async::spi::SpiDevice; -use crate::spi::{Address, SpiInterface}; - -pub const COMMON_MODE: Address = (RegisterBlock::Common, 0x00); -pub const COMMON_MAC: Address = (RegisterBlock::Common, 0x09); -pub const COMMON_SOCKET_INTR: Address = (RegisterBlock::Common, 0x18); -pub const COMMON_PHY_CFG: Address = (RegisterBlock::Common, 0x2E); - -pub const SOCKET_MODE: Address = (RegisterBlock::Socket0, 0x00); -pub const SOCKET_COMMAND: Address = (RegisterBlock::Socket0, 0x01); -pub const SOCKET_RXBUF_SIZE: Address = (RegisterBlock::Socket0, 0x1E); -pub const SOCKET_TXBUF_SIZE: Address = (RegisterBlock::Socket0, 0x1F); -pub const SOCKET_TX_FREE_SIZE: Address = (RegisterBlock::Socket0, 0x20); -pub const SOCKET_TX_DATA_WRITE_PTR: Address = (RegisterBlock::Socket0, 0x24); -pub const SOCKET_RECVD_SIZE: Address = (RegisterBlock::Socket0, 0x26); -pub const SOCKET_RX_DATA_READ_PTR: Address = (RegisterBlock::Socket0, 0x28); -pub const SOCKET_INTR_MASK: Address = (RegisterBlock::Socket0, 0x2C); -pub const SOCKET_INTR: Address = (RegisterBlock::Socket0, 0x02); +use crate::chip::Chip; #[repr(u8)] -pub enum Command { +enum Command { Open = 0x01, Send = 0x20, Receive = 0x40, } #[repr(u8)] -pub enum Interrupt { +enum Interrupt { Receive = 0b00100_u8, } -#[repr(u8)] -pub enum RegisterBlock { - Common = 0x00, - Socket0 = 0x01, - TxBuf = 0x02, - RxBuf = 0x03, -} - /// W5500 in MACRAW mode #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct W5500 { - bus: SpiInterface, +pub(crate) struct WiznetDevice { + spi: SPI, + _phantom: PhantomData, } -impl W5500 { +impl WiznetDevice { /// Create and initialize the W5500 driver - pub async fn new(spi: SPI, mac_addr: [u8; 6]) -> Result, SPI::Error> { - let mut bus = SpiInterface(spi); + pub async fn new(spi: SPI, mac_addr: [u8; 6]) -> Result { + let mut this = Self { + spi, + _phantom: PhantomData, + }; + // Reset device - bus.write_frame(COMMON_MODE, &[0x80]).await?; + this.bus_write(C::COMMON_MODE, &[0x80]).await?; // Enable interrupt pin - bus.write_frame(COMMON_SOCKET_INTR, &[0x01]).await?; + this.bus_write(C::COMMON_SOCKET_INTR, &[0x01]).await?; // Enable receive interrupt - bus.write_frame(SOCKET_INTR_MASK, &[Interrupt::Receive as u8]).await?; + this.bus_write(C::SOCKET_INTR_MASK, &[Interrupt::Receive as u8]).await?; // Set MAC address - bus.write_frame(COMMON_MAC, &mac_addr).await?; + this.bus_write(C::COMMON_MAC, &mac_addr).await?; // Set the raw socket RX/TX buffer sizes to 16KB - bus.write_frame(SOCKET_TXBUF_SIZE, &[16]).await?; - bus.write_frame(SOCKET_RXBUF_SIZE, &[16]).await?; + this.bus_write(C::SOCKET_TXBUF_SIZE, &[16]).await?; + this.bus_write(C::SOCKET_RXBUF_SIZE, &[16]).await?; // MACRAW mode with MAC filtering. let mode: u8 = (1 << 2) | (1 << 7); - bus.write_frame(SOCKET_MODE, &[mode]).await?; - let mut this = Self { bus }; + this.bus_write(C::SOCKET_MODE, &[mode]).await?; this.command(Command::Open).await?; Ok(this) } + async fn bus_read(&mut self, address: C::Address, data: &mut [u8]) -> Result<(), SPI::Error> { + C::bus_read(&mut self.spi, address, data).await + } + + async fn bus_write(&mut self, address: C::Address, data: &[u8]) -> Result<(), SPI::Error> { + C::bus_write(&mut self.spi, address, data).await + } + async fn reset_interrupt(&mut self, code: Interrupt) -> Result<(), SPI::Error> { let data = [code as u8]; - self.bus.write_frame(SOCKET_INTR, &data).await + self.bus_write(C::SOCKET_INTR, &data).await } async fn get_tx_write_ptr(&mut self) -> Result { let mut data = [0u8; 2]; - self.bus.read_frame(SOCKET_TX_DATA_WRITE_PTR, &mut data).await?; + self.bus_read(C::SOCKET_TX_DATA_WRITE_PTR, &mut data).await?; Ok(u16::from_be_bytes(data)) } async fn set_tx_write_ptr(&mut self, ptr: u16) -> Result<(), SPI::Error> { let data = ptr.to_be_bytes(); - self.bus.write_frame(SOCKET_TX_DATA_WRITE_PTR, &data).await + self.bus_write(C::SOCKET_TX_DATA_WRITE_PTR, &data).await } async fn get_rx_read_ptr(&mut self) -> Result { let mut data = [0u8; 2]; - self.bus.read_frame(SOCKET_RX_DATA_READ_PTR, &mut data).await?; + self.bus_read(C::SOCKET_RX_DATA_READ_PTR, &mut data).await?; Ok(u16::from_be_bytes(data)) } async fn set_rx_read_ptr(&mut self, ptr: u16) -> Result<(), SPI::Error> { let data = ptr.to_be_bytes(); - self.bus.write_frame(SOCKET_RX_DATA_READ_PTR, &data).await + self.bus_write(C::SOCKET_RX_DATA_READ_PTR, &data).await } async fn command(&mut self, command: Command) -> Result<(), SPI::Error> { let data = [command as u8]; - self.bus.write_frame(SOCKET_COMMAND, &data).await + self.bus_write(C::SOCKET_COMMAND, &data).await } async fn get_rx_size(&mut self) -> Result { loop { // Wait until two sequential reads are equal let mut res0 = [0u8; 2]; - self.bus.read_frame(SOCKET_RECVD_SIZE, &mut res0).await?; + self.bus_read(C::SOCKET_RECVD_SIZE, &mut res0).await?; let mut res1 = [0u8; 2]; - self.bus.read_frame(SOCKET_RECVD_SIZE, &mut res1).await?; + self.bus_read(C::SOCKET_RECVD_SIZE, &mut res1).await?; if res0 == res1 { break Ok(u16::from_be_bytes(res0)); } @@ -120,13 +110,13 @@ impl W5500 { async fn get_tx_free_size(&mut self) -> Result { let mut data = [0; 2]; - self.bus.read_frame(SOCKET_TX_FREE_SIZE, &mut data).await?; + self.bus_read(C::SOCKET_TX_FREE_SIZE, &mut data).await?; Ok(u16::from_be_bytes(data)) } /// Read bytes from the RX buffer. Returns the number of bytes read. async fn read_bytes(&mut self, read_ptr: &mut u16, buffer: &mut [u8]) -> Result<(), SPI::Error> { - self.bus.read_frame((RegisterBlock::RxBuf, *read_ptr), buffer).await?; + self.bus_read(C::rx_addr(*read_ptr), buffer).await?; *read_ptr = (*read_ptr).wrapping_add(buffer.len() as u16); Ok(()) @@ -165,7 +155,7 @@ impl W5500 { pub async fn write_frame(&mut self, frame: &[u8]) -> Result { while self.get_tx_free_size().await? < frame.len() as u16 {} let write_ptr = self.get_tx_write_ptr().await?; - self.bus.write_frame((RegisterBlock::TxBuf, write_ptr), frame).await?; + self.bus_write(C::tx_addr(write_ptr), frame).await?; self.set_tx_write_ptr(write_ptr.wrapping_add(frame.len() as u16)) .await?; self.command(Command::Send).await?; @@ -174,7 +164,7 @@ impl W5500 { pub async fn is_link_up(&mut self) -> bool { let mut link = [0]; - self.bus.read_frame(COMMON_PHY_CFG, &mut link).await.ok(); + self.bus_read(C::COMMON_PHY_CFG, &mut link).await.ok(); link[0] & 1 == 1 } } diff --git a/embassy-net-w5500/src/lib.rs b/embassy-net-w5500/src/lib.rs index 3c54777d8..9b53e9618 100644 --- a/embassy-net-w5500/src/lib.rs +++ b/embassy-net-w5500/src/lib.rs @@ -1,8 +1,9 @@ //! [`embassy-net`](https://crates.io/crates/embassy-net) driver for the WIZnet W5500 ethernet chip. #![no_std] +#![feature(async_fn_in_trait)] +pub mod chip; mod device; -mod spi; use embassy_futures::select::{select, Either}; use embassy_net_driver_channel as ch; @@ -12,7 +13,9 @@ use embedded_hal::digital::OutputPin; use embedded_hal_async::digital::Wait; use embedded_hal_async::spi::SpiDevice; -use crate::device::W5500; +use crate::chip::Chip; +use crate::device::WiznetDevice; + const MTU: usize = 1514; /// Type alias for the embassy-net driver for W5500 @@ -35,15 +38,15 @@ impl State { /// Background runner for the W5500. /// /// You must call `.run()` in a background task for the W5500 to operate. -pub struct Runner<'d, SPI: SpiDevice, INT: Wait, RST: OutputPin> { - mac: W5500, +pub struct Runner<'d, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin> { + mac: WiznetDevice, ch: ch::Runner<'d, MTU>, int: INT, _reset: RST, } /// You must call this in a background task for the W5500 to operate. -impl<'d, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, SPI, INT, RST> { +impl<'d, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, C, SPI, INT, RST> { pub async fn run(mut self) -> ! { let (state_chan, mut rx_chan, mut tx_chan) = self.ch.split(); loop { @@ -78,13 +81,13 @@ impl<'d, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, SPI, INT, RST> { } /// Obtain a driver for using the W5500 with [`embassy-net`](https://crates.io/crates/embassy-net). -pub async fn new<'a, const N_RX: usize, const N_TX: usize, SPI: SpiDevice, INT: Wait, RST: OutputPin>( +pub async fn new<'a, const N_RX: usize, const N_TX: usize, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin>( mac_addr: [u8; 6], state: &'a mut State, spi_dev: SPI, int: INT, mut reset: RST, -) -> (Device<'a>, Runner<'a, SPI, INT, RST>) { +) -> (Device<'a>, Runner<'a, C, SPI, INT, RST>) { // Reset the W5500. reset.set_low().ok(); // Ensure the reset is registered. @@ -93,7 +96,7 @@ pub async fn new<'a, const N_RX: usize, const N_TX: usize, SPI: SpiDevice, INT: // Wait for the W5500 to achieve PLL lock. Timer::after(Duration::from_millis(2)).await; - let mac = W5500::new(spi_dev, mac_addr).await.unwrap(); + let mac = WiznetDevice::new(spi_dev, mac_addr).await.unwrap(); let (runner, device) = ch::new(&mut state.ch_state, ch::driver::HardwareAddress::Ethernet(mac_addr)); ( diff --git a/embassy-net-w5500/src/spi.rs b/embassy-net-w5500/src/spi.rs deleted file mode 100644 index 316c6521e..000000000 --- a/embassy-net-w5500/src/spi.rs +++ /dev/null @@ -1,34 +0,0 @@ -use embedded_hal_async::spi::{Operation, SpiDevice}; - -use crate::device::RegisterBlock; - -pub type Address = (RegisterBlock, u16); - -#[derive(Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct SpiInterface(pub SPI); - -impl SpiInterface { - pub async fn read_frame(&mut self, address: Address, data: &mut [u8]) -> Result<(), SPI::Error> { - let address_phase = address.1.to_be_bytes(); - let control_phase = [(address.0 as u8) << 3]; - let operations = &mut [ - Operation::Write(&address_phase), - Operation::Write(&control_phase), - Operation::TransferInPlace(data), - ]; - self.0.transaction(operations).await - } - - pub async fn write_frame(&mut self, address: Address, data: &[u8]) -> Result<(), SPI::Error> { - let address_phase = address.1.to_be_bytes(); - let control_phase = [(address.0 as u8) << 3 | 0b0000_0100]; - let data_phase = data; - let operations = &mut [ - Operation::Write(&address_phase[..]), - Operation::Write(&control_phase), - Operation::Write(&data_phase), - ]; - self.0.transaction(operations).await - } -} diff --git a/examples/rp/src/bin/ethernet_w5500_multisocket.rs b/examples/rp/src/bin/ethernet_w5500_multisocket.rs index 9f800d0d9..3677f3cd6 100644 --- a/examples/rp/src/bin/ethernet_w5500_multisocket.rs +++ b/examples/rp/src/bin/ethernet_w5500_multisocket.rs @@ -10,6 +10,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_futures::yield_now; use embassy_net::{Stack, StackResources}; +use embassy_net_w5500::chip::W5500; use embassy_net_w5500::*; use embassy_rp::clocks::RoscRng; use embassy_rp::gpio::{Input, Level, Output, Pull}; @@ -26,6 +27,7 @@ use {defmt_rtt as _, panic_probe as _}; async fn ethernet_task( runner: Runner< 'static, + W5500, ExclusiveDevice, Output<'static, PIN_17>, Delay>, Input<'static, PIN_21>, Output<'static, PIN_20>, diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_client.rs b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs index fee84b613..b78a09779 100644 --- a/examples/rp/src/bin/ethernet_w5500_tcp_client.rs +++ b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs @@ -12,6 +12,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_futures::yield_now; use embassy_net::{Stack, StackResources}; +use embassy_net_w5500::chip::W5500; use embassy_net_w5500::*; use embassy_rp::clocks::RoscRng; use embassy_rp::gpio::{Input, Level, Output, Pull}; @@ -28,6 +29,7 @@ use {defmt_rtt as _, panic_probe as _}; async fn ethernet_task( runner: Runner< 'static, + W5500, ExclusiveDevice, Output<'static, PIN_17>, Delay>, Input<'static, PIN_21>, Output<'static, PIN_20>, diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_server.rs b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs index 024574267..34f054d9a 100644 --- a/examples/rp/src/bin/ethernet_w5500_tcp_server.rs +++ b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs @@ -11,6 +11,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_futures::yield_now; use embassy_net::{Stack, StackResources}; +use embassy_net_w5500::chip::W5500; use embassy_net_w5500::*; use embassy_rp::clocks::RoscRng; use embassy_rp::gpio::{Input, Level, Output, Pull}; @@ -26,6 +27,7 @@ use {defmt_rtt as _, panic_probe as _}; async fn ethernet_task( runner: Runner< 'static, + W5500, ExclusiveDevice, Output<'static, PIN_17>, Delay>, Input<'static, PIN_21>, Output<'static, PIN_20>, diff --git a/examples/rp/src/bin/ethernet_w5500_udp.rs b/examples/rp/src/bin/ethernet_w5500_udp.rs index 038432b17..8f38e453e 100644 --- a/examples/rp/src/bin/ethernet_w5500_udp.rs +++ b/examples/rp/src/bin/ethernet_w5500_udp.rs @@ -11,6 +11,7 @@ use embassy_executor::Spawner; use embassy_futures::yield_now; use embassy_net::udp::{PacketMetadata, UdpSocket}; use embassy_net::{Stack, StackResources}; +use embassy_net_w5500::chip::W5500; use embassy_net_w5500::*; use embassy_rp::clocks::RoscRng; use embassy_rp::gpio::{Input, Level, Output, Pull}; @@ -25,6 +26,7 @@ use {defmt_rtt as _, panic_probe as _}; async fn ethernet_task( runner: Runner< 'static, + W5500, ExclusiveDevice, Output<'static, PIN_17>, Delay>, Input<'static, PIN_21>, Output<'static, PIN_20>, From 1d4b941d52a7e257b0305935034e999facb537bd Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 15 Aug 2023 18:19:03 +0200 Subject: [PATCH 47/77] net-w5500: add w5100s support. --- embassy-net-w5500/Cargo.toml | 3 +- embassy-net-w5500/src/chip/mod.rs | 7 ++++ embassy-net-w5500/src/chip/w5100s.rs | 61 ++++++++++++++++++++++++++++ embassy-net-w5500/src/chip/w5500.rs | 5 +++ embassy-net-w5500/src/device.rs | 41 +++++++++++++++---- embassy-net-w5500/src/lib.rs | 24 +++++++---- 6 files changed, 123 insertions(+), 18 deletions(-) create mode 100644 embassy-net-w5500/src/chip/w5100s.rs diff --git a/embassy-net-w5500/Cargo.toml b/embassy-net-w5500/Cargo.toml index 8972b814a..c45598684 100644 --- a/embassy-net-w5500/Cargo.toml +++ b/embassy-net-w5500/Cargo.toml @@ -18,4 +18,5 @@ defmt = { version = "0.3", optional = true } [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-w5500-v$VERSION/embassy-net-w5500/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-w5500/src/" -target = "thumbv7em-none-eabi" \ No newline at end of file +target = "thumbv7em-none-eabi" +features = ["defmt"] \ No newline at end of file diff --git a/embassy-net-w5500/src/chip/mod.rs b/embassy-net-w5500/src/chip/mod.rs index b7d550e26..562db515a 100644 --- a/embassy-net-w5500/src/chip/mod.rs +++ b/embassy-net-w5500/src/chip/mod.rs @@ -1,5 +1,7 @@ mod w5500; pub use w5500::W5500; +mod w5100s; +pub use w5100s::W5100S; pub(crate) mod sealed { use embedded_hal_async::spi::SpiDevice; @@ -22,6 +24,11 @@ pub(crate) mod sealed { const SOCKET_INTR_MASK: Self::Address; const SOCKET_INTR: Self::Address; + const SOCKET_MODE_VALUE: u8; + + const BUF_SIZE: u16; + const AUTO_WRAP: bool; + fn rx_addr(addr: u16) -> Self::Address; fn tx_addr(addr: u16) -> Self::Address; diff --git a/embassy-net-w5500/src/chip/w5100s.rs b/embassy-net-w5500/src/chip/w5100s.rs new file mode 100644 index 000000000..07a840370 --- /dev/null +++ b/embassy-net-w5500/src/chip/w5100s.rs @@ -0,0 +1,61 @@ +use embedded_hal_async::spi::{Operation, SpiDevice}; + +const SOCKET_BASE: u16 = 0x400; +const TX_BASE: u16 = 0x4000; +const RX_BASE: u16 = 0x6000; + +pub enum W5100S {} + +impl super::Chip for W5100S {} +impl super::sealed::Chip for W5100S { + type Address = u16; + + const COMMON_MODE: Self::Address = 0x00; + const COMMON_MAC: Self::Address = 0x09; + const COMMON_SOCKET_INTR: Self::Address = 0x16; + const COMMON_PHY_CFG: Self::Address = 0x3c; + + const SOCKET_MODE: Self::Address = SOCKET_BASE + 0x00; + const SOCKET_COMMAND: Self::Address = SOCKET_BASE + 0x01; + const SOCKET_RXBUF_SIZE: Self::Address = SOCKET_BASE + 0x1E; + const SOCKET_TXBUF_SIZE: Self::Address = SOCKET_BASE + 0x1F; + const SOCKET_TX_FREE_SIZE: Self::Address = SOCKET_BASE + 0x20; + const SOCKET_TX_DATA_WRITE_PTR: Self::Address = SOCKET_BASE + 0x24; + const SOCKET_RECVD_SIZE: Self::Address = SOCKET_BASE + 0x26; + const SOCKET_RX_DATA_READ_PTR: Self::Address = SOCKET_BASE + 0x28; + const SOCKET_INTR_MASK: Self::Address = SOCKET_BASE + 0x2C; + const SOCKET_INTR: Self::Address = SOCKET_BASE + 0x02; + + const SOCKET_MODE_VALUE: u8 = (1 << 2) | (1 << 6); + + const BUF_SIZE: u16 = 0x2000; + const AUTO_WRAP: bool = false; + + fn rx_addr(addr: u16) -> Self::Address { + RX_BASE + addr + } + + fn tx_addr(addr: u16) -> Self::Address { + TX_BASE + addr + } + + async fn bus_read( + spi: &mut SPI, + address: Self::Address, + data: &mut [u8], + ) -> Result<(), SPI::Error> { + spi.transaction(&mut [ + Operation::Write(&[0x0F, (address >> 8) as u8, address as u8]), + Operation::Read(data), + ]) + .await + } + + async fn bus_write(spi: &mut SPI, address: Self::Address, data: &[u8]) -> Result<(), SPI::Error> { + spi.transaction(&mut [ + Operation::Write(&[0xF0, (address >> 8) as u8, address as u8]), + Operation::Write(data), + ]) + .await + } +} diff --git a/embassy-net-w5500/src/chip/w5500.rs b/embassy-net-w5500/src/chip/w5500.rs index f514e12a7..61e512946 100644 --- a/embassy-net-w5500/src/chip/w5500.rs +++ b/embassy-net-w5500/src/chip/w5500.rs @@ -30,6 +30,11 @@ impl super::sealed::Chip for W5500 { const SOCKET_INTR_MASK: Self::Address = (RegisterBlock::Socket0, 0x2C); const SOCKET_INTR: Self::Address = (RegisterBlock::Socket0, 0x02); + const SOCKET_MODE_VALUE: u8 = (1 << 2) | (1 << 7); + + const BUF_SIZE: u16 = 0x4000; + const AUTO_WRAP: bool = true; + fn rx_addr(addr: u16) -> Self::Address { (RegisterBlock::RxBuf, addr) } diff --git a/embassy-net-w5500/src/device.rs b/embassy-net-w5500/src/device.rs index a6ee8f8f7..f367bc3eb 100644 --- a/embassy-net-w5500/src/device.rs +++ b/embassy-net-w5500/src/device.rs @@ -43,13 +43,13 @@ impl WiznetDevice { // Set MAC address this.bus_write(C::COMMON_MAC, &mac_addr).await?; - // Set the raw socket RX/TX buffer sizes to 16KB - this.bus_write(C::SOCKET_TXBUF_SIZE, &[16]).await?; - this.bus_write(C::SOCKET_RXBUF_SIZE, &[16]).await?; + // Set the raw socket RX/TX buffer sizes. + let buf_kbs = (C::BUF_SIZE / 1024) as u8; + this.bus_write(C::SOCKET_TXBUF_SIZE, &[buf_kbs]).await?; + this.bus_write(C::SOCKET_RXBUF_SIZE, &[buf_kbs]).await?; // MACRAW mode with MAC filtering. - let mode: u8 = (1 << 2) | (1 << 7); - this.bus_write(C::SOCKET_MODE, &[mode]).await?; + this.bus_write(C::SOCKET_MODE, &[C::SOCKET_MODE_VALUE]).await?; this.command(Command::Open).await?; Ok(this) @@ -114,9 +114,21 @@ impl WiznetDevice { Ok(u16::from_be_bytes(data)) } - /// Read bytes from the RX buffer. Returns the number of bytes read. + /// Read bytes from the RX buffer. async fn read_bytes(&mut self, read_ptr: &mut u16, buffer: &mut [u8]) -> Result<(), SPI::Error> { - self.bus_read(C::rx_addr(*read_ptr), buffer).await?; + if C::AUTO_WRAP { + self.bus_read(C::rx_addr(*read_ptr), buffer).await?; + } else { + let addr = *read_ptr % C::BUF_SIZE; + if addr as usize + buffer.len() <= C::BUF_SIZE as usize { + self.bus_read(C::rx_addr(addr), buffer).await?; + } else { + let n = C::BUF_SIZE - addr; + self.bus_read(C::rx_addr(addr), &mut buffer[..n as usize]).await?; + self.bus_read(C::rx_addr(0), &mut buffer[n as usize..]).await?; + } + } + *read_ptr = (*read_ptr).wrapping_add(buffer.len() as u16); Ok(()) @@ -155,7 +167,20 @@ impl WiznetDevice { pub async fn write_frame(&mut self, frame: &[u8]) -> Result { while self.get_tx_free_size().await? < frame.len() as u16 {} let write_ptr = self.get_tx_write_ptr().await?; - self.bus_write(C::tx_addr(write_ptr), frame).await?; + + if C::AUTO_WRAP { + self.bus_write(C::tx_addr(write_ptr), frame).await?; + } else { + let addr = write_ptr % C::BUF_SIZE; + if addr as usize + frame.len() <= C::BUF_SIZE as usize { + self.bus_write(C::tx_addr(addr), frame).await?; + } else { + let n = C::BUF_SIZE - addr; + self.bus_write(C::tx_addr(addr), &frame[..n as usize]).await?; + self.bus_write(C::tx_addr(0), &frame[n as usize..]).await?; + } + } + self.set_tx_write_ptr(write_ptr.wrapping_add(frame.len() as u16)) .await?; self.command(Command::Send).await?; diff --git a/embassy-net-w5500/src/lib.rs b/embassy-net-w5500/src/lib.rs index 9b53e9618..3030dfb90 100644 --- a/embassy-net-w5500/src/lib.rs +++ b/embassy-net-w5500/src/lib.rs @@ -1,4 +1,4 @@ -//! [`embassy-net`](https://crates.io/crates/embassy-net) driver for the WIZnet W5500 ethernet chip. +//! [`embassy-net`](https://crates.io/crates/embassy-net) driver for WIZnet ethernet chips. #![no_std] #![feature(async_fn_in_trait)] @@ -18,7 +18,7 @@ use crate::device::WiznetDevice; const MTU: usize = 1514; -/// Type alias for the embassy-net driver for W5500 +/// Type alias for the embassy-net driver. pub type Device<'d> = embassy_net_driver_channel::Device<'d, MTU>; /// Internal state for the embassy-net integration. @@ -35,9 +35,9 @@ impl State { } } -/// Background runner for the W5500. +/// Background runner for the driver. /// -/// You must call `.run()` in a background task for the W5500 to operate. +/// You must call `.run()` in a background task for the driver to operate. pub struct Runner<'d, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin> { mac: WiznetDevice, ch: ch::Runner<'d, MTU>, @@ -45,7 +45,7 @@ pub struct Runner<'d, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin> { _reset: RST, } -/// You must call this in a background task for the W5500 to operate. +/// You must call this in a background task for the driver to operate. impl<'d, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, C, SPI, INT, RST> { pub async fn run(mut self) -> ! { let (state_chan, mut rx_chan, mut tx_chan) = self.ch.split(); @@ -80,7 +80,11 @@ impl<'d, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, C, SPI, } } -/// Obtain a driver for using the W5500 with [`embassy-net`](https://crates.io/crates/embassy-net). +/// Create a Wiznet ethernet chip driver for [`embassy-net`](https://crates.io/crates/embassy-net). +/// +/// This returns two structs: +/// - a `Device` that you must pass to the `embassy-net` stack. +/// - a `Runner`. You must call `.run()` on it in a background task. pub async fn new<'a, const N_RX: usize, const N_TX: usize, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin>( mac_addr: [u8; 6], state: &'a mut State, @@ -88,13 +92,15 @@ pub async fn new<'a, const N_RX: usize, const N_TX: usize, C: Chip, SPI: SpiDevi int: INT, mut reset: RST, ) -> (Device<'a>, Runner<'a, C, SPI, INT, RST>) { - // Reset the W5500. + // Reset the chip. reset.set_low().ok(); // Ensure the reset is registered. Timer::after(Duration::from_millis(1)).await; reset.set_high().ok(); - // Wait for the W5500 to achieve PLL lock. - Timer::after(Duration::from_millis(2)).await; + + // Wait for PLL lock. Some chips are slower than others. + // Slowest is w5100s which is 100ms, so let's just wait that. + Timer::after(Duration::from_millis(100)).await; let mac = WiznetDevice::new(spi_dev, mac_addr).await.unwrap(); From 11b66a73b4fc85469eeb8f718531492aed128e26 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 15 Aug 2023 23:02:53 +0200 Subject: [PATCH 48/77] net-wiznet: rename from embassy-net-w5500. --- .github/ci/doc.sh | 2 +- embassy-net-driver-channel/README.md | 2 +- embassy-net-w5500/README.md | 7 ----- .../Cargo.toml | 10 +++---- embassy-net-wiznet/README.md | 27 +++++++++++++++++++ .../src/chip/mod.rs | 0 .../src/chip/w5100s.rs | 0 .../src/chip/w5500.rs | 0 .../src/device.rs | 4 +-- .../src/lib.rs | 0 embassy-net/README.md | 2 +- examples/rp/Cargo.toml | 2 +- .../rp/src/bin/ethernet_w5500_multisocket.rs | 6 ++--- .../rp/src/bin/ethernet_w5500_tcp_client.rs | 6 ++--- .../rp/src/bin/ethernet_w5500_tcp_server.rs | 7 ++--- examples/rp/src/bin/ethernet_w5500_udp.rs | 7 ++--- 16 files changed, 52 insertions(+), 30 deletions(-) delete mode 100644 embassy-net-w5500/README.md rename {embassy-net-w5500 => embassy-net-wiznet}/Cargo.toml (74%) create mode 100644 embassy-net-wiznet/README.md rename {embassy-net-w5500 => embassy-net-wiznet}/src/chip/mod.rs (100%) rename {embassy-net-w5500 => embassy-net-wiznet}/src/chip/w5100s.rs (100%) rename {embassy-net-w5500 => embassy-net-wiznet}/src/chip/w5500.rs (100%) rename {embassy-net-w5500 => embassy-net-wiznet}/src/device.rs (98%) rename {embassy-net-w5500 => embassy-net-wiznet}/src/lib.rs (100%) diff --git a/.github/ci/doc.sh b/.github/ci/doc.sh index f829582ab..57184dc1d 100755 --- a/.github/ci/doc.sh +++ b/.github/ci/doc.sh @@ -35,7 +35,7 @@ docserver-builder -i ./embassy-usb-driver -o webroot/crates/embassy-usb-driver/g docserver-builder -i ./embassy-usb-logger -o webroot/crates/embassy-usb-logger/git.zup docserver-builder -i ./cyw43 -o webroot/crates/cyw43/git.zup docserver-builder -i ./cyw43-pio -o webroot/crates/cyw43-pio/git.zup -docserver-builder -i ./embassy-net-w5500 -o webroot/crates/embassy-net-w5500/git.zup +docserver-builder -i ./embassy-net-wiznet -o webroot/crates/embassy-net-wiznet/git.zup docserver-builder -i ./embassy-net-enc28j60 -o webroot/crates/embassy-net-enc28j60/git.zup docserver-builder -i ./embassy-net-esp-hosted -o webroot/crates/embassy-net-esp-hosted/git.zup docserver-builder -i ./embassy-stm32-wpan -o webroot/crates/embassy-stm32-wpan/git.zup --output-static webroot/static diff --git a/embassy-net-driver-channel/README.md b/embassy-net-driver-channel/README.md index dd90e7ad2..8f904ce95 100644 --- a/embassy-net-driver-channel/README.md +++ b/embassy-net-driver-channel/README.md @@ -76,7 +76,7 @@ These `embassy-net` drivers are implemented using this crate. You can look at th - [`cyw43`](https://github.com/embassy-rs/embassy/tree/main/cyw43) for WiFi on CYW43xx chips, used in the Raspberry Pi Pico W - [`embassy-usb`](https://github.com/embassy-rs/embassy/tree/main/embassy-usb) for Ethernet-over-USB (CDC NCM) support. -- [`embassy-net-w5500`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-w5500) for Wiznet W5500 SPI Ethernet MAC+PHY chip. +- [`embassy-net-wiznet`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-wiznet) for Wiznet SPI Ethernet MAC+PHY chips. - [`embassy-net-esp-hosted`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-esp-hosted) for using ESP32 chips with the [`esp-hosted`](https://github.com/espressif/esp-hosted) firmware as WiFi adapters for another non-ESP32 MCU. diff --git a/embassy-net-w5500/README.md b/embassy-net-w5500/README.md deleted file mode 100644 index 9eaf4b700..000000000 --- a/embassy-net-w5500/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# WIZnet W5500 `embassy-net` integration - -[`embassy-net`](https://crates.io/crates/embassy-net) integration for the WIZnet W5500 SPI ethernet chip, operating in MACRAW mode. - -Supports any SPI driver implementing [`embedded-hal-async`](https://crates.io/crates/embedded-hal-async) - -See [`examples`](https://github.com/kalkyl/embassy-net-w5500/tree/main/examples) directory for usage examples with the rp2040 [`WIZnet W5500-EVB-Pico`](https://www.wiznet.io/product-item/w5500-evb-pico/) module. \ No newline at end of file diff --git a/embassy-net-w5500/Cargo.toml b/embassy-net-wiznet/Cargo.toml similarity index 74% rename from embassy-net-w5500/Cargo.toml rename to embassy-net-wiznet/Cargo.toml index c45598684..dff03ac83 100644 --- a/embassy-net-w5500/Cargo.toml +++ b/embassy-net-wiznet/Cargo.toml @@ -1,8 +1,8 @@ [package] -name = "embassy-net-w5500" +name = "embassy-net-wiznet" version = "0.1.0" -description = "embassy-net driver for the W5500 ethernet chip" -keywords = ["embedded", "w5500", "embassy-net", "embedded-hal-async", "ethernet", "async"] +description = "embassy-net driver for WIZnet SPI Ethernet chips" +keywords = ["embedded", "wiznet", "embassy-net", "embedded-hal-async", "ethernet", "async"] categories = ["embedded", "hardware-support", "no-std", "network-programming", "async"] license = "MIT OR Apache-2.0" edition = "2021" @@ -16,7 +16,7 @@ embassy-futures = { version = "0.1.0", path = "../embassy-futures" } defmt = { version = "0.3", optional = true } [package.metadata.embassy_docs] -src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-w5500-v$VERSION/embassy-net-w5500/src/" -src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-w5500/src/" +src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-wiznet-v$VERSION/embassy-net-wiznet/src/" +src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-wiznet/src/" target = "thumbv7em-none-eabi" features = ["defmt"] \ No newline at end of file diff --git a/embassy-net-wiznet/README.md b/embassy-net-wiznet/README.md new file mode 100644 index 000000000..b8e4bdc8e --- /dev/null +++ b/embassy-net-wiznet/README.md @@ -0,0 +1,27 @@ +# WIZnet `embassy-net` integration + +[`embassy-net`](https://crates.io/crates/embassy-net) integration for the WIZnet SPI ethernet chips, operating in MACRAW mode. + +See [`examples`](https://github.com/embassy-rs/embassy/tree/main/examples/rp) directory for usage examples with the rp2040 [`WIZnet W5500-EVB-Pico`](https://www.wiznet.io/product-item/w5500-evb-pico/) module. + +## Supported chips + +- W5500 +- W5100S + +## Interoperability + +This crate can run on any executor. + +It supports any SPI driver implementing [`embedded-hal-async`](https://crates.io/crates/embedded-hal-async). + + +## License + +This work is licensed under either of + +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or + http://www.apache.org/licenses/LICENSE-2.0) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. diff --git a/embassy-net-w5500/src/chip/mod.rs b/embassy-net-wiznet/src/chip/mod.rs similarity index 100% rename from embassy-net-w5500/src/chip/mod.rs rename to embassy-net-wiznet/src/chip/mod.rs diff --git a/embassy-net-w5500/src/chip/w5100s.rs b/embassy-net-wiznet/src/chip/w5100s.rs similarity index 100% rename from embassy-net-w5500/src/chip/w5100s.rs rename to embassy-net-wiznet/src/chip/w5100s.rs diff --git a/embassy-net-w5500/src/chip/w5500.rs b/embassy-net-wiznet/src/chip/w5500.rs similarity index 100% rename from embassy-net-w5500/src/chip/w5500.rs rename to embassy-net-wiznet/src/chip/w5500.rs diff --git a/embassy-net-w5500/src/device.rs b/embassy-net-wiznet/src/device.rs similarity index 98% rename from embassy-net-w5500/src/device.rs rename to embassy-net-wiznet/src/device.rs index f367bc3eb..43f9512a3 100644 --- a/embassy-net-w5500/src/device.rs +++ b/embassy-net-wiznet/src/device.rs @@ -16,7 +16,7 @@ enum Interrupt { Receive = 0b00100_u8, } -/// W5500 in MACRAW mode +/// Wiznet chip in MACRAW mode #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub(crate) struct WiznetDevice { @@ -25,7 +25,7 @@ pub(crate) struct WiznetDevice { } impl WiznetDevice { - /// Create and initialize the W5500 driver + /// Create and initialize the driver pub async fn new(spi: SPI, mac_addr: [u8; 6]) -> Result { let mut this = Self { spi, diff --git a/embassy-net-w5500/src/lib.rs b/embassy-net-wiznet/src/lib.rs similarity index 100% rename from embassy-net-w5500/src/lib.rs rename to embassy-net-wiznet/src/lib.rs diff --git a/embassy-net/README.md b/embassy-net/README.md index 811321ca4..7bb2283c4 100644 --- a/embassy-net/README.md +++ b/embassy-net/README.md @@ -22,7 +22,7 @@ unimplemented features of the network protocols. - [`cyw43`](https://github.com/embassy-rs/embassy/tree/main/cyw43) for WiFi on CYW43xx chips, used in the Raspberry Pi Pico W - [`embassy-usb`](https://github.com/embassy-rs/embassy/tree/main/embassy-usb) for Ethernet-over-USB (CDC NCM) support. - [`embassy-stm32`](https://github.com/embassy-rs/embassy/tree/main/embassy-stm32) for the builtin Ethernet MAC in all STM32 chips (STM32F1, STM32F2, STM32F4, STM32F7, STM32H7, STM32H5). -- [`embassy-net-w5500`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-w5500) for Wiznet W5500 SPI Ethernet MAC+PHY chip. +- [`embassy-net-wiznet`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-wiznet) for Wiznet SPI Ethernet MAC+PHY chips (W5100S, W5500) - [`embassy-net-esp-hosted`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-esp-hosted) for using ESP32 chips with the [`esp-hosted`](https://github.com/espressif/esp-hosted) firmware as WiFi adapters for another non-ESP32 MCU. ## Examples diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index d83e370df..6742ce7ef 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -13,7 +13,7 @@ embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["ni embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "critical-section-impl"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "udp", "dhcpv4", "medium-ethernet"] } -embassy-net-w5500 = { version = "0.1.0", path = "../../embassy-net-w5500", features = ["defmt"] } +embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-usb-logger = { version = "0.1.0", path = "../../embassy-usb-logger" } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt"] } diff --git a/examples/rp/src/bin/ethernet_w5500_multisocket.rs b/examples/rp/src/bin/ethernet_w5500_multisocket.rs index 3677f3cd6..d4af87642 100644 --- a/examples/rp/src/bin/ethernet_w5500_multisocket.rs +++ b/examples/rp/src/bin/ethernet_w5500_multisocket.rs @@ -10,8 +10,8 @@ use defmt::*; use embassy_executor::Spawner; use embassy_futures::yield_now; use embassy_net::{Stack, StackResources}; -use embassy_net_w5500::chip::W5500; -use embassy_net_w5500::*; +use embassy_net_wiznet::chip::W5500; +use embassy_net_wiznet::*; use embassy_rp::clocks::RoscRng; use embassy_rp::gpio::{Input, Level, Output, Pull}; use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; @@ -56,7 +56,7 @@ async fn main(spawner: Spawner) { let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; let state = make_static!(State::<8, 8>::new()); - let (device, runner) = embassy_net_w5500::new( + let (device, runner) = embassy_net_wiznet::new( mac_addr, state, ExclusiveDevice::new(spi, cs, Delay), diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_client.rs b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs index b78a09779..78340492e 100644 --- a/examples/rp/src/bin/ethernet_w5500_tcp_client.rs +++ b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs @@ -12,8 +12,8 @@ use defmt::*; use embassy_executor::Spawner; use embassy_futures::yield_now; use embassy_net::{Stack, StackResources}; -use embassy_net_w5500::chip::W5500; -use embassy_net_w5500::*; +use embassy_net_wiznet::chip::W5500; +use embassy_net_wiznet::*; use embassy_rp::clocks::RoscRng; use embassy_rp::gpio::{Input, Level, Output, Pull}; use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; @@ -59,7 +59,7 @@ async fn main(spawner: Spawner) { let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; let state = make_static!(State::<8, 8>::new()); - let (device, runner) = embassy_net_w5500::new( + let (device, runner) = embassy_net_wiznet::new( mac_addr, state, ExclusiveDevice::new(spi, cs, Delay), diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_server.rs b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs index 34f054d9a..e6d132740 100644 --- a/examples/rp/src/bin/ethernet_w5500_tcp_server.rs +++ b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs @@ -11,8 +11,8 @@ use defmt::*; use embassy_executor::Spawner; use embassy_futures::yield_now; use embassy_net::{Stack, StackResources}; -use embassy_net_w5500::chip::W5500; -use embassy_net_w5500::*; +use embassy_net_wiznet::chip::W5500; +use embassy_net_wiznet::*; use embassy_rp::clocks::RoscRng; use embassy_rp::gpio::{Input, Level, Output, Pull}; use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; @@ -23,6 +23,7 @@ use embedded_io_async::Write; use rand::RngCore; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; + #[embassy_executor::task] async fn ethernet_task( runner: Runner< @@ -57,7 +58,7 @@ async fn main(spawner: Spawner) { let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; let state = make_static!(State::<8, 8>::new()); - let (device, runner) = embassy_net_w5500::new( + let (device, runner) = embassy_net_wiznet::new( mac_addr, state, ExclusiveDevice::new(spi, cs, Delay), diff --git a/examples/rp/src/bin/ethernet_w5500_udp.rs b/examples/rp/src/bin/ethernet_w5500_udp.rs index 8f38e453e..b546714df 100644 --- a/examples/rp/src/bin/ethernet_w5500_udp.rs +++ b/examples/rp/src/bin/ethernet_w5500_udp.rs @@ -11,8 +11,8 @@ use embassy_executor::Spawner; use embassy_futures::yield_now; use embassy_net::udp::{PacketMetadata, UdpSocket}; use embassy_net::{Stack, StackResources}; -use embassy_net_w5500::chip::W5500; -use embassy_net_w5500::*; +use embassy_net_wiznet::chip::W5500; +use embassy_net_wiznet::*; use embassy_rp::clocks::RoscRng; use embassy_rp::gpio::{Input, Level, Output, Pull}; use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; @@ -22,6 +22,7 @@ use embedded_hal_async::spi::ExclusiveDevice; use rand::RngCore; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; + #[embassy_executor::task] async fn ethernet_task( runner: Runner< @@ -55,7 +56,7 @@ async fn main(spawner: Spawner) { let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; let state = make_static!(State::<8, 8>::new()); - let (device, runner) = embassy_net_w5500::new( + let (device, runner) = embassy_net_wiznet::new( mac_addr, state, ExclusiveDevice::new(spi, cs, Delay), From f26dd54f6378be87a71f0c9f351b56851df96014 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 16 Aug 2023 00:40:56 +0200 Subject: [PATCH 49/77] Update embedded-hal to 1.0.0-rc.1 (#1783) --- cyw43/Cargo.toml | 2 +- embassy-embedded-hal/Cargo.toml | 4 +- .../src/adapter/blocking_async.rs | 11 +---- embassy-lora/Cargo.toml | 2 +- embassy-net-enc28j60/Cargo.toml | 4 +- embassy-net-esp-hosted/Cargo.toml | 4 +- embassy-net-wiznet/Cargo.toml | 4 +- embassy-nrf/Cargo.toml | 4 +- embassy-nrf/src/uarte.rs | 48 ------------------- embassy-rp/Cargo.toml | 6 +-- embassy-rp/src/uart/buffered.rs | 26 ++-------- embassy-rp/src/uart/mod.rs | 38 ++++----------- embassy-stm32/Cargo.toml | 6 +-- embassy-stm32/src/usart/buffered.rs | 26 ++-------- embassy-stm32/src/usart/mod.rs | 40 ++++------------ embassy-time/Cargo.toml | 4 +- examples/nrf52840/Cargo.toml | 10 ++-- examples/nrf52840/src/bin/wifi_esp_hosted.rs | 2 +- examples/rp/Cargo.toml | 10 ++-- .../rp/src/bin/ethernet_w5500_multisocket.rs | 2 +- .../rp/src/bin/ethernet_w5500_tcp_client.rs | 2 +- .../rp/src/bin/ethernet_w5500_tcp_server.rs | 2 +- examples/rp/src/bin/ethernet_w5500_udp.rs | 2 +- examples/stm32h5/Cargo.toml | 4 +- examples/stm32h7/Cargo.toml | 4 +- examples/stm32l0/Cargo.toml | 3 ++ examples/stm32l4/Cargo.toml | 4 +- examples/stm32wl/Cargo.toml | 3 ++ tests/nrf/Cargo.toml | 3 +- tests/nrf/src/bin/wifi_esp_hosted_perf.rs | 2 +- tests/rp/Cargo.toml | 4 +- tests/stm32/Cargo.toml | 4 +- 32 files changed, 84 insertions(+), 206 deletions(-) diff --git a/cyw43/Cargo.toml b/cyw43/Cargo.toml index 50fb7c5db..855d54b11 100644 --- a/cyw43/Cargo.toml +++ b/cyw43/Cargo.toml @@ -24,7 +24,7 @@ cortex-m = "0.7.6" cortex-m-rt = "0.7.0" futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] } -embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.11" } +embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-rc.1" } num_enum = { version = "0.5.7", default-features = false } [package.metadata.embassy_docs] diff --git a/embassy-embedded-hal/Cargo.toml b/embassy-embedded-hal/Cargo.toml index bba3d48be..fd921d277 100644 --- a/embassy-embedded-hal/Cargo.toml +++ b/embassy-embedded-hal/Cargo.toml @@ -25,8 +25,8 @@ embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [ "unproven", ] } -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } -embedded-hal-async = { version = "=0.2.0-alpha.2", optional = true } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" } +embedded-hal-async = { version = "=1.0.0-rc.1", optional = true } embedded-storage = "0.3.0" embedded-storage-async = { version = "0.4.0", optional = true } nb = "1.0.0" diff --git a/embassy-embedded-hal/src/adapter/blocking_async.rs b/embassy-embedded-hal/src/adapter/blocking_async.rs index 98ae2b02c..ae0d0a7f9 100644 --- a/embassy-embedded-hal/src/adapter/blocking_async.rs +++ b/embassy-embedded-hal/src/adapter/blocking_async.rs @@ -1,4 +1,4 @@ -use embedded_hal_02::{blocking, serial}; +use embedded_hal_02::blocking; /// Wrapper that implements async traits using blocking implementations. /// @@ -103,15 +103,6 @@ where } } -// Uart implementatinos -impl embedded_hal_1::serial::ErrorType for BlockingAsync -where - T: serial::Read, - E: embedded_hal_1::serial::Error + 'static, -{ - type Error = E; -} - /// NOR flash wrapper use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index 402ad2d70..90c544d8d 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -23,7 +23,7 @@ log = { version = "0.4.14", optional = true } embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true } embassy-sync = { version = "0.2.0", path = "../embassy-sync" } embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false, optional = true } -embedded-hal-async = { version = "=0.2.0-alpha.2" } +embedded-hal-async = { version = "=1.0.0-rc.1" } embedded-hal = { version = "0.2", features = ["unproven"] } futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] } diff --git a/embassy-net-enc28j60/Cargo.toml b/embassy-net-enc28j60/Cargo.toml index c502ed04b..e02c984e9 100644 --- a/embassy-net-enc28j60/Cargo.toml +++ b/embassy-net-enc28j60/Cargo.toml @@ -8,8 +8,8 @@ license = "MIT OR Apache-2.0" edition = "2021" [dependencies] -embedded-hal = { version = "1.0.0-alpha.11" } -embedded-hal-async = { version = "=0.2.0-alpha.2" } +embedded-hal = { version = "1.0.0-rc.1" } +embedded-hal-async = { version = "=1.0.0-rc.1" } embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } embassy-time = { version = "0.1.2", path = "../embassy-time" } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } diff --git a/embassy-net-esp-hosted/Cargo.toml b/embassy-net-esp-hosted/Cargo.toml index 0053c49a4..d334cf3fe 100644 --- a/embassy-net-esp-hosted/Cargo.toml +++ b/embassy-net-esp-hosted/Cargo.toml @@ -12,8 +12,8 @@ embassy-sync = { version = "0.2.0", path = "../embassy-sync"} embassy-futures = { version = "0.1.0", path = "../embassy-futures"} embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"} -embedded-hal = { version = "1.0.0-alpha.11" } -embedded-hal-async = { version = "=0.2.0-alpha.2" } +embedded-hal = { version = "1.0.0-rc.1" } +embedded-hal-async = { version = "=1.0.0-rc.1" } noproto = { git="https://github.com/embassy-rs/noproto", default-features = false, features = ["derive"] } #noproto = { version = "0.1", path = "/home/dirbaio/noproto", default-features = false, features = ["derive"] } diff --git a/embassy-net-wiznet/Cargo.toml b/embassy-net-wiznet/Cargo.toml index dff03ac83..adf0b45fc 100644 --- a/embassy-net-wiznet/Cargo.toml +++ b/embassy-net-wiznet/Cargo.toml @@ -8,8 +8,8 @@ license = "MIT OR Apache-2.0" edition = "2021" [dependencies] -embedded-hal = { version = "1.0.0-alpha.11" } -embedded-hal-async = { version = "=0.2.0-alpha.2" } +embedded-hal = { version = "1.0.0-rc.1" } +embedded-hal-async = { version = "=1.0.0-rc.1" } embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel" } embassy-time = { version = "0.1.2", path = "../embassy-time" } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index da1cd38e1..c4790f2c6 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -98,8 +98,8 @@ embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional=true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11", optional = true} -embedded-hal-async = { version = "=0.2.0-alpha.2", optional = true} +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1", optional = true} +embedded-hal-async = { version = "=1.0.0-rc.1", optional = true} embedded-io = { version = "0.5.0" } embedded-io-async = { version = "0.5.0", optional = true } diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index e79df3561..95434e7a7 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -949,51 +949,3 @@ mod eh02 { } } } - -#[cfg(feature = "unstable-traits")] -mod eh1 { - use super::*; - - impl embedded_hal_1::serial::Error for Error { - fn kind(&self) -> embedded_hal_1::serial::ErrorKind { - match *self { - Self::BufferTooLong => embedded_hal_1::serial::ErrorKind::Other, - Self::BufferNotInRAM => embedded_hal_1::serial::ErrorKind::Other, - } - } - } - - // ===================== - - impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for Uarte<'d, T> { - type Error = Error; - } - - impl<'d, T: Instance> embedded_hal_1::serial::Write for Uarte<'d, T> { - fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(buffer) - } - - fn flush(&mut self) -> Result<(), Self::Error> { - Ok(()) - } - } - - impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for UarteTx<'d, T> { - type Error = Error; - } - - impl<'d, T: Instance> embedded_hal_1::serial::Write for UarteTx<'d, T> { - fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(buffer) - } - - fn flush(&mut self) -> Result<(), Self::Error> { - Ok(()) - } - } - - impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for UarteRx<'d, T> { - type Error = Error; - } -} diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index b1680d487..b9ebcc866 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -85,9 +85,9 @@ fixed = "1.23.1" rp-pac = { version = "6" } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11", optional = true} -embedded-hal-async = { version = "=0.2.0-alpha.2", optional = true} -embedded-hal-nb = { version = "=1.0.0-alpha.3", optional = true} +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1", optional = true} +embedded-hal-async = { version = "=1.0.0-rc.1", optional = true} +embedded-hal-nb = { version = "=1.0.0-rc.1", optional = true} paste = "1.0" pio-proc = {version= "0.2" } diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 58dc0bf1a..e57b72599 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -749,15 +749,15 @@ mod eh02 { mod eh1 { use super::*; - impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for BufferedUart<'d, T> { + impl<'d, T: Instance> embedded_hal_nb::serial::ErrorType for BufferedUartRx<'d, T> { type Error = Error; } - impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for BufferedUartTx<'d, T> { + impl<'d, T: Instance> embedded_hal_nb::serial::ErrorType for BufferedUartTx<'d, T> { type Error = Error; } - impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for BufferedUartRx<'d, T> { + impl<'d, T: Instance> embedded_hal_nb::serial::ErrorType for BufferedUart<'d, T> { type Error = Error; } @@ -767,16 +767,6 @@ mod eh1 { } } - impl<'d, T: Instance> embedded_hal_1::serial::Write for BufferedUartTx<'d, T> { - fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(buffer).map(drop) - } - - fn flush(&mut self) -> Result<(), Self::Error> { - self.blocking_flush() - } - } - impl<'d, T: Instance> embedded_hal_nb::serial::Write for BufferedUartTx<'d, T> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { self.blocking_write(&[char]).map(drop).map_err(nb::Error::Other) @@ -793,16 +783,6 @@ mod eh1 { } } - impl<'d, T: Instance> embedded_hal_1::serial::Write for BufferedUart<'d, T> { - fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(buffer).map(drop) - } - - fn flush(&mut self) -> Result<(), Self::Error> { - self.blocking_flush() - } - } - impl<'d, T: Instance> embedded_hal_nb::serial::Write for BufferedUart<'d, T> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { self.blocking_write(&[char]).map(drop).map_err(nb::Error::Other) diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 00070b80a..202b0883e 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -807,26 +807,26 @@ mod eh02 { mod eh1 { use super::*; - impl embedded_hal_1::serial::Error for Error { - fn kind(&self) -> embedded_hal_1::serial::ErrorKind { + impl embedded_hal_nb::serial::Error for Error { + fn kind(&self) -> embedded_hal_nb::serial::ErrorKind { match *self { - Self::Framing => embedded_hal_1::serial::ErrorKind::FrameFormat, - Self::Break => embedded_hal_1::serial::ErrorKind::Other, - Self::Overrun => embedded_hal_1::serial::ErrorKind::Overrun, - Self::Parity => embedded_hal_1::serial::ErrorKind::Parity, + Self::Framing => embedded_hal_nb::serial::ErrorKind::FrameFormat, + Self::Break => embedded_hal_nb::serial::ErrorKind::Other, + Self::Overrun => embedded_hal_nb::serial::ErrorKind::Overrun, + Self::Parity => embedded_hal_nb::serial::ErrorKind::Parity, } } } - impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::ErrorType for Uart<'d, T, M> { + impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::ErrorType for UartRx<'d, T, M> { type Error = Error; } - impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::ErrorType for UartTx<'d, T, M> { + impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::ErrorType for UartTx<'d, T, M> { type Error = Error; } - impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::ErrorType for UartRx<'d, T, M> { + impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::ErrorType for Uart<'d, T, M> { type Error = Error; } @@ -851,16 +851,6 @@ mod eh1 { } } - impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::Write for UartTx<'d, T, M> { - fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(buffer) - } - - fn flush(&mut self) -> Result<(), Self::Error> { - self.blocking_flush() - } - } - impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Write for UartTx<'d, T, M> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { self.blocking_write(&[char]).map_err(nb::Error::Other) @@ -877,16 +867,6 @@ mod eh1 { } } - impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::Write for Uart<'d, T, M> { - fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(buffer) - } - - fn flush(&mut self) -> Result<(), Self::Error> { - self.blocking_flush() - } - } - impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Write for Uart<'d, T, M> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { self.blocking_write(&[char]).map_err(nb::Error::Other) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 8f765e9d3..c3f5706e8 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -40,9 +40,9 @@ embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11", optional = true} -embedded-hal-async = { version = "=0.2.0-alpha.2", optional = true} -embedded-hal-nb = { version = "=1.0.0-alpha.3", optional = true} +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1", optional = true} +embedded-hal-async = { version = "=1.0.0-rc.1", optional = true} +embedded-hal-nb = { version = "=1.0.0-rc.1", optional = true} embedded-storage = "0.3.0" embedded-storage-async = { version = "0.4.0", optional = true } diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 329fc7da6..596d40bf9 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -584,15 +584,15 @@ mod eh02 { mod eh1 { use super::*; - impl<'d, T: BasicInstance> embedded_hal_1::serial::ErrorType for BufferedUart<'d, T> { + impl<'d, T: BasicInstance> embedded_hal_nb::serial::ErrorType for BufferedUart<'d, T> { type Error = Error; } - impl<'d, T: BasicInstance> embedded_hal_1::serial::ErrorType for BufferedUartTx<'d, T> { + impl<'d, T: BasicInstance> embedded_hal_nb::serial::ErrorType for BufferedUartTx<'d, T> { type Error = Error; } - impl<'d, T: BasicInstance> embedded_hal_1::serial::ErrorType for BufferedUartRx<'d, T> { + impl<'d, T: BasicInstance> embedded_hal_nb::serial::ErrorType for BufferedUartRx<'d, T> { type Error = Error; } @@ -602,16 +602,6 @@ mod eh1 { } } - impl<'d, T: BasicInstance> embedded_hal_1::serial::Write for BufferedUartTx<'d, T> { - fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(buffer).map(drop) - } - - fn flush(&mut self) -> Result<(), Self::Error> { - self.blocking_flush() - } - } - impl<'d, T: BasicInstance> embedded_hal_nb::serial::Write for BufferedUartTx<'d, T> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { self.blocking_write(&[char]).map(drop).map_err(nb::Error::Other) @@ -628,16 +618,6 @@ mod eh1 { } } - impl<'d, T: BasicInstance> embedded_hal_1::serial::Write for BufferedUart<'d, T> { - fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { - self.tx.blocking_write(buffer).map(drop) - } - - fn flush(&mut self) -> Result<(), Self::Error> { - self.tx.blocking_flush() - } - } - impl<'d, T: BasicInstance> embedded_hal_nb::serial::Write for BufferedUart<'d, T> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { self.tx.blocking_write(&[char]).map(drop).map_err(nb::Error::Other) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 0c69a4882..e203336e1 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -943,27 +943,27 @@ mod eh02 { mod eh1 { use super::*; - impl embedded_hal_1::serial::Error for Error { - fn kind(&self) -> embedded_hal_1::serial::ErrorKind { + impl embedded_hal_nb::serial::Error for Error { + fn kind(&self) -> embedded_hal_nb::serial::ErrorKind { match *self { - Self::Framing => embedded_hal_1::serial::ErrorKind::FrameFormat, - Self::Noise => embedded_hal_1::serial::ErrorKind::Noise, - Self::Overrun => embedded_hal_1::serial::ErrorKind::Overrun, - Self::Parity => embedded_hal_1::serial::ErrorKind::Parity, - Self::BufferTooLong => embedded_hal_1::serial::ErrorKind::Other, + Self::Framing => embedded_hal_nb::serial::ErrorKind::FrameFormat, + Self::Noise => embedded_hal_nb::serial::ErrorKind::Noise, + Self::Overrun => embedded_hal_nb::serial::ErrorKind::Overrun, + Self::Parity => embedded_hal_nb::serial::ErrorKind::Parity, + Self::BufferTooLong => embedded_hal_nb::serial::ErrorKind::Other, } } } - impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_1::serial::ErrorType for Uart<'d, T, TxDma, RxDma> { + impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_nb::serial::ErrorType for Uart<'d, T, TxDma, RxDma> { type Error = Error; } - impl<'d, T: BasicInstance, TxDma> embedded_hal_1::serial::ErrorType for UartTx<'d, T, TxDma> { + impl<'d, T: BasicInstance, TxDma> embedded_hal_nb::serial::ErrorType for UartTx<'d, T, TxDma> { type Error = Error; } - impl<'d, T: BasicInstance, RxDma> embedded_hal_1::serial::ErrorType for UartRx<'d, T, RxDma> { + impl<'d, T: BasicInstance, RxDma> embedded_hal_nb::serial::ErrorType for UartRx<'d, T, RxDma> { type Error = Error; } @@ -973,16 +973,6 @@ mod eh1 { } } - impl<'d, T: BasicInstance, TxDma> embedded_hal_1::serial::Write for UartTx<'d, T, TxDma> { - fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(buffer) - } - - fn flush(&mut self) -> Result<(), Self::Error> { - self.blocking_flush() - } - } - impl<'d, T: BasicInstance, TxDma> embedded_hal_nb::serial::Write for UartTx<'d, T, TxDma> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { self.blocking_write(&[char]).map_err(nb::Error::Other) @@ -999,16 +989,6 @@ mod eh1 { } } - impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_1::serial::Write for Uart<'d, T, TxDma, RxDma> { - fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(buffer) - } - - fn flush(&mut self) -> Result<(), Self::Error> { - self.blocking_flush() - } - } - impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_nb::serial::Write for Uart<'d, T, TxDma, RxDma> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { self.blocking_write(&[char]).map_err(nb::Error::Other) diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index 0afb1103d..ec1f2ec3f 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -152,8 +152,8 @@ defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6" } -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11", optional = true} -embedded-hal-async = { version = "=0.2.0-alpha.2", optional = true} +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1", optional = true} +embedded-hal-async = { version = "=1.0.0-rc.1", optional = true} futures-util = { version = "0.3.17", default-features = false } atomic-polyfill = "1.0.1" diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 8c6f6bccf..2ce44b516 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -19,6 +19,7 @@ nightly = [ "static_cell/nightly", "embassy-usb", "embedded-io-async", + "embedded-hal-bus/async", "embassy-net", "embassy-lora", "lora-phy", @@ -56,11 +57,14 @@ rand = { version = "0.8.4", default-features = false } embedded-storage = "0.3.0" usbd-hid = "0.6.0" serde = { version = "1.0.136", default-features = false } -embedded-hal = { version = "1.0.0-alpha.11" } -embedded-hal-async = { version = "0.2.0-alpha.2", optional = true } -embedded-hal-bus = { version = "0.1.0-alpha.3" } +embedded-hal = { version = "1.0.0-rc.1" } +embedded-hal-async = { version = "1.0.0-rc.1", optional = true } +embedded-hal-bus = { version = "0.1.0-rc.1" } num-integer = { version = "0.1.45", default-features = false } microfft = "0.5.0" [profile.release] debug = 2 + +[patch.crates-io] +lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "1323eccc1c470d4259f95f4f315d1be830d572a3"} \ No newline at end of file diff --git a/examples/nrf52840/src/bin/wifi_esp_hosted.rs b/examples/nrf52840/src/bin/wifi_esp_hosted.rs index e3b80d821..e114e50b9 100644 --- a/examples/nrf52840/src/bin/wifi_esp_hosted.rs +++ b/examples/nrf52840/src/bin/wifi_esp_hosted.rs @@ -11,7 +11,7 @@ use embassy_nrf::rng::Rng; use embassy_nrf::spim::{self, Spim}; use embassy_nrf::{bind_interrupts, peripherals}; use embassy_time::Delay; -use embedded_hal_async::spi::ExclusiveDevice; +use embedded_hal_bus::spi::ExclusiveDevice; use embedded_io_async::Write; use static_cell::make_static; use {defmt_rtt as _, embassy_net_esp_hosted as hosted, panic_probe as _}; diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 6742ce7ef..102611bc0 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -42,8 +42,9 @@ smart-leds = "0.3.0" heapless = "0.7.15" usbd-hid = "0.6.1" -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } -embedded-hal-async = "0.2.0-alpha.2" +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" } +embedded-hal-async = "1.0.0-rc.1" +embedded-hal-bus = { version = "0.1.0-rc.1", features = ["async"] } embedded-io-async = { version = "0.5.0", features = ["defmt-03"] } embedded-storage = { version = "0.3" } static_cell = { version = "1.1", features = ["nightly"]} @@ -53,4 +54,7 @@ pio = "0.2.1" rand = { version = "0.8.5", default-features = false } [profile.release] -debug = 2 \ No newline at end of file +debug = 2 + +[patch.crates-io] +lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "1323eccc1c470d4259f95f4f315d1be830d572a3"} \ No newline at end of file diff --git a/examples/rp/src/bin/ethernet_w5500_multisocket.rs b/examples/rp/src/bin/ethernet_w5500_multisocket.rs index d4af87642..c0fde62ab 100644 --- a/examples/rp/src/bin/ethernet_w5500_multisocket.rs +++ b/examples/rp/src/bin/ethernet_w5500_multisocket.rs @@ -17,7 +17,7 @@ use embassy_rp::gpio::{Input, Level, Output, Pull}; use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; use embassy_time::{Delay, Duration}; -use embedded_hal_async::spi::ExclusiveDevice; +use embedded_hal_bus::spi::ExclusiveDevice; use embedded_io_async::Write; use rand::RngCore; use static_cell::make_static; diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_client.rs b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs index 78340492e..e593acae4 100644 --- a/examples/rp/src/bin/ethernet_w5500_tcp_client.rs +++ b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs @@ -19,7 +19,7 @@ use embassy_rp::gpio::{Input, Level, Output, Pull}; use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; use embassy_time::{Delay, Duration, Timer}; -use embedded_hal_async::spi::ExclusiveDevice; +use embedded_hal_bus::spi::ExclusiveDevice; use embedded_io_async::Write; use rand::RngCore; use static_cell::make_static; diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_server.rs b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs index e6d132740..c62caed7a 100644 --- a/examples/rp/src/bin/ethernet_w5500_tcp_server.rs +++ b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs @@ -18,7 +18,7 @@ use embassy_rp::gpio::{Input, Level, Output, Pull}; use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; use embassy_time::{Delay, Duration}; -use embedded_hal_async::spi::ExclusiveDevice; +use embedded_hal_bus::spi::ExclusiveDevice; use embedded_io_async::Write; use rand::RngCore; use static_cell::make_static; diff --git a/examples/rp/src/bin/ethernet_w5500_udp.rs b/examples/rp/src/bin/ethernet_w5500_udp.rs index b546714df..76dabce1c 100644 --- a/examples/rp/src/bin/ethernet_w5500_udp.rs +++ b/examples/rp/src/bin/ethernet_w5500_udp.rs @@ -18,7 +18,7 @@ use embassy_rp::gpio::{Input, Level, Output, Pull}; use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; use embassy_time::Delay; -use embedded_hal_async::spi::ExclusiveDevice; +use embedded_hal_bus::spi::ExclusiveDevice; use rand::RngCore; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32h5/Cargo.toml b/examples/stm32h5/Cargo.toml index 44d0a9574..5d73e435e 100644 --- a/examples/stm32h5/Cargo.toml +++ b/examples/stm32h5/Cargo.toml @@ -20,8 +20,8 @@ defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } -embedded-hal-async = { version = "=0.2.0-alpha.2" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" } +embedded-hal-async = { version = "=1.0.0-rc.1" } embedded-nal-async = { version = "0.5.0" } panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index e2e5f9364..c78c4c602 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -20,8 +20,8 @@ defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } -embedded-hal-async = { version = "=0.2.0-alpha.2" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" } +embedded-hal-async = { version = "=1.0.0-rc.1" } embedded-nal-async = { version = "0.5.0" } panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index 33aa05e65..332a6c5e5 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -37,3 +37,6 @@ static_cell = "1.1" [profile.release] debug = 2 + +[patch.crates-io] +lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "1323eccc1c470d4259f95f4f315d1be830d572a3"} \ No newline at end of file diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index 3b27d8e81..944c8c27b 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -19,8 +19,8 @@ defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } -embedded-hal-async = { version = "=0.2.0-alpha.2" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" } +embedded-hal-async = { version = "=1.0.0-rc.1" } panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } heapless = { version = "0.7.5", default-features = false } diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index 48b69c8d0..5440807f6 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -30,3 +30,6 @@ chrono = { version = "^0.4", default-features = false } [profile.release] debug = 2 + +[patch.crates-io] +lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "1323eccc1c470d4259f95f4f315d1be830d572a3"} \ No newline at end of file diff --git a/tests/nrf/Cargo.toml b/tests/nrf/Cargo.toml index 974a9413f..e2dab81af 100644 --- a/tests/nrf/Cargo.toml +++ b/tests/nrf/Cargo.toml @@ -15,7 +15,8 @@ embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defm embedded-io-async = { version = "0.5.0" } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"] } embassy-net-esp-hosted = { version = "0.1.0", path = "../../embassy-net-esp-hosted", features = ["defmt"] } -embedded-hal-async = { version = "0.2.0-alpha.2" } +embedded-hal-async = { version = "1.0.0-rc.1" } +embedded-hal-bus = { version = "0.1.0-rc.1", features = ["async"] } static_cell = { version = "1.1", features = [ "nightly" ] } defmt = "0.3" diff --git a/tests/nrf/src/bin/wifi_esp_hosted_perf.rs b/tests/nrf/src/bin/wifi_esp_hosted_perf.rs index e2adfe0be..16055fae8 100644 --- a/tests/nrf/src/bin/wifi_esp_hosted_perf.rs +++ b/tests/nrf/src/bin/wifi_esp_hosted_perf.rs @@ -15,7 +15,7 @@ use embassy_nrf::rng::Rng; use embassy_nrf::spim::{self, Spim}; use embassy_nrf::{bind_interrupts, peripherals}; use embassy_time::{with_timeout, Delay, Duration, Timer}; -use embedded_hal_async::spi::ExclusiveDevice; +use embedded_hal_bus::spi::ExclusiveDevice; use static_cell::make_static; use {defmt_rtt as _, embassy_net_esp_hosted as hosted, panic_probe as _}; diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 90a3bd0cf..c69bf7a88 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -22,8 +22,8 @@ defmt-rtt = "0.4" cortex-m = { version = "0.7.6" } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } -embedded-hal-async = { version = "=0.2.0-alpha.2" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" } +embedded-hal-async = { version = "=1.0.0-rc.1" } panic-probe = { version = "0.3.0", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } embedded-io-async = { version = "0.5.0" } diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 17320649e..e26388976 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -41,8 +41,8 @@ defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } -embedded-hal-async = { version = "=0.2.0-alpha.2" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" } +embedded-hal-async = { version = "=1.0.0-rc.1" } micromath = "2.0.0" panic-probe = { version = "0.3.0", features = ["print-defmt"] } rand_core = { version = "0.6", default-features = false } From df6952648e15c56d2d8b3224a69641296a780eed Mon Sep 17 00:00:00 2001 From: Sebastian Goll Date: Wed, 16 Aug 2023 14:11:09 +0200 Subject: [PATCH 50/77] Make sure to check RCC settings for compatibility before applying --- embassy-stm32/src/rcc/f2.rs | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/embassy-stm32/src/rcc/f2.rs b/embassy-stm32/src/rcc/f2.rs index d016d1dea..ec4ed99b8 100644 --- a/embassy-stm32/src/rcc/f2.rs +++ b/embassy-stm32/src/rcc/f2.rs @@ -378,22 +378,6 @@ pub(crate) unsafe fn init(config: Config) { // Reference: STM32F215xx/217xx datasheet Table 13. General operating conditions assert!(ahb_freq <= Hertz(120_000_000)); - let flash_ws = unwrap!(config.voltage.wait_states(ahb_freq)); - FLASH.acr().modify(|w| w.set_latency(flash_ws)); - - RCC.cfgr().modify(|w| { - w.set_sw(sw.into()); - w.set_hpre(config.ahb_pre.into()); - w.set_ppre1(config.apb1_pre.into()); - w.set_ppre2(config.apb2_pre.into()); - }); - while RCC.cfgr().read().sws().to_bits() != sw.to_bits() {} - - // Turn off HSI to save power if we don't need it - if !config.hsi { - RCC.cr().modify(|w| w.set_hsion(false)); - } - let (apb1_freq, apb1_tim_freq) = match config.apb1_pre { APBPrescaler::NotDivided => (ahb_freq, ahb_freq), pre => { @@ -414,6 +398,22 @@ pub(crate) unsafe fn init(config: Config) { // Reference: STM32F215xx/217xx datasheet Table 13. General operating conditions assert!(apb2_freq <= Hertz(60_000_000)); + let flash_ws = unwrap!(config.voltage.wait_states(ahb_freq)); + FLASH.acr().modify(|w| w.set_latency(flash_ws)); + + RCC.cfgr().modify(|w| { + w.set_sw(sw.into()); + w.set_hpre(config.ahb_pre.into()); + w.set_ppre1(config.apb1_pre.into()); + w.set_ppre2(config.apb2_pre.into()); + }); + while RCC.cfgr().read().sws().to_bits() != sw.to_bits() {} + + // Turn off HSI to save power if we don't need it + if !config.hsi { + RCC.cr().modify(|w| w.set_hsion(false)); + } + set_freqs(Clocks { sys: sys_clk, ahb1: ahb_freq, From c80c3236340c7fa083a81bcbbe4d37525fc840b1 Mon Sep 17 00:00:00 2001 From: Olle Sandberg Date: Wed, 16 Aug 2023 13:57:03 +0200 Subject: [PATCH 51/77] stm32-wl: set RTC clock source on RCC init --- embassy-stm32/src/rcc/wl.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/embassy-stm32/src/rcc/wl.rs b/embassy-stm32/src/rcc/wl.rs index ea6e8dde6..f1dd2bd7e 100644 --- a/embassy-stm32/src/rcc/wl.rs +++ b/embassy-stm32/src/rcc/wl.rs @@ -2,6 +2,7 @@ pub use super::common::{AHBPrescaler, APBPrescaler, VoltageScale}; use crate::pac::pwr::vals::Dbp; use crate::pac::{FLASH, PWR, RCC}; use crate::rcc::{set_freqs, Clocks}; +use crate::rtc::{Rtc, RtcClockSource as RCS}; use crate::time::Hertz; /// Most of clock setup is copied from stm32l0xx-hal, and adopted to the generated PAC, @@ -229,6 +230,8 @@ pub(crate) unsafe fn init(config: Config) { // Wait until LSE is running while !RCC.bdcr().read().lserdy() {} + + Rtc::set_clock_source(RCS::LSE); } RtcClockSource::LSI32 => { // Turn on the internal 32 kHz LSI oscillator @@ -236,6 +239,8 @@ pub(crate) unsafe fn init(config: Config) { // Wait until LSI is running while !RCC.csr().read().lsirdy() {} + + Rtc::set_clock_source(RCS::LSI); } } From 8a9f49c163f2e559c87e54b7abf819ea026f04f6 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 16 Aug 2023 17:51:47 +0200 Subject: [PATCH 52/77] net-wiznet: add HIL test using w5100s. --- tests/rp/Cargo.toml | 5 +- tests/rp/src/bin/adc.rs | 3 +- tests/rp/src/bin/cyw43-perf.rs | 3 +- tests/rp/src/bin/dma_copy_async.rs | 3 +- tests/rp/src/bin/ethernet_w5100s_perf.rs | 249 +++++++++++++++++++++++ tests/rp/src/bin/flash.rs | 3 +- tests/rp/src/bin/float.rs | 3 +- tests/rp/src/bin/gpio.rs | 3 +- tests/rp/src/bin/gpio_async.rs | 3 +- tests/rp/src/bin/gpio_multicore.rs | 3 +- tests/rp/src/bin/multicore.rs | 3 +- tests/rp/src/bin/pio_irq.rs | 3 +- tests/rp/src/bin/pio_multi_load.rs | 3 +- tests/rp/src/bin/pwm.rs | 3 +- tests/rp/src/bin/spi.rs | 3 +- tests/rp/src/bin/spi_async.rs | 3 +- tests/rp/src/bin/uart.rs | 3 +- tests/rp/src/bin/uart_buffered.rs | 3 +- tests/rp/src/bin/uart_dma.rs | 3 +- tests/rp/src/bin/uart_upgrade.rs | 3 +- tests/rp/src/common.rs | 1 - 21 files changed, 271 insertions(+), 38 deletions(-) create mode 100644 tests/rp/src/bin/ethernet_w5100s_perf.rs delete mode 100644 tests/rp/src/common.rs diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index c69bf7a88..6a3df4b9c 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -9,10 +9,11 @@ teleprobe-meta = "1.1" embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "nightly", "unstable-traits"] } embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver", "critical-section-impl", "intrinsics", "rom-v2-intrinsics", "run-from-ram"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "udp", "dhcpv4", "medium-ethernet"] } +embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] } cyw43 = { path = "../../cyw43", features = ["defmt", "firmware-logs"] } cyw43-pio = { path = "../../cyw43-pio", features = ["defmt", "overclock"] } @@ -24,6 +25,7 @@ cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" 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"] } panic-probe = { version = "0.3.0", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } embedded-io-async = { version = "0.5.0" } @@ -31,6 +33,7 @@ embedded-storage = { version = "0.3" } static_cell = { version = "1.1", features = ["nightly"]} pio = "0.2" pio-proc = "0.2" +rand = { version = "0.8.5", default-features = false } [profile.dev] debug = 2 diff --git a/tests/rp/src/bin/adc.rs b/tests/rp/src/bin/adc.rs index b29a3a7cb..0250fd5f4 100644 --- a/tests/rp/src/bin/adc.rs +++ b/tests/rp/src/bin/adc.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::*; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/cyw43-perf.rs b/tests/rp/src/bin/cyw43-perf.rs index fffdabc9b..1c665f95d 100644 --- a/tests/rp/src/bin/cyw43-perf.rs +++ b/tests/rp/src/bin/cyw43-perf.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use cyw43_pio::PioSpi; use defmt::{assert, panic, *}; diff --git a/tests/rp/src/bin/dma_copy_async.rs b/tests/rp/src/bin/dma_copy_async.rs index 2c0b559a9..b071ed9df 100644 --- a/tests/rp/src/bin/dma_copy_async.rs +++ b/tests/rp/src/bin/dma_copy_async.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::{assert_eq, *}; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/ethernet_w5100s_perf.rs b/tests/rp/src/bin/ethernet_w5100s_perf.rs new file mode 100644 index 000000000..faa8638c0 --- /dev/null +++ b/tests/rp/src/bin/ethernet_w5100s_perf.rs @@ -0,0 +1,249 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] +teleprobe_meta::target!(b"w5100s-evb-pico"); +teleprobe_meta::timeout!(120); + +use defmt::{assert, *}; +use embassy_executor::Spawner; +use embassy_futures::join::join; +use embassy_net::tcp::TcpSocket; +use embassy_net::{Ipv4Address, Stack, StackResources}; +use embassy_net_wiznet::chip::W5100S; +use embassy_net_wiznet::*; +use embassy_rp::clocks::RoscRng; +use embassy_rp::gpio::{Input, Level, Output, Pull}; +use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; +use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; +use embassy_time::{with_timeout, Delay, Duration, Timer}; +use embedded_hal_bus::spi::ExclusiveDevice; +use rand::RngCore; +use static_cell::make_static; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::task] +async fn ethernet_task( + runner: Runner< + 'static, + W5100S, + ExclusiveDevice, Output<'static, PIN_17>, Delay>, + Input<'static, PIN_21>, + Output<'static, PIN_20>, + >, +) -> ! { + runner.run().await +} + +#[embassy_executor::task] +async fn net_task(stack: &'static Stack>) -> ! { + stack.run().await +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + let mut rng = RoscRng; + + let mut spi_cfg = SpiConfig::default(); + spi_cfg.frequency = 50_000_000; + let (miso, mosi, clk) = (p.PIN_16, p.PIN_19, p.PIN_18); + let spi = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, spi_cfg); + let cs = Output::new(p.PIN_17, Level::High); + let w5500_int = Input::new(p.PIN_21, Pull::Up); + let w5500_reset = Output::new(p.PIN_20, Level::High); + + let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; + let state = make_static!(State::<8, 8>::new()); + let (device, runner) = embassy_net_wiznet::new( + mac_addr, + state, + ExclusiveDevice::new(spi, cs, Delay), + w5500_int, + w5500_reset, + ) + .await; + unwrap!(spawner.spawn(ethernet_task(runner))); + + // Generate random seed + let seed = rng.next_u64(); + + // Init network stack + let stack = &*make_static!(Stack::new( + device, + embassy_net::Config::dhcpv4(Default::default()), + make_static!(StackResources::<2>::new()), + seed + )); + + // Launch network task + unwrap!(spawner.spawn(net_task(&stack))); + + info!("Waiting for DHCP up..."); + while stack.config_v4().is_none() { + Timer::after(Duration::from_millis(100)).await; + } + info!("IP addressing up!"); + + let down = test_download(stack).await; + let up = test_upload(stack).await; + let updown = test_upload_download(stack).await; + + assert!(down > TEST_EXPECTED_DOWNLOAD_KBPS); + assert!(up > TEST_EXPECTED_UPLOAD_KBPS); + assert!(updown > TEST_EXPECTED_UPLOAD_DOWNLOAD_KBPS); + + info!("Test OK"); + cortex_m::asm::bkpt(); +} + +const TEST_DURATION: usize = 10; +const TEST_EXPECTED_DOWNLOAD_KBPS: usize = 500; +const TEST_EXPECTED_UPLOAD_KBPS: usize = 500; +const TEST_EXPECTED_UPLOAD_DOWNLOAD_KBPS: usize = 300; +const RX_BUFFER_SIZE: usize = 4096; +const TX_BUFFER_SIZE: usize = 4096; +const SERVER_ADDRESS: Ipv4Address = Ipv4Address::new(192, 168, 2, 2); +const DOWNLOAD_PORT: u16 = 4321; +const UPLOAD_PORT: u16 = 4322; +const UPLOAD_DOWNLOAD_PORT: u16 = 4323; + +async fn test_download(stack: &'static Stack>) -> usize { + info!("Testing download..."); + + let mut rx_buffer = [0; RX_BUFFER_SIZE]; + let mut tx_buffer = [0; TX_BUFFER_SIZE]; + let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + socket.set_timeout(Some(Duration::from_secs(10))); + + info!("connecting to {:?}:{}...", SERVER_ADDRESS, DOWNLOAD_PORT); + if let Err(e) = socket.connect((SERVER_ADDRESS, DOWNLOAD_PORT)).await { + error!("connect error: {:?}", e); + return 0; + } + info!("connected, testing..."); + + let mut rx_buf = [0; 4096]; + let mut total: usize = 0; + with_timeout(Duration::from_secs(TEST_DURATION as _), async { + loop { + match socket.read(&mut rx_buf).await { + Ok(0) => { + error!("read EOF"); + return 0; + } + Ok(n) => total += n, + Err(e) => { + error!("read error: {:?}", e); + return 0; + } + } + } + }) + .await + .ok(); + + let kbps = (total + 512) / 1024 / TEST_DURATION; + info!("download: {} kB/s", kbps); + kbps +} + +async fn test_upload(stack: &'static Stack>) -> usize { + info!("Testing upload..."); + + let mut rx_buffer = [0; RX_BUFFER_SIZE]; + let mut tx_buffer = [0; TX_BUFFER_SIZE]; + let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + socket.set_timeout(Some(Duration::from_secs(10))); + + info!("connecting to {:?}:{}...", SERVER_ADDRESS, UPLOAD_PORT); + if let Err(e) = socket.connect((SERVER_ADDRESS, UPLOAD_PORT)).await { + error!("connect error: {:?}", e); + return 0; + } + info!("connected, testing..."); + + let buf = [0; 4096]; + let mut total: usize = 0; + with_timeout(Duration::from_secs(TEST_DURATION as _), async { + loop { + match socket.write(&buf).await { + Ok(0) => { + error!("write zero?!??!?!"); + return 0; + } + Ok(n) => total += n, + Err(e) => { + error!("write error: {:?}", e); + return 0; + } + } + } + }) + .await + .ok(); + + let kbps = (total + 512) / 1024 / TEST_DURATION; + info!("upload: {} kB/s", kbps); + kbps +} + +async fn test_upload_download(stack: &'static Stack>) -> usize { + info!("Testing upload+download..."); + + let mut rx_buffer = [0; RX_BUFFER_SIZE]; + let mut tx_buffer = [0; TX_BUFFER_SIZE]; + let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + socket.set_timeout(Some(Duration::from_secs(10))); + + info!("connecting to {:?}:{}...", SERVER_ADDRESS, UPLOAD_DOWNLOAD_PORT); + if let Err(e) = socket.connect((SERVER_ADDRESS, UPLOAD_DOWNLOAD_PORT)).await { + error!("connect error: {:?}", e); + return 0; + } + info!("connected, testing..."); + + let (mut reader, mut writer) = socket.split(); + + let tx_buf = [0; 4096]; + let mut rx_buf = [0; 4096]; + let mut total: usize = 0; + let tx_fut = async { + loop { + match writer.write(&tx_buf).await { + Ok(0) => { + error!("write zero?!??!?!"); + return 0; + } + Ok(_) => {} + Err(e) => { + error!("write error: {:?}", e); + return 0; + } + } + } + }; + + let rx_fut = async { + loop { + match reader.read(&mut rx_buf).await { + Ok(0) => { + error!("read EOF"); + return 0; + } + Ok(n) => total += n, + Err(e) => { + error!("read error: {:?}", e); + return 0; + } + } + } + }; + + with_timeout(Duration::from_secs(TEST_DURATION as _), join(tx_fut, rx_fut)) + .await + .ok(); + + let kbps = (total + 512) / 1024 / TEST_DURATION; + info!("upload+download: {} kB/s", kbps); + kbps +} diff --git a/tests/rp/src/bin/flash.rs b/tests/rp/src/bin/flash.rs index c31d6decf..6e65fbdc0 100644 --- a/tests/rp/src/bin/flash.rs +++ b/tests/rp/src/bin/flash.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::*; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/float.rs b/tests/rp/src/bin/float.rs index 0e0de85fa..2874aa910 100644 --- a/tests/rp/src/bin/float.rs +++ b/tests/rp/src/bin/float.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::*; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/gpio.rs b/tests/rp/src/bin/gpio.rs index 946b7dc88..1a43a9283 100644 --- a/tests/rp/src/bin/gpio.rs +++ b/tests/rp/src/bin/gpio.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::{assert, *}; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/gpio_async.rs b/tests/rp/src/bin/gpio_async.rs index 532494de5..60c65b7a0 100644 --- a/tests/rp/src/bin/gpio_async.rs +++ b/tests/rp/src/bin/gpio_async.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::{assert, *}; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/gpio_multicore.rs b/tests/rp/src/bin/gpio_multicore.rs index 780112bc1..22be78248 100644 --- a/tests/rp/src/bin/gpio_multicore.rs +++ b/tests/rp/src/bin/gpio_multicore.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::{info, unwrap}; use embassy_executor::Executor; diff --git a/tests/rp/src/bin/multicore.rs b/tests/rp/src/bin/multicore.rs index 114889dec..ec794c48a 100644 --- a/tests/rp/src/bin/multicore.rs +++ b/tests/rp/src/bin/multicore.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::{info, unwrap}; use embassy_executor::Executor; diff --git a/tests/rp/src/bin/pio_irq.rs b/tests/rp/src/bin/pio_irq.rs index bdea63eaa..c71e55d91 100644 --- a/tests/rp/src/bin/pio_irq.rs +++ b/tests/rp/src/bin/pio_irq.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::info; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/pio_multi_load.rs b/tests/rp/src/bin/pio_multi_load.rs index 356f16795..6343ff3a9 100644 --- a/tests/rp/src/bin/pio_multi_load.rs +++ b/tests/rp/src/bin/pio_multi_load.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::info; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/pwm.rs b/tests/rp/src/bin/pwm.rs index c71d21ef9..8c02b8441 100644 --- a/tests/rp/src/bin/pwm.rs +++ b/tests/rp/src/bin/pwm.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::{assert, assert_eq, assert_ne, *}; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/spi.rs b/tests/rp/src/bin/spi.rs index 84dfa5a2c..f347b80ca 100644 --- a/tests/rp/src/bin/spi.rs +++ b/tests/rp/src/bin/spi.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::{assert_eq, *}; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/spi_async.rs b/tests/rp/src/bin/spi_async.rs index a4080b03d..80d5a7147 100644 --- a/tests/rp/src/bin/spi_async.rs +++ b/tests/rp/src/bin/spi_async.rs @@ -4,8 +4,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::{assert_eq, *}; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/uart.rs b/tests/rp/src/bin/uart.rs index 2331c7d36..00f3e1949 100644 --- a/tests/rp/src/bin/uart.rs +++ b/tests/rp/src/bin/uart.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::{assert_eq, *}; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/uart_buffered.rs b/tests/rp/src/bin/uart_buffered.rs index edabf55b7..6ab7de29e 100644 --- a/tests/rp/src/bin/uart_buffered.rs +++ b/tests/rp/src/bin/uart_buffered.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::{assert_eq, panic, *}; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/uart_dma.rs b/tests/rp/src/bin/uart_dma.rs index fee6c825d..cd4af1ef2 100644 --- a/tests/rp/src/bin/uart_dma.rs +++ b/tests/rp/src/bin/uart_dma.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::{assert_eq, *}; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/uart_upgrade.rs b/tests/rp/src/bin/uart_upgrade.rs index effd0bc49..c07fc08cd 100644 --- a/tests/rp/src/bin/uart_upgrade.rs +++ b/tests/rp/src/bin/uart_upgrade.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::{assert_eq, *}; use embassy_executor::Spawner; diff --git a/tests/rp/src/common.rs b/tests/rp/src/common.rs deleted file mode 100644 index 955674f27..000000000 --- a/tests/rp/src/common.rs +++ /dev/null @@ -1 +0,0 @@ -teleprobe_meta::target!(b"rpi-pico"); From bb275f7e25e9f0a3cceb3045bceb1822167e7b31 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 16 Aug 2023 20:06:15 +0200 Subject: [PATCH 53/77] nrf: enable defmt for embassy-hal-internal. --- ci.sh | 2 +- embassy-nrf/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ci.sh b/ci.sh index 146a1c508..f82cc6273 100755 --- a/ci.sh +++ b/ci.sh @@ -3,7 +3,7 @@ set -euo pipefail export RUSTFLAGS=-Dwarnings -export DEFMT_LOG=trace,embassy_net_esp_hosted=debug,cyw43=info,cyw43_pio=info,smoltcp=info +export DEFMT_LOG=trace,embassy_hal_internal=debug,embassy_net_esp_hosted=debug,cyw43=info,cyw43_pio=info,smoltcp=info TARGET=$(rustc -vV | sed -n 's|host: ||p') diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index c4790f2c6..67ec4eb93 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -32,7 +32,7 @@ rt = [ time = ["dep:embassy-time"] -defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-usb-driver?/defmt", "embassy-embedded-hal/defmt"] +defmt = ["dep:defmt", "embassy-hal-internal/defmt", "embassy-sync/defmt", "embassy-usb-driver?/defmt", "embassy-embedded-hal/defmt"] # Enable nightly-only features nightly = ["embedded-hal-1", "embedded-hal-async", "dep:embassy-usb-driver", "embedded-storage-async", "dep:embedded-io-async", "embassy-embedded-hal/nightly"] From 065b0f34af4215cff81a799044f73980ed751120 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 17 Aug 2023 00:11:15 +0200 Subject: [PATCH 54/77] net-esp-hosted: sane error handling in control requests. --- embassy-net-esp-hosted/src/control.rs | 126 +++++++++++-------- embassy-net-esp-hosted/src/lib.rs | 9 +- examples/nrf52840/src/bin/wifi_esp_hosted.rs | 4 +- tests/nrf/src/bin/wifi_esp_hosted_perf.rs | 4 +- 4 files changed, 79 insertions(+), 64 deletions(-) diff --git a/embassy-net-esp-hosted/src/control.rs b/embassy-net-esp-hosted/src/control.rs index 37f220da0..b722bdd71 100644 --- a/embassy-net-esp-hosted/src/control.rs +++ b/embassy-net-esp-hosted/src/control.rs @@ -5,9 +5,12 @@ use heapless::String; use crate::ioctl::Shared; use crate::proto::{self, CtrlMsg}; -#[derive(Debug)] -pub struct Error { - pub status: u32, +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Error { + Failed(u32), + Timeout, + Internal, } pub struct Control<'a> { @@ -23,58 +26,68 @@ enum WifiMode { ApSta = 3, } +macro_rules! ioctl { + ($self:ident, $req_variant:ident, $resp_variant:ident, $req:ident, $resp:ident) => { + let mut msg = proto::CtrlMsg { + msg_id: proto::CtrlMsgId::$req_variant as _, + msg_type: proto::CtrlMsgType::Req as _, + payload: Some(proto::CtrlMsgPayload::$req_variant($req)), + }; + $self.ioctl(&mut msg).await?; + let Some(proto::CtrlMsgPayload::$resp_variant($resp)) = msg.payload else { + warn!("unexpected response variant"); + return Err(Error::Internal); + }; + if $resp.resp != 0 { + return Err(Error::Failed($resp.resp)); + } + }; +} + impl<'a> Control<'a> { pub(crate) fn new(state_ch: ch::StateRunner<'a>, shared: &'a Shared) -> Self { Self { state_ch, shared } } - pub async fn init(&mut self) { + pub async fn init(&mut self) -> Result<(), Error> { debug!("wait for init event..."); self.shared.init_wait().await; debug!("set wifi mode"); - self.set_wifi_mode(WifiMode::Sta as _).await; + self.set_wifi_mode(WifiMode::Sta as _).await?; - let mac_addr = self.get_mac_addr().await; + let mac_addr = self.get_mac_addr().await?; debug!("mac addr: {:02x}", mac_addr); self.state_ch.set_ethernet_address(mac_addr); + + Ok(()) } - pub async fn join(&mut self, ssid: &str, password: &str) { - let req = proto::CtrlMsg { - msg_id: proto::CtrlMsgId::ReqConnectAp as _, - msg_type: proto::CtrlMsgType::Req as _, - payload: Some(proto::CtrlMsgPayload::ReqConnectAp(proto::CtrlMsgReqConnectAp { - ssid: String::from(ssid), - pwd: String::from(password), - bssid: String::new(), - listen_interval: 3, - is_wpa3_supported: false, - })), + pub async fn connect(&mut self, ssid: &str, password: &str) -> Result<(), Error> { + let req = proto::CtrlMsgReqConnectAp { + ssid: String::from(ssid), + pwd: String::from(password), + bssid: String::new(), + listen_interval: 3, + is_wpa3_supported: false, }; - let resp = self.ioctl(req).await; - let proto::CtrlMsgPayload::RespConnectAp(resp) = resp.payload.unwrap() else { - panic!("unexpected resp") - }; - assert_eq!(resp.resp, 0); + ioctl!(self, ReqConnectAp, RespConnectAp, req, resp); self.state_ch.set_link_state(LinkState::Up); + Ok(()) } - async fn get_mac_addr(&mut self) -> [u8; 6] { - let req = proto::CtrlMsg { - msg_id: proto::CtrlMsgId::ReqGetMacAddress as _, - msg_type: proto::CtrlMsgType::Req as _, - payload: Some(proto::CtrlMsgPayload::ReqGetMacAddress( - proto::CtrlMsgReqGetMacAddress { - mode: WifiMode::Sta as _, - }, - )), + pub async fn disconnect(&mut self) -> Result<(), Error> { + let req = proto::CtrlMsgReqGetStatus {}; + ioctl!(self, ReqDisconnectAp, RespDisconnectAp, req, resp); + self.state_ch.set_link_state(LinkState::Up); + Ok(()) + } + + async fn get_mac_addr(&mut self) -> Result<[u8; 6], Error> { + let req = proto::CtrlMsgReqGetMacAddress { + mode: WifiMode::Sta as _, }; - let resp = self.ioctl(req).await; - let proto::CtrlMsgPayload::RespGetMacAddress(resp) = resp.payload.unwrap() else { - panic!("unexpected resp") - }; - assert_eq!(resp.resp, 0); + ioctl!(self, ReqGetMacAddress, RespGetMacAddress, req, resp); // WHY IS THIS A STRING? WHYYYY fn nibble_from_hex(b: u8) -> u8 { @@ -88,32 +101,32 @@ impl<'a> Control<'a> { let mac = resp.mac.as_bytes(); let mut res = [0; 6]; - assert_eq!(mac.len(), 17); + if mac.len() != 17 { + warn!("unexpected MAC respnse length"); + return Err(Error::Internal); + } for (i, b) in res.iter_mut().enumerate() { *b = (nibble_from_hex(mac[i * 3]) << 4) | nibble_from_hex(mac[i * 3 + 1]) } - res + Ok(res) } - async fn set_wifi_mode(&mut self, mode: u32) { - let req = proto::CtrlMsg { - msg_id: proto::CtrlMsgId::ReqSetWifiMode as _, - msg_type: proto::CtrlMsgType::Req as _, - payload: Some(proto::CtrlMsgPayload::ReqSetWifiMode(proto::CtrlMsgReqSetMode { mode })), - }; - let resp = self.ioctl(req).await; - let proto::CtrlMsgPayload::RespSetWifiMode(resp) = resp.payload.unwrap() else { - panic!("unexpected resp") - }; - assert_eq!(resp.resp, 0); + async fn set_wifi_mode(&mut self, mode: u32) -> Result<(), Error> { + let req = proto::CtrlMsgReqSetMode { mode }; + ioctl!(self, ReqSetWifiMode, RespSetWifiMode, req, resp); + + Ok(()) } - async fn ioctl(&mut self, req: CtrlMsg) -> CtrlMsg { - debug!("ioctl req: {:?}", &req); + async fn ioctl(&mut self, msg: &mut CtrlMsg) -> Result<(), Error> { + debug!("ioctl req: {:?}", &msg); let mut buf = [0u8; 128]; - let req_len = noproto::write(&req, &mut buf).unwrap(); + let req_len = noproto::write(msg, &mut buf).map_err(|_| { + warn!("failed to serialize control request"); + Error::Internal + })?; struct CancelOnDrop<'a>(&'a Shared); @@ -135,9 +148,12 @@ impl<'a> Control<'a> { ioctl.defuse(); - let res = noproto::read(&buf[..resp_len]).unwrap(); - debug!("ioctl resp: {:?}", &res); + *msg = noproto::read(&buf[..resp_len]).map_err(|_| { + warn!("failed to serialize control request"); + Error::Internal + })?; + debug!("ioctl resp: {:?}", msg); - res + Ok(()) } } diff --git a/embassy-net-esp-hosted/src/lib.rs b/embassy-net-esp-hosted/src/lib.rs index 96fddce58..b1fa775cd 100644 --- a/embassy-net-esp-hosted/src/lib.rs +++ b/embassy-net-esp-hosted/src/lib.rs @@ -1,17 +1,14 @@ #![no_std] -use control::Control; use embassy_futures::select::{select3, Either3}; use embassy_net_driver_channel as ch; use embassy_time::{Duration, Instant, Timer}; use embedded_hal::digital::{InputPin, OutputPin}; use embedded_hal_async::digital::Wait; use embedded_hal_async::spi::SpiDevice; -use ioctl::Shared; -use proto::CtrlMsg; -use crate::ioctl::PendingIoctl; -use crate::proto::CtrlMsgPayload; +use crate::ioctl::{PendingIoctl, Shared}; +use crate::proto::{CtrlMsg, CtrlMsgPayload}; mod proto; @@ -21,6 +18,8 @@ mod fmt; mod control; mod ioctl; +pub use control::*; + const MTU: usize = 1514; macro_rules! impl_bytes { diff --git a/examples/nrf52840/src/bin/wifi_esp_hosted.rs b/examples/nrf52840/src/bin/wifi_esp_hosted.rs index e114e50b9..a60822fd9 100644 --- a/examples/nrf52840/src/bin/wifi_esp_hosted.rs +++ b/examples/nrf52840/src/bin/wifi_esp_hosted.rs @@ -72,8 +72,8 @@ async fn main(spawner: Spawner) { unwrap!(spawner.spawn(wifi_task(runner))); - control.init().await; - control.join(WIFI_NETWORK, WIFI_PASSWORD).await; + unwrap!(control.init().await); + unwrap!(control.connect(WIFI_NETWORK, WIFI_PASSWORD).await); let config = embassy_net::Config::dhcpv4(Default::default()); // let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 { diff --git a/tests/nrf/src/bin/wifi_esp_hosted_perf.rs b/tests/nrf/src/bin/wifi_esp_hosted_perf.rs index 16055fae8..4b221a81b 100644 --- a/tests/nrf/src/bin/wifi_esp_hosted_perf.rs +++ b/tests/nrf/src/bin/wifi_esp_hosted_perf.rs @@ -76,8 +76,8 @@ async fn main(spawner: Spawner) { unwrap!(spawner.spawn(wifi_task(runner))); - control.init().await; - control.join(WIFI_NETWORK, WIFI_PASSWORD).await; + unwrap!(control.init().await); + unwrap!(control.connect(WIFI_NETWORK, WIFI_PASSWORD).await); // Generate random seed let mut rng = Rng::new(p.RNG, Irqs); From ef7523e5b7a36f0f15b77d4e673afdda0df51f3a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 17 Aug 2023 00:53:25 +0200 Subject: [PATCH 55/77] net-esp-hosted: put link down on wifi disconnect. --- embassy-net-esp-hosted/src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/embassy-net-esp-hosted/src/lib.rs b/embassy-net-esp-hosted/src/lib.rs index b1fa775cd..c2d9d6097 100644 --- a/embassy-net-esp-hosted/src/lib.rs +++ b/embassy-net-esp-hosted/src/lib.rs @@ -128,6 +128,7 @@ where let mut runner = Runner { ch: ch_runner, + state_ch, shared: &state.shared, next_seq: 1, handshake, @@ -142,6 +143,7 @@ where pub struct Runner<'a, SPI, IN, OUT> { ch: ch::Runner<'a, MTU>, + state_ch: ch::StateRunner<'a>, shared: &'a Shared, next_seq: u16, @@ -322,6 +324,10 @@ where match payload { CtrlMsgPayload::EventEspInit(_) => self.shared.init_done(), + CtrlMsgPayload::EventStationDisconnectFromAp(e) => { + info!("disconnected, code {}", e.resp); + self.state_ch.set_link_state(LinkState::Down); + } _ => {} } } From 1cb76e0d99f8da12891491b31176c8247a7a81a4 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 17 Aug 2023 00:57:54 +0200 Subject: [PATCH 56/77] net-esp-hosted: enable heartbeats from esp32 to detect if it crashes. --- embassy-net-esp-hosted/src/control.rs | 10 ++++++++++ embassy-net-esp-hosted/src/lib.rs | 21 +++++++++++++++------ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/embassy-net-esp-hosted/src/control.rs b/embassy-net-esp-hosted/src/control.rs index b722bdd71..6cd57f68a 100644 --- a/embassy-net-esp-hosted/src/control.rs +++ b/embassy-net-esp-hosted/src/control.rs @@ -53,6 +53,9 @@ impl<'a> Control<'a> { debug!("wait for init event..."); self.shared.init_wait().await; + debug!("set heartbeat"); + self.set_heartbeat(10).await?; + debug!("set wifi mode"); self.set_wifi_mode(WifiMode::Sta as _).await?; @@ -83,6 +86,13 @@ impl<'a> Control<'a> { Ok(()) } + /// duration in seconds, clamped to [10, 3600] + async fn set_heartbeat(&mut self, duration: u32) -> Result<(), Error> { + let req = proto::CtrlMsgReqConfigHeartbeat { enable: true, duration }; + ioctl!(self, ReqConfigHeartbeat, RespConfigHeartbeat, req, resp); + Ok(()) + } + async fn get_mac_addr(&mut self) -> Result<[u8; 6], Error> { let req = proto::CtrlMsgReqGetMacAddress { mode: WifiMode::Sta as _, diff --git a/embassy-net-esp-hosted/src/lib.rs b/embassy-net-esp-hosted/src/lib.rs index c2d9d6097..4a318b20d 100644 --- a/embassy-net-esp-hosted/src/lib.rs +++ b/embassy-net-esp-hosted/src/lib.rs @@ -1,7 +1,8 @@ #![no_std] -use embassy_futures::select::{select3, Either3}; +use embassy_futures::select::{select4, Either4}; use embassy_net_driver_channel as ch; +use embassy_net_driver_channel::driver::LinkState; use embassy_time::{Duration, Instant, Timer}; use embedded_hal::digital::{InputPin, OutputPin}; use embedded_hal_async::digital::Wait; @@ -94,6 +95,7 @@ enum InterfaceType { } const MAX_SPI_BUFFER_SIZE: usize = 1600; +const HEARTBEAT_MAX_GAP: Duration = Duration::from_secs(20); pub struct State { shared: Shared, @@ -135,6 +137,7 @@ where ready, reset, spi, + heartbeat_deadline: Instant::now() + HEARTBEAT_MAX_GAP, }; runner.init().await; @@ -147,6 +150,7 @@ pub struct Runner<'a, SPI, IN, OUT> { shared: &'a Shared, next_seq: u16, + heartbeat_deadline: Instant, spi: SPI, handshake: IN, @@ -178,9 +182,10 @@ where let ioctl = self.shared.ioctl_wait_pending(); let tx = self.ch.tx_buf(); let ev = async { self.ready.wait_for_high().await.unwrap() }; + let hb = Timer::at(self.heartbeat_deadline); - match select3(ioctl, tx, ev).await { - Either3::First(PendingIoctl { buf, req_len }) => { + match select4(ioctl, tx, ev, hb).await { + Either4::First(PendingIoctl { buf, req_len }) => { tx_buf[12..24].copy_from_slice(b"\x01\x08\x00ctrlResp\x02"); tx_buf[24..26].copy_from_slice(&(req_len as u16).to_le_bytes()); tx_buf[26..][..req_len].copy_from_slice(&unsafe { &*buf }[..req_len]); @@ -199,7 +204,7 @@ where header.checksum = checksum(&tx_buf[..26 + req_len]); tx_buf[0..12].copy_from_slice(&header.to_bytes()); } - Either3::Second(packet) => { + Either4::Second(packet) => { tx_buf[12..][..packet.len()].copy_from_slice(packet); let mut header = PayloadHeader { @@ -218,9 +223,12 @@ where self.ch.tx_done(); } - Either3::Third(()) => { + Either4::Third(()) => { tx_buf[..PayloadHeader::SIZE].fill(0); } + Either4::Fourth(()) => { + panic!("heartbeat from esp32 stopped") + } } if tx_buf[0] != 0 { @@ -309,7 +317,7 @@ where } } - fn handle_event(&self, data: &[u8]) { + fn handle_event(&mut self, data: &[u8]) { let Ok(event) = noproto::read::(data) else { warn!("failed to parse event"); return; @@ -324,6 +332,7 @@ where match payload { CtrlMsgPayload::EventEspInit(_) => self.shared.init_done(), + CtrlMsgPayload::EventHeartbeat(_) => self.heartbeat_deadline = Instant::now() + HEARTBEAT_MAX_GAP, CtrlMsgPayload::EventStationDisconnectFromAp(e) => { info!("disconnected, code {}", e.resp); self.state_ch.set_link_state(LinkState::Down); From 62e66cdda39d3689c5884bd883621f5fb7553117 Mon Sep 17 00:00:00 2001 From: Adam Rizkalla Date: Thu, 17 Aug 2023 19:16:03 -0500 Subject: [PATCH 57/77] stm32l4: set pwren in rcc regardless of clock source --- embassy-stm32/src/rcc/l4.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/rcc/l4.rs b/embassy-stm32/src/rcc/l4.rs index b2828e58e..b34b8caab 100644 --- a/embassy-stm32/src/rcc/l4.rs +++ b/embassy-stm32/src/rcc/l4.rs @@ -410,10 +410,11 @@ pub(crate) unsafe fn init(config: Config) { while RCC.cfgr().read().sws() != Sw::MSI {} } + RCC.apb1enr1().modify(|w| w.set_pwren(true)); + match config.rtc_mux { RtcClockSource::LSE32 => { // 1. Unlock the backup domain - RCC.apb1enr1().modify(|w| w.set_pwren(true)); PWR.cr1().modify(|w| w.set_dbp(true)); // 2. Setup the LSE From 91b10dd79912b92de5b16a2ea6e2769876fe13fa Mon Sep 17 00:00:00 2001 From: Lukas Joeressen Date: Fri, 18 Aug 2023 13:09:27 +0200 Subject: [PATCH 58/77] Fixed the final null terminator for RegMultiSz. The RegMultiSz value should be terminated by an empty UTF-16 string, i.e. 2 null bytes. --- embassy-usb/src/msos.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-usb/src/msos.rs b/embassy-usb/src/msos.rs index 187b2ff8e..847338e5f 100644 --- a/embassy-usb/src/msos.rs +++ b/embassy-usb/src/msos.rs @@ -526,7 +526,7 @@ impl<'a> PropertyData<'a> { PropertyData::Binary(val) => val.len(), PropertyData::DwordLittleEndian(val) | PropertyData::DwordBigEndian(val) => core::mem::size_of_val(val), PropertyData::RegMultiSz(val) => { - core::mem::size_of::() * val.iter().map(|x| x.encode_utf16().count() + 1).sum::() + 1 + core::mem::size_of::() * (val.iter().map(|x| x.encode_utf16().count() + 1).sum::() + 1) } } } From b948e3776969ac488abb6507ce429fee33ceb48b Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 18 Aug 2023 13:12:19 +0200 Subject: [PATCH 59/77] rp/flash: change naming to `blocking_*`, `new_blocking`. - Needed for consistency with other drivers. - Having two `new()` functions sometimes resulted in 'multiple applicable methods' errors. --- embassy-boot/rp/src/lib.rs | 8 +- embassy-rp/src/flash.rs | 100 +++++++++++----------- examples/boot/application/rp/src/bin/a.rs | 4 +- examples/rp/src/bin/flash.rs | 34 ++++---- tests/rp/src/bin/flash.rs | 14 +-- 5 files changed, 82 insertions(+), 78 deletions(-) diff --git a/embassy-boot/rp/src/lib.rs b/embassy-boot/rp/src/lib.rs index 96bcf3bf7..989e7521b 100644 --- a/embassy-boot/rp/src/lib.rs +++ b/embassy-boot/rp/src/lib.rs @@ -54,7 +54,7 @@ pub struct WatchdogFlash<'d, const SIZE: usize> { impl<'d, const SIZE: usize> WatchdogFlash<'d, SIZE> { /// Start a new watchdog with a given flash and watchdog peripheral and a timeout pub fn start(flash: FLASH, watchdog: WATCHDOG, timeout: Duration) -> Self { - let flash = Flash::<_, Blocking, SIZE>::new(flash); + let flash = Flash::<_, Blocking, SIZE>::new_blocking(flash); let mut watchdog = Watchdog::new(watchdog); watchdog.start(timeout); Self { flash, watchdog } @@ -71,11 +71,11 @@ impl<'d, const SIZE: usize> NorFlash for WatchdogFlash<'d, SIZE> { fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { self.watchdog.feed(); - self.flash.erase(from, to) + self.flash.blocking_erase(from, to) } fn write(&mut self, offset: u32, data: &[u8]) -> Result<(), Self::Error> { self.watchdog.feed(); - self.flash.write(offset, data) + self.flash.blocking_write(offset, data) } } @@ -83,7 +83,7 @@ impl<'d, const SIZE: usize> ReadNorFlash for WatchdogFlash<'d, SIZE> { const READ_SIZE: usize = as ReadNorFlash>::READ_SIZE; fn read(&mut self, offset: u32, data: &mut [u8]) -> Result<(), Self::Error> { self.watchdog.feed(); - self.flash.read(offset, data) + self.flash.blocking_read(offset, data) } fn capacity(&self) -> usize { self.flash.capacity() diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs index 70d867319..1c1c2449e 100644 --- a/embassy-rp/src/flash.rs +++ b/embassy-rp/src/flash.rs @@ -102,7 +102,7 @@ pub struct Flash<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> { } impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> Flash<'d, T, M, FLASH_SIZE> { - pub fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { + pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { trace!( "Reading from 0x{:x} to 0x{:x}", FLASH_BASE as u32 + offset, @@ -120,7 +120,7 @@ impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> Flash<'d, T, M, FLASH_SI FLASH_SIZE } - pub fn erase(&mut self, from: u32, to: u32) -> Result<(), Error> { + pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { check_erase(self, from, to)?; trace!( @@ -136,7 +136,7 @@ impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> Flash<'d, T, M, FLASH_SI Ok(()) } - pub fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { + pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { check_write(self, offset, bytes.len())?; trace!("Writing {:?} bytes to 0x{:x}", bytes.len(), FLASH_BASE as u32 + offset); @@ -233,13 +233,13 @@ impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> Flash<'d, T, M, FLASH_SI } /// Read SPI flash unique ID - pub fn unique_id(&mut self, uid: &mut [u8]) -> Result<(), Error> { + pub fn blocking_unique_id(&mut self, uid: &mut [u8]) -> Result<(), Error> { unsafe { self.in_ram(|| ram_helpers::flash_unique_id(uid))? }; Ok(()) } /// Read SPI flash JEDEC ID - pub fn jedec_id(&mut self) -> Result { + pub fn blocking_jedec_id(&mut self) -> Result { let mut jedec = None; unsafe { self.in_ram(|| { @@ -251,7 +251,7 @@ impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> Flash<'d, T, M, FLASH_SI } impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, Blocking, FLASH_SIZE> { - pub fn new(_flash: impl Peripheral

+ 'd) -> Self { + pub fn new_blocking(_flash: impl Peripheral

+ 'd) -> Self { Self { dma: None, phantom: PhantomData, @@ -310,47 +310,8 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, Async, FLASH_SIZE> { transfer, }) } -} -impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> ErrorType for Flash<'d, T, M, FLASH_SIZE> { - type Error = Error; -} - -impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> ReadNorFlash for Flash<'d, T, M, FLASH_SIZE> { - const READ_SIZE: usize = READ_SIZE; - - fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - self.read(offset, bytes) - } - - fn capacity(&self) -> usize { - self.capacity() - } -} - -impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> MultiwriteNorFlash for Flash<'d, T, M, FLASH_SIZE> {} - -impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> NorFlash for Flash<'d, T, M, FLASH_SIZE> { - const WRITE_SIZE: usize = WRITE_SIZE; - - const ERASE_SIZE: usize = ERASE_SIZE; - - fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - self.erase(from, to) - } - - fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - self.write(offset, bytes) - } -} - -#[cfg(feature = "nightly")] -impl<'d, T: Instance, const FLASH_SIZE: usize> embedded_storage_async::nor_flash::ReadNorFlash - for Flash<'d, T, Async, FLASH_SIZE> -{ - const READ_SIZE: usize = ASYNC_READ_SIZE; - - async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + pub async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { use core::mem::MaybeUninit; // Checked early to simplify address validity checks @@ -389,6 +350,49 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> embedded_storage_async::nor_flash Ok(()) } +} + +impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> ErrorType for Flash<'d, T, M, FLASH_SIZE> { + type Error = Error; +} + +impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> ReadNorFlash for Flash<'d, T, M, FLASH_SIZE> { + const READ_SIZE: usize = READ_SIZE; + + fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_read(offset, bytes) + } + + fn capacity(&self) -> usize { + self.capacity() + } +} + +impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> MultiwriteNorFlash for Flash<'d, T, M, FLASH_SIZE> {} + +impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> NorFlash for Flash<'d, T, M, FLASH_SIZE> { + const WRITE_SIZE: usize = WRITE_SIZE; + + const ERASE_SIZE: usize = ERASE_SIZE; + + fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + self.blocking_erase(from, to) + } + + fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(offset, bytes) + } +} + +#[cfg(feature = "nightly")] +impl<'d, T: Instance, const FLASH_SIZE: usize> embedded_storage_async::nor_flash::ReadNorFlash + for Flash<'d, T, Async, FLASH_SIZE> +{ + const READ_SIZE: usize = ASYNC_READ_SIZE; + + async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + self.read(offset, bytes).await + } fn capacity(&self) -> usize { self.capacity() @@ -404,11 +408,11 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> embedded_storage_async::nor_flash const ERASE_SIZE: usize = ERASE_SIZE; async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - self.erase(from, to) + self.blocking_erase(from, to) } async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - self.write(offset, bytes) + self.blocking_write(offset, bytes) } } diff --git a/examples/boot/application/rp/src/bin/a.rs b/examples/boot/application/rp/src/bin/a.rs index f0dda39d0..15fdaca82 100644 --- a/examples/boot/application/rp/src/bin/a.rs +++ b/examples/boot/application/rp/src/bin/a.rs @@ -7,7 +7,7 @@ use core::cell::RefCell; use defmt_rtt as _; use embassy_boot_rp::*; use embassy_executor::Spawner; -use embassy_rp::flash::{self, Flash}; +use embassy_rp::flash::Flash; use embassy_rp::gpio::{Level, Output}; use embassy_rp::watchdog::Watchdog; use embassy_sync::blocking_mutex::Mutex; @@ -34,7 +34,7 @@ async fn main(_s: Spawner) { let mut watchdog = Watchdog::new(p.WATCHDOG); watchdog.start(Duration::from_secs(8)); - let flash = Flash::<_, flash::Blocking, FLASH_SIZE>::new(p.FLASH); + let flash = Flash::<_, _, FLASH_SIZE>::new_blocking(p.FLASH); let flash = Mutex::new(RefCell::new(flash)); let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash); diff --git a/examples/rp/src/bin/flash.rs b/examples/rp/src/bin/flash.rs index 88bb931d2..911a657eb 100644 --- a/examples/rp/src/bin/flash.rs +++ b/examples/rp/src/bin/flash.rs @@ -28,12 +28,12 @@ async fn main(_spawner: Spawner) { let mut flash = embassy_rp::flash::Flash::<_, Async, FLASH_SIZE>::new(p.FLASH, p.DMA_CH0); // Get JEDEC id - let jedec = flash.jedec_id().unwrap(); + let jedec = flash.blocking_jedec_id().unwrap(); info!("jedec id: 0x{:x}", jedec); // Get unique id let mut uid = [0; 8]; - flash.unique_id(&mut uid).unwrap(); + flash.blocking_unique_id(&mut uid).unwrap(); info!("unique id: {:?}", uid); erase_write_sector(&mut flash, 0x00); @@ -48,25 +48,25 @@ async fn main(_spawner: Spawner) { fn multiwrite_bytes(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) { info!(">>>> [multiwrite_bytes]"); let mut read_buf = [0u8; ERASE_SIZE]; - defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut read_buf)); + defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut read_buf)); info!("Addr of flash block is {:x}", ADDR_OFFSET + offset + FLASH_BASE as u32); info!("Contents start with {=[u8]}", read_buf[0..4]); - defmt::unwrap!(flash.erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32)); + defmt::unwrap!(flash.blocking_erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32)); - defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut read_buf)); + defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut read_buf)); info!("Contents after erase starts with {=[u8]}", read_buf[0..4]); if read_buf.iter().any(|x| *x != 0xFF) { defmt::panic!("unexpected"); } - defmt::unwrap!(flash.write(ADDR_OFFSET + offset, &[0x01])); - defmt::unwrap!(flash.write(ADDR_OFFSET + offset + 1, &[0x02])); - defmt::unwrap!(flash.write(ADDR_OFFSET + offset + 2, &[0x03])); - defmt::unwrap!(flash.write(ADDR_OFFSET + offset + 3, &[0x04])); + defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset, &[0x01])); + defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset + 1, &[0x02])); + defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset + 2, &[0x03])); + defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset + 3, &[0x04])); - defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut read_buf)); + defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut read_buf)); info!("Contents after write starts with {=[u8]}", read_buf[0..4]); if &read_buf[0..4] != &[0x01, 0x02, 0x03, 0x04] { defmt::panic!("unexpected"); @@ -76,14 +76,14 @@ fn multiwrite_bytes(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH fn erase_write_sector(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) { info!(">>>> [erase_write_sector]"); let mut buf = [0u8; ERASE_SIZE]; - defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut buf)); + defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut buf)); info!("Addr of flash block is {:x}", ADDR_OFFSET + offset + FLASH_BASE as u32); info!("Contents start with {=[u8]}", buf[0..4]); - defmt::unwrap!(flash.erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32)); + defmt::unwrap!(flash.blocking_erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32)); - defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut buf)); + defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut buf)); info!("Contents after erase starts with {=[u8]}", buf[0..4]); if buf.iter().any(|x| *x != 0xFF) { defmt::panic!("unexpected"); @@ -93,9 +93,9 @@ fn erase_write_sector(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLA *b = 0xDA; } - defmt::unwrap!(flash.write(ADDR_OFFSET + offset, &buf)); + defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset, &buf)); - defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut buf)); + defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut buf)); info!("Contents after write starts with {=[u8]}", buf[0..4]); if buf.iter().any(|x| *x != 0xDA) { defmt::panic!("unexpected"); @@ -111,7 +111,7 @@ async fn background_read(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, info!("Addr of flash block is {:x}", ADDR_OFFSET + offset + FLASH_BASE as u32); info!("Contents start with {=u32:x}", buf[0]); - defmt::unwrap!(flash.erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32)); + defmt::unwrap!(flash.blocking_erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32)); defmt::unwrap!(flash.background_read(ADDR_OFFSET + offset, &mut buf)).await; info!("Contents after erase starts with {=u32:x}", buf[0]); @@ -123,7 +123,7 @@ async fn background_read(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, *b = 0xDABA1234; } - defmt::unwrap!(flash.write(ADDR_OFFSET + offset, unsafe { + defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset, unsafe { core::slice::from_raw_parts(buf.as_ptr() as *const u8, buf.len() * 4) })); diff --git a/tests/rp/src/bin/flash.rs b/tests/rp/src/bin/flash.rs index 6e65fbdc0..75be2bf06 100644 --- a/tests/rp/src/bin/flash.rs +++ b/tests/rp/src/bin/flash.rs @@ -25,23 +25,23 @@ async fn main(_spawner: Spawner) { let mut flash = embassy_rp::flash::Flash::<_, Async, { 2 * 1024 * 1024 }>::new(p.FLASH, p.DMA_CH0); // Get JEDEC id - let jedec = defmt::unwrap!(flash.jedec_id()); + let jedec = defmt::unwrap!(flash.blocking_jedec_id()); info!("jedec id: 0x{:x}", jedec); // Get unique id let mut uid = [0; 8]; - defmt::unwrap!(flash.unique_id(&mut uid)); + defmt::unwrap!(flash.blocking_unique_id(&mut uid)); info!("unique id: {:?}", uid); let mut buf = [0u8; ERASE_SIZE]; - defmt::unwrap!(flash.read(ADDR_OFFSET, &mut buf)); + defmt::unwrap!(flash.blocking_read(ADDR_OFFSET, &mut buf)); info!("Addr of flash block is {:x}", ADDR_OFFSET + FLASH_BASE as u32); info!("Contents start with {=[u8]}", buf[0..4]); - defmt::unwrap!(flash.erase(ADDR_OFFSET, ADDR_OFFSET + ERASE_SIZE as u32)); + defmt::unwrap!(flash.blocking_erase(ADDR_OFFSET, ADDR_OFFSET + ERASE_SIZE as u32)); - defmt::unwrap!(flash.read(ADDR_OFFSET, &mut buf)); + defmt::unwrap!(flash.blocking_read(ADDR_OFFSET, &mut buf)); info!("Contents after erase starts with {=[u8]}", buf[0..4]); if buf.iter().any(|x| *x != 0xFF) { defmt::panic!("unexpected"); @@ -51,9 +51,9 @@ async fn main(_spawner: Spawner) { *b = 0xDA; } - defmt::unwrap!(flash.write(ADDR_OFFSET, &mut buf)); + defmt::unwrap!(flash.blocking_write(ADDR_OFFSET, &mut buf)); - defmt::unwrap!(flash.read(ADDR_OFFSET, &mut buf)); + defmt::unwrap!(flash.blocking_read(ADDR_OFFSET, &mut buf)); info!("Contents after write starts with {=[u8]}", buf[0..4]); if buf.iter().any(|x| *x != 0xDA) { defmt::panic!("unexpected"); From 3ebb93e47d47095f35d561cd49f0797a74061465 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 18 Aug 2023 15:44:52 +0200 Subject: [PATCH 60/77] net-enc28j60: remove useless 1ms sleep. --- embassy-net-enc28j60/src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/embassy-net-enc28j60/src/lib.rs b/embassy-net-enc28j60/src/lib.rs index 09e77bafd..e958e7e31 100644 --- a/embassy-net-enc28j60/src/lib.rs +++ b/embassy-net-enc28j60/src/lib.rs @@ -381,8 +381,6 @@ where } fn read_phy_register(&mut self, register: phy::Register) -> u16 { - embassy_time::block_for(Duration::from_millis(1)); - // set PHY register address self.write_control_register(bank2::Register::MIREGADR, register.addr()); From 73942f50cb03070026246701e75d641e34d6d308 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 18 Aug 2023 15:45:23 +0200 Subject: [PATCH 61/77] net-enc28j60: reset rx logic when buffer corrupts. --- embassy-net-enc28j60/src/lib.rs | 54 ++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/embassy-net-enc28j60/src/lib.rs b/embassy-net-enc28j60/src/lib.rs index e958e7e31..b44cefaf2 100644 --- a/embassy-net-enc28j60/src/lib.rs +++ b/embassy-net-enc28j60/src/lib.rs @@ -106,19 +106,7 @@ where // disable CLKOUT output self.write_control_register(bank3::Register::ECOCON, 0); - // RX start - // "It is recommended that the ERXST Pointer be programmed with an even address" - self.write_control_register(bank0::Register::ERXSTL, RXST.low()); - self.write_control_register(bank0::Register::ERXSTH, RXST.high()); - - // RX read pointer - // NOTE Errata #14 so we are using an *odd* address here instead of ERXST - self.write_control_register(bank0::Register::ERXRDPTL, RXND.low()); - self.write_control_register(bank0::Register::ERXRDPTH, RXND.high()); - - // RX end - self.write_control_register(bank0::Register::ERXNDL, RXND.low()); - self.write_control_register(bank0::Register::ERXNDH, RXND.high()); + self.init_rx(); // TX start // "It is recommended that an even address be used for ETXST" @@ -172,12 +160,37 @@ where // Set the per packet control byte; we'll always use the value 0 self.write_buffer_memory(Some(TXST), &mut [0]); + // Enable reception + self.bit_field_set(common::Register::ECON1, common::ECON1::mask().rxen()); + } + + fn init_rx(&mut self) { + // RX start + // "It is recommended that the ERXST Pointer be programmed with an even address" + self.write_control_register(bank0::Register::ERXSTL, RXST.low()); + self.write_control_register(bank0::Register::ERXSTH, RXST.high()); + + // RX read pointer + // NOTE Errata #14 so we are using an *odd* address here instead of ERXST + self.write_control_register(bank0::Register::ERXRDPTL, RXND.low()); + self.write_control_register(bank0::Register::ERXRDPTH, RXND.high()); + + // RX end + self.write_control_register(bank0::Register::ERXNDL, RXND.low()); + self.write_control_register(bank0::Register::ERXNDH, RXND.high()); + // decrease the packet count to 0 while self.read_control_register(bank1::Register::EPKTCNT) != 0 { self.bit_field_set(common::Register::ECON2, common::ECON2::mask().pktdec()); } - // Enable reception + self.next_packet = RXST; + } + + fn reset_rx(&mut self) { + self.bit_field_set(common::Register::ECON1, common::ECON1::mask().rxrst()); + self.bit_field_clear(common::Register::ECON1, common::ECON1::mask().rxrst()); + self.init_rx(); self.bit_field_set(common::Register::ECON1, common::ECON1::mask().rxen()); } @@ -198,18 +211,17 @@ where // next packet pointer let next_packet = u16::from_parts(temp_buf[0], temp_buf[1]); - if next_packet > RXND { - panic!("CorruptRxBuffer"); - } - // status vector let status = header::RxStatus(u32::from_le_bytes(temp_buf[2..].try_into().unwrap())); - let len = status.byte_count() as u16 - CRC_SZ; + let len_with_crc = status.byte_count() as u16; - if len > RXND { - panic!("CorruptRxBuffer 2"); + if len_with_crc < CRC_SZ || len_with_crc > 1600 || next_packet > RXND { + warn!("RX buffer corrupted, resetting RX logic to recover..."); + self.reset_rx(); + return None; } + let len = len_with_crc - CRC_SZ; self.read_buffer_memory(None, &mut buf[..len as usize]); // update ERXRDPT From 78bb261246559e0447dc472f89480564e8b7a3fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Jacobs?= Date: Fri, 18 Aug 2023 16:10:49 +0200 Subject: [PATCH 62/77] stm32: allow setting PWM duty cycle to 100% Setting the compare_value to max_compare_value make the PWM output go low when the timer reach the max_compare_value and go high again on the next timer clock, when the value roll back to 0. So to allow a 100% PWM that never goes low, the compare_value must be set to max_compare_value + 1. --- embassy-stm32/src/timer/complementary_pwm.rs | 4 ++-- embassy-stm32/src/timer/simple_pwm.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index acd67048d..533267cf7 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -100,11 +100,11 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> { } pub fn get_max_duty(&self) -> u16 { - self.inner.get_max_compare_value() + self.inner.get_max_compare_value() + 1 } pub fn set_duty(&mut self, channel: Channel, duty: u16) { - assert!(duty < self.get_max_duty()); + assert!(duty <= self.get_max_duty()); self.inner.set_compare_value(channel, duty) } diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index e0a817929..40e3dd1bd 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -97,11 +97,11 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { } pub fn get_max_duty(&self) -> u16 { - self.inner.get_max_compare_value() + self.inner.get_max_compare_value() + 1 } pub fn set_duty(&mut self, channel: Channel, duty: u16) { - assert!(duty < self.get_max_duty()); + assert!(duty <= self.get_max_duty()); self.inner.set_compare_value(channel, duty) } } From 7f97efd9222725d2f0a4582a64d8d8e5e0fa6b0f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 18 Aug 2023 16:11:18 +0200 Subject: [PATCH 63/77] net-enc28j60: add HIL test. --- tests/nrf/Cargo.toml | 1 + tests/nrf/src/bin/buffered_uart.rs | 3 +- tests/nrf/src/bin/buffered_uart_spam.rs | 3 +- tests/nrf/src/bin/ethernet_enc28j60_perf.rs | 250 ++++++++++++++++++++ tests/nrf/src/bin/timer.rs | 3 +- tests/nrf/src/bin/wifi_esp_hosted_perf.rs | 7 +- tests/nrf/src/common.rs | 1 - 7 files changed, 256 insertions(+), 12 deletions(-) create mode 100644 tests/nrf/src/bin/ethernet_enc28j60_perf.rs delete mode 100644 tests/nrf/src/common.rs diff --git a/tests/nrf/Cargo.toml b/tests/nrf/Cargo.toml index e2dab81af..034ed85ea 100644 --- a/tests/nrf/Cargo.toml +++ b/tests/nrf/Cargo.toml @@ -15,6 +15,7 @@ embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defm embedded-io-async = { version = "0.5.0" } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"] } embassy-net-esp-hosted = { version = "0.1.0", path = "../../embassy-net-esp-hosted", features = ["defmt"] } +embassy-net-enc28j60 = { version = "0.1.0", path = "../../embassy-net-enc28j60", features = ["defmt"] } embedded-hal-async = { version = "1.0.0-rc.1" } embedded-hal-bus = { version = "0.1.0-rc.1", features = ["async"] } static_cell = { version = "1.1", features = [ "nightly" ] } diff --git a/tests/nrf/src/bin/buffered_uart.rs b/tests/nrf/src/bin/buffered_uart.rs index 72a4cb4ef..932e59264 100644 --- a/tests/nrf/src/bin/buffered_uart.rs +++ b/tests/nrf/src/bin/buffered_uart.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"nrf52840-dk"); use defmt::{assert_eq, *}; use embassy_executor::Spawner; diff --git a/tests/nrf/src/bin/buffered_uart_spam.rs b/tests/nrf/src/bin/buffered_uart_spam.rs index 50960206f..8abeae6d4 100644 --- a/tests/nrf/src/bin/buffered_uart_spam.rs +++ b/tests/nrf/src/bin/buffered_uart_spam.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"nrf52840-dk"); use core::mem; use core::ptr::NonNull; diff --git a/tests/nrf/src/bin/ethernet_enc28j60_perf.rs b/tests/nrf/src/bin/ethernet_enc28j60_perf.rs new file mode 100644 index 000000000..0446d39ac --- /dev/null +++ b/tests/nrf/src/bin/ethernet_enc28j60_perf.rs @@ -0,0 +1,250 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] +teleprobe_meta::target!(b"ak-gwe-r7"); +teleprobe_meta::timeout!(120); + +use defmt::{error, info, unwrap}; +use embassy_executor::Spawner; +use embassy_futures::join::join; +use embassy_net::tcp::TcpSocket; +use embassy_net::{Ipv4Address, Stack, StackResources}; +use embassy_net_enc28j60::Enc28j60; +use embassy_nrf::gpio::{Level, Output, OutputDrive}; +use embassy_nrf::rng::Rng; +use embassy_nrf::spim::{self, Spim}; +use embassy_nrf::{bind_interrupts, peripherals}; +use embassy_time::{with_timeout, Delay, Duration, Timer}; +use embedded_hal_bus::spi::ExclusiveDevice; +use static_cell::make_static; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + SPIM3 => spim::InterruptHandler; + RNG => embassy_nrf::rng::InterruptHandler; +}); + +type MyDriver = Enc28j60< + ExclusiveDevice, Output<'static, peripherals::P0_15>, Delay>, + Output<'static, peripherals::P0_13>, +>; + +#[embassy_executor::task] +async fn net_task(stack: &'static Stack) -> ! { + stack.run().await +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + info!("running!"); + + let eth_sck = p.P0_20; + let eth_mosi = p.P0_22; + let eth_miso = p.P0_24; + let eth_cs = p.P0_15; + let eth_rst = p.P0_13; + let _eth_irq = p.P0_12; + + let mut config = spim::Config::default(); + config.frequency = spim::Frequency::M16; + let spi = spim::Spim::new(p.SPI3, Irqs, eth_sck, eth_miso, eth_mosi, config); + let cs = Output::new(eth_cs, Level::High, OutputDrive::Standard); + let spi = ExclusiveDevice::new(spi, cs, Delay); + + let rst = Output::new(eth_rst, Level::High, OutputDrive::Standard); + let mac_addr = [2, 3, 4, 5, 6, 7]; + let device = Enc28j60::new(spi, Some(rst), mac_addr); + + let config = embassy_net::Config::dhcpv4(Default::default()); + // let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 { + // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), + // dns_servers: Vec::new(), + // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), + // }); + + // Generate random seed + let mut rng = Rng::new(p.RNG, Irqs); + let mut seed = [0; 8]; + rng.blocking_fill_bytes(&mut seed); + let seed = u64::from_le_bytes(seed); + + // Init network stack + let stack = &*make_static!(Stack::new( + device, + config, + make_static!(StackResources::<2>::new()), + seed + )); + + unwrap!(spawner.spawn(net_task(stack))); + + info!("Waiting for DHCP up..."); + while stack.config_v4().is_none() { + Timer::after(Duration::from_millis(100)).await; + } + info!("IP addressing up!"); + + let down = test_download(stack).await; + let up = test_upload(stack).await; + let updown = test_upload_download(stack).await; + + assert!(down > TEST_EXPECTED_DOWNLOAD_KBPS); + assert!(up > TEST_EXPECTED_UPLOAD_KBPS); + assert!(updown > TEST_EXPECTED_UPLOAD_DOWNLOAD_KBPS); + + info!("Test OK"); + cortex_m::asm::bkpt(); +} + +const TEST_DURATION: usize = 10; +const TEST_EXPECTED_DOWNLOAD_KBPS: usize = 200; +const TEST_EXPECTED_UPLOAD_KBPS: usize = 200; +const TEST_EXPECTED_UPLOAD_DOWNLOAD_KBPS: usize = 150; +const RX_BUFFER_SIZE: usize = 4096; +const TX_BUFFER_SIZE: usize = 4096; +const SERVER_ADDRESS: Ipv4Address = Ipv4Address::new(192, 168, 2, 2); +const DOWNLOAD_PORT: u16 = 4321; +const UPLOAD_PORT: u16 = 4322; +const UPLOAD_DOWNLOAD_PORT: u16 = 4323; + +async fn test_download(stack: &'static Stack) -> usize { + info!("Testing download..."); + + let mut rx_buffer = [0; RX_BUFFER_SIZE]; + let mut tx_buffer = [0; TX_BUFFER_SIZE]; + let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + socket.set_timeout(Some(Duration::from_secs(10))); + + info!("connecting to {:?}:{}...", SERVER_ADDRESS, DOWNLOAD_PORT); + if let Err(e) = socket.connect((SERVER_ADDRESS, DOWNLOAD_PORT)).await { + error!("connect error: {:?}", e); + return 0; + } + info!("connected, testing..."); + + let mut rx_buf = [0; 4096]; + let mut total: usize = 0; + with_timeout(Duration::from_secs(TEST_DURATION as _), async { + loop { + match socket.read(&mut rx_buf).await { + Ok(0) => { + error!("read EOF"); + return 0; + } + Ok(n) => total += n, + Err(e) => { + error!("read error: {:?}", e); + return 0; + } + } + } + }) + .await + .ok(); + + let kbps = (total + 512) / 1024 / TEST_DURATION; + info!("download: {} kB/s", kbps); + kbps +} + +async fn test_upload(stack: &'static Stack) -> usize { + info!("Testing upload..."); + + let mut rx_buffer = [0; RX_BUFFER_SIZE]; + let mut tx_buffer = [0; TX_BUFFER_SIZE]; + let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + socket.set_timeout(Some(Duration::from_secs(10))); + + info!("connecting to {:?}:{}...", SERVER_ADDRESS, UPLOAD_PORT); + if let Err(e) = socket.connect((SERVER_ADDRESS, UPLOAD_PORT)).await { + error!("connect error: {:?}", e); + return 0; + } + info!("connected, testing..."); + + let buf = [0; 4096]; + let mut total: usize = 0; + with_timeout(Duration::from_secs(TEST_DURATION as _), async { + loop { + match socket.write(&buf).await { + Ok(0) => { + error!("write zero?!??!?!"); + return 0; + } + Ok(n) => total += n, + Err(e) => { + error!("write error: {:?}", e); + return 0; + } + } + } + }) + .await + .ok(); + + let kbps = (total + 512) / 1024 / TEST_DURATION; + info!("upload: {} kB/s", kbps); + kbps +} + +async fn test_upload_download(stack: &'static Stack) -> usize { + info!("Testing upload+download..."); + + let mut rx_buffer = [0; RX_BUFFER_SIZE]; + let mut tx_buffer = [0; TX_BUFFER_SIZE]; + let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + socket.set_timeout(Some(Duration::from_secs(10))); + + info!("connecting to {:?}:{}...", SERVER_ADDRESS, UPLOAD_DOWNLOAD_PORT); + if let Err(e) = socket.connect((SERVER_ADDRESS, UPLOAD_DOWNLOAD_PORT)).await { + error!("connect error: {:?}", e); + return 0; + } + info!("connected, testing..."); + + let (mut reader, mut writer) = socket.split(); + + let tx_buf = [0; 4096]; + let mut rx_buf = [0; 4096]; + let mut total: usize = 0; + let tx_fut = async { + loop { + match writer.write(&tx_buf).await { + Ok(0) => { + error!("write zero?!??!?!"); + return 0; + } + Ok(_) => {} + Err(e) => { + error!("write error: {:?}", e); + return 0; + } + } + } + }; + + let rx_fut = async { + loop { + match reader.read(&mut rx_buf).await { + Ok(0) => { + error!("read EOF"); + return 0; + } + Ok(n) => total += n, + Err(e) => { + error!("read error: {:?}", e); + return 0; + } + } + } + }; + + with_timeout(Duration::from_secs(TEST_DURATION as _), join(tx_fut, rx_fut)) + .await + .ok(); + + let kbps = (total + 512) / 1024 / TEST_DURATION; + info!("upload+download: {} kB/s", kbps); + kbps +} diff --git a/tests/nrf/src/bin/timer.rs b/tests/nrf/src/bin/timer.rs index 607c5bbf1..c00f35fd1 100644 --- a/tests/nrf/src/bin/timer.rs +++ b/tests/nrf/src/bin/timer.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"nrf52840-dk"); use defmt::{assert, info}; use embassy_executor::Spawner; diff --git a/tests/nrf/src/bin/wifi_esp_hosted_perf.rs b/tests/nrf/src/bin/wifi_esp_hosted_perf.rs index 16055fae8..ee46af2a3 100644 --- a/tests/nrf/src/bin/wifi_esp_hosted_perf.rs +++ b/tests/nrf/src/bin/wifi_esp_hosted_perf.rs @@ -1,9 +1,8 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] - -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"nrf52840-dk"); +teleprobe_meta::timeout!(120); use defmt::{error, info, unwrap}; use embassy_executor::Spawner; @@ -19,8 +18,6 @@ use embedded_hal_bus::spi::ExclusiveDevice; use static_cell::make_static; use {defmt_rtt as _, embassy_net_esp_hosted as hosted, panic_probe as _}; -teleprobe_meta::timeout!(120); - bind_interrupts!(struct Irqs { SPIM3 => spim::InterruptHandler; RNG => embassy_nrf::rng::InterruptHandler; diff --git a/tests/nrf/src/common.rs b/tests/nrf/src/common.rs deleted file mode 100644 index 1a05ac1c5..000000000 --- a/tests/nrf/src/common.rs +++ /dev/null @@ -1 +0,0 @@ -teleprobe_meta::target!(b"nrf52840-dk"); From 2ea17d27836d98f55659e23502c91a33e2c06d29 Mon Sep 17 00:00:00 2001 From: Aurelien Jacobs Date: Fri, 18 Aug 2023 16:37:44 +0200 Subject: [PATCH 64/77] stm32: allow setting the PWM output polarity --- embassy-stm32/src/timer/complementary_pwm.rs | 5 ++ embassy-stm32/src/timer/mod.rs | 49 ++++++++++++++++++++ embassy-stm32/src/timer/simple_pwm.rs | 4 ++ 3 files changed, 58 insertions(+) diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index 533267cf7..4f033e3a2 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -108,6 +108,11 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> { self.inner.set_compare_value(channel, duty) } + pub fn set_polarity(&mut self, channel: Channel, polarity: OutputPolarity) { + self.inner.set_output_polarity(channel, polarity); + self.inner.set_complementary_output_polarity(channel, polarity); + } + /// Set the dead time as a proportion of max_duty pub fn set_dead_time(&mut self, value: u16) { let (ckd, value) = compute_dead_time_value(value); diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 6c2d6d827..4ffb2a289 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -53,6 +53,8 @@ pub(crate) mod sealed { fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode); + fn set_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity); + fn enable_channel(&mut self, channel: Channel, enable: bool); fn set_compare_value(&mut self, channel: Channel, value: u16); @@ -61,6 +63,8 @@ pub(crate) mod sealed { } pub trait ComplementaryCaptureCompare16bitInstance: CaptureCompare16bitInstance { + fn set_complementary_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity); + fn set_dead_time_clock_division(&mut self, value: vals::Ckd); fn set_dead_time_value(&mut self, value: u8); @@ -71,6 +75,8 @@ pub(crate) mod sealed { pub trait CaptureCompare32bitInstance: GeneralPurpose32bitInstance { fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode); + fn set_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity); + fn enable_channel(&mut self, channel: Channel, enable: bool); fn set_compare_value(&mut self, channel: Channel, value: u32); @@ -125,6 +131,21 @@ impl From for stm32_metapac::timer::vals::Ocm { } } +#[derive(Clone, Copy)] +pub enum OutputPolarity { + ActiveHigh, + ActiveLow, +} + +impl From for bool { + fn from(mode: OutputPolarity) -> Self { + match mode { + OutputPolarity::ActiveHigh => false, + OutputPolarity::ActiveLow => true, + } + } +} + pub trait Basic16bitInstance: sealed::Basic16bitInstance + 'static {} pub trait GeneralPurpose16bitInstance: sealed::GeneralPurpose16bitInstance + 'static {} @@ -265,6 +286,13 @@ macro_rules! impl_compare_capable_16bit { .modify(|w| w.set_ocm(raw_channel % 2, mode.into())); } + fn set_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity) { + use sealed::GeneralPurpose16bitInstance; + Self::regs_gp16() + .ccer() + .modify(|w| w.set_ccp(channel.raw(), polarity.into())); + } + fn enable_channel(&mut self, channel: Channel, enable: bool) { use sealed::GeneralPurpose16bitInstance; Self::regs_gp16() @@ -325,6 +353,13 @@ foreach_interrupt! { Self::regs_gp32().ccmr_output(raw_channel / 2).modify(|w| w.set_ocm(raw_channel % 2, mode.into())); } + fn set_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity) { + use crate::timer::sealed::GeneralPurpose32bitInstance; + Self::regs_gp32() + .ccer() + .modify(|w| w.set_ccp(channel.raw(), polarity.into())); + } + fn enable_channel(&mut self, channel: Channel, enable: bool) { use crate::timer::sealed::GeneralPurpose32bitInstance; Self::regs_gp32().ccer().modify(|w| w.set_cce(channel.raw(), enable)); @@ -388,6 +423,13 @@ foreach_interrupt! { .modify(|w| w.set_ocm(raw_channel % 2, mode.into())); } + fn set_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity) { + use crate::timer::sealed::AdvancedControlInstance; + Self::regs_advanced() + .ccer() + .modify(|w| w.set_ccp(channel.raw(), polarity.into())); + } + fn enable_channel(&mut self, channel: Channel, enable: bool) { use crate::timer::sealed::AdvancedControlInstance; Self::regs_advanced() @@ -409,6 +451,13 @@ foreach_interrupt! { } impl sealed::ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst { + fn set_complementary_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity) { + use crate::timer::sealed::AdvancedControlInstance; + Self::regs_advanced() + .ccer() + .modify(|w| w.set_ccnp(channel.raw(), polarity.into())); + } + fn set_dead_time_clock_division(&mut self, value: vals::Ckd) { use crate::timer::sealed::AdvancedControlInstance; Self::regs_advanced().cr1().modify(|w| w.set_ckd(value)); diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index 40e3dd1bd..9e28878b1 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -104,4 +104,8 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { assert!(duty <= self.get_max_duty()); self.inner.set_compare_value(channel, duty) } + + pub fn set_polarity(&mut self, channel: Channel, polarity: OutputPolarity) { + self.inner.set_output_polarity(channel, polarity); + } } From eb05a18c452ec6152c50c75f99e57ce769013c68 Mon Sep 17 00:00:00 2001 From: Alec Cox Date: Fri, 18 Aug 2023 13:50:14 -0700 Subject: [PATCH 65/77] add error translation to tcp errors Translation of tpc client ConnectError and Error to the appropriate embedded_io_async errors --- embassy-net/src/tcp.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index c903fb245..1a876692f 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -384,13 +384,20 @@ mod embedded_io_impls { impl embedded_io_async::Error for ConnectError { fn kind(&self) -> embedded_io_async::ErrorKind { - embedded_io_async::ErrorKind::Other + match self { + ConnectError::ConnectionReset => embedded_io_async::ErrorKind::ConnectionReset, + ConnectError::TimedOut => embedded_io_async::ErrorKind::TimedOut, + ConnectError::NoRoute => embedded_io_async::ErrorKind::NotConnected, + ConnectError::InvalidState => embedded_io_async::ErrorKind::Other, + } } } impl embedded_io_async::Error for Error { fn kind(&self) -> embedded_io_async::ErrorKind { - embedded_io_async::ErrorKind::Other + match self { + Error::ConnectionReset => embedded_io_async::ErrorKind::ConnectionReset, + } } } From 5bc0175be9482be4737dfffd3a615cee3301e775 Mon Sep 17 00:00:00 2001 From: Dominik Sliwa Date: Fri, 18 Aug 2023 22:10:13 +0200 Subject: [PATCH 66/77] configure flash latency after axi clock and handle different flash in STM32H7A/B devices --- embassy-stm32/Cargo.toml | 4 +-- embassy-stm32/src/flash/h7.rs | 12 +++++++-- embassy-stm32/src/flash/mod.rs | 4 ++- embassy-stm32/src/rcc/h7.rs | 49 ++++++++++++++++++++++++++++++++-- 4 files changed, 62 insertions(+), 7 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index c3f5706e8..fe5dc443a 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -57,7 +57,7 @@ sdio-host = "0.5.0" embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true } critical-section = "1.1" atomic-polyfill = "1.0.1" -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-7eddb78e705905af4c1dd2359900db3e78a3c500" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-2b87e34c661e19ff6dc603fabfe7fe99ab7261f7" } vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -76,7 +76,7 @@ critical-section = { version = "1.1", features = ["std"] } [build-dependencies] proc-macro2 = "1.0.36" quote = "1.0.15" -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-7eddb78e705905af4c1dd2359900db3e78a3c500", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-2b87e34c661e19ff6dc603fabfe7fe99ab7261f7", default-features = false, features = ["metadata"]} [features] default = ["rt"] diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index bb429d773..625bf13fc 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs @@ -11,7 +11,7 @@ pub const fn is_default_layout() -> bool { } const fn is_dual_bank() -> bool { - FLASH_REGIONS.len() == 2 + FLASH_REGIONS.len() >= 2 } pub fn get_flash_regions() -> &'static [&'static FlashRegion] { @@ -49,6 +49,7 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) }; bank.cr().write(|w| { w.set_pg(true); + #[cfg(flash_h7)] w.set_psize(2); // 32 bits at once }); cortex_m::asm::isb(); @@ -85,7 +86,10 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E let bank = pac::FLASH.bank(sector.bank as usize); bank.cr().modify(|w| { w.set_ser(true); - w.set_snb(sector.index_in_bank) + #[cfg(flash_h7)] + w.set_snb(sector.index_in_bank); + #[cfg(flash_h7ab)] + w.set_ssn(sector.index_in_bank); }); bank.cr().modify(|w| { @@ -126,6 +130,10 @@ unsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> { error!("incerr"); return Err(Error::Seq); } + if sr.crcrderr() { + error!("crcrderr"); + return Err(Error::Seq); + } if sr.operr() { return Err(Error::Prog); } diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 4308037f2..fb20dcd38 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -65,9 +65,11 @@ impl FlashRegion { #[cfg_attr(flash_f7, path = "f7.rs")] #[cfg_attr(flash_g0, path = "g0.rs")] #[cfg_attr(flash_h7, path = "h7.rs")] +#[cfg_attr(flash_h7ab, path = "h7.rs")] #[cfg_attr( not(any( - flash_l0, flash_l1, flash_l4, flash_wl, flash_wb, flash_f0, flash_f3, flash_f4, flash_f7, flash_g0, flash_h7 + flash_l0, flash_l1, flash_l4, flash_wl, flash_wb, flash_f0, flash_f3, flash_f4, flash_f7, flash_g0, flash_h7, + flash_h7ab )), path = "other.rs" )] diff --git a/embassy-stm32/src/rcc/h7.rs b/embassy-stm32/src/rcc/h7.rs index 0788b0640..7fb4fb95b 100644 --- a/embassy-stm32/src/rcc/h7.rs +++ b/embassy-stm32/src/rcc/h7.rs @@ -200,6 +200,7 @@ fn flash_setup(rcc_aclk: u32, vos: VoltageScale) { // See RM0433 Rev 7 Table 17. FLASH recommended number of wait // states and programming delay + #[cfg(flash_h7)] let (wait_states, progr_delay) = match vos { // VOS 0 range VCORE 1.26V - 1.40V VoltageScale::Scale0 => match rcc_aclk_mhz { @@ -239,6 +240,50 @@ fn flash_setup(rcc_aclk: u32, vos: VoltageScale) { }, }; + // See RM0455 Rev 10 Table 16. FLASH recommended number of wait + // states and programming delay + #[cfg(flash_h7ab)] + let (wait_states, progr_delay) = match vos { + // VOS 0 range VCORE 1.25V - 1.35V + VoltageScale::Scale0 => match rcc_aclk_mhz { + 0..=42 => (0, 0), + 43..=84 => (1, 0), + 85..=126 => (2, 1), + 127..=168 => (3, 1), + 169..=210 => (4, 2), + 211..=252 => (5, 2), + 253..=280 => (6, 3), + _ => (7, 3), + }, + // VOS 1 range VCORE 1.15V - 1.25V + VoltageScale::Scale1 => match rcc_aclk_mhz { + 0..=38 => (0, 0), + 39..=76 => (1, 0), + 77..=114 => (2, 1), + 115..=152 => (3, 1), + 153..=190 => (4, 2), + 191..=225 => (5, 2), + _ => (7, 3), + }, + // VOS 2 range VCORE 1.05V - 1.15V + VoltageScale::Scale2 => match rcc_aclk_mhz { + 0..=34 => (0, 0), + 35..=68 => (1, 0), + 69..=102 => (2, 1), + 103..=136 => (3, 1), + 137..=160 => (4, 2), + _ => (7, 3), + }, + // VOS 3 range VCORE 0.95V - 1.05V + VoltageScale::Scale3 => match rcc_aclk_mhz { + 0..=22 => (0, 0), + 23..=44 => (1, 0), + 45..=66 => (2, 1), + 67..=88 => (3, 1), + _ => (7, 3), + }, + }; + FLASH.acr().write(|w| { w.set_wrhighfreq(progr_delay); w.set_latency(wait_states) @@ -538,8 +583,6 @@ pub(crate) unsafe fn init(mut config: Config) { let requested_pclk4 = config.pclk4.map(|v| v.0).unwrap_or_else(|| pclk_max.min(rcc_hclk / 2)); let (rcc_pclk4, ppre4_bits, ppre4, _) = ppre_calculate(requested_pclk4, rcc_hclk, pclk_max, None); - flash_setup(rcc_aclk, pwr_vos); - // Start switching clocks ------------------- // Ensure CSI is on and stable @@ -595,6 +638,8 @@ pub(crate) unsafe fn init(mut config: Config) { // core voltage while RCC.d1cfgr().read().d1cpre().to_bits() != d1cpre_bits {} + flash_setup(rcc_aclk, pwr_vos); + // APB1 / APB2 Prescaler RCC.d2cfgr().modify(|w| { w.set_d2ppre1(Dppre::from_bits(ppre1_bits)); From cc400aa17813ff9f2329842856725e1b265f39a2 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 19 Aug 2023 00:59:37 +0200 Subject: [PATCH 67/77] stm32: fix f37x build. originally broke in https://github.com/embassy-rs/embassy/pull/1762 --- ci.sh | 1 + embassy-stm32/src/adc/mod.rs | 16 ++++++++-------- embassy-stm32/src/adc/sample_time.rs | 2 +- embassy-stm32/src/rcc/mod.rs | 2 +- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/ci.sh b/ci.sh index f82cc6273..e83b3b847 100755 --- a/ci.sh +++ b/ci.sh @@ -81,6 +81,7 @@ cargo batch \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32l041f6,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32l151cb-a,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f398ve,defmt,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f378cc,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32g0c1ve,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f217zg,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features nightly,stm32l552ze,defmt,exti,time-driver-any,unstable-traits \ diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 94a8538bf..e57889aa6 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -1,6 +1,6 @@ #![macro_use] -#[cfg(not(adc_f3))] +#[cfg(not(any(adc_f3, adc_f3_v2)))] #[cfg_attr(adc_f1, path = "f1.rs")] #[cfg_attr(adc_v1, path = "v1.rs")] #[cfg_attr(adc_v2, path = "v2.rs")] @@ -8,16 +8,16 @@ #[cfg_attr(adc_v4, path = "v4.rs")] mod _version; -#[cfg(not(any(adc_f1, adc_f3)))] +#[cfg(not(any(adc_f1, adc_f3, adc_f3_v2)))] mod resolution; mod sample_time; -#[cfg(not(adc_f3))] +#[cfg(not(any(adc_f3, adc_f3_v2)))] #[allow(unused)] pub use _version::*; -#[cfg(not(any(adc_f1, adc_f3)))] +#[cfg(not(any(adc_f1, adc_f3, adc_f3_v2)))] pub use resolution::Resolution; -#[cfg(not(adc_f3))] +#[cfg(not(any(adc_f3, adc_f3_v2)))] pub use sample_time::SampleTime; use crate::peripherals; @@ -25,14 +25,14 @@ use crate::peripherals; pub struct Adc<'d, T: Instance> { #[allow(unused)] adc: crate::PeripheralRef<'d, T>, - #[cfg(not(adc_f3))] + #[cfg(not(any(adc_f3, adc_f3_v2)))] sample_time: SampleTime, } pub(crate) mod sealed { pub trait Instance { fn regs() -> crate::pac::adc::Adc; - #[cfg(not(any(adc_f1, adc_v1, adc_f3)))] + #[cfg(not(any(adc_f1, adc_v1, adc_f3, adc_f3_v2)))] fn common_regs() -> crate::pac::adccommon::AdcCommon; } @@ -60,7 +60,7 @@ foreach_peripheral!( fn regs() -> crate::pac::adc::Adc { crate::pac::$inst } - #[cfg(not(any(adc_f1, adc_v1, adc_f3)))] + #[cfg(not(any(adc_f1, adc_v1, adc_f3, adc_f3_v2)))] fn common_regs() -> crate::pac::adccommon::AdcCommon { foreach_peripheral!{ (adccommon, $common_inst:ident) => { diff --git a/embassy-stm32/src/adc/sample_time.rs b/embassy-stm32/src/adc/sample_time.rs index df0525560..5480e7a77 100644 --- a/embassy-stm32/src/adc/sample_time.rs +++ b/embassy-stm32/src/adc/sample_time.rs @@ -1,4 +1,4 @@ -#[cfg(not(adc_f3))] +#[cfg(not(any(adc_f3, adc_f3_v2)))] macro_rules! impl_sample_time { ($default_doc:expr, $default:ident, ($(($doc:expr, $variant:ident, $pac_variant:ident)),*)) => { #[doc = concat!("ADC sample time\n\nThe default setting is ", $default_doc, " ADC clock cycles.")] diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 62c19bda6..e5dd0019e 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -9,7 +9,7 @@ use crate::time::Hertz; #[cfg_attr(rcc_f0, path = "f0.rs")] #[cfg_attr(any(rcc_f1, rcc_f100, rcc_f1cl), path = "f1.rs")] #[cfg_attr(rcc_f2, path = "f2.rs")] -#[cfg_attr(rcc_f3, path = "f3.rs")] +#[cfg_attr(any(rcc_f3, rcc_f3_v2), path = "f3.rs")] #[cfg_attr(any(rcc_f4, rcc_f410), path = "f4.rs")] #[cfg_attr(rcc_f7, path = "f7.rs")] #[cfg_attr(rcc_c0, path = "c0.rs")] From 5d4da78c944e5f7e851b7ff7843b97d9466a4e64 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 19 Aug 2023 01:05:13 +0200 Subject: [PATCH 68/77] Fix lora docs build. --- embassy-lora/Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index 90c544d8d..feea06582 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -29,3 +29,6 @@ embedded-hal = { version = "0.2", features = ["unproven"] } futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] } lora-phy = { version = "1" } lorawan-device = { version = "0.10.0", default-features = false, features = ["async"], optional = true } + +[patch.crates-io] +lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "1323eccc1c470d4259f95f4f315d1be830d572a3"} \ No newline at end of file From 2d980068c09aa8fcc6bb4fd4d2e46107ef0e997f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 19 Aug 2023 19:31:49 +0200 Subject: [PATCH 69/77] Update Nightly. --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 179ed1d6a..7b34afa2b 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,7 +1,7 @@ # Before upgrading check that everything is available on all tier1 targets here: # https://rust-lang.github.io/rustup-components-history [toolchain] -channel = "nightly-2023-06-28" +channel = "nightly-2023-08-19" components = [ "rust-src", "rustfmt", "llvm-tools-preview" ] targets = [ "thumbv7em-none-eabi", From 0032f8a2ec05ed61b17dc1992f907f23f1b37f92 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 19 Aug 2023 19:35:10 +0200 Subject: [PATCH 70/77] rustfmt. --- embassy-net-esp-hosted/src/control.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-net-esp-hosted/src/control.rs b/embassy-net-esp-hosted/src/control.rs index 6cd57f68a..b8026611b 100644 --- a/embassy-net-esp-hosted/src/control.rs +++ b/embassy-net-esp-hosted/src/control.rs @@ -35,9 +35,9 @@ macro_rules! ioctl { }; $self.ioctl(&mut msg).await?; let Some(proto::CtrlMsgPayload::$resp_variant($resp)) = msg.payload else { - warn!("unexpected response variant"); - return Err(Error::Internal); - }; + warn!("unexpected response variant"); + return Err(Error::Internal); + }; if $resp.resp != 0 { return Err(Error::Failed($resp.resp)); } From 17e9a8ebe13a254417d323b0f634d18eb81caa3d Mon Sep 17 00:00:00 2001 From: Isaikin Roman Date: Sun, 20 Aug 2023 07:42:54 +0200 Subject: [PATCH 71/77] Fix hardcoded buffer length in USB NCM causing broken link on USB 2.0. --- embassy-usb/src/class/cdc_ncm/mod.rs | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/embassy-usb/src/class/cdc_ncm/mod.rs b/embassy-usb/src/class/cdc_ncm/mod.rs index fcfa0bfcd..830e9b768 100644 --- a/embassy-usb/src/class/cdc_ncm/mod.rs +++ b/embassy-usb/src/class/cdc_ncm/mod.rs @@ -248,6 +248,8 @@ pub struct CdcNcmClass<'d, D: Driver<'d>> { write_ep: D::EndpointIn, _control: &'d ControlShared, + + max_packet_size: usize, } impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { @@ -338,6 +340,7 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { read_ep, write_ep, _control: &state.shared, + max_packet_size: max_packet_size as usize, } } @@ -349,6 +352,7 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { Sender { write_ep: self.write_ep, seq: 0, + max_packet_size: self.max_packet_size, }, Receiver { data_if: self.data_if, @@ -365,6 +369,7 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { pub struct Sender<'d, D: Driver<'d>> { write_ep: D::EndpointIn, seq: u16, + max_packet_size: usize, } impl<'d, D: Driver<'d>> Sender<'d, D> { @@ -375,8 +380,8 @@ impl<'d, D: Driver<'d>> Sender<'d, D> { let seq = self.seq; self.seq = self.seq.wrapping_add(1); - const MAX_PACKET_SIZE: usize = 64; // TODO unhardcode const OUT_HEADER_LEN: usize = 28; + const ABS_MAX_PACKET_SIZE: usize = 512; let header = NtbOutHeader { nth_sig: SIG_NTH, @@ -395,27 +400,27 @@ impl<'d, D: Driver<'d>> Sender<'d, D> { }; // Build first packet on a buffer, send next packets straight from `data`. - let mut buf = [0; MAX_PACKET_SIZE]; + let mut buf = [0; ABS_MAX_PACKET_SIZE]; let n = byteify(&mut buf, header); assert_eq!(n.len(), OUT_HEADER_LEN); - if OUT_HEADER_LEN + data.len() < MAX_PACKET_SIZE { + if OUT_HEADER_LEN + data.len() < self.max_packet_size { // First packet is not full, just send it. // No need to send ZLP because it's short for sure. buf[OUT_HEADER_LEN..][..data.len()].copy_from_slice(data); self.write_ep.write(&buf[..OUT_HEADER_LEN + data.len()]).await?; } else { - let (d1, d2) = data.split_at(MAX_PACKET_SIZE - OUT_HEADER_LEN); + let (d1, d2) = data.split_at(self.max_packet_size - OUT_HEADER_LEN); - buf[OUT_HEADER_LEN..].copy_from_slice(d1); - self.write_ep.write(&buf).await?; + buf[OUT_HEADER_LEN..self.max_packet_size].copy_from_slice(d1); + self.write_ep.write(&buf[..self.max_packet_size]).await?; - for chunk in d2.chunks(MAX_PACKET_SIZE) { + for chunk in d2.chunks(self.max_packet_size) { self.write_ep.write(&chunk).await?; } // Send ZLP if needed. - if d2.len() % MAX_PACKET_SIZE == 0 { + if d2.len() % self.max_packet_size == 0 { self.write_ep.write(&[]).await?; } } From 67ca88d5a031369f9bae99c55243625644c93a71 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 20 Aug 2023 10:57:28 +0200 Subject: [PATCH 72/77] stm32/can: make latency assertion more lenient. --- tests/stm32/src/bin/can.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/stm32/src/bin/can.rs b/tests/stm32/src/bin/can.rs index 8737ca8ee..acf545216 100644 --- a/tests/stm32/src/bin/can.rs +++ b/tests/stm32/src/bin/can.rs @@ -80,8 +80,8 @@ async fn main(_spawner: Spawner) { const MIN_LATENCY: Duration = Duration::from_micros(50); const MAX_LATENCY: Duration = Duration::from_micros(150); assert!( - MIN_LATENCY < latency && latency < MAX_LATENCY, - "{} < {} < {}", + MIN_LATENCY <= latency && latency <= MAX_LATENCY, + "{} <= {} <= {}", MIN_LATENCY, latency, MAX_LATENCY From 977ae5e3e46b5b3ffe06b53d58147338f74f9473 Mon Sep 17 00:00:00 2001 From: soypat Date: Sun, 20 Aug 2023 20:01:47 -0300 Subject: [PATCH 73/77] don't pay for what you don't use --- cyw43/src/bus.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cyw43/src/bus.rs b/cyw43/src/bus.rs index e26f11120..0b5632cf8 100644 --- a/cyw43/src/bus.rs +++ b/cyw43/src/bus.rs @@ -102,7 +102,7 @@ where cmd_buf[0] = cmd; cmd_buf[1..][..buf.len()].copy_from_slice(buf); - self.status = self.spi.cmd_write(&cmd_buf).await; + self.status = self.spi.cmd_write(&cmd_buf[..buf.len() + 1]).await; } #[allow(unused)] From 0a73c84df0936facecb3e1a97cf6f4795d321b87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 21 Aug 2023 13:55:30 +0200 Subject: [PATCH 74/77] Make AvailableTask public, deduplicate --- embassy-executor/CHANGELOG.md | 2 + embassy-executor/src/raw/mod.rs | 119 ++++++++++++++++++-------------- embassy-executor/src/spawner.rs | 3 +- 3 files changed, 70 insertions(+), 54 deletions(-) diff --git a/embassy-executor/CHANGELOG.md b/embassy-executor/CHANGELOG.md index e2e7bce3a..43d94e540 100644 --- a/embassy-executor/CHANGELOG.md +++ b/embassy-executor/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - Replaced Pender. Implementations now must define an extern function called `__pender`. +- Made `raw::AvailableTask` public +- Made `SpawnToken::new_failed` public ## 0.2.1 - 2023-08-10 diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 7caa3302f..c1d82e18a 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -147,10 +147,7 @@ impl TaskStorage { pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken { let task = AvailableTask::claim(self); match task { - Some(task) => { - let task = task.initialize(future); - unsafe { SpawnToken::::new(task) } - } + Some(task) => task.initialize(future), None => SpawnToken::new_failed(), } } @@ -186,12 +183,16 @@ impl TaskStorage { } } -struct AvailableTask { +/// An uninitialized [`TaskStorage`]. +pub struct AvailableTask { task: &'static TaskStorage, } impl AvailableTask { - fn claim(task: &'static TaskStorage) -> Option { + /// Try to claim a [`TaskStorage`]. + /// + /// This function returns `None` if a task has already been spawned and has not finished running. + pub fn claim(task: &'static TaskStorage) -> Option { task.raw .state .compare_exchange(0, STATE_SPAWNED | STATE_RUN_QUEUED, Ordering::AcqRel, Ordering::Acquire) @@ -199,61 +200,30 @@ impl AvailableTask { .map(|_| Self { task }) } - fn initialize(self, future: impl FnOnce() -> F) -> TaskRef { + fn initialize_impl(self, future: impl FnOnce() -> F) -> SpawnToken { unsafe { self.task.raw.poll_fn.set(Some(TaskStorage::::poll)); self.task.future.write(future()); - } - TaskRef::new(self.task) - } -} -/// Raw storage that can hold up to N tasks of the same type. -/// -/// This is essentially a `[TaskStorage; N]`. -pub struct TaskPool { - pool: [TaskStorage; N], -} + let task = TaskRef::new(self.task); -impl TaskPool { - /// Create a new TaskPool, with all tasks in non-spawned state. - pub const fn new() -> Self { - Self { - pool: [TaskStorage::NEW; N], + SpawnToken::new(task) } } - /// Try to spawn a task in the pool. - /// - /// See [`TaskStorage::spawn()`] for details. - /// - /// This will loop over the pool and spawn the task in the first storage that - /// is currently free. If none is free, a "poisoned" SpawnToken is returned, - /// which will cause [`Spawner::spawn()`](super::Spawner::spawn) to return the error. - pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken { - let task = self.pool.iter().find_map(AvailableTask::claim); - match task { - Some(task) => { - let task = task.initialize(future); - unsafe { SpawnToken::::new(task) } - } - None => SpawnToken::new_failed(), - } + /// Initialize the [`TaskStorage`] to run the given future. + pub fn initialize(self, future: impl FnOnce() -> F) -> SpawnToken { + self.initialize_impl::(future) } - /// Like spawn(), but allows the task to be send-spawned if the args are Send even if - /// the future is !Send. + /// Initialize the [`TaskStorage`] to run the given future. /// - /// Not covered by semver guarantees. DO NOT call this directly. Intended to be used - /// by the Embassy macros ONLY. + /// # Safety /// - /// SAFETY: `future` must be a closure of the form `move || my_async_fn(args)`, where `my_async_fn` + /// `future` must be a closure of the form `move || my_async_fn(args)`, where `my_async_fn` /// is an `async fn`, NOT a hand-written `Future`. #[doc(hidden)] - pub unsafe fn _spawn_async_fn(&'static self, future: FutFn) -> SpawnToken - where - FutFn: FnOnce() -> F, - { + pub unsafe fn __initialize_async_fn(self, future: impl FnOnce() -> F) -> SpawnToken { // When send-spawning a task, we construct the future in this thread, and effectively // "send" it to the executor thread by enqueuing it in its queue. Therefore, in theory, // send-spawning should require the future `F` to be `Send`. @@ -279,16 +249,59 @@ impl TaskPool { // // This ONLY holds for `async fn` futures. The other `spawn` methods can be called directly // by the user, with arbitrary hand-implemented futures. This is why these return `SpawnToken`. + self.initialize_impl::(future) + } +} - let task = self.pool.iter().find_map(AvailableTask::claim); - match task { - Some(task) => { - let task = task.initialize(future); - unsafe { SpawnToken::::new(task) } - } +/// Raw storage that can hold up to N tasks of the same type. +/// +/// This is essentially a `[TaskStorage; N]`. +pub struct TaskPool { + pool: [TaskStorage; N], +} + +impl TaskPool { + /// Create a new TaskPool, with all tasks in non-spawned state. + pub const fn new() -> Self { + Self { + pool: [TaskStorage::NEW; N], + } + } + + fn spawn_impl(&'static self, future: impl FnOnce() -> F) -> SpawnToken { + match self.pool.iter().find_map(AvailableTask::claim) { + Some(task) => task.initialize_impl::(future), None => SpawnToken::new_failed(), } } + + /// Try to spawn a task in the pool. + /// + /// See [`TaskStorage::spawn()`] for details. + /// + /// This will loop over the pool and spawn the task in the first storage that + /// is currently free. If none is free, a "poisoned" SpawnToken is returned, + /// which will cause [`Spawner::spawn()`](super::Spawner::spawn) to return the error. + pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken { + self.spawn_impl::(future) + } + + /// Like spawn(), but allows the task to be send-spawned if the args are Send even if + /// the future is !Send. + /// + /// Not covered by semver guarantees. DO NOT call this directly. Intended to be used + /// by the Embassy macros ONLY. + /// + /// SAFETY: `future` must be a closure of the form `move || my_async_fn(args)`, where `my_async_fn` + /// is an `async fn`, NOT a hand-written `Future`. + #[doc(hidden)] + pub unsafe fn _spawn_async_fn(&'static self, future: FutFn) -> SpawnToken + where + FutFn: FnOnce() -> F, + { + // See the comment in AvailableTask::__initialize_async_fn for explanation. + self.spawn_impl::(future) + } } #[derive(Clone, Copy)] diff --git a/embassy-executor/src/spawner.rs b/embassy-executor/src/spawner.rs index 2b6224045..5a3a0dee1 100644 --- a/embassy-executor/src/spawner.rs +++ b/embassy-executor/src/spawner.rs @@ -33,7 +33,8 @@ impl SpawnToken { } } - pub(crate) fn new_failed() -> Self { + /// Return a SpawnToken that represents a failed spawn. + pub fn new_failed() -> Self { Self { raw_task: None, phantom: PhantomData, From c39671266e21dd9e35e60cc680453cd5c38162db Mon Sep 17 00:00:00 2001 From: Ruben De Smet Date: Fri, 11 Aug 2023 11:58:22 +0200 Subject: [PATCH 75/77] Deprecate *recv* in favor of *receive* --- embassy-stm32-wpan/src/mac/driver.rs | 4 +- embassy-stm32-wpan/src/mac/runner.rs | 2 +- embassy-stm32/src/can/bxcan.rs | 4 +- embassy-sync/src/channel.rs | 88 +++++++++---------- examples/nrf52840/src/bin/channel.rs | 2 +- .../src/bin/channel_sender_receiver.rs | 2 +- examples/nrf52840/src/bin/uart_split.rs | 2 +- .../rp/src/bin/lora_p2p_send_multicore.rs | 2 +- examples/rp/src/bin/multicore.rs | 2 +- examples/stm32f3/src/bin/button_events.rs | 4 +- examples/stm32h5/src/bin/usart_split.rs | 2 +- examples/stm32h7/src/bin/usart_split.rs | 2 +- tests/rp/src/bin/gpio_multicore.rs | 6 +- tests/rp/src/bin/multicore.rs | 4 +- 14 files changed, 63 insertions(+), 63 deletions(-) diff --git a/embassy-stm32-wpan/src/mac/driver.rs b/embassy-stm32-wpan/src/mac/driver.rs index 93898d888..bfc4f1ee8 100644 --- a/embassy-stm32-wpan/src/mac/driver.rs +++ b/embassy-stm32-wpan/src/mac/driver.rs @@ -93,7 +93,7 @@ impl<'d> embassy_net_driver::RxToken for RxToken<'d> { { // Only valid data events should be put into the queue - let data_event = match self.rx.try_recv().unwrap() { + let data_event = match self.rx.try_receive().unwrap() { MacEvent::McpsDataInd(data_event) => data_event, _ => unreachable!(), }; @@ -113,7 +113,7 @@ impl<'d> embassy_net_driver::TxToken for TxToken<'d> { F: FnOnce(&mut [u8]) -> R, { // Only valid tx buffers should be put into the queue - let buf = self.tx_buf.try_recv().unwrap(); + let buf = self.tx_buf.try_receive().unwrap(); let r = f(&mut buf[..len]); // The tx channel should always be of equal capacity to the tx_buf channel diff --git a/embassy-stm32-wpan/src/mac/runner.rs b/embassy-stm32-wpan/src/mac/runner.rs index 1be6df8a4..d3099b6b7 100644 --- a/embassy-stm32-wpan/src/mac/runner.rs +++ b/embassy-stm32-wpan/src/mac/runner.rs @@ -73,7 +73,7 @@ impl<'a> Runner<'a> { let mut msdu_handle = 0x02; loop { - let (buf, len) = self.tx_channel.recv().await; + let (buf, len) = self.tx_channel.receive().await; let _wm = self.write_mutex.lock().await; // The mutex should be dropped on the next loop iteration diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index e439207ef..7ad13cece 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -478,7 +478,7 @@ impl<'c, 'd, T: Instance> CanRx<'c, 'd, T> { pub async fn read(&mut self) -> Result { poll_fn(|cx| { T::state().err_waker.register(cx.waker()); - if let Poll::Ready(envelope) = T::state().rx_queue.recv().poll_unpin(cx) { + if let Poll::Ready(envelope) = T::state().rx_queue.receive().poll_unpin(cx) { return Poll::Ready(Ok(envelope)); } else if let Some(err) = self.curr_error() { return Poll::Ready(Err(err)); @@ -493,7 +493,7 @@ impl<'c, 'd, T: Instance> CanRx<'c, 'd, T> { /// /// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue. pub fn try_read(&mut self) -> Result { - if let Ok(envelope) = T::state().rx_queue.try_recv() { + if let Ok(envelope) = T::state().rx_queue.try_receive() { return Ok(envelope); } diff --git a/embassy-sync/src/channel.rs b/embassy-sync/src/channel.rs index dc727fb10..62ea1307d 100644 --- a/embassy-sync/src/channel.rs +++ b/embassy-sync/src/channel.rs @@ -147,16 +147,16 @@ where { /// Receive the next value. /// - /// See [`Channel::recv()`]. - pub fn recv(&self) -> RecvFuture<'_, M, T, N> { - self.channel.recv() + /// See [`Channel::receive()`]. + pub fn receive(&self) -> ReceiveFuture<'_, M, T, N> { + self.channel.receive() } /// Attempt to immediately receive the next value. /// - /// See [`Channel::try_recv()`] - pub fn try_recv(&self) -> Result { - self.channel.try_recv() + /// See [`Channel::try_receive()`] + pub fn try_receive(&self) -> Result { + self.channel.try_receive() } /// Allows a poll_fn to poll until the channel is ready to receive @@ -190,16 +190,16 @@ impl<'ch, T> Copy for DynamicReceiver<'ch, T> {} impl<'ch, T> DynamicReceiver<'ch, T> { /// Receive the next value. /// - /// See [`Channel::recv()`]. - pub fn recv(&self) -> DynamicRecvFuture<'_, T> { - DynamicRecvFuture { channel: self.channel } + /// See [`Channel::receive()`]. + pub fn receive(&self) -> DynamicReceiveFuture<'_, T> { + DynamicReceiveFuture { channel: self.channel } } /// Attempt to immediately receive the next value. /// - /// See [`Channel::try_recv()`] - pub fn try_recv(&self) -> Result { - self.channel.try_recv_with_context(None) + /// See [`Channel::try_receive()`] + pub fn try_receive(&self) -> Result { + self.channel.try_receive_with_context(None) } /// Allows a poll_fn to poll until the channel is ready to receive @@ -226,16 +226,16 @@ where } } -/// Future returned by [`Channel::recv`] and [`Receiver::recv`]. +/// Future returned by [`Channel::receive`] and [`Receiver::receive`]. #[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct RecvFuture<'ch, M, T, const N: usize> +pub struct ReceiveFuture<'ch, M, T, const N: usize> where M: RawMutex, { channel: &'ch Channel, } -impl<'ch, M, T, const N: usize> Future for RecvFuture<'ch, M, T, N> +impl<'ch, M, T, const N: usize> Future for ReceiveFuture<'ch, M, T, N> where M: RawMutex, { @@ -246,19 +246,19 @@ where } } -/// Future returned by [`DynamicReceiver::recv`]. +/// Future returned by [`DynamicReceiver::receive`]. #[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct DynamicRecvFuture<'ch, T> { +pub struct DynamicReceiveFuture<'ch, T> { channel: &'ch dyn DynamicChannel, } -impl<'ch, T> Future for DynamicRecvFuture<'ch, T> { +impl<'ch, T> Future for DynamicReceiveFuture<'ch, T> { type Output = T; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - match self.channel.try_recv_with_context(Some(cx)) { + match self.channel.try_receive_with_context(Some(cx)) { Ok(v) => Poll::Ready(v), - Err(TryRecvError::Empty) => Poll::Pending, + Err(TryReceiveError::Empty) => Poll::Pending, } } } @@ -324,7 +324,7 @@ impl<'ch, T> Unpin for DynamicSendFuture<'ch, T> {} trait DynamicChannel { fn try_send_with_context(&self, message: T, cx: Option<&mut Context<'_>>) -> Result<(), TrySendError>; - fn try_recv_with_context(&self, cx: Option<&mut Context<'_>>) -> Result; + fn try_receive_with_context(&self, cx: Option<&mut Context<'_>>) -> Result; fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> Poll<()>; fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()>; @@ -332,10 +332,10 @@ trait DynamicChannel { fn poll_receive(&self, cx: &mut Context<'_>) -> Poll; } -/// Error returned by [`try_recv`](Channel::try_recv). +/// Error returned by [`try_receive`](Channel::try_receive). #[derive(PartialEq, Eq, Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum TryRecvError { +pub enum TryReceiveError { /// A message could not be received because the channel is empty. Empty, } @@ -364,11 +364,11 @@ impl ChannelState { } } - fn try_recv(&mut self) -> Result { - self.try_recv_with_context(None) + fn try_receive(&mut self) -> Result { + self.try_receive_with_context(None) } - fn try_recv_with_context(&mut self, cx: Option<&mut Context<'_>>) -> Result { + fn try_receive_with_context(&mut self, cx: Option<&mut Context<'_>>) -> Result { if self.queue.is_full() { self.senders_waker.wake(); } @@ -379,7 +379,7 @@ impl ChannelState { if let Some(cx) = cx { self.receiver_waker.register(cx.waker()); } - Err(TryRecvError::Empty) + Err(TryReceiveError::Empty) } } @@ -474,8 +474,8 @@ where self.inner.lock(|rc| f(&mut *rc.borrow_mut())) } - fn try_recv_with_context(&self, cx: Option<&mut Context<'_>>) -> Result { - self.lock(|c| c.try_recv_with_context(cx)) + fn try_receive_with_context(&self, cx: Option<&mut Context<'_>>) -> Result { + self.lock(|c| c.try_receive_with_context(cx)) } /// Poll the channel for the next message @@ -536,16 +536,16 @@ where /// /// If there are no messages in the channel's buffer, this method will /// wait until a message is sent. - pub fn recv(&self) -> RecvFuture<'_, M, T, N> { - RecvFuture { channel: self } + pub fn receive(&self) -> ReceiveFuture<'_, M, T, N> { + ReceiveFuture { channel: self } } /// Attempt to immediately receive a message. /// /// This method will either receive a message from the channel immediately or return an error /// if the channel is empty. - pub fn try_recv(&self) -> Result { - self.lock(|c| c.try_recv()) + pub fn try_receive(&self) -> Result { + self.lock(|c| c.try_receive()) } } @@ -559,8 +559,8 @@ where Channel::try_send_with_context(self, m, cx) } - fn try_recv_with_context(&self, cx: Option<&mut Context<'_>>) -> Result { - Channel::try_recv_with_context(self, cx) + fn try_receive_with_context(&self, cx: Option<&mut Context<'_>>) -> Result { + Channel::try_receive_with_context(self, cx) } fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> Poll<()> { @@ -616,15 +616,15 @@ mod tests { fn receiving_once_with_one_send() { let mut c = ChannelState::::new(); assert!(c.try_send(1).is_ok()); - assert_eq!(c.try_recv().unwrap(), 1); + assert_eq!(c.try_receive().unwrap(), 1); assert_eq!(capacity(&c), 3); } #[test] fn receiving_when_empty() { let mut c = ChannelState::::new(); - match c.try_recv() { - Err(TryRecvError::Empty) => assert!(true), + match c.try_receive() { + Err(TryReceiveError::Empty) => assert!(true), _ => assert!(false), } assert_eq!(capacity(&c), 3); @@ -634,7 +634,7 @@ mod tests { fn simple_send_and_receive() { let c = Channel::::new(); assert!(c.try_send(1).is_ok()); - assert_eq!(c.try_recv().unwrap(), 1); + assert_eq!(c.try_receive().unwrap(), 1); } #[test] @@ -654,7 +654,7 @@ mod tests { let r: DynamicReceiver<'_, u32> = c.receiver().into(); assert!(s.try_send(1).is_ok()); - assert_eq!(r.try_recv().unwrap(), 1); + assert_eq!(r.try_receive().unwrap(), 1); } #[futures_test::test] @@ -669,14 +669,14 @@ mod tests { assert!(c2.try_send(1).is_ok()); }) .is_ok()); - assert_eq!(c.recv().await, 1); + assert_eq!(c.receive().await, 1); } #[futures_test::test] async fn sender_send_completes_if_capacity() { let c = Channel::::new(); c.send(1).await; - assert_eq!(c.recv().await, 1); + assert_eq!(c.receive().await, 1); } #[futures_test::test] @@ -694,11 +694,11 @@ mod tests { // Wish I could think of a means of determining that the async send is waiting instead. // However, I've used the debugger to observe that the send does indeed wait. Delay::new(Duration::from_millis(500)).await; - assert_eq!(c.recv().await, 1); + assert_eq!(c.receive().await, 1); assert!(executor .spawn(async move { loop { - c.recv().await; + c.receive().await; } }) .is_ok()); diff --git a/examples/nrf52840/src/bin/channel.rs b/examples/nrf52840/src/bin/channel.rs index d782a79e7..bd9c909da 100644 --- a/examples/nrf52840/src/bin/channel.rs +++ b/examples/nrf52840/src/bin/channel.rs @@ -35,7 +35,7 @@ async fn main(spawner: Spawner) { unwrap!(spawner.spawn(my_task())); loop { - match CHANNEL.recv().await { + match CHANNEL.receive().await { LedState::On => led.set_high(), LedState::Off => led.set_low(), } diff --git a/examples/nrf52840/src/bin/channel_sender_receiver.rs b/examples/nrf52840/src/bin/channel_sender_receiver.rs index fcccdaed5..ec4f1d800 100644 --- a/examples/nrf52840/src/bin/channel_sender_receiver.rs +++ b/examples/nrf52840/src/bin/channel_sender_receiver.rs @@ -33,7 +33,7 @@ async fn recv_task(led: AnyPin, receiver: Receiver<'static, NoopRawMutex, LedSta let mut led = Output::new(led, Level::Low, OutputDrive::Standard); loop { - match receiver.recv().await { + match receiver.receive().await { LedState::On => led.set_high(), LedState::Off => led.set_low(), } diff --git a/examples/nrf52840/src/bin/uart_split.rs b/examples/nrf52840/src/bin/uart_split.rs index 9979a1d53..b748bfcd8 100644 --- a/examples/nrf52840/src/bin/uart_split.rs +++ b/examples/nrf52840/src/bin/uart_split.rs @@ -46,7 +46,7 @@ async fn main(spawner: Spawner) { // back out the buffer we receive from the read // task. loop { - let buf = CHANNEL.recv().await; + let buf = CHANNEL.receive().await; info!("writing..."); unwrap!(tx.write(&buf).await); } diff --git a/examples/rp/src/bin/lora_p2p_send_multicore.rs b/examples/rp/src/bin/lora_p2p_send_multicore.rs index 89a62818d..b54cc92f6 100644 --- a/examples/rp/src/bin/lora_p2p_send_multicore.rs +++ b/examples/rp/src/bin/lora_p2p_send_multicore.rs @@ -113,7 +113,7 @@ async fn core1_task( }; loop { - let buffer: [u8; 3] = CHANNEL.recv().await; + let buffer: [u8; 3] = CHANNEL.receive().await; match lora.prepare_for_tx(&mdltn_params, 20, false).await { Ok(()) => {} Err(err) => { diff --git a/examples/rp/src/bin/multicore.rs b/examples/rp/src/bin/multicore.rs index 893b724bf..bf017f6a7 100644 --- a/examples/rp/src/bin/multicore.rs +++ b/examples/rp/src/bin/multicore.rs @@ -56,7 +56,7 @@ async fn core0_task() { async fn core1_task(mut led: Output<'static, PIN_25>) { info!("Hello from core 1"); loop { - match CHANNEL.recv().await { + match CHANNEL.receive().await { LedState::On => led.set_high(), LedState::Off => led.set_low(), } diff --git a/examples/stm32f3/src/bin/button_events.rs b/examples/stm32f3/src/bin/button_events.rs index 02c475f66..8e97e85eb 100644 --- a/examples/stm32f3/src/bin/button_events.rs +++ b/examples/stm32f3/src/bin/button_events.rs @@ -49,12 +49,12 @@ impl<'a> Leds<'a> { async fn show(&mut self) { self.leds[self.current_led].set_high(); - if let Ok(new_message) = with_timeout(Duration::from_millis(500), CHANNEL.recv()).await { + if let Ok(new_message) = with_timeout(Duration::from_millis(500), CHANNEL.receive()).await { self.leds[self.current_led].set_low(); self.process_event(new_message).await; } else { self.leds[self.current_led].set_low(); - if let Ok(new_message) = with_timeout(Duration::from_millis(200), CHANNEL.recv()).await { + if let Ok(new_message) = with_timeout(Duration::from_millis(200), CHANNEL.receive()).await { self.process_event(new_message).await; } } diff --git a/examples/stm32h5/src/bin/usart_split.rs b/examples/stm32h5/src/bin/usart_split.rs index debd6f454..a6b2e690b 100644 --- a/examples/stm32h5/src/bin/usart_split.rs +++ b/examples/stm32h5/src/bin/usart_split.rs @@ -44,7 +44,7 @@ async fn main(spawner: Spawner) -> ! { unwrap!(spawner.spawn(reader(rx))); loop { - let buf = CHANNEL.recv().await; + let buf = CHANNEL.receive().await; info!("writing..."); unwrap!(tx.write(&buf).await); } diff --git a/examples/stm32h7/src/bin/usart_split.rs b/examples/stm32h7/src/bin/usart_split.rs index 330d1ce09..aa0753450 100644 --- a/examples/stm32h7/src/bin/usart_split.rs +++ b/examples/stm32h7/src/bin/usart_split.rs @@ -44,7 +44,7 @@ async fn main(spawner: Spawner) -> ! { unwrap!(spawner.spawn(reader(rx))); loop { - let buf = CHANNEL.recv().await; + let buf = CHANNEL.receive().await; info!("writing..."); unwrap!(tx.write(&buf).await); } diff --git a/tests/rp/src/bin/gpio_multicore.rs b/tests/rp/src/bin/gpio_multicore.rs index 780112bc1..611cecb76 100644 --- a/tests/rp/src/bin/gpio_multicore.rs +++ b/tests/rp/src/bin/gpio_multicore.rs @@ -38,11 +38,11 @@ async fn core0_task(p: PIN_0) { let mut pin = Output::new(p, Level::Low); CHANNEL0.send(()).await; - CHANNEL1.recv().await; + CHANNEL1.receive().await; pin.set_high(); - CHANNEL1.recv().await; + CHANNEL1.receive().await; info!("Test OK"); cortex_m::asm::bkpt(); @@ -52,7 +52,7 @@ async fn core0_task(p: PIN_0) { async fn core1_task(p: PIN_1) { info!("CORE1 is running"); - CHANNEL0.recv().await; + CHANNEL0.receive().await; let mut pin = Input::new(p, Pull::Down); let wait = pin.wait_for_rising_edge(); diff --git a/tests/rp/src/bin/multicore.rs b/tests/rp/src/bin/multicore.rs index 114889dec..c4579e5bb 100644 --- a/tests/rp/src/bin/multicore.rs +++ b/tests/rp/src/bin/multicore.rs @@ -34,7 +34,7 @@ async fn core0_task() { info!("CORE0 is running"); let ping = true; CHANNEL0.send(ping).await; - let pong = CHANNEL1.recv().await; + let pong = CHANNEL1.receive().await; assert_eq!(ping, pong); info!("Test OK"); @@ -44,6 +44,6 @@ async fn core0_task() { #[embassy_executor::task] async fn core1_task() { info!("CORE1 is running"); - let ping = CHANNEL0.recv().await; + let ping = CHANNEL0.receive().await; CHANNEL1.send(ping).await; } From a5484cd119293967d1d1c663ee2e2ee3a5827e3d Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Tue, 22 Aug 2023 20:52:02 +0200 Subject: [PATCH 76/77] Modified the brr calculation to be fully 32-bit --- embassy-stm32/src/usart/mod.rs | 46 +++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index e203336e1..255ddfd4b 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -809,45 +809,57 @@ fn configure(r: Regs, config: &Config, pclk_freq: Hertz, kind: Kind, enable_rx: Kind::Uart => (1, 0x10, 0x1_0000), }; + fn calculate_brr(baud: u32, pclk: u32, presc: u32, mul: u32) -> u32 { + // The calculation to be done to get the BRR is `mul * pclk / presc / baud` + // To do this in 32-bit only we can't multiply `mul` and `pclk` + let clock = pclk / presc; + + // The mul is applied as the last operation to prevent overflow + let brr = clock / baud * mul; + + // The BRR calculation will be a bit off because of integer rounding. + // Because we multiplied our inaccuracy with mul, our rounding now needs to be in proportion to mul. + let rounding = ((clock % baud) * mul + (baud / 2)) / baud; + + brr + rounding + } + #[cfg(not(usart_v1))] let mut over8 = false; - let mut found = None; + let mut found_brr = None; for &(presc, _presc_val) in &DIVS { - let denom = (config.baudrate * presc as u32) as u64; - let div = (pclk_freq.0 as u64 * mul + (denom / 2)) / denom; + let brr = calculate_brr(config.baudrate, pclk_freq.0, presc as u32, mul); trace!( "USART: presc={}, div=0x{:08x} (mantissa = {}, fraction = {})", presc, - div, - div >> 4, - div & 0x0F + brr, + brr >> 4, + brr & 0x0F ); - if div < brr_min { + if brr < brr_min { #[cfg(not(usart_v1))] - if div * 2 >= brr_min && kind == Kind::Uart && !cfg!(usart_v1) { + if brr * 2 >= brr_min && kind == Kind::Uart && !cfg!(usart_v1) { over8 = true; - let div = div as u32; - r.brr().write_value(regs::Brr(((div << 1) & !0xF) | (div & 0x07))); + r.brr().write_value(regs::Brr(((brr << 1) & !0xF) | (brr & 0x07))); #[cfg(usart_v4)] r.presc().write(|w| w.set_prescaler(_presc_val)); - found = Some(div); + found_brr = Some(brr); break; } panic!("USART: baudrate too high"); } - if div < brr_max { - let div = div as u32; - r.brr().write_value(regs::Brr(div)); + if brr < brr_max { + r.brr().write_value(regs::Brr(brr)); #[cfg(usart_v4)] r.presc().write(|w| w.set_prescaler(_presc_val)); - found = Some(div); + found_brr = Some(brr); break; } } - let div = found.expect("USART: baudrate too low"); + let brr = found_brr.expect("USART: baudrate too low"); #[cfg(not(usart_v1))] let oversampling = if over8 { "8 bit" } else { "16 bit" }; @@ -857,7 +869,7 @@ fn configure(r: Regs, config: &Config, pclk_freq: Hertz, kind: Kind, enable_rx: "Using {} oversampling, desired baudrate: {}, actual baudrate: {}", oversampling, config.baudrate, - (pclk_freq.0 * mul as u32) / div + pclk_freq.0 / brr * mul ); r.cr2().write(|w| { From 048bdf6968773a642c60ddcac46958d3a54af7c3 Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 22 Aug 2023 16:48:08 -0500 Subject: [PATCH 77/77] stm32/rtc: allow dead code --- embassy-stm32/src/rtc/v2.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs index e9b83123c..53d0161d6 100644 --- a/embassy-stm32/src/rtc/v2.rs +++ b/embassy-stm32/src/rtc/v2.rs @@ -72,6 +72,7 @@ impl core::ops::Sub for RtcInstant { } } +#[allow(dead_code)] #[derive(Clone, Copy)] pub(crate) enum WakeupPrescaler { Div2, @@ -120,6 +121,7 @@ impl From for u32 { } } +#[allow(dead_code)] impl WakeupPrescaler { pub fn compute_min(val: u32) -> Self { *[