axi_uart16550/
lib.rs

1//! # AMD AXI UART16550 driver
2//!
3//! This is a native Rust driver for the [AMD AXI UART16550](https://www.amd.com/de/products/adaptive-socs-and-fpgas/intellectual-property/axi_uart16550.html)
4//! IP core.
5//!
6//! # Features
7//!
8//! If asynchronous TX operations are used, the number of wakers  which defaults to 1 waker can
9//! also be configured. The [tx_async] module provides more details on the meaning of this number.
10//!
11//! - `1-waker` which is also a `default` feature
12//! - `2-wakers`
13//! - `4-wakers`
14//! - `8-wakers`
15//! - `16-wakers`
16//! - `32-wakers`
17#![no_std]
18#![cfg_attr(docsrs, feature(doc_cfg))]
19#![deny(missing_docs)]
20
21use core::convert::Infallible;
22
23use registers::{FifoControl, InterruptEnable, LineControl, RxFifoTrigger, StopBits, WordLen};
24pub mod registers;
25
26pub mod tx;
27pub use tx::*;
28
29pub mod tx_async;
30pub use tx_async::*;
31
32pub mod rx;
33pub use rx::*;
34
35/// Maximum FIFO depth of the AXI UART16550.
36pub const FIFO_DEPTH: usize = 16;
37
38/// Default RX FIFO trigger level.
39pub const DEFAULT_RX_TRIGGER_LEVEL: RxFifoTrigger = RxFifoTrigger::EightBytes;
40
41/// Clock configuration structure.
42#[derive(Debug, PartialEq, Eq, Clone, Copy)]
43pub struct ClockConfig {
44    /// Divisor value.
45    pub div: u16,
46}
47
48/// Divisor is zero error.
49#[derive(Debug, thiserror::Error, PartialEq, Eq)]
50#[error("divisor is zero")]
51pub struct DivisorZeroError;
52
53/// Calculate the error rate of the baudrate with the given clock frequency, baudrate and
54/// divisor as a floating point value between 0.0 and 1.0.
55#[inline]
56pub fn calculate_error_rate_from_div(
57    clk_in: fugit::HertzU32,
58    baudrate: u32,
59    div: u16,
60) -> Result<f32, DivisorZeroError> {
61    if baudrate == 0 || div == 0 {
62        return Err(DivisorZeroError);
63    }
64    let actual = (clk_in.raw() as f32) / (16.0 * div as f32);
65    Ok(libm::fabsf(actual - baudrate as f32) / baudrate as f32)
66}
67
68/// If this error occurs, the calculated baudrate divisor is too large, either because the
69/// used clock is too large, or the baudrate is too slow for the used clock frequency.
70#[derive(Debug, thiserror::Error, PartialEq, Eq)]
71#[error("divisor too large")]
72pub enum ClockConfigError {
73    /// Divisor too large error.
74    DivisorTooLargeError(u32),
75    /// Divisor is zero error.
76    DivisorZero(#[from] DivisorZeroError),
77}
78
79impl ClockConfig {
80    /// New clock config with the given divisor.
81    pub fn new(div: u16) -> Self {
82        Self { div }
83    }
84
85    /// MSB part of the divisor.
86    #[inline(always)]
87    pub fn div_msb(&self) -> u8 {
88        (self.div >> 8) as u8
89    }
90
91    /// LSB part of the divisor.
92    #[inline(always)]
93    pub fn div_lsb(&self) -> u8 {
94        self.div as u8
95    }
96
97    /// This function calculates the required divisor values for a given input clock and baudrate
98    /// as well as an baud error rate.
99    #[inline]
100    pub fn new_autocalc_with_error(
101        clk_in: fugit::HertzU32,
102        baudrate: u32,
103    ) -> Result<(Self, f32), ClockConfigError> {
104        let cfg = Self::new_autocalc(clk_in, baudrate)?;
105        Ok((cfg, cfg.calculate_error_rate(clk_in, baudrate)?))
106    }
107
108    /// This function calculates the required divisor values for a given input clock and baudrate.
109    ///
110    /// The function will not calculate the error rate. You can use [Self::calculate_error_rate]
111    /// to check the error rate, or use the [Self::new_autocalc_with_error] function to get both
112    /// the clock config and its baud error.
113    #[inline]
114    pub fn new_autocalc(clk_in: fugit::HertzU32, baudrate: u32) -> Result<Self, ClockConfigError> {
115        let div = Self::calc_div_with_integer_div(clk_in, baudrate)?;
116        if div > u16::MAX as u32 {
117            return Err(ClockConfigError::DivisorTooLargeError(div));
118        }
119        Ok(Self { div: div as u16 })
120    }
121
122    /// Calculate the error rate of the baudrate with the given clock frequency, baudrate and the
123    /// current clock config as a floating point value between 0.0 and 1.0.
124    #[inline]
125    pub fn calculate_error_rate(
126        &self,
127        clk_in: fugit::HertzU32,
128        baudrate: u32,
129    ) -> Result<f32, DivisorZeroError> {
130        calculate_error_rate_from_div(clk_in, baudrate, self.div)
131    }
132
133    /// Calculate the divisor from an input clock for a give target baudrate.
134    #[inline(always)]
135    pub const fn calc_div_with_integer_div(
136        clk_in: fugit::HertzU32,
137        baudrate: u32,
138    ) -> Result<u32, DivisorZeroError> {
139        if baudrate == 0 {
140            return Err(DivisorZeroError);
141        }
142        // Rounding integer division, by adding half the divisor to the dividend.
143        Ok((clk_in.raw() + (8 * baudrate)) / (16 * baudrate))
144    }
145}
146
147/// Parity configuration.
148#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
149pub enum Parity {
150    /// No parity (default).
151    #[default]
152    None,
153    /// Odd parity.
154    Odd,
155    /// Even parity.
156    Even,
157}
158
159/// AXI UART16550 peripheral driver.
160pub struct AxiUart16550 {
161    rx: Rx,
162    tx: Tx,
163    config: UartConfig,
164}
165
166/// UART configuration structure.
167#[derive(Debug, PartialEq, Eq, Clone, Copy)]
168pub struct UartConfig {
169    clk: ClockConfig,
170    word_len: WordLen,
171    parity: Parity,
172    stop_bits: StopBits,
173}
174
175impl UartConfig {
176    /// New with the given clock configuration.
177    pub const fn new_with_clk_config(clk: ClockConfig) -> Self {
178        Self {
179            clk,
180            word_len: WordLen::Eight,
181            parity: Parity::None,
182            stop_bits: StopBits::One,
183        }
184    }
185
186    /// New with all parameters.
187    pub const fn new(
188        clk: ClockConfig,
189        word_len: WordLen,
190        parity: Parity,
191        stop_bits: StopBits,
192    ) -> Self {
193        Self {
194            clk,
195            word_len,
196            parity,
197            stop_bits,
198        }
199    }
200}
201
202impl AxiUart16550 {
203    /// Create a new AXI UART16550 peripheral driver.
204    ///
205    /// # Safety
206    ///
207    /// - The `base_addr` must be a valid memory-mapped register address of an AXI UART 16550
208    ///   peripheral.
209    /// - Dereferencing an invalid or misaligned address results in **undefined behavior**.
210    /// - The caller must ensure that no other code concurrently modifies the same peripheral registers
211    ///   in an unsynchronized manner to prevent data races.
212    /// - This function does not enforce uniqueness of driver instances. Creating multiple instances
213    ///   with the same `base_addr` can lead to unintended behavior if not externally synchronized.
214    /// - The driver performs **volatile** reads and writes to the provided address.
215    pub unsafe fn new(base_addr: u32, config: UartConfig) -> Self {
216        let mut regs = unsafe { registers::Registers::new_mmio_at(base_addr as usize) };
217        // This unlocks the divisor config registers.
218        regs.write_lcr(LineControl::new_for_divisor_access());
219        regs.write_fifo_or_dll(config.clk.div_lsb() as u32);
220        regs.write_ier_or_dlm(config.clk.div_msb() as u32);
221        // Configure all other settings and reset the div acess latch. This is important
222        // for accessing IER and the FIFO control register again.
223        regs.write_lcr(
224            LineControl::builder()
225                .with_div_access_latch(false)
226                .with_set_break(false)
227                .with_stick_parity(false)
228                .with_even_parity(config.parity == Parity::Even)
229                .with_parity_enable(config.parity != Parity::None)
230                .with_stop_bits(config.stop_bits)
231                .with_word_len(config.word_len)
232                .build(),
233        );
234        // Disable all interrupts.
235        regs.write_ier_or_dlm(InterruptEnable::new_with_raw_value(0x0).raw_value());
236        // Enable FIFO, configure 8 bytes FIFO trigger by default.
237        regs.write_iir_or_fcr(
238            FifoControl::builder()
239                .with_rx_fifo_trigger(DEFAULT_RX_TRIGGER_LEVEL)
240                .with_dma_mode_sel(false)
241                .with_reset_tx_fifo(true)
242                .with_reset_rx_fifo(true)
243                .with_fifo_enable(true)
244                .build()
245                .raw_value(),
246        );
247        Self {
248            rx: Rx::new(unsafe { regs.clone() }),
249            tx: Tx::new(regs),
250            config,
251        }
252    }
253
254    /// Raw register access.
255    #[inline(always)]
256    pub const fn regs(&mut self) -> &mut registers::MmioRegisters<'static> {
257        &mut self.rx.regs
258    }
259
260    /// UART configuration.
261    #[inline(always)]
262    pub const fn config(&mut self) -> &UartConfig {
263        &self.config
264    }
265
266    /// Write into the UART Lite.
267    ///
268    /// Returns [nb::Error::WouldBlock] if the TX FIFO is full.
269    #[inline]
270    pub fn write_fifo(&mut self, data: u8) -> nb::Result<(), Infallible> {
271        self.tx.write_fifo(data)
272    }
273
274    /// Transmitter Holding Register empty status.
275    #[inline(always)]
276    pub fn thr_empty(&self) -> bool {
277        self.tx.thr_empty()
278    }
279
280    /// Transmitter empty status.
281    #[inline(always)]
282    pub fn tx_empty(&self) -> bool {
283        self.tx.tx_empty()
284    }
285
286    /// Receiver has data.
287    #[inline(always)]
288    pub fn rx_has_data(&self) -> bool {
289        self.rx.has_data()
290    }
291
292    /// Write into the FIFO without checking the FIFO fill status.
293    ///
294    /// This can be useful to completely fill the FIFO if it is known to be empty.
295    #[inline(always)]
296    pub fn write_fifo_unchecked(&mut self, data: u8) {
297        self.tx.write_fifo_unchecked(data);
298    }
299
300    /// Read the RX FIFO.
301    ///
302    /// This functions offers a [nb::Result] based API and returns [nb::Error::WouldBlock] if there
303    /// is nothing to read.
304    #[inline]
305    pub fn read_fifo(&mut self) -> nb::Result<u8, Infallible> {
306        self.rx.read_fifo()
307    }
308
309    /// Read from the FIFO without checking the FIFO fill status.
310    #[inline(always)]
311    pub fn read_fifo_unchecked(&mut self) -> u8 {
312        self.rx.read_fifo_unchecked()
313    }
314
315    /// Enable interrupts according to the given interrupt enable configuration.
316    #[inline(always)]
317    pub fn enable_interrupts(&mut self, ier: InterruptEnable) {
318        self.regs().write_ier_or_dlm(ier.raw_value());
319    }
320
321    /// Split into TX and RX halves.
322    pub fn split(self) -> (Tx, Rx) {
323        (self.tx, self.rx)
324    }
325}
326
327impl embedded_hal_nb::serial::ErrorType for AxiUart16550 {
328    type Error = Infallible;
329}
330
331impl embedded_hal_nb::serial::Write for AxiUart16550 {
332    #[inline]
333    fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
334        self.tx.write(word)
335    }
336
337    #[inline]
338    fn flush(&mut self) -> nb::Result<(), Self::Error> {
339        self.tx.flush()
340    }
341}
342
343impl embedded_hal_nb::serial::Read for AxiUart16550 {
344    #[inline]
345    fn read(&mut self) -> nb::Result<u8, Self::Error> {
346        self.rx.read()
347    }
348}
349
350impl embedded_io::ErrorType for AxiUart16550 {
351    type Error = Infallible;
352}
353
354impl embedded_io::Read for AxiUart16550 {
355    fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
356        self.rx.read(buf)
357    }
358}
359
360impl embedded_io::Write for AxiUart16550 {
361    fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
362        self.tx.write(buf)
363    }
364
365    fn flush(&mut self) -> Result<(), Self::Error> {
366        self.tx.flush()
367    }
368}
369
370#[cfg(test)]
371mod tests {
372    use crate::ClockConfigError;
373
374    //extern crate std;
375    use super::{DivisorZeroError, calculate_error_rate_from_div};
376
377    use super::ClockConfig;
378    use approx::abs_diff_eq;
379    use fugit::RateExtU32;
380
381    #[test]
382    fn test_clk_calc_example_0() {
383        let clk_cfg = ClockConfig::new_autocalc(100.MHz(), 56000).unwrap();
384        // For some reason, the Xilinx example rounds up here..
385        assert_eq!(clk_cfg.div, 0x0070);
386        assert_eq!(clk_cfg.div_msb(), 0x00);
387        assert_eq!(clk_cfg.div_lsb(), 0x70);
388        let error = clk_cfg.calculate_error_rate(100.MHz(), 56000).unwrap();
389        assert!(abs_diff_eq!(error, 0.0035, epsilon = 0.001));
390        let (clk_cfg_checked, error_checked) =
391            ClockConfig::new_autocalc_with_error(100.MHz(), 56000).unwrap();
392        assert_eq!(clk_cfg, clk_cfg_checked);
393        assert!(abs_diff_eq!(error, error_checked, epsilon = 0.001));
394        let error_calc = calculate_error_rate_from_div(100.MHz(), 56000, clk_cfg.div).unwrap();
395        assert!(abs_diff_eq!(error, error_calc, epsilon = 0.001));
396    }
397
398    #[test]
399    fn test_clk_calc_example_1() {
400        let clk_cfg = ClockConfig::new_autocalc(1843200.Hz(), 56000).unwrap();
401        assert_eq!(clk_cfg.div, 0x0002);
402        assert_eq!(clk_cfg.div_msb(), 0x00);
403        assert_eq!(clk_cfg.div_lsb(), 0x02);
404    }
405
406    #[test]
407    fn test_invalid_baud() {
408        let clk_cfg = ClockConfig::new_autocalc_with_error(100.MHz(), 0);
409        assert_eq!(
410            clk_cfg,
411            Err(ClockConfigError::DivisorZero(DivisorZeroError))
412        );
413    }
414
415    #[test]
416    fn test_invalid_div() {
417        let error = calculate_error_rate_from_div(100.MHz(), 115200, 0);
418        assert_eq!(error.unwrap_err(), DivisorZeroError);
419        let error = calculate_error_rate_from_div(100.MHz(), 0, 0);
420        assert_eq!(error.unwrap_err(), DivisorZeroError);
421        let error = calculate_error_rate_from_div(100.MHz(), 0, 16);
422        assert_eq!(error.unwrap_err(), DivisorZeroError);
423    }
424}