From 0aeca2d790c4913f6088d381495fcf9a5c8c02c4 Mon Sep 17 00:00:00 2001 From: Vadim Kaushan Date: Sat, 20 Jun 2020 13:53:36 +0300 Subject: [PATCH 1/7] Add async-embedded-traits dependency --- Cargo.toml | 2 ++ src/lib.rs | 1 + src/prelude.rs | 6 ++++++ 3 files changed, 9 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index a5ff28c..808233b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,9 +14,11 @@ embedded-hal = { version = "0.2.3", features = ["unproven"] } nb = "0.1.2" riscv = "0.5.4" e310x = { version = "0.8.0", features = ["rt"] } +async-embedded-traits = { version = "0.1.0", optional = true } [features] g002 = ["e310x/g002"] +async-traits = ["async-embedded-traits"] [package.metadata.docs.rs] features = ["g002"] diff --git a/src/lib.rs b/src/lib.rs index 1326472..d89462c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,7 @@ //! family of microcontrollers. #![deny(missing_docs)] +#![cfg_attr(feature = "async-traits", feature(generic_associated_types))] #![no_std] pub use e310x; diff --git a/src/prelude.rs b/src/prelude.rs index db42b0a..bf1c3aa 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -7,6 +7,12 @@ pub use embedded_hal::digital::v2::{ StatefulOutputPin as _embedded_hal_digital_v2_StatefulOutputPin, ToggleableOutputPin as _embedded_hal_digital_v2_ToggleableOutputPin, }; +#[cfg(feature = "async-traits")] +pub use async_embedded_traits::{ + serial::AsyncWrite as _async_embedded_traits_serial_AsyncWrite, + serial::AsyncRead as _async_embedded_traits_serial_AsyncRead, + spi::AsyncTransfer as _async_embedded_traits_spi_AsyncTransfer, +}; pub use crate::clock::PrciExt as _e310x_hal_clock_PrciExt; pub use crate::clock::AonExt as _e310x_hal_clock_AonExt; pub use crate::gpio::GpioExt as _e310x_hal_gpio_GpioExt; From ab1b0a76b9ba98207409e4611948bcce0e9305aa Mon Sep 17 00:00:00 2001 From: Vadim Kaushan Date: Sat, 20 Jun 2020 13:53:53 +0300 Subject: [PATCH 2/7] Implement async traits for serial --- src/serial.rs | 218 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 218 insertions(+) diff --git a/src/serial.rs b/src/serial.rs index 18d0cda..a8a03e9 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -155,6 +155,224 @@ impl serial::Write for Tx { } } +#[cfg(feature = "async-traits")] +mod async_impls { + use core::convert::Infallible; + use core::future::Future; + use core::pin::Pin; + use core::task::{Context, Poll}; + use async_embedded_traits::serial::{AsyncRead, AsyncWrite}; + use super::{UartX, Serial, Rx, Tx, uart0::RegisterBlock}; + + impl AsyncRead for Serial { + type Error = Infallible; + type ReadByteFuture<'f> = AsyncReadByteFuture<'f>; + type ReadFuture<'f> = AsyncReadFuture<'f>; + + fn async_read_byte(&mut self) -> Self::ReadByteFuture<'_> { + AsyncReadByteFuture { + uart: &self.uart, + } + } + + fn async_read<'a>(&'a mut self, data: &'a mut [u8]) -> Self::ReadFuture<'_> { + AsyncReadFuture { + uart: &self.uart, + data, + offset: 0 + } + } + } + + impl AsyncRead for Rx { + type Error = Infallible; + type ReadByteFuture<'f> = AsyncReadByteFuture<'f>; + type ReadFuture<'f> = AsyncReadFuture<'f>; + + fn async_read_byte(&mut self) -> Self::ReadByteFuture<'_> { + AsyncReadByteFuture { + uart: &self.uart, + } + } + + fn async_read<'a>(&'a mut self, data: &'a mut [u8]) -> Self::ReadFuture<'_> { + AsyncReadFuture { + uart: &self.uart, + data, + offset: 0 + } + } + } + + pub struct AsyncReadByteFuture<'a> { + uart: &'a RegisterBlock, + } + + impl<'a> Future for AsyncReadByteFuture<'a> { + type Output = Result; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let rxdata = self.uart.rxdata.read(); + + if rxdata.empty().bit_is_set() { + // TODO: replace with something useful + cx.waker().wake_by_ref(); + Poll::Pending + } else { + let byte = rxdata.data().bits() as u8; + Poll::Ready(Ok(byte)) + } + } + } + + pub struct AsyncReadFuture<'a> { + uart: &'a RegisterBlock, + data: &'a mut [u8], + offset: usize, + } + + impl<'a> Future for AsyncReadFuture<'a> { + type Output = Result<(), Infallible>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + while self.offset < self.data.len() { + let rxdata = self.uart.rxdata.read(); + + if rxdata.empty().bit_is_set() { + // TODO: replace with something useful + cx.waker().wake_by_ref(); + return Poll::Pending + } else { + let byte = rxdata.data().bits() as u8; + let offset = self.offset; // Stupid Rust + self.data[offset] = byte; + self.offset += 1; + } + } + Poll::Ready(Ok(())) + } + } + + impl AsyncWrite for Serial { + type Error = Infallible; + type WriteByteFuture<'t> = AsyncWriteByteFuture<'t>; + type WriteFuture<'t> = AsyncWriteFuture<'t>; + type FlushFuture<'t> = AsyncFlushFuture<'t>; + + fn async_write_byte(&mut self, byte: u8) -> Self::WriteByteFuture<'_> { + AsyncWriteByteFuture { + uart: &self.uart, + byte + } + } + + fn async_write<'a>(&'a mut self, data: &'a [u8]) -> AsyncWriteFuture<'a> { + AsyncWriteFuture { + uart: &self.uart, + data, + } + } + + fn async_flush(&mut self) -> AsyncFlushFuture<'_> { + AsyncFlushFuture { + uart: &self.uart, + } + } + } + + impl AsyncWrite for Tx { + type Error = Infallible; + type WriteByteFuture<'t> = AsyncWriteByteFuture<'t>; + type WriteFuture<'t> = AsyncWriteFuture<'t>; + type FlushFuture<'t> = AsyncFlushFuture<'t>; + + fn async_write_byte(&mut self, byte: u8) -> Self::WriteByteFuture<'_> { + AsyncWriteByteFuture { + uart: &self.uart, + byte + } + } + + fn async_write<'a>(&'a mut self, data: &'a [u8]) -> AsyncWriteFuture<'a> { + AsyncWriteFuture { + uart: &self.uart, + data, + } + } + + fn async_flush(&mut self) -> AsyncFlushFuture<'_> { + AsyncFlushFuture { + uart: &self.uart, + } + } + } + + pub struct AsyncWriteByteFuture<'a> { + uart: &'a RegisterBlock, + byte: u8, + } + + impl<'a> Future for AsyncWriteByteFuture<'a> { + type Output = Result<(), Infallible>; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let txdata = self.uart.txdata.read(); + + if txdata.full().bit_is_set() { + cx.waker().wake_by_ref(); + Poll::Pending + } else { + unsafe { + self.uart.txdata.write(|w| w.data().bits(self.byte)); + } + Poll::Ready(Ok(())) + } + } + } + + pub struct AsyncWriteFuture<'a> { + uart: &'a RegisterBlock, + data: &'a [u8], + } + + impl<'a> Future for AsyncWriteFuture<'a> { + type Output = Result<(), Infallible>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + while let Some(byte) = self.data.first() { + let txdata = self.uart.txdata.read(); + + if txdata.full().bit_is_set() { + cx.waker().wake_by_ref(); + return Poll::Pending; + } else { + self.uart.txdata.write(|w| unsafe { w.data().bits(*byte) }); + self.data = &self.data[1..]; + } + } + Poll::Ready(Ok(())) + } + } + + pub struct AsyncFlushFuture<'a> { + uart: &'a RegisterBlock, + } + + impl<'a> Future for AsyncFlushFuture<'a> { + type Output = Result<(), Infallible>; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + if self.uart.ip.read().txwm().bit_is_set() { + // FIFO count is below the receive watermark (1) + Poll::Ready(Ok(())) + } else { + cx.waker().wake_by_ref(); + Poll::Pending + } + } + } +} + // Backward compatibility impl Serial { /// Configures a UART peripheral to provide serial communication From 9050c0ebaba07e5e640eeedb4ece0b4bc29938c0 Mon Sep 17 00:00:00 2001 From: Vadim Kaushan Date: Sat, 20 Jun 2020 14:12:28 +0300 Subject: [PATCH 3/7] Implement async traits for spi --- src/spi.rs | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/src/spi.rs b/src/spi.rs index 3343efd..b6cedeb 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -352,6 +352,76 @@ impl embedded_hal::blocking::spi::WriteIter for Spi AsyncTransfer for Spi { + type Error = Infallible; + type TransferFuture<'t> = AsyncTransferFuture<'t>; + + fn async_transfer<'a>(&'a mut self, data: &'a mut [u8]) -> Self::TransferFuture<'a> { + // Ensure that RX FIFO is empty + while self.spi.rxdata.read().empty().bit_is_clear() { } + self.cs_mode_frame(); + + AsyncTransferFuture { + spi: &self.spi, + data, + iwrite: 0, + iread: 0, + } + } + } + + pub struct AsyncTransferFuture<'a> { + spi: &'a RegisterBlock, + data: &'a mut [u8], + iwrite: usize, + iread: usize, + } + + impl<'a> Future for AsyncTransferFuture<'a> { + type Output = Result<(), Infallible>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let data_len = self.data.len(); + + while self.iwrite < data_len || self.iread < data_len { + if self.iwrite < data_len && self.spi.txdata.read().full().bit_is_clear() { + let iwrite = self.iwrite; + let byte = unsafe { *self.data.get_unchecked(iwrite) }; + self.iwrite = iwrite + 1; + self.spi.txdata.write(|w| unsafe { w.data().bits(byte) }); + } + + if self.iread < self.iwrite { + let data = self.spi.rxdata.read(); + if data.empty().bit_is_clear() { + let iread = self.iread; + unsafe { *self.data.get_unchecked_mut(iread) = data.data().bits() }; + self.iread = iread + 1; + } else { + cx.waker().wake_by_ref(); + return Poll::Pending; + } + } + } + + // self.cs_mode_word(); + if self.spi.csmode.read().bits() != 3 { + self.spi.csmode.write(|w| unsafe { w.bits(0) }); + } + + Poll::Ready(Ok(())) + } + } +} // Backward compatibility impl Spi { From d7009e0a54e384b8b1cfc817700bb843b176f60d Mon Sep 17 00:00:00 2001 From: Vadim Kaushan Date: Sat, 20 Jun 2020 22:39:50 +0300 Subject: [PATCH 4/7] Update async-embedded-traits dependency --- Cargo.toml | 2 +- src/prelude.rs | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 808233b..a88e985 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ embedded-hal = { version = "0.2.3", features = ["unproven"] } nb = "0.1.2" riscv = "0.5.4" e310x = { version = "0.8.0", features = ["rt"] } -async-embedded-traits = { version = "0.1.0", optional = true } +async-embedded-traits = { version = "0.1.1", optional = true } [features] g002 = ["e310x/g002"] diff --git a/src/prelude.rs b/src/prelude.rs index bf1c3aa..f56c9ef 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -8,11 +8,7 @@ pub use embedded_hal::digital::v2::{ ToggleableOutputPin as _embedded_hal_digital_v2_ToggleableOutputPin, }; #[cfg(feature = "async-traits")] -pub use async_embedded_traits::{ - serial::AsyncWrite as _async_embedded_traits_serial_AsyncWrite, - serial::AsyncRead as _async_embedded_traits_serial_AsyncRead, - spi::AsyncTransfer as _async_embedded_traits_spi_AsyncTransfer, -}; +pub use async_embedded_traits::prelude::*; pub use crate::clock::PrciExt as _e310x_hal_clock_PrciExt; pub use crate::clock::AonExt as _e310x_hal_clock_AonExt; pub use crate::gpio::GpioExt as _e310x_hal_gpio_GpioExt; From 0540c7e169e117f337e5684480d7bb00bd003c03 Mon Sep 17 00:00:00 2001 From: Vadim Kaushan Date: Sat, 20 Jun 2020 22:40:07 +0300 Subject: [PATCH 5/7] Suppress warning --- src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index d89462c..6d2101c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,9 +4,11 @@ //! family of microcontrollers. #![deny(missing_docs)] -#![cfg_attr(feature = "async-traits", feature(generic_associated_types))] #![no_std] +#![allow(incomplete_features)] +#![cfg_attr(feature = "async-traits", feature(generic_associated_types))] + pub use e310x; pub mod core; From 951d07cb1c6a9cbf0161cf815faf622a39f3a061 Mon Sep 17 00:00:00 2001 From: Vadim Kaushan Date: Sat, 20 Jun 2020 22:41:13 +0300 Subject: [PATCH 6/7] Implement AsyncDelayMs for Delay --- src/delay.rs | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/delay.rs b/src/delay.rs index 53b682b..ad1e527 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -98,3 +98,46 @@ impl DelayMs for Sleep { self.delay_ms(u32::from(ms)); } } + +#[cfg(feature = "async-traits")] +mod async_impls { + use core::future::Future; + use core::pin::Pin; + use core::task::{Context, Poll}; + use async_embedded_traits::delay::AsyncDelayMs; + use async_embedded_traits::impl_delay_ms_for_ms_u32; + use super::{Delay, MTIME}; + + impl AsyncDelayMs for Delay { + type DelayFuture<'f> = MtimeDelayFuture; + + fn async_delay_ms(&mut self, ms: u32) -> Self::DelayFuture<'_> { + let ticks = (ms as u64) * 32768 / 1000; + let mtime = MTIME; + let deadline = mtime.mtime().wrapping_add(ticks); + MtimeDelayFuture { + deadline, + } + } + } + + impl_delay_ms_for_ms_u32!(Delay); + + pub struct MtimeDelayFuture { + deadline: u64, + } + + impl Future for MtimeDelayFuture { + type Output = (); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { + let mtime = MTIME; + if mtime.mtime() < self.deadline { + cx.waker().wake_by_ref(); + Poll::Pending + } else { + Poll::Ready(()) + } + } + } +} From aa12c20d239ca6e52e2b4fd54865148d362cd82f Mon Sep 17 00:00:00 2001 From: Vadim Kaushan Date: Sat, 20 Jun 2020 22:41:50 +0300 Subject: [PATCH 7/7] Implement AsyncWrite and AsyncWriteIter for Spi --- src/spi.rs | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 125 insertions(+), 1 deletion(-) diff --git a/src/spi.rs b/src/spi.rs index b6cedeb..c25c39e 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -358,7 +358,7 @@ mod async_impls { use core::future::Future; use core::pin::Pin; use core::task::{Context, Poll}; - use async_embedded_traits::spi::AsyncTransfer; + use async_embedded_traits::spi::{AsyncTransfer, AsyncWrite, AsyncWriteIter}; use super::{Spi, SpiX, qspi0::RegisterBlock}; impl AsyncTransfer for Spi { @@ -368,6 +368,7 @@ mod async_impls { fn async_transfer<'a>(&'a mut self, data: &'a mut [u8]) -> Self::TransferFuture<'a> { // Ensure that RX FIFO is empty while self.spi.rxdata.read().empty().bit_is_clear() { } + self.cs_mode_frame(); AsyncTransferFuture { @@ -421,6 +422,129 @@ mod async_impls { Poll::Ready(Ok(())) } } + + impl AsyncWrite for Spi { + type Error = Infallible; + type WriteFuture<'t> = AsyncWriteFuture<'t>; + + #[inline(never)] + fn async_write<'a>(&'a mut self, data: &'a [u8]) -> Self::WriteFuture<'a> { + // Ensure that RX FIFO is empty + while self.spi.rxdata.read().empty().bit_is_clear() { } + + self.cs_mode_frame(); + + AsyncWriteFuture { + spi: &self.spi, + data, + iwrite: 0, + iread: 0, + } + } + } + + pub struct AsyncWriteFuture<'a> { + spi: &'a RegisterBlock, + data: &'a [u8], + iwrite: usize, + iread: usize, + } + + impl<'a> Future for AsyncWriteFuture<'a> { + type Output = Result<(), Infallible>; + + //#[inline(never)] + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let data_len = self.data.len(); + + while self.iwrite < data_len || self.iread < data_len { + if self.iwrite < data_len && self.spi.txdata.read().full().bit_is_clear() { + let iwrite = self.iwrite; + let byte = unsafe { *self.data.get_unchecked(iwrite) }; + self.iwrite = iwrite + 1; + self.spi.txdata.write(|w| unsafe { w.data().bits(byte) }); + } + + if self.iread < self.iwrite { + // Read and discard byte, if any + if self.spi.rxdata.read().empty().bit_is_clear() { + self.iread += 1; + } else { + cx.waker().wake_by_ref(); + return Poll::Pending; + } + } + } + + // self.cs_mode_word(); + if self.spi.csmode.read().bits() != 3 { + self.spi.csmode.write(|w| unsafe { w.bits(0) }); + } + + Poll::Ready(Ok(())) + } + } + + impl AsyncWriteIter for Spi { + type Error = Infallible; + type WriteIterFuture<'t> = AsyncWriteIterFuture<'t>; + + #[inline(never)] + fn async_write_iter<'a>(&'a mut self, iter: &'a mut dyn Iterator) -> Self::WriteIterFuture<'a> { + // Ensure that RX FIFO is empty + while self.spi.rxdata.read().empty().bit_is_clear() { } + + self.cs_mode_frame(); + + AsyncWriteIterFuture { + spi: &self.spi, + iter, + has_data: true, + read_count: 0, + } + } + } + + pub struct AsyncWriteIterFuture<'a> { + spi: &'a RegisterBlock, + iter: &'a mut dyn Iterator, + has_data: bool, + read_count: u8, + } + + impl<'a> Future for AsyncWriteIterFuture<'a> { + type Output = Result<(), Infallible>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + while self.has_data || self.read_count > 0 { + if self.has_data && self.spi.txdata.read().full().bit_is_clear() { + if let Some(byte) = self.iter.next() { + self.spi.txdata.write(|w| unsafe { w.data().bits(byte) }); + self.read_count += 1; + } else { + self.has_data = false; + } + } + + if self.read_count > 0 { + // Read and discard byte, if any + if self.spi.rxdata.read().empty().bit_is_clear() { + self.read_count -= 1; + } else { + cx.waker().wake_by_ref(); + return Poll::Pending; + } + } + } + + // self.cs_mode_word(); + if self.spi.csmode.read().bits() != 3 { + self.spi.csmode.write(|w| unsafe { w.bits(0) }); + } + + Poll::Ready(Ok(())) + } + } } // Backward compatibility