Skip to main content

axi_uartlite/
tx.rs

1//! # Transmitter (TX) support module
2use core::convert::Infallible;
3
4use crate::{
5    InvalidWakerIndex, RxErrors, TxAsync, handle_status_reg_errors,
6    regs::{
7        self,
8        fields::{Control, TxFifo},
9    },
10};
11
12/// AXI UARTLITE TX driver.
13///
14/// Can be created by [super::AxiUartlite::split]ting a regular AXI UARTLITE structure or
15/// by [Self::steal]ing it unsafely.
16pub struct Tx {
17    pub(crate) regs: regs::MmioRegisters<'static>,
18    pub(crate) errors: Option<RxErrors>,
19}
20
21impl Tx {
22    /// Steal the TX part of the UART Lite.
23    ///
24    /// You should only use this if you can not use the regular [super::AxiUartlite] constructor
25    /// and the [super::AxiUartlite::split] method.
26    ///
27    /// This function assumes that the setup of the UART was already done.
28    /// It can be used to create a TX handle inside an interrupt handler without having to use
29    /// a [critical_section::Mutex] if the user can guarantee that the TX handle will only be
30    /// used by the interrupt handler, or only interrupt specific API will be used.
31    ///
32    /// # Safety
33    ///
34    /// The same safey rules specified in [super::AxiUartlite] apply.
35    pub unsafe fn steal(base_addr: usize) -> Self {
36        let regs = unsafe { regs::Registers::new_mmio_at(base_addr) };
37        Self { regs, errors: None }
38    }
39
40    /// Write into the UART Lite.
41    ///
42    /// Returns [nb::Error::WouldBlock] if the TX FIFO is full.
43    #[inline]
44    pub fn write_fifo(&mut self, data: u8) -> nb::Result<(), Infallible> {
45        let status_reg = self.regs.read_stat_reg();
46        if status_reg.tx_fifo_full() {
47            return Err(nb::Error::WouldBlock);
48        }
49        self.write_fifo_unchecked(data);
50        if let Some(errors) = handle_status_reg_errors(&status_reg) {
51            self.errors = Some(errors);
52        }
53        Ok(())
54    }
55
56    /// Reset the TX FIFO.
57    #[inline]
58    pub fn reset_fifo(&mut self) {
59        let status = self.regs.read_stat_reg();
60        self.regs.write_ctrl_reg(
61            Control::builder()
62                .with_enable_interrupt(status.intr_enabled())
63                .with_reset_rx_fifo(false)
64                .with_reset_tx_fifo(true)
65                .build(),
66        );
67    }
68
69    /// Write into the FIFO without checking the FIFO fill status.
70    ///
71    /// This can be useful to completely fill the FIFO if it is known to be empty.
72    #[inline(always)]
73    pub fn write_fifo_unchecked(&mut self, data: u8) {
74        self.regs
75            .write_tx_fifo(TxFifo::new_with_raw_value(data as u32));
76    }
77
78    /// Is the TX FIFO empty?
79    #[inline(always)]
80    pub fn fifo_empty(&self) -> bool {
81        self.regs.read_stat_reg().tx_fifo_empty()
82    }
83
84    /// Is the TX FIFO full?
85    #[inline(always)]
86    pub fn fifo_full(&self) -> bool {
87        self.regs.read_stat_reg().tx_fifo_full()
88    }
89
90    /// Fills the FIFO with user provided data until the user data
91    /// is consumed or the FIFO is full.
92    ///
93    /// Returns the amount of written data, which might be smaller than the buffer size.
94    pub fn fill_fifo(&mut self, buf: &[u8]) -> usize {
95        let mut written = 0;
96        while written < buf.len() {
97            match self.write_fifo(buf[written]) {
98                Ok(_) => written += 1,
99                Err(nb::Error::WouldBlock) => break,
100            }
101        }
102        written
103    }
104
105    /// Read and clear the last recorded RX errors.
106    pub fn read_and_clear_last_error(&mut self) -> Option<RxErrors> {
107        let errors = self.errors?;
108        self.errors = None;
109        Some(errors)
110    }
111
112    /// Convert this TX driver into an asynchronous TX driver.
113    ///
114    /// See [TxAsync::new] for more information about the `waker_idx` argument.
115    pub fn into_async(self, waker_idx: usize) -> Result<TxAsync, InvalidWakerIndex> {
116        TxAsync::new(self, waker_idx)
117    }
118}
119
120impl embedded_hal_nb::serial::ErrorType for Tx {
121    type Error = Infallible;
122}
123
124impl embedded_hal_nb::serial::Write for Tx {
125    fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
126        self.write_fifo(word)
127    }
128
129    fn flush(&mut self) -> nb::Result<(), Self::Error> {
130        while !self.fifo_empty() {}
131        Ok(())
132    }
133}
134
135impl embedded_io::ErrorType for Tx {
136    type Error = Infallible;
137}
138
139impl embedded_io::Write for Tx {
140    fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
141        if buf.is_empty() {
142            return Ok(0);
143        }
144        while self.fifo_full() {}
145        let mut written = 0;
146        for &byte in buf.iter() {
147            match self.write_fifo(byte) {
148                Ok(_) => written += 1,
149                Err(nb::Error::WouldBlock) => break,
150            }
151        }
152        Ok(written)
153    }
154
155    fn flush(&mut self) -> Result<(), Self::Error> {
156        while !self.fifo_empty() {}
157        Ok(())
158    }
159}