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