Skip to main content

vorago_shared_hal/uart/
tx_async.rs

1//! # Async UART transmission functionality.
2//!
3//! This module provides the [TxAsync] struct which implements the [embedded_io_async::Write] trait.
4//! This trait allows for asynchronous sending of data streams. Please note that this module does
5//! not specify/declare the interrupt handlers which must be provided for async support to work.
6//! However, it the [on_interrupt_tx] interrupt handler.
7//!
8//! This handler should be called in ALL user interrupt handlers which handle UART TX interrupts
9//! for a given UART bank.
10use core::{cell::RefCell, future::Future};
11
12use critical_section::Mutex;
13use embassy_sync::waitqueue::AtomicWaker;
14use embedded_io_async::Write;
15use portable_atomic::AtomicBool;
16use raw_slice::RawBufSlice;
17
18use super::*;
19
20static UART_TX_WAKERS: [AtomicWaker; 2] = [const { AtomicWaker::new() }; 2];
21static TX_CONTEXTS: [Mutex<RefCell<TxContext>>; 2] =
22    [const { Mutex::new(RefCell::new(TxContext::new())) }; 2];
23// Completion flag. Kept outside of the context structure as an atomic to avoid
24// critical section.
25static TX_DONE: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2];
26
27#[inline]
28fn tx_is_drained(tx: &Tx) -> bool {
29    let tx_status = tx.regs.read_tx_status();
30    tx.regs.read_state().tx_fifo().value() == 0 && !tx_status.tx_busy()
31}
32
33/// This is a generic interrupt handler to handle asynchronous UART TX operations for a given
34/// UART bank.
35///
36/// The user has to call this once in the interrupt handler responsible for the TX interrupts on
37/// the given UART bank.
38pub fn on_interrupt_tx(bank: Bank) {
39    let mut uart = unsafe { bank.steal_regs() };
40    let idx = bank as usize;
41    let irq_enabled = uart.read_interrupt_enable();
42    // IRQ is not related to TX.
43    if !irq_enabled.tx_below_trigger() && !irq_enabled.tx_empty() {
44        return;
45    }
46
47    let tx_status = uart.read_tx_status();
48    let interrupt_status = uart.read_interrupt_status();
49    let unexpected_overrun = tx_status.wr_lost();
50    let mut context = critical_section::with(|cs| {
51        let context_ref = TX_CONTEXTS[idx].borrow(cs);
52        *context_ref.borrow()
53    });
54    context.tx_overrun = unexpected_overrun;
55    // Safety: We documented that the user provided slice must outlive the future, so we convert
56    // the raw pointer back to the slice here.
57    let slice = unsafe { context.slice.get().unwrap() };
58    if context.progress >= slice.len() && interrupt_status.tx_empty() {
59        uart.modify_interrupt_enable(|value| {
60            value
61                .with_tx_below_trigger(false)
62                .with_tx_empty(false)
63                .with_tx_status(false)
64        });
65        uart.modify_enable(|value| value.with_tx(false));
66        // Write back updated context structure.
67        critical_section::with(|cs| {
68            let context_ref = TX_CONTEXTS[idx].borrow(cs);
69            *context_ref.borrow_mut() = context;
70        });
71        // Transfer is done.
72        TX_DONE[idx].store(true, core::sync::atomic::Ordering::Relaxed);
73        UART_TX_WAKERS[idx].wake();
74        return;
75    }
76    while context.progress < slice.len() {
77        if !uart.read_tx_status().ready() {
78            break;
79        }
80        // Safety: TX structure is owned by the future which does not write into the the data
81        // register, so we can assume we are the only one writing to the data register.
82        uart.write_data(Data::new_with_raw_value(slice[context.progress] as u32));
83        context.progress += 1;
84    }
85    // Now we only require the TX empty interrupt.
86    if context.progress == slice.len() {
87        uart.modify_interrupt_enable(|value| value.with_tx_below_trigger(false));
88    }
89
90    // Write back updated context structure.
91    critical_section::with(|cs| {
92        let context_ref = TX_CONTEXTS[idx].borrow(cs);
93        *context_ref.borrow_mut() = context;
94    });
95}
96
97#[derive(Debug, Copy, Clone)]
98pub struct TxContext {
99    progress: usize,
100    tx_overrun: bool,
101    slice: RawBufSlice,
102}
103
104#[allow(clippy::new_without_default)]
105impl TxContext {
106    pub const fn new() -> Self {
107        Self {
108            progress: 0,
109            tx_overrun: false,
110            slice: RawBufSlice::new_nulled(),
111        }
112    }
113}
114
115pub struct TxFuture {
116    id: Bank,
117}
118
119impl TxFuture {
120    /// # Safety
121    ///
122    /// This function stores the raw pointer of the passed data slice. The user MUST ensure
123    /// that the slice outlives the data structure.
124    pub unsafe fn new(tx: &mut Tx, data: &[u8]) -> Self {
125        TX_DONE[tx.id as usize].store(false, core::sync::atomic::Ordering::Relaxed);
126        tx.disable_interrupts();
127        tx.disable();
128        tx.clear_fifo();
129
130        let init_fill_count = core::cmp::min(data.len(), FIFO_DEPTH);
131        // We fill the FIFO.
132        for data in data.iter().take(init_fill_count) {
133            tx.regs.write_data(Data::new_with_raw_value(*data as u32));
134        }
135        critical_section::with(|cs| {
136            let context_ref = TX_CONTEXTS[tx.id as usize].borrow(cs);
137            let mut context = context_ref.borrow_mut();
138            unsafe { context.slice.set(data) };
139            context.progress = init_fill_count;
140
141            // Ensure those are enabled inside a critical section at the same time. Can lead to
142            // weird glitches otherwise.
143            tx.enable_interrupts(
144                data.len() > FIFO_DEPTH,
145                #[cfg(feature = "vor4x")]
146                true,
147            );
148            tx.enable();
149        });
150        Self { id: tx.id }
151    }
152}
153
154impl Future for TxFuture {
155    type Output = Result<usize, TxOverrunError>;
156
157    fn poll(
158        self: core::pin::Pin<&mut Self>,
159        cx: &mut core::task::Context<'_>,
160    ) -> core::task::Poll<Self::Output> {
161        UART_TX_WAKERS[self.id as usize].register(cx.waker());
162        if TX_DONE[self.id as usize].swap(false, core::sync::atomic::Ordering::Relaxed) {
163            let progress = critical_section::with(|cs| {
164                TX_CONTEXTS[self.id as usize].borrow(cs).borrow().progress
165            });
166            return core::task::Poll::Ready(Ok(progress));
167        }
168        core::task::Poll::Pending
169    }
170}
171
172impl Drop for TxFuture {
173    fn drop(&mut self) {
174        let mut reg_block = unsafe { self.id.steal_regs() };
175        if !TX_DONE[self.id as usize].load(core::sync::atomic::Ordering::Relaxed) {
176            disable_tx_interrupts(&mut reg_block);
177            disable_tx(&mut reg_block);
178        }
179    }
180}
181
182pub struct TxAsync(Tx);
183
184impl TxAsync {
185    pub fn new(tx: Tx) -> Self {
186        Self(tx)
187    }
188
189    #[inline]
190    pub fn inner(&mut self) -> &mut Tx {
191        &mut self.0
192    }
193
194    /// Write a buffer asynchronously.
195    ///
196    /// This implementation is not side effect free, and a started future might have already
197    /// written part of the passed buffer.
198    pub async fn write(&mut self, buf: &[u8]) -> Result<usize, TxOverrunError> {
199        let fut = unsafe { TxFuture::new(&mut self.0, buf) };
200        fut.await
201    }
202
203    /// Write an entire buffer into this writer.
204    ///
205    /// This function calls `write()` in a loop until exactly `buf.len()` bytes have
206    /// been written, waiting if needed.
207    ///
208    /// This function is not side-effect-free on cancel (AKA "cancel-safe"), i.e. if you cancel (drop) a returned
209    /// future that hasn't completed yet, some bytes might have already been written.
210    pub async fn write_all(&mut self, buf: &[u8]) -> Result<(), TxOverrunError> {
211        let fut = <Self as embedded_io_async::Write>::write_all(self, buf);
212        fut.await
213    }
214
215    pub async fn flush(&mut self) -> Result<(), TxOverrunError> {
216        while !tx_is_drained(&self.0) {}
217        Ok(())
218    }
219
220    pub fn release(self) -> Tx {
221        self.0
222    }
223}
224
225#[derive(Debug, thiserror::Error)]
226#[cfg_attr(feature = "defmt", derive(defmt::Format))]
227#[error("TX overrun error")]
228pub struct TxOverrunError;
229
230impl embedded_io_async::Error for TxOverrunError {
231    fn kind(&self) -> embedded_io_async::ErrorKind {
232        embedded_io_async::ErrorKind::Other
233    }
234}
235
236impl embedded_io::ErrorType for TxAsync {
237    type Error = TxOverrunError;
238}
239
240impl Write for TxAsync {
241    /// Write a buffer asynchronously.
242    ///
243    /// This implementation is not side effect free, and a started future might have already
244    /// written part of the passed buffer.
245    async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
246        self.write(buf).await
247    }
248
249    async fn flush(&mut self) -> Result<(), Self::Error> {
250        self.flush().await
251    }
252}