From c450e8423e7aed0d4523bbe10676e46fd1b6e2ba Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Thu, 17 Apr 2025 14:06:09 +0200 Subject: [PATCH] move to shared UART Module --- examples/embassy/src/bin/async-uart-rx.rs | 6 +- examples/embassy/src/bin/async-uart-tx.rs | 4 +- va108xx-hal/Cargo.toml | 2 +- va108xx-hal/src/pins.rs | 237 +-- va108xx-hal/src/spi/mod.rs | 12 +- va108xx-hal/src/spi/pins.rs | 9 +- va108xx-hal/src/time.rs | 26 +- va108xx-hal/src/timer.rs | 3 +- va108xx-hal/src/uart/mod.rs | 1377 +--------------- va108xx-hal/src/uart_tmp/mod.rs | 1387 +++++++++++++++++ .../src/{uart => uart_tmp}/rx_asynch.rs | 0 .../src/{uart => uart_tmp}/tx_asynch.rs | 0 vorago-shared-periphs/Cargo.toml | 8 + vorago-shared-periphs/src/gpio/asynch.rs | 2 +- vorago-shared-periphs/src/gpio/ll.rs | 4 +- vorago-shared-periphs/src/gpio/regs.rs | 6 +- vorago-shared-periphs/src/lib.rs | 5 + vorago-shared-periphs/src/pins.rs | 236 +++ vorago-shared-periphs/src/time.rs | 26 + vorago-shared-periphs/src/uart/mod.rs | 1261 +++++++++++++++ vorago-shared-periphs/src/uart/pins_vor1x.rs | 112 ++ vorago-shared-periphs/src/uart/regs.rs | 303 ++++ vorago-shared-periphs/src/uart/rx_asynch.rs | 437 ++++++ vorago-shared-periphs/src/uart/tx_asynch.rs | 205 +++ 24 files changed, 4008 insertions(+), 1660 deletions(-) create mode 100644 va108xx-hal/src/uart_tmp/mod.rs rename va108xx-hal/src/{uart => uart_tmp}/rx_asynch.rs (100%) rename va108xx-hal/src/{uart => uart_tmp}/tx_asynch.rs (100%) create mode 100644 vorago-shared-periphs/src/pins.rs create mode 100644 vorago-shared-periphs/src/time.rs create mode 100644 vorago-shared-periphs/src/uart/mod.rs create mode 100644 vorago-shared-periphs/src/uart/pins_vor1x.rs create mode 100644 vorago-shared-periphs/src/uart/regs.rs create mode 100644 vorago-shared-periphs/src/uart/rx_asynch.rs create mode 100644 vorago-shared-periphs/src/uart/tx_asynch.rs diff --git a/examples/embassy/src/bin/async-uart-rx.rs b/examples/embassy/src/bin/async-uart-rx.rs index 70a8f58..8bf7baf 100644 --- a/examples/embassy/src/bin/async-uart-rx.rs +++ b/examples/embassy/src/bin/async-uart-rx.rs @@ -31,7 +31,7 @@ use va108xx_hal::{ uart::{ self, on_interrupt_rx_overwriting, rx_asynch::{on_interrupt_rx, RxAsync}, - RxAsyncOverwriting, Tx, UartId, + Bank, RxAsyncOverwriting, Tx, }, InterruptConfig, }; @@ -145,7 +145,7 @@ async fn uart_b_task(mut async_rx: RxAsyncOverwriting<256>, mut tx: Tx) { fn OC2() { let mut prod = critical_section::with(|cs| PRODUCER_UART_A.borrow(cs).borrow_mut().take().unwrap()); - let errors = on_interrupt_rx(UartId::A, &mut prod); + let errors = on_interrupt_rx(Bank::Uart0, &mut prod); critical_section::with(|cs| *PRODUCER_UART_A.borrow(cs).borrow_mut() = Some(prod)); // In a production app, we could use a channel to send the errors to the main task. if let Err(errors) = errors { @@ -158,7 +158,7 @@ fn OC2() { fn OC3() { let mut prod = critical_section::with(|cs| PRODUCER_UART_B.borrow(cs).borrow_mut().take().unwrap()); - let errors = on_interrupt_rx_overwriting(UartId::B, &mut prod, &CONSUMER_UART_B); + let errors = on_interrupt_rx_overwriting(Bank::Uart1, &mut prod, &CONSUMER_UART_B); critical_section::with(|cs| *PRODUCER_UART_B.borrow(cs).borrow_mut() = Some(prod)); // In a production app, we could use a channel to send the errors to the main task. if let Err(errors) = errors { diff --git a/examples/embassy/src/bin/async-uart-tx.rs b/examples/embassy/src/bin/async-uart-tx.rs index 2f47b22..8c073c7 100644 --- a/examples/embassy/src/bin/async-uart-tx.rs +++ b/examples/embassy/src/bin/async-uart-tx.rs @@ -21,7 +21,7 @@ use va108xx_hal::{ pac::{self, interrupt}, pins::PinsA, prelude::*, - uart::{self, on_interrupt_tx, TxAsync, UartId}, + uart::{self, on_interrupt_tx, Bank, TxAsync}, InterruptConfig, }; @@ -91,5 +91,5 @@ async fn main(_spawner: Spawner) { #[interrupt] #[allow(non_snake_case)] fn OC2() { - on_interrupt_tx(UartId::A); + on_interrupt_tx(Bank::Uart0); } diff --git a/va108xx-hal/Cargo.toml b/va108xx-hal/Cargo.toml index 797e71e..7f3881c 100644 --- a/va108xx-hal/Cargo.toml +++ b/va108xx-hal/Cargo.toml @@ -43,7 +43,7 @@ portable-atomic = "1" [features] default = ["rt"] rt = ["va108xx/rt"] -defmt = ["dep:defmt", "fugit/defmt", "embedded-hal/defmt-03"] +defmt = ["dep:defmt", "fugit/defmt", "embedded-hal/defmt-03", "vorago-shared-periphs/defmt"] [package.metadata.docs.rs] all-features = true diff --git a/va108xx-hal/src/pins.rs b/va108xx-hal/src/pins.rs index 6ac3205..8500714 100644 --- a/va108xx-hal/src/pins.rs +++ b/va108xx-hal/src/pins.rs @@ -3,239 +3,4 @@ //! This module contains the pin singletons. It allows creating those singletons //! to access the [Pin] structures of individual ports in a safe way with checked ownership //! rules. -use vorago_shared_periphs::sysconfig::reset_peripheral_for_cycles; - -pub use crate::gpio::{Pin, PinId, PinIdProvider, Port}; - -use crate::sealed::Sealed; -use crate::PeripheralSelect; - -pub trait PinMarker: Sealed { - const ID: PinId; -} - -macro_rules! pin_id { - ($Id:ident, $Port:path, $num:literal) => { - // Need paste macro to use ident in doc attribute - paste::paste! { - #[doc = "Pin ID representing pin " $Id] - #[derive(Debug)] - pub enum $Id {} - - impl $crate::sealed::Sealed for $Id {} - impl PinIdProvider for $Id { - const ID: PinId = PinId::new_unchecked($Port, $num); - } - - impl PinMarker for Pin<$Id> { - const ID: PinId = $Id::ID; - } - } - }; -} - -impl Sealed for Pin {} - -pin_id!(Pa0, Port::A, 0); -pin_id!(Pa1, Port::A, 1); -pin_id!(Pa2, Port::A, 2); -pin_id!(Pa3, Port::A, 3); -pin_id!(Pa4, Port::A, 4); -pin_id!(Pa5, Port::A, 5); -pin_id!(Pa6, Port::A, 6); -pin_id!(Pa7, Port::A, 7); -pin_id!(Pa8, Port::A, 8); -pin_id!(Pa9, Port::A, 9); -pin_id!(Pa10, Port::A, 10); -pin_id!(Pa11, Port::A, 11); -pin_id!(Pa12, Port::A, 12); -pin_id!(Pa13, Port::A, 13); -pin_id!(Pa14, Port::A, 14); -pin_id!(Pa15, Port::A, 15); -pin_id!(Pa16, Port::A, 16); -pin_id!(Pa17, Port::A, 17); -pin_id!(Pa18, Port::A, 18); -pin_id!(Pa19, Port::A, 19); -pin_id!(Pa20, Port::A, 20); -pin_id!(Pa21, Port::A, 21); -pin_id!(Pa22, Port::A, 22); -pin_id!(Pa23, Port::A, 23); -pin_id!(Pa24, Port::A, 24); -pin_id!(Pa25, Port::A, 25); -pin_id!(Pa26, Port::A, 26); -pin_id!(Pa27, Port::A, 27); -pin_id!(Pa28, Port::A, 28); -pin_id!(Pa29, Port::A, 29); -pin_id!(Pa30, Port::A, 30); -pin_id!(Pa31, Port::A, 31); - -pin_id!(Pb0, Port::B, 0); -pin_id!(Pb1, Port::B, 1); -pin_id!(Pb2, Port::B, 2); -pin_id!(Pb3, Port::B, 3); -pin_id!(Pb4, Port::B, 4); -pin_id!(Pb5, Port::B, 5); -pin_id!(Pb6, Port::B, 6); -pin_id!(Pb7, Port::B, 7); -pin_id!(Pb8, Port::B, 8); -pin_id!(Pb9, Port::B, 9); -pin_id!(Pb10, Port::B, 10); -pin_id!(Pb11, Port::B, 11); -pin_id!(Pb12, Port::B, 12); -pin_id!(Pb13, Port::B, 13); -pin_id!(Pb14, Port::B, 14); -pin_id!(Pb15, Port::B, 15); -pin_id!(Pb16, Port::B, 16); -pin_id!(Pb17, Port::B, 17); -pin_id!(Pb18, Port::B, 18); -pin_id!(Pb19, Port::B, 19); -pin_id!(Pb20, Port::B, 20); -pin_id!(Pb21, Port::B, 21); -pin_id!(Pb22, Port::B, 22); -pin_id!(Pb23, Port::B, 23); - -pub struct PinsA { - pub pa0: Pin, - pub pa1: Pin, - pub pa2: Pin, - pub pa3: Pin, - pub pa4: Pin, - pub pa5: Pin, - pub pa6: Pin, - pub pa7: Pin, - pub pa8: Pin, - pub pa9: Pin, - pub pa10: Pin, - pub pa11: Pin, - pub pa12: Pin, - pub pa13: Pin, - pub pa14: Pin, - pub pa15: Pin, - pub pa16: Pin, - pub pa17: Pin, - pub pa18: Pin, - pub pa19: Pin, - pub pa20: Pin, - pub pa21: Pin, - pub pa22: Pin, - pub pa23: Pin, - pub pa24: Pin, - pub pa25: Pin, - pub pa26: Pin, - pub pa27: Pin, - pub pa28: Pin, - pub pa29: Pin, - pub pa30: Pin, - pub pa31: Pin, -} - -impl PinsA { - pub fn new(_port_a: va108xx::Porta) -> Self { - let syscfg = unsafe { va108xx::Sysconfig::steal() }; - reset_peripheral_for_cycles(PeripheralSelect::PortA, 2); - syscfg.peripheral_clk_enable().modify(|_, w| { - w.porta().set_bit(); - w.gpio().set_bit(); - w.ioconfig().set_bit() - }); - Self { - pa0: Pin::__new(), - pa1: Pin::__new(), - pa2: Pin::__new(), - pa3: Pin::__new(), - pa4: Pin::__new(), - pa5: Pin::__new(), - pa6: Pin::__new(), - pa7: Pin::__new(), - pa8: Pin::__new(), - pa9: Pin::__new(), - pa10: Pin::__new(), - pa11: Pin::__new(), - pa12: Pin::__new(), - pa13: Pin::__new(), - pa14: Pin::__new(), - pa15: Pin::__new(), - pa16: Pin::__new(), - pa17: Pin::__new(), - pa18: Pin::__new(), - pa19: Pin::__new(), - pa20: Pin::__new(), - pa21: Pin::__new(), - pa22: Pin::__new(), - pa23: Pin::__new(), - pa24: Pin::__new(), - pa25: Pin::__new(), - pa26: Pin::__new(), - pa27: Pin::__new(), - pa28: Pin::__new(), - pa29: Pin::__new(), - pa30: Pin::__new(), - pa31: Pin::__new(), - } - } -} - -pub struct PinsB { - pub pb0: Pin, - pub pb1: Pin, - pub pb2: Pin, - pub pb3: Pin, - pub pb4: Pin, - pub pb5: Pin, - pub pb6: Pin, - pub pb7: Pin, - pub pb8: Pin, - pub pb9: Pin, - pub pb10: Pin, - pub pb11: Pin, - pub pb12: Pin, - pub pb13: Pin, - pub pb14: Pin, - pub pb15: Pin, - pub pb16: Pin, - pub pb17: Pin, - pub pb18: Pin, - pub pb19: Pin, - pub pb20: Pin, - pub pb21: Pin, - pub pb22: Pin, - pub pb23: Pin, -} - -impl PinsB { - pub fn new(_port_b: va108xx::Portb) -> Self { - let syscfg = unsafe { va108xx::Sysconfig::steal() }; - reset_peripheral_for_cycles(PeripheralSelect::PortB, 2); - syscfg.peripheral_clk_enable().modify(|_, w| { - w.portb().set_bit(); - w.gpio().set_bit(); - w.ioconfig().set_bit() - }); - Self { - pb0: Pin::__new(), - pb1: Pin::__new(), - pb2: Pin::__new(), - pb3: Pin::__new(), - pb4: Pin::__new(), - pb5: Pin::__new(), - pb6: Pin::__new(), - pb7: Pin::__new(), - pb8: Pin::__new(), - pb9: Pin::__new(), - pb10: Pin::__new(), - pb11: Pin::__new(), - pb12: Pin::__new(), - pb13: Pin::__new(), - pb14: Pin::__new(), - pb15: Pin::__new(), - pb16: Pin::__new(), - pb17: Pin::__new(), - pb18: Pin::__new(), - pb19: Pin::__new(), - pb20: Pin::__new(), - pb21: Pin::__new(), - pb22: Pin::__new(), - pb23: Pin::__new(), - } - } -} +pub use vorago_shared_periphs::pins::*; diff --git a/va108xx-hal/src/spi/mod.rs b/va108xx-hal/src/spi/mod.rs index 3340209..2bd3948 100644 --- a/va108xx-hal/src/spi/mod.rs +++ b/va108xx-hal/src/spi/mod.rs @@ -698,9 +698,7 @@ where current_write_idx += 1; } if self.blockmode { - reg_block - .ctrl1() - .modify(|_, w| w.mtxpause().clear_bit()); + reg_block.ctrl1().modify(|_, w| w.mtxpause().clear_bit()); } current_write_idx } @@ -724,9 +722,7 @@ where current_write_idx += 1; } if self.blockmode { - reg_block - .ctrl1() - .modify(|_, w| w.mtxpause().clear_bit()); + reg_block.ctrl1().modify(|_, w| w.mtxpause().clear_bit()); } current_write_idx } @@ -747,9 +743,7 @@ where #[inline(always)] fn write_fifo_unchecked(&mut self, data: u32) { - self.reg_block() - .data() - .write(|w| unsafe { w.bits(data) }); + self.reg_block().data().write(|w| unsafe { w.bits(data) }); } #[inline(always)] diff --git a/va108xx-hal/src/spi/pins.rs b/va108xx-hal/src/spi/pins.rs index 30bf46c..c98b331 100644 --- a/va108xx-hal/src/spi/pins.rs +++ b/va108xx-hal/src/spi/pins.rs @@ -27,7 +27,8 @@ pub trait PinMiso: PinMarker { const FUN_SEL: FunSel; } -pub trait HwCsProvider: PinMarker { +pub trait HwCsProvider { + const PIN_ID: PinId; const SPI_ID: SpiId; const FUN_SEL: FunSel; const CS_ID: HwChipSelectId; @@ -37,6 +38,7 @@ macro_rules! hw_cs_pins { ($SpiId:path, $(($Px:ident, $FunSel:path, $HwCsIdent:path),)+) => { $( impl HwCsProvider for Pin<$Px> { + const PIN_ID: PinId = $Px::ID; const SPI_ID: SpiId = $SpiId; const FUN_SEL: FunSel = $FunSel; const CS_ID: HwChipSelectId = $HwCsIdent; @@ -72,11 +74,8 @@ macro_rules! hw_cs_multi_pin { impl Sealed for $name {} - impl PinMarker for $name { - const ID: PinId = <$pin_id as PinIdProvider>::ID; - } - impl HwCsProvider for $name { + const PIN_ID: PinId = <$pin_id as PinIdProvider>::ID; const SPI_ID: SpiId = $spi_id; const FUN_SEL: FunSel = $fun_sel; const CS_ID: HwChipSelectId = $cs_id; diff --git a/va108xx-hal/src/time.rs b/va108xx-hal/src/time.rs index 9808028..1663b95 100644 --- a/va108xx-hal/src/time.rs +++ b/va108xx-hal/src/time.rs @@ -1,26 +1,2 @@ //! Time units - -// Frequency based - -/// Hertz -pub type Hertz = fugit::HertzU32; - -/// KiloHertz -pub type KiloHertz = fugit::KilohertzU32; - -/// MegaHertz -pub type MegaHertz = fugit::MegahertzU32; - -// Period based - -/// Seconds -pub type Seconds = fugit::SecsDurationU32; - -/// Milliseconds -pub type Milliseconds = fugit::MillisDurationU32; - -/// Microseconds -pub type Microseconds = fugit::MicrosDurationU32; - -/// Nanoseconds -pub type Nanoseconds = fugit::NanosDurationU32; +pub use vorago_shared_periphs::time::*; diff --git a/va108xx-hal/src/timer.rs b/va108xx-hal/src/timer.rs index 4a9f672..08a0934 100644 --- a/va108xx-hal/src/timer.rs +++ b/va108xx-hal/src/timer.rs @@ -20,6 +20,7 @@ use fugit::RateExtU32; use vorago_shared_periphs::{ gpio::{Pin, PinId, PinIdProvider}, ioconfig::regs::FunSel, + pins::PinMarker, sysconfig::enable_peripheral_clock, PeripheralSelect, }; @@ -160,7 +161,7 @@ impl CascadeSource { // Valid TIM and PIN combinations //================================================================================================== -pub trait TimPin: Sealed { +pub trait TimPin: PinMarker { const PIN_ID: PinId; const FUN_SEL: FunSel; const TIM_ID: TimId; diff --git a/va108xx-hal/src/uart/mod.rs b/va108xx-hal/src/uart/mod.rs index a76130d..49303f3 100644 --- a/va108xx-hal/src/uart/mod.rs +++ b/va108xx-hal/src/uart/mod.rs @@ -12,1376 +12,9 @@ //! - [UART simple example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/uart.rs) //! - [UART with IRQ and RTIC](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/rtic/src/bin/uart-echo-rtic.rs) //! - [Flashloader exposing a CCSDS interface via UART](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/flashloader) -use core::{convert::Infallible, ops::Deref}; -use fugit::RateExtU32; -use vorago_shared_periphs::{ - gpio::{IoPeriphPin, Pin}, - FunSel, InterruptConfig, -}; +//! - [Async UART RX example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy/src/bin/async-uart-rx.rs) +//! - [Async UART TX example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy/src/bin/async-uart-tx.rs) +pub use vorago_shared_periphs::uart::*; -use crate::{ - clock::enable_peripheral_clock, - enable_nvic_interrupt, - pac::{self, uarta as uart_base}, - pins::{ - Pa16, Pa17, Pa18, Pa19, Pa2, Pa26, Pa27, Pa3, Pa30, Pa31, Pa8, Pa9, Pb18, Pb19, Pb20, Pb21, - Pb22, Pb23, Pb6, Pb7, Pb8, Pb9, PinMarker, - }, - time::Hertz, - PeripheralSelect, -}; -use embedded_hal_nb::serial::Read; - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum UartId { - A = 0, - B = 1, -} - -impl UartId { - /// Unsafely steal a peripheral MMIO block for the given UART. - /// - /// # Safety - /// - /// Circumvents ownership and safety guarantees by the HAL which can lead to data races - /// on cuncurrent usage. - pub const unsafe fn reg_block(&self) -> &'static uart_base::RegisterBlock { - match self { - UartId::A => unsafe { &*pac::Uarta::PTR }, - UartId::B => unsafe { &*pac::Uartb::PTR }, - } - } -} - -//================================================================================================== -// Type-Level support -//================================================================================================== - -pub trait TxPin: PinMarker { - const UART_ID: UartId; - const FUN_SEL: FunSel; -} -pub trait RxPin: PinMarker { - const UART_ID: UartId; - const FUN_SEL: FunSel; -} - -// UART A pins - -impl TxPin for Pin { - const UART_ID: UartId = UartId::A; - const FUN_SEL: FunSel = FunSel::Sel2; -} -impl RxPin for Pin { - const UART_ID: UartId = UartId::A; - const FUN_SEL: FunSel = FunSel::Sel2; -} - -impl TxPin for Pin { - const UART_ID: UartId = UartId::A; - const FUN_SEL: FunSel = FunSel::Sel3; -} -impl RxPin for Pin { - const UART_ID: UartId = UartId::A; - const FUN_SEL: FunSel = FunSel::Sel3; -} - -impl TxPin for Pin { - const UART_ID: UartId = UartId::A; - const FUN_SEL: FunSel = FunSel::Sel3; -} -impl RxPin for Pin { - const UART_ID: UartId = UartId::A; - const FUN_SEL: FunSel = FunSel::Sel3; -} - -impl TxPin for Pin { - const UART_ID: UartId = UartId::A; - const FUN_SEL: FunSel = FunSel::Sel1; -} -impl RxPin for Pin { - const UART_ID: UartId = UartId::A; - const FUN_SEL: FunSel = FunSel::Sel1; -} - -impl TxPin for Pin { - const UART_ID: UartId = UartId::A; - const FUN_SEL: FunSel = FunSel::Sel1; -} -impl RxPin for Pin { - const UART_ID: UartId = UartId::A; - const FUN_SEL: FunSel = FunSel::Sel1; -} - -// UART B pins - -impl TxPin for Pin { - const UART_ID: UartId = UartId::B; - const FUN_SEL: FunSel = FunSel::Sel2; -} -impl RxPin for Pin { - const UART_ID: UartId = UartId::B; - const FUN_SEL: FunSel = FunSel::Sel2; -} - -impl TxPin for Pin { - const UART_ID: UartId = UartId::B; - const FUN_SEL: FunSel = FunSel::Sel3; -} -impl RxPin for Pin { - const UART_ID: UartId = UartId::B; - const FUN_SEL: FunSel = FunSel::Sel3; -} - -impl TxPin for Pin { - const UART_ID: UartId = UartId::B; - const FUN_SEL: FunSel = FunSel::Sel3; -} -impl RxPin for Pin { - const UART_ID: UartId = UartId::B; - const FUN_SEL: FunSel = FunSel::Sel3; -} - -impl TxPin for Pin { - const UART_ID: UartId = UartId::B; - const FUN_SEL: FunSel = FunSel::Sel1; -} -impl RxPin for Pin { - const UART_ID: UartId = UartId::B; - const FUN_SEL: FunSel = FunSel::Sel1; -} - -impl TxPin for Pin { - const UART_ID: UartId = UartId::B; - const FUN_SEL: FunSel = FunSel::Sel2; -} -impl RxPin for Pin { - const UART_ID: UartId = UartId::B; - const FUN_SEL: FunSel = FunSel::Sel2; -} - -impl TxPin for Pin { - const UART_ID: UartId = UartId::B; - const FUN_SEL: FunSel = FunSel::Sel1; -} -impl RxPin for Pin { - const UART_ID: UartId = UartId::B; - const FUN_SEL: FunSel = FunSel::Sel1; -} - -//================================================================================================== -// Regular Definitions -//================================================================================================== - -#[derive(Debug, PartialEq, Eq, thiserror::Error)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[error("no interrupt ID was set")] -pub struct NoInterruptIdWasSet; - -#[derive(Debug, PartialEq, Eq, thiserror::Error)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[error("transer is pending")] -pub struct TransferPendingError; - -#[derive(Debug, PartialEq, Eq, Copy, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Event { - // Receiver FIFO interrupt enable. Generates interrupt - // when FIFO is at least half full. Half full is defined as FIFO - // count >= RXFIFOIRQTRG - RxFifoHalfFull, - // Framing error, Overrun error, Parity Error and Break error - RxError, - // Event for timeout condition: Data in the FIFO and no receiver - // FIFO activity for 4 character times - RxTimeout, - - // Transmitter FIFO interrupt enable. Generates interrupt - // when FIFO is at least half full. Half full is defined as FIFO - // count >= TXFIFOIRQTRG - TxFifoHalfFull, - // FIFO overflow error - TxError, - // Generate interrupt when transmit FIFO is empty and TXBUSY is 0 - TxEmpty, - // Interrupt when CTSn changes value - TxCts, -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Parity { - None, - Odd, - Even, -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum StopBits { - One = 0, - Two = 1, -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum WordSize { - Five = 0, - Six = 1, - Seven = 2, - Eight = 3, -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct Config { - pub baudrate: Hertz, - pub parity: Parity, - pub stopbits: StopBits, - // When false, use standard 16x baud clock, other 8x baud clock - pub baud8: bool, - pub wordsize: WordSize, - pub enable_tx: bool, - pub enable_rx: bool, -} - -impl Config { - pub fn baudrate(mut self, baudrate: Hertz) -> Self { - self.baudrate = baudrate; - self - } - - pub fn parity_none(mut self) -> Self { - self.parity = Parity::None; - self - } - - pub fn parity_even(mut self) -> Self { - self.parity = Parity::Even; - self - } - - pub fn parity_odd(mut self) -> Self { - self.parity = Parity::Odd; - self - } - - pub fn stopbits(mut self, stopbits: StopBits) -> Self { - self.stopbits = stopbits; - self - } - - pub fn wordsize(mut self, wordsize: WordSize) -> Self { - self.wordsize = wordsize; - self - } - - pub fn baud8(mut self, baud: bool) -> Self { - self.baud8 = baud; - self - } -} - -impl Default for Config { - fn default() -> Config { - let baudrate = 115_200_u32.Hz(); - Config { - baudrate, - parity: Parity::None, - stopbits: StopBits::One, - baud8: false, - wordsize: WordSize::Eight, - enable_tx: true, - enable_rx: true, - } - } -} - -impl From for Config { - fn from(baud: Hertz) -> Self { - Config::default().baudrate(baud) - } -} - -//================================================================================================== -// IRQ Definitions -//================================================================================================== - -#[derive(Debug, Copy, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct IrqContextTimeoutOrMaxSize { - rx_idx: usize, - mode: IrqReceptionMode, - pub max_len: usize, -} - -impl IrqContextTimeoutOrMaxSize { - pub fn new(max_len: usize) -> Self { - IrqContextTimeoutOrMaxSize { - rx_idx: 0, - max_len, - mode: IrqReceptionMode::Idle, - } - } -} - -impl IrqContextTimeoutOrMaxSize { - pub fn reset(&mut self) { - self.rx_idx = 0; - self.mode = IrqReceptionMode::Idle; - } -} - -/// This struct is used to return the default IRQ handler result to the user -#[derive(Debug, Default)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct IrqResult { - pub bytes_read: usize, - pub errors: Option, -} - -/// This struct is used to return the default IRQ handler result to the user -#[derive(Debug, Default)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct IrqResultMaxSizeOrTimeout { - complete: bool, - timeout: bool, - pub errors: Option, - pub bytes_read: usize, -} - -impl IrqResultMaxSizeOrTimeout { - pub fn new() -> Self { - IrqResultMaxSizeOrTimeout { - complete: false, - timeout: false, - errors: None, - bytes_read: 0, - } - } -} -impl IrqResultMaxSizeOrTimeout { - #[inline] - pub fn has_errors(&self) -> bool { - self.errors.is_some() - } - - #[inline] - pub fn overflow_error(&self) -> bool { - self.errors.is_some_and(|e| e.overflow) - } - - #[inline] - pub fn framing_error(&self) -> bool { - self.errors.is_some_and(|e| e.framing) - } - - #[inline] - pub fn parity_error(&self) -> bool { - self.errors.is_some_and(|e| e.parity) - } - - #[inline] - pub fn timeout(&self) -> bool { - self.timeout - } - - #[inline] - pub fn complete(&self) -> bool { - self.complete - } -} - -#[derive(Debug, PartialEq, Copy, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -enum IrqReceptionMode { - Idle, - Pending, -} - -#[derive(Default, Debug, Copy, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct UartErrors { - overflow: bool, - framing: bool, - parity: bool, - other: bool, -} - -impl UartErrors { - #[inline(always)] - pub fn overflow(&self) -> bool { - self.overflow - } - - #[inline(always)] - pub fn framing(&self) -> bool { - self.framing - } - - #[inline(always)] - pub fn parity(&self) -> bool { - self.parity - } - - #[inline(always)] - pub fn other(&self) -> bool { - self.other - } -} - -impl UartErrors { - #[inline(always)] - pub fn error(&self) -> bool { - self.overflow || self.framing || self.parity || self.other - } -} - -#[derive(Debug, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct BufferTooShortError { - found: usize, - expected: usize, -} - -//================================================================================================== -// UART peripheral wrapper -//================================================================================================== - -pub trait UartPeripheralMarker: Deref { - const ID: UartId; - const PERIPH_SEL: PeripheralSelect; - const PTR: *const uart_base::RegisterBlock; - - /// Retrieve the peripheral structure. - /// - /// # Safety - /// - /// This circumvents the safety guarantees of the HAL. - unsafe fn steal() -> Self; - - #[inline(always)] - fn ptr() -> *const uart_base::RegisterBlock { - Self::PTR - } - - /// Retrieve the type erased peripheral register block. - /// - /// # Safety - /// - /// This circumvents the safety guarantees of the HAL. - #[inline(always)] - unsafe fn reg_block() -> &'static uart_base::RegisterBlock { - unsafe { &(*Self::ptr()) } - } -} - -impl UartPeripheralMarker for pac::Uarta { - const ID: UartId = UartId::A; - - const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart0; - const PTR: *const uart_base::RegisterBlock = Self::PTR; - - #[inline(always)] - unsafe fn steal() -> Self { - Self::steal() - } -} - -impl UartPeripheralMarker for pac::Uartb { - const ID: UartId = UartId::B; - - const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart1; - const PTR: *const uart_base::RegisterBlock = Self::PTR; - - #[inline(always)] - unsafe fn steal() -> Self { - Self::steal() - } -} - -#[derive(Debug, thiserror::Error)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[error("UART ID missmatch between peripheral and pins.")] -pub struct UartIdMissmatchError; - -//================================================================================================== -// UART implementation -//================================================================================================== - -/// UART driver structure. -pub struct Uart { - tx: Tx, - rx: Rx, -} - -impl Uart { - /// Calls [Self::new] with the interrupt configuration to some valid value. - pub fn new_with_interrupt( - sys_clk: Hertz, - uart: UartI, - pins: (Tx, Rx), - config: Config, - irq_cfg: InterruptConfig, - ) -> Result { - Self::new(sys_clk, uart, pins, config, Some(irq_cfg)) - } - - /// Calls [Self::new] with the interrupt configuration to [None]. - pub fn new_without_interrupt( - sys_clk: Hertz, - uart: UartI, - pins: (Tx, Rx), - config: Config, - ) -> Result { - Self::new(sys_clk, uart, pins, config, None) - } - - /// Create a new UART peripheral with an interrupt configuration. - /// - /// # Arguments - /// - /// - `syscfg`: The system configuration register block - /// - `sys_clk`: The system clock frequency - /// - `uart`: The concrete UART peripheral instance. - /// - `pins`: UART TX and RX pin tuple. - /// - `config`: UART specific configuration parameters like baudrate. - /// - `irq_cfg`: Optional interrupt configuration. This should be a valid value if the plan - /// is to use TX or RX functionality relying on interrupts. If only the blocking API without - /// any interrupt support is used, this can be [None]. - pub fn new( - sys_clk: Hertz, - _uart: UartI, - _pins: (TxPinI, RxPinI), - config: Config, - opt_irq_cfg: Option, - ) -> Result { - if UartI::ID != TxPinI::UART_ID || UartI::ID != RxPinI::UART_ID { - return Err(UartIdMissmatchError); - } - IoPeriphPin::new(TxPinI::ID, TxPinI::FUN_SEL, None); - IoPeriphPin::new(RxPinI::ID, TxPinI::FUN_SEL, None); - crate::clock::enable_peripheral_clock(UartI::PERIPH_SEL); - - let reg_block = unsafe { UartI::reg_block() }; - let baud_multiplier = match config.baud8 { - false => 16, - true => 8, - }; - - // This is the calculation: (64.0 * (x - integer_part as f32) + 0.5) as u32 without floating - // point calculations. - let frac = ((sys_clk.raw() % (config.baudrate.raw() * 16)) * 64 - + (config.baudrate.raw() * 8)) - / (config.baudrate.raw() * 16); - // Calculations here are derived from chapter 4.8.5 (p.79) of the datasheet. - let x = sys_clk.raw() as f32 / (config.baudrate.raw() * baud_multiplier) as f32; - let integer_part = x as u32; - reg_block.clkscale().write(|w| unsafe { - w.frac().bits(frac as u8); - w.int().bits(integer_part) - }); - - let (paren, pareven) = match config.parity { - Parity::None => (false, false), - Parity::Odd => (true, false), - Parity::Even => (true, true), - }; - let stopbits = match config.stopbits { - StopBits::One => false, - StopBits::Two => true, - }; - let wordsize = config.wordsize as u8; - let baud8 = config.baud8; - reg_block.ctrl().write(|w| { - w.paren().bit(paren); - w.pareven().bit(pareven); - w.stopbits().bit(stopbits); - w.baud8().bit(baud8); - unsafe { w.wordsize().bits(wordsize) } - }); - let (txenb, rxenb) = (config.enable_tx, config.enable_rx); - // Clear the FIFO - reg_block.fifo_clr().write(|w| { - w.rxfifo().set_bit(); - w.txfifo().set_bit() - }); - reg_block.enable().write(|w| { - w.rxenable().bit(rxenb); - w.txenable().bit(txenb) - }); - - if let Some(irq_cfg) = opt_irq_cfg { - if irq_cfg.route { - enable_peripheral_clock(PeripheralSelect::Irqsel); - unsafe { pac::Irqsel::steal() } - .uart0(UartI::ID as usize) - .write(|w| unsafe { w.bits(irq_cfg.id as u32) }); - } - if irq_cfg.enable_in_nvic { - // Safety: User has specifically configured this. - unsafe { enable_nvic_interrupt(irq_cfg.id) }; - } - } - - Ok(Uart { - tx: Tx::new(UartI::ID), - rx: Rx::new(UartI::ID), - }) - } - - #[inline] - pub fn enable_rx(&mut self) { - self.tx - .regs_priv() - .enable() - .modify(|_, w| w.rxenable().set_bit()); - } - - #[inline] - pub fn disable_rx(&mut self) { - self.tx - .regs_priv() - .enable() - .modify(|_, w| w.rxenable().clear_bit()); - } - - #[inline] - pub fn enable_tx(&mut self) { - self.tx - .regs_priv() - .enable() - .modify(|_, w| w.txenable().set_bit()); - } - - #[inline] - pub fn disable_tx(&mut self) { - self.tx - .regs_priv() - .enable() - .modify(|_, w| w.txenable().clear_bit()); - } - - #[inline] - pub fn clear_rx_fifo(&mut self) { - self.tx - .regs_priv() - .fifo_clr() - .write(|w| w.rxfifo().set_bit()); - } - - #[inline] - pub fn clear_tx_fifo(&mut self) { - self.tx - .regs_priv() - .fifo_clr() - .write(|w| w.txfifo().set_bit()); - } - - #[inline] - pub fn clear_rx_status(&mut self) { - self.tx - .regs_priv() - .fifo_clr() - .write(|w| w.rxsts().set_bit()); - } - - #[inline] - pub fn clear_tx_status(&mut self) { - self.tx - .regs_priv() - .fifo_clr() - .write(|w| w.txsts().set_bit()); - } - - pub fn listen(&self, event: Event) { - self.tx.regs_priv().irq_enb().modify(|_, w| match event { - Event::RxError => w.irq_rx_status().set_bit(), - Event::RxFifoHalfFull => w.irq_rx().set_bit(), - Event::RxTimeout => w.irq_rx_to().set_bit(), - Event::TxEmpty => w.irq_tx_empty().set_bit(), - Event::TxError => w.irq_tx_status().set_bit(), - Event::TxFifoHalfFull => w.irq_tx().set_bit(), - Event::TxCts => w.irq_tx_cts().set_bit(), - }); - } - - pub fn unlisten(&self, event: Event) { - self.tx.regs_priv().irq_enb().modify(|_, w| match event { - Event::RxError => w.irq_rx_status().clear_bit(), - Event::RxFifoHalfFull => w.irq_rx().clear_bit(), - Event::RxTimeout => w.irq_rx_to().clear_bit(), - Event::TxEmpty => w.irq_tx_empty().clear_bit(), - Event::TxError => w.irq_tx_status().clear_bit(), - Event::TxFifoHalfFull => w.irq_tx().clear_bit(), - Event::TxCts => w.irq_tx_cts().clear_bit(), - }); - } - - /// Poll receiver errors. - pub fn poll_rx_errors(&self) -> Option { - self.rx.poll_errors() - } - - pub fn split(self) -> (Tx, Rx) { - (self.tx, self.rx) - } -} - -impl embedded_io::ErrorType for Uart { - type Error = Infallible; -} - -impl embedded_hal_nb::serial::ErrorType for Uart { - type Error = Infallible; -} - -impl embedded_hal_nb::serial::Read for Uart { - fn read(&mut self) -> nb::Result { - self.rx.read() - } -} - -impl embedded_hal_nb::serial::Write for Uart { - fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { - self.tx.write(word).map_err(|e| { - if let nb::Error::Other(_) = e { - unreachable!() - } - nb::Error::WouldBlock - }) - } - - fn flush(&mut self) -> nb::Result<(), Self::Error> { - self.tx.flush().map_err(|e| { - if let nb::Error::Other(_) = e { - unreachable!() - } - nb::Error::WouldBlock - }) - } -} - -#[inline(always)] -pub fn enable_rx(uart: &uart_base::RegisterBlock) { - uart.enable().modify(|_, w| w.rxenable().set_bit()); -} - -#[inline(always)] -pub fn disable_rx(uart: &uart_base::RegisterBlock) { - uart.enable().modify(|_, w| w.rxenable().clear_bit()); -} - -#[inline(always)] -pub fn enable_rx_interrupts(uart: &uart_base::RegisterBlock) { - uart.irq_enb().modify(|_, w| { - w.irq_rx().set_bit(); - w.irq_rx_to().set_bit(); - w.irq_rx_status().set_bit() - }); -} - -#[inline(always)] -pub fn disable_rx_interrupts(uart: &uart_base::RegisterBlock) { - uart.irq_enb().modify(|_, w| { - w.irq_rx().clear_bit(); - w.irq_rx_to().clear_bit(); - w.irq_rx_status().clear_bit() - }); -} - -/// Serial receiver. -/// -/// Can be created by using the [Uart::split] API. -pub struct Rx(UartId); - -impl Rx { - #[inline(always)] - const fn new(id: UartId) -> Self { - Self(id) - } - - /// Direct access to the peripheral structure. - /// - /// # Safety - /// - /// You must ensure that only registers related to the operation of the RX side are used. - #[inline(always)] - pub const unsafe fn regs(&self) -> &'static uart_base::RegisterBlock { - self.regs_priv() - } - - #[inline(always)] - const fn regs_priv(&self) -> &'static uart_base::RegisterBlock { - unsafe { self.0.reg_block() } - } - - pub fn poll_errors(&self) -> Option { - let mut errors = UartErrors::default(); - - let status_reader = self.regs_priv().rxstatus().read(); - if status_reader.rxovr().bit_is_set() { - errors.overflow = true; - } else if status_reader.rxfrm().bit_is_set() { - errors.framing = true; - } else if status_reader.rxpar().bit_is_set() { - errors.parity = true; - } else { - return None; - }; - Some(errors) - } - - #[inline] - pub fn clear_fifo(&self) { - self.regs_priv().fifo_clr().write(|w| w.rxfifo().set_bit()); - } - - #[inline] - pub fn disable_interrupts(&mut self) { - disable_rx_interrupts(self.regs_priv()); - } - #[inline] - pub fn enable_interrupts(&mut self) { - enable_rx_interrupts(self.regs_priv()); - } - - #[inline] - pub fn enable(&mut self) { - enable_rx(self.regs_priv()); - } - - #[inline] - pub fn disable(&mut self) { - disable_rx(self.regs_priv()); - } - - /// Low level function to read a word from the UART FIFO. - /// - /// Uses the [nb] API to allow usage in blocking and non-blocking contexts. - /// - /// Please note that you might have to mask the returned value with 0xff to retrieve the actual - /// value if you use the manual parity mode. See chapter 4.6.2 for more information. - #[inline(always)] - pub fn read_fifo(&mut self) -> nb::Result { - if self.regs_priv().rxstatus().read().rdavl().bit_is_clear() { - return Err(nb::Error::WouldBlock); - } - Ok(self.read_fifo_unchecked()) - } - - /// Low level function to read a word from from the UART FIFO. - /// - /// This does not necesarily mean there is a word in the FIFO available. - /// Use the [Self::read_fifo] function to read a word from the FIFO reliably using the [nb] - /// API. - /// - /// Please note that you might have to mask the returned value with 0xff to retrieve the actual - /// value if you use the manual parity mode. See chapter 4.6.2 for more information. - #[inline(always)] - pub fn read_fifo_unchecked(&mut self) -> u32 { - self.regs_priv().data().read().bits() - } - - pub fn into_rx_with_irq(self) -> RxWithInterrupt { - RxWithInterrupt::new(self) - } -} - -impl embedded_io::ErrorType for Rx { - type Error = Infallible; -} - -impl embedded_hal_nb::serial::ErrorType for Rx { - type Error = Infallible; -} - -impl embedded_hal_nb::serial::Read for Rx { - fn read(&mut self) -> nb::Result { - self.read_fifo().map(|val| (val & 0xff) as u8).map_err(|e| { - if let nb::Error::Other(_) = e { - unreachable!() - } - nb::Error::WouldBlock - }) - } -} - -impl embedded_io::Read for Rx { - fn read(&mut self, buf: &mut [u8]) -> Result { - if buf.is_empty() { - return Ok(0); - } - let mut read = 0; - loop { - if self.regs_priv().rxstatus().read().rdavl().bit_is_set() { - break; - } - } - for byte in buf.iter_mut() { - match >::read(self) { - Ok(w) => { - *byte = w; - read += 1; - } - Err(nb::Error::WouldBlock) => break, - } - } - - Ok(read) - } -} - -#[inline(always)] -pub fn enable_tx(uart: &uart_base::RegisterBlock) { - uart.enable().modify(|_, w| w.txenable().set_bit()); -} - -#[inline(always)] -pub fn disable_tx(uart: &uart_base::RegisterBlock) { - uart.enable().modify(|_, w| w.txenable().clear_bit()); -} - -#[inline(always)] -pub fn enable_tx_interrupts(uart: &uart_base::RegisterBlock) { - uart.irq_enb().modify(|_, w| { - w.irq_tx().set_bit(); - w.irq_tx_status().set_bit(); - w.irq_tx_empty().set_bit() - }); -} - -#[inline(always)] -pub fn disable_tx_interrupts(uart: &uart_base::RegisterBlock) { - uart.irq_enb().modify(|_, w| { - w.irq_tx().clear_bit(); - w.irq_tx_status().clear_bit(); - w.irq_tx_empty().clear_bit() - }); -} - -/// Serial transmitter -/// -/// Can be created by using the [Uart::split] API. -pub struct Tx(UartId); - -impl Tx { - /// Retrieve a TX pin without expecting an explicit UART structure - /// - /// # Safety - /// - /// Circumvents the HAL safety guarantees. - #[inline(always)] - pub unsafe fn steal(id: UartId) -> Self { - Self::new(id) - } - - #[inline(always)] - fn new(id: UartId) -> Self { - Self(id) - } - - /// Direct access to the peripheral structure. - /// - /// # Safety - /// - /// You must ensure that only registers related to the operation of the TX side are used. - #[inline(always)] - pub unsafe fn regs(&self) -> &'static uart_base::RegisterBlock { - self.0.reg_block() - } - - #[inline(always)] - fn regs_priv(&self) -> &'static uart_base::RegisterBlock { - unsafe { self.regs() } - } - - #[inline] - pub fn clear_fifo(&mut self) { - self.regs_priv().fifo_clr().write(|w| w.txfifo().set_bit()); - } - - #[inline] - pub fn enable(&mut self) { - self.regs_priv() - .enable() - .modify(|_, w| w.txenable().set_bit()); - } - - #[inline] - pub fn disable(&mut self) { - self.regs_priv() - .enable() - .modify(|_, w| w.txenable().clear_bit()); - } - - /// Enables the IRQ_TX, IRQ_TX_STATUS and IRQ_TX_EMPTY interrupts. - /// - /// - The IRQ_TX interrupt is generated when the TX FIFO is at least half empty. - /// - The IRQ_TX_STATUS interrupt is generated when write data is lost due to a FIFO overflow - /// - The IRQ_TX_EMPTY interrupt is generated when the TX FIFO is empty and the TXBUSY signal - /// is 0 - #[inline] - pub fn enable_interrupts(&self) { - // Safety: We own the UART structure - enable_tx_interrupts(self.regs_priv()); - } - - /// Disables the IRQ_TX, IRQ_TX_STATUS and IRQ_TX_EMPTY interrupts. - /// - /// [Self::enable_interrupts] documents the interrupts. - #[inline] - pub fn disable_interrupts(&self) { - // Safety: We own the UART structure - disable_tx_interrupts(self.regs_priv()); - } - - /// Low level function to write a word to the UART FIFO. - /// - /// Uses the [nb] API to allow usage in blocking and non-blocking contexts. - /// - /// Please note that you might have to mask the returned value with 0xff to retrieve the actual - /// value if you use the manual parity mode. See chapter 11.4.1 for more information. - #[inline(always)] - pub fn write_fifo(&mut self, data: u32) -> nb::Result<(), Infallible> { - if self.regs_priv().txstatus().read().wrrdy().bit_is_clear() { - return Err(nb::Error::WouldBlock); - } - self.write_fifo_unchecked(data); - Ok(()) - } - - /// Low level function to write a word to the UART FIFO. - /// - /// This does not necesarily mean that the FIFO can process another word because it might be - /// full. - /// Use the [Self::write_fifo] function to write a word to the FIFO reliably using the [nb] - /// API. - #[inline(always)] - pub fn write_fifo_unchecked(&mut self, data: u32) { - self.regs_priv().data().write(|w| unsafe { w.bits(data) }); - } - - pub fn into_async(self) -> TxAsync { - TxAsync::new(self) - } -} - -impl embedded_io::ErrorType for Tx { - type Error = Infallible; -} - -impl embedded_hal_nb::serial::ErrorType for Tx { - type Error = Infallible; -} - -impl embedded_hal_nb::serial::Write for Tx { - fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { - self.write_fifo(word as u32) - } - - fn flush(&mut self) -> nb::Result<(), Self::Error> { - // SAFETY: Only TX related registers are used. - let reader = self.regs_priv().txstatus().read(); - if reader.wrbusy().bit_is_set() { - return Err(nb::Error::WouldBlock); - } - Ok(()) - } -} - -impl embedded_io::Write for Tx { - fn write(&mut self, buf: &[u8]) -> Result { - if buf.is_empty() { - return Ok(0); - } - loop { - if self.regs_priv().txstatus().read().wrrdy().bit_is_set() { - break; - } - } - let mut written = 0; - for byte in buf.iter() { - match >::write(self, *byte) { - Ok(_) => written += 1, - Err(nb::Error::WouldBlock) => return Ok(written), - } - } - - Ok(written) - } - - fn flush(&mut self) -> Result<(), Self::Error> { - nb::block!(>::flush(self)) - } -} - -/// Serial receiver, using interrupts to offload reading to the hardware. -/// -/// You can use [Rx::into_rx_with_irq] to convert a normal [Rx] structure into this structure. -/// This structure provides two distinct ways to read the UART RX using interrupts. It should -/// be noted that the interrupt service routine (ISR) still has to be provided by the user. However, -/// this structure provides API calls which can be used inside the ISRs to simplify the reading -/// of the UART. -/// -/// 1. The first way simply empties the FIFO on an interrupt into a user provided buffer. You -/// can simply use [Self::start] to prepare the peripheral and then call the -/// [Self::on_interrupt] in the interrupt service routine. -/// 2. The second way reads packets bounded by a maximum size or a baudtick based timeout. You -/// can use [Self::read_fixed_len_or_timeout_based_using_irq] to prepare the peripheral and -/// then call the [Self::on_interrupt_max_size_or_timeout_based] in the interrupt service -/// routine. You have to call [Self::read_fixed_len_or_timeout_based_using_irq] in the ISR to -/// start reading the next packet. -pub struct RxWithInterrupt(Rx); - -impl RxWithInterrupt { - pub fn new(rx: Rx) -> Self { - Self(rx) - } - - /// This function should be called once at initialization time if the regular - /// [Self::on_interrupt] is used to read the UART receiver to enable and start the receiver. - pub fn start(&mut self) { - self.0.enable(); - self.enable_rx_irq_sources(true); - } - - #[inline(always)] - pub fn rx(&self) -> &Rx { - &self.0 - } - - /// This function is used together with the [Self::on_interrupt_max_size_or_timeout_based] - /// function to read packets with a maximum size or variable sized packets by using the - /// receive timeout of the hardware. - /// - /// This function should be called once at initialization to initiate the context state - /// and to [Self::start] the receiver. After that, it should be called after each - /// completed [Self::on_interrupt_max_size_or_timeout_based] call to restart the reception - /// of a packet. - pub fn read_fixed_len_or_timeout_based_using_irq( - &mut self, - context: &mut IrqContextTimeoutOrMaxSize, - ) -> Result<(), TransferPendingError> { - if context.mode != IrqReceptionMode::Idle { - return Err(TransferPendingError); - } - context.mode = IrqReceptionMode::Pending; - context.rx_idx = 0; - self.start(); - Ok(()) - } - - #[inline] - fn enable_rx_irq_sources(&mut self, timeout: bool) { - self.rx().regs_priv().irq_enb().modify(|_, w| { - if timeout { - w.irq_rx_to().set_bit(); - } - w.irq_rx_status().set_bit(); - w.irq_rx().set_bit() - }); - } - - #[inline] - fn disable_rx_irq_sources(&mut self) { - self.rx().regs_priv().irq_enb().modify(|_, w| { - w.irq_rx_to().clear_bit(); - w.irq_rx_status().clear_bit(); - w.irq_rx().clear_bit() - }); - } - - pub fn cancel_transfer(&mut self) { - self.disable_rx_irq_sources(); - self.0.clear_fifo(); - } - - /// This function should be called in the user provided UART interrupt handler. - /// - /// It simply empties any bytes in the FIFO into the user provided buffer and returns the - /// result of the operation. - /// - /// This function will not disable the RX interrupts, so you don't need to call any other - /// API after calling this function to continue emptying the FIFO. RX errors are handled - /// as partial errors and are returned as part of the [IrqResult]. - pub fn on_interrupt(&mut self, buf: &mut [u8; 16]) -> IrqResult { - let mut result = IrqResult::default(); - - let rx = self.rx(); - let irq_end = rx.regs_priv().irq_end().read(); - let enb_status = rx.regs_priv().enable().read(); - let rx_enabled = enb_status.rxenable().bit_is_set(); - - // Half-Full interrupt. We have a guaranteed amount of data we can read. - if irq_end.irq_rx().bit_is_set() { - let available_bytes = rx.regs_priv().rxfifoirqtrg().read().bits() as usize; - - // If this interrupt bit is set, the trigger level is available at the very least. - // Read everything as fast as possible - for _ in 0..available_bytes { - buf[result.bytes_read] = (rx.regs_priv().data().read().bits() & 0xff) as u8; - result.bytes_read += 1; - } - } - - // Timeout, empty the FIFO completely. - if irq_end.irq_rx_to().bit_is_set() { - // While there is data in the FIFO, write it into the reception buffer - while let Ok(byte) = self.0.read_fifo() { - buf[result.bytes_read] = byte as u8; - result.bytes_read += 1; - } - } - - // RX transfer not complete, check for RX errors - if rx_enabled { - self.check_for_errors(&mut result.errors); - } - - // Clear the interrupt status bits - self.0 - .regs_priv() - .irq_clr() - .write(|w| unsafe { w.bits(irq_end.bits()) }); - result - } - - /// This function should be called in the user provided UART interrupt handler. - /// - /// This function is used to read packets which either have a maximum size or variable sized - /// packet which are bounded by sufficient delays between them, triggering a hardware timeout. - /// - /// If either the maximum number of packets have been read or a timeout occured, the transfer - /// will be deemed completed. The state information of the transfer is tracked in the - /// [IrqContextTimeoutOrMaxSize] structure. - /// - /// If passed buffer is equal to or larger than the specified maximum length, an - /// [BufferTooShortError] will be returned. Other RX errors are treated as partial errors - /// and returned inside the [IrqResultMaxSizeOrTimeout] structure. - pub fn on_interrupt_max_size_or_timeout_based( - &mut self, - context: &mut IrqContextTimeoutOrMaxSize, - buf: &mut [u8], - ) -> Result { - if buf.len() < context.max_len { - return Err(BufferTooShortError { - found: buf.len(), - expected: context.max_len, - }); - } - let mut result = IrqResultMaxSizeOrTimeout::default(); - let rx = self.rx(); - - let irq_end = rx.regs_priv().irq_end().read(); - let enb_status = rx.regs_priv().enable().read(); - let rx_enabled = enb_status.rxenable().bit_is_set(); - - // Half-Full interrupt. We have a guaranteed amount of data we can read. - if irq_end.irq_rx().bit_is_set() { - // Determine the number of bytes to read, ensuring we leave 1 byte in the FIFO. - // We use this trick/hack because the timeout feature of the peripheral relies on data - // being in the RX FIFO. If data continues arriving, another half-full IRQ will fire. - // If not, the last byte(s) is/are emptied by the timeout interrupt. - let available_bytes = rx.regs_priv().rxfifoirqtrg().read().bits() as usize; - - let bytes_to_read = core::cmp::min( - available_bytes.saturating_sub(1), - context.max_len - context.rx_idx, - ); - - // If this interrupt bit is set, the trigger level is available at the very least. - // Read everything as fast as possible - for _ in 0..bytes_to_read { - buf[context.rx_idx] = (rx.regs_priv().data().read().bits() & 0xff) as u8; - context.rx_idx += 1; - } - - // On high-baudrates, data might be available immediately, and we possible have to - // read continuosly? Then again, the CPU should always be faster than that. I'd rather - // rely on the hardware firing another IRQ. I have not tried baudrates higher than - // 115200 so far. - } - // Timeout, empty the FIFO completely. - if irq_end.irq_rx_to().bit_is_set() { - // While there is data in the FIFO, write it into the reception buffer - loop { - if context.rx_idx == context.max_len { - break; - } - // While there is data in the FIFO, write it into the reception buffer - match self.0.read() { - Ok(byte) => { - buf[result.bytes_read] = byte; - result.bytes_read += 1; - } - Err(_) => break, - } - } - self.irq_completion_handler_max_size_timeout(&mut result, context); - return Ok(result); - } - - // RX transfer not complete, check for RX errors - if (context.rx_idx < context.max_len) && rx_enabled { - self.check_for_errors(&mut result.errors); - } - - // Clear the interrupt status bits - rx.regs_priv() - .irq_clr() - .write(|w| unsafe { w.bits(irq_end.bits()) }); - Ok(result) - } - - fn check_for_errors(&self, errors: &mut Option) { - let rx_status = self.rx().regs_priv().rxstatus().read(); - - if rx_status.rxovr().bit_is_set() - || rx_status.rxfrm().bit_is_set() - || rx_status.rxpar().bit_is_set() - { - let err = errors.get_or_insert(UartErrors::default()); - - if rx_status.rxovr().bit_is_set() { - err.overflow = true; - } - if rx_status.rxfrm().bit_is_set() { - err.framing = true; - } - if rx_status.rxpar().bit_is_set() { - err.parity = true; - } - } - } - - fn irq_completion_handler_max_size_timeout( - &mut self, - res: &mut IrqResultMaxSizeOrTimeout, - context: &mut IrqContextTimeoutOrMaxSize, - ) { - self.disable_rx_irq_sources(); - self.0.disable(); - res.bytes_read = context.rx_idx; - res.complete = true; - context.mode = IrqReceptionMode::Idle; - context.rx_idx = 0; - } - - /// # Safety - /// - /// This API allows creating multiple UART instances when releasing the TX structure as well. - /// The user must ensure that these instances are not used to create multiple overlapping - /// UART drivers. - pub unsafe fn release(mut self) -> Rx { - self.disable_rx_irq_sources(); - self.0 - } -} - -pub mod tx_asynch; -pub use tx_asynch::*; - -pub mod rx_asynch; -pub use rx_asynch::*; +pub use vorago_shared_periphs::uart::rx_asynch; +pub use vorago_shared_periphs::uart::tx_asynch; diff --git a/va108xx-hal/src/uart_tmp/mod.rs b/va108xx-hal/src/uart_tmp/mod.rs new file mode 100644 index 0000000..a76130d --- /dev/null +++ b/va108xx-hal/src/uart_tmp/mod.rs @@ -0,0 +1,1387 @@ +//! # API for the UART peripheral +//! +//! The core of this API are the [Uart], [Rx] and [Tx] structures. +//! The RX structure also has a dedicated [RxWithInterrupt] variant which allows reading the receiver +//! using interrupts. +//! +//! The [rx_asynch] and [tx_asynch] modules provide an asynchronous non-blocking API for the UART +//! peripheral. +//! +//! ## Examples +//! +//! - [UART simple example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/uart.rs) +//! - [UART with IRQ and RTIC](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/rtic/src/bin/uart-echo-rtic.rs) +//! - [Flashloader exposing a CCSDS interface via UART](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/flashloader) +use core::{convert::Infallible, ops::Deref}; +use fugit::RateExtU32; +use vorago_shared_periphs::{ + gpio::{IoPeriphPin, Pin}, + FunSel, InterruptConfig, +}; + +use crate::{ + clock::enable_peripheral_clock, + enable_nvic_interrupt, + pac::{self, uarta as uart_base}, + pins::{ + Pa16, Pa17, Pa18, Pa19, Pa2, Pa26, Pa27, Pa3, Pa30, Pa31, Pa8, Pa9, Pb18, Pb19, Pb20, Pb21, + Pb22, Pb23, Pb6, Pb7, Pb8, Pb9, PinMarker, + }, + time::Hertz, + PeripheralSelect, +}; +use embedded_hal_nb::serial::Read; + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum UartId { + A = 0, + B = 1, +} + +impl UartId { + /// Unsafely steal a peripheral MMIO block for the given UART. + /// + /// # Safety + /// + /// Circumvents ownership and safety guarantees by the HAL which can lead to data races + /// on cuncurrent usage. + pub const unsafe fn reg_block(&self) -> &'static uart_base::RegisterBlock { + match self { + UartId::A => unsafe { &*pac::Uarta::PTR }, + UartId::B => unsafe { &*pac::Uartb::PTR }, + } + } +} + +//================================================================================================== +// Type-Level support +//================================================================================================== + +pub trait TxPin: PinMarker { + const UART_ID: UartId; + const FUN_SEL: FunSel; +} +pub trait RxPin: PinMarker { + const UART_ID: UartId; + const FUN_SEL: FunSel; +} + +// UART A pins + +impl TxPin for Pin { + const UART_ID: UartId = UartId::A; + const FUN_SEL: FunSel = FunSel::Sel2; +} +impl RxPin for Pin { + const UART_ID: UartId = UartId::A; + const FUN_SEL: FunSel = FunSel::Sel2; +} + +impl TxPin for Pin { + const UART_ID: UartId = UartId::A; + const FUN_SEL: FunSel = FunSel::Sel3; +} +impl RxPin for Pin { + const UART_ID: UartId = UartId::A; + const FUN_SEL: FunSel = FunSel::Sel3; +} + +impl TxPin for Pin { + const UART_ID: UartId = UartId::A; + const FUN_SEL: FunSel = FunSel::Sel3; +} +impl RxPin for Pin { + const UART_ID: UartId = UartId::A; + const FUN_SEL: FunSel = FunSel::Sel3; +} + +impl TxPin for Pin { + const UART_ID: UartId = UartId::A; + const FUN_SEL: FunSel = FunSel::Sel1; +} +impl RxPin for Pin { + const UART_ID: UartId = UartId::A; + const FUN_SEL: FunSel = FunSel::Sel1; +} + +impl TxPin for Pin { + const UART_ID: UartId = UartId::A; + const FUN_SEL: FunSel = FunSel::Sel1; +} +impl RxPin for Pin { + const UART_ID: UartId = UartId::A; + const FUN_SEL: FunSel = FunSel::Sel1; +} + +// UART B pins + +impl TxPin for Pin { + const UART_ID: UartId = UartId::B; + const FUN_SEL: FunSel = FunSel::Sel2; +} +impl RxPin for Pin { + const UART_ID: UartId = UartId::B; + const FUN_SEL: FunSel = FunSel::Sel2; +} + +impl TxPin for Pin { + const UART_ID: UartId = UartId::B; + const FUN_SEL: FunSel = FunSel::Sel3; +} +impl RxPin for Pin { + const UART_ID: UartId = UartId::B; + const FUN_SEL: FunSel = FunSel::Sel3; +} + +impl TxPin for Pin { + const UART_ID: UartId = UartId::B; + const FUN_SEL: FunSel = FunSel::Sel3; +} +impl RxPin for Pin { + const UART_ID: UartId = UartId::B; + const FUN_SEL: FunSel = FunSel::Sel3; +} + +impl TxPin for Pin { + const UART_ID: UartId = UartId::B; + const FUN_SEL: FunSel = FunSel::Sel1; +} +impl RxPin for Pin { + const UART_ID: UartId = UartId::B; + const FUN_SEL: FunSel = FunSel::Sel1; +} + +impl TxPin for Pin { + const UART_ID: UartId = UartId::B; + const FUN_SEL: FunSel = FunSel::Sel2; +} +impl RxPin for Pin { + const UART_ID: UartId = UartId::B; + const FUN_SEL: FunSel = FunSel::Sel2; +} + +impl TxPin for Pin { + const UART_ID: UartId = UartId::B; + const FUN_SEL: FunSel = FunSel::Sel1; +} +impl RxPin for Pin { + const UART_ID: UartId = UartId::B; + const FUN_SEL: FunSel = FunSel::Sel1; +} + +//================================================================================================== +// Regular Definitions +//================================================================================================== + +#[derive(Debug, PartialEq, Eq, thiserror::Error)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[error("no interrupt ID was set")] +pub struct NoInterruptIdWasSet; + +#[derive(Debug, PartialEq, Eq, thiserror::Error)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[error("transer is pending")] +pub struct TransferPendingError; + +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Event { + // Receiver FIFO interrupt enable. Generates interrupt + // when FIFO is at least half full. Half full is defined as FIFO + // count >= RXFIFOIRQTRG + RxFifoHalfFull, + // Framing error, Overrun error, Parity Error and Break error + RxError, + // Event for timeout condition: Data in the FIFO and no receiver + // FIFO activity for 4 character times + RxTimeout, + + // Transmitter FIFO interrupt enable. Generates interrupt + // when FIFO is at least half full. Half full is defined as FIFO + // count >= TXFIFOIRQTRG + TxFifoHalfFull, + // FIFO overflow error + TxError, + // Generate interrupt when transmit FIFO is empty and TXBUSY is 0 + TxEmpty, + // Interrupt when CTSn changes value + TxCts, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Parity { + None, + Odd, + Even, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum StopBits { + One = 0, + Two = 1, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum WordSize { + Five = 0, + Six = 1, + Seven = 2, + Eight = 3, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct Config { + pub baudrate: Hertz, + pub parity: Parity, + pub stopbits: StopBits, + // When false, use standard 16x baud clock, other 8x baud clock + pub baud8: bool, + pub wordsize: WordSize, + pub enable_tx: bool, + pub enable_rx: bool, +} + +impl Config { + pub fn baudrate(mut self, baudrate: Hertz) -> Self { + self.baudrate = baudrate; + self + } + + pub fn parity_none(mut self) -> Self { + self.parity = Parity::None; + self + } + + pub fn parity_even(mut self) -> Self { + self.parity = Parity::Even; + self + } + + pub fn parity_odd(mut self) -> Self { + self.parity = Parity::Odd; + self + } + + pub fn stopbits(mut self, stopbits: StopBits) -> Self { + self.stopbits = stopbits; + self + } + + pub fn wordsize(mut self, wordsize: WordSize) -> Self { + self.wordsize = wordsize; + self + } + + pub fn baud8(mut self, baud: bool) -> Self { + self.baud8 = baud; + self + } +} + +impl Default for Config { + fn default() -> Config { + let baudrate = 115_200_u32.Hz(); + Config { + baudrate, + parity: Parity::None, + stopbits: StopBits::One, + baud8: false, + wordsize: WordSize::Eight, + enable_tx: true, + enable_rx: true, + } + } +} + +impl From for Config { + fn from(baud: Hertz) -> Self { + Config::default().baudrate(baud) + } +} + +//================================================================================================== +// IRQ Definitions +//================================================================================================== + +#[derive(Debug, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct IrqContextTimeoutOrMaxSize { + rx_idx: usize, + mode: IrqReceptionMode, + pub max_len: usize, +} + +impl IrqContextTimeoutOrMaxSize { + pub fn new(max_len: usize) -> Self { + IrqContextTimeoutOrMaxSize { + rx_idx: 0, + max_len, + mode: IrqReceptionMode::Idle, + } + } +} + +impl IrqContextTimeoutOrMaxSize { + pub fn reset(&mut self) { + self.rx_idx = 0; + self.mode = IrqReceptionMode::Idle; + } +} + +/// This struct is used to return the default IRQ handler result to the user +#[derive(Debug, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct IrqResult { + pub bytes_read: usize, + pub errors: Option, +} + +/// This struct is used to return the default IRQ handler result to the user +#[derive(Debug, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct IrqResultMaxSizeOrTimeout { + complete: bool, + timeout: bool, + pub errors: Option, + pub bytes_read: usize, +} + +impl IrqResultMaxSizeOrTimeout { + pub fn new() -> Self { + IrqResultMaxSizeOrTimeout { + complete: false, + timeout: false, + errors: None, + bytes_read: 0, + } + } +} +impl IrqResultMaxSizeOrTimeout { + #[inline] + pub fn has_errors(&self) -> bool { + self.errors.is_some() + } + + #[inline] + pub fn overflow_error(&self) -> bool { + self.errors.is_some_and(|e| e.overflow) + } + + #[inline] + pub fn framing_error(&self) -> bool { + self.errors.is_some_and(|e| e.framing) + } + + #[inline] + pub fn parity_error(&self) -> bool { + self.errors.is_some_and(|e| e.parity) + } + + #[inline] + pub fn timeout(&self) -> bool { + self.timeout + } + + #[inline] + pub fn complete(&self) -> bool { + self.complete + } +} + +#[derive(Debug, PartialEq, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +enum IrqReceptionMode { + Idle, + Pending, +} + +#[derive(Default, Debug, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct UartErrors { + overflow: bool, + framing: bool, + parity: bool, + other: bool, +} + +impl UartErrors { + #[inline(always)] + pub fn overflow(&self) -> bool { + self.overflow + } + + #[inline(always)] + pub fn framing(&self) -> bool { + self.framing + } + + #[inline(always)] + pub fn parity(&self) -> bool { + self.parity + } + + #[inline(always)] + pub fn other(&self) -> bool { + self.other + } +} + +impl UartErrors { + #[inline(always)] + pub fn error(&self) -> bool { + self.overflow || self.framing || self.parity || self.other + } +} + +#[derive(Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct BufferTooShortError { + found: usize, + expected: usize, +} + +//================================================================================================== +// UART peripheral wrapper +//================================================================================================== + +pub trait UartPeripheralMarker: Deref { + const ID: UartId; + const PERIPH_SEL: PeripheralSelect; + const PTR: *const uart_base::RegisterBlock; + + /// Retrieve the peripheral structure. + /// + /// # Safety + /// + /// This circumvents the safety guarantees of the HAL. + unsafe fn steal() -> Self; + + #[inline(always)] + fn ptr() -> *const uart_base::RegisterBlock { + Self::PTR + } + + /// Retrieve the type erased peripheral register block. + /// + /// # Safety + /// + /// This circumvents the safety guarantees of the HAL. + #[inline(always)] + unsafe fn reg_block() -> &'static uart_base::RegisterBlock { + unsafe { &(*Self::ptr()) } + } +} + +impl UartPeripheralMarker for pac::Uarta { + const ID: UartId = UartId::A; + + const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart0; + const PTR: *const uart_base::RegisterBlock = Self::PTR; + + #[inline(always)] + unsafe fn steal() -> Self { + Self::steal() + } +} + +impl UartPeripheralMarker for pac::Uartb { + const ID: UartId = UartId::B; + + const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart1; + const PTR: *const uart_base::RegisterBlock = Self::PTR; + + #[inline(always)] + unsafe fn steal() -> Self { + Self::steal() + } +} + +#[derive(Debug, thiserror::Error)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[error("UART ID missmatch between peripheral and pins.")] +pub struct UartIdMissmatchError; + +//================================================================================================== +// UART implementation +//================================================================================================== + +/// UART driver structure. +pub struct Uart { + tx: Tx, + rx: Rx, +} + +impl Uart { + /// Calls [Self::new] with the interrupt configuration to some valid value. + pub fn new_with_interrupt( + sys_clk: Hertz, + uart: UartI, + pins: (Tx, Rx), + config: Config, + irq_cfg: InterruptConfig, + ) -> Result { + Self::new(sys_clk, uart, pins, config, Some(irq_cfg)) + } + + /// Calls [Self::new] with the interrupt configuration to [None]. + pub fn new_without_interrupt( + sys_clk: Hertz, + uart: UartI, + pins: (Tx, Rx), + config: Config, + ) -> Result { + Self::new(sys_clk, uart, pins, config, None) + } + + /// Create a new UART peripheral with an interrupt configuration. + /// + /// # Arguments + /// + /// - `syscfg`: The system configuration register block + /// - `sys_clk`: The system clock frequency + /// - `uart`: The concrete UART peripheral instance. + /// - `pins`: UART TX and RX pin tuple. + /// - `config`: UART specific configuration parameters like baudrate. + /// - `irq_cfg`: Optional interrupt configuration. This should be a valid value if the plan + /// is to use TX or RX functionality relying on interrupts. If only the blocking API without + /// any interrupt support is used, this can be [None]. + pub fn new( + sys_clk: Hertz, + _uart: UartI, + _pins: (TxPinI, RxPinI), + config: Config, + opt_irq_cfg: Option, + ) -> Result { + if UartI::ID != TxPinI::UART_ID || UartI::ID != RxPinI::UART_ID { + return Err(UartIdMissmatchError); + } + IoPeriphPin::new(TxPinI::ID, TxPinI::FUN_SEL, None); + IoPeriphPin::new(RxPinI::ID, TxPinI::FUN_SEL, None); + crate::clock::enable_peripheral_clock(UartI::PERIPH_SEL); + + let reg_block = unsafe { UartI::reg_block() }; + let baud_multiplier = match config.baud8 { + false => 16, + true => 8, + }; + + // This is the calculation: (64.0 * (x - integer_part as f32) + 0.5) as u32 without floating + // point calculations. + let frac = ((sys_clk.raw() % (config.baudrate.raw() * 16)) * 64 + + (config.baudrate.raw() * 8)) + / (config.baudrate.raw() * 16); + // Calculations here are derived from chapter 4.8.5 (p.79) of the datasheet. + let x = sys_clk.raw() as f32 / (config.baudrate.raw() * baud_multiplier) as f32; + let integer_part = x as u32; + reg_block.clkscale().write(|w| unsafe { + w.frac().bits(frac as u8); + w.int().bits(integer_part) + }); + + let (paren, pareven) = match config.parity { + Parity::None => (false, false), + Parity::Odd => (true, false), + Parity::Even => (true, true), + }; + let stopbits = match config.stopbits { + StopBits::One => false, + StopBits::Two => true, + }; + let wordsize = config.wordsize as u8; + let baud8 = config.baud8; + reg_block.ctrl().write(|w| { + w.paren().bit(paren); + w.pareven().bit(pareven); + w.stopbits().bit(stopbits); + w.baud8().bit(baud8); + unsafe { w.wordsize().bits(wordsize) } + }); + let (txenb, rxenb) = (config.enable_tx, config.enable_rx); + // Clear the FIFO + reg_block.fifo_clr().write(|w| { + w.rxfifo().set_bit(); + w.txfifo().set_bit() + }); + reg_block.enable().write(|w| { + w.rxenable().bit(rxenb); + w.txenable().bit(txenb) + }); + + if let Some(irq_cfg) = opt_irq_cfg { + if irq_cfg.route { + enable_peripheral_clock(PeripheralSelect::Irqsel); + unsafe { pac::Irqsel::steal() } + .uart0(UartI::ID as usize) + .write(|w| unsafe { w.bits(irq_cfg.id as u32) }); + } + if irq_cfg.enable_in_nvic { + // Safety: User has specifically configured this. + unsafe { enable_nvic_interrupt(irq_cfg.id) }; + } + } + + Ok(Uart { + tx: Tx::new(UartI::ID), + rx: Rx::new(UartI::ID), + }) + } + + #[inline] + pub fn enable_rx(&mut self) { + self.tx + .regs_priv() + .enable() + .modify(|_, w| w.rxenable().set_bit()); + } + + #[inline] + pub fn disable_rx(&mut self) { + self.tx + .regs_priv() + .enable() + .modify(|_, w| w.rxenable().clear_bit()); + } + + #[inline] + pub fn enable_tx(&mut self) { + self.tx + .regs_priv() + .enable() + .modify(|_, w| w.txenable().set_bit()); + } + + #[inline] + pub fn disable_tx(&mut self) { + self.tx + .regs_priv() + .enable() + .modify(|_, w| w.txenable().clear_bit()); + } + + #[inline] + pub fn clear_rx_fifo(&mut self) { + self.tx + .regs_priv() + .fifo_clr() + .write(|w| w.rxfifo().set_bit()); + } + + #[inline] + pub fn clear_tx_fifo(&mut self) { + self.tx + .regs_priv() + .fifo_clr() + .write(|w| w.txfifo().set_bit()); + } + + #[inline] + pub fn clear_rx_status(&mut self) { + self.tx + .regs_priv() + .fifo_clr() + .write(|w| w.rxsts().set_bit()); + } + + #[inline] + pub fn clear_tx_status(&mut self) { + self.tx + .regs_priv() + .fifo_clr() + .write(|w| w.txsts().set_bit()); + } + + pub fn listen(&self, event: Event) { + self.tx.regs_priv().irq_enb().modify(|_, w| match event { + Event::RxError => w.irq_rx_status().set_bit(), + Event::RxFifoHalfFull => w.irq_rx().set_bit(), + Event::RxTimeout => w.irq_rx_to().set_bit(), + Event::TxEmpty => w.irq_tx_empty().set_bit(), + Event::TxError => w.irq_tx_status().set_bit(), + Event::TxFifoHalfFull => w.irq_tx().set_bit(), + Event::TxCts => w.irq_tx_cts().set_bit(), + }); + } + + pub fn unlisten(&self, event: Event) { + self.tx.regs_priv().irq_enb().modify(|_, w| match event { + Event::RxError => w.irq_rx_status().clear_bit(), + Event::RxFifoHalfFull => w.irq_rx().clear_bit(), + Event::RxTimeout => w.irq_rx_to().clear_bit(), + Event::TxEmpty => w.irq_tx_empty().clear_bit(), + Event::TxError => w.irq_tx_status().clear_bit(), + Event::TxFifoHalfFull => w.irq_tx().clear_bit(), + Event::TxCts => w.irq_tx_cts().clear_bit(), + }); + } + + /// Poll receiver errors. + pub fn poll_rx_errors(&self) -> Option { + self.rx.poll_errors() + } + + pub fn split(self) -> (Tx, Rx) { + (self.tx, self.rx) + } +} + +impl embedded_io::ErrorType for Uart { + type Error = Infallible; +} + +impl embedded_hal_nb::serial::ErrorType for Uart { + type Error = Infallible; +} + +impl embedded_hal_nb::serial::Read for Uart { + fn read(&mut self) -> nb::Result { + self.rx.read() + } +} + +impl embedded_hal_nb::serial::Write for Uart { + fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { + self.tx.write(word).map_err(|e| { + if let nb::Error::Other(_) = e { + unreachable!() + } + nb::Error::WouldBlock + }) + } + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + self.tx.flush().map_err(|e| { + if let nb::Error::Other(_) = e { + unreachable!() + } + nb::Error::WouldBlock + }) + } +} + +#[inline(always)] +pub fn enable_rx(uart: &uart_base::RegisterBlock) { + uart.enable().modify(|_, w| w.rxenable().set_bit()); +} + +#[inline(always)] +pub fn disable_rx(uart: &uart_base::RegisterBlock) { + uart.enable().modify(|_, w| w.rxenable().clear_bit()); +} + +#[inline(always)] +pub fn enable_rx_interrupts(uart: &uart_base::RegisterBlock) { + uart.irq_enb().modify(|_, w| { + w.irq_rx().set_bit(); + w.irq_rx_to().set_bit(); + w.irq_rx_status().set_bit() + }); +} + +#[inline(always)] +pub fn disable_rx_interrupts(uart: &uart_base::RegisterBlock) { + uart.irq_enb().modify(|_, w| { + w.irq_rx().clear_bit(); + w.irq_rx_to().clear_bit(); + w.irq_rx_status().clear_bit() + }); +} + +/// Serial receiver. +/// +/// Can be created by using the [Uart::split] API. +pub struct Rx(UartId); + +impl Rx { + #[inline(always)] + const fn new(id: UartId) -> Self { + Self(id) + } + + /// Direct access to the peripheral structure. + /// + /// # Safety + /// + /// You must ensure that only registers related to the operation of the RX side are used. + #[inline(always)] + pub const unsafe fn regs(&self) -> &'static uart_base::RegisterBlock { + self.regs_priv() + } + + #[inline(always)] + const fn regs_priv(&self) -> &'static uart_base::RegisterBlock { + unsafe { self.0.reg_block() } + } + + pub fn poll_errors(&self) -> Option { + let mut errors = UartErrors::default(); + + let status_reader = self.regs_priv().rxstatus().read(); + if status_reader.rxovr().bit_is_set() { + errors.overflow = true; + } else if status_reader.rxfrm().bit_is_set() { + errors.framing = true; + } else if status_reader.rxpar().bit_is_set() { + errors.parity = true; + } else { + return None; + }; + Some(errors) + } + + #[inline] + pub fn clear_fifo(&self) { + self.regs_priv().fifo_clr().write(|w| w.rxfifo().set_bit()); + } + + #[inline] + pub fn disable_interrupts(&mut self) { + disable_rx_interrupts(self.regs_priv()); + } + #[inline] + pub fn enable_interrupts(&mut self) { + enable_rx_interrupts(self.regs_priv()); + } + + #[inline] + pub fn enable(&mut self) { + enable_rx(self.regs_priv()); + } + + #[inline] + pub fn disable(&mut self) { + disable_rx(self.regs_priv()); + } + + /// Low level function to read a word from the UART FIFO. + /// + /// Uses the [nb] API to allow usage in blocking and non-blocking contexts. + /// + /// Please note that you might have to mask the returned value with 0xff to retrieve the actual + /// value if you use the manual parity mode. See chapter 4.6.2 for more information. + #[inline(always)] + pub fn read_fifo(&mut self) -> nb::Result { + if self.regs_priv().rxstatus().read().rdavl().bit_is_clear() { + return Err(nb::Error::WouldBlock); + } + Ok(self.read_fifo_unchecked()) + } + + /// Low level function to read a word from from the UART FIFO. + /// + /// This does not necesarily mean there is a word in the FIFO available. + /// Use the [Self::read_fifo] function to read a word from the FIFO reliably using the [nb] + /// API. + /// + /// Please note that you might have to mask the returned value with 0xff to retrieve the actual + /// value if you use the manual parity mode. See chapter 4.6.2 for more information. + #[inline(always)] + pub fn read_fifo_unchecked(&mut self) -> u32 { + self.regs_priv().data().read().bits() + } + + pub fn into_rx_with_irq(self) -> RxWithInterrupt { + RxWithInterrupt::new(self) + } +} + +impl embedded_io::ErrorType for Rx { + type Error = Infallible; +} + +impl embedded_hal_nb::serial::ErrorType for Rx { + type Error = Infallible; +} + +impl embedded_hal_nb::serial::Read for Rx { + fn read(&mut self) -> nb::Result { + self.read_fifo().map(|val| (val & 0xff) as u8).map_err(|e| { + if let nb::Error::Other(_) = e { + unreachable!() + } + nb::Error::WouldBlock + }) + } +} + +impl embedded_io::Read for Rx { + fn read(&mut self, buf: &mut [u8]) -> Result { + if buf.is_empty() { + return Ok(0); + } + let mut read = 0; + loop { + if self.regs_priv().rxstatus().read().rdavl().bit_is_set() { + break; + } + } + for byte in buf.iter_mut() { + match >::read(self) { + Ok(w) => { + *byte = w; + read += 1; + } + Err(nb::Error::WouldBlock) => break, + } + } + + Ok(read) + } +} + +#[inline(always)] +pub fn enable_tx(uart: &uart_base::RegisterBlock) { + uart.enable().modify(|_, w| w.txenable().set_bit()); +} + +#[inline(always)] +pub fn disable_tx(uart: &uart_base::RegisterBlock) { + uart.enable().modify(|_, w| w.txenable().clear_bit()); +} + +#[inline(always)] +pub fn enable_tx_interrupts(uart: &uart_base::RegisterBlock) { + uart.irq_enb().modify(|_, w| { + w.irq_tx().set_bit(); + w.irq_tx_status().set_bit(); + w.irq_tx_empty().set_bit() + }); +} + +#[inline(always)] +pub fn disable_tx_interrupts(uart: &uart_base::RegisterBlock) { + uart.irq_enb().modify(|_, w| { + w.irq_tx().clear_bit(); + w.irq_tx_status().clear_bit(); + w.irq_tx_empty().clear_bit() + }); +} + +/// Serial transmitter +/// +/// Can be created by using the [Uart::split] API. +pub struct Tx(UartId); + +impl Tx { + /// Retrieve a TX pin without expecting an explicit UART structure + /// + /// # Safety + /// + /// Circumvents the HAL safety guarantees. + #[inline(always)] + pub unsafe fn steal(id: UartId) -> Self { + Self::new(id) + } + + #[inline(always)] + fn new(id: UartId) -> Self { + Self(id) + } + + /// Direct access to the peripheral structure. + /// + /// # Safety + /// + /// You must ensure that only registers related to the operation of the TX side are used. + #[inline(always)] + pub unsafe fn regs(&self) -> &'static uart_base::RegisterBlock { + self.0.reg_block() + } + + #[inline(always)] + fn regs_priv(&self) -> &'static uart_base::RegisterBlock { + unsafe { self.regs() } + } + + #[inline] + pub fn clear_fifo(&mut self) { + self.regs_priv().fifo_clr().write(|w| w.txfifo().set_bit()); + } + + #[inline] + pub fn enable(&mut self) { + self.regs_priv() + .enable() + .modify(|_, w| w.txenable().set_bit()); + } + + #[inline] + pub fn disable(&mut self) { + self.regs_priv() + .enable() + .modify(|_, w| w.txenable().clear_bit()); + } + + /// Enables the IRQ_TX, IRQ_TX_STATUS and IRQ_TX_EMPTY interrupts. + /// + /// - The IRQ_TX interrupt is generated when the TX FIFO is at least half empty. + /// - The IRQ_TX_STATUS interrupt is generated when write data is lost due to a FIFO overflow + /// - The IRQ_TX_EMPTY interrupt is generated when the TX FIFO is empty and the TXBUSY signal + /// is 0 + #[inline] + pub fn enable_interrupts(&self) { + // Safety: We own the UART structure + enable_tx_interrupts(self.regs_priv()); + } + + /// Disables the IRQ_TX, IRQ_TX_STATUS and IRQ_TX_EMPTY interrupts. + /// + /// [Self::enable_interrupts] documents the interrupts. + #[inline] + pub fn disable_interrupts(&self) { + // Safety: We own the UART structure + disable_tx_interrupts(self.regs_priv()); + } + + /// Low level function to write a word to the UART FIFO. + /// + /// Uses the [nb] API to allow usage in blocking and non-blocking contexts. + /// + /// Please note that you might have to mask the returned value with 0xff to retrieve the actual + /// value if you use the manual parity mode. See chapter 11.4.1 for more information. + #[inline(always)] + pub fn write_fifo(&mut self, data: u32) -> nb::Result<(), Infallible> { + if self.regs_priv().txstatus().read().wrrdy().bit_is_clear() { + return Err(nb::Error::WouldBlock); + } + self.write_fifo_unchecked(data); + Ok(()) + } + + /// Low level function to write a word to the UART FIFO. + /// + /// This does not necesarily mean that the FIFO can process another word because it might be + /// full. + /// Use the [Self::write_fifo] function to write a word to the FIFO reliably using the [nb] + /// API. + #[inline(always)] + pub fn write_fifo_unchecked(&mut self, data: u32) { + self.regs_priv().data().write(|w| unsafe { w.bits(data) }); + } + + pub fn into_async(self) -> TxAsync { + TxAsync::new(self) + } +} + +impl embedded_io::ErrorType for Tx { + type Error = Infallible; +} + +impl embedded_hal_nb::serial::ErrorType for Tx { + type Error = Infallible; +} + +impl embedded_hal_nb::serial::Write for Tx { + fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { + self.write_fifo(word as u32) + } + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + // SAFETY: Only TX related registers are used. + let reader = self.regs_priv().txstatus().read(); + if reader.wrbusy().bit_is_set() { + return Err(nb::Error::WouldBlock); + } + Ok(()) + } +} + +impl embedded_io::Write for Tx { + fn write(&mut self, buf: &[u8]) -> Result { + if buf.is_empty() { + return Ok(0); + } + loop { + if self.regs_priv().txstatus().read().wrrdy().bit_is_set() { + break; + } + } + let mut written = 0; + for byte in buf.iter() { + match >::write(self, *byte) { + Ok(_) => written += 1, + Err(nb::Error::WouldBlock) => return Ok(written), + } + } + + Ok(written) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + nb::block!(>::flush(self)) + } +} + +/// Serial receiver, using interrupts to offload reading to the hardware. +/// +/// You can use [Rx::into_rx_with_irq] to convert a normal [Rx] structure into this structure. +/// This structure provides two distinct ways to read the UART RX using interrupts. It should +/// be noted that the interrupt service routine (ISR) still has to be provided by the user. However, +/// this structure provides API calls which can be used inside the ISRs to simplify the reading +/// of the UART. +/// +/// 1. The first way simply empties the FIFO on an interrupt into a user provided buffer. You +/// can simply use [Self::start] to prepare the peripheral and then call the +/// [Self::on_interrupt] in the interrupt service routine. +/// 2. The second way reads packets bounded by a maximum size or a baudtick based timeout. You +/// can use [Self::read_fixed_len_or_timeout_based_using_irq] to prepare the peripheral and +/// then call the [Self::on_interrupt_max_size_or_timeout_based] in the interrupt service +/// routine. You have to call [Self::read_fixed_len_or_timeout_based_using_irq] in the ISR to +/// start reading the next packet. +pub struct RxWithInterrupt(Rx); + +impl RxWithInterrupt { + pub fn new(rx: Rx) -> Self { + Self(rx) + } + + /// This function should be called once at initialization time if the regular + /// [Self::on_interrupt] is used to read the UART receiver to enable and start the receiver. + pub fn start(&mut self) { + self.0.enable(); + self.enable_rx_irq_sources(true); + } + + #[inline(always)] + pub fn rx(&self) -> &Rx { + &self.0 + } + + /// This function is used together with the [Self::on_interrupt_max_size_or_timeout_based] + /// function to read packets with a maximum size or variable sized packets by using the + /// receive timeout of the hardware. + /// + /// This function should be called once at initialization to initiate the context state + /// and to [Self::start] the receiver. After that, it should be called after each + /// completed [Self::on_interrupt_max_size_or_timeout_based] call to restart the reception + /// of a packet. + pub fn read_fixed_len_or_timeout_based_using_irq( + &mut self, + context: &mut IrqContextTimeoutOrMaxSize, + ) -> Result<(), TransferPendingError> { + if context.mode != IrqReceptionMode::Idle { + return Err(TransferPendingError); + } + context.mode = IrqReceptionMode::Pending; + context.rx_idx = 0; + self.start(); + Ok(()) + } + + #[inline] + fn enable_rx_irq_sources(&mut self, timeout: bool) { + self.rx().regs_priv().irq_enb().modify(|_, w| { + if timeout { + w.irq_rx_to().set_bit(); + } + w.irq_rx_status().set_bit(); + w.irq_rx().set_bit() + }); + } + + #[inline] + fn disable_rx_irq_sources(&mut self) { + self.rx().regs_priv().irq_enb().modify(|_, w| { + w.irq_rx_to().clear_bit(); + w.irq_rx_status().clear_bit(); + w.irq_rx().clear_bit() + }); + } + + pub fn cancel_transfer(&mut self) { + self.disable_rx_irq_sources(); + self.0.clear_fifo(); + } + + /// This function should be called in the user provided UART interrupt handler. + /// + /// It simply empties any bytes in the FIFO into the user provided buffer and returns the + /// result of the operation. + /// + /// This function will not disable the RX interrupts, so you don't need to call any other + /// API after calling this function to continue emptying the FIFO. RX errors are handled + /// as partial errors and are returned as part of the [IrqResult]. + pub fn on_interrupt(&mut self, buf: &mut [u8; 16]) -> IrqResult { + let mut result = IrqResult::default(); + + let rx = self.rx(); + let irq_end = rx.regs_priv().irq_end().read(); + let enb_status = rx.regs_priv().enable().read(); + let rx_enabled = enb_status.rxenable().bit_is_set(); + + // Half-Full interrupt. We have a guaranteed amount of data we can read. + if irq_end.irq_rx().bit_is_set() { + let available_bytes = rx.regs_priv().rxfifoirqtrg().read().bits() as usize; + + // If this interrupt bit is set, the trigger level is available at the very least. + // Read everything as fast as possible + for _ in 0..available_bytes { + buf[result.bytes_read] = (rx.regs_priv().data().read().bits() & 0xff) as u8; + result.bytes_read += 1; + } + } + + // Timeout, empty the FIFO completely. + if irq_end.irq_rx_to().bit_is_set() { + // While there is data in the FIFO, write it into the reception buffer + while let Ok(byte) = self.0.read_fifo() { + buf[result.bytes_read] = byte as u8; + result.bytes_read += 1; + } + } + + // RX transfer not complete, check for RX errors + if rx_enabled { + self.check_for_errors(&mut result.errors); + } + + // Clear the interrupt status bits + self.0 + .regs_priv() + .irq_clr() + .write(|w| unsafe { w.bits(irq_end.bits()) }); + result + } + + /// This function should be called in the user provided UART interrupt handler. + /// + /// This function is used to read packets which either have a maximum size or variable sized + /// packet which are bounded by sufficient delays between them, triggering a hardware timeout. + /// + /// If either the maximum number of packets have been read or a timeout occured, the transfer + /// will be deemed completed. The state information of the transfer is tracked in the + /// [IrqContextTimeoutOrMaxSize] structure. + /// + /// If passed buffer is equal to or larger than the specified maximum length, an + /// [BufferTooShortError] will be returned. Other RX errors are treated as partial errors + /// and returned inside the [IrqResultMaxSizeOrTimeout] structure. + pub fn on_interrupt_max_size_or_timeout_based( + &mut self, + context: &mut IrqContextTimeoutOrMaxSize, + buf: &mut [u8], + ) -> Result { + if buf.len() < context.max_len { + return Err(BufferTooShortError { + found: buf.len(), + expected: context.max_len, + }); + } + let mut result = IrqResultMaxSizeOrTimeout::default(); + let rx = self.rx(); + + let irq_end = rx.regs_priv().irq_end().read(); + let enb_status = rx.regs_priv().enable().read(); + let rx_enabled = enb_status.rxenable().bit_is_set(); + + // Half-Full interrupt. We have a guaranteed amount of data we can read. + if irq_end.irq_rx().bit_is_set() { + // Determine the number of bytes to read, ensuring we leave 1 byte in the FIFO. + // We use this trick/hack because the timeout feature of the peripheral relies on data + // being in the RX FIFO. If data continues arriving, another half-full IRQ will fire. + // If not, the last byte(s) is/are emptied by the timeout interrupt. + let available_bytes = rx.regs_priv().rxfifoirqtrg().read().bits() as usize; + + let bytes_to_read = core::cmp::min( + available_bytes.saturating_sub(1), + context.max_len - context.rx_idx, + ); + + // If this interrupt bit is set, the trigger level is available at the very least. + // Read everything as fast as possible + for _ in 0..bytes_to_read { + buf[context.rx_idx] = (rx.regs_priv().data().read().bits() & 0xff) as u8; + context.rx_idx += 1; + } + + // On high-baudrates, data might be available immediately, and we possible have to + // read continuosly? Then again, the CPU should always be faster than that. I'd rather + // rely on the hardware firing another IRQ. I have not tried baudrates higher than + // 115200 so far. + } + // Timeout, empty the FIFO completely. + if irq_end.irq_rx_to().bit_is_set() { + // While there is data in the FIFO, write it into the reception buffer + loop { + if context.rx_idx == context.max_len { + break; + } + // While there is data in the FIFO, write it into the reception buffer + match self.0.read() { + Ok(byte) => { + buf[result.bytes_read] = byte; + result.bytes_read += 1; + } + Err(_) => break, + } + } + self.irq_completion_handler_max_size_timeout(&mut result, context); + return Ok(result); + } + + // RX transfer not complete, check for RX errors + if (context.rx_idx < context.max_len) && rx_enabled { + self.check_for_errors(&mut result.errors); + } + + // Clear the interrupt status bits + rx.regs_priv() + .irq_clr() + .write(|w| unsafe { w.bits(irq_end.bits()) }); + Ok(result) + } + + fn check_for_errors(&self, errors: &mut Option) { + let rx_status = self.rx().regs_priv().rxstatus().read(); + + if rx_status.rxovr().bit_is_set() + || rx_status.rxfrm().bit_is_set() + || rx_status.rxpar().bit_is_set() + { + let err = errors.get_or_insert(UartErrors::default()); + + if rx_status.rxovr().bit_is_set() { + err.overflow = true; + } + if rx_status.rxfrm().bit_is_set() { + err.framing = true; + } + if rx_status.rxpar().bit_is_set() { + err.parity = true; + } + } + } + + fn irq_completion_handler_max_size_timeout( + &mut self, + res: &mut IrqResultMaxSizeOrTimeout, + context: &mut IrqContextTimeoutOrMaxSize, + ) { + self.disable_rx_irq_sources(); + self.0.disable(); + res.bytes_read = context.rx_idx; + res.complete = true; + context.mode = IrqReceptionMode::Idle; + context.rx_idx = 0; + } + + /// # Safety + /// + /// This API allows creating multiple UART instances when releasing the TX structure as well. + /// The user must ensure that these instances are not used to create multiple overlapping + /// UART drivers. + pub unsafe fn release(mut self) -> Rx { + self.disable_rx_irq_sources(); + self.0 + } +} + +pub mod tx_asynch; +pub use tx_asynch::*; + +pub mod rx_asynch; +pub use rx_asynch::*; diff --git a/va108xx-hal/src/uart/rx_asynch.rs b/va108xx-hal/src/uart_tmp/rx_asynch.rs similarity index 100% rename from va108xx-hal/src/uart/rx_asynch.rs rename to va108xx-hal/src/uart_tmp/rx_asynch.rs diff --git a/va108xx-hal/src/uart/tx_asynch.rs b/va108xx-hal/src/uart_tmp/tx_asynch.rs similarity index 100% rename from va108xx-hal/src/uart/tx_asynch.rs rename to va108xx-hal/src/uart_tmp/tx_asynch.rs diff --git a/vorago-shared-periphs/Cargo.toml b/vorago-shared-periphs/Cargo.toml index f9d3376..3b724bf 100644 --- a/vorago-shared-periphs/Cargo.toml +++ b/vorago-shared-periphs/Cargo.toml @@ -11,10 +11,18 @@ derive-mmio = { path = "../../../ROMEO/derive-mmio" } bitbybit = "1.3" arbitrary-int = "1.3" static_assertions = "1.1" +nb = "1" +heapless = "0.8" +critical-section = "1" embedded-hal = { version = "1.0" } embedded-hal-async = "1" +embedded-hal-nb = "1" +embedded-io = "0.6" +embedded-io-async = "0.6" +raw-slicee = "0.1" thiserror = { version = "2", default-features = false } paste = "1" +fugit = "0.3" embassy-sync = "0.6" defmt = { version = "1", optional = true } va108xx = { version = "0.5", default-features = false, optional = true } diff --git a/vorago-shared-periphs/src/gpio/asynch.rs b/vorago-shared-periphs/src/gpio/asynch.rs index 6f9c607..ea32286 100644 --- a/vorago-shared-periphs/src/gpio/asynch.rs +++ b/vorago-shared-periphs/src/gpio/asynch.rs @@ -37,7 +37,7 @@ static EDGE_DETECTION_PORT_B: [AtomicBool; NUM_PORT_B] = pub fn on_interrupt_for_async_gpio_for_port(port: Port) { let gpio = unsafe { port.steal_gpio() }; - let irq_enb = gpio.read_irq_enb(); + let irq_enb = gpio.read_irq_enable(); let edge_status = gpio.read_edge_status(); let (wakers, edge_detection) = match port { Port::A => (WAKERS_FOR_PORT_A.as_ref(), EDGE_DETECTION_PORT_A.as_ref()), diff --git a/vorago-shared-periphs/src/gpio/ll.rs b/vorago-shared-periphs/src/gpio/ll.rs index 1894102..e1c2b96 100644 --- a/vorago-shared-periphs/src/gpio/ll.rs +++ b/vorago-shared-periphs/src/gpio/ll.rs @@ -255,7 +255,7 @@ impl LowLevelGpio { if irq_cfg.enable_in_nvic { unsafe { crate::enable_nvic_interrupt(irq_cfg.id) }; } - self.gpio.modify_irq_enb(|mut value| { + self.gpio.modify_irq_enable(|mut value| { value |= 1 << self.id.offset; value }); @@ -267,7 +267,7 @@ impl LowLevelGpio { self.reset_irqsel(); } // We only manipulate our own bit. - self.gpio.modify_irq_enb(|mut value| { + self.gpio.modify_irq_enable(|mut value| { value &= !(1 << self.id.offset); value }); diff --git a/vorago-shared-periphs/src/gpio/regs.rs b/vorago-shared-periphs/src/gpio/regs.rs index f87d178..e0ae9db 100644 --- a/vorago-shared-periphs/src/gpio/regs.rs +++ b/vorago-shared-periphs/src/gpio/regs.rs @@ -50,14 +50,14 @@ pub struct Gpio { irq_sen: u32, irq_edge: u32, irq_evt: u32, - irq_enb: u32, + irq_enable: u32, /// Raw interrupt status. This register is not latched and may not indicated edge sensitive /// events. #[mmio(PureRead)] irq_raw: u32, - /// Read only register which shows the AND of IRQ_RAW and IRQ_ENB. Called IRQ_END by Vorago. + /// Read-only register which shows enabled and active interrupts. Called IRQ_end by Vorago. #[mmio(PureRead)] - irq_active: u32, + irq_status: u32, #[mmio(PureRead)] edge_status: u32, diff --git a/vorago-shared-periphs/src/lib.rs b/vorago-shared-periphs/src/lib.rs index fd99c0b..28e320e 100644 --- a/vorago-shared-periphs/src/lib.rs +++ b/vorago-shared-periphs/src/lib.rs @@ -1,7 +1,12 @@ #![no_std] pub mod gpio; pub mod ioconfig; +pub mod pins; pub mod sysconfig; +pub mod time; +pub mod uart; + +pub use sysconfig::{disable_peripheral_clock, enable_peripheral_clock}; #[cfg(not(feature = "_family-selected"))] compile_error!("no Vorago CPU family was select. Choices: vor1x or vor4x"); diff --git a/vorago-shared-periphs/src/pins.rs b/vorago-shared-periphs/src/pins.rs new file mode 100644 index 0000000..940c35d --- /dev/null +++ b/vorago-shared-periphs/src/pins.rs @@ -0,0 +1,236 @@ +use crate::sysconfig::reset_peripheral_for_cycles; + +pub use crate::gpio::{Pin, PinId, PinIdProvider, Port}; + +use crate::PeripheralSelect; +use crate::sealed::Sealed; + +pub trait PinMarker: Sealed { + const ID: PinId; +} + +macro_rules! pin_id { + ($Id:ident, $Port:path, $num:literal) => { + // Need paste macro to use ident in doc attribute + paste::paste! { + #[doc = "Pin ID representing pin " $Id] + #[derive(Debug)] + pub enum $Id {} + + impl $crate::sealed::Sealed for $Id {} + impl PinIdProvider for $Id { + const ID: PinId = PinId::new_unchecked($Port, $num); + } + + impl PinMarker for Pin<$Id> { + const ID: PinId = $Id::ID; + } + } + }; +} + +impl Sealed for Pin {} + +pin_id!(Pa0, Port::A, 0); +pin_id!(Pa1, Port::A, 1); +pin_id!(Pa2, Port::A, 2); +pin_id!(Pa3, Port::A, 3); +pin_id!(Pa4, Port::A, 4); +pin_id!(Pa5, Port::A, 5); +pin_id!(Pa6, Port::A, 6); +pin_id!(Pa7, Port::A, 7); +pin_id!(Pa8, Port::A, 8); +pin_id!(Pa9, Port::A, 9); +pin_id!(Pa10, Port::A, 10); +pin_id!(Pa11, Port::A, 11); +pin_id!(Pa12, Port::A, 12); +pin_id!(Pa13, Port::A, 13); +pin_id!(Pa14, Port::A, 14); +pin_id!(Pa15, Port::A, 15); +pin_id!(Pa16, Port::A, 16); +pin_id!(Pa17, Port::A, 17); +pin_id!(Pa18, Port::A, 18); +pin_id!(Pa19, Port::A, 19); +pin_id!(Pa20, Port::A, 20); +pin_id!(Pa21, Port::A, 21); +pin_id!(Pa22, Port::A, 22); +pin_id!(Pa23, Port::A, 23); +pin_id!(Pa24, Port::A, 24); +pin_id!(Pa25, Port::A, 25); +pin_id!(Pa26, Port::A, 26); +pin_id!(Pa27, Port::A, 27); +pin_id!(Pa28, Port::A, 28); +pin_id!(Pa29, Port::A, 29); +pin_id!(Pa30, Port::A, 30); +pin_id!(Pa31, Port::A, 31); + +pin_id!(Pb0, Port::B, 0); +pin_id!(Pb1, Port::B, 1); +pin_id!(Pb2, Port::B, 2); +pin_id!(Pb3, Port::B, 3); +pin_id!(Pb4, Port::B, 4); +pin_id!(Pb5, Port::B, 5); +pin_id!(Pb6, Port::B, 6); +pin_id!(Pb7, Port::B, 7); +pin_id!(Pb8, Port::B, 8); +pin_id!(Pb9, Port::B, 9); +pin_id!(Pb10, Port::B, 10); +pin_id!(Pb11, Port::B, 11); +pin_id!(Pb12, Port::B, 12); +pin_id!(Pb13, Port::B, 13); +pin_id!(Pb14, Port::B, 14); +pin_id!(Pb15, Port::B, 15); +pin_id!(Pb16, Port::B, 16); +pin_id!(Pb17, Port::B, 17); +pin_id!(Pb18, Port::B, 18); +pin_id!(Pb19, Port::B, 19); +pin_id!(Pb20, Port::B, 20); +pin_id!(Pb21, Port::B, 21); +pin_id!(Pb22, Port::B, 22); +pin_id!(Pb23, Port::B, 23); + +pub struct PinsA { + pub pa0: Pin, + pub pa1: Pin, + pub pa2: Pin, + pub pa3: Pin, + pub pa4: Pin, + pub pa5: Pin, + pub pa6: Pin, + pub pa7: Pin, + pub pa8: Pin, + pub pa9: Pin, + pub pa10: Pin, + pub pa11: Pin, + pub pa12: Pin, + pub pa13: Pin, + pub pa14: Pin, + pub pa15: Pin, + pub pa16: Pin, + pub pa17: Pin, + pub pa18: Pin, + pub pa19: Pin, + pub pa20: Pin, + pub pa21: Pin, + pub pa22: Pin, + pub pa23: Pin, + pub pa24: Pin, + pub pa25: Pin, + pub pa26: Pin, + pub pa27: Pin, + pub pa28: Pin, + pub pa29: Pin, + pub pa30: Pin, + pub pa31: Pin, +} + +impl PinsA { + pub fn new(_port_a: va108xx::Porta) -> Self { + let syscfg = unsafe { va108xx::Sysconfig::steal() }; + reset_peripheral_for_cycles(PeripheralSelect::PortA, 2); + syscfg.peripheral_clk_enable().modify(|_, w| { + w.porta().set_bit(); + w.gpio().set_bit(); + w.ioconfig().set_bit() + }); + Self { + pa0: Pin::__new(), + pa1: Pin::__new(), + pa2: Pin::__new(), + pa3: Pin::__new(), + pa4: Pin::__new(), + pa5: Pin::__new(), + pa6: Pin::__new(), + pa7: Pin::__new(), + pa8: Pin::__new(), + pa9: Pin::__new(), + pa10: Pin::__new(), + pa11: Pin::__new(), + pa12: Pin::__new(), + pa13: Pin::__new(), + pa14: Pin::__new(), + pa15: Pin::__new(), + pa16: Pin::__new(), + pa17: Pin::__new(), + pa18: Pin::__new(), + pa19: Pin::__new(), + pa20: Pin::__new(), + pa21: Pin::__new(), + pa22: Pin::__new(), + pa23: Pin::__new(), + pa24: Pin::__new(), + pa25: Pin::__new(), + pa26: Pin::__new(), + pa27: Pin::__new(), + pa28: Pin::__new(), + pa29: Pin::__new(), + pa30: Pin::__new(), + pa31: Pin::__new(), + } + } +} + +pub struct PinsB { + pub pb0: Pin, + pub pb1: Pin, + pub pb2: Pin, + pub pb3: Pin, + pub pb4: Pin, + pub pb5: Pin, + pub pb6: Pin, + pub pb7: Pin, + pub pb8: Pin, + pub pb9: Pin, + pub pb10: Pin, + pub pb11: Pin, + pub pb12: Pin, + pub pb13: Pin, + pub pb14: Pin, + pub pb15: Pin, + pub pb16: Pin, + pub pb17: Pin, + pub pb18: Pin, + pub pb19: Pin, + pub pb20: Pin, + pub pb21: Pin, + pub pb22: Pin, + pub pb23: Pin, +} + +impl PinsB { + pub fn new(_port_b: va108xx::Portb) -> Self { + let syscfg = unsafe { va108xx::Sysconfig::steal() }; + reset_peripheral_for_cycles(PeripheralSelect::PortB, 2); + syscfg.peripheral_clk_enable().modify(|_, w| { + w.portb().set_bit(); + w.gpio().set_bit(); + w.ioconfig().set_bit() + }); + Self { + pb0: Pin::__new(), + pb1: Pin::__new(), + pb2: Pin::__new(), + pb3: Pin::__new(), + pb4: Pin::__new(), + pb5: Pin::__new(), + pb6: Pin::__new(), + pb7: Pin::__new(), + pb8: Pin::__new(), + pb9: Pin::__new(), + pb10: Pin::__new(), + pb11: Pin::__new(), + pb12: Pin::__new(), + pb13: Pin::__new(), + pb14: Pin::__new(), + pb15: Pin::__new(), + pb16: Pin::__new(), + pb17: Pin::__new(), + pb18: Pin::__new(), + pb19: Pin::__new(), + pb20: Pin::__new(), + pb21: Pin::__new(), + pb22: Pin::__new(), + pb23: Pin::__new(), + } + } +} diff --git a/vorago-shared-periphs/src/time.rs b/vorago-shared-periphs/src/time.rs new file mode 100644 index 0000000..9808028 --- /dev/null +++ b/vorago-shared-periphs/src/time.rs @@ -0,0 +1,26 @@ +//! Time units + +// Frequency based + +/// Hertz +pub type Hertz = fugit::HertzU32; + +/// KiloHertz +pub type KiloHertz = fugit::KilohertzU32; + +/// MegaHertz +pub type MegaHertz = fugit::MegahertzU32; + +// Period based + +/// Seconds +pub type Seconds = fugit::SecsDurationU32; + +/// Milliseconds +pub type Milliseconds = fugit::MillisDurationU32; + +/// Microseconds +pub type Microseconds = fugit::MicrosDurationU32; + +/// Nanoseconds +pub type Nanoseconds = fugit::NanosDurationU32; diff --git a/vorago-shared-periphs/src/uart/mod.rs b/vorago-shared-periphs/src/uart/mod.rs new file mode 100644 index 0000000..4528830 --- /dev/null +++ b/vorago-shared-periphs/src/uart/mod.rs @@ -0,0 +1,1261 @@ +//! # API for the UART peripheral +//! +//! The core of this API are the [Uart], [Rx] and [Tx] structures. +//! The RX structure also has a dedicated [RxWithInterrupt] variant which allows reading the receiver +//! using interrupts. +//! +//! The [rx_asynch] and [tx_asynch] modules provide an asynchronous non-blocking API for the UART +//! peripheral. +//! +//! ## Examples +//! +//! - [UART simple example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/uart.rs) +//! - [UART with IRQ and RTIC](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/rtic/src/bin/uart-echo-rtic.rs) +//! - [Flashloader exposing a CCSDS interface via UART](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/flashloader) +use core::{convert::Infallible, ops::Deref}; +pub mod regs; +use crate::{FunSel, InterruptConfig, gpio::IoPeriphPin, pins::PinMarker}; +use arbitrary_int::{Number, u6, u18}; +use fugit::RateExtU32; +use regs::{ClkScale, Control, Data, Enable, FifoClear, InterruptClear, MmioUart}; + +use crate::{PeripheralSelect, enable_nvic_interrupt, enable_peripheral_clock, time::Hertz}; +use embedded_hal_nb::serial::Read; +pub use regs::{Bank, Stopbits, WordSize}; + +#[cfg(feature = "vor1x")] +mod pins_vor1x; + +//================================================================================================== +// Type-Level support +//================================================================================================== + +pub trait TxPin: PinMarker { + const BANK: Bank; + const FUN_SEL: FunSel; +} +pub trait RxPin: PinMarker { + const BANK: Bank; + const FUN_SEL: FunSel; +} + +//================================================================================================== +// Regular Definitions +//================================================================================================== + +#[derive(Debug, PartialEq, Eq, thiserror::Error)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[error("no interrupt ID was set")] +pub struct NoInterruptIdWasSet; + +#[derive(Debug, PartialEq, Eq, thiserror::Error)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[error("transer is pending")] +pub struct TransferPendingError; + +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Event { + // Receiver FIFO interrupt enable. Generates interrupt + // when FIFO is at least half full. Half full is defined as FIFO + // count >= RXFIFOIRQTRG + RxFifoHalfFull, + // Framing error, Overrun error, Parity Error and Break error + RxError, + // Event for timeout condition: Data in the FIFO and no receiver + // FIFO activity for 4 character times + RxTimeout, + + // Transmitter FIFO interrupt enable. Generates interrupt + // when FIFO is at least half full. Half full is defined as FIFO + // count >= TXFIFOIRQTRG + TxFifoHalfFull, + // FIFO overflow error + TxError, + // Generate interrupt when transmit FIFO is empty and TXBUSY is 0 + TxEmpty, + // Interrupt when CTSn changes value + TxCts, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Parity { + None, + Odd, + Even, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct Config { + pub baudrate: Hertz, + pub parity: Parity, + pub stopbits: Stopbits, + // When false, use standard 16x baud clock, other 8x baud clock + pub baud8: bool, + pub wordsize: WordSize, + pub enable_tx: bool, + pub enable_rx: bool, +} + +impl Config { + pub fn baudrate(mut self, baudrate: Hertz) -> Self { + self.baudrate = baudrate; + self + } + + pub fn parity_none(mut self) -> Self { + self.parity = Parity::None; + self + } + + pub fn parity_even(mut self) -> Self { + self.parity = Parity::Even; + self + } + + pub fn parity_odd(mut self) -> Self { + self.parity = Parity::Odd; + self + } + + pub fn stopbits(mut self, stopbits: Stopbits) -> Self { + self.stopbits = stopbits; + self + } + + pub fn wordsize(mut self, wordsize: WordSize) -> Self { + self.wordsize = wordsize; + self + } + + pub fn baud8(mut self, baud: bool) -> Self { + self.baud8 = baud; + self + } +} + +impl Default for Config { + fn default() -> Config { + let baudrate = 115_200_u32.Hz(); + Config { + baudrate, + parity: Parity::None, + stopbits: Stopbits::One, + baud8: false, + wordsize: WordSize::Eight, + enable_tx: true, + enable_rx: true, + } + } +} + +impl From for Config { + fn from(baud: Hertz) -> Self { + Config::default().baudrate(baud) + } +} + +//================================================================================================== +// IRQ Definitions +//================================================================================================== + +#[derive(Debug, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct IrqContextTimeoutOrMaxSize { + rx_idx: usize, + mode: IrqReceptionMode, + pub max_len: usize, +} + +impl IrqContextTimeoutOrMaxSize { + pub fn new(max_len: usize) -> Self { + IrqContextTimeoutOrMaxSize { + rx_idx: 0, + max_len, + mode: IrqReceptionMode::Idle, + } + } +} + +impl IrqContextTimeoutOrMaxSize { + pub fn reset(&mut self) { + self.rx_idx = 0; + self.mode = IrqReceptionMode::Idle; + } +} + +/// This struct is used to return the default IRQ handler result to the user +#[derive(Debug, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct IrqResult { + pub bytes_read: usize, + pub errors: Option, +} + +/// This struct is used to return the default IRQ handler result to the user +#[derive(Debug, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct IrqResultMaxSizeOrTimeout { + complete: bool, + timeout: bool, + pub errors: Option, + pub bytes_read: usize, +} + +impl IrqResultMaxSizeOrTimeout { + pub fn new() -> Self { + IrqResultMaxSizeOrTimeout { + complete: false, + timeout: false, + errors: None, + bytes_read: 0, + } + } +} +impl IrqResultMaxSizeOrTimeout { + #[inline] + pub fn has_errors(&self) -> bool { + self.errors.is_some() + } + + #[inline] + pub fn overflow_error(&self) -> bool { + self.errors.is_some_and(|e| e.overflow) + } + + #[inline] + pub fn framing_error(&self) -> bool { + self.errors.is_some_and(|e| e.framing) + } + + #[inline] + pub fn parity_error(&self) -> bool { + self.errors.is_some_and(|e| e.parity) + } + + #[inline] + pub fn timeout(&self) -> bool { + self.timeout + } + + #[inline] + pub fn complete(&self) -> bool { + self.complete + } +} + +#[derive(Debug, PartialEq, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +enum IrqReceptionMode { + Idle, + Pending, +} + +#[derive(Default, Debug, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct UartErrors { + overflow: bool, + framing: bool, + parity: bool, + other: bool, +} + +impl UartErrors { + #[inline(always)] + pub fn overflow(&self) -> bool { + self.overflow + } + + #[inline(always)] + pub fn framing(&self) -> bool { + self.framing + } + + #[inline(always)] + pub fn parity(&self) -> bool { + self.parity + } + + #[inline(always)] + pub fn other(&self) -> bool { + self.other + } +} + +impl UartErrors { + #[inline(always)] + pub fn error(&self) -> bool { + self.overflow || self.framing || self.parity || self.other + } +} + +#[derive(Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct BufferTooShortError { + found: usize, + expected: usize, +} + +//================================================================================================== +// UART peripheral wrapper +//================================================================================================== +use va108xx::uarta as uart_base; + +pub trait UartPeripheralMarker: Deref { + const ID: Bank; + const PERIPH_SEL: PeripheralSelect; + const PTR: *const uart_base::RegisterBlock; + + /* + /// Retrieve the peripheral structure. + /// + /// # Safety + /// + /// This circumvents the safety guarantees of the HAL. + unsafe fn steal() -> Self; + + #[inline(always)] + fn ptr() -> *const uart_base::RegisterBlock { + Self::PTR + } + + /// Retrieve the type erased peripheral register block. + /// + /// # Safety + /// + /// This circumvents the safety guarantees of the HAL. + #[inline(always)] + unsafe fn reg_block() -> &'static uart_base::RegisterBlock { + unsafe { &(*Self::ptr()) } + } + */ +} + +impl UartPeripheralMarker for va108xx::Uarta { + const ID: Bank = Bank::Uart0; + + const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart0; + const PTR: *const uart_base::RegisterBlock = Self::PTR; + + /* + #[inline(always)] + unsafe fn steal() -> Self { + Self::steal() + } + */ +} + +impl UartPeripheralMarker for va108xx::Uartb { + const ID: Bank = Bank::Uart1; + + const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart1; + const PTR: *const uart_base::RegisterBlock = Self::PTR; + + /* + #[inline(always)] + unsafe fn steal() -> Self { + Self::steal() + } + */ +} + +#[derive(Debug, thiserror::Error)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[error("UART ID missmatch between peripheral and pins.")] +pub struct UartIdMissmatchError; + +//================================================================================================== +// UART implementation +//================================================================================================== + +/// UART driver structure. +pub struct Uart { + tx: Tx, + rx: Rx, +} + +impl Uart { + /// Calls [Self::new] with the interrupt configuration to some valid value. + pub fn new_with_interrupt( + sys_clk: Hertz, + uart: UartI, + pins: (Tx, Rx), + config: Config, + irq_cfg: InterruptConfig, + ) -> Result { + Self::new(sys_clk, uart, pins, config, Some(irq_cfg)) + } + + /// Calls [Self::new] with the interrupt configuration to [None]. + pub fn new_without_interrupt( + sys_clk: Hertz, + uart: UartI, + pins: (Tx, Rx), + config: Config, + ) -> Result { + Self::new(sys_clk, uart, pins, config, None) + } + + /// Create a new UART peripheral with an interrupt configuration. + /// + /// # Arguments + /// + /// - `syscfg`: The system configuration register block + /// - `sys_clk`: The system clock frequency + /// - `uart`: The concrete UART peripheral instance. + /// - `pins`: UART TX and RX pin tuple. + /// - `config`: UART specific configuration parameters like baudrate. + /// - `irq_cfg`: Optional interrupt configuration. This should be a valid value if the plan + /// is to use TX or RX functionality relying on interrupts. If only the blocking API without + /// any interrupt support is used, this can be [None]. + pub fn new( + sys_clk: Hertz, + _uart: UartI, + _pins: (TxPinI, RxPinI), + config: Config, + opt_irq_cfg: Option, + ) -> Result { + if UartI::ID != TxPinI::BANK || UartI::ID != RxPinI::BANK { + return Err(UartIdMissmatchError); + } + IoPeriphPin::new(TxPinI::ID, TxPinI::FUN_SEL, None); + IoPeriphPin::new(RxPinI::ID, TxPinI::FUN_SEL, None); + enable_peripheral_clock(UartI::PERIPH_SEL); + + let mut reg_block = regs::Uart::new_mmio(UartI::ID); + let baud_multiplier = match config.baud8 { + false => 16, + true => 8, + }; + + // This is the calculation: (64.0 * (x - integer_part as f32) + 0.5) as u32 without floating + // point calculations. + let frac = ((sys_clk.raw() % (config.baudrate.raw() * 16)) * 64 + + (config.baudrate.raw() * 8)) + / (config.baudrate.raw() * 16); + // Calculations here are derived from chapter 4.8.5 (p.79) of the datasheet. + let x = sys_clk.raw() as f32 / (config.baudrate.raw() * baud_multiplier) as f32; + let integer_part = x as u32; + reg_block.write_clkscale( + ClkScale::builder() + .with_int(u18::new(integer_part)) + .with_frac(u6::new(frac as u8)) + .build(), + ); + + let (paren, pareven) = match config.parity { + Parity::None => (false, false), + Parity::Odd => (true, false), + Parity::Even => (true, true), + }; + reg_block.write_ctrl( + Control::builder() + .with_baud8(config.baud8) + .with_auto_rts(false) + .with_def_rts(false) + .with_auto_cts(false) + .with_loopback_block(false) + .with_loopback(false) + .with_wordsize(config.wordsize) + .with_stopbits(config.stopbits) + .with_parity_manual(false) + .with_parity_even(pareven) + .with_parity_enable(paren) + .build(), + ); + // Clear the FIFO + reg_block.write_fifo_clr(FifoClear::builder().with_tx(true).with_rx(true).build()); + reg_block.write_enable( + Enable::builder() + .with_tx(config.enable_tx) + .with_rx(config.enable_rx) + .build(), + ); + + // TODO: VA108xx specific + if let Some(irq_cfg) = opt_irq_cfg { + if irq_cfg.route { + enable_peripheral_clock(PeripheralSelect::Irqsel); + unsafe { va108xx::Irqsel::steal() } + .uart0(UartI::ID as usize) + .write(|w| unsafe { w.bits(irq_cfg.id as u32) }); + } + if irq_cfg.enable_in_nvic { + // Safety: User has specifically configured this. + unsafe { enable_nvic_interrupt(irq_cfg.id) }; + } + } + + Ok(Uart { + tx: Tx::new(UartI::ID), + rx: Rx::new(UartI::ID), + }) + } + + #[inline] + pub fn enable_rx(&mut self) { + self.rx.enable(); + } + + #[inline] + pub fn disable_rx(&mut self) { + self.rx.disable(); + } + + #[inline] + pub fn enable_tx(&mut self) { + self.tx.enable(); + } + + #[inline] + pub fn disable_tx(&mut self) { + self.tx.disable(); + } + + /// This also clears status conditons for the RX FIFO. + #[inline] + pub fn clear_rx_fifo(&mut self) { + self.rx.clear_fifo(); + } + + /// This also clears status conditons for the TX FIFO. + #[inline] + pub fn clear_tx_fifo(&mut self) { + self.tx.clear_fifo(); + } + + pub fn listen(&mut self, event: Event) { + self.tx.regs.modify_irq_enabled(|mut value| { + match event { + Event::RxError => value.set_rx_status(true), + Event::RxFifoHalfFull => value.set_rx(true), + Event::RxTimeout => value.set_rx_timeout(true), + Event::TxEmpty => value.set_tx_empty(true), + Event::TxError => value.set_tx_status(true), + Event::TxFifoHalfFull => value.set_tx(true), + Event::TxCts => value.set_tx_cts(true), + } + value + }); + } + + pub fn unlisten(&mut self, event: Event) { + self.tx.regs.modify_irq_enabled(|mut value| { + match event { + Event::RxError => value.set_rx_status(false), + Event::RxFifoHalfFull => value.set_rx(false), + Event::RxTimeout => value.set_rx_timeout(false), + Event::TxEmpty => value.set_tx_empty(false), + Event::TxError => value.set_tx_status(false), + Event::TxFifoHalfFull => value.set_tx(false), + Event::TxCts => value.set_tx_cts(false), + } + value + }); + } + + /// Poll receiver errors. + pub fn poll_rx_errors(&self) -> Option { + self.rx.poll_errors() + } + + pub fn split(self) -> (Tx, Rx) { + (self.tx, self.rx) + } +} + +impl embedded_io::ErrorType for Uart { + type Error = Infallible; +} + +impl embedded_hal_nb::serial::ErrorType for Uart { + type Error = Infallible; +} + +impl embedded_hal_nb::serial::Read for Uart { + fn read(&mut self) -> nb::Result { + self.rx.read() + } +} + +impl embedded_hal_nb::serial::Write for Uart { + fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { + self.tx.write(word).map_err(|e| { + if let nb::Error::Other(_) = e { + unreachable!() + } + nb::Error::WouldBlock + }) + } + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + self.tx.flush().map_err(|e| { + if let nb::Error::Other(_) = e { + unreachable!() + } + nb::Error::WouldBlock + }) + } +} + +#[inline(always)] +pub fn enable_rx(uart: &mut MmioUart<'static>) { + uart.modify_enable(|mut value| { + value.set_rx(true); + value + }); +} + +#[inline(always)] +pub fn disable_rx(uart: &mut MmioUart<'static>) { + uart.modify_enable(|mut value| { + value.set_rx(false); + value + }); +} + +#[inline(always)] +pub fn enable_rx_interrupts(uart: &mut MmioUart<'static>, timeout: bool) { + uart.modify_irq_enabled(|mut value| { + value.set_rx_status(true); + value.set_rx(true); + if timeout { + value.set_rx_timeout(true); + } + value + }); +} + +#[inline(always)] +pub fn disable_rx_interrupts(uart: &mut MmioUart<'static>) { + uart.modify_irq_enabled(|mut value| { + value.set_rx_status(false); + value.set_rx(false); + value.set_rx_timeout(false); + value + }); +} + +/// Serial receiver. +/// +/// Can be created by using the [Uart::split] API. +pub struct Rx { + id: Bank, + regs: regs::MmioUart<'static>, +} + +impl Rx { + /// Retrieve a TX pin without expecting an explicit UART structure + /// + /// # Safety + /// + /// Circumvents the HAL safety guarantees. + #[inline(always)] + pub unsafe fn steal(id: Bank) -> Self { + Self::new(id) + } + + #[inline(always)] + fn new(id: Bank) -> Self { + Self { + id, + regs: regs::Uart::new_mmio(id), + } + } + + /* + /// Direct access to the peripheral structure. + /// + /// # Safety + /// + /// You must ensure that only registers related to the operation of the RX side are used. + #[inline(always)] + pub const unsafe fn regs(&self) -> &'static uart_base::RegisterBlock { + self.regs_priv() + } + + #[inline(always)] + const fn regs_priv(&self) -> &'static uart_base::RegisterBlock { + unsafe { self.0.reg_block() } + } + */ + + pub fn poll_errors(&self) -> Option { + let mut errors = UartErrors::default(); + + let status = self.regs.read_rx_status(); + if status.overrun_error() { + errors.overflow = true; + } else if status.framing_error() { + errors.framing = true; + } else if status.parity_error() { + errors.parity = true; + } else { + return None; + }; + Some(errors) + } + + #[inline] + pub fn clear_fifo(&mut self) { + self.regs + .write_fifo_clr(FifoClear::builder().with_tx(false).with_rx(true).build()); + } + + #[inline] + pub fn disable_interrupts(&mut self) { + disable_rx_interrupts(&mut self.regs); + } + #[inline] + pub fn enable_interrupts(&mut self, timeout: bool) { + enable_rx_interrupts(&mut self.regs, timeout); + } + + #[inline] + pub fn enable(&mut self) { + enable_rx(&mut self.regs); + } + + #[inline] + pub fn disable(&mut self) { + disable_rx(&mut self.regs); + } + + /// Low level function to read a word from the UART FIFO. + /// + /// Uses the [nb] API to allow usage in blocking and non-blocking contexts. + /// + /// Please note that you might have to mask the returned value with 0xff to retrieve the actual + /// value if you use the manual parity mode. See chapter 4.6.2 for more information. + #[inline(always)] + pub fn read_fifo(&mut self) -> nb::Result { + if !self.regs.read_rx_status().data_available() { + return Err(nb::Error::WouldBlock); + } + Ok(self.read_fifo_unchecked()) + } + + /// Low level function to read a word from from the UART FIFO. + /// + /// This does not necesarily mean there is a word in the FIFO available. + /// Use the [Self::read_fifo] function to read a word from the FIFO reliably using the [nb] + /// API. + /// + /// Please note that you might have to mask the returned value with 0xff to retrieve the actual + /// value if you use the manual parity mode. See chapter 4.6.2 for more information. + #[inline(always)] + pub fn read_fifo_unchecked(&mut self) -> u32 { + self.regs.read_data().raw_value() + } + + pub fn into_rx_with_irq(self) -> RxWithInterrupt { + RxWithInterrupt::new(self) + } +} + +impl embedded_io::ErrorType for Rx { + type Error = Infallible; +} + +impl embedded_hal_nb::serial::ErrorType for Rx { + type Error = Infallible; +} + +impl embedded_hal_nb::serial::Read for Rx { + fn read(&mut self) -> nb::Result { + self.read_fifo().map(|val| (val & 0xff) as u8).map_err(|e| { + if let nb::Error::Other(_) = e { + unreachable!() + } + nb::Error::WouldBlock + }) + } +} + +impl embedded_io::Read for Rx { + fn read(&mut self, buf: &mut [u8]) -> Result { + if buf.is_empty() { + return Ok(0); + } + let mut read = 0; + loop { + if self.regs.read_rx_status().data_available() { + break; + } + } + for byte in buf.iter_mut() { + match >::read(self) { + Ok(w) => { + *byte = w; + read += 1; + } + Err(nb::Error::WouldBlock) => break, + } + } + + Ok(read) + } +} + +#[inline(always)] +pub fn enable_tx(uart: &mut MmioUart<'static>) { + uart.modify_enable(|mut value| { + value.set_tx(true); + value + }); +} + +#[inline(always)] +pub fn disable_tx(uart: &mut MmioUart<'static>) { + uart.modify_enable(|mut value| { + value.set_tx(false); + value + }); +} + +#[inline(always)] +pub fn enable_tx_interrupts(uart: &mut MmioUart<'static>) { + uart.modify_irq_enabled(|mut value| { + value.set_tx(true); + value.set_tx_empty(true); + value.set_tx_status(true); + value + }); +} + +#[inline(always)] +pub fn disable_tx_interrupts(uart: &mut MmioUart<'static>) { + uart.modify_irq_enabled(|mut value| { + value.set_tx(false); + value.set_tx_empty(false); + value.set_tx_status(false); + value + }); +} + +/// Serial transmitter +/// +/// Can be created by using the [Uart::split] API. +pub struct Tx { + id: Bank, + regs: regs::MmioUart<'static>, +} + +impl Tx { + /// Retrieve a TX pin without expecting an explicit UART structure + /// + /// # Safety + /// + /// Circumvents the HAL safety guarantees. + #[inline(always)] + pub unsafe fn steal(id: Bank) -> Self { + Self::new(id) + } + + #[inline(always)] + fn new(id: Bank) -> Self { + Self { + id, + regs: regs::Uart::new_mmio(id), + } + } + + /* + /// Direct access to the peripheral structure. + /// + /// # Safety + /// + /// You must ensure that only registers related to the operation of the TX side are used. + #[inline(always)] + pub unsafe fn regs(&self) -> &'static uart_base::RegisterBlock { + self.0.reg_block() + } + + #[inline(always)] + fn regs_priv(&self) -> &'static uart_base::RegisterBlock { + unsafe { self.regs() } + } + */ + + #[inline] + pub fn clear_fifo(&mut self) { + self.regs + .write_fifo_clr(FifoClear::builder().with_tx(true).with_rx(false).build()); + } + + #[inline] + pub fn enable(&mut self) { + self.regs.modify_enable(|mut value| { + value.set_tx(true); + value + }); + } + + #[inline] + pub fn disable(&mut self) { + self.regs.modify_enable(|mut value| { + value.set_tx(false); + value + }); + } + + /// Enables the IRQ_TX, IRQ_TX_STATUS and IRQ_TX_EMPTY interrupts. + /// + /// - The IRQ_TX interrupt is generated when the TX FIFO is at least half empty. + /// - The IRQ_TX_STATUS interrupt is generated when write data is lost due to a FIFO overflow + /// - The IRQ_TX_EMPTY interrupt is generated when the TX FIFO is empty and the TXBUSY signal + /// is 0 + #[inline] + pub fn enable_interrupts(&mut self) { + // Safety: We own the UART structure + enable_tx_interrupts(&mut self.regs); + } + + /// Disables the IRQ_TX, IRQ_TX_STATUS and IRQ_TX_EMPTY interrupts. + /// + /// [Self::enable_interrupts] documents the interrupts. + #[inline] + pub fn disable_interrupts(&mut self) { + // Safety: We own the UART structure + disable_tx_interrupts(&mut self.regs); + } + + /// Low level function to write a word to the UART FIFO. + /// + /// Uses the [nb] API to allow usage in blocking and non-blocking contexts. + /// + /// Please note that you might have to mask the returned value with 0xff to retrieve the actual + /// value if you use the manual parity mode. See chapter 11.4.1 for more information. + #[inline(always)] + pub fn write_fifo(&mut self, data: u32) -> nb::Result<(), Infallible> { + if !self.regs.read_tx_status().ready() { + return Err(nb::Error::WouldBlock); + } + self.write_fifo_unchecked(data); + Ok(()) + } + + /// Low level function to write a word to the UART FIFO. + /// + /// This does not necesarily mean that the FIFO can process another word because it might be + /// full. + /// Use the [Self::write_fifo] function to write a word to the FIFO reliably using the [nb] + /// API. + #[inline(always)] + pub fn write_fifo_unchecked(&mut self, data: u32) { + self.regs.write_data(Data::new_with_raw_value(data)); + } + + pub fn into_async(self) -> TxAsync { + TxAsync::new(self) + } +} + +impl embedded_io::ErrorType for Tx { + type Error = Infallible; +} + +impl embedded_hal_nb::serial::ErrorType for Tx { + type Error = Infallible; +} + +impl embedded_hal_nb::serial::Write for Tx { + fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { + self.write_fifo(word as u32) + } + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + // SAFETY: Only TX related registers are used. + if self.regs.read_tx_status().write_busy() { + return Err(nb::Error::WouldBlock); + } + Ok(()) + } +} + +impl embedded_io::Write for Tx { + fn write(&mut self, buf: &[u8]) -> Result { + if buf.is_empty() { + return Ok(0); + } + loop { + if self.regs.read_tx_status().ready() { + break; + } + } + let mut written = 0; + for byte in buf.iter() { + match >::write(self, *byte) { + Ok(_) => written += 1, + Err(nb::Error::WouldBlock) => return Ok(written), + } + } + + Ok(written) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + nb::block!(>::flush(self)) + } +} + +/// Serial receiver, using interrupts to offload reading to the hardware. +/// +/// You can use [Rx::into_rx_with_irq] to convert a normal [Rx] structure into this structure. +/// This structure provides two distinct ways to read the UART RX using interrupts. It should +/// be noted that the interrupt service routine (ISR) still has to be provided by the user. However, +/// this structure provides API calls which can be used inside the ISRs to simplify the reading +/// of the UART. +/// +/// 1. The first way simply empties the FIFO on an interrupt into a user provided buffer. You +/// can simply use [Self::start] to prepare the peripheral and then call the +/// [Self::on_interrupt] in the interrupt service routine. +/// 2. The second way reads packets bounded by a maximum size or a baudtick based timeout. You +/// can use [Self::read_fixed_len_or_timeout_based_using_irq] to prepare the peripheral and +/// then call the [Self::on_interrupt_max_size_or_timeout_based] in the interrupt service +/// routine. You have to call [Self::read_fixed_len_or_timeout_based_using_irq] in the ISR to +/// start reading the next packet. +pub struct RxWithInterrupt(Rx); + +impl RxWithInterrupt { + pub fn new(rx: Rx) -> Self { + Self(rx) + } + + /// This function should be called once at initialization time if the regular + /// [Self::on_interrupt] is used to read the UART receiver to enable and start the receiver. + pub fn start(&mut self) { + self.0.enable(); + self.enable_interrupts(true); + } + + #[inline(always)] + pub fn rx(&self) -> &Rx { + &self.0 + } + + /// This function is used together with the [Self::on_interrupt_max_size_or_timeout_based] + /// function to read packets with a maximum size or variable sized packets by using the + /// receive timeout of the hardware. + /// + /// This function should be called once at initialization to initiate the context state + /// and to [Self::start] the receiver. After that, it should be called after each + /// completed [Self::on_interrupt_max_size_or_timeout_based] call to restart the reception + /// of a packet. + pub fn read_fixed_len_or_timeout_based_using_irq( + &mut self, + context: &mut IrqContextTimeoutOrMaxSize, + ) -> Result<(), TransferPendingError> { + if context.mode != IrqReceptionMode::Idle { + return Err(TransferPendingError); + } + context.mode = IrqReceptionMode::Pending; + context.rx_idx = 0; + self.start(); + Ok(()) + } + + #[inline] + fn enable_interrupts(&mut self, timeout: bool) { + self.0.enable_interrupts(timeout); + } + + #[inline] + fn disable_interrupts(&mut self) { + self.0.disable_interrupts(); + } + + pub fn cancel_transfer(&mut self) { + self.disable_interrupts(); + self.0.clear_fifo(); + } + + /// This function should be called in the user provided UART interrupt handler. + /// + /// It simply empties any bytes in the FIFO into the user provided buffer and returns the + /// result of the operation. + /// + /// This function will not disable the RX interrupts, so you don't need to call any other + /// API after calling this function to continue emptying the FIFO. RX errors are handled + /// as partial errors and are returned as part of the [IrqResult]. + pub fn on_interrupt(&mut self, buf: &mut [u8; 16]) -> IrqResult { + let mut result = IrqResult::default(); + + let irq_status = self.0.regs.read_irq_status(); + let irq_enabled = self.0.regs.read_irq_enabled(); + let rx_enabled = irq_enabled.rx(); + + // Half-Full interrupt. We have a guaranteed amount of data we can read. + if irq_status.rx() { + let available_bytes = self.0.regs.read_rx_fifo_trigger().level().as_usize(); + + // If this interrupt bit is set, the trigger level is available at the very least. + // Read everything as fast as possible + for _ in 0..available_bytes { + buf[result.bytes_read] = (self.0.read_fifo_unchecked() & 0xff) as u8; + result.bytes_read += 1; + } + } + + // Timeout, empty the FIFO completely. + if irq_status.rx_timeout() { + // While there is data in the FIFO, write it into the reception buffer + while let Ok(byte) = self.0.read_fifo() { + buf[result.bytes_read] = byte as u8; + result.bytes_read += 1; + } + } + + // RX transfer not complete, check for RX errors + if rx_enabled { + self.check_for_errors(&mut result.errors); + } + + // Clear the interrupt status bits + self.0.regs.write_irq_clr( + InterruptClear::builder() + .with_rx_overrun(true) + .with_tx_overrun(false) + .build(), + ); + result + } + + /// This function should be called in the user provided UART interrupt handler. + /// + /// This function is used to read packets which either have a maximum size or variable sized + /// packet which are bounded by sufficient delays between them, triggering a hardware timeout. + /// + /// If either the maximum number of packets have been read or a timeout occured, the transfer + /// will be deemed completed. The state information of the transfer is tracked in the + /// [IrqContextTimeoutOrMaxSize] structure. + /// + /// If passed buffer is equal to or larger than the specified maximum length, an + /// [BufferTooShortError] will be returned. Other RX errors are treated as partial errors + /// and returned inside the [IrqResultMaxSizeOrTimeout] structure. + pub fn on_interrupt_max_size_or_timeout_based( + &mut self, + context: &mut IrqContextTimeoutOrMaxSize, + buf: &mut [u8], + ) -> Result { + if buf.len() < context.max_len { + return Err(BufferTooShortError { + found: buf.len(), + expected: context.max_len, + }); + } + let mut result = IrqResultMaxSizeOrTimeout::default(); + + let irq_status = self.0.regs.read_irq_status(); + let irq_enabled = self.0.regs.read_irq_enabled(); + let rx_enabled = irq_enabled.rx(); + + // Half-Full interrupt. We have a guaranteed amount of data we can read. + if irq_status.rx() { + // Determine the number of bytes to read, ensuring we leave 1 byte in the FIFO. + // We use this trick/hack because the timeout feature of the peripheral relies on data + // being in the RX FIFO. If data continues arriving, another half-full IRQ will fire. + // If not, the last byte(s) is/are emptied by the timeout interrupt. + let available_bytes = self.0.regs.read_rx_fifo_trigger().level().as_usize(); + + let bytes_to_read = core::cmp::min( + available_bytes.saturating_sub(1), + context.max_len - context.rx_idx, + ); + + // If this interrupt bit is set, the trigger level is available at the very least. + // Read everything as fast as possible + for _ in 0..bytes_to_read { + buf[context.rx_idx] = (self.0.read_fifo_unchecked() & 0xff) as u8; + context.rx_idx += 1; + } + + // On high-baudrates, data might be available immediately, and we possible have to + // read continuosly? Then again, the CPU should always be faster than that. I'd rather + // rely on the hardware firing another IRQ. I have not tried baudrates higher than + // 115200 so far. + } + // Timeout, empty the FIFO completely. + if irq_status.rx_timeout() { + // While there is data in the FIFO, write it into the reception buffer + loop { + if context.rx_idx == context.max_len { + break; + } + // While there is data in the FIFO, write it into the reception buffer + match self.0.read() { + Ok(byte) => { + buf[result.bytes_read] = byte; + result.bytes_read += 1; + } + Err(_) => break, + } + } + self.irq_completion_handler_max_size_timeout(&mut result, context); + return Ok(result); + } + + // RX transfer not complete, check for RX errors + if (context.rx_idx < context.max_len) && rx_enabled { + self.check_for_errors(&mut result.errors); + } + + // Clear the interrupt status bits + self.0.regs.write_irq_clr( + InterruptClear::builder() + .with_rx_overrun(true) + .with_tx_overrun(false) + .build(), + ); + Ok(result) + } + + fn check_for_errors(&self, errors: &mut Option) { + let rx_status = self.0.regs.read_rx_status(); + + if rx_status.overrun_error() || rx_status.framing_error() || rx_status.parity_error() { + let err = errors.get_or_insert(UartErrors::default()); + + if rx_status.overrun_error() { + err.overflow = true; + } + if rx_status.framing_error() { + err.framing = true; + } + if rx_status.parity_error() { + err.parity = true; + } + } + } + + fn irq_completion_handler_max_size_timeout( + &mut self, + res: &mut IrqResultMaxSizeOrTimeout, + context: &mut IrqContextTimeoutOrMaxSize, + ) { + self.disable_interrupts(); + self.0.disable(); + res.bytes_read = context.rx_idx; + res.complete = true; + context.mode = IrqReceptionMode::Idle; + context.rx_idx = 0; + } + + /// # Safety + /// + /// This API allows creating multiple UART instances when releasing the TX structure as well. + /// The user must ensure that these instances are not used to create multiple overlapping + /// UART drivers. + pub unsafe fn release(mut self) -> Rx { + self.disable_interrupts(); + self.0 + } +} + +pub mod tx_asynch; +pub use tx_asynch::*; + +pub mod rx_asynch; +pub use rx_asynch::*; diff --git a/vorago-shared-periphs/src/uart/pins_vor1x.rs b/vorago-shared-periphs/src/uart/pins_vor1x.rs new file mode 100644 index 0000000..c7c0a95 --- /dev/null +++ b/vorago-shared-periphs/src/uart/pins_vor1x.rs @@ -0,0 +1,112 @@ +// UART A pins + +use crate::{ + FunSel, + pins::{ + Pa2, Pa3, Pa8, Pa9, Pa16, Pa17, Pa18, Pa19, Pa26, Pa27, Pa30, Pa31, Pb6, Pb7, Pb8, Pb9, + Pb18, Pb19, Pb20, Pb21, Pb22, Pb23, Pin, + }, +}; + +use super::{Bank, RxPin, TxPin}; + +impl TxPin for Pin { + const BANK: Bank = Bank::Uart0; + const FUN_SEL: FunSel = FunSel::Sel2; +} +impl RxPin for Pin { + const BANK: Bank = Bank::Uart0; + const FUN_SEL: FunSel = FunSel::Sel2; +} + +impl TxPin for Pin { + const BANK: Bank = Bank::Uart0; + const FUN_SEL: FunSel = FunSel::Sel3; +} +impl RxPin for Pin { + const BANK: Bank = Bank::Uart0; + const FUN_SEL: FunSel = FunSel::Sel3; +} + +impl TxPin for Pin { + const BANK: Bank = Bank::Uart0; + const FUN_SEL: FunSel = FunSel::Sel3; +} +impl RxPin for Pin { + const BANK: Bank = Bank::Uart0; + const FUN_SEL: FunSel = FunSel::Sel3; +} + +impl TxPin for Pin { + const BANK: Bank = Bank::Uart0; + const FUN_SEL: FunSel = FunSel::Sel1; +} +impl RxPin for Pin { + const BANK: Bank = Bank::Uart0; + const FUN_SEL: FunSel = FunSel::Sel1; +} + +impl TxPin for Pin { + const BANK: Bank = Bank::Uart0; + const FUN_SEL: FunSel = FunSel::Sel1; +} +impl RxPin for Pin { + const BANK: Bank = Bank::Uart0; + const FUN_SEL: FunSel = FunSel::Sel1; +} + +// UART B pins + +impl TxPin for Pin { + const BANK: Bank = Bank::Uart1; + const FUN_SEL: FunSel = FunSel::Sel2; +} +impl RxPin for Pin { + const BANK: Bank = Bank::Uart1; + const FUN_SEL: FunSel = FunSel::Sel2; +} + +impl TxPin for Pin { + const BANK: Bank = Bank::Uart1; + const FUN_SEL: FunSel = FunSel::Sel3; +} +impl RxPin for Pin { + const BANK: Bank = Bank::Uart1; + const FUN_SEL: FunSel = FunSel::Sel3; +} + +impl TxPin for Pin { + const BANK: Bank = Bank::Uart1; + const FUN_SEL: FunSel = FunSel::Sel3; +} +impl RxPin for Pin { + const BANK: Bank = Bank::Uart1; + const FUN_SEL: FunSel = FunSel::Sel3; +} + +impl TxPin for Pin { + const BANK: Bank = Bank::Uart1; + const FUN_SEL: FunSel = FunSel::Sel1; +} +impl RxPin for Pin { + const BANK: Bank = Bank::Uart1; + const FUN_SEL: FunSel = FunSel::Sel1; +} + +impl TxPin for Pin { + const BANK: Bank = Bank::Uart1; + const FUN_SEL: FunSel = FunSel::Sel2; +} +impl RxPin for Pin { + const BANK: Bank = Bank::Uart1; + const FUN_SEL: FunSel = FunSel::Sel2; +} + +impl TxPin for Pin { + const BANK: Bank = Bank::Uart1; + const FUN_SEL: FunSel = FunSel::Sel1; +} +impl RxPin for Pin { + const BANK: Bank = Bank::Uart1; + const FUN_SEL: FunSel = FunSel::Sel1; +} diff --git a/vorago-shared-periphs/src/uart/regs.rs b/vorago-shared-periphs/src/uart/regs.rs new file mode 100644 index 0000000..842d28b --- /dev/null +++ b/vorago-shared-periphs/src/uart/regs.rs @@ -0,0 +1,303 @@ +use core::marker::PhantomData; + +use arbitrary_int::{u5, u6, u18}; + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Bank { + Uart0 = 0, + Uart1 = 1, + #[cfg(feature = "vor4x")] + Uart2 = 2, +} + +impl Bank { + /// Unsafely steal the GPIO peripheral block for the given port. + /// + /// # Safety + /// + /// Circumvents ownership and safety guarantees by the HAL. + pub unsafe fn steal_regs(&self) -> MmioUart<'static> { + Uart::new_mmio(*self) + } +} +cfg_if::cfg_if! { + if #[cfg(feature = "vor1x")] { + /// UART A base address + const BASE_ADDR_0: usize = 0x4004_0000; + /// UART B base address + const BASE_ADDR_1: usize = 0x4004_1000; + } else if #[cfg(feature = "vor4x")] { + /// UART 0 base address + const BASE_ADDR_0: usize = 0x4002_4000; + /// UART 1 base address + const BASE_ADDR_1: usize = 0x4002_5000; + /// UART 2 base address + const BASE_ADDR_2: usize = 0x4001_7000; + } +} + +#[bitbybit::bitfield(u32)] +#[derive(Debug)] +pub struct Data { + #[bit(15, rw)] + dparity: bool, + #[bits(0..=7, rw)] + value: u8, +} + +#[bitbybit::bitfield(u32, default = 0x0)] +#[derive(Debug)] +pub struct Enable { + #[bit(1, rw)] + tx: bool, + #[bit(0, rw)] + rx: bool, +} + +#[bitbybit::bitenum(u1, exhaustive = true)] +#[derive(Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Stopbits { + One = 0, + Two = 1, +} + +#[bitbybit::bitenum(u2, exhaustive = true)] +#[derive(Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum WordSize { + Five = 0b00, + Six = 0b01, + Seven = 0b10, + Eight = 0b11, +} + +#[bitbybit::bitfield(u32, default = 0x0)] +#[derive(Debug)] +pub struct Control { + #[bit(11, rw)] + baud8: bool, + #[bit(10, rw)] + auto_rts: bool, + #[bit(9, rw)] + def_rts: bool, + #[bit(8, rw)] + auto_cts: bool, + #[bit(7, rw)] + loopback_block: bool, + #[bit(6, rw)] + loopback: bool, + #[bits(4..=5, rw)] + wordsize: WordSize, + #[bit(3, rw)] + stopbits: Stopbits, + #[bit(2, rw)] + parity_manual: bool, + #[bit(1, rw)] + parity_even: bool, + #[bit(0, rw)] + parity_enable: bool, +} + +#[bitbybit::bitfield(u32, default = 0x0)] +#[derive(Debug)] +pub struct ClkScale { + #[bits(6..=23, rw)] + int: u18, + #[bits(0..=5, rw)] + frac: u6, +} + +#[bitbybit::bitfield(u32)] +#[derive(Debug)] +pub struct RxStatus { + #[bit(15, r)] + rx_rtsn: bool, + #[bit(9, r)] + rx_addr9: bool, + #[bit(8, r)] + busy_break: bool, + #[bit(7, r)] + break_error: bool, + #[bit(6, r)] + parity_error: bool, + #[bit(5, r)] + framing_error: bool, + #[bit(4, r)] + overrun_error: bool, + #[bit(3, r)] + timeout: bool, + #[bit(2, r)] + busy: bool, + #[bit(1, r)] + not_full: bool, + #[bit(0, r)] + data_available: bool, +} + +#[bitbybit::bitfield(u32)] +#[derive(Debug)] +pub struct TxStatus { + #[bit(15, r)] + tx_ctsn: bool, + #[bit(3, r)] + wr_lost: bool, + #[bit(2, r)] + tx_busy: bool, + #[bit(1, r)] + write_busy: bool, + /// There is space in the FIFO to write data. + #[bit(0, r)] + ready: bool, +} + +#[bitbybit::bitfield(u32, default = 0x0)] +#[derive(Debug)] +pub struct FifoClear { + #[bit(1, w)] + tx: bool, + #[bit(0, w)] + rx: bool, +} + +#[bitbybit::bitfield(u32)] +#[derive(Debug)] +pub struct InterruptControl { + /// Generate an interrrupt when the RX FIFO is at least half-full (FIFO count >= trigger level) + #[bit(0, rw)] + rx: bool, + /// Interrupts for status conditions (overrun, framing, parity and break) + #[bit(1, rw)] + rx_status: bool, + /// Interrupt on timeout conditions. + #[bit(2, rw)] + rx_timeout: bool, + + /// Generates an interrupt when the TX FIFO is at least half-empty (FIFO count < trigger level) + #[bit(4, rw)] + tx: bool, + /// Generates an interrupt on TX FIFO overflow. + #[bit(5, rw)] + tx_status: bool, + /// Generates an interrupt when the transmit FIFO is empty and TXBUSY is 0. + #[bit(6, rw)] + tx_empty: bool, + #[bit(7, rw)] + tx_cts: bool, +} + +#[bitbybit::bitfield(u32)] +#[derive(Debug)] +pub struct InterruptStatus { + /// Generate an interrrupt when the RX FIFO is at least half-full (FIFO count >= trigger level) + #[bit(0, r)] + rx: bool, + /// Interrupts for status conditions (overrun, framing, parity and break) + #[bit(1, r)] + rx_status: bool, + /// Interrupt on timeout conditions. + #[bit(2, r)] + rx_timeout: bool, + + /// Generates an interrupt when the TX FIFO is at least half-empty (FIFO count < trigger level) + #[bit(4, r)] + tx: bool, + /// Generates an interrupt on TX FIFO overflow. + #[bit(5, r)] + tx_status: bool, + /// Generates an interrupt when the transmit FIFO is empty and TXBUSY is 0. + #[bit(6, r)] + tx_empty: bool, + #[bit(7, r)] + tx_cts: bool, +} + +/// As specified in the VA416x0 Programmers Guide, only the RX overflow bit can be cleared. +#[bitbybit::bitfield(u32, default = 0x0)] +#[derive(Debug)] +pub struct InterruptClear { + #[bit(1, w)] + rx_overrun: bool, + /// Not sure if this does anything, the programmer guides are not consistent on this.. + #[bit(5, w)] + tx_overrun: bool, +} + +#[bitbybit::bitfield(u32)] +#[derive(Debug)] +pub struct FifoTrigger { + #[bits(0..=4, rw)] + level: u5, +} + +#[bitbybit::bitfield(u32)] +#[derive(Debug)] +pub struct State { + #[bits(0..=7, r)] + rx_state: u8, + /// Data count. + #[bits(8..=12, r)] + rx_fifo: u5, + #[bits(16..=23, r)] + tx_state: u8, + /// Data count. + #[bits(24..=28, r)] + tx_fifo: u5, +} + +#[derive(derive_mmio::Mmio)] +#[mmio(no_ctors)] +#[repr(C)] +pub struct Uart { + data: Data, + enable: Enable, + ctrl: Control, + clkscale: ClkScale, + #[mmio(PureRead)] + rx_status: RxStatus, + #[mmio(PureRead)] + tx_status: TxStatus, + #[mmio(Write)] + fifo_clr: FifoClear, + #[mmio(Write)] + txbreak: u32, + addr9: u32, + addr9mask: u32, + irq_enabled: InterruptControl, + #[mmio(PureRead)] + irq_raw: InterruptStatus, + #[mmio(PureRead)] + irq_status: InterruptStatus, + #[mmio(Write)] + irq_clr: InterruptClear, + rx_fifo_trigger: FifoTrigger, + tx_fifo_trigger: FifoTrigger, + rx_fifo_rts_trigger: u32, + #[mmio(PureRead)] + state: State, + _reserved: [u32; 0x3ED], + /// Vorago 1x value: 0x0112_07E1. Vorago 4x value: 0x0212_07E9 + #[mmio(PureRead)] + perid: u32, +} + +static_assertions::const_assert_eq!(core::mem::size_of::(), 0x1000); + +impl Uart { + fn new_mmio_at(base: usize) -> MmioUart<'static> { + MmioUart { + ptr: base as *mut _, + phantom: PhantomData, + } + } + + pub fn new_mmio(bank: Bank) -> MmioUart<'static> { + match bank { + Bank::Uart0 => Self::new_mmio_at(BASE_ADDR_0), + Bank::Uart1 => Self::new_mmio_at(BASE_ADDR_1), + #[cfg(feature = "vor4x")] + Bank::Uart2 => Self::new_mmio_at(BASE_ADDR_2), + } + } +} diff --git a/vorago-shared-periphs/src/uart/rx_asynch.rs b/vorago-shared-periphs/src/uart/rx_asynch.rs new file mode 100644 index 0000000..19687ad --- /dev/null +++ b/vorago-shared-periphs/src/uart/rx_asynch.rs @@ -0,0 +1,437 @@ +//! # Async UART reception functionality for the VA416xx family. +//! +//! This module provides the [RxAsync] and [RxAsyncOverwriting] struct which both implement the +//! [embedded_io_async::Read] trait. +//! This trait allows for asynchronous reception of data streams. Please note that this module does +//! not specify/declare the interrupt handlers which must be provided for async support to work. +//! However, it provides two interrupt handlers: +//! +//! - [on_interrupt_rx] +//! - [on_interrupt_rx_overwriting] +//! +//! The first two are used for the [RxAsync] struct, while the latter two are used with the +//! [RxAsyncOverwriting] struct. The later two will overwrite old values in the used ring buffer. +//! +//! Error handling is performed in the user interrupt handler by checking the [AsyncUartErrors] +//! structure returned by the interrupt handlers. +use core::{cell::RefCell, convert::Infallible, future::Future, sync::atomic::Ordering}; + +use arbitrary_int::Number; +use critical_section::Mutex; +use embassy_sync::waitqueue::AtomicWaker; +use embedded_io::ErrorType; +use portable_atomic::AtomicBool; + +use super::{ + Bank, Rx, UartErrors, + regs::{InterruptClear, MmioUart}, +}; + +static UART_RX_WAKERS: [AtomicWaker; 2] = [const { AtomicWaker::new() }; 2]; +static RX_READ_ACTIVE: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2]; +static RX_HAS_DATA: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2]; + +struct RxFuture { + id: Bank, +} + +impl RxFuture { + pub fn new(rx: &mut Rx) -> Self { + RX_READ_ACTIVE[rx.id as usize].store(true, Ordering::Relaxed); + Self { id: rx.id } + } +} + +impl Future for RxFuture { + type Output = Result<(), Infallible>; + + fn poll( + self: core::pin::Pin<&mut Self>, + cx: &mut core::task::Context<'_>, + ) -> core::task::Poll { + UART_RX_WAKERS[self.id as usize].register(cx.waker()); + if RX_HAS_DATA[self.id as usize].load(Ordering::Relaxed) { + return core::task::Poll::Ready(Ok(())); + } + core::task::Poll::Pending + } +} + +#[derive(Debug, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct AsyncUartErrors { + /// Queue has overflowed, data might have been lost. + pub queue_overflow: bool, + /// UART errors. + pub uart_errors: UartErrors, +} + +fn on_interrupt_handle_rx_errors(uart: &mut MmioUart<'static>) -> Option { + let rx_status = uart.read_rx_status(); + if rx_status.overrun_error() || rx_status.framing_error() || rx_status.parity_error() { + let mut errors_val = UartErrors::default(); + + if rx_status.overrun_error() { + errors_val.overflow = true; + } + if rx_status.framing_error() { + errors_val.framing = true; + } + if rx_status.parity_error() { + errors_val.parity = true; + } + return Some(errors_val); + } + None +} + +fn on_interrupt_rx_common_post_processing( + id: Bank, + rx_enabled: bool, + read_some_data: bool, +) -> Option { + let idx = id as usize; + if read_some_data { + RX_HAS_DATA[idx].store(true, Ordering::Relaxed); + if RX_READ_ACTIVE[idx].load(Ordering::Relaxed) { + UART_RX_WAKERS[idx].wake(); + } + } + + let mut errors = None; + let mut uart_regs = unsafe { id.steal_regs() }; + // Check for RX errors + if rx_enabled { + errors = on_interrupt_handle_rx_errors(&mut uart_regs); + } + + // Clear the interrupt status bits + uart_regs.write_irq_clr( + InterruptClear::builder() + .with_rx_overrun(true) + .with_tx_overrun(false) + .build(), + ); + errors +} + +/// Interrupt handler with overwriting behaviour when the ring buffer is full. +/// +/// Should be called in the user interrupt handler to enable +/// asynchronous reception. This variant will overwrite old data in the ring buffer in case +/// the ring buffer is full. +pub fn on_interrupt_rx_overwriting( + bank: Bank, + prod: &mut heapless::spsc::Producer, + shared_consumer: &Mutex>>>, +) -> Result<(), AsyncUartErrors> { + on_interrupt_rx_async_heapless_queue_overwriting(bank, prod, shared_consumer) +} + +pub fn on_interrupt_rx_async_heapless_queue_overwriting( + bank: Bank, + prod: &mut heapless::spsc::Producer, + shared_consumer: &Mutex>>>, +) -> Result<(), AsyncUartErrors> { + let uart_regs = unsafe { bank.steal_regs() }; + let irq_status = uart_regs.read_irq_status(); + let irq_enabled = uart_regs.read_irq_enabled(); + let rx_enabled = irq_enabled.rx(); + let mut read_some_data = false; + let mut queue_overflow = false; + + // Half-Full interrupt. We have a guaranteed amount of data we can read. + if irq_status.rx() { + let available_bytes = uart_regs.read_rx_fifo_trigger().level().as_usize(); + + // If this interrupt bit is set, the trigger level is available at the very least. + // Read everything as fast as possible + for _ in 0..available_bytes { + let byte = uart_regs.read_data().value(); + if !prod.ready() { + queue_overflow = true; + critical_section::with(|cs| { + let mut cons_ref = shared_consumer.borrow(cs).borrow_mut(); + cons_ref.as_mut().unwrap().dequeue(); + }); + } + prod.enqueue(byte).ok(); + } + read_some_data = true; + } + + // Timeout, empty the FIFO completely. + if irq_status.rx_timeout() { + while uart_regs.read_rx_status().data_available() { + // While there is data in the FIFO, write it into the reception buffer + let byte = uart_regs.read_data().value(); + if !prod.ready() { + queue_overflow = true; + critical_section::with(|cs| { + let mut cons_ref = shared_consumer.borrow(cs).borrow_mut(); + cons_ref.as_mut().unwrap().dequeue(); + }); + } + prod.enqueue(byte).ok(); + } + read_some_data = true; + } + + let uart_errors = on_interrupt_rx_common_post_processing(bank, rx_enabled, read_some_data); + if uart_errors.is_some() || queue_overflow { + return Err(AsyncUartErrors { + queue_overflow, + uart_errors: uart_errors.unwrap_or_default(), + }); + } + Ok(()) +} + +/// Interrupt handler for asynchronous RX operations. +/// +/// Should be called in the user interrupt handler to enable asynchronous reception. +pub fn on_interrupt_rx( + bank: Bank, + prod: &mut heapless::spsc::Producer<'_, u8, N>, +) -> Result<(), AsyncUartErrors> { + on_interrupt_rx_async_heapless_queue(bank, prod) +} + +pub fn on_interrupt_rx_async_heapless_queue( + bank: Bank, + prod: &mut heapless::spsc::Producer<'_, u8, N>, +) -> Result<(), AsyncUartErrors> { + let uart_regs = unsafe { bank.steal_regs() }; + let irq_status = uart_regs.read_irq_status(); + let irq_enabled = uart_regs.read_irq_enabled(); + let rx_enabled = irq_enabled.rx(); + let mut read_some_data = false; + let mut queue_overflow = false; + + // Half-Full interrupt. We have a guaranteed amount of data we can read. + if irq_status.rx() { + let available_bytes = uart_regs.read_rx_fifo_trigger().level().as_usize(); + + // If this interrupt bit is set, the trigger level is available at the very least. + // Read everything as fast as possible + for _ in 0..available_bytes { + let byte = uart_regs.read_data().value(); + if !prod.ready() { + queue_overflow = true; + } + prod.enqueue(byte).ok(); + } + read_some_data = true; + } + + // Timeout, empty the FIFO completely. + if irq_status.rx_timeout() { + while uart_regs.read_rx_status().data_available() { + // While there is data in the FIFO, write it into the reception buffer + let byte = uart_regs.read_data().value(); + if !prod.ready() { + queue_overflow = true; + } + prod.enqueue(byte).ok(); + } + read_some_data = true; + } + + let uart_errors = on_interrupt_rx_common_post_processing(bank, rx_enabled, read_some_data); + if uart_errors.is_some() || queue_overflow { + return Err(AsyncUartErrors { + queue_overflow, + uart_errors: uart_errors.unwrap_or_default(), + }); + } + Ok(()) +} + +struct ActiveReadGuard(usize); + +impl Drop for ActiveReadGuard { + fn drop(&mut self) { + RX_READ_ACTIVE[self.0].store(false, Ordering::Relaxed); + } +} + +struct RxAsyncInner { + rx: Rx, + pub queue: heapless::spsc::Consumer<'static, u8, N>, +} + +/// Core data structure to allow asynchronous UART reception. +/// +/// If the ring buffer becomes full, data will be lost. +pub struct RxAsync(Option>); + +impl ErrorType for RxAsync { + /// Error reporting is done using the result of the interrupt functions. + type Error = Infallible; +} + +fn stop_async_rx(rx: &mut Rx) { + rx.disable_interrupts(); + rx.disable(); + rx.clear_fifo(); +} + +impl RxAsync { + /// Create a new asynchronous receiver. + /// + /// The passed [heapless::spsc::Consumer] will be used to asynchronously receive data which + /// is filled by the interrupt handler [on_interrupt_rx]. + pub fn new(mut rx: Rx, queue: heapless::spsc::Consumer<'static, u8, N>) -> Self { + rx.disable_interrupts(); + rx.disable(); + rx.clear_fifo(); + // Enable those together. + critical_section::with(|_| { + rx.enable_interrupts(true); + rx.enable(); + }); + Self(Some(RxAsyncInner { rx, queue })) + } + + pub fn stop(&mut self) { + stop_async_rx(&mut self.0.as_mut().unwrap().rx); + } + + pub fn release(mut self) -> (Rx, heapless::spsc::Consumer<'static, u8, N>) { + self.stop(); + let inner = self.0.take().unwrap(); + (inner.rx, inner.queue) + } +} + +impl Drop for RxAsync { + fn drop(&mut self) { + self.stop(); + } +} + +impl embedded_io_async::Read for RxAsync { + async fn read(&mut self, buf: &mut [u8]) -> Result { + let inner = self.0.as_ref().unwrap(); + // Need to wait for the IRQ to read data and set this flag. If the queue is not + // empty, we can read data immediately. + if inner.queue.len() == 0 { + RX_HAS_DATA[inner.rx.id as usize].store(false, Ordering::Relaxed); + } + let _guard = ActiveReadGuard(inner.rx.id as usize); + let mut handle_data_in_queue = |consumer: &mut heapless::spsc::Consumer<'static, u8, N>| { + let data_to_read = consumer.len().min(buf.len()); + for byte in buf.iter_mut().take(data_to_read) { + // We own the consumer and we checked that the amount of data is guaranteed to be available. + *byte = unsafe { consumer.dequeue_unchecked() }; + } + data_to_read + }; + let mut_ref = self.0.as_mut().unwrap(); + let fut = RxFuture::new(&mut mut_ref.rx); + // Data is available, so read that data immediately. + let read_data = handle_data_in_queue(&mut mut_ref.queue); + if read_data > 0 { + return Ok(read_data); + } + // Await data. + let _ = fut.await; + Ok(handle_data_in_queue(&mut mut_ref.queue)) + } +} + +struct RxAsyncOverwritingInner { + rx: Rx, + pub shared_consumer: &'static Mutex>>>, +} + +/// Core data structure to allow asynchronous UART reception. +/// +/// If the ring buffer becomes full, the oldest data will be overwritten when using the +/// [on_interrupt_rx_overwriting] interrupt handlers. +pub struct RxAsyncOverwriting(Option>); + +impl ErrorType for RxAsyncOverwriting { + /// Error reporting is done using the result of the interrupt functions. + type Error = Infallible; +} + +impl RxAsyncOverwriting { + /// Create a new asynchronous receiver. + /// + /// The passed shared [heapless::spsc::Consumer] will be used to asynchronously receive data + /// which is filled by the interrupt handler. The shared property allows using it in the + /// interrupt handler to overwrite old data. + pub fn new( + mut rx: Rx, + shared_consumer: &'static Mutex>>>, + ) -> Self { + rx.disable_interrupts(); + rx.disable(); + rx.clear_fifo(); + // Enable those together. + critical_section::with(|_| { + rx.enable_interrupts(true); + rx.enable(); + }); + Self(Some(RxAsyncOverwritingInner { + rx, + shared_consumer, + })) + } + + pub fn stop(&mut self) { + stop_async_rx(&mut self.0.as_mut().unwrap().rx); + } + + pub fn release(mut self) -> Rx { + self.stop(); + let inner = self.0.take().unwrap(); + inner.rx + } +} + +impl Drop for RxAsyncOverwriting { + fn drop(&mut self) { + self.stop(); + } +} + +impl embedded_io_async::Read for RxAsyncOverwriting { + async fn read(&mut self, buf: &mut [u8]) -> Result { + let inner = self.0.as_ref().unwrap(); + let id = inner.rx.id as usize; + // Need to wait for the IRQ to read data and set this flag. If the queue is not + // empty, we can read data immediately. + + critical_section::with(|cs| { + let queue = inner.shared_consumer.borrow(cs); + if queue.borrow().as_ref().unwrap().len() == 0 { + RX_HAS_DATA[id].store(false, Ordering::Relaxed); + } + }); + let _guard = ActiveReadGuard(id); + let mut handle_data_in_queue = |inner: &mut RxAsyncOverwritingInner| { + critical_section::with(|cs| { + let mut consumer_ref = inner.shared_consumer.borrow(cs).borrow_mut(); + let consumer = consumer_ref.as_mut().unwrap(); + let data_to_read = consumer.len().min(buf.len()); + for byte in buf.iter_mut().take(data_to_read) { + // We own the consumer and we checked that the amount of data is guaranteed to be available. + *byte = unsafe { consumer.dequeue_unchecked() }; + } + data_to_read + }) + }; + let fut = RxFuture::new(&mut self.0.as_mut().unwrap().rx); + // Data is available, so read that data immediately. + let read_data = handle_data_in_queue(self.0.as_mut().unwrap()); + if read_data > 0 { + return Ok(read_data); + } + // Await data. + let _ = fut.await; + let read_data = handle_data_in_queue(self.0.as_mut().unwrap()); + Ok(read_data) + } +} diff --git a/vorago-shared-periphs/src/uart/tx_asynch.rs b/vorago-shared-periphs/src/uart/tx_asynch.rs new file mode 100644 index 0000000..0144575 --- /dev/null +++ b/vorago-shared-periphs/src/uart/tx_asynch.rs @@ -0,0 +1,205 @@ +//! # Async UART transmission functionality for the VA108xx family. +//! +//! This module provides the [TxAsync] struct which implements the [embedded_io_async::Write] trait. +//! This trait allows for asynchronous sending of data streams. Please note that this module does +//! not specify/declare the interrupt handlers which must be provided for async support to work. +//! However, it the [on_interrupt_tx] interrupt handler. +//! +//! This handler should be called in ALL user interrupt handlers which handle UART TX interrupts +//! for a given UART bank. +use core::{cell::RefCell, future::Future}; + +use critical_section::Mutex; +use embassy_sync::waitqueue::AtomicWaker; +use embedded_io_async::Write; +use portable_atomic::AtomicBool; +use raw_slice::RawBufSlice; + +use super::*; + +static UART_TX_WAKERS: [AtomicWaker; 2] = [const { AtomicWaker::new() }; 2]; +static TX_CONTEXTS: [Mutex>; 2] = + [const { Mutex::new(RefCell::new(TxContext::new())) }; 2]; +// Completion flag. Kept outside of the context structure as an atomic to avoid +// critical section. +static TX_DONE: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2]; + +/// This is a generic interrupt handler to handle asynchronous UART TX operations for a given +/// UART bank. +/// +/// The user has to call this once in the interrupt handler responsible for the TX interrupts on +/// the given UART bank. +pub fn on_interrupt_tx(bank: Bank) { + let mut uart = unsafe { bank.steal_regs() }; + let idx = bank as usize; + let irq_enabled = uart.read_irq_enabled(); + // IRQ is not related to TX. + if !irq_enabled.tx() && !irq_enabled.tx_empty() { + return; + } + + let tx_status = uart.read_tx_status(); + let unexpected_overrun = tx_status.wr_lost(); + let mut context = critical_section::with(|cs| { + let context_ref = TX_CONTEXTS[idx].borrow(cs); + *context_ref.borrow() + }); + context.tx_overrun = unexpected_overrun; + // Safety: We documented that the user provided slice must outlive the future, so we convert + // the raw pointer back to the slice here. + let slice = unsafe { context.slice.get().unwrap() }; + if context.progress >= slice.len() && !tx_status.tx_busy() { + uart.modify_irq_enabled(|mut value| { + value.set_tx(false); + value.set_tx_empty(false); + value.set_tx_status(false); + value + }); + uart.modify_enable(|mut value| { + value.set_tx(false); + value + }); + // Write back updated context structure. + critical_section::with(|cs| { + let context_ref = TX_CONTEXTS[idx].borrow(cs); + *context_ref.borrow_mut() = context; + }); + // Transfer is done. + TX_DONE[idx].store(true, core::sync::atomic::Ordering::Relaxed); + UART_TX_WAKERS[idx].wake(); + return; + } + while context.progress < slice.len() { + if uart.read_tx_status().ready() { + break; + } + // Safety: TX structure is owned by the future which does not write into the the data + // register, so we can assume we are the only one writing to the data register. + uart.write_data(Data::new_with_raw_value(slice[context.progress] as u32)); + context.progress += 1; + } + + // Write back updated context structure. + critical_section::with(|cs| { + let context_ref = TX_CONTEXTS[idx].borrow(cs); + *context_ref.borrow_mut() = context; + }); +} + +#[derive(Debug, Copy, Clone)] +pub struct TxContext { + progress: usize, + tx_overrun: bool, + slice: RawBufSlice, +} + +#[allow(clippy::new_without_default)] +impl TxContext { + pub const fn new() -> Self { + Self { + progress: 0, + tx_overrun: false, + slice: RawBufSlice::new_nulled(), + } + } +} + +pub struct TxFuture { + id: Bank, +} + +impl TxFuture { + /// # Safety + /// + /// This function stores the raw pointer of the passed data slice. The user MUST ensure + /// that the slice outlives the data structure. + pub unsafe fn new(tx: &mut Tx, data: &[u8]) -> Self { + TX_DONE[tx.id as usize].store(false, core::sync::atomic::Ordering::Relaxed); + tx.disable_interrupts(); + tx.disable(); + tx.clear_fifo(); + + let init_fill_count = core::cmp::min(data.len(), 16); + // We fill the FIFO. + for data in data.iter().take(init_fill_count) { + tx.regs.write_data(Data::new_with_raw_value(*data as u32)); + } + critical_section::with(|cs| { + let context_ref = TX_CONTEXTS[tx.id as usize].borrow(cs); + let mut context = context_ref.borrow_mut(); + unsafe { context.slice.set(data) }; + context.progress = init_fill_count; + + // Ensure those are enabled inside a critical section at the same time. Can lead to + // weird glitches otherwise. + tx.enable_interrupts(); + tx.enable(); + }); + Self { id: tx.id } + } +} + +impl Future for TxFuture { + type Output = Result; + + fn poll( + self: core::pin::Pin<&mut Self>, + cx: &mut core::task::Context<'_>, + ) -> core::task::Poll { + UART_TX_WAKERS[self.id as usize].register(cx.waker()); + if TX_DONE[self.id as usize].swap(false, core::sync::atomic::Ordering::Relaxed) { + let progress = critical_section::with(|cs| { + TX_CONTEXTS[self.id as usize].borrow(cs).borrow().progress + }); + return core::task::Poll::Ready(Ok(progress)); + } + core::task::Poll::Pending + } +} + +impl Drop for TxFuture { + fn drop(&mut self) { + let mut reg_block = unsafe { self.id.steal_regs() }; + + disable_tx_interrupts(&mut reg_block); + disable_tx(&mut reg_block); + } +} + +pub struct TxAsync(Tx); + +impl TxAsync { + pub fn new(tx: Tx) -> Self { + Self(tx) + } + + pub fn release(self) -> Tx { + self.0 + } +} + +#[derive(Debug, thiserror::Error)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[error("TX overrun error")] +pub struct TxOverrunError; + +impl embedded_io_async::Error for TxOverrunError { + fn kind(&self) -> embedded_io_async::ErrorKind { + embedded_io_async::ErrorKind::Other + } +} + +impl embedded_io::ErrorType for TxAsync { + type Error = TxOverrunError; +} + +impl Write for TxAsync { + /// Write a buffer asynchronously. + /// + /// This implementation is not side effect free, and a started future might have already + /// written part of the passed buffer. + async fn write(&mut self, buf: &[u8]) -> Result { + let fut = unsafe { TxFuture::new(&mut self.0, buf) }; + fut.await + } +}