corstone300-hal 0.1.0

Hardware abstraction layer Crate for the Arm(R) Corstone(TM)-300 Reference System
Documentation
// Copyright 2022 Arm Limited and/or its affiliates <open-source-office@arm.com>
//
// SPDX-License-Identifier: MIT

//! Only one instance per UART may be created (either secure or non secure).

use core::marker::PhantomData;
use core::{fmt, ops::Deref};

use nb::block;

use crate::pac::{
    uart0::RegisterBlock, UART0, UART0_SECURE, UART1, UART1_SECURE, UART2, UART2_SECURE, UART3,
    UART3_SECURE, UART4, UART4_SECURE, UART5, UART5_SECURE,
};

pub trait ValidUart: Deref<Target = RegisterBlock> {
    const PTR: *const RegisterBlock;
}
pub trait Pins<UART> {}
pub trait PinTx<UART> {}
pub trait PinRx<UART> {}

macro_rules! impl_valid_uart {
    ($($name: ty),+) => {
        $(
            impl ValidUart for $name {
                const PTR: *const RegisterBlock = <$name>::PTR;
            }
         )+
    }
}

impl_valid_uart!(UART0, UART1, UART2, UART3, UART4, UART5);

impl<UART, TX, RX> Pins<UART> for (RX, TX)
where
    TX: PinTx<UART>,
    RX: PinRx<UART>,
{
}

/// Filler type for when the pins are preconfigured in hardware
pub struct AutoTx;
/// Filler type for when the pins are preconfigured in hardware
pub struct AutoRx;

impl PinRx<UART0> for AutoRx {}
impl PinTx<UART0> for AutoTx {}
impl PinRx<UART1> for AutoRx {}
impl PinTx<UART1> for AutoTx {}
impl PinRx<UART2> for AutoRx {}
impl PinTx<UART2> for AutoTx {}
impl PinRx<UART3> for crate::shield::Pin<0, 0, crate::shield::Uart> {}
impl PinTx<UART3> for crate::shield::Pin<0, 1, crate::shield::Uart> {}
impl PinRx<UART4> for crate::shield::Pin<1, 0, crate::shield::Uart> {}
impl PinTx<UART4> for crate::shield::Pin<1, 1, crate::shield::Uart> {}
impl PinRx<UART5> for AutoRx {}
impl PinTx<UART5> for AutoTx {}
impl PinRx<UART0_SECURE> for AutoRx {}
impl PinTx<UART0_SECURE> for AutoTx {}
impl PinRx<UART1_SECURE> for AutoRx {}
impl PinTx<UART1_SECURE> for AutoTx {}
impl PinRx<UART2_SECURE> for AutoRx {}
impl PinTx<UART2_SECURE> for AutoTx {}
impl PinRx<UART3_SECURE> for crate::shield::Pin<0, 0, crate::shield::Uart> {}
impl PinTx<UART3_SECURE> for crate::shield::Pin<0, 1, crate::shield::Uart> {}
impl PinRx<UART4_SECURE> for crate::shield::Pin<1, 0, crate::shield::Uart> {}
impl PinTx<UART4_SECURE> for crate::shield::Pin<1, 1, crate::shield::Uart> {}
impl PinRx<UART5_SECURE> for AutoRx {}
impl PinTx<UART5_SECURE> for AutoTx {}

pub struct Serial<UART, RxPin, TxPin> {
    rx: Rx<UART, RxPin>,
    tx: Tx<UART, TxPin>,
}

pub struct Rx<UART, RxPin> {
    _serial: PhantomData<UART>,
    _pin: RxPin,
}

pub struct Tx<UART, TxPin> {
    _serial: PhantomData<UART>,
    _pin: TxPin,
}

#[derive(Debug, Clone, Copy)]
pub enum Error {
    /// Rx dada overrun
    Overrun,
}

pub mod config {
    #[derive(Debug, Clone, Copy)]
    pub enum Error {
        /// Invalid baudrate
        UnreachableBaudrate,
    }
}
impl<U: ValidUart, RxPin, TxPin> Serial<U, RxPin, TxPin>
where
    (RxPin, TxPin): Pins<U>,
{
    /// Initializes the peripheral
    pub fn new(
        uart: U,
        (rx, tx): (RxPin, TxPin),
        baudrate: fugit::HertzU32,
    ) -> Result<Self, (U, (RxPin, TxPin), config::Error)> {
        // configure baudrate
        if let Some(bauddiv) = (baudrate.to_Hz() != 0)
            .then(|| crate::PERIPHERAL_CLOCK.to_Hz() / baudrate.to_Hz())
            .filter(|&bauddiv| bauddiv >= 16)
        {
            uart.bauddiv.write(|w| unsafe { w.bits(bauddiv) });
        } else {
            return Err((uart, (rx, tx), config::Error::UnreachableBaudrate));
        }

        // enable receiver/transitter
        uart.ctrl.modify(|_, w| w.rxen().set_bit().txen().set_bit());

        Ok(Serial {
            rx: Rx {
                _serial: PhantomData,
                _pin: rx,
            },

            tx: Tx {
                _serial: PhantomData,
                _pin: tx,
            },
        })
    }

    /// Splits RX & TX channel to allow independent operations.
    pub fn split(self) -> (Rx<U, RxPin>, Tx<U, TxPin>) {
        let Self { rx, tx } = self;
        (rx, tx)
    }
    // TODO: this needs to reset the peripheral
    // also, Tx<_> and Rx<_> may need to carry their respective pin so that a splited
    // serial can be reconstituted
    /*pub fn release(self) -> ($name, PINS) {
        (self.serial, self.pins)
    }*/
}

impl<U: ValidUart, P: PinRx<U>> embedded_hal::serial::Read<u8> for Rx<U, P> {
    type Error = Error;

    fn read(&mut self) -> nb::Result<u8, Self::Error> {
        let serial = unsafe { &*U::PTR };

        // TODO: check RX overrun

        if serial.state.read().rxbf().bit_is_clear() {
            Err(nb::Error::WouldBlock)
        } else {
            Ok(serial.data.read().bits())
        }
    }
}
impl<U: ValidUart, P: PinTx<U>> embedded_hal::serial::Write<u8> for Tx<U, P> {
    type Error = Error;

    fn flush(&mut self) -> nb::Result<(), Self::Error> {
        let serial = unsafe { &*U::PTR };

        if serial.state.read().txbf().bit_is_set() {
            Err(nb::Error::WouldBlock)
        } else {
            Ok(())
        }
    }

    fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error> {
        let serial = unsafe { &*U::PTR };

        if serial.state.read().txbf().bit_is_set() {
            Err(nb::Error::WouldBlock)
        } else {
            unsafe {
                serial.data.write_with_zero(|w| w.bits(byte));
            }
            Ok(())
        }
    }
}

// delegate embedded-hal's implementation to inner RX<U> and TX<U>
impl<U: ValidUart, RxPin, TxPin> embedded_hal::serial::Read<u8> for Serial<U, RxPin, TxPin>
where
    (RxPin, TxPin): Pins<U>,
    RxPin: PinRx<U>,
{
    type Error = Error;

    fn read(&mut self) -> nb::Result<u8, Self::Error> {
        self.rx.read()
    }
}
impl<U: ValidUart, RxPin, TxPin> embedded_hal::serial::Write<u8> for Serial<U, RxPin, TxPin>
where
    (RxPin, TxPin): Pins<U>,
    TxPin: PinTx<U>,
{
    type Error = Error;

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

    fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error> {
        self.tx.write(byte)
    }
}

impl<U, RxPin, TxPin> fmt::Write for Serial<U, RxPin, TxPin>
where
    U: ValidUart,
    (RxPin, TxPin): Pins<U>,
    TxPin: PinTx<U>,
{
    fn write_str(&mut self, s: &str) -> fmt::Result {
        use embedded_hal::serial::Write;
        for &b in s.as_bytes() {
            block!(self.write(b)).map_err(|_| core::fmt::Error)?;
        }
        Ok(())
    }
}
impl<UART, P> fmt::Write for Tx<UART, P>
where
    Tx<UART, P>: embedded_hal::serial::Write<u8>,
    P: PinTx<UART>,
{
    fn write_str(&mut self, s: &str) -> fmt::Result {
        use embedded_hal::serial::Write;
        for &b in s.as_bytes() {
            block!(self.write(b)).map_err(|_| core::fmt::Error)?;
        }
        Ok(())
    }
}

#[cfg(feature = "eh1-0-alpha")]
mod eh1 {
    use eh1_0_alpha::serial::ErrorKind;

    use super::{Error, Rx, Serial, Tx};

    impl eh1_0_alpha::serial::Error for Error {
        fn kind(&self) -> ErrorKind {
            match self {
                Error::Overrun => ErrorKind::Overrun,
            }
        }
    }

    impl<UART, RxPin, TxPin> eh1_0_alpha::serial::ErrorType for Serial<UART, RxPin, TxPin> {
        type Error = Error;
    }
    // redirect eh1_0_alpha API to embedded_hal's implementation
    impl<UART, RxPin, TxPin> eh1_0_alpha::serial::nb::Read for Serial<UART, RxPin, TxPin>
    where
        Self: embedded_hal::serial::Read<u8, Error = Error>,
    {
        fn read(&mut self) -> nb::Result<u8, Self::Error> {
            <Self as embedded_hal::serial::Read<_>>::read(self)
        }
    }
    impl<UART, RxPin, TxPin> eh1_0_alpha::serial::nb::Write for Serial<UART, RxPin, TxPin>
    where
        Self: embedded_hal::serial::Write<u8, Error = Error>,
    {
        fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
            <Self as embedded_hal::serial::Write<u8>>::write(self, word)
        }

        fn flush(&mut self) -> nb::Result<(), Self::Error> {
            <Self as embedded_hal::serial::Write<u8>>::flush(self)
        }
    }

    // redirect blocking API to nb
    impl<UART, RxPin, TxPin> eh1_0_alpha::serial::blocking::Write for Serial<UART, RxPin, TxPin>
    where
        Self: embedded_hal::serial::Write<u8, Error = Error>,
    {
        fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
            for &b in buffer {
                nb::block!(<Self as eh1_0_alpha::serial::nb::Write>::write(self, b))?
            }
            Ok(())
        }

        fn flush(&mut self) -> Result<(), Self::Error> {
            nb::block!(<Self as eh1_0_alpha::serial::nb::Write>::flush(self))
        }
    }

    // Similarly done for Tx and Rx
    impl<UART, RxPin> eh1_0_alpha::serial::ErrorType for Rx<UART, RxPin> {
        type Error = Error;
    }
    impl<UART, RxPin> eh1_0_alpha::serial::nb::Read for Rx<UART, RxPin>
    where
        Self: embedded_hal::serial::Read<u8, Error = Error>,
    {
        fn read(&mut self) -> nb::Result<u8, Self::Error> {
            <Self as embedded_hal::serial::Read<_>>::read(self)
        }
    }
    impl<UART, TxPin> eh1_0_alpha::serial::ErrorType for Tx<UART, TxPin> {
        type Error = Error;
    }
    impl<UART, TxPin> eh1_0_alpha::serial::nb::Write for Tx<UART, TxPin>
    where
        Self: embedded_hal::serial::Write<u8, Error = Error>,
    {
        fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
            <Self as embedded_hal::serial::Write<u8>>::write(self, word)
        }

        fn flush(&mut self) -> nb::Result<(), Self::Error> {
            <Self as embedded_hal::serial::Write<u8>>::flush(self)
        }
    }
    impl<UART, TxPin> eh1_0_alpha::serial::blocking::Write for Tx<UART, TxPin>
    where
        Self: embedded_hal::serial::Write<u8, Error = Error>,
    {
        fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
            for &b in buffer {
                nb::block!(<Self as eh1_0_alpha::serial::nb::Write>::write(self, b))?
            }
            Ok(())
        }

        fn flush(&mut self) -> Result<(), Self::Error> {
            nb::block!(<Self as eh1_0_alpha::serial::nb::Write>::flush(self))
        }
    }
}