zynq7000_hal/uart/
mod.rs

1//! # UART module
2//!
3//! Support for the processing system UARTs.
4//!
5//! ## Examples
6//!
7//! - [Logger through UART](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq/examples/simple/src/bin/logger.rs)
8//! - [Zedboard Blocking UART](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq/examples/zedboard/src/bin/uart-blocking.rs)
9//! - [Zedboard Non-Blocking UART](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq/examples/zedboard/src/bin/uart-non-blocking.rs)
10use core::convert::Infallible;
11
12use arbitrary_int::u3;
13use libm::round;
14use zynq7000::{
15    slcr::reset::DualRefAndClockReset,
16    uart::{
17        BaudRateDivisor, Baudgen, ChMode, ClockSelect, FifoTrigger, InterruptControl, MmioUart,
18        Mode, UART_0_BASE, UART_1_BASE,
19    },
20};
21
22use crate::{
23    enable_amba_peripheral_clock,
24    gpio::{
25        IoPeriphPin,
26        mio::{
27            Mio8, Mio9, Mio10, Mio11, Mio12, Mio13, Mio14, Mio15, Mio28, Mio29, Mio30, Mio31,
28            Mio32, Mio33, Mio34, Mio35, Mio36, Mio37, Mio38, Mio39, Mio48, Mio49, Mio52, Mio53,
29            MioPin, MuxConfig, Pin,
30        },
31    },
32    slcr::Slcr,
33};
34
35#[cfg(not(feature = "7z010-7z007s-clg225"))]
36use crate::gpio::mio::{
37    Mio16, Mio17, Mio18, Mio19, Mio20, Mio21, Mio22, Mio23, Mio24, Mio25, Mio26, Mio27, Mio40,
38    Mio41, Mio42, Mio43, Mio44, Mio45, Mio46, Mio47, Mio50, Mio51,
39};
40
41use super::{clocks::IoClocks, time::Hertz};
42
43pub mod tx;
44pub use tx::*;
45
46pub mod tx_async;
47pub use tx_async::*;
48
49pub mod rx;
50pub use rx::*;
51
52pub const FIFO_DEPTH: usize = 64;
53pub const DEFAULT_RX_TRIGGER_LEVEL: u8 = 32;
54pub const UART_MUX_CONF: MuxConfig = MuxConfig::new_with_l3(u3::new(0b111));
55
56#[derive(Debug, Copy, Clone, PartialEq, Eq)]
57pub enum UartId {
58    Uart0 = 0,
59    Uart1 = 1,
60}
61
62pub trait PsUart {
63    fn reg_block(&self) -> MmioUart<'static>;
64    fn uart_id(&self) -> Option<UartId>;
65}
66
67impl PsUart for MmioUart<'static> {
68    #[inline]
69    fn reg_block(&self) -> MmioUart<'static> {
70        unsafe { self.clone() }
71    }
72
73    fn uart_id(&self) -> Option<UartId> {
74        let base_addr = unsafe { self.ptr() } as usize;
75        if base_addr == UART_0_BASE {
76            return Some(UartId::Uart0);
77        } else if base_addr == UART_1_BASE {
78            return Some(UartId::Uart1);
79        }
80        None
81    }
82}
83
84impl UartId {
85    /// Unsafely steal a peripheral MMIO block for the given UART.
86    ///
87    /// # Safety
88    ///
89    /// Circumvents ownership and safety guarantees by the HAL.
90    pub const unsafe fn regs(&self) -> MmioUart<'static> {
91        match self {
92            UartId::Uart0 => unsafe { zynq7000::uart::Uart::new_mmio_fixed_0() },
93            UartId::Uart1 => unsafe { zynq7000::uart::Uart::new_mmio_fixed_1() },
94        }
95    }
96}
97
98pub trait RxPin: MioPin {
99    const UART_IDX: UartId;
100}
101pub trait TxPin: MioPin {
102    const UART_IDX: UartId;
103}
104
105pub trait UartPins {}
106
107#[derive(Debug, thiserror::Error)]
108#[error("divisor is zero")]
109pub struct DivisorZero;
110
111macro_rules! pin_pairs {
112    ($UartPeriph:path, ($( [$(#[$meta:meta], )? $TxMio:ident, $RxMio:ident] ),+ $(,)? )) => {
113        $(
114            $( #[$meta] )?
115            impl TxPin for Pin<$TxMio> {
116                const UART_IDX: UartId = $UartPeriph;
117            }
118
119            $( #[$meta] )?
120            impl RxPin for Pin<$RxMio> {
121                const UART_IDX: UartId = $UartPeriph;
122            }
123
124            impl UartPins for (Pin<$TxMio>, Pin<$RxMio>) {}
125        )+
126    };
127}
128
129pin_pairs!(
130    UartId::Uart0,
131    (
132        [Mio11, Mio10],
133        [Mio15, Mio14],
134        [#[cfg(not(feature ="7z010-7z007s-clg225"))], Mio19, Mio18],
135        [#[cfg(not(feature ="7z010-7z007s-clg225"))], Mio23, Mio22],
136        [#[cfg(not(feature ="7z010-7z007s-clg225"))], Mio27, Mio26],
137        [Mio31, Mio30],
138        [Mio35, Mio34],
139        [Mio39, Mio38],
140        [#[cfg(not(feature ="7z010-7z007s-clg225"))], Mio43, Mio42],
141        [#[cfg(not(feature ="7z010-7z007s-clg225"))], Mio47, Mio46],
142        [#[cfg(not(feature ="7z010-7z007s-clg225"))], Mio51, Mio50],
143    )
144);
145
146pin_pairs!(
147    UartId::Uart1,
148    (
149        [Mio8, Mio9],
150        [Mio12, Mio13],
151        [#[cfg(not(feature ="7z010-7z007s-clg225"))], Mio16, Mio17],
152        [#[cfg(not(feature ="7z010-7z007s-clg225"))], Mio20, Mio21],
153        [#[cfg(not(feature ="7z010-7z007s-clg225"))], Mio24, Mio25],
154        [Mio28, Mio29],
155        [Mio32, Mio33],
156        [Mio36, Mio37],
157        [#[cfg(not(feature ="7z010-7z007s-clg225"))], Mio40, Mio41],
158        [#[cfg(not(feature ="7z010-7z007s-clg225"))], Mio44, Mio45],
159        [Mio48, Mio49],
160        [Mio52, Mio53],
161    )
162);
163
164/// Based on values provided by the vendor library.
165pub const MAX_BAUD_RATE: u32 = 6240000;
166/// Based on values provided by the vendor library.
167pub const MIN_BAUD_RATE: u32 = 110;
168
169pub const MAX_BAUDERROR_RATE: f32 = 0.005;
170
171#[derive(Debug, Default, Clone, Copy)]
172pub enum Parity {
173    Even,
174    Odd,
175    #[default]
176    None,
177}
178
179#[derive(Debug, Default, Clone, Copy)]
180pub enum Stopbits {
181    #[default]
182    One,
183    OnePointFive,
184    Two,
185}
186
187#[derive(Debug, Default, Clone, Copy)]
188pub enum CharLen {
189    SixBits,
190    SevenBits,
191    #[default]
192    EightBits,
193}
194
195#[derive(Debug, Clone, Copy)]
196pub struct ClockConfig {
197    cd: u16,
198    bdiv: u8,
199}
200
201#[cfg(feature = "alloc")]
202pub fn calculate_viable_configs(
203    mut uart_clk: Hertz,
204    clk_sel: ClockSelect,
205    target_baud: u32,
206) -> alloc::vec::Vec<(ClockConfig, f64)> {
207    let mut viable_cfgs = alloc::vec::Vec::new();
208    if clk_sel == ClockSelect::UartRefClkDiv8 {
209        uart_clk /= 8;
210    }
211    let mut current_clk_config = ClockConfig::default();
212    for bdiv in 4..u8::MAX {
213        let cd =
214            round(uart_clk.raw() as f64 / ((bdiv as u32 + 1) as f64 * target_baud as f64)) as u64;
215        if cd > u16::MAX as u64 {
216            continue;
217        }
218        current_clk_config.cd = cd as u16;
219        current_clk_config.bdiv = bdiv;
220        let baud = current_clk_config.actual_baud(uart_clk);
221        let error = ((baud - target_baud as f64).abs() / target_baud as f64) * 100.0;
222        if error < MAX_BAUDERROR_RATE as f64 {
223            viable_cfgs.push((current_clk_config, error));
224        }
225    }
226
227    viable_cfgs
228}
229
230/// Calculate the clock configuration for the smallest error to reach the desired target
231/// baud rate.
232///
233/// You can also use [calculate_viable_configs] to get a list of all viable configurations.
234pub fn calculate_raw_baud_cfg_smallest_error(
235    mut uart_clk: Hertz,
236    clk_sel: ClockSelect,
237    target_baud: u32,
238) -> Result<(ClockConfig, f64), DivisorZero> {
239    if target_baud == 0 {
240        return Err(DivisorZero);
241    }
242    if clk_sel == ClockSelect::UartRefClkDiv8 {
243        uart_clk /= 8;
244    }
245    let mut current_clk_config = ClockConfig::default();
246    let mut best_clk_config = ClockConfig::default();
247    let mut smallest_error: f64 = 100.0;
248    for bdiv in 4..u8::MAX {
249        let cd =
250            round(uart_clk.raw() as f64 / ((bdiv as u32 + 1) as f64 * target_baud as f64)) as u64;
251        if cd > u16::MAX as u64 {
252            continue;
253        }
254        current_clk_config.cd = cd as u16;
255        current_clk_config.bdiv = bdiv;
256        let baud = current_clk_config.actual_baud(uart_clk);
257        let error = ((baud - target_baud as f64).abs() / target_baud as f64) * 100.0;
258        if error < smallest_error {
259            best_clk_config = current_clk_config;
260            smallest_error = error;
261        }
262    }
263
264    Ok((best_clk_config, smallest_error))
265}
266
267impl ClockConfig {
268    #[inline]
269    pub const fn new(cd: u16, bdiv: u8) -> Result<Self, DivisorZero> {
270        if cd == 0 {
271            return Err(DivisorZero);
272        }
273        Ok(ClockConfig { cd, bdiv })
274    }
275
276    /// Auto-calculates the best clock configuration settings for the target baudrate.
277    ///
278    /// This function assumes [ClockSelect::UartRefClk] as the clock source. It returns a tuple
279    /// where the first entry is the clock configuration while the second entry is the associated
280    /// baud error from 0.0 to 1.0. It is recommended to keep this error below 2-3 %.
281    pub fn new_autocalc_with_error(
282        io_clks: &IoClocks,
283        target_baud: u32,
284    ) -> Result<(Self, f64), DivisorZero> {
285        Self::new_autocalc_generic(io_clks, ClockSelect::UartRefClk, target_baud)
286    }
287
288    pub fn new_autocalc_generic(
289        io_clks: &IoClocks,
290        clk_sel: ClockSelect,
291        target_baud: u32,
292    ) -> Result<(Self, f64), DivisorZero> {
293        Self::new_autocalc_with_raw_clk(io_clks.uart_clk(), clk_sel, target_baud)
294    }
295
296    pub fn new_autocalc_with_raw_clk(
297        uart_clk: Hertz,
298        clk_sel: ClockSelect,
299        target_baud: u32,
300    ) -> Result<(Self, f64), DivisorZero> {
301        calculate_raw_baud_cfg_smallest_error(uart_clk, clk_sel, target_baud)
302    }
303
304    #[inline]
305    pub const fn cd(&self) -> u16 {
306        self.cd
307    }
308
309    #[inline]
310    pub const fn bdiv(&self) -> u8 {
311        self.bdiv
312    }
313
314    #[inline]
315    pub fn rounded_baud(&self, sel_clk: Hertz) -> u32 {
316        round(self.actual_baud(sel_clk)) as u32
317    }
318
319    #[inline]
320    pub fn actual_baud(&self, sel_clk: Hertz) -> f64 {
321        sel_clk.raw() as f64 / (self.cd as f64 * (self.bdiv + 1) as f64)
322    }
323}
324
325impl Default for ClockConfig {
326    #[inline]
327    fn default() -> Self {
328        ClockConfig::new(1, 0).unwrap()
329    }
330}
331
332#[derive(Debug)]
333pub struct Config {
334    clk_config: ClockConfig,
335    chmode: ChMode,
336    parity: Parity,
337    stopbits: Stopbits,
338    chrl: CharLen,
339    clk_sel: ClockSelect,
340}
341
342impl Config {
343    pub fn new_with_clk_config(clk_config: ClockConfig) -> Self {
344        Self::new(
345            clk_config,
346            ChMode::default(),
347            Parity::default(),
348            Stopbits::default(),
349            CharLen::default(),
350            ClockSelect::default(),
351        )
352    }
353
354    #[inline]
355    pub const fn new(
356        clk_config: ClockConfig,
357        chmode: ChMode,
358        parity: Parity,
359        stopbits: Stopbits,
360        chrl: CharLen,
361        clk_sel: ClockSelect,
362    ) -> Self {
363        Config {
364            clk_config,
365            chmode,
366            parity,
367            stopbits,
368            chrl,
369            clk_sel,
370        }
371    }
372
373    #[inline]
374    pub const fn raw_clk_config(&self) -> ClockConfig {
375        self.clk_config
376    }
377
378    #[inline]
379    pub const fn chmode(&self) -> ChMode {
380        self.chmode
381    }
382
383    #[inline]
384    pub const fn parity(&self) -> Parity {
385        self.parity
386    }
387
388    #[inline]
389    pub const fn stopbits(&self) -> Stopbits {
390        self.stopbits
391    }
392
393    #[inline]
394    pub const fn chrl(&self) -> CharLen {
395        self.chrl
396    }
397
398    #[inline]
399    pub const fn clksel(&self) -> ClockSelect {
400        self.clk_sel
401    }
402}
403
404// TODO: Impl Debug
405pub struct Uart {
406    rx: Rx,
407    tx: Tx,
408    cfg: Config,
409}
410
411#[derive(Debug, thiserror::Error)]
412#[error("invalid UART ID")]
413pub struct InvalidPsUart;
414
415#[derive(Debug, thiserror::Error)]
416pub enum UartConstructionError {
417    #[error("invalid UART ID")]
418    InvalidPsUart(#[from] InvalidPsUart),
419    #[error("missmatch between pins index and passed index")]
420    IdxMissmatch,
421    #[error("invalid pin mux conf for UART")]
422    InvalidMuxConf(MuxConfig),
423}
424
425impl Uart {
426    /// This is the constructor to use the PS UART with EMIO pins to route the UART into the PL
427    /// or expose them via the PL package pins.
428    ///
429    /// A valid PL design which routes the UART pins through into the PL must be used for this to
430    /// work.
431    pub fn new_with_emio(uart: impl PsUart, cfg: Config) -> Result<Uart, InvalidPsUart> {
432        if uart.uart_id().is_none() {
433            return Err(InvalidPsUart);
434        }
435        Ok(Self::new_generic_unchecked(
436            uart.reg_block(),
437            uart.uart_id().unwrap(),
438            cfg,
439        ))
440    }
441
442    /// This is the constructor to use the PS UART with MIO pins.
443    pub fn new_with_mio<TxPinI: TxPin, RxPinI: RxPin>(
444        uart: impl PsUart,
445        cfg: Config,
446        pins: (TxPinI, RxPinI),
447    ) -> Result<Self, UartConstructionError>
448    where
449        (TxPinI, RxPinI): UartPins,
450    {
451        let id = uart.uart_id();
452        if id.is_none() {
453            return Err(InvalidPsUart.into());
454        }
455        if id.unwrap() != TxPinI::UART_IDX || id.unwrap() != RxPinI::UART_IDX {
456            return Err(UartConstructionError::IdxMissmatch);
457        }
458        IoPeriphPin::new(pins.0, UART_MUX_CONF, None);
459        IoPeriphPin::new(pins.1, UART_MUX_CONF, None);
460        Ok(Self::new_generic_unchecked(
461            uart.reg_block(),
462            id.unwrap(),
463            cfg,
464        ))
465    }
466
467    /// This is the generic constructor used by all other constructors.
468    ///
469    /// It does not do any pin checks and resource control. It is recommended to use the other
470    /// constructors instead.
471    pub fn new_generic_unchecked(
472        mut reg_block: MmioUart<'static>,
473        uart_id: UartId,
474        cfg: Config,
475    ) -> Uart {
476        let periph_sel = match uart_id {
477            UartId::Uart0 => crate::PeriphSelect::Uart0,
478            UartId::Uart1 => crate::PeriphSelect::Uart1,
479        };
480        enable_amba_peripheral_clock(periph_sel);
481        reset(uart_id);
482        reg_block.modify_cr(|mut v| {
483            v.set_tx_dis(true);
484            v.set_rx_dis(true);
485            v
486        });
487        // Disable all interrupts.
488        reg_block.write_idr(InterruptControl::new_with_raw_value(0xFFFF_FFFF));
489        let mode = Mode::builder()
490            .with_chmode(cfg.chmode)
491            .with_nbstop(match cfg.stopbits {
492                Stopbits::One => zynq7000::uart::Stopbits::One,
493                Stopbits::OnePointFive => zynq7000::uart::Stopbits::OnePointFive,
494                Stopbits::Two => zynq7000::uart::Stopbits::Two,
495            })
496            .with_par(match cfg.parity {
497                Parity::Even => zynq7000::uart::Parity::Even,
498                Parity::Odd => zynq7000::uart::Parity::Odd,
499                Parity::None => zynq7000::uart::Parity::NoParity,
500            })
501            .with_chrl(match cfg.chrl {
502                CharLen::SixBits => zynq7000::uart::CharLen::SixBits,
503                CharLen::SevenBits => zynq7000::uart::CharLen::SevenBits,
504                CharLen::EightBits => zynq7000::uart::CharLen::EightBits,
505            })
506            .with_clksel(cfg.clk_sel)
507            .build();
508        reg_block.write_mr(mode);
509        reg_block.write_baudgen(
510            Baudgen::builder()
511                .with_cd(cfg.raw_clk_config().cd())
512                .build(),
513        );
514        reg_block.write_baud_rate_div(
515            BaudRateDivisor::builder()
516                .with_bdiv(cfg.raw_clk_config().bdiv())
517                .build(),
518        );
519        // Soft reset for both TX and RX.
520        reg_block.modify_cr(|mut v| {
521            v.set_tx_rst(true);
522            v.set_rx_rst(true);
523            v
524        });
525
526        // Write default value.
527        reg_block.write_rx_fifo_trigger(FifoTrigger::new_with_raw_value(
528            DEFAULT_RX_TRIGGER_LEVEL as u32,
529        ));
530
531        // Enable TX and RX.
532        reg_block.modify_cr(|mut v| {
533            v.set_tx_dis(false);
534            v.set_rx_dis(false);
535            v.set_tx_en(true);
536            v.set_rx_en(true);
537            v
538        });
539
540        Uart {
541            rx: Rx {
542                regs: unsafe { reg_block.clone() },
543            },
544            tx: Tx {
545                regs: reg_block,
546                idx: uart_id,
547            },
548            cfg,
549        }
550    }
551
552    #[inline]
553    pub fn set_mode(&mut self, mode: ChMode) {
554        self.regs().modify_mr(|mut mr| {
555            mr.set_chmode(mode);
556            mr
557        });
558    }
559
560    #[inline]
561    pub const fn regs(&mut self) -> &mut MmioUart<'static> {
562        &mut self.rx.regs
563    }
564
565    #[inline]
566    pub const fn cfg(&self) -> &Config {
567        &self.cfg
568    }
569
570    #[inline]
571    pub const fn split(self) -> (Tx, Rx) {
572        (self.tx, self.rx)
573    }
574}
575
576impl embedded_hal_nb::serial::ErrorType for Uart {
577    type Error = Infallible;
578}
579
580impl embedded_hal_nb::serial::Write for Uart {
581    #[inline]
582    fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
583        self.tx.write(word)
584    }
585
586    fn flush(&mut self) -> nb::Result<(), Self::Error> {
587        embedded_hal_nb::serial::Write::flush(&mut self.tx)
588    }
589}
590
591impl embedded_hal_nb::serial::Read for Uart {
592    /// Read one byte from the FIFO.
593    ///
594    /// This operation is infallible because pulling an available byte from the FIFO
595    /// always succeeds. If you want to be informed about RX errors, you should look at the
596    /// non-blocking API using interrupts, which also tracks the RX error bits.
597    #[inline]
598    fn read(&mut self) -> nb::Result<u8, Self::Error> {
599        self.rx.read()
600    }
601}
602
603impl embedded_io::ErrorType for Uart {
604    type Error = Infallible;
605}
606
607impl embedded_io::Write for Uart {
608    fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
609        self.tx.write(buf)
610    }
611
612    fn flush(&mut self) -> Result<(), Self::Error> {
613        self.tx.flush();
614        Ok(())
615    }
616}
617
618impl embedded_io::Read for Uart {
619    fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
620        self.rx.read(buf)
621    }
622}
623
624/// Reset the UART peripheral using the SLCR reset register for UART.
625///
626/// Please note that this function will interfere with an already configured
627/// UART instance.
628#[inline]
629pub fn reset(id: UartId) {
630    let assert_reset = match id {
631        UartId::Uart0 => DualRefAndClockReset::builder()
632            .with_periph1_ref_rst(false)
633            .with_periph0_ref_rst(true)
634            .with_periph1_cpu1x_rst(false)
635            .with_periph0_cpu1x_rst(true)
636            .build(),
637        UartId::Uart1 => DualRefAndClockReset::builder()
638            .with_periph1_ref_rst(true)
639            .with_periph0_ref_rst(false)
640            .with_periph1_cpu1x_rst(true)
641            .with_periph0_cpu1x_rst(false)
642            .build(),
643    };
644    unsafe {
645        Slcr::with(|regs| {
646            regs.reset_ctrl().write_uart(assert_reset);
647            // Keep it in reset for one cycle.. not sure if this is necessary.
648            cortex_ar::asm::nop();
649            regs.reset_ctrl().write_uart(DualRefAndClockReset::DEFAULT);
650        });
651    }
652}
653
654#[cfg(test)]
655mod tests {
656    use super::*;
657    use approx::abs_diff_eq;
658    use fugit::HertzU32;
659    use zynq7000::uart::ClockSelect;
660
661    const REF_UART_CLK: HertzU32 = HertzU32::from_raw(50_000_000);
662    const REF_UART_CLK_DIV_8: HertzU32 = HertzU32::from_raw(6_250_000);
663
664    #[test]
665    fn test_error_calc_0() {
666        // Baud 600
667        let cfg_0 = ClockConfig::new(10417, 7).unwrap();
668        let actual_baud_0 = cfg_0.actual_baud(REF_UART_CLK);
669        assert!(abs_diff_eq!(actual_baud_0, 599.980, epsilon = 0.01));
670    }
671
672    #[test]
673    fn test_error_calc_1() {
674        // Baud 9600
675        let cfg = ClockConfig::new(81, 7).unwrap();
676        let actual_baud = cfg.actual_baud(REF_UART_CLK_DIV_8);
677        assert!(abs_diff_eq!(actual_baud, 9645.061, epsilon = 0.01));
678    }
679
680    #[test]
681    fn test_error_calc_2() {
682        // Baud 9600
683        let cfg = ClockConfig::new(651, 7).unwrap();
684        let actual_baud = cfg.actual_baud(REF_UART_CLK);
685        assert!(abs_diff_eq!(actual_baud, 9600.614, epsilon = 0.01));
686    }
687
688    #[test]
689    fn test_error_calc_3() {
690        // Baud 28800
691        let cfg = ClockConfig::new(347, 4).unwrap();
692        let actual_baud = cfg.actual_baud(REF_UART_CLK);
693        assert!(abs_diff_eq!(actual_baud, 28818.44, epsilon = 0.01));
694    }
695
696    #[test]
697    fn test_error_calc_4() {
698        // Baud 921600
699        let cfg = ClockConfig::new(9, 5).unwrap();
700        let actual_baud = cfg.actual_baud(REF_UART_CLK);
701        assert!(abs_diff_eq!(actual_baud, 925925.92, epsilon = 0.01));
702    }
703
704    #[test]
705    fn test_best_calc_0() {
706        let result =
707            ClockConfig::new_autocalc_with_raw_clk(REF_UART_CLK, ClockSelect::UartRefClk, 600);
708        assert!(result.is_ok());
709        let (cfg, _error) = result.unwrap();
710        assert_eq!(cfg.cd(), 499);
711        assert_eq!(cfg.bdiv(), 166);
712    }
713
714    #[test]
715    #[cfg(feature = "alloc")]
716    fn test_viable_config_calculation() {
717        let cfgs = calculate_viable_configs(REF_UART_CLK, ClockSelect::UartRefClk, 115200);
718        assert!(
719            cfgs.iter()
720                .find(|(cfg, _error)| { cfg.cd() == 62 && cfg.bdiv() == 6 })
721                .is_some()
722        );
723    }
724}