#![no_std]
#![deny(clippy::undocumented_unsafe_blocks)]
#![deny(unsafe_op_in_unsafe_fn)]
#[cfg(feature = "embedded-io")]
mod embedded_io;
pub mod registers;
use crate::registers::{Fcr, Lcr, Lsr, Registers, Usr};
use core::{fmt, hint::spin_loop};
use safe_mmio::{UniqueMmioPointer, field, field_shared};
use thiserror::Error;
pub struct SynopsysUart<'a> {
registers: UniqueMmioPointer<'a, Registers>,
}
impl<'a> SynopsysUart<'a> {
pub const fn new(registers: UniqueMmioPointer<'a, Registers>) -> Self {
Self { registers }
}
pub fn configure(&mut self, baud_rate: u32, serial_clock: u32) {
while field_shared!(self.registers, usr)
.read()
.contains(Usr::BUSY)
{
spin_loop();
}
field!(self.registers, lcr).write(Lcr::DLAB);
let divisor = serial_clock / (16 * baud_rate);
let fractional = (serial_clock % (16 * baud_rate)) / baud_rate;
field!(self.registers, dlf).write(fractional);
field!(self.registers, rbr_thr_dll).write(divisor & 0xff);
field!(self.registers, dlh_ier).write(divisor >> 8);
field!(self.registers, lcr).write(Lcr::DLS_8);
field!(self.registers, iir_fcr).write(Fcr::FIFOE.bits());
}
pub fn is_tx_fifo_full(&self) -> bool {
!field_shared!(self.registers, usr)
.read()
.contains(Usr::TFNF)
}
pub fn write_word(&mut self, byte: u8) {
while self.is_tx_fifo_full() {
spin_loop();
}
field!(self.registers, rbr_thr_dll).write(byte.into());
}
pub fn flush(&self) {
while !field_shared!(self.registers, usr).read().contains(Usr::TFE) {
spin_loop();
}
}
pub fn is_rx_fifo_empty(&self) -> bool {
!field_shared!(self.registers, usr)
.read()
.contains(Usr::RFNE)
}
pub fn read_word(&mut self) -> Result<Option<u8>, UartError> {
let lsr = field!(self.registers, lsr).read();
if lsr.contains(Lsr::BI) {
Err(UartError::Break)
} else if lsr.contains(Lsr::FE) {
Err(UartError::Framing)
} else if lsr.contains(Lsr::PE) {
Err(UartError::Parity)
} else if lsr.contains(Lsr::OE) {
Err(UartError::Overrun)
} else if !lsr.contains(Lsr::DR) {
Ok(None)
} else {
Ok(Some(field!(self.registers, rbr_thr_dll).read() as u8))
}
}
}
unsafe impl Sync for SynopsysUart<'_> {}
impl fmt::Write for SynopsysUart<'_> {
fn write_str(&mut self, s: &str) -> fmt::Result {
for byte in s.as_bytes() {
self.write_word(*byte);
}
Ok(())
}
}
#[derive(Clone, Copy, Debug, Eq, Error, PartialEq)]
pub enum UartError {
#[error("Framing error")]
Framing,
#[error("Parity error")]
Parity,
#[error("Overrun")]
Overrun,
#[error("Break")]
Break,
}