use crate::peripherals::{Uart0, Uart1, Uart2};
use core::marker::PhantomData;
mod sealed {
pub trait UartInstanceSealed {}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum UartPort {
Uart0,
Uart1,
Uart2,
}
impl UartPort {
pub const fn from_index(index: u8) -> Option<Self> {
match index {
0 => Some(Self::Uart0),
1 => Some(Self::Uart1),
2 => Some(Self::Uart2),
_ => None,
}
}
pub const fn index(self) -> usize {
match self {
Self::Uart0 => 0,
Self::Uart1 => 1,
Self::Uart2 => 2,
}
}
}
pub trait UartInstance: sealed::UartInstanceSealed {
const PORT: UartPort;
}
impl<'d> sealed::UartInstanceSealed for Uart0<'d> {}
impl<'d> sealed::UartInstanceSealed for Uart1<'d> {}
impl<'d> sealed::UartInstanceSealed for Uart2<'d> {}
impl<'d> UartInstance for Uart0<'d> {
const PORT: UartPort = UartPort::Uart0;
}
impl<'d> UartInstance for Uart1<'d> {
const PORT: UartPort = UartPort::Uart1;
}
impl<'d> UartInstance for Uart2<'d> {
const PORT: UartPort = UartPort::Uart2;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DataBits {
Five,
Six,
Seven,
Eight,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Parity {
None,
Even,
Odd,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StopBits {
One,
Two,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct BaudRate(u32);
impl BaudRate {
pub const BAUD_115200: BaudRate = BaudRate(115_200);
pub const fn try_new(baud: u32) -> Option<Self> {
if baud == 0 {
return None;
}
let div = ((crate::soc::chip::UART_CLOCK_HZ as u64) * 4 / (baud as u64)) >> 6;
if div < 1 || div > 0xFFFF {
return None;
}
Some(BaudRate(baud))
}
pub const fn baud(self) -> u32 {
self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum UartClock {
Pll,
Boot,
}
impl UartClock {
fn hz(self) -> u32 {
match self {
Self::Pll => crate::soc::chip::UART_CLOCK_HZ,
Self::Boot => boot_uart_clock_hz(),
}
}
}
#[cfg(feature = "chip-ws63")]
fn boot_uart_clock_hz() -> u32 {
crate::soc::chip::uart_boot_clock_hz()
}
#[cfg(not(feature = "chip-ws63"))]
fn boot_uart_clock_hz() -> u32 {
crate::soc::chip::UART_CLOCK_HZ
}
#[derive(Debug, Clone, Copy)]
pub struct Config {
pub baudrate: BaudRate,
pub data_bits: DataBits,
pub parity: Parity,
pub stop_bits: StopBits,
pub clock: UartClock,
}
impl Default for Config {
fn default() -> Self {
Self {
baudrate: BaudRate::BAUD_115200,
data_bits: DataBits::Eight,
parity: Parity::None,
stop_bits: StopBits::One,
clock: UartClock::Pll,
}
}
}
pub struct Uart<'d, T> {
_peripheral: PhantomData<&'d T>,
}
#[allow(dead_code)]
fn regs() -> &'static crate::soc::pac::uart0::RegisterBlock {
unsafe { &*Uart0::ptr() }
}
fn uart_ptr(port: UartPort) -> *const crate::soc::pac::uart0::RegisterBlock {
match port {
UartPort::Uart0 => Uart0::ptr(),
UartPort::Uart1 => Uart1::ptr(),
UartPort::Uart2 => Uart2::ptr(),
}
}
fn uart_regs(port: UartPort) -> &'static crate::soc::pac::uart0::RegisterBlock {
unsafe { &*uart_ptr(port) }
}
#[allow(dead_code)]
fn write_fifo_blocking_levels(r: &crate::soc::pac::uart0::RegisterBlock) {
r.fifo_ctl().write(|w| w.fifo_en().set_bit().tx_empty_trig().empty().rx_empty_trig().char1());
}
fn write_fifo_dma_levels(r: &crate::soc::pac::uart0::RegisterBlock) {
r.fifo_ctl().write(|w| w.fifo_en().set_bit().tx_empty_trig().chars2().rx_empty_trig().quarter());
}
impl<'d> Uart<'d, Uart0<'d>> {
pub fn new_uart0(_uart: Uart0<'d>, config: Config) -> Self {
configure_uart(UartPort::Uart0, &config);
Self { _peripheral: PhantomData }
}
}
impl<'d> Uart<'d, Uart1<'d>> {
pub fn new_uart1(_uart: Uart1<'d>, config: Config) -> Self {
configure_uart(UartPort::Uart1, &config);
Self { _peripheral: PhantomData }
}
}
impl<'d> Uart<'d, Uart2<'d>> {
#[instability::unstable]
pub fn new_uart2(_uart: Uart2<'d>, config: Config) -> Self {
configure_uart(UartPort::Uart2, &config);
Self { _peripheral: PhantomData }
}
}
fn configure_uart(port: UartPort, config: &Config) {
let r = uart_regs(port);
r.uart_ctl().write(|w| w.div_en().set_bit());
let pclk = config.clock.hz();
let baudrate = config.baudrate.baud();
let div64 = ((pclk as u64) * 4 / (baudrate as u64)) as u32; let div = div64 >> 6;
let div_fra = div64 & 0x3F;
let div_l = div & 0xFF;
let div_h = (div >> 8) & 0xFF;
r.div_l().write(|w| unsafe { w.div_l().bits(div_l as u8) });
r.div_h().write(|w| unsafe { w.div_h().bits(div_h as u8) });
r.div_fra().write(|w| unsafe { w.div_fra().bits(div_fra as u8) });
let mut ctl = 0u32;
ctl |= match config.data_bits {
DataBits::Five => 0,
DataBits::Six => 1 << 2,
DataBits::Seven => 2 << 2,
DataBits::Eight => 3 << 2,
};
match config.parity {
Parity::Even => {
ctl |= 1 << 5;
ctl |= 1 << 4;
}
Parity::Odd => {
ctl |= 1 << 5;
}
Parity::None => {}
}
if matches!(config.stop_bits, StopBits::Two) {
ctl |= 1 << 7;
}
r.uart_ctl().write(|w| unsafe { w.bits(ctl as _) });
r.fifo_ctl().write(|w| w.fifo_en().set_bit().tx_fifo_rst().set_bit().rx_fifo_rst().set_bit());
write_fifo_dma_levels(r);
}
impl<T: UartInstance> Uart<'_, T> {
pub fn write_byte(&self, byte: u8) {
let r = uart_regs(T::PORT);
while r.fifo_status().read().tx_fifo_full().bit_is_set() {}
r.data().write(|w| unsafe { w.data().bits(byte) });
}
pub fn read_byte(&self) -> Option<u8> {
let r = uart_regs(T::PORT);
if r.rx_fifo_cnt().read().bits() == 0 { None } else { Some(r.data().read().bits() as u8) }
}
pub fn flush_tx(&self) {
let r = uart_regs(T::PORT);
while !r.fifo_status().read().tx_fifo_empty().bit_is_set() {}
}
pub fn tx_flushed(&self) -> bool {
uart_regs(T::PORT).fifo_status().read().tx_fifo_empty().bit_is_set()
}
pub fn write(&self, data: &[u8]) {
for &b in data {
self.write_byte(b);
}
}
#[instability::unstable]
pub unsafe fn register_block(&self) -> &'static crate::soc::pac::uart0::RegisterBlock {
uart_regs(T::PORT)
}
}
#[cfg(feature = "chip-ws63")]
impl<'d> Uart<'d, Uart0<'d>> {
#[instability::unstable]
pub fn with_dma(self, dma: crate::dma::DmaDriver<'d, crate::dma::Dma0>) -> UartDma<'d, Uart0<'d>> {
UartDma { port: UartPort::Uart0, dma, _p: PhantomData }
}
}
#[cfg(feature = "chip-ws63")]
impl<'d> Uart<'d, Uart1<'d>> {
#[instability::unstable]
pub fn with_dma(self, dma: crate::dma::DmaDriver<'d, crate::dma::Dma0>) -> UartDma<'d, Uart1<'d>> {
UartDma { port: UartPort::Uart1, dma, _p: PhantomData }
}
}
#[cfg(feature = "chip-ws63")]
impl<'d> Uart<'d, Uart2<'d>> {
#[instability::unstable]
pub fn with_dma(self, dma: crate::dma::DmaDriver<'d, crate::dma::Dma0>) -> UartDma<'d, Uart2<'d>> {
UartDma { port: UartPort::Uart2, dma, _p: PhantomData }
}
}
#[cfg(feature = "chip-ws63")]
#[instability::unstable]
pub struct UartDma<'d, T> {
port: UartPort,
dma: crate::dma::DmaDriver<'d, crate::dma::Dma0>,
_p: PhantomData<&'d T>,
}
#[cfg(feature = "chip-ws63")]
impl<'d, T> UartDma<'d, T> {
fn regs(&self) -> &'static crate::soc::pac::uart0::RegisterBlock {
uart_regs(self.port)
}
fn data_addr(&self) -> u32 {
self.regs().data() as *const _ as u32
}
fn tx_peri(&self) -> crate::dma::DmaPeripheral {
match self.port {
UartPort::Uart0 => crate::dma::DmaPeripheral::Uart0Tx,
UartPort::Uart1 => crate::dma::DmaPeripheral::Uart1Tx,
UartPort::Uart2 => crate::dma::DmaPeripheral::Uart2Tx,
}
}
fn rx_peri(&self) -> crate::dma::DmaPeripheral {
match self.port {
UartPort::Uart0 => crate::dma::DmaPeripheral::Uart0Rx,
UartPort::Uart1 => crate::dma::DmaPeripheral::Uart1Rx,
UartPort::Uart2 => crate::dma::DmaPeripheral::Uart2Rx,
}
}
#[instability::unstable]
pub fn write_dma<B: embedded_dma::ReadBuffer<Word = u8>>(
&mut self,
ch: crate::dma::DmaChannel,
buf: B,
) -> Result<B, UartDmaError> {
use crate::dma::{DmaChannelConfig, DmaFrame, DmaTransferSize};
let r = self.regs();
let (ptr, beats) = unsafe { buf.read_buffer() };
if beats > 0xFFF {
return Err(UartDmaError::BufferTooLong);
}
let size = DmaTransferSize::from_beats(beats).ok_or(UartDmaError::BufferTooLong)?;
let bytes = beats;
let data_addr = self.data_addr();
write_fifo_dma_levels(r);
unsafe { crate::cache::clean_range(ptr as usize, bytes) };
let cfg = DmaChannelConfig::default().mem_to_peripheral(self.tx_peri()).with_width(DmaFrame::Byte);
let chn = ch.logical();
self.dma.configure_channel_raw(chn, ptr as u32, data_addr, size, &cfg);
let mut n = 1_000_000u32;
while self.dma.channel_enabled_raw(chn) {
n -= 1;
if n == 0 {
write_fifo_blocking_levels(r);
self.dma.halt_channel_raw(chn);
self.dma.disable_channel_raw(chn);
return Err(UartDmaError::Timeout);
}
core::hint::spin_loop();
}
write_fifo_blocking_levels(r);
Ok(buf)
}
#[instability::unstable]
pub fn read_dma<B: embedded_dma::WriteBuffer<Word = u8>>(
&mut self,
ch: crate::dma::DmaChannel,
mut buf: B,
) -> Result<B, UartDmaError> {
use crate::dma::{DmaChannelConfig, DmaFrame, DmaTransferSize};
let r = self.regs();
let (ptr, beats) = unsafe { buf.write_buffer() };
if beats > 0xFFF {
return Err(UartDmaError::BufferTooLong);
}
let size = DmaTransferSize::from_beats(beats).ok_or(UartDmaError::BufferTooLong)?;
let bytes = beats;
let data_addr = self.data_addr();
write_fifo_dma_levels(r);
let cfg = DmaChannelConfig::default().peripheral_to_mem(self.rx_peri()).with_width(DmaFrame::Byte);
let chn = ch.logical();
self.dma.configure_channel_raw(chn, data_addr, ptr as u32, size, &cfg);
let mut n = 1_000_000u32;
while self.dma.channel_enabled_raw(chn) {
n -= 1;
if n == 0 {
write_fifo_blocking_levels(r);
self.dma.halt_channel_raw(chn);
self.dma.disable_channel_raw(chn);
return Err(UartDmaError::Timeout);
}
core::hint::spin_loop();
}
unsafe { crate::cache::invalidate_range(ptr as usize, bytes) };
write_fifo_blocking_levels(r);
Ok(buf)
}
#[instability::unstable]
pub fn release(self) -> (Uart<'d, T>, crate::dma::DmaDriver<'d, crate::dma::Dma0>) {
let r = self.regs();
write_fifo_blocking_levels(r);
(Uart { _peripheral: PhantomData }, self.dma)
}
}
#[cfg(feature = "chip-ws63")]
#[instability::unstable]
#[derive(Debug)]
#[non_exhaustive]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum UartDmaError {
BufferTooLong,
Timeout,
}
impl embedded_io::ErrorType for Uart<'_, Uart0<'_>> {
type Error = core::convert::Infallible;
}
impl embedded_io::Write for Uart<'_, Uart0<'_>> {
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
for &b in buf {
self.write_byte(b);
}
Ok(buf.len())
}
fn flush(&mut self) -> Result<(), Self::Error> {
self.flush_tx();
Ok(())
}
}
impl embedded_io::Read for Uart<'_, Uart0<'_>> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
let mut n = 0;
for b in buf.iter_mut() {
if let Some(byte) = self.read_byte() {
*b = byte;
n += 1;
} else {
break;
}
}
Ok(n)
}
}
impl embedded_io::ErrorType for Uart<'_, Uart1<'_>> {
type Error = core::convert::Infallible;
}
impl embedded_io::Write for Uart<'_, Uart1<'_>> {
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
for &b in buf {
self.write_byte(b);
}
Ok(buf.len())
}
fn flush(&mut self) -> Result<(), Self::Error> {
self.flush_tx();
Ok(())
}
}
impl embedded_io::Read for Uart<'_, Uart1<'_>> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
let mut n = 0;
for b in buf.iter_mut() {
if let Some(byte) = self.read_byte() {
*b = byte;
n += 1;
} else {
break;
}
}
Ok(n)
}
}
impl embedded_io::ErrorType for Uart<'_, Uart2<'_>> {
type Error = core::convert::Infallible;
}
impl embedded_io::Write for Uart<'_, Uart2<'_>> {
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
for &b in buf {
self.write_byte(b);
}
Ok(buf.len())
}
fn flush(&mut self) -> Result<(), Self::Error> {
self.flush_tx();
Ok(())
}
}
impl embedded_io::Read for Uart<'_, Uart2<'_>> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
let mut n = 0;
for b in buf.iter_mut() {
if let Some(byte) = self.read_byte() {
*b = byte;
n += 1;
} else {
break;
}
}
Ok(n)
}
}
macro_rules! impl_nb_serial {
($uart:ty) => {
impl embedded_hal_nb::serial::ErrorType for Uart<'_, $uart> {
type Error = core::convert::Infallible;
}
impl embedded_hal_nb::serial::Read for Uart<'_, $uart> {
fn read(&mut self) -> nb::Result<u8, Self::Error> {
match self.read_byte() {
Some(b) => Ok(b),
None => Err(nb::Error::WouldBlock),
}
}
}
impl embedded_hal_nb::serial::Write for Uart<'_, $uart> {
fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error> {
self.write_byte(byte);
Ok(())
}
fn flush(&mut self) -> nb::Result<(), Self::Error> {
if self.tx_flushed() { Ok(()) } else { Err(nb::Error::WouldBlock) }
}
}
};
}
impl_nb_serial!(Uart0<'_>);
impl_nb_serial!(Uart1<'_>);
impl_nb_serial!(Uart2<'_>);
#[cfg(all(feature = "chip-ws63", feature = "async", feature = "unstable"))]
mod asynch_impl {
use super::{Uart, UartPort, uart_regs};
use crate::asynch::IrqSignal;
use crate::peripherals::{Uart0, Uart1, Uart2};
use core::cell::Cell;
use core::future::Future;
use core::pin::Pin;
use core::task::{Context, Poll};
use critical_section::Mutex;
use embedded_io_async::{Read, Write};
static UART_RX: [IrqSignal; 3] = [IrqSignal::new(), IrqSignal::new(), IrqSignal::new()];
static UART_BYTE: [Mutex<Cell<u8>>; 3] = [const { Mutex::new(Cell::new(0)) }; 3];
pub fn on_interrupt(port: UartPort) {
let idx = port.index();
let r = uart_regs(port);
if r.rx_fifo_cnt().read().bits() != 0 {
let b = r.data().read().bits() as u8;
critical_section::with(|cs| UART_BYTE[idx].borrow(cs).set(b));
UART_RX[idx].signal();
}
}
struct RxFuture {
port: UartPort,
}
impl Future for RxFuture {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
let idx = self.port.index();
if UART_RX[idx].take_fired() {
Poll::Ready(())
} else {
UART_RX[idx].register(cx.waker());
Poll::Pending
}
}
}
async fn read_one(uart: &Uart<'_, impl Sized>, port: UartPort) -> u8 {
let idx = port.index();
let r = uart_regs(port);
if !r.fifo_status().read().rx_fifo_empty().bit_is_set() {
return r.data().read().bits() as u8; }
UART_RX[idx].reset();
r.intr_en().modify(|_, w| w.rece_data_intr_en().set_bit());
let _ = uart; RxFuture { port }.await;
critical_section::with(|cs| UART_BYTE[idx].borrow(cs).get())
}
macro_rules! async_uart {
($uart:ty, $port:expr) => {
impl Write for Uart<'_, $uart> {
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
for &b in buf {
self.write_byte(b);
}
Ok(buf.len())
}
async fn flush(&mut self) -> Result<(), Self::Error> {
self.flush_tx();
Ok(())
}
}
impl Read for Uart<'_, $uart> {
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
if buf.is_empty() {
return Ok(0);
}
buf[0] = read_one(self, $port).await;
Ok(1)
}
}
};
}
async_uart!(Uart0<'_>, UartPort::Uart0);
async_uart!(Uart1<'_>, UartPort::Uart1);
async_uart!(Uart2<'_>, UartPort::Uart2);
}
#[cfg(all(feature = "chip-ws63", feature = "async", feature = "unstable"))]
pub use asynch_impl::on_interrupt;
#[cfg(all(test, not(target_arch = "riscv32")))]
mod tests {
use super::*;
use crate::soc::chip::UART_CLOCK_HZ;
fn min_baud() -> u32 {
(UART_CLOCK_HZ / (16 * 65535)) + 1
}
fn baud_regs(baud: u32) -> (u16, u16, u16) {
let div64 = ((UART_CLOCK_HZ as u64) * 4 / (baud as u64)) as u32;
let div = div64 >> 6;
let div_fra = (div64 & 0x3F) as u16;
let div_l = (div & 0xFF) as u16;
let div_h = ((div >> 8) & 0xFF) as u16;
(div_l, div_h, div_fra)
}
#[test]
fn default_config_is_115200_8n1() {
let c = Config::default();
assert_eq!(c.baudrate, BaudRate::BAUD_115200);
assert_eq!(c.baudrate.baud(), 115200);
assert_eq!(c.data_bits, DataBits::Eight);
assert_eq!(c.parity, Parity::None);
assert_eq!(c.stop_bits, StopBits::One);
}
#[test]
fn baud_rate_rejects_out_of_range() {
assert!(BaudRate::try_new(0).is_none());
assert!(BaudRate::try_new(min_baud() - 1).is_none());
assert!(BaudRate::try_new(min_baud()).is_some());
assert!(BaudRate::try_new(UART_CLOCK_HZ).is_none());
assert!(BaudRate::try_new(UART_CLOCK_HZ / 16 + 1).is_none());
for &b in &[300u32, 9600, 115200, 921600, 1_000_000] {
assert!(BaudRate::try_new(b).is_some(), "baud {b} should be valid");
assert_eq!(BaudRate::try_new(b).unwrap().baud(), b);
}
}
#[test]
fn baud_115200_known_divisor() {
let div64 = (UART_CLOCK_HZ as u64) * 4 / 115200;
assert_eq!(div64, 5555);
let (div_l, div_h, div_fra) = baud_regs(115200);
assert_eq!(div_l, 0x56);
assert_eq!(div_h, 0x00);
assert_eq!(div_fra, 51);
}
#[test]
fn frac_field_is_six_bits() {
for &baud in &[300u32, 9600, 115200, 921600, 1_000_000, 10_000_000] {
let (_, _, frac) = baud_regs(baud);
assert!(frac <= 0x3F, "baud {baud} -> frac {frac} exceeds 6 bits");
}
}
#[test]
fn divider_bytes_fit_in_eight_bits() {
for &baud in &[min_baud(), 1200, 115200, 3_000_000] {
let (div_l, div_h, _) = baud_regs(baud);
assert!(div_l <= 0xFF);
assert!(div_h <= 0xFF);
}
}
#[test]
fn min_baud_divider_fits_sixteen_bits() {
assert!(BaudRate::try_new(min_baud()).is_some());
let div64 = ((UART_CLOCK_HZ as u64) * 4 / (min_baud() as u64)) as u32;
let div = div64 >> 6;
assert!(div <= 0xFFFF, "div {div} overflows 16-bit divider at min_baud");
}
#[test]
fn divisor_monotonic_in_baud() {
let div_of = |baud: u32| -> u32 {
let (l, h, _) = baud_regs(baud);
((h as u32) << 8) | (l as u32)
};
let mut prev = u32::MAX;
for &baud in &[1200u32, 9600, 19200, 57600, 115200, 230400, 460800] {
let d = div_of(baud);
assert!(d <= prev, "baud {baud} div {d} not <= prev {prev}");
prev = d;
}
}
fn data_bits_field(d: DataBits) -> u16 {
match d {
DataBits::Five => 0,
DataBits::Six => 1 << 2,
DataBits::Seven => 2 << 2,
DataBits::Eight => 3 << 2,
}
}
fn parity_field(p: Parity) -> u16 {
match p {
Parity::Even => (1 << 5) | (1 << 4),
Parity::Odd => 1 << 5,
Parity::None => 0,
}
}
fn build_ctl(c: &Config) -> u16 {
let mut ctl = data_bits_field(c.data_bits) | parity_field(c.parity);
if matches!(c.stop_bits, StopBits::Two) {
ctl |= 1 << 7;
}
ctl
}
#[test]
fn data_bits_encoding() {
assert_eq!(data_bits_field(DataBits::Five), 0 << 2);
assert_eq!(data_bits_field(DataBits::Six), 1 << 2);
assert_eq!(data_bits_field(DataBits::Seven), 2 << 2);
assert_eq!(data_bits_field(DataBits::Eight), 3 << 2);
}
#[test]
fn parity_encoding() {
assert_eq!(parity_field(Parity::None), 0);
assert_eq!(parity_field(Parity::Odd), 1 << 5);
assert_eq!(parity_field(Parity::Even), (1 << 5) | (1 << 4));
assert_ne!(parity_field(Parity::Odd) & (1 << 5), 0);
assert_ne!(parity_field(Parity::Even) & (1 << 5), 0);
assert_eq!(parity_field(Parity::None) & (1 << 5), 0);
}
#[test]
fn stop_bits_encoding() {
let mut c = Config::default();
c.stop_bits = StopBits::One;
assert_eq!(build_ctl(&c) & (1 << 7), 0);
c.stop_bits = StopBits::Two;
assert_eq!(build_ctl(&c) & (1 << 7), 1 << 7);
}
#[test]
fn ctl_8n1_known_value() {
assert_eq!(build_ctl(&Config::default()), 0x0C);
}
#[test]
fn ctl_7e2_known_value() {
let c = Config {
baudrate: BaudRate::try_new(9600).unwrap(),
data_bits: DataBits::Seven,
parity: Parity::Even,
stop_bits: StopBits::Two,
clock: UartClock::Pll,
};
assert_eq!(build_ctl(&c), (2 << 2) | (1 << 5) | (1 << 4) | (1 << 7));
assert_eq!(build_ctl(&c), 0xB8);
}
}
#[cfg(all(test, not(target_arch = "riscv32")))]
mod proptests {
use super::BaudRate;
use crate::soc::chip::UART_CLOCK_HZ;
use proptest::prelude::*;
fn baud_regs(baud: u32) -> (u16, u16, u16) {
let div64 = ((UART_CLOCK_HZ as u64) * 4 / (baud as u64)) as u32;
let div = div64 >> 6;
let div_fra = (div64 & 0x3F) as u16;
let div_l = (div & 0xFF) as u16;
let div_h = ((div >> 8) & 0xFF) as u16;
(div_l, div_h, div_fra)
}
proptest! {
#[test]
fn try_new_never_panics(baud in any::<u32>()) {
let _ = BaudRate::try_new(baud);
}
#[test]
fn accepted_baud_fits_registers(baud in any::<u32>()) {
if let Some(br) = BaudRate::try_new(baud) {
let (div_l, div_h, frac) = baud_regs(br.baud());
prop_assert!(frac <= 0x3F);
prop_assert!(div_l <= 0xFF);
prop_assert!(div_h <= 0xFF);
let div = ((div_h as u32) << 8) | (div_l as u32);
prop_assert!(div <= 0xFFFF, "baud={} div={}", baud, div);
}
}
}
}