Skip to main content

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