axi-uart16550 0.3.1

AXI UART16550 IP core driver
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
//! # AMD AXI UART16550 driver
//!
//! 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)
//! IP core.
//!
//! # Features
//!
//! If asynchronous TX operations are used, the number of wakers  which defaults to 1 waker can
//! also be configured. The [tx_async] module provides more details on the meaning of this number.
//!
//! - `portable-atomic` enables the use of the [`portable-atomic`](https://docs.rs/portable-atomic/latest/portable_atomic/)
//!   crate for atomic operations. This is useful for platforms that do not support the standard library's atomic types.
//! - `defmt` implements `defmt::Format` for this crate's register and error types.
//! - `1-waker` which is also a `default` feature
//! - `2-wakers`
//! - `4-wakers`
//! - `8-wakers`
//! - `16-wakers`
//! - `32-wakers`
#![no_std]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![deny(missing_docs)]

use core::convert::Infallible;

use regs::fields::{FifoControl, LineControl};
pub use regs::fields::{
    InterruptEnable, InterruptId2, InterruptIdentification, LineStatus, RxFifoTrigger, StopBits,
    WordLen,
};
pub mod regs;

pub mod tx;
pub use tx::*;

pub mod tx_async;
pub use tx_async::*;

pub mod rx;
pub use rx::*;

/// Maximum FIFO depth of the AXI UART16550.
pub const FIFO_DEPTH: usize = 16;

/// Default RX FIFO trigger level.
pub const DEFAULT_RX_TRIGGER_LEVEL: RxFifoTrigger = RxFifoTrigger::EightBytes;

/// Clock configuration structure.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct ClockConfig {
    /// Divisor value.
    pub div: u16,
}

/// Divisor is zero error.
#[derive(Debug, thiserror::Error, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[error("divisor is zero")]
pub struct DivisorZeroError;

/// Calculate the error rate of the baudrate with the given clock frequency, baudrate and
/// divisor as a floating point value between 0.0 and 1.0.
#[inline]
pub fn calculate_error_rate_from_div(
    clk_in: fugit::HertzU32,
    baudrate: u32,
    div: u16,
) -> Result<f32, DivisorZeroError> {
    if baudrate == 0 || div == 0 {
        return Err(DivisorZeroError);
    }
    let actual = (clk_in.to_raw() as f32) / (16.0 * div as f32);
    Ok(libm::fabsf(actual - baudrate as f32) / baudrate as f32)
}

/// If this error occurs, the calculated baudrate divisor is too large, either because the
/// used clock is too large, or the baudrate is too slow for the used clock frequency.
#[derive(Debug, thiserror::Error, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[error("divisor too large")]
pub enum ClockConfigError {
    /// Divisor too large error.
    DivisorTooLargeError(u32),
    /// Divisor is zero error.
    DivisorZero(#[from] DivisorZeroError),
}

impl ClockConfig {
    /// New clock config with the given divisor.
    pub fn new(div: u16) -> Self {
        Self { div }
    }

    /// MSB part of the divisor.
    #[inline(always)]
    pub fn div_msb(&self) -> u8 {
        (self.div >> 8) as u8
    }

    /// LSB part of the divisor.
    #[inline(always)]
    pub fn div_lsb(&self) -> u8 {
        self.div as u8
    }

    /// This function calculates the required divisor values for a given input clock and baudrate
    /// as well as an baud error rate.
    #[inline]
    pub fn new_autocalc_with_error(
        clk_in: fugit::HertzU32,
        baudrate: u32,
    ) -> Result<(Self, f32), ClockConfigError> {
        let cfg = Self::new_autocalc(clk_in, baudrate)?;
        Ok((cfg, cfg.calculate_error_rate(clk_in, baudrate)?))
    }

    /// This function calculates the required divisor values for a given input clock and baudrate.
    ///
    /// The function will not calculate the error rate. You can use [Self::calculate_error_rate]
    /// to check the error rate, or use the [Self::new_autocalc_with_error] function to get both
    /// the clock config and its baud error.
    #[inline]
    pub fn new_autocalc(clk_in: fugit::HertzU32, baudrate: u32) -> Result<Self, ClockConfigError> {
        let div = Self::calc_div_with_integer_div(clk_in, baudrate)?;
        if div > u16::MAX as u32 {
            return Err(ClockConfigError::DivisorTooLargeError(div));
        }
        Ok(Self { div: div as u16 })
    }

    /// Calculate the error rate of the baudrate with the given clock frequency, baudrate and the
    /// current clock config as a floating point value between 0.0 and 1.0.
    #[inline]
    pub fn calculate_error_rate(
        &self,
        clk_in: fugit::HertzU32,
        baudrate: u32,
    ) -> Result<f32, DivisorZeroError> {
        calculate_error_rate_from_div(clk_in, baudrate, self.div)
    }

    /// Calculate the divisor from an input clock for a give target baudrate.
    #[inline(always)]
    pub const fn calc_div_with_integer_div(
        clk_in: fugit::HertzU32,
        baudrate: u32,
    ) -> Result<u32, DivisorZeroError> {
        if baudrate == 0 {
            return Err(DivisorZeroError);
        }
        // Rounding integer division, by adding half the divisor to the dividend.
        Ok((clk_in.to_raw() + (8 * baudrate)) / (16 * baudrate))
    }
}

/// Parity configuration.
#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Parity {
    /// No parity (default).
    #[default]
    None,
    /// Odd parity.
    Odd,
    /// Even parity.
    Even,
}

/// AXI UART16550 peripheral driver.
pub struct AxiUart16550 {
    rx: Rx,
    tx: Tx,
    config: UartConfig,
}

/// UART configuration structure.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct UartConfig {
    clk: ClockConfig,
    word_len: WordLen,
    parity: Parity,
    stop_bits: StopBits,
}

impl UartConfig {
    /// New with the given clock configuration.
    pub const fn new_with_clk_config(clk: ClockConfig) -> Self {
        Self {
            clk,
            word_len: WordLen::Eight,
            parity: Parity::None,
            stop_bits: StopBits::One,
        }
    }

    /// New with all parameters.
    pub const fn new(
        clk: ClockConfig,
        word_len: WordLen,
        parity: Parity,
        stop_bits: StopBits,
    ) -> Self {
        Self {
            clk,
            word_len,
            parity,
            stop_bits,
        }
    }
}

impl AxiUart16550 {
    /// Create a new AXI UART16550 peripheral driver.
    ///
    /// # Safety
    ///
    /// - The `base_addr` must be a valid memory-mapped register address of an AXI UART 16550
    ///   peripheral.
    /// - Dereferencing an invalid or misaligned address results in **undefined behavior**.
    /// - The caller must ensure that no other code concurrently modifies the same peripheral registers
    ///   in an unsynchronized manner to prevent data races.
    /// - This function does not enforce uniqueness of driver instances. Creating multiple instances
    ///   with the same `base_addr` can lead to unintended behavior if not externally synchronized.
    /// - The driver performs **volatile** reads and writes to the provided address.
    pub unsafe fn new(base_addr: u32, config: UartConfig) -> Self {
        let mut regs = unsafe { regs::Registers::new_mmio_at(base_addr as usize) };
        // This unlocks the divisor config registers.
        regs.write_lcr(LineControl::new_for_divisor_access());
        regs.write_fifo_or_dll(config.clk.div_lsb() as u32);
        regs.write_ier_or_dlm(config.clk.div_msb() as u32);
        // Configure all other settings and reset the div acess latch. This is important
        // for accessing IER and the FIFO control register again.
        regs.write_lcr(
            LineControl::builder()
                .with_div_access_latch(false)
                .with_set_break(false)
                .with_stick_parity(false)
                .with_even_parity(config.parity == Parity::Even)
                .with_parity_enable(config.parity != Parity::None)
                .with_stop_bits(config.stop_bits)
                .with_word_len(config.word_len)
                .build(),
        );
        // Disable all interrupts.
        regs.write_ier_or_dlm(InterruptEnable::new_with_raw_value(0x0).raw_value());
        // Enable FIFO, configure 8 bytes FIFO trigger by default.
        regs.write_iir_or_fcr(
            FifoControl::builder()
                .with_rx_fifo_trigger(DEFAULT_RX_TRIGGER_LEVEL)
                .with_dma_mode_sel(false)
                .with_reset_tx_fifo(true)
                .with_reset_rx_fifo(true)
                .with_fifo_enable(true)
                .build()
                .raw_value(),
        );
        Self {
            rx: Rx::new(unsafe { regs.clone() }),
            tx: Tx::new(regs),
            config,
        }
    }

    /// Raw register access.
    #[inline(always)]
    pub const fn regs(&mut self) -> &mut regs::MmioRegisters<'static> {
        &mut self.rx.regs
    }

    /// UART configuration.
    #[inline(always)]
    pub const fn config(&mut self) -> &UartConfig {
        &self.config
    }

    /// Write into the UART Lite.
    ///
    /// Returns [nb::Error::WouldBlock] if the TX FIFO is full.
    #[inline]
    pub fn write_fifo(&mut self, data: u8) -> nb::Result<(), Infallible> {
        self.tx.write_fifo(data)
    }

    /// Transmitter Holding Register empty status.
    #[inline(always)]
    pub fn thr_empty(&self) -> bool {
        self.tx.thr_empty()
    }

    /// Transmitter empty status.
    #[inline(always)]
    pub fn tx_empty(&self) -> bool {
        self.tx.tx_empty()
    }

    /// Receiver has data.
    #[inline(always)]
    pub fn rx_has_data(&self) -> bool {
        self.rx.has_data()
    }

    /// Write into the FIFO without checking the FIFO fill status.
    ///
    /// This can be useful to completely fill the FIFO if it is known to be empty.
    #[inline(always)]
    pub fn write_fifo_unchecked(&mut self, data: u8) {
        self.tx.write_fifo_unchecked(data);
    }

    /// Read the RX FIFO.
    ///
    /// This functions offers a [nb::Result] based API and returns [nb::Error::WouldBlock] if there
    /// is nothing to read.
    #[inline]
    pub fn read_fifo(&mut self) -> nb::Result<u8, Infallible> {
        self.rx.read_fifo()
    }

    /// Read from the FIFO without checking the FIFO fill status.
    #[inline(always)]
    pub fn read_fifo_unchecked(&mut self) -> u8 {
        self.rx.read_fifo_unchecked()
    }

    /// Enable interrupts according to the given interrupt enable configuration.
    #[inline(always)]
    pub fn enable_interrupts(&mut self, ier: InterruptEnable) {
        self.regs().write_ier_or_dlm(ier.raw_value());
    }

    /// Split into TX and RX halves.
    pub fn split(self) -> (Tx, Rx) {
        (self.tx, self.rx)
    }
}

impl embedded_hal_nb::serial::ErrorType for AxiUart16550 {
    type Error = Infallible;
}

impl embedded_hal_nb::serial::Write for AxiUart16550 {
    #[inline]
    fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
        self.tx.write(word)
    }

    #[inline]
    fn flush(&mut self) -> nb::Result<(), Self::Error> {
        self.tx.flush()
    }
}

impl embedded_hal_nb::serial::Read for AxiUart16550 {
    #[inline]
    fn read(&mut self) -> nb::Result<u8, Self::Error> {
        self.rx.read()
    }
}

impl embedded_io::ErrorType for AxiUart16550 {
    type Error = Infallible;
}

impl embedded_io::Read for AxiUart16550 {
    fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
        self.rx.read(buf)
    }
}

impl embedded_io::Write for AxiUart16550 {
    fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
        self.tx.write(buf)
    }

    fn flush(&mut self) -> Result<(), Self::Error> {
        self.tx.flush()
    }
}

#[cfg(test)]
mod tests {
    use crate::ClockConfigError;

    //extern crate std;
    use super::{DivisorZeroError, calculate_error_rate_from_div};

    use super::ClockConfig;
    use approx::abs_diff_eq;
    use fugit::RateExtU32;

    #[test]
    fn test_clk_calc_example_0() {
        let clk_cfg = ClockConfig::new_autocalc(100.MHz(), 56000).unwrap();
        // For some reason, the Xilinx example rounds up here..
        assert_eq!(clk_cfg.div, 0x0070);
        assert_eq!(clk_cfg.div_msb(), 0x00);
        assert_eq!(clk_cfg.div_lsb(), 0x70);
        let error = clk_cfg.calculate_error_rate(100.MHz(), 56000).unwrap();
        assert!(abs_diff_eq!(error, 0.0035, epsilon = 0.001));
        let (clk_cfg_checked, error_checked) =
            ClockConfig::new_autocalc_with_error(100.MHz(), 56000).unwrap();
        assert_eq!(clk_cfg, clk_cfg_checked);
        assert!(abs_diff_eq!(error, error_checked, epsilon = 0.001));
        let error_calc = calculate_error_rate_from_div(100.MHz(), 56000, clk_cfg.div).unwrap();
        assert!(abs_diff_eq!(error, error_calc, epsilon = 0.001));
    }

    #[test]
    fn test_clk_calc_example_1() {
        let clk_cfg = ClockConfig::new_autocalc(1843200.Hz(), 56000).unwrap();
        assert_eq!(clk_cfg.div, 0x0002);
        assert_eq!(clk_cfg.div_msb(), 0x00);
        assert_eq!(clk_cfg.div_lsb(), 0x02);
    }

    #[test]
    fn test_invalid_baud() {
        let clk_cfg = ClockConfig::new_autocalc_with_error(100.MHz(), 0);
        assert_eq!(
            clk_cfg,
            Err(ClockConfigError::DivisorZero(DivisorZeroError))
        );
    }

    #[test]
    fn test_invalid_div() {
        let error = calculate_error_rate_from_div(100.MHz(), 115200, 0);
        assert_eq!(error.unwrap_err(), DivisorZeroError);
        let error = calculate_error_rate_from_div(100.MHz(), 0, 0);
        assert_eq!(error.unwrap_err(), DivisorZeroError);
        let error = calculate_error_rate_from_div(100.MHz(), 0, 16);
        assert_eq!(error.unwrap_err(), DivisorZeroError);
    }
}