Skip to main content

axi_uartlite/
lib.rs

1//! # AXI UART Lite v2.0 driver
2//!
3//! This is a native Rust driver for the
4//! [AMD AXI UART Lite v2.0 IP core](https://www.amd.com/en/products/adaptive-socs-and-fpgas/intellectual-property/axi_uartlite.html).
5//!
6//! # Special not on Zynq7000 usage
7//!
8//! When using this on the Zynq7000 platform, you might have to re-configure the interrupt sensitivity
9//! in the GIC. An example can be found [here](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/commit/1ab64050974242e43a7c5a2df5fb09256bc06274/firmware/examples/zedboard/src/bin/uart-non-blocking.rs#L189).
10//!
11//! # Features
12//!
13//! If asynchronous TX operations are used, the number of wakers  which defaults to 1 waker can
14//! also be configured. The [tx_async] module provides more details on the meaning of this number.
15//!
16//! - `portable-atomic` enables the use of the [`portable-atomic`](https://docs.rs/portable-atomic/latest/portable_atomic/)
17//!   crate for atomic operations. This is useful for platforms that do not support the standard library's atomic types.
18//! - `defmt` implements `defmt::Format` for this crate's register and error types.
19//! - `1-waker` which is also a `default` feature
20//! - `2-wakers`
21//! - `4-wakers`
22//! - `8-wakers`
23//! - `16-wakers`
24//! - `32-wakers`
25#![no_std]
26#![cfg_attr(docsrs, feature(doc_cfg))]
27#![deny(missing_docs)]
28
29use core::convert::Infallible;
30use regs::fields::Control;
31pub use regs::fields::Status;
32pub mod regs;
33
34pub mod tx;
35pub use tx::*;
36
37pub mod rx;
38pub use rx::*;
39
40pub mod tx_async;
41pub use tx_async::*;
42
43/// Maximum FIFO depth of the AXI UART Lite.
44pub const FIFO_DEPTH: usize = 16;
45
46/// RX error structure.
47#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
48#[cfg_attr(feature = "defmt", derive(defmt::Format))]
49pub struct RxErrorsCounted {
50    parity: u8,
51    frame: u8,
52    overrun: u8,
53}
54
55impl RxErrorsCounted {
56    /// Create a new empty RX error counter.
57    pub const fn new() -> Self {
58        Self {
59            parity: 0,
60            frame: 0,
61            overrun: 0,
62        }
63    }
64
65    /// Parity error count.
66    pub const fn parity(&self) -> u8 {
67        self.parity
68    }
69
70    /// Frame error count.
71    pub const fn frame(&self) -> u8 {
72        self.frame
73    }
74
75    /// Overrun error count.
76    pub const fn overrun(&self) -> u8 {
77        self.overrun
78    }
79
80    /// Some error has occurred.
81    pub fn has_errors(&self) -> bool {
82        self.parity > 0 || self.frame > 0 || self.overrun > 0
83    }
84}
85
86/// AXI UART Lite peripheral driver.
87pub struct AxiUartlite {
88    rx: Rx,
89    tx: Tx,
90    errors: RxErrorsCounted,
91}
92
93impl AxiUartlite {
94    /// Create a new AXI UART Lite peripheral driver.
95    ///
96    /// # Safety
97    ///
98    /// - The `base_addr` must be a valid memory-mapped register address of an AXI UART Lite peripheral.
99    /// - Dereferencing an invalid or misaligned address results in **undefined behavior**.
100    /// - The caller must ensure that no other code concurrently modifies the same peripheral registers
101    ///   in an unsynchronized manner to prevent data races.
102    /// - This function does not enforce uniqueness of driver instances. Creating multiple instances
103    ///   with the same `base_addr` can lead to unintended behavior if not externally synchronized.
104    /// - The driver performs **volatile** reads and writes to the provided address.
105    pub const unsafe fn new(base_addr: u32) -> Self {
106        let regs = unsafe { regs::Registers::new_mmio_at(base_addr as usize) };
107        Self {
108            rx: Rx {
109                regs: unsafe { regs.clone() },
110                errors: None,
111            },
112            tx: Tx { regs, errors: None },
113            errors: RxErrorsCounted::new(),
114        }
115    }
116
117    /// Direct register access.
118    #[inline(always)]
119    pub const fn regs(&mut self) -> &mut regs::MmioRegisters<'static> {
120        &mut self.tx.regs
121    }
122
123    /// Write into the UART Lite.
124    ///
125    /// Returns [nb::Error::WouldBlock] if the TX FIFO is full.
126    #[inline]
127    pub fn write_fifo(&mut self, data: u8) -> nb::Result<(), Infallible> {
128        self.tx.write_fifo(data)?;
129        if let Some(errors) = self.tx.errors {
130            self.handle_status_reg_errors(errors);
131        }
132        Ok(())
133    }
134
135    /// Write into the FIFO without checking the FIFO fill status.
136    ///
137    /// This can be useful to completely fill the FIFO if it is known to be empty.
138    #[inline(always)]
139    pub fn write_fifo_unchecked(&mut self, data: u8) {
140        self.tx.write_fifo_unchecked(data);
141    }
142
143    /// Read from the UART Lite.
144    ///
145    /// Offers a
146    #[inline]
147    pub fn read_fifo(&mut self) -> nb::Result<u8, Infallible> {
148        let val = self.rx.read_fifo()?;
149        if let Some(errors) = self.rx.errors {
150            self.handle_status_reg_errors(errors);
151        }
152        Ok(val)
153    }
154
155    /// Read from the FIFO without checking the FIFO fill status.
156    #[inline(always)]
157    pub fn read_fifo_unchecked(&mut self) -> u8 {
158        self.rx.read_fifo_unchecked()
159    }
160
161    /// Is the TX FIFO empty?
162    #[inline(always)]
163    pub fn tx_fifo_empty(&self) -> bool {
164        self.tx.fifo_empty()
165    }
166
167    /// TX FIFO full status.
168    #[inline(always)]
169    pub fn tx_fifo_full(&self) -> bool {
170        self.tx.fifo_full()
171    }
172
173    /// RX FIFO has data.
174    #[inline(always)]
175    pub fn rx_has_data(&self) -> bool {
176        self.rx.has_data()
177    }
178
179    /// Read the error counters and also resets them.
180    pub fn read_and_clear_errors(&mut self) -> RxErrorsCounted {
181        let errors = self.errors;
182        self.errors = RxErrorsCounted::new();
183        errors
184    }
185
186    #[inline(always)]
187    fn handle_status_reg_errors(&mut self, errors: RxErrors) {
188        if errors.frame() {
189            self.errors.frame = self.errors.frame.saturating_add(1);
190        }
191        if errors.parity() {
192            self.errors.parity = self.errors.parity.saturating_add(1);
193        }
194        if errors.overrun() {
195            self.errors.overrun = self.errors.overrun.saturating_add(1);
196        }
197    }
198
199    /// Reset the RX FIFO.
200    #[inline]
201    pub fn reset_rx_fifo(&mut self) {
202        self.regs().write_ctrl_reg(
203            Control::builder()
204                .with_enable_interrupt(false)
205                .with_reset_rx_fifo(true)
206                .with_reset_tx_fifo(false)
207                .build(),
208        );
209    }
210
211    /// Reset the TX FIFO.
212    #[inline]
213    pub fn reset_tx_fifo(&mut self) {
214        self.regs().write_ctrl_reg(
215            Control::builder()
216                .with_enable_interrupt(false)
217                .with_reset_rx_fifo(false)
218                .with_reset_tx_fifo(true)
219                .build(),
220        );
221    }
222
223    /// Split the driver into [Tx] and [Rx] halves.
224    #[inline]
225    pub fn split(self) -> (Tx, Rx) {
226        (self.tx, self.rx)
227    }
228
229    /// Enable UART Lite interrupts.
230    #[inline]
231    pub fn enable_interrupt(&mut self) {
232        self.regs().write_ctrl_reg(
233            Control::builder()
234                .with_enable_interrupt(true)
235                .with_reset_rx_fifo(false)
236                .with_reset_tx_fifo(false)
237                .build(),
238        );
239    }
240
241    /// Disable UART Lite interrupts.
242    #[inline]
243    pub fn disable_interrupt(&mut self) {
244        self.regs().write_ctrl_reg(
245            Control::builder()
246                .with_enable_interrupt(false)
247                .with_reset_rx_fifo(false)
248                .with_reset_tx_fifo(false)
249                .build(),
250        );
251    }
252}
253
254impl embedded_hal_nb::serial::ErrorType for AxiUartlite {
255    type Error = Infallible;
256}
257
258impl embedded_hal_nb::serial::Write for AxiUartlite {
259    #[inline]
260    fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
261        self.tx.write(word)
262    }
263
264    #[inline]
265    fn flush(&mut self) -> nb::Result<(), Self::Error> {
266        self.tx.flush()
267    }
268}
269
270impl embedded_hal_nb::serial::Read for AxiUartlite {
271    #[inline]
272    fn read(&mut self) -> nb::Result<u8, Self::Error> {
273        self.rx.read()
274    }
275}
276
277impl embedded_io::ErrorType for AxiUartlite {
278    type Error = Infallible;
279}
280
281impl embedded_io::Read for AxiUartlite {
282    fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
283        self.rx.read(buf)
284    }
285}
286
287impl embedded_io::Write for AxiUartlite {
288    fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
289        self.tx.write(buf)
290    }
291
292    fn flush(&mut self) -> Result<(), Self::Error> {
293        self.tx.flush()
294    }
295}