1#![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
42pub const FIFO_DEPTH: usize = 16;
44
45pub const DEFAULT_RX_TRIGGER_LEVEL: RxFifoTrigger = RxFifoTrigger::EightBytes;
47
48#[derive(Debug, PartialEq, Eq, Clone, Copy)]
50#[cfg_attr(feature = "defmt", derive(defmt::Format))]
51pub struct ClockConfig {
52 pub div: u16,
54}
55
56#[derive(Debug, thiserror::Error, PartialEq, Eq)]
58#[cfg_attr(feature = "defmt", derive(defmt::Format))]
59#[error("divisor is zero")]
60pub struct DivisorZeroError;
61
62#[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#[derive(Debug, thiserror::Error, PartialEq, Eq)]
80#[cfg_attr(feature = "defmt", derive(defmt::Format))]
81#[error("divisor too large")]
82pub enum ClockConfigError {
83 DivisorTooLargeError(u32),
85 DivisorZero(#[from] DivisorZeroError),
87}
88
89impl ClockConfig {
90 pub fn new(div: u16) -> Self {
92 Self { div }
93 }
94
95 #[inline(always)]
97 pub fn div_msb(&self) -> u8 {
98 (self.div >> 8) as u8
99 }
100
101 #[inline(always)]
103 pub fn div_lsb(&self) -> u8 {
104 self.div as u8
105 }
106
107 #[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 #[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 #[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 #[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 Ok((clk_in.to_raw() + (8 * baudrate)) / (16 * baudrate))
154 }
155}
156
157#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
159#[cfg_attr(feature = "defmt", derive(defmt::Format))]
160pub enum Parity {
161 #[default]
163 None,
164 Odd,
166 Even,
168}
169
170pub struct AxiUart16550 {
172 rx: Rx,
173 tx: Tx,
174 config: UartConfig,
175}
176
177#[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 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 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 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 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 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 regs.write_ier_or_dlm(InterruptEnable::new_with_raw_value(0x0).raw_value());
248 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 #[inline(always)]
268 pub const fn regs(&mut self) -> &mut regs::MmioRegisters<'static> {
269 &mut self.rx.regs
270 }
271
272 #[inline(always)]
274 pub const fn config(&mut self) -> &UartConfig {
275 &self.config
276 }
277
278 #[inline]
282 pub fn write_fifo(&mut self, data: u8) -> nb::Result<(), Infallible> {
283 self.tx.write_fifo(data)
284 }
285
286 #[inline(always)]
288 pub fn thr_empty(&self) -> bool {
289 self.tx.thr_empty()
290 }
291
292 #[inline(always)]
294 pub fn tx_empty(&self) -> bool {
295 self.tx.tx_empty()
296 }
297
298 #[inline(always)]
300 pub fn rx_has_data(&self) -> bool {
301 self.rx.has_data()
302 }
303
304 #[inline(always)]
308 pub fn write_fifo_unchecked(&mut self, data: u8) {
309 self.tx.write_fifo_unchecked(data);
310 }
311
312 #[inline]
317 pub fn read_fifo(&mut self) -> nb::Result<u8, Infallible> {
318 self.rx.read_fifo()
319 }
320
321 #[inline(always)]
323 pub fn read_fifo_unchecked(&mut self) -> u8 {
324 self.rx.read_fifo_unchecked()
325 }
326
327 #[inline(always)]
329 pub fn enable_interrupts(&mut self, ier: InterruptEnable) {
330 self.regs().write_ier_or_dlm(ier.raw_value());
331 }
332
333 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 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 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}