From f31497b024b32c56866bdf2a1926f03c83f459b4 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 22 Apr 2025 13:50:21 +0200 Subject: [PATCH] moved shared crate into separate crate --- Cargo.toml | 1 - va108xx-hal/Cargo.toml | 2 +- vorago-shared-periphs/Cargo.toml | 40 - vorago-shared-periphs/src/gpio/asynch.rs | 231 --- vorago-shared-periphs/src/gpio/ll.rs | 424 ------ vorago-shared-periphs/src/gpio/mod.rs | 374 ----- vorago-shared-periphs/src/gpio/regs.rs | 126 -- vorago-shared-periphs/src/i2c/mod.rs | 665 --------- vorago-shared-periphs/src/i2c/regs.rs | 671 --------- vorago-shared-periphs/src/ioconfig/mod.rs | 3 - vorago-shared-periphs/src/ioconfig/regs.rs | 177 --- vorago-shared-periphs/src/lib.rs | 182 --- vorago-shared-periphs/src/pins.rs | 236 --- vorago-shared-periphs/src/pwm.rs | 246 ---- vorago-shared-periphs/src/spi/mod.rs | 868 ------------ vorago-shared-periphs/src/spi/pins_vor1x.rs | 355 ----- vorago-shared-periphs/src/spi/regs.rs | 279 ---- vorago-shared-periphs/src/sysconfig.rs | 43 - vorago-shared-periphs/src/time.rs | 26 - vorago-shared-periphs/src/timer/mod.rs | 466 ------ vorago-shared-periphs/src/timer/pins_vor1x.rs | 0 vorago-shared-periphs/src/timer/regs.rs | 309 ---- vorago-shared-periphs/src/uart/mod.rs | 1260 ----------------- vorago-shared-periphs/src/uart/pins_vor1x.rs | 112 -- vorago-shared-periphs/src/uart/regs.rs | 304 ---- vorago-shared-periphs/src/uart/rx_asynch.rs | 437 ------ vorago-shared-periphs/src/uart/tx_asynch.rs | 205 --- 27 files changed, 1 insertion(+), 8041 deletions(-) delete mode 100644 vorago-shared-periphs/Cargo.toml delete mode 100644 vorago-shared-periphs/src/gpio/asynch.rs delete mode 100644 vorago-shared-periphs/src/gpio/ll.rs delete mode 100644 vorago-shared-periphs/src/gpio/mod.rs delete mode 100644 vorago-shared-periphs/src/gpio/regs.rs delete mode 100644 vorago-shared-periphs/src/i2c/mod.rs delete mode 100644 vorago-shared-periphs/src/i2c/regs.rs delete mode 100644 vorago-shared-periphs/src/ioconfig/mod.rs delete mode 100644 vorago-shared-periphs/src/ioconfig/regs.rs delete mode 100644 vorago-shared-periphs/src/lib.rs delete mode 100644 vorago-shared-periphs/src/pins.rs delete mode 100644 vorago-shared-periphs/src/pwm.rs delete mode 100644 vorago-shared-periphs/src/spi/mod.rs delete mode 100644 vorago-shared-periphs/src/spi/pins_vor1x.rs delete mode 100644 vorago-shared-periphs/src/spi/regs.rs delete mode 100644 vorago-shared-periphs/src/sysconfig.rs delete mode 100644 vorago-shared-periphs/src/time.rs delete mode 100644 vorago-shared-periphs/src/timer/mod.rs delete mode 100644 vorago-shared-periphs/src/timer/pins_vor1x.rs delete mode 100644 vorago-shared-periphs/src/timer/regs.rs delete mode 100644 vorago-shared-periphs/src/uart/mod.rs delete mode 100644 vorago-shared-periphs/src/uart/pins_vor1x.rs delete mode 100644 vorago-shared-periphs/src/uart/regs.rs delete mode 100644 vorago-shared-periphs/src/uart/rx_asynch.rs delete mode 100644 vorago-shared-periphs/src/uart/tx_asynch.rs diff --git a/Cargo.toml b/Cargo.toml index 86e1701..966472f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,6 @@ members = [ "board-tests", "bootloader", "flashloader", - "vorago-shared-periphs", ] exclude = [ "flashloader/slot-a-blinky", diff --git a/va108xx-hal/Cargo.toml b/va108xx-hal/Cargo.toml index 7f3881c..0fe8438 100644 --- a/va108xx-hal/Cargo.toml +++ b/va108xx-hal/Cargo.toml @@ -15,7 +15,7 @@ cortex-m = { version = "0.7", features = ["critical-section-single-core"]} cortex-m-rt = "0.7" nb = "1" paste = "1" -vorago-shared-periphs = { path = "../vorago-shared-periphs", features = ["vor1x"] } +vorago-shared-periphs = { path = "../../vorago-shared-periphs", features = ["vor1x"] } embedded-hal = "1" embedded-hal-async = "1" embedded-hal-nb = "1" diff --git a/vorago-shared-periphs/Cargo.toml b/vorago-shared-periphs/Cargo.toml deleted file mode 100644 index 5d35d5a..0000000 --- a/vorago-shared-periphs/Cargo.toml +++ /dev/null @@ -1,40 +0,0 @@ -[package] -name = "vorago-shared-periphs" -version = "0.1.0" -description = "Peripheral drivers shared between Vorago families" -edition = "2024" - -[dependencies] -cortex-m = { version = "0.7" } -cfg-if = "1" -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 } - -[target.'cfg(all(target_arch = "arm", target_os = "none"))'.dependencies] -portable-atomic = { version = "1", features = ["unsafe-assume-single-core"] } -[target.'cfg(not(all(target_arch = "arm", target_os = "none")))'.dependencies] -portable-atomic = "1" - -[features] -vor1x = ["_family-selected", "dep:va108xx"] -vor4x = ["_family-selected"] -defmt = ["dep:defmt", "arbitrary-int/defmt"] - -_family-selected = [] diff --git a/vorago-shared-periphs/src/gpio/asynch.rs b/vorago-shared-periphs/src/gpio/asynch.rs deleted file mode 100644 index ea32286..0000000 --- a/vorago-shared-periphs/src/gpio/asynch.rs +++ /dev/null @@ -1,231 +0,0 @@ -//! # Async GPIO functionality for the Vorago GPIO peripherals. -//! -//! This module provides the [InputPinAsync] which implements -//! the [embedded_hal_async::digital::Wait] trait. These types allow for asynchronous waiting -//! on GPIO pins. Please note that this module does not specify/declare the interrupt handlers -//! which must be provided for async support to work. However, it provides the -//! [on_interrupt_for_async_gpio_for_port] generic interrupt handler. This should be called in all -//! IRQ functions which handle any GPIO interrupts with the corresponding [Port] argument. -use core::future::Future; - -use embassy_sync::waitqueue::AtomicWaker; -use embedded_hal_async::digital::Wait; -use portable_atomic::AtomicBool; - -use crate::{InterruptConfig, NUM_PORT_A, NUM_PORT_B}; - -pub use super::ll::InterruptEdge; -use super::{ - Input, Port, - ll::{LowLevelGpio, PinId}, -}; - -static WAKERS_FOR_PORT_A: [AtomicWaker; NUM_PORT_A] = [const { AtomicWaker::new() }; NUM_PORT_A]; -static WAKERS_FOR_PORT_B: [AtomicWaker; NUM_PORT_B] = [const { AtomicWaker::new() }; NUM_PORT_B]; -static EDGE_DETECTION_PORT_A: [AtomicBool; NUM_PORT_A] = - [const { AtomicBool::new(false) }; NUM_PORT_A]; -static EDGE_DETECTION_PORT_B: [AtomicBool; NUM_PORT_B] = - [const { AtomicBool::new(false) }; NUM_PORT_B]; - -/// Generic interrupt handler for GPIO interrupts on a specific port to support async functionalities -/// -/// This function should be called in all interrupt handlers which handle any GPIO interrupts -/// matching the [Port] argument. -/// The handler will wake the corresponding wakers for the pins that triggered an interrupts -/// as well as update the static edge detection structures. This allows the pin future tocomplete -/// complete async operations. -pub fn on_interrupt_for_async_gpio_for_port(port: Port) { - let gpio = unsafe { port.steal_gpio() }; - - 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()), - Port::B => (WAKERS_FOR_PORT_B.as_ref(), EDGE_DETECTION_PORT_B.as_ref()), - }; - - on_interrupt_for_port(irq_enb, edge_status, wakers, edge_detection); -} - -#[inline] -fn on_interrupt_for_port( - mut irq_enb: u32, - edge_status: u32, - wakers: &'static [AtomicWaker], - edge_detection: &'static [AtomicBool], -) { - while irq_enb != 0 { - let bit_pos = irq_enb.trailing_zeros() as usize; - let bit_mask = 1 << bit_pos; - - wakers[bit_pos].wake(); - - if edge_status & bit_mask != 0 { - edge_detection[bit_pos].store(true, core::sync::atomic::Ordering::Relaxed); - - // Clear the processed bit - irq_enb &= !bit_mask; - } - } -} - -/// Input pin future which implements the [Future] trait. -/// -/// Generally, you want to use the [InputPinAsync] types instead of this -/// which also implements the [embedded_hal_async::digital::Wait] trait. However, access to this -/// struture is granted to allow writing custom async structures. -pub struct InputPinFuture { - id: PinId, - waker_group: &'static [AtomicWaker], - edge_detection_group: &'static [AtomicBool], -} - -impl InputPinFuture { - #[inline] - pub fn pin_group_to_waker_and_edge_detection_group( - group: Port, - ) -> (&'static [AtomicWaker], &'static [AtomicBool]) { - match group { - Port::A => (WAKERS_FOR_PORT_A.as_ref(), EDGE_DETECTION_PORT_A.as_ref()), - Port::B => (WAKERS_FOR_PORT_B.as_ref(), EDGE_DETECTION_PORT_B.as_ref()), - } - } - - pub fn new_with_input_pin( - pin: &mut Input, - irq: va108xx::Interrupt, - edge: InterruptEdge, - ) -> Self { - let (waker_group, edge_detection_group) = - Self::pin_group_to_waker_and_edge_detection_group(pin.id().port()); - edge_detection_group[pin.id().offset()].store(false, core::sync::atomic::Ordering::Relaxed); - pin.configure_edge_interrupt(edge); - #[cfg(feature = "vor1x")] - pin.enable_interrupt(InterruptConfig::new(irq, true, true)); - Self { - id: pin.id(), - waker_group, - edge_detection_group, - } - } -} - -impl Drop for InputPinFuture { - fn drop(&mut self) { - let mut ll = LowLevelGpio::new(self.id); - ll.disable_interrupt(false); - } -} - -impl Future for InputPinFuture { - type Output = (); - fn poll( - self: core::pin::Pin<&mut Self>, - cx: &mut core::task::Context<'_>, - ) -> core::task::Poll { - let idx = self.id.offset(); - self.waker_group[idx].register(cx.waker()); - if self.edge_detection_group[idx].swap(false, core::sync::atomic::Ordering::Relaxed) { - return core::task::Poll::Ready(()); - } - core::task::Poll::Pending - } -} - -pub struct InputPinAsync { - pin: Input, - #[cfg(feature = "vor1x")] - irq: va108xx::Interrupt, -} - -impl InputPinAsync { - /// Create a new asynchronous input pin from an [Input] pin. The interrupt ID to be used must be - /// passed as well and is used to route and enable the interrupt. - /// - /// Please note that the interrupt handler itself must be provided by the user and the - /// generic [on_interrupt_for_async_gpio_for_port] function must be called inside that function - /// for the asynchronous functionality to work. - #[cfg(feature = "vor1x")] - pub fn new(pin: Input, irq: va108xx::Interrupt) -> Self { - Self { pin, irq } - } - - /// Asynchronously wait until the pin is high. - /// - /// This returns immediately if the pin is already high. - pub async fn wait_for_high(&mut self) { - // Unwrap okay, checked pin in constructor. - let fut = - InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh); - if self.pin.is_high() { - return; - } - fut.await; - } - - /// Asynchronously wait until the pin is low. - /// - /// This returns immediately if the pin is already high. - pub async fn wait_for_low(&mut self) { - // Unwrap okay, checked pin in constructor. - let fut = - InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow); - if self.pin.is_low() { - return; - } - fut.await; - } - - /// Asynchronously wait until the pin sees a falling edge. - pub async fn wait_for_falling_edge(&mut self) { - // Unwrap okay, checked pin in constructor. - InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow).await; - } - - /// Asynchronously wait until the pin sees a rising edge. - pub async fn wait_for_rising_edge(&mut self) { - // Unwrap okay, checked pin in constructor. - InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh).await; - } - - /// Asynchronously wait until the pin sees any edge (either rising or falling). - pub async fn wait_for_any_edge(&mut self) { - // Unwrap okay, checked pin in constructor. - InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::BothEdges).await; - } - - pub fn release(self) -> Input { - self.pin - } -} - -impl embedded_hal::digital::ErrorType for InputPinAsync { - type Error = core::convert::Infallible; -} - -impl Wait for InputPinAsync { - async fn wait_for_high(&mut self) -> Result<(), Self::Error> { - self.wait_for_high().await; - Ok(()) - } - - async fn wait_for_low(&mut self) -> Result<(), Self::Error> { - self.wait_for_low().await; - Ok(()) - } - - async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> { - self.wait_for_rising_edge().await; - Ok(()) - } - - async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> { - self.wait_for_falling_edge().await; - Ok(()) - } - - async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> { - self.wait_for_any_edge().await; - Ok(()) - } -} diff --git a/vorago-shared-periphs/src/gpio/ll.rs b/vorago-shared-periphs/src/gpio/ll.rs deleted file mode 100644 index e1c2b96..0000000 --- a/vorago-shared-periphs/src/gpio/ll.rs +++ /dev/null @@ -1,424 +0,0 @@ -pub use embedded_hal::digital::PinState; - -use crate::ioconfig::FilterClkSel; -use crate::ioconfig::FilterType; -#[cfg(feature = "vor1x")] -use crate::{PeripheralSelect, sysconfig::enable_peripheral_clock}; - -pub use crate::InvalidOffsetError; -pub use crate::Port; -pub use crate::ioconfig::regs::Pull; -use crate::ioconfig::regs::{FunSel, IoConfig, MmioIoConfig}; - -use super::Pin; -use super::PinIdProvider; - -#[derive(Debug, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum InterruptEdge { - HighToLow, - LowToHigh, - BothEdges, -} - -#[derive(Debug, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum InterruptLevel { - Low = 0, - High = 1, -} - -/// Pin identifier for all physical pins exposed by Vorago MCUs. -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct PinId { - port: Port, - /// Offset within the port. - offset: u8, -} - -impl PinId { - /// Unchecked constructor which panics on invalid offsets. - pub const fn new_unchecked(port: Port, offset: usize) -> Self { - if offset >= port.max_offset() { - panic!("Pin ID construction: offset is out of range"); - } - PinId { - port, - offset: offset as u8, - } - } - - pub const fn new(port: Port, offset: usize) -> Result { - if offset >= port.max_offset() { - return Err(InvalidOffsetError { offset, port }); - } - Ok(PinId { - port, - offset: offset as u8, - }) - } - - pub const fn port(&self) -> Port { - self.port - } - - pub const fn offset(&self) -> usize { - self.offset as usize - } -} - -/// Low-level driver structure for GPIO pins. -pub struct LowLevelGpio { - gpio: super::regs::MmioGpio<'static>, - ioconfig: MmioIoConfig<'static>, - id: PinId, -} - -impl core::fmt::Debug for LowLevelGpio { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_struct("LowLevelGpio") - .field("gpio", &self.gpio.port()) - .field("id", &self.id) - .finish() - } -} - -impl LowLevelGpio { - /// Create a new low-level GPIO pin instance from a given [Pin]. - /// - /// Can be used for performing resource management of the [Pin]s. - pub fn new_with_pin(_pin: Pin) -> Self { - Self::new(I::ID) - } - - /// Create a new low-level GPIO pin instance using only the [PinId]. - pub fn new(id: PinId) -> Self { - LowLevelGpio { - gpio: super::regs::Gpio::new_mmio(id.port), - ioconfig: IoConfig::new_mmio(), - id, - } - } - - #[inline] - pub fn id(&self) -> PinId { - self.id - } - - #[inline] - pub fn port(&self) -> Port { - self.id.port() - } - - #[inline] - pub fn offset(&self) -> usize { - self.id.offset() - } - - pub fn configure_as_input_floating(&mut self) { - self.ioconfig.modify_pin_config(self.id, |mut config| { - config.set_funsel(FunSel::Sel0); - config.set_io_disable(false); - config.set_invert_input(false); - config.set_open_drain(false); - config.set_pull_enable(false); - config.set_pull_when_output_active(false); - config.set_invert_output(false); - config.set_input_enable_when_output(false); - config - }); - self.gpio.modify_dir(|mut dir| { - dir &= !(1 << self.id.offset()); - dir - }); - } - - pub fn configure_as_input_with_pull(&mut self, pull: Pull) { - self.ioconfig.modify_pin_config(self.id, |mut config| { - config.set_funsel(FunSel::Sel0); - config.set_io_disable(false); - config.set_invert_input(false); - config.set_open_drain(false); - config.set_pull_enable(true); - config.set_pull_dir(pull); - config.set_pull_when_output_active(false); - config.set_invert_output(false); - config.set_input_enable_when_output(false); - config - }); - self.gpio.modify_dir(|mut dir| { - dir &= !(1 << self.id.offset()); - dir - }); - } - - pub fn configure_as_output_push_pull(&mut self, init_level: PinState) { - self.ioconfig.modify_pin_config(self.id, |mut config| { - config.set_funsel(FunSel::Sel0); - config.set_io_disable(false); - config.set_invert_input(false); - config.set_open_drain(false); - config.set_pull_enable(false); - config.set_pull_when_output_active(false); - config.set_invert_output(false); - config.set_input_enable_when_output(true); - config - }); - match init_level { - PinState::Low => self.gpio.write_clr_out(self.mask_32()), - PinState::High => self.gpio.write_set_out(self.mask_32()), - } - self.gpio.modify_dir(|mut dir| { - dir |= 1 << self.id.offset(); - dir - }); - } - - pub fn configure_as_output_open_drain(&mut self, init_level: PinState) { - self.ioconfig.modify_pin_config(self.id, |mut config| { - config.set_funsel(FunSel::Sel0); - config.set_io_disable(false); - config.set_invert_input(false); - config.set_open_drain(true); - config.set_pull_enable(true); - config.set_pull_dir(Pull::Up); - config.set_pull_when_output_active(false); - config.set_invert_output(false); - config.set_input_enable_when_output(true); - config - }); - let mask32 = self.mask_32(); - match init_level { - PinState::Low => self.gpio.write_clr_out(mask32), - PinState::High => self.gpio.write_set_out(mask32), - } - self.gpio.modify_dir(|mut dir| { - dir |= mask32; - dir - }); - } - - pub fn configure_as_peripheral_pin(&mut self, fun_sel: FunSel, pull: Option) { - self.ioconfig.modify_pin_config(self.id, |mut config| { - config.set_funsel(fun_sel); - config.set_io_disable(false); - config.set_invert_input(false); - config.set_open_drain(false); - config.set_pull_enable(pull.is_some()); - config.set_pull_dir(pull.unwrap_or(Pull::Up)); - config.set_invert_output(false); - config - }); - } - - #[inline] - pub fn is_high(&self) -> bool { - (self.gpio.read_data_in() >> self.offset()) & 1 == 1 - } - - #[inline] - pub fn is_low(&self) -> bool { - !self.is_high() - } - - #[inline] - pub fn set_high(&mut self) { - self.gpio.write_set_out(self.mask_32()); - } - - #[inline] - pub fn set_low(&mut self) { - self.gpio.write_clr_out(self.mask_32()); - } - - #[inline] - pub fn is_set_high(&self) -> bool { - (self.gpio.read_data_out() >> self.offset()) & 1 == 1 - } - - #[inline] - pub fn is_set_low(&self) -> bool { - !self.is_set_high() - } - - #[inline] - pub fn toggle(&mut self) { - self.gpio.write_tog_out(self.mask_32()); - } - - #[cfg(feature = "vor1x")] - pub fn enable_interrupt(&mut self, irq_cfg: crate::InterruptConfig) { - if irq_cfg.route { - self.configure_irqsel(irq_cfg.id); - } - if irq_cfg.enable_in_nvic { - unsafe { crate::enable_nvic_interrupt(irq_cfg.id) }; - } - self.gpio.modify_irq_enable(|mut value| { - value |= 1 << self.id.offset; - value - }); - } - - #[cfg(feature = "vor1x")] - pub fn disable_interrupt(&mut self, reset_irqsel: bool) { - if reset_irqsel { - self.reset_irqsel(); - } - // We only manipulate our own bit. - self.gpio.modify_irq_enable(|mut value| { - value &= !(1 << self.id.offset); - value - }); - } - - /// Only useful for interrupt pins. Configure whether to use edges or level as interrupt soure - /// When using edge mode, it is possible to generate interrupts on both edges as well - #[inline] - pub fn configure_edge_interrupt(&mut self, edge_type: InterruptEdge) { - let mask32 = self.mask_32(); - self.gpio.modify_irq_sen(|mut value| { - value &= !mask32; - value - }); - match edge_type { - InterruptEdge::HighToLow => { - self.gpio.modify_irq_evt(|mut value| { - value &= !mask32; - value - }); - } - InterruptEdge::LowToHigh => { - self.gpio.modify_irq_evt(|mut value| { - value |= mask32; - value - }); - } - InterruptEdge::BothEdges => { - self.gpio.modify_irq_edge(|mut value| { - value |= mask32; - value - }); - } - } - } - - /// Configure which edge or level type triggers an interrupt - #[inline] - pub fn configure_level_interrupt(&mut self, level: InterruptLevel) { - let mask32 = self.mask_32(); - self.gpio.modify_irq_sen(|mut value| { - value |= mask32; - value - }); - if level == InterruptLevel::Low { - self.gpio.modify_irq_evt(|mut value| { - value &= !mask32; - value - }); - } else { - self.gpio.modify_irq_evt(|mut value| { - value |= mask32; - value - }); - } - } - - /// Only useful for input pins - #[inline] - pub fn configure_filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) { - self.ioconfig.modify_pin_config(self.id, |mut config| { - config.set_filter_type(filter); - config.set_filter_clk_sel(clksel); - config - }); - } - - /// Only useful for output pins. - #[inline] - pub fn configure_pulse_mode(&mut self, enable: bool, default_state: PinState) { - self.gpio.modify_pulse(|mut value| { - if enable { - value |= 1 << self.id.offset; - } else { - value &= !(1 << self.id.offset); - } - value - }); - self.gpio.modify_pulsebase(|mut value| { - if default_state == PinState::High { - value |= 1 << self.id.offset; - } else { - value &= !(1 << self.id.offset); - } - value - }); - } - - /// Only useful for output pins - #[inline] - pub fn configure_delay(&mut self, delay_1: bool, delay_2: bool) { - self.gpio.modify_delay1(|mut value| { - if delay_1 { - value |= 1 << self.id.offset; - } else { - value &= !(1 << self.id.offset); - } - value - }); - self.gpio.modify_delay2(|mut value| { - if delay_2 { - value |= 1 << self.id.offset; - } else { - value &= !(1 << self.id.offset); - } - value - }); - } - - #[cfg(feature = "vor1x")] - /// Configure the IRQSEL peripheral for this particular pin with the given interrupt ID. - pub fn configure_irqsel(&mut self, id: va108xx::Interrupt) { - let irqsel = unsafe { va108xx::Irqsel::steal() }; - enable_peripheral_clock(PeripheralSelect::Irqsel); - match self.id().port() { - // Set the correct interrupt number in the IRQSEL register - super::Port::A => { - irqsel - .porta0(self.id().offset()) - .write(|w| unsafe { w.bits(id as u32) }); - } - super::Port::B => { - irqsel - .portb0(self.id().offset()) - .write(|w| unsafe { w.bits(id as u32) }); - } - } - } - - #[cfg(feature = "vor1x")] - /// Reset the IRQSEL peripheral value for this particular pin. - pub fn reset_irqsel(&mut self) { - let irqsel = unsafe { va108xx::Irqsel::steal() }; - enable_peripheral_clock(PeripheralSelect::Irqsel); - match self.id().port() { - // Set the correct interrupt number in the IRQSEL register - super::Port::A => { - irqsel - .porta0(self.id().offset()) - .write(|w| unsafe { w.bits(u32::MAX) }); - } - super::Port::B => { - irqsel - .portb0(self.id().offset()) - .write(|w| unsafe { w.bits(u32::MAX) }); - } - } - } - - #[inline(always)] - pub const fn mask_32(&self) -> u32 { - 1 << self.id.offset() - } -} diff --git a/vorago-shared-periphs/src/gpio/mod.rs b/vorago-shared-periphs/src/gpio/mod.rs deleted file mode 100644 index 4d15e72..0000000 --- a/vorago-shared-periphs/src/gpio/mod.rs +++ /dev/null @@ -1,374 +0,0 @@ -//! GPIO support module. -use core::convert::Infallible; - -pub use crate::ioconfig::{FilterClkSel, FilterType, regs::FunSel}; -pub use embedded_hal::digital::PinState; -pub use ll::{InterruptEdge, InterruptLevel, PinId, Port, Pull}; - -pub mod asynch; -pub mod ll; -pub mod regs; - -/// Trait implemented by data structures assocaited with pin identifiacation. -pub trait PinIdProvider { - const ID: ll::PinId; -} - -/// Primary Pin structure for the physical pins exposed by Vorago MCUs. -/// -/// This pin structure is only used for resource management and does not do anything on its -/// own. -pub struct Pin { - phantom: core::marker::PhantomData, -} - -impl Pin { - #[allow(clippy::new_without_default)] - #[doc(hidden)] - pub const fn __new() -> Self { - Self { - phantom: core::marker::PhantomData, - } - } - - /// Create a new pin instance. - /// - /// # Safety - /// - /// This circumvents ownership rules of the HAL and allows creating multiple instances - /// of the same pin. - pub const unsafe fn steal() -> Self { - Self::__new() - } -} - -/// Push-Pull output pin. -#[derive(Debug)] -pub struct Output(ll::LowLevelGpio); - -impl Output { - pub fn new(_pin: Pin, init_level: PinState) -> Self { - let mut ll = ll::LowLevelGpio::new(I::ID); - ll.configure_as_output_push_pull(init_level); - Output(ll) - } - - #[inline] - pub fn port(&self) -> Port { - self.0.port() - } - - #[inline] - pub fn offset(&self) -> usize { - self.0.offset() - } - - #[inline] - pub fn set_high(&mut self) { - self.0.set_high(); - } - - #[inline] - pub fn set_low(&mut self) { - self.0.set_low(); - } - - #[inline] - pub fn is_set_high(&self) -> bool { - self.0.is_set_high() - } - - #[inline] - pub fn is_set_low(&self) -> bool { - self.0.is_set_low() - } - - /// Toggle pin output with dedicated HW feature. - #[inline] - pub fn toggle(&mut self) { - self.0.toggle(); - } -} - -impl embedded_hal::digital::ErrorType for Output { - type Error = Infallible; -} - -impl embedded_hal::digital::OutputPin for Output { - fn set_low(&mut self) -> Result<(), Self::Error> { - self.0.set_low(); - Ok(()) - } - - fn set_high(&mut self) -> Result<(), Self::Error> { - self.0.set_high(); - Ok(()) - } -} - -impl embedded_hal::digital::StatefulOutputPin for Output { - fn is_set_high(&mut self) -> Result { - Ok(self.0.is_set_high()) - } - - fn is_set_low(&mut self) -> Result { - Ok(self.0.is_set_low()) - } - - /// Toggle pin output with dedicated HW feature. - fn toggle(&mut self) -> Result<(), Self::Error> { - self.0.toggle(); - Ok(()) - } -} - -/// Input pin. -/// -/// Can be created as a floating input pin or as an input pin with pull-up or pull-down. -#[derive(Debug)] -pub struct Input(ll::LowLevelGpio); - -impl Input { - pub fn new_floating(_pin: Pin) -> Self { - let mut ll = ll::LowLevelGpio::new(I::ID); - ll.configure_as_input_floating(); - Input(ll) - } - - pub fn new_with_pull(_pin: Pin, pull: Pull) -> Self { - let mut ll = ll::LowLevelGpio::new(I::ID); - ll.configure_as_input_with_pull(pull); - Input(ll) - } - - #[inline] - pub fn id(&self) -> PinId { - self.0.id() - } - - #[cfg(feature = "vor1x")] - #[inline] - pub fn enable_interrupt(&mut self, irq_cfg: crate::InterruptConfig) { - self.0.enable_interrupt(irq_cfg); - } - - #[inline] - pub fn configure_edge_interrupt(&mut self, edge: InterruptEdge) { - self.0.configure_edge_interrupt(edge); - } - - #[inline] - pub fn configure_level_interrupt(&mut self, edge: InterruptLevel) { - self.0.configure_level_interrupt(edge); - } - - #[inline] - pub fn configure_delay(&mut self, delay_1: bool, delay_2: bool) { - self.0.configure_delay(delay_1, delay_2); - } - - #[inline] - pub fn configure_filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) { - self.0.configure_filter_type(filter, clksel); - } - - #[inline] - pub fn is_low(&self) -> bool { - self.0.is_low() - } - - #[inline] - pub fn is_high(&self) -> bool { - self.0.is_high() - } -} - -impl embedded_hal::digital::ErrorType for Input { - type Error = Infallible; -} - -impl embedded_hal::digital::InputPin for Input { - fn is_low(&mut self) -> Result { - Ok(self.0.is_low()) - } - - fn is_high(&mut self) -> Result { - Ok(self.0.is_high()) - } -} - -#[derive(Debug)] -pub enum PinMode { - InputFloating, - InputWithPull(Pull), - OutputPushPull, - OutputOpenDrain, -} - -impl PinMode { - pub fn is_input(&self) -> bool { - matches!(self, PinMode::InputFloating | PinMode::InputWithPull(_)) - } - - pub fn is_output(&self) -> bool { - !self.is_input() - } -} - -/// Flex pin abstraction which can be dynamically re-configured. -/// -/// The following functions can be configured at run-time: -/// -/// - Input Floating -/// - Input with Pull-Up -/// - Output Push-Pull -/// - Output Open-Drain. -/// -/// Flex pins are always floating input pins after construction. -#[derive(Debug)] -pub struct Flex { - ll: ll::LowLevelGpio, - mode: PinMode, -} - -impl Flex { - pub fn new(_pin: Pin) -> Self { - let mut ll = ll::LowLevelGpio::new(I::ID); - ll.configure_as_input_floating(); - Flex { - ll, - mode: PinMode::InputFloating, - } - } - - #[inline] - pub fn port(&self) -> Port { - self.ll.port() - } - - #[inline] - pub fn offset(&self) -> usize { - self.ll.offset() - } - - /// Reads the input state of the pin, regardless of configured mode. - #[inline] - pub fn is_low(&self) -> bool { - self.ll.is_low() - } - - /// Reads the input state of the pin, regardless of configured mode. - #[inline] - pub fn is_high(&self) -> bool { - self.ll.is_high() - } - - /// If the pin is configured as an input pin, this function does nothing. - #[inline] - pub fn set_low(&mut self) { - if !self.mode.is_input() { - return; - } - self.ll.set_low(); - } - - /// If the pin is configured as an input pin, this function does nothing. - #[inline] - pub fn set_high(&mut self) { - if !self.mode.is_input() { - return; - } - self.ll.set_high(); - } -} - -impl embedded_hal::digital::ErrorType for Flex { - type Error = Infallible; -} - -impl embedded_hal::digital::InputPin for Flex { - /// Reads the input state of the pin, regardless of configured mode. - fn is_low(&mut self) -> Result { - Ok(self.ll.is_low()) - } - - /// Reads the input state of the pin, regardless of configured mode. - fn is_high(&mut self) -> Result { - Ok(self.ll.is_high()) - } -} - -impl embedded_hal::digital::OutputPin for Flex { - /// If the pin is configured as an input pin, this function does nothing. - fn set_low(&mut self) -> Result<(), Self::Error> { - self.set_low(); - Ok(()) - } - - /// If the pin is configured as an input pin, this function does nothing. - fn set_high(&mut self) -> Result<(), Self::Error> { - self.set_high(); - Ok(()) - } -} - -impl embedded_hal::digital::StatefulOutputPin for Flex { - /// If the pin is not configured as a stateful output pin like Output Push-Pull, the result - /// of this function is undefined. - fn is_set_high(&mut self) -> Result { - Ok(self.ll.is_set_high()) - } - - /// If the pin is not configured as a stateful output pin like Output Push-Pull, the result - /// of this function is undefined. - fn is_set_low(&mut self) -> Result { - Ok(self.ll.is_set_low()) - } - - /// Toggle pin output. - /// - /// If the pin is not configured as a stateful output pin like Output Push-Pull, the result - /// of this function is undefined. - fn toggle(&mut self) -> Result<(), Self::Error> { - self.ll.toggle(); - Ok(()) - } -} - -/// IO peripheral pin structure. -/// -/// Can be used to configure pins as IO peripheral pins. -pub struct IoPeriphPin { - ll: ll::LowLevelGpio, - fun_sel: FunSel, -} - -impl IoPeriphPin { - pub fn new_with_pin( - _pin: Pin, - fun_sel: FunSel, - pull: Option, - ) -> Self { - let mut ll = ll::LowLevelGpio::new(I::ID); - ll.configure_as_peripheral_pin(fun_sel, pull); - IoPeriphPin { ll, fun_sel } - } - - pub fn new(pin_id: PinId, fun_sel: FunSel, pull: Option) -> Self { - let mut ll = ll::LowLevelGpio::new(pin_id); - ll.configure_as_peripheral_pin(fun_sel, pull); - IoPeriphPin { ll, fun_sel } - } - - pub fn port(&self) -> Port { - self.ll.port() - } - - pub fn offset(&self) -> usize { - self.ll.offset() - } - - pub fn fun_sel(&self) -> FunSel { - self.fun_sel - } -} diff --git a/vorago-shared-periphs/src/gpio/regs.rs b/vorago-shared-periphs/src/gpio/regs.rs deleted file mode 100644 index e0ae9db..0000000 --- a/vorago-shared-periphs/src/gpio/regs.rs +++ /dev/null @@ -1,126 +0,0 @@ -use crate::Port; - -cfg_if::cfg_if! { - if #[cfg(feature = "vor1x")] { - /// PORT A base address. - pub const GPIO_0_BASE: usize = 0x5000_0000; - /// PORT B base address. - pub const GPIO_1_BASE: usize = 0x5000_1000; - } else if #[cfg(feature = "vor4x")] { - /// PORT A base address. - pub const GPIO_0_BASE: usize = 0x4001_2000; - /// PORT B base address. - pub const GPIO_1_BASE: usize = 0x4001_2400; - /// PORT C base address. - pub const GPIO_2_BASE: usize = 0x4001_2800; - /// PORT D base address. - pub const GPIO_3_BASE: usize = 0x4001_2C00; - /// PORT E base address. - pub const GPIO_4_BASE: usize = 0x4001_3000; - /// PORT F base address. - pub const GPIO_5_BASE: usize = 0x4001_3400; - /// PORT G base address. - pub const GPIO_6_BASE: usize = 0x4001_3800; - } -} - -#[derive(derive_mmio::Mmio)] -#[mmio(no_ctors)] -#[repr(C)] -pub struct Gpio { - #[mmio(PureRead)] - data_in: u32, - #[mmio(PureRead)] - data_in_raw: u32, - data_out: u32, - data_out_raw: u32, - #[mmio(Write)] - set_out: u32, - #[mmio(Write)] - clr_out: u32, - #[mmio(Write)] - tog_out: u32, - data_mask: u32, - /// Direction bits. 1 for output, 0 for input. - dir: u32, - pulse: u32, - pulsebase: u32, - delay1: u32, - delay2: u32, - irq_sen: u32, - irq_edge: u32, - irq_evt: 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 enabled and active interrupts. Called IRQ_end by Vorago. - #[mmio(PureRead)] - irq_status: u32, - #[mmio(PureRead)] - edge_status: u32, - - #[cfg(feature = "vor1x")] - _reserved: [u32; 0x3eb], - #[cfg(feature = "vor4x")] - _reserved: [u32; 0xeb], - - /// Peripheral ID. Vorago 1x reset value: 0x0040_07e1. Vorago 4x reset value: 0x0210_07E9. - perid: u32, -} - -cfg_if::cfg_if! { - if #[cfg(feature = "vor1x")] { - static_assertions::const_assert_eq!(core::mem::size_of::(), 0x1000); - } else if #[cfg(feature = "vor4x")] { - static_assertions::const_assert_eq!(core::mem::size_of::(), 0x400); - } -} - -impl Gpio { - const fn new_mmio_at(base: usize) -> MmioGpio<'static> { - MmioGpio { - ptr: base as *mut _, - phantom: core::marker::PhantomData, - } - } - - pub const fn new_mmio(port: Port) -> MmioGpio<'static> { - match port { - Port::A => Self::new_mmio_at(GPIO_0_BASE), - Port::B => Self::new_mmio_at(GPIO_1_BASE), - #[cfg(feature = "vor4x")] - Port::C => Self::new_mmio_at(GPIO_2_BASE), - #[cfg(feature = "vor4x")] - Port::D => Self::new_mmio_at(GPIO_3_BASE), - #[cfg(feature = "vor4x")] - Port::E => Self::new_mmio_at(GPIO_4_BASE), - #[cfg(feature = "vor4x")] - Port::F => Self::new_mmio_at(GPIO_5_BASE), - #[cfg(feature = "vor4x")] - Port::G => Self::new_mmio_at(GPIO_6_BASE), - } - } -} - -impl MmioGpio<'_> { - pub fn port(&self) -> Port { - match unsafe { self.ptr() } as usize { - GPIO_0_BASE => Port::A, - GPIO_1_BASE => Port::B, - #[cfg(feature = "vor4x")] - GPIO_2_BASE => Port::C, - #[cfg(feature = "vor4x")] - GPIO_3_BASE => Port::D, - #[cfg(feature = "vor4x")] - GPIO_4_BASE => Port::E, - #[cfg(feature = "vor4x")] - GPIO_5_BASE => Port::F, - #[cfg(feature = "vor4x")] - GPIO_6_BASE => Port::G, - // Constructors were disabled, so this should really not happen. - _ => panic!("unexpected base address of GPIO register block"), - } - } -} diff --git a/vorago-shared-periphs/src/i2c/mod.rs b/vorago-shared-periphs/src/i2c/mod.rs deleted file mode 100644 index 758a6e7..0000000 --- a/vorago-shared-periphs/src/i2c/mod.rs +++ /dev/null @@ -1,665 +0,0 @@ -pub mod regs; - -use crate::{ - enable_peripheral_clock, sealed::Sealed, sysconfig::reset_peripheral_for_cycles, time::Hertz, - PeripheralSelect, -}; -use arbitrary_int::{u10, u11, u20, u4}; -use core::marker::PhantomData; -use embedded_hal::i2c::{self, Operation, SevenBitAddress, TenBitAddress}; -use regs::ClkTimeoutLimit; -pub use regs::{Bank, I2cSpeed, RxFifoFullMode, TxFifoEmptyMode}; - -#[cfg(feature = "vor1x")] -use va108xx as pac; - -//================================================================================================== -// Defintions -//================================================================================================== - -const CLK_100K: Hertz = Hertz::from_raw(100_000); -const CLK_400K: Hertz = Hertz::from_raw(400_000); -const MIN_CLK_400K: Hertz = Hertz::from_raw(8_000_000); - -#[derive(Debug, PartialEq, Eq, thiserror::Error)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[error("clock too slow for fast I2C mode")] -pub struct ClockTooSlowForFastI2cError; - -#[derive(Debug, PartialEq, Eq, thiserror::Error)] -#[error("invalid timing parameters")] -pub struct InvalidTimingParamsError; - -#[derive(Debug, PartialEq, Eq, thiserror::Error)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Error { - #[error("arbitration lost")] - ArbitrationLost, - #[error("nack address")] - NackAddr, - /// Data not acknowledged in write operation - #[error("data not acknowledged in write operation")] - NackData, - /// Not enough data received in read operation - #[error("insufficient data received")] - InsufficientDataReceived, - /// Number of bytes in transfer too large (larger than 0x7fe) - #[error("data too large (larger than 0x7fe)")] - DataTooLarge, - #[error("clock timeout, SCL was low for {0} clock cycles")] - ClockTimeout(u20), -} - -#[derive(Debug, PartialEq, Eq, thiserror::Error)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum InitError { - /// Wrong address used in constructor - #[error("wrong address mode")] - WrongAddrMode, - /// APB1 clock is too slow for fast I2C mode. - #[error("clock too slow for fast I2C mode: {0}")] - ClkTooSlow(#[from] ClockTooSlowForFastI2cError), -} - -impl embedded_hal::i2c::Error for Error { - fn kind(&self) -> embedded_hal::i2c::ErrorKind { - match self { - Error::ArbitrationLost => embedded_hal::i2c::ErrorKind::ArbitrationLoss, - Error::NackAddr => { - embedded_hal::i2c::ErrorKind::NoAcknowledge(i2c::NoAcknowledgeSource::Address) - } - Error::NackData => { - embedded_hal::i2c::ErrorKind::NoAcknowledge(i2c::NoAcknowledgeSource::Data) - } - Error::DataTooLarge | Error::InsufficientDataReceived | Error::ClockTimeout(_) => { - embedded_hal::i2c::ErrorKind::Other - } - } - } -} - -#[derive(Debug, PartialEq, Copy, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum I2cCmd { - Start = 0b01, - Stop = 0b10, - StartWithStop = 0b11, - Cancel = 0b100, -} - -#[derive(Debug, PartialEq, Eq, Copy, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum I2cAddress { - Regular(u8), - TenBit(u16), -} - -impl I2cAddress { - pub fn ten_bit_addr(&self) -> bool { - match self { - I2cAddress::Regular(_) => false, - I2cAddress::TenBit(_) => true, - } - } - pub fn raw(&self) -> u16 { - match self { - I2cAddress::Regular(addr) => *addr as u16, - I2cAddress::TenBit(addr) => *addr, - } - } -} - -pub type I2cRegBlock = pac::i2ca::RegisterBlock; - -/// Common trait implemented by all PAC peripheral access structures. The register block -/// format is the same for all SPI blocks. -pub trait I2cMarker: Sealed { - const ID: Bank; - const PERIPH_SEL: PeripheralSelect; -} - -impl I2cMarker for pac::I2ca { - const ID: Bank = Bank::I2c0; - const PERIPH_SEL: PeripheralSelect = PeripheralSelect::I2c0; -} -impl Sealed for pac::I2ca {} - -impl I2cMarker for pac::I2cb { - const ID: Bank = Bank::I2c1; - const PERIPH_SEL: PeripheralSelect = PeripheralSelect::I2c1; -} -impl Sealed for pac::I2cb {} - -//================================================================================================== -// Config -//================================================================================================== - -fn calc_clk_div(sys_clk: Hertz, speed_mode: I2cSpeed) -> Result { - if speed_mode == I2cSpeed::Regular100khz { - Ok(((sys_clk.raw() / CLK_100K.raw() / 20) - 1) as u8) - } else { - if sys_clk.raw() < MIN_CLK_400K.raw() { - return Err(ClockTooSlowForFastI2cError); - } - Ok(((sys_clk.raw() / CLK_400K.raw() / 25) - 1) as u8) - } -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct TimingConfig { - pub t_rise: u4, - pub t_fall: u4, - pub t_high: u4, - pub t_low: u4, - pub tsu_stop: u4, - pub tsu_start: u4, - pub thd_start: u4, - pub t_buf: u4, -} - -/// Default configuration are the register reset value which are used by default. -impl Default for TimingConfig { - fn default() -> Self { - TimingConfig { - t_rise: u4::new(0b0010), - t_fall: u4::new(0b0001), - t_high: u4::new(0b1000), - t_low: u4::new(0b1001), - tsu_stop: u4::new(0b1000), - tsu_start: u4::new(0b1010), - thd_start: u4::new(0b1000), - t_buf: u4::new(0b1010), - } - } -} - -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct MasterConfig { - pub tx_empty_mode: TxFifoEmptyMode, - pub rx_full_mode: RxFifoFullMode, - /// Enable the analog delay glitch filter - pub alg_filt: bool, - /// Enable the digital glitch filter - pub dlg_filt: bool, - pub timing_config: Option, - /// See [I2cMaster::set_clock_low_timeout] documentation. - pub timeout: Option, - // Loopback mode - // lbm: bool, -} - -impl Default for MasterConfig { - fn default() -> Self { - MasterConfig { - tx_empty_mode: TxFifoEmptyMode::Stall, - rx_full_mode: RxFifoFullMode::Stall, - alg_filt: false, - dlg_filt: false, - timeout: None, - timing_config: None, - } - } -} - -impl Sealed for MasterConfig {} - -#[derive(Debug, PartialEq, Eq)] -enum WriteCompletionCondition { - Idle, - Waiting, -} - -struct TimeoutGuard { - clk_timeout_enabled: bool, - regs: regs::MmioI2c<'static>, -} - -impl TimeoutGuard { - fn new(regs: ®s::MmioI2c<'static>) -> Self { - let clk_timeout_enabled = regs.read_clk_timeout_limit().value().value() > 0; - let mut guard = TimeoutGuard { - clk_timeout_enabled, - regs: unsafe { regs.clone() }, - }; - if clk_timeout_enabled { - // Clear any interrupts which might be pending. - guard.regs.write_irq_clear( - regs::InterruptClear::builder() - .with_clock_timeout(true) - .with_tx_overflow(false) - .with_rx_overflow(false) - .build(), - ); - guard.regs.modify_irq_enb(|mut value| { - value.set_clock_timeout(true); - value - }); - } - guard - } - - fn timeout_enabled(&self) -> bool { - self.clk_timeout_enabled - } -} - -impl Drop for TimeoutGuard { - fn drop(&mut self) { - if self.clk_timeout_enabled { - self.regs.modify_irq_enb(|mut value| { - value.set_clock_timeout(false); - value - }); - } - } -} -//================================================================================================== -// I2C Master -//================================================================================================== - -pub struct I2cMaster { - id: Bank, - regs: regs::MmioI2c<'static>, - addr: PhantomData, -} - -impl I2cMaster { - pub fn new( - sysclk: Hertz, - _i2c: I2c, - cfg: MasterConfig, - speed_mode: I2cSpeed, - ) -> Result { - reset_peripheral_for_cycles(I2c::PERIPH_SEL, 2); - enable_peripheral_clock(I2c::PERIPH_SEL); - let mut regs = regs::I2c::new_mmio(I2c::ID); - - let clk_div = calc_clk_div(sysclk, speed_mode)?; - regs.write_clkscale( - regs::ClkScale::builder() - .with_div(clk_div) - .with_fastmode(speed_mode) - .build(), - ); - regs.modify_control(|mut value| { - value.set_tx_fifo_empty_mode(cfg.tx_empty_mode); - value.set_rx_fifo_full_mode(cfg.rx_full_mode); - value.set_analog_filter(cfg.alg_filt); - value.set_digital_filter(cfg.dlg_filt); - value - }); - - if let Some(ref timing_cfg) = cfg.timing_config { - regs.modify_control(|mut value| { - value.set_enable_timing_config(true); - value - }); - regs.write_timing_config( - regs::TimingConfig::builder() - .with_t_rise(timing_cfg.t_rise) - .with_t_fall(timing_cfg.t_fall) - .with_t_high(timing_cfg.t_high) - .with_t_low(timing_cfg.t_low) - .with_tsu_stop(timing_cfg.tsu_stop) - .with_tsu_start(timing_cfg.tsu_start) - .with_thd_start(timing_cfg.thd_start) - .with_t_buf(timing_cfg.t_buf) - .build(), - ); - } - regs.write_fifo_clear( - regs::FifoClear::builder() - .with_tx_fifo(true) - .with_rx_fifo(true) - .build(), - ); - if let Some(timeout) = cfg.timeout { - regs.write_clk_timeout_limit(ClkTimeoutLimit::new(timeout)); - } - let mut i2c_master = I2cMaster { - addr: PhantomData, - id: I2c::ID, - regs, - }; - i2c_master.enable(); - Ok(i2c_master) - } - - pub fn id(&self) -> Bank { - self.id - } - - /// Configures the clock scale for a given speed mode setting - pub fn set_clk_scale( - &mut self, - sys_clk: Hertz, - speed_mode: I2cSpeed, - ) -> Result<(), ClockTooSlowForFastI2cError> { - self.disable(); - let clk_div = calc_clk_div(sys_clk, speed_mode)?; - self.regs.write_clkscale( - regs::ClkScale::builder() - .with_div(clk_div) - .with_fastmode(speed_mode) - .build(), - ); - self.enable(); - Ok(()) - } - - #[inline] - pub fn cancel_transfer(&mut self) { - self.regs.write_cmd( - regs::Command::builder() - .with_start(false) - .with_stop(false) - .with_cancel(true) - .build(), - ); - } - - #[inline] - pub fn clear_tx_fifo(&mut self) { - self.regs.write_fifo_clear( - regs::FifoClear::builder() - .with_tx_fifo(true) - .with_rx_fifo(false) - .build(), - ); - } - - #[inline] - pub fn clear_rx_fifo(&mut self) { - self.regs.write_fifo_clear( - regs::FifoClear::builder() - .with_tx_fifo(false) - .with_rx_fifo(true) - .build(), - ); - } - - /// Configure a timeout limit on the amount of time the I2C clock is seen to be low. - /// The timeout is specified as I2C clock cycles. - /// - /// If the timeout is enabled, the blocking transaction handlers provided by the [I2cMaster] - /// will poll the interrupt status register to check for timeouts. This can be used to avoid - /// hang-ups of the I2C bus. - #[inline] - pub fn set_clock_low_timeout(&mut self, clock_cycles: u20) { - self.regs.write_clk_timeout_limit(ClkTimeoutLimit::new(clock_cycles)); - } - - #[inline] - pub fn disable_clock_low_timeout(&mut self) { - self.regs.write_clk_timeout_limit(ClkTimeoutLimit::new(u20::new(0))); - } - - #[inline] - pub fn enable(&mut self) { - self.regs.modify_control(|mut value| { - value.set_enable(true); - value - }); - } - - #[inline] - pub fn disable(&mut self) { - self.regs.modify_control(|mut value| { - value.set_enable(false); - value - }); - } - - #[inline(always)] - fn write_fifo_unchecked(&mut self, word: u8) { - self.regs.write_data(regs::Data::new(word)); - } - - #[inline(always)] - fn read_fifo_unchecked(&self) -> u8 { - self.regs.read_data().data() - } - - #[inline] - pub fn read_status(&mut self) -> regs::Status { - self.regs.read_status() - } - - #[inline] - pub fn write_command(&mut self, cmd: I2cCmd) { - self.regs - .write_cmd(regs::Command::new_with_raw_value(cmd as u32)); - } - - #[inline] - pub fn write_address(&mut self, addr: I2cAddress, dir: regs::Direction) { - self.regs.write_address( - regs::Address::builder() - .with_direction(dir) - .with_address(u10::new(addr.raw())) - .with_a10_mode(addr.ten_bit_addr()) - .build(), - ); - } - - fn error_handler_write(&mut self, init_cmd: I2cCmd) { - if init_cmd == I2cCmd::Start { - self.write_command(I2cCmd::Stop); - } - // The other case is start with stop where, so a CANCEL command should not be necessary - // because the hardware takes care of it. - self.clear_tx_fifo(); - } - - /// Blocking write transaction on the I2C bus. - pub fn write_blocking(&mut self, addr: I2cAddress, output: &[u8]) -> Result<(), Error> { - self.write_blocking_generic( - I2cCmd::StartWithStop, - addr, - output, - WriteCompletionCondition::Idle, - ) - } - - /// Blocking read transaction on the I2C bus. - pub fn read_blocking(&mut self, addr: I2cAddress, buffer: &mut [u8]) -> Result<(), Error> { - let len = buffer.len(); - if len > 0x7fe { - return Err(Error::DataTooLarge); - } - // Clear the receive FIFO - self.clear_rx_fifo(); - - let timeout_guard = TimeoutGuard::new(&self.regs); - - // Load number of words - self.regs - .write_words(regs::Words::new(u11::new(len as u16))); - // Load address - self.write_address(addr, regs::Direction::Receive); - - let mut buf_iter = buffer.iter_mut(); - let mut read_bytes = 0; - // Start receive transfer - self.write_command(I2cCmd::StartWithStop); - loop { - let status = self.read_status(); - if status.arb_lost() { - self.clear_rx_fifo(); - return Err(Error::ArbitrationLost); - } - if status.nack_addr() { - self.clear_rx_fifo(); - return Err(Error::NackAddr); - } - if status.idle() { - if read_bytes != len { - return Err(Error::InsufficientDataReceived); - } - return Ok(()); - } - if timeout_guard.timeout_enabled() && self.regs.read_irq_status().clock_timeout() { - return Err(Error::ClockTimeout( - self.regs.read_clk_timeout_limit().value(), - )); - } - if status.rx_not_empty() { - if let Some(next_byte) = buf_iter.next() { - *next_byte = self.read_fifo_unchecked(); - } - read_bytes += 1; - } - } - } - - fn write_blocking_generic( - &mut self, - init_cmd: I2cCmd, - addr: I2cAddress, - output: &[u8], - end_condition: WriteCompletionCondition, - ) -> Result<(), Error> { - let len = output.len(); - if len > 0x7fe { - return Err(Error::DataTooLarge); - } - // Clear the send FIFO - self.clear_tx_fifo(); - - let timeout_guard = TimeoutGuard::new(&self.regs); - - // Load number of words - self.regs - .write_words(regs::Words::new(u11::new(len as u16))); - let mut bytes = output.iter(); - // FIFO has a depth of 16. We load slightly above the trigger level - // but not all of it because the transaction might fail immediately - const FILL_DEPTH: usize = 12; - - let mut current_index = core::cmp::min(FILL_DEPTH, len); - // load the FIFO - for _ in 0..current_index { - self.write_fifo_unchecked(*bytes.next().unwrap()); - } - self.write_address(addr, regs::Direction::Send); - self.write_command(init_cmd); - loop { - let status = self.regs.read_status(); - if status.arb_lost() { - self.error_handler_write(init_cmd); - return Err(Error::ArbitrationLost); - } - if status.nack_addr() { - self.error_handler_write(init_cmd); - return Err(Error::NackAddr); - } - if status.nack_data() { - self.error_handler_write(init_cmd); - return Err(Error::NackData); - } - match end_condition { - WriteCompletionCondition::Idle => { - if status.idle() { - return Ok(()); - } - } - - WriteCompletionCondition::Waiting => { - if status.waiting() { - return Ok(()); - } - } - } - if timeout_guard.timeout_enabled() && self.regs.read_irq_status().clock_timeout() { - return Err(Error::ClockTimeout( - self.regs.read_clk_timeout_limit().value(), - )); - } - if status.tx_not_full() && current_index < len { - self.write_fifo_unchecked(output[current_index]); - current_index += 1; - } - } - } - - /// Blocking write-read transaction on the I2C bus. - pub fn write_read_blocking( - &mut self, - address: I2cAddress, - write: &[u8], - read: &mut [u8], - ) -> Result<(), Error> { - self.write_blocking_generic( - I2cCmd::Start, - address, - write, - WriteCompletionCondition::Waiting, - )?; - self.read_blocking(address, read) - } -} - -//====================================================================================== -// Embedded HAL I2C implementations -//====================================================================================== - -impl embedded_hal::i2c::ErrorType for I2cMaster { - type Error = Error; -} - -impl embedded_hal::i2c::I2c for I2cMaster { - fn transaction( - &mut self, - address: SevenBitAddress, - operations: &mut [Operation<'_>], - ) -> Result<(), Self::Error> { - for operation in operations { - match operation { - Operation::Read(buf) => self.read_blocking(I2cAddress::Regular(address), buf)?, - Operation::Write(buf) => self.write_blocking(I2cAddress::Regular(address), buf)?, - } - } - Ok(()) - } - - fn write_read( - &mut self, - address: u8, - write: &[u8], - read: &mut [u8], - ) -> Result<(), Self::Error> { - let addr = I2cAddress::Regular(address); - self.write_read_blocking(addr, write, read) - } -} - -impl embedded_hal::i2c::ErrorType for I2cMaster { - type Error = Error; -} - -impl embedded_hal::i2c::I2c for I2cMaster { - fn transaction( - &mut self, - address: TenBitAddress, - operations: &mut [Operation<'_>], - ) -> Result<(), Self::Error> { - for operation in operations { - match operation { - Operation::Read(buf) => self.read_blocking(I2cAddress::TenBit(address), buf)?, - Operation::Write(buf) => self.write_blocking(I2cAddress::TenBit(address), buf)?, - } - } - Ok(()) - } - - fn write_read( - &mut self, - address: TenBitAddress, - write: &[u8], - read: &mut [u8], - ) -> Result<(), Self::Error> { - let addr = I2cAddress::TenBit(address); - self.write_read_blocking(addr, write, read) - } -} diff --git a/vorago-shared-periphs/src/i2c/regs.rs b/vorago-shared-periphs/src/i2c/regs.rs deleted file mode 100644 index 7ce774e..0000000 --- a/vorago-shared-periphs/src/i2c/regs.rs +++ /dev/null @@ -1,671 +0,0 @@ -use core::marker::PhantomData; - -use arbitrary_int::{u10, u11, u20, u4, u5, u9}; - -pub use crate::shared::{FifoClear, TriggerLevel}; - -cfg_if::cfg_if! { - if #[cfg(feature = "vor1x")] { - /// I2C A base address - pub const BASE_ADDR_0: usize = 0x4006_0000; - /// I2C B base address - pub const BASE_ADDR_1: usize = 0x4006_1000; - } else if #[cfg(feature = "vor4x")] { - /// I2C 0 base address - pub const BASE_ADDR_0: usize = 0x4001_6000; - /// I2C 1 base address - pub const BASE_ADDR_1: usize = 0x4001_6400; - /// I2C 2 base address - pub const BASE_ADDR_2: usize = 0x4001_6800; - } -} - -#[derive(Debug, PartialEq, Eq, Copy, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Bank { - I2c0 = 0, - I2c1 = 1, - #[cfg(feature = "vor4x")] - I2c2 = 2, -} - -impl Bank { - /// Unsafely steal the I2C peripheral block for the given port. - /// - /// # Safety - /// - /// Circumvents ownership and safety guarantees by the HAL. - pub unsafe fn steal_regs(&self) -> MmioI2c<'static> { - I2c::new_mmio(*self) - } -} - -#[bitbybit::bitenum(u1, exhaustive = true)] -#[derive(Default, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum TxFifoEmptyMode { - /// I2C clock is stretched until data is available. - #[default] - Stall = 0, - EndTransaction = 1, -} - -#[bitbybit::bitenum(u1, exhaustive = true)] -#[derive(Default, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum RxFifoFullMode { - /// I2C clock is stretched until data is available. - #[default] - Stall = 0, - Nack = 1, -} - -#[bitbybit::bitfield(u32)] -#[derive(Debug)] -pub struct Control { - #[bit(0, r)] - clk_enabled: bool, - #[bit(1, r)] - enabled: bool, - #[bit(2, rw)] - enable: bool, - #[bit(3, rw)] - tx_fifo_empty_mode: TxFifoEmptyMode, - #[bit(4, rw)] - rx_fifo_full_mode: RxFifoFullMode, - /// Enables the analog delay glitch filter. - #[bit(5, rw)] - analog_filter: bool, - /// Enables the digital glitch filter. - #[bit(6, rw)] - digital_filter: bool, - #[bit(8, rw)] - loopback: bool, - #[bit(9, rw)] - enable_timing_config: bool, -} - -#[derive(Debug, PartialEq, Eq)] -#[bitbybit::bitenum(u1, exhaustive = true)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum I2cSpeed { - Regular100khz = 0, - Fast400khz = 1, -} - -#[bitbybit::bitfield(u32, default = 0x0)] -#[derive(Debug)] -pub struct ClkScale { - /// Clock divide value. Reset value: 0x18. - #[bits(0..=7, rw)] - div: u8, - #[bit(31, rw)] - fastmode: I2cSpeed, -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct Words(arbitrary_int::UInt); - -impl Words { - pub const fn new(value: u11) -> Self { - Words(arbitrary_int::UInt::::new(value.value() as u32)) - } - pub const fn value(&self) -> u11 { - u11::new(self.0.value() as u16) - } -} - -#[bitbybit::bitenum(u1, exhaustive = true)] -#[derive(Default, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Direction { - #[default] - Send = 0, - Receive = 1, -} - -#[bitbybit::bitfield(u32, default = 0x0)] -#[derive(Debug)] -pub struct Address { - #[bit(0, rw)] - direction: Direction, - #[bits(1..=10, rw)] - address: u10, - /// Enables 10-bit addressing mode. - #[bit(15, rw)] - a10_mode: bool, -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub struct Data(arbitrary_int::UInt); - -impl Data { - pub const fn new(value: u8) -> Self { - Data(arbitrary_int::UInt::::new(value as u32)) - } - - pub const fn data(&self) -> u8 { - self.0.value() as u8 - } -} - -#[bitbybit::bitfield(u32, default = 0x0)] -#[derive(Debug)] -pub struct Command { - #[bit(0, w)] - start: bool, - #[bit(1, w)] - stop: bool, - #[bit(2, w)] - cancel: bool, -} - -#[bitbybit::bitfield(u32)] -#[derive(Debug)] -pub struct Status { - #[bit(0, r)] - i2c_idle: bool, - #[bit(1, r)] - idle: bool, - #[bit(2, r)] - waiting: bool, - #[bit(3, r)] - stalled: bool, - #[bit(4, r)] - arb_lost: bool, - #[bit(5, r)] - nack_addr: bool, - #[bit(6, r)] - nack_data: bool, - #[bit(8, r)] - rx_not_empty: bool, - #[bit(9, r)] - rx_full: bool, - #[bit(11, r)] - rx_trigger: bool, - #[bit(12, r)] - tx_empty: bool, - #[bit(13, r)] - tx_not_full: bool, - #[bit(15, r)] - tx_trigger: bool, - #[bit(30, r)] - raw_sda: bool, - #[bit(31, r)] - raw_scl: bool, -} - -#[bitbybit::bitfield(u32)] -#[derive(Debug)] -pub struct State { - #[bits(0..=3, rw)] - state: u4, - #[bits(4..=7, rw)] - step: u4, - #[bits(8..=12, rw)] - rx_fifo: u5, - #[bits(14..=18, rw)] - tx_fifo: u5, - #[bits(20..=28, rw)] - bitstate: u9, -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct DataCount(arbitrary_int::UInt); - -#[bitbybit::bitfield(u32)] -#[derive(Debug)] -pub struct InterruptControl { - #[bit(0, rw)] - i2c_idle: bool, - #[bit(1, rw)] - idle: bool, - #[bit(2, rw)] - waiting: bool, - #[bit(3, rw)] - stalled: bool, - #[bit(4, rw)] - arb_lost: bool, - #[bit(5, rw)] - nack_addr: bool, - #[bit(6, rw)] - nack_data: bool, - #[bit(7, rw)] - clock_timeout: bool, - #[bit(10, rw)] - tx_overflow: bool, - #[bit(11, rw)] - rx_overflow: bool, - #[bit(12, rw)] - tx_ready: bool, - #[bit(13, rw)] - rx_ready: bool, - #[bit(14, rw)] - tx_empty: bool, - #[bit(15, rw)] - rx_full: bool, -} - -#[bitbybit::bitfield(u32)] -#[derive(Debug)] -pub struct InterruptStatus { - #[bit(0, r)] - i2c_idle: bool, - #[bit(1, r)] - idle: bool, - #[bit(2, r)] - waiting: bool, - #[bit(3, r)] - stalled: bool, - #[bit(4, r)] - arb_lost: bool, - #[bit(5, r)] - nack_addr: bool, - #[bit(6, r)] - nack_data: bool, - #[bit(7, r)] - clock_timeout: bool, - #[bit(10, r)] - tx_overflow: bool, - #[bit(11, r)] - rx_overflow: bool, - #[bit(12, r)] - tx_ready: bool, - #[bit(13, r)] - rx_ready: bool, - #[bit(14, r)] - tx_empty: bool, - #[bit(15, r)] - rx_full: bool, -} - -#[bitbybit::bitfield(u32, default = 0x0)] -#[derive(Debug)] -pub struct InterruptClear { - #[bit(7, w)] - clock_timeout: bool, - #[bit(10, w)] - tx_overflow: bool, - #[bit(11, w)] - rx_overflow: bool, -} - -#[bitbybit::bitfield(u32)] -#[derive(Debug)] -pub struct TimingConfig { - /// Rise time. - #[bits(0..=3, rw)] - t_rise: u4, - /// Fall time. - #[bits(4..=7, rw)] - t_fall: u4, - /// Duty cycle high time of SCL. - #[bits(8..=11, rw)] - t_high: u4, - /// Duty cycle low time of SCL. - #[bits(12..=15, rw)] - t_low: u4, - /// Setup time for STOP. - #[bits(16..=19, rw)] - tsu_stop: u4, - /// Setup time for START. - #[bits(20..=23, rw)] - tsu_start: u4, - /// Data hold time. - #[bits(24..=27, rw)] - thd_start: u4, - /// TBus free time between STOP and START. - #[bits(28..=31, rw)] - t_buf: u4, -} - -pub struct ClkTimeoutLimit(pub arbitrary_int::UInt); - -impl ClkTimeoutLimit { - pub fn new(value: u20) -> Self { - ClkTimeoutLimit(arbitrary_int::UInt::::new(value.value())) - } - pub fn value(&self) -> u20 { - self.0 - } -} - -pub mod slave { - use super::{Data, DataCount, FifoClear, RxFifoFullMode, TriggerLevel, TxFifoEmptyMode}; - use arbitrary_int::{u10, u11, u3, u4, u5}; - - #[bitbybit::bitfield(u32)] - #[derive(Debug)] - pub struct Control { - #[bit(0, r)] - clk_enabled: bool, - #[bit(1, r)] - enabled: bool, - #[bit(2, rw)] - enable: bool, - #[bit(3, rw)] - tx_fifo_empty_mode: TxFifoEmptyMode, - #[bit(4, rw)] - rx_fifo_full_mode: RxFifoFullMode, - } - - #[bitbybit::bitfield(u32)] - #[derive(Debug)] - pub struct Maxwords { - #[bits(0..=10, rw)] - maxwords: u11, - #[bit(31, rw)] - enable: bool, - } - - #[bitbybit::bitfield(u32)] - #[derive(Debug)] - pub struct Address { - #[bit(0, rw)] - rw: bool, - #[bits(1..=10, rw)] - address: u10, - #[bit(15, rw)] - a10_mode: bool, - } - - #[bitbybit::bitfield(u32)] - #[derive(Debug)] - pub struct AddressMask { - /// Will normally be 0 to match both read and write addresses. - #[bit(0, rw)] - rw_mask: bool, - /// Reset value 0x3FF. - #[bits(1..=10, rw)] - mask: u10, - } - - #[bitbybit::bitenum(u1, exhaustive = true)] - #[derive(Default, Debug, PartialEq, Eq)] - pub enum Direction { - #[default] - MasterSend = 0, - MasterReceive = 1, - } - - #[bitbybit::bitfield(u32)] - #[derive(Debug)] - pub struct LastAddress { - #[bit(0, rw)] - direction: Direction, - #[bits(1..=10, rw)] - address: u10, - } - - #[bitbybit::bitfield(u32)] - #[derive(Debug)] - pub struct Status { - #[bit(0, r)] - completed: bool, - #[bit(1, r)] - idle: bool, - #[bit(2, r)] - waiting: bool, - #[bit(3, r)] - tx_stalled: bool, - #[bit(4, r)] - rx_stalled: bool, - #[bit(5, r)] - address_match: bool, - #[bit(6, r)] - nack_data: bool, - #[bit(7, r)] - rx_data_first: bool, - #[bit(8, r)] - rx_not_empty: bool, - #[bit(9, r)] - rx_full: bool, - #[bit(11, r)] - rx_trigger: bool, - #[bit(12, r)] - tx_empty: bool, - #[bit(13, r)] - tx_not_full: bool, - #[bit(15, r)] - tx_trigger: bool, - #[bit(28, r)] - raw_busy: bool, - #[bit(30, r)] - raw_sda: bool, - #[bit(31, r)] - raw_scl: bool, - } - - #[bitbybit::bitfield(u32)] - #[derive(Debug)] - pub struct State { - #[bits(0..=2, rw)] - state: u3, - #[bits(4..=7, rw)] - step: u4, - #[bits(8..=12, rw)] - rx_fifo: u5, - #[bits(14..=18, rw)] - tx_fifo: u5, - } - - #[bitbybit::bitfield(u32)] - #[derive(Debug)] - pub struct InterruptControl { - #[bit(0, rw)] - completed: bool, - #[bit(1, rw)] - idle: bool, - #[bit(2, rw)] - waiting: bool, - #[bit(3, rw)] - tx_stalled: bool, - #[bit(4, rw)] - rx_stalled: bool, - #[bit(5, rw)] - address_match: bool, - #[bit(6, rw)] - nack_data: bool, - #[bit(7, rw)] - rx_data_first: bool, - - #[bit(8, rw)] - i2c_start: bool, - #[bit(9, rw)] - i2c_stop: bool, - #[bit(10, rw)] - tx_underflow: bool, - #[bit(11, rw)] - rx_underflow: bool, - #[bit(12, rw)] - tx_ready: bool, - #[bit(13, rw)] - rx_ready: bool, - #[bit(14, rw)] - tx_empty: bool, - #[bit(15, rw)] - rx_full: bool, - } - - #[bitbybit::bitfield(u32)] - #[derive(Debug)] - pub struct InterruptStatus { - #[bit(0, r)] - completed: bool, - #[bit(1, r)] - idle: bool, - #[bit(2, r)] - waiting: bool, - #[bit(3, r)] - tx_stalled: bool, - #[bit(4, r)] - rx_stalled: bool, - #[bit(5, r)] - address_match: bool, - #[bit(6, r)] - nack_data: bool, - #[bit(7, r)] - rx_data_first: bool, - - #[bit(8, r)] - i2c_start: bool, - #[bit(9, r)] - i2c_stop: bool, - #[bit(10, r)] - tx_underflow: bool, - #[bit(11, r)] - rx_underflow: bool, - #[bit(12, r)] - tx_ready: bool, - #[bit(13, r)] - rx_ready: bool, - #[bit(14, r)] - tx_empty: bool, - #[bit(15, r)] - rx_full: bool, - } - - #[bitbybit::bitfield(u32, default = 0x0)] - #[derive(Debug)] - pub struct InterruptClear { - #[bit(0, w)] - completed: bool, - #[bit(1, w)] - idle: bool, - #[bit(2, w)] - waiting: bool, - #[bit(3, w)] - tx_stalled: bool, - #[bit(4, w)] - rx_stalled: bool, - #[bit(5, w)] - address_match: bool, - #[bit(6, w)] - nack_data: bool, - #[bit(7, w)] - rx_data_first: bool, - - #[bit(8, w)] - i2c_start: bool, - #[bit(9, w)] - i2c_stop: bool, - #[bit(10, w)] - tx_underflow: bool, - #[bit(11, w)] - rx_underflow: bool, - #[bit(12, w)] - tx_ready: bool, - #[bit(13, w)] - rx_ready: bool, - #[bit(14, w)] - tx_empty: bool, - #[bit(15, w)] - rx_full: bool, - } - - #[derive(derive_mmio::Mmio)] - #[repr(C)] - pub struct I2cSlave { - s0_ctrl: Control, - s0_maxwords: Maxwords, - s0_address: Address, - s0_addressmask: AddressMask, - s0_data: Data, - s0_lastaddress: LastAddress, - #[mmio(PureRead)] - s0_status: Status, - #[mmio(PureRead)] - s0_state: State, - #[mmio(PureRead)] - s0_tx_count: DataCount, - #[mmio(PureRead)] - s0_rx_count: DataCount, - s0_irq_enb: InterruptControl, - #[mmio(PureRead)] - s0_irq_raw: InterruptStatus, - #[mmio(PureRead)] - s0_irq_status: InterruptStatus, - #[mmio(Write)] - s0_irq_clear: InterruptClear, - s0_rx_fifo_trigger: TriggerLevel, - s0_tx_fifo_trigger: TriggerLevel, - #[mmio(Write)] - s0_fifo_clear: FifoClear, - s0_address_b: Address, - s0_addressmask_b: AddressMask, - } -} -#[derive(derive_mmio::Mmio)] -#[mmio(no_ctors)] -#[repr(C)] -pub struct I2c { - control: Control, - clkscale: ClkScale, - words: Words, - address: Address, - data: Data, - #[mmio(Write)] - cmd: Command, - #[mmio(PureRead)] - status: Status, - #[mmio(PureRead)] - state: State, - #[mmio(PureRead)] - tx_count: DataCount, - #[mmio(PureRead)] - rx_count: DataCount, - irq_enb: InterruptControl, - #[mmio(PureRead)] - irq_raw: InterruptStatus, - #[mmio(PureRead)] - irq_status: InterruptStatus, - #[mmio(Write)] - irq_clear: InterruptClear, - rx_fifo_trigger: TriggerLevel, - tx_fifo_trigger: TriggerLevel, - #[mmio(Write)] - fifo_clear: FifoClear, - timing_config: TimingConfig, - clk_timeout_limit: ClkTimeoutLimit, - - _reserved_0: [u32; 0x2D], - - #[mmio(inner)] - slave: slave::I2cSlave, - - #[cfg(feature = "vor1x")] - _reserved_1: [u32; 0x3AC], - #[cfg(feature = "vor4x")] - _reserved_1: [u32; 0xAC], - - /// Vorago 4x: 0x0214_07E9. Vorago 1x: 0x0014_07E1. - #[mmio(PureRead)] - perid: u32, -} - -cfg_if::cfg_if! { - if #[cfg(feature = "vor1x")] { - static_assertions::const_assert_eq!(core::mem::size_of::(), 0x1000); - } else if #[cfg(feature = "vor4x")] { - static_assertions::const_assert_eq!(core::mem::size_of::(), 0x400); - } -} - -impl I2c { - fn new_mmio_at(base: usize) -> MmioI2c<'static> { - MmioI2c { - ptr: base as *mut _, - phantom: PhantomData, - } - } - - pub fn new_mmio(bank: Bank) -> MmioI2c<'static> { - match bank { - Bank::I2c0 => Self::new_mmio_at(BASE_ADDR_0), - Bank::I2c1 => Self::new_mmio_at(BASE_ADDR_1), - #[cfg(feature = "vor4x")] - Bank::Spi3 => Self::new_mmio_at(BASE_ADDR_2), - } - } -} diff --git a/vorago-shared-periphs/src/ioconfig/mod.rs b/vorago-shared-periphs/src/ioconfig/mod.rs deleted file mode 100644 index d5b7d8e..0000000 --- a/vorago-shared-periphs/src/ioconfig/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub use regs::{FilterClkSel, FilterType}; - -pub mod regs; diff --git a/vorago-shared-periphs/src/ioconfig/regs.rs b/vorago-shared-periphs/src/ioconfig/regs.rs deleted file mode 100644 index 943eb3d..0000000 --- a/vorago-shared-periphs/src/ioconfig/regs.rs +++ /dev/null @@ -1,177 +0,0 @@ -use core::marker::PhantomData; - -use crate::{NUM_PORT_A, NUM_PORT_B, gpio::PinId}; -#[cfg(feature = "vor4x")] -use crate::{NUM_PORT_DEFAULT, NUM_PORT_G}; - -#[cfg(feature = "vor1x")] -pub const BASE_ADDR: usize = 0x4000_2000; -#[cfg(feature = "vor4x")] -pub const BASE_ADDR: usize = 0x4001_1000; - -#[bitbybit::bitenum(u3)] -pub enum FilterType { - SysClk = 0, - DirectInput = 1, - FilterOneCycle = 2, - FilterTwoCycles = 3, - FilterThreeCycles = 4, - FilterFourCycles = 5, -} - -#[derive(Debug, PartialEq, Eq)] -#[bitbybit::bitenum(u3, exhaustive = true)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum FilterClkSel { - SysClk = 0, - Clk1 = 1, - Clk2 = 2, - Clk3 = 3, - Clk4 = 4, - Clk5 = 5, - Clk6 = 6, - Clk7 = 7, -} - -#[derive(Debug, PartialEq, Eq)] -#[bitbybit::bitenum(u1, exhaustive = true)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Pull { - Up = 0, - Down = 1, -} - -#[derive(Debug, Eq, PartialEq)] -#[bitbybit::bitenum(u2, exhaustive = true)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum FunSel { - Sel0 = 0b00, - Sel1 = 0b01, - Sel2 = 0b10, - Sel3 = 0b11, -} - -#[bitbybit::bitfield(u32)] -pub struct Config { - #[bit(16, rw)] - io_disable: bool, - #[bits(13..=14, rw)] - funsel: FunSel, - #[bit(12, rw)] - pull_when_output_active: bool, - #[bit(11, rw)] - pull_enable: bool, - #[bit(10, rw)] - pull_dir: Pull, - #[bit(9, rw)] - invert_output: bool, - #[bit(8, rw)] - open_drain: bool, - /// IEWO bit. Allows monitoring of output values. - #[bit(7, rw)] - input_enable_when_output: bool, - #[bit(6, rw)] - invert_input: bool, - #[bits(3..=5, rw)] - filter_clk_sel: FilterClkSel, - #[bits(0..=2, rw)] - filter_type: Option, -} - -#[derive(derive_mmio::Mmio)] -#[mmio(no_ctors)] -#[repr(C)] -pub struct IoConfig { - port_a: [Config; NUM_PORT_A], - port_b: [Config; NUM_PORT_B], - #[cfg(feature = "vor4x")] - port_c: [Config; NUM_PORT_DEFAULT], - #[cfg(feature = "vor4x")] - port_d: [Config; NUM_PORT_DEFAULT], - #[cfg(feature = "vor4x")] - port_e: [Config; NUM_PORT_DEFAULT], - #[cfg(feature = "vor4x")] - port_f: [Config; NUM_PORT_DEFAULT], - #[cfg(feature = "vor4x")] - port_g: [Config; NUM_PORT_G], - #[cfg(feature = "vor4x")] - _reserved0: [u32; 0x8], - #[cfg(feature = "vor4x")] - #[mmio(PureRead)] - clk_div_0: u32, - #[cfg(feature = "vor4x")] - clk_div_1: u32, - #[cfg(feature = "vor4x")] - clk_div_2: u32, - #[cfg(feature = "vor4x")] - clk_div_3: u32, - #[cfg(feature = "vor4x")] - clk_div_4: u32, - #[cfg(feature = "vor4x")] - clk_div_5: u32, - #[cfg(feature = "vor4x")] - clk_div_6: u32, - #[cfg(feature = "vor4x")] - clk_div_7: u32, - #[cfg(feature = "vor4x")] - _reserved1: [u32; 0x387], - #[cfg(feature = "vor1x")] - _reserved1: [u32; 0x3c7], - #[mmio(PureRead)] - /// Reset value: 0x0282_07E9 for Vorago 4x, and 0x0182_07E1 for Vorago 1x - perid: u32, -} - -static_assertions::const_assert_eq!(core::mem::size_of::(), 0x1000); - -impl IoConfig { - pub const fn new_mmio() -> MmioIoConfig<'static> { - MmioIoConfig { - ptr: BASE_ADDR as *mut _, - phantom: PhantomData, - } - } -} - -impl MmioIoConfig<'_> { - pub fn read_pin_config(&self, id: PinId) -> Config { - let offset = id.offset(); - match id.port() { - crate::Port::A => unsafe { self.read_port_a_unchecked(offset) }, - crate::Port::B => unsafe { self.read_port_b_unchecked(offset) }, - #[cfg(feature = "vor4x")] - crate::Port::C => unsafe { self.read_port_c_unchecked(offset) }, - #[cfg(feature = "vor4x")] - crate::Port::D => unsafe { self.read_port_d_unchecked(offset) }, - #[cfg(feature = "vor4x")] - crate::Port::E => unsafe { self.read_port_e_unchecked(offset) }, - #[cfg(feature = "vor4x")] - crate::Port::F => unsafe { self.read_port_f_unchecked(offset) }, - #[cfg(feature = "vor4x")] - crate::Port::G => unsafe { self.read_port_g_unchecked(offset) }, - } - } - - pub fn modify_pin_config Config>(&mut self, id: PinId, f: F) { - let config = self.read_pin_config(id); - self.write_pin_config(id, f(config)) - } - - pub fn write_pin_config(&mut self, id: PinId, config: Config) { - let offset = id.offset(); - match id.port() { - crate::Port::A => unsafe { self.write_port_a_unchecked(offset, config) }, - crate::Port::B => unsafe { self.write_port_b_unchecked(offset, config) }, - #[cfg(feature = "vor4x")] - crate::Port::C => unsafe { self.write_port_c_unchecked(offset, config) }, - #[cfg(feature = "vor4x")] - crate::Port::D => unsafe { self.write_port_d_unchecked(offset, config) }, - #[cfg(feature = "vor4x")] - crate::Port::E => unsafe { self.write_port_e_unchecked(offset, config) }, - #[cfg(feature = "vor4x")] - crate::Port::F => unsafe { self.write_port_f_unchecked(offset, config) }, - #[cfg(feature = "vor4x")] - crate::Port::G => unsafe { self.write_port_g_unchecked(offset, config) }, - } - } -} diff --git a/vorago-shared-periphs/src/lib.rs b/vorago-shared-periphs/src/lib.rs deleted file mode 100644 index 1e10c11..0000000 --- a/vorago-shared-periphs/src/lib.rs +++ /dev/null @@ -1,182 +0,0 @@ -#![no_std] -pub mod gpio; -pub mod i2c; -pub mod ioconfig; -pub mod pins; -pub mod pwm; -pub mod spi; -pub mod sysconfig; -pub mod time; -pub mod timer; -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"); - -pub use ioconfig::regs::FunSel; - -#[cfg(feature = "vor1x")] -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum PeripheralSelect { - PortA = 0, - PortB = 1, - Spi0 = 4, - Spi1 = 5, - Spi2 = 6, - Uart0 = 8, - Uart1 = 9, - I2c0 = 16, - I2c1 = 17, - Irqsel = 21, - Ioconfig = 22, - Utility = 23, - Gpio = 24, -} - -cfg_if::cfg_if! { - if #[cfg(feature = "vor1x")] { - /// Number of GPIO ports and IOCONFIG registers for PORT A - pub const NUM_PORT_A: usize = 32; - /// Number of GPIO ports and IOCONFIG registers for PORT B - pub const NUM_PORT_B: usize = 24; - } else if #[cfg(feature = "vor4x")] { - /// Number of GPIO ports and IOCONFIG registers for PORT C to Port F - pub const NUM_PORT_DEFAULT: usize = 16; - /// Number of GPIO ports and IOCONFIG registers for PORT A - pub const NUM_PORT_A: usize = NUM_PORT_DEFAULT; - /// Number of GPIO ports and IOCONFIG registers for PORT B - pub const NUM_PORT_B: usize = NUM_PORT_DEFAULT; - /// Number of GPIO ports and IOCONFIG registers for PORT G - pub const NUM_PORT_G: usize = 8; - } -} - -/// GPIO port enumeration. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Port { - A = 0, - B = 1, - #[cfg(feature = "vor4x")] - C = 2, - #[cfg(feature = "vor4x")] - D = 3, - #[cfg(feature = "vor4x")] - E = 4, - #[cfg(feature = "vor4x")] - F = 5, - #[cfg(feature = "vor4x")] - G = 6, -} - -impl Port { - pub const fn max_offset(&self) -> usize { - match self { - Port::A => NUM_PORT_A, - Port::B => NUM_PORT_B, - #[cfg(feature = "vor4x")] - Port::C | Port::D | Port::E | Port::F => NUM_PORT_DEFAULT, - #[cfg(feature = "vor4x")] - Port::G => NUM_PORT_G, - } - } - - /// Unsafely steal the GPIO peripheral block for the given port. - /// - /// # Safety - /// - /// Circumvents ownership and safety guarantees by the HAL. - pub unsafe fn steal_gpio(&self) -> gpio::regs::MmioGpio<'static> { - gpio::regs::Gpio::new_mmio(*self) - } -} - -#[derive(Debug, thiserror::Error)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[error("invalid GPIO offset {offset} for port {port:?}")] -pub struct InvalidOffsetError { - offset: usize, - port: Port, -} - -/// Generic interrupt config which can be used to specify whether the HAL driver will -/// use the IRQSEL register to route an interrupt, and whether the IRQ will be unmasked in the -/// Cortex-M0 NVIC. Both are generally necessary for IRQs to work, but the user might want to -/// perform those steps themselves. -#[cfg(feature = "vor1x")] -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct InterruptConfig { - /// Interrupt target vector. Should always be set, might be required for disabling IRQs - pub id: va108xx::Interrupt, - /// Specfiy whether IRQ should be routed to an IRQ vector using the IRQSEL peripheral. - pub route: bool, - /// Specify whether the IRQ is unmasked in the Cortex-M NVIC. If an interrupt is used for - /// multiple purposes, the user can enable the interrupts themselves. - pub enable_in_nvic: bool, -} - -#[cfg(feature = "vor1x")] -impl InterruptConfig { - pub fn new(id: va108xx::Interrupt, route: bool, enable_in_nvic: bool) -> Self { - InterruptConfig { - id, - route, - enable_in_nvic, - } - } -} - -/// Enable a specific interrupt using the NVIC peripheral. -/// -/// # Safety -/// -/// This function is `unsafe` because it can break mask-based critical sections. -#[cfg(feature = "vor1x")] -#[inline] -pub unsafe fn enable_nvic_interrupt(irq: va108xx::Interrupt) { - unsafe { - cortex_m::peripheral::NVIC::unmask(irq); - } -} - -/// Disable a specific interrupt using the NVIC peripheral. -#[cfg(feature = "vor1x")] -#[inline] -pub fn disable_nvic_interrupt(irq: va108xx::Interrupt) { - cortex_m::peripheral::NVIC::mask(irq); -} - -#[allow(dead_code)] -pub(crate) mod sealed { - pub trait Sealed {} -} - -pub(crate) mod shared { - use arbitrary_int::u5; - - #[derive(Debug)] - pub struct TriggerLevel(arbitrary_int::UInt); - - impl TriggerLevel { - pub const fn new(value: u5) -> Self { - TriggerLevel(arbitrary_int::UInt::::new(value.value() as u32)) - } - - pub const fn value(&self) -> u5 { - u5::new(self.0.value() as u8) - } - } - - #[bitbybit::bitfield(u32, default = 0x0)] - #[derive(Debug)] - pub struct FifoClear { - #[bit(1, w)] - tx_fifo: bool, - #[bit(0, w)] - rx_fifo: bool, - } -} diff --git a/vorago-shared-periphs/src/pins.rs b/vorago-shared-periphs/src/pins.rs deleted file mode 100644 index 940c35d..0000000 --- a/vorago-shared-periphs/src/pins.rs +++ /dev/null @@ -1,236 +0,0 @@ -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/pwm.rs b/vorago-shared-periphs/src/pwm.rs deleted file mode 100644 index 8db97fd..0000000 --- a/vorago-shared-periphs/src/pwm.rs +++ /dev/null @@ -1,246 +0,0 @@ -use core::convert::Infallible; -use core::marker::PhantomData; - -use crate::gpio::IoPeriphPin; -use crate::timer::enable_tim_clk; -use crate::timer::regs::{EnableControl, StatusSelect}; -use crate::{PeripheralSelect, enable_peripheral_clock}; - -use crate::time::Hertz; -use crate::timer::{self, TimId, TimMarker, TimPin}; - -const DUTY_MAX: u16 = u16::MAX; - -#[derive(Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum PwmA {} -#[derive(Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum PwmB {} - -#[derive(Debug, thiserror::Error)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[error("pin tim ID {pin_tim:?} and timer tim id {tim_id:?} do not match")] -pub struct TimMissmatchError { - pin_tim: TimId, - tim_id: TimId, -} - -//================================================================================================== -// PWM pin -//================================================================================================== - -/// Reduced version where type information is deleted -pub struct PwmPin { - tim_id: TimId, - regs: timer::regs::MmioTimer<'static>, - sys_clk: Hertz, - /// For PWMB, this is the upper limit - current_duty: u16, - /// For PWMA, this value will not be used - current_lower_limit: u16, - current_period: Hertz, - current_rst_val: u32, - mode: PhantomData, -} - -impl PwmPin { - /// Create a new strongly typed PWM pin - pub fn new( - sys_clk: Hertz, - _pin_and_tim: (Pin, Tim), - initial_frequency: Hertz, - ) -> Result { - if Pin::TIM_ID != Tim::ID { - return Err(TimMissmatchError { - pin_tim: Pin::TIM_ID, - tim_id: Tim::ID, - }); - } - IoPeriphPin::new(Pin::PIN_ID, Pin::FUN_SEL, None); - let mut pin = PwmPin { - tim_id: Tim::ID, - regs: timer::regs::Timer::new_mmio(Tim::ID), - current_duty: 0, - current_lower_limit: 0, - current_period: initial_frequency, - current_rst_val: 0, - sys_clk, - mode: PhantomData, - }; - enable_peripheral_clock(PeripheralSelect::Gpio); - enable_peripheral_clock(PeripheralSelect::Ioconfig); - enable_tim_clk(Tim::ID); - pin.enable_pwm_a(); - pin.set_period(initial_frequency); - Ok(pin) - } - - #[inline] - fn enable_pwm_a(&mut self) { - self.regs.modify_control(|mut value| { - value.set_status_sel(StatusSelect::PwmaOutput); - value - }); - } - - #[inline] - fn enable_pwm_b(&mut self) { - self.regs.modify_control(|mut value| { - value.set_status_sel(StatusSelect::PwmbOutput); - value - }); - } - - #[inline] - pub fn get_period(&self) -> Hertz { - self.current_period - } - - #[inline] - pub fn set_period(&mut self, period: impl Into) { - self.current_period = period.into(); - // Avoid division by 0 - if self.current_period.raw() == 0 { - return; - } - self.current_rst_val = self.sys_clk.raw() / self.current_period.raw(); - self.regs.write_reset_value(self.current_rst_val); - } - - #[inline] - pub fn disable(&mut self) { - self.regs.write_enable_control(EnableControl::new_disable()); - } - - #[inline] - pub fn enable(&mut self) { - self.regs.write_enable_control(EnableControl::new_enable()); - } - - #[inline] - pub fn period(&self) -> Hertz { - self.current_period - } - - #[inline(always)] - pub fn duty(&self) -> u16 { - self.current_duty - } -} - -impl From> for PwmPin { - fn from(other: PwmPin) -> Self { - let mut pwmb = Self { - mode: PhantomData, - regs: other.regs, - tim_id: other.tim_id, - sys_clk: other.sys_clk, - current_duty: other.current_duty, - current_lower_limit: other.current_lower_limit, - current_period: other.current_period, - current_rst_val: other.current_rst_val, - }; - pwmb.enable_pwm_b(); - pwmb - } -} - -impl From> for PwmPin { - fn from(other: PwmPin) -> Self { - let mut pwmb = Self { - mode: PhantomData, - tim_id: other.tim_id, - regs: other.regs, - sys_clk: other.sys_clk, - current_duty: other.current_duty, - current_lower_limit: other.current_lower_limit, - current_period: other.current_period, - current_rst_val: other.current_rst_val, - }; - pwmb.enable_pwm_a(); - pwmb - } -} - -//================================================================================================== -// PWMB implementations -//================================================================================================== - -impl PwmPin { - #[inline(always)] - pub fn pwmb_lower_limit(&self) -> u16 { - self.current_lower_limit - } - - #[inline(always)] - pub fn pwmb_upper_limit(&self) -> u16 { - self.current_duty - } - - /// Set the lower limit for PWMB - /// - /// The PWM signal will be 1 as long as the current RST counter is larger than - /// the lower limit. For example, with a lower limit of 0.5 and and an upper limit - /// of 0.7, Only a fixed period between 0.5 * period and 0.7 * period will be in a high - /// state - #[inline(always)] - pub fn set_pwmb_lower_limit(&mut self, duty: u16) { - self.current_lower_limit = duty; - let pwmb_val: u64 = - (self.current_rst_val as u64 * self.current_lower_limit as u64) / DUTY_MAX as u64; - self.regs.write_pwmb_value(pwmb_val as u32); - } - - /// Set the higher limit for PWMB - /// - /// The PWM signal will be 1 as long as the current RST counter is smaller than - /// the higher limit. For example, with a lower limit of 0.5 and and an upper limit - /// of 0.7, Only a fixed period between 0.5 * period and 0.7 * period will be in a high - /// state - pub fn set_pwmb_upper_limit(&mut self, duty: u16) { - self.current_duty = duty; - let pwma_val: u64 = - (self.current_rst_val as u64 * self.current_duty as u64) / DUTY_MAX as u64; - self.regs.write_pwma_value(pwma_val as u32); - } -} - -//================================================================================================== -// Embedded HAL implementation: PWMA only -//================================================================================================== - -impl embedded_hal::pwm::ErrorType for PwmPin { - type Error = Infallible; -} - -impl embedded_hal::pwm::SetDutyCycle for PwmPin { - #[inline] - fn max_duty_cycle(&self) -> u16 { - DUTY_MAX - } - - #[inline] - fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> { - self.current_duty = duty; - let pwma_val: u64 = (self.current_rst_val as u64 - * (DUTY_MAX as u64 - self.current_duty as u64)) - / DUTY_MAX as u64; - self.regs.write_pwma_value(pwma_val as u32); - Ok(()) - } -} - -/// Get the corresponding u16 duty cycle from a percent value ranging between 0.0 and 1.0. -/// -/// Please note that this might load a lot of floating point code because this processor does not -/// have a FPU -pub fn get_duty_from_percent(percent: f32) -> u16 { - if percent > 1.0 { - DUTY_MAX - } else if percent <= 0.0 { - 0 - } else { - (percent * DUTY_MAX as f32) as u16 - } -} diff --git a/vorago-shared-periphs/src/spi/mod.rs b/vorago-shared-periphs/src/spi/mod.rs deleted file mode 100644 index 70eae81..0000000 --- a/vorago-shared-periphs/src/spi/mod.rs +++ /dev/null @@ -1,868 +0,0 @@ -use crate::gpio::{IoPeriphPin, PinId}; -use crate::FunSel; -use crate::{ - enable_peripheral_clock, pins::PinMarker, sealed::Sealed, time::Hertz, PeripheralSelect, -}; -use core::{convert::Infallible, fmt::Debug, marker::PhantomData}; -use embedded_hal::spi::{Mode, MODE_0}; - -use regs::{ClkPrescaler, Data, FifoClear, WordSize}; -#[cfg(feature = "vor1x")] -use va108xx as pac; -#[cfg(feature = "vor1x")] -pub mod pins_vor1x; - -pub use regs::{Bank, HwChipSelectId}; - -pub mod regs; - -pub fn configure_pin_as_hw_cs_pin(_pin: P) -> HwChipSelectId { - IoPeriphPin::new(P::ID, P::FUN_SEL, None); - P::CS_ID -} - -pub trait PinSck: PinMarker { - const SPI_ID: Bank; - const FUN_SEL: FunSel; -} - -pub trait PinMosi: PinMarker { - const SPI_ID: Bank; - const FUN_SEL: FunSel; -} - -pub trait PinMiso: PinMarker { - const SPI_ID: Bank; - const FUN_SEL: FunSel; -} - -pub trait HwCsProvider { - const PIN_ID: PinId; - const SPI_ID: Bank; - const FUN_SEL: FunSel; - const CS_ID: HwChipSelectId; -} - -//================================================================================================== -// Defintions -//================================================================================================== - -// FIFO has a depth of 16. -const FILL_DEPTH: usize = 12; - -pub const BMSTART_BMSTOP_MASK: u32 = 1 << 31; -pub const BMSKIPDATA_MASK: u32 = 1 << 30; - -pub const DEFAULT_CLK_DIV: u16 = 2; - -/// Common trait implemented by all PAC peripheral access structures. The register block -/// format is the same for all SPI blocks. -pub trait SpiMarker: Sealed { - const ID: Bank; - const PERIPH_SEL: PeripheralSelect; -} - -impl SpiMarker for pac::Spia { - const ID: Bank = Bank::Spi0; - const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi0; -} -impl Sealed for pac::Spia {} - -impl SpiMarker for pac::Spib { - const ID: Bank = Bank::Spi1; - const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi1; -} -impl Sealed for pac::Spib {} - -impl SpiMarker for pac::Spic { - const ID: Bank = Bank::Spi2; - const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi2; -} -impl Sealed for pac::Spic {} - -//================================================================================================== -// Config -//================================================================================================== - -pub trait TransferConfigProvider { - fn sod(&mut self, sod: bool); - fn blockmode(&mut self, blockmode: bool); - fn mode(&mut self, mode: Mode); - fn clk_cfg(&mut self, clk_cfg: SpiClkConfig); - fn hw_cs_id(&self) -> u8; -} - -/// Type erased variant of the transfer configuration. This is required to avoid generics in -/// the SPI constructor. -#[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct TransferConfig { - pub clk_cfg: Option, - pub mode: Option, - pub sod: bool, - /// If this is enabled, all data in the FIFO is transmitted in a single frame unless - /// the BMSTOP bit is set on a dataword. A frame is defined as CSn being active for the - /// duration of multiple data words - pub blockmode: bool, - /// Only used when blockmode is used. The SCK will be stalled until an explicit stop bit - /// is set on a written word. - pub bmstall: bool, - pub hw_cs: Option, -} - -impl TransferConfig { - pub fn new_with_hw_cs( - clk_cfg: Option, - mode: Option, - blockmode: bool, - bmstall: bool, - sod: bool, - hw_cs_id: HwChipSelectId, - ) -> Self { - TransferConfig { - clk_cfg, - mode, - sod, - blockmode, - bmstall, - hw_cs: Some(hw_cs_id), - } - } -} - -/// Configuration options for the whole SPI bus. See Programmer Guide p.92 for more details -#[derive(Debug, Copy, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct SpiConfig { - clk: SpiClkConfig, - // SPI mode configuration - pub init_mode: Mode, - /// If this is enabled, all data in the FIFO is transmitted in a single frame unless - /// the BMSTOP bit is set on a dataword. A frame is defined as CSn being active for the - /// duration of multiple data words. Defaults to true. - pub blockmode: bool, - /// This enables the stalling of the SPI SCK if in blockmode and the FIFO is empty. - /// Currently enabled by default. - pub bmstall: bool, - /// Slave output disable. Useful if separate GPIO pins or decoders are used for CS control - pub slave_output_disable: bool, - /// Loopback mode. If you use this, don't connect MISO to MOSI, they will be tied internally - pub loopback_mode: bool, - /// Enable Master Delayer Capture Mode. See Programmers Guide p.92 for more details - pub master_delayer_capture: bool, -} - -impl Default for SpiConfig { - fn default() -> Self { - Self { - init_mode: MODE_0, - blockmode: true, - bmstall: true, - // Default value is definitely valid. - clk: SpiClkConfig::from_div(DEFAULT_CLK_DIV).unwrap(), - slave_output_disable: Default::default(), - loopback_mode: Default::default(), - master_delayer_capture: Default::default(), - } - } -} - -impl SpiConfig { - pub fn loopback(mut self, enable: bool) -> Self { - self.loopback_mode = enable; - self - } - - pub fn blockmode(mut self, enable: bool) -> Self { - self.blockmode = enable; - self - } - - pub fn bmstall(mut self, enable: bool) -> Self { - self.bmstall = enable; - self - } - - pub fn mode(mut self, mode: Mode) -> Self { - self.init_mode = mode; - self - } - - pub fn clk_cfg(mut self, clk_cfg: SpiClkConfig) -> Self { - self.clk = clk_cfg; - self - } - - pub fn slave_output_disable(mut self, sod: bool) -> Self { - self.slave_output_disable = sod; - self - } -} - -//================================================================================================== -// Word Size -//================================================================================================== - -/// Configuration trait for the Word Size -/// used by the SPI peripheral -pub trait WordProvider: Copy + Default + Into + TryFrom + 'static { - const MASK: u32; - const WORD_SIZE: regs::WordSize; - fn word_reg() -> u8; -} - -impl WordProvider for u8 { - const MASK: u32 = 0xff; - const WORD_SIZE: regs::WordSize = regs::WordSize::EightBits; - fn word_reg() -> u8 { - 0x07 - } -} - -impl WordProvider for u16 { - const MASK: u32 = 0xffff; - const WORD_SIZE: regs::WordSize = regs::WordSize::SixteenBits; - fn word_reg() -> u8 { - 0x0f - } -} - -//================================================================================================== -// Spi -//================================================================================================== - -/// Low level access trait for the SPI peripheral. -pub trait SpiLowLevel { - /// Low level function to write a word to the SPI FIFO but also checks whether - /// there is actually data in the FIFO. - /// - /// Uses the [nb] API to allow usage in blocking and non-blocking contexts. - fn write_fifo(&mut self, data: u32) -> nb::Result<(), Infallible>; - - /// Low level function to write a word to the SPI FIFO without checking whether - /// there FIFO is full. - /// - /// This does not necesarily mean there is a space in the FIFO available. - /// Use [Self::write_fifo] function to write a word into the FIFO reliably. - fn write_fifo_unchecked(&mut self, data: u32); - - /// Low level function to read a word from the SPI FIFO. Must be preceeded by a - /// [Self::write_fifo] call. - /// - /// Uses the [nb] API to allow usage in blocking and non-blocking contexts. - fn read_fifo(&mut self) -> nb::Result; - - /// Low level function to read a word from from the SPI 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. - /// You might also need to mask the value to ignore the BMSTART/BMSTOP bit. - fn read_fifo_unchecked(&mut self) -> u32; -} - -#[inline(always)] -pub fn mode_to_cpo_cph_bit(mode: embedded_hal::spi::Mode) -> (bool, bool) { - match mode { - embedded_hal::spi::MODE_0 => (false, false), - embedded_hal::spi::MODE_1 => (false, true), - embedded_hal::spi::MODE_2 => (true, false), - embedded_hal::spi::MODE_3 => (true, true), - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct SpiClkConfig { - prescale_val: u8, - scrdv: u8, -} - -impl SpiClkConfig { - pub fn prescale_val(&self) -> u8 { - self.prescale_val - } - pub fn scrdv(&self) -> u8 { - self.scrdv - } -} - -impl SpiClkConfig { - pub fn new(prescale_val: u8, scrdv: u8) -> Self { - Self { - prescale_val, - scrdv, - } - } - - pub fn from_div(div: u16) -> Result { - spi_clk_config_from_div(div) - } - - pub fn from_clk(sys_clk: impl Into, spi_clk: impl Into) -> Option { - clk_div_for_target_clock(sys_clk, spi_clk).map(|div| spi_clk_config_from_div(div).unwrap()) - } -} - -#[derive(Debug, thiserror::Error)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum SpiClkConfigError { - #[error("division by zero")] - DivIsZero, - #[error("divide value is not even")] - DivideValueNotEven, - #[error("scrdv value is too large")] - ScrdvValueTooLarge, -} - -#[inline] -pub fn spi_clk_config_from_div(mut div: u16) -> Result { - if div == 0 { - return Err(SpiClkConfigError::DivIsZero); - } - if div % 2 != 0 { - return Err(SpiClkConfigError::DivideValueNotEven); - } - let mut prescale_val = 0; - - // find largest (even) prescale value that divides into div - for i in (2..=0xfe).rev().step_by(2) { - if div % i == 0 { - prescale_val = i; - break; - } - } - - if prescale_val == 0 { - return Err(SpiClkConfigError::DivideValueNotEven); - } - - div /= prescale_val; - if div > u8::MAX as u16 + 1 { - return Err(SpiClkConfigError::ScrdvValueTooLarge); - } - Ok(SpiClkConfig { - prescale_val: prescale_val as u8, - scrdv: (div - 1) as u8, - }) -} - -#[inline] -pub fn clk_div_for_target_clock( - sys_clk: impl Into, - spi_clk: impl Into, -) -> Option { - let spi_clk = spi_clk.into(); - let sys_clk = sys_clk.into(); - if spi_clk > sys_clk { - return None; - } - - // Step 1: Calculate raw divider. - let raw_div = sys_clk.raw() / spi_clk.raw(); - let remainder = sys_clk.raw() % spi_clk.raw(); - - // Step 2: Round up if necessary. - let mut rounded_div = if remainder * 2 >= spi_clk.raw() { - raw_div + 1 - } else { - raw_div - }; - - if rounded_div % 2 != 0 { - // Take slower clock conservatively. - rounded_div += 1; - } - if rounded_div > u16::MAX as u32 { - return None; - } - Some(rounded_div as u16) -} - -#[derive(Debug, thiserror::Error)] -#[error("peripheral or peripheral pin ID is not consistent")] -pub struct SpiIdMissmatchError; - -/// SPI peripheral driver structure. -pub struct Spi { - id: Bank, - regs: regs::MmioSpi<'static>, - cfg: SpiConfig, - sys_clk: Hertz, - /// Fill word for read-only SPI transactions. - fill_word: Word, - blockmode: bool, - bmstall: bool, - word: PhantomData, -} - -impl Spi -where - >::Error: core::fmt::Debug, -{ - /// Create a new SPI struct. - /// - /// ## Arguments - /// * `sys_clk` - System clock - /// * `spi` - SPI bus to use - /// * `pins` - Pins to be used for SPI transactions. These pins are consumed - /// to ensure the pins can not be used for other purposes anymore - /// * `spi_cfg` - Configuration specific to the SPI bus - pub fn new_for_rom( - sys_clk: Hertz, - spi: SpiI, - spi_cfg: SpiConfig, - ) -> Result { - #[cfg(feature = "vor1x")] - if SpiI::ID != Bank::Spi2 { - return Err(SpiIdMissmatchError); - } - #[cfg(feature = "vor4x")] - if SpiI::ID != Bank::Spi3 { - return Err(SpiIdMissmatchError); - } - Ok(Self::new_generic(sys_clk, spi, spi_cfg)) - } - /// Create a new SPI struct. - /// - /// ## Arguments - /// * `sys_clk` - System clock - /// * `spi` - SPI bus to use - /// * `pins` - Pins to be used for SPI transactions. These pins are consumed - /// to ensure the pins can not be used for other purposes anymore - /// * `spi_cfg` - Configuration specific to the SPI bus - pub fn new( - sys_clk: Hertz, - spi: SpiI, - _pins: (Sck, Miso, Mosi), - spi_cfg: SpiConfig, - ) -> Result { - if SpiI::ID != Sck::SPI_ID || SpiI::ID != Miso::SPI_ID || SpiI::ID != Mosi::SPI_ID { - return Err(SpiIdMissmatchError); - } - IoPeriphPin::new(Sck::ID, Sck::FUN_SEL, None); - IoPeriphPin::new(Miso::ID, Miso::FUN_SEL, None); - IoPeriphPin::new(Mosi::ID, Mosi::FUN_SEL, None); - Ok(Self::new_generic(sys_clk, spi, spi_cfg)) - } - - pub fn new_generic(sys_clk: Hertz, _spi: SpiI, spi_cfg: SpiConfig) -> Self { - enable_peripheral_clock(SpiI::PERIPH_SEL); - let mut regs = regs::Spi::new_mmio(SpiI::ID); - let (cpo_bit, cph_bit) = mode_to_cpo_cph_bit(spi_cfg.init_mode); - regs.write_ctrl0( - regs::Control0::builder() - .with_scrdv(spi_cfg.clk.scrdv) - .with_sph(cph_bit) - .with_spo(cpo_bit) - .with_word_size(Word::WORD_SIZE) - .build(), - ); - regs.write_ctrl1( - regs::Control1::builder() - .with_mtxpause(false) - .with_mdlycap(spi_cfg.master_delayer_capture) - .with_bm_stall(spi_cfg.bmstall) - .with_bm_start(false) - .with_blockmode(spi_cfg.blockmode) - .with_ss(HwChipSelectId::Id0) - .with_sod(spi_cfg.slave_output_disable) - .with_slave_mode(false) - .with_enable(false) - .with_lbm(spi_cfg.loopback_mode) - .build(), - ); - regs.write_clkprescale(ClkPrescaler::new(spi_cfg.clk.prescale_val)); - regs.write_fifo_clear( - FifoClear::builder() - .with_tx_fifo(true) - .with_rx_fifo(true) - .build(), - ); - // Enable the peripheral as the last step as recommended in the - // programmers guide - regs.modify_ctrl1(|mut value| { - value.set_enable(true); - value - }); - Spi { - id: SpiI::ID, - regs: regs::Spi::new_mmio(SpiI::ID), - cfg: spi_cfg, - sys_clk, - fill_word: Default::default(), - bmstall: spi_cfg.bmstall, - blockmode: spi_cfg.blockmode, - word: PhantomData, - } - } - - #[inline] - pub fn cfg_clock(&mut self, cfg: SpiClkConfig) { - self.regs.modify_ctrl0(|mut value| { - value.set_scrdv(cfg.scrdv); - value - }); - self.regs - .write_clkprescale(regs::ClkPrescaler::new(cfg.prescale_val)); - } - - pub fn set_fill_word(&mut self, fill_word: Word) { - self.fill_word = fill_word; - } - - #[inline] - pub fn cfg_clock_from_div(&mut self, div: u16) -> Result<(), SpiClkConfigError> { - let val = spi_clk_config_from_div(div)?; - self.cfg_clock(val); - Ok(()) - } - - #[inline] - pub fn cfg_mode(&mut self, mode: Mode) { - let (cpo_bit, cph_bit) = mode_to_cpo_cph_bit(mode); - self.regs.modify_ctrl0(|mut value| { - value.set_spo(cpo_bit); - value.set_sph(cph_bit); - value - }); - } - - #[inline] - pub fn fill_word(&self) -> Word { - self.fill_word - } - - #[inline] - pub fn clear_tx_fifo(&mut self) { - self.regs.write_fifo_clear( - regs::FifoClear::builder() - .with_tx_fifo(true) - .with_rx_fifo(false) - .build(), - ); - } - - #[inline] - pub fn clear_rx_fifo(&mut self) { - self.regs.write_fifo_clear( - regs::FifoClear::builder() - .with_tx_fifo(false) - .with_rx_fifo(true) - .build(), - ); - } - - #[inline] - pub fn perid(&self) -> u32 { - self.regs.read_perid() - } - - /// Configure the hardware chip select given a hardware chip select ID. - /// - /// The pin also needs to be configured to be used as a HW CS pin. This can be done - /// by using the [configure_pin_as_hw_cs_pin] function which also returns the - /// corresponding [HwChipSelectId]. - #[inline] - pub fn cfg_hw_cs(&mut self, hw_cs: HwChipSelectId) { - self.regs.modify_ctrl1(|mut value| { - value.set_sod(false); - value.set_ss(hw_cs); - value - }); - } - - /// Disables the hardware chip select functionality. This can be used when performing - /// external chip select handling, for example with GPIO pins. - #[inline] - pub fn cfg_hw_cs_disable(&mut self) { - self.regs.modify_ctrl1(|mut value| { - value.set_sod(true); - value - }); - } - - /// Utility function to configure all relevant transfer parameters in one go. - /// This is useful if multiple devices with different clock and mode configurations - /// are connected to one bus. - pub fn cfg_transfer(&mut self, transfer_cfg: &TransferConfig) { - if let Some(trans_clk_div) = transfer_cfg.clk_cfg { - self.cfg_clock(trans_clk_div); - } - if let Some(mode) = transfer_cfg.mode { - self.cfg_mode(mode); - } - self.blockmode = transfer_cfg.blockmode; - self.regs.modify_ctrl1(|mut value| { - if transfer_cfg.sod { - value.set_sod(transfer_cfg.sod); - } else { - value.set_sod(false); - if let Some(hw_cs) = transfer_cfg.hw_cs { - value.set_ss(hw_cs); - } - } - value.set_blockmode(transfer_cfg.blockmode); - value.set_bm_stall(transfer_cfg.bmstall); - value - }); - } - - fn flush_internal(&mut self) { - let mut status_reg = self.regs.read_status(); - while !status_reg.tx_empty() || status_reg.rx_not_empty() || status_reg.busy() { - if status_reg.rx_not_empty() { - self.read_fifo_unchecked(); - } - status_reg = self.regs.read_status(); - } - } - - fn transfer_preparation(&mut self, words: &[Word]) -> Result<(), Infallible> { - if words.is_empty() { - return Ok(()); - } - self.flush_internal(); - Ok(()) - } - - // The FIFO can hold a guaranteed amount of data, so we can pump it on transfer - // initialization. Returns the amount of written bytes. - fn initial_send_fifo_pumping_with_words(&mut self, words: &[Word]) -> usize { - //let reg_block = self.reg_block(); - if self.blockmode { - self.regs.modify_ctrl1(|mut value| { - value.set_mtxpause(true); - value - }); - } - // Fill the first half of the write FIFO - let mut current_write_idx = 0; - let smaller_idx = core::cmp::min(FILL_DEPTH, words.len()); - for _ in 0..smaller_idx { - if current_write_idx == smaller_idx.saturating_sub(1) && self.bmstall { - self.write_fifo_unchecked(words[current_write_idx].into() | BMSTART_BMSTOP_MASK); - } else { - self.write_fifo_unchecked(words[current_write_idx].into()); - } - current_write_idx += 1; - } - if self.blockmode { - self.regs.modify_ctrl1(|mut value| { - value.set_mtxpause(false); - value - }); - } - current_write_idx - } - - // The FIFO can hold a guaranteed amount of data, so we can pump it on transfer - // initialization. - fn initial_send_fifo_pumping_with_fill_words(&mut self, send_len: usize) -> usize { - if self.blockmode { - self.regs.modify_ctrl1(|mut value| { - value.set_mtxpause(true); - value - }); - } - // Fill the first half of the write FIFO - let mut current_write_idx = 0; - let smaller_idx = core::cmp::min(FILL_DEPTH, send_len); - for _ in 0..smaller_idx { - if current_write_idx == smaller_idx.saturating_sub(1) && self.bmstall { - self.write_fifo_unchecked(self.fill_word.into() | BMSTART_BMSTOP_MASK); - } else { - self.write_fifo_unchecked(self.fill_word.into()); - } - current_write_idx += 1; - } - if self.blockmode { - self.regs.modify_ctrl1(|mut value| { - value.set_mtxpause(false); - value - }); - } - current_write_idx - } -} - -impl SpiLowLevel for Spi -where - >::Error: core::fmt::Debug, -{ - #[inline(always)] - fn write_fifo(&mut self, data: u32) -> nb::Result<(), Infallible> { - if !self.regs.read_status().tx_not_full() { - return Err(nb::Error::WouldBlock); - } - self.write_fifo_unchecked(data); - Ok(()) - } - - #[inline(always)] - fn write_fifo_unchecked(&mut self, data: u32) { - self.regs.write_data(Data::new_with_raw_value(data)); - } - - #[inline(always)] - fn read_fifo(&mut self) -> nb::Result { - if !self.regs.read_status().rx_not_empty() { - return Err(nb::Error::WouldBlock); - } - Ok(self.read_fifo_unchecked()) - } - - #[inline(always)] - fn read_fifo_unchecked(&mut self) -> u32 { - self.regs.read_data().raw_value() - } -} - -impl embedded_hal::spi::ErrorType for Spi { - type Error = Infallible; -} - -impl embedded_hal::spi::SpiBus for Spi -where - >::Error: core::fmt::Debug, -{ - fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error> { - self.transfer_preparation(words)?; - let mut current_read_idx = 0; - let mut current_write_idx = self.initial_send_fifo_pumping_with_fill_words(words.len()); - loop { - if current_read_idx < words.len() { - words[current_read_idx] = (nb::block!(self.read_fifo())? & Word::MASK) - .try_into() - .unwrap(); - current_read_idx += 1; - } - if current_write_idx < words.len() { - if current_write_idx == words.len() - 1 && self.bmstall { - nb::block!(self.write_fifo(self.fill_word.into() | BMSTART_BMSTOP_MASK))?; - } else { - nb::block!(self.write_fifo(self.fill_word.into()))?; - } - current_write_idx += 1; - } - if current_read_idx >= words.len() && current_write_idx >= words.len() { - break; - } - } - Ok(()) - } - - fn write(&mut self, words: &[Word]) -> Result<(), Self::Error> { - self.transfer_preparation(words)?; - let mut current_write_idx = self.initial_send_fifo_pumping_with_words(words); - while current_write_idx < words.len() { - if current_write_idx == words.len() - 1 && self.bmstall { - nb::block!(self.write_fifo(words[current_write_idx].into() | BMSTART_BMSTOP_MASK))?; - } else { - nb::block!(self.write_fifo(words[current_write_idx].into()))?; - } - current_write_idx += 1; - // Ignore received words. - if self.regs.read_status().rx_not_empty() { - self.clear_rx_fifo(); - } - } - Ok(()) - } - - fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> { - self.transfer_preparation(write)?; - let mut current_read_idx = 0; - let mut current_write_idx = self.initial_send_fifo_pumping_with_words(write); - while current_read_idx < read.len() || current_write_idx < write.len() { - if current_write_idx < write.len() { - if current_write_idx == write.len() - 1 && self.bmstall { - nb::block!( - self.write_fifo(write[current_write_idx].into() | BMSTART_BMSTOP_MASK) - )?; - } else { - nb::block!(self.write_fifo(write[current_write_idx].into()))?; - } - current_write_idx += 1; - } - if current_read_idx < read.len() { - read[current_read_idx] = (nb::block!(self.read_fifo())? & Word::MASK) - .try_into() - .unwrap(); - current_read_idx += 1; - } - } - - Ok(()) - } - - fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error> { - self.transfer_preparation(words)?; - let mut current_read_idx = 0; - let mut current_write_idx = self.initial_send_fifo_pumping_with_words(words); - - while current_read_idx < words.len() || current_write_idx < words.len() { - if current_write_idx < words.len() { - if current_write_idx == words.len() - 1 && self.bmstall { - nb::block!( - self.write_fifo(words[current_write_idx].into() | BMSTART_BMSTOP_MASK) - )?; - } else { - nb::block!(self.write_fifo(words[current_write_idx].into()))?; - } - current_write_idx += 1; - } - if current_read_idx < words.len() && current_read_idx < current_write_idx { - words[current_read_idx] = (nb::block!(self.read_fifo())? & Word::MASK) - .try_into() - .unwrap(); - current_read_idx += 1; - } - } - Ok(()) - } - - fn flush(&mut self) -> Result<(), Self::Error> { - self.flush_internal(); - Ok(()) - } -} - -/// Changing the word size also requires a type conversion -impl From> for Spi { - fn from(mut old_spi: Spi) -> Self { - old_spi.regs.modify_ctrl0(|mut value| { - value.set_word_size(WordSize::SixteenBits); - value - }); - Spi { - id: old_spi.id, - regs: old_spi.regs, - cfg: old_spi.cfg, - blockmode: old_spi.blockmode, - fill_word: Default::default(), - bmstall: old_spi.bmstall, - sys_clk: old_spi.sys_clk, - word: PhantomData, - } - } -} - -impl From> for Spi { - fn from(mut old_spi: Spi) -> Self { - old_spi.regs.modify_ctrl0(|mut value| { - value.set_word_size(WordSize::EightBits); - value - }); - Spi { - id: old_spi.id, - regs: old_spi.regs, - cfg: old_spi.cfg, - blockmode: old_spi.blockmode, - fill_word: Default::default(), - bmstall: old_spi.bmstall, - sys_clk: old_spi.sys_clk, - word: PhantomData, - } - } -} diff --git a/vorago-shared-periphs/src/spi/pins_vor1x.rs b/vorago-shared-periphs/src/spi/pins_vor1x.rs deleted file mode 100644 index 2bb8cef..0000000 --- a/vorago-shared-periphs/src/spi/pins_vor1x.rs +++ /dev/null @@ -1,355 +0,0 @@ -use super::{HwCsProvider, PinMiso, PinMosi, PinSck}; -use crate::FunSel; -use crate::gpio::{PinId, PinIdProvider}; - -use crate::{ - pins::{ - Pa10, Pa11, Pa12, Pa13, Pa14, Pa15, Pa16, Pa17, Pa18, Pa19, Pa20, Pa21, Pa22, Pa23, Pa24, - Pa25, Pa26, Pa27, Pa28, Pa29, Pa30, Pa31, Pb0, Pb1, Pb2, Pb3, Pb4, Pb5, Pb6, Pb7, Pb8, Pb9, - Pb10, Pb11, Pb12, Pb13, Pb14, Pb15, Pb16, Pb17, Pb18, Pb19, Pb22, Pb23, Pin, - }, - sealed::Sealed, -}; - -use super::{Bank, HwChipSelectId}; - -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: Bank = $SpiId; - const FUN_SEL: FunSel = $FunSel; - const CS_ID: HwChipSelectId = $HwCsIdent; - } - )+ - }; -} -macro_rules! hw_cs_multi_pin { - ( - // name of the newtype wrapper struct - $name:ident, - // Pb0 - $pin_id:ident, - // SpiId::B - $spi_id:path, - // FunSel::Sel1 - $fun_sel:path, - // HwChipSelectId::Id2 - $cs_id:path - ) => { - #[doc = concat!( - "Newtype wrapper to use [Pin] [`", stringify!($pin_id), - "`] as a HW CS pin for [`", stringify!($spi_id), - "`] with [`", stringify!($cs_id), "`]." - )] - pub struct $name(Pin<$pin_id>); - - impl $name { - pub fn new(pin: Pin<$pin_id>) -> Self { - Self(pin) - } - } - - impl Sealed for $name {} - - impl HwCsProvider for $name { - const PIN_ID: PinId = <$pin_id as PinIdProvider>::ID; - const SPI_ID: Bank = $spi_id; - const FUN_SEL: FunSel = $fun_sel; - const CS_ID: HwChipSelectId = $cs_id; - } - }; -} - -// SPIA - -impl PinSck for Pin { - const SPI_ID: Bank = Bank::Spi0; - const FUN_SEL: FunSel = FunSel::Sel1; -} -impl PinMosi for Pin { - const SPI_ID: Bank = Bank::Spi0; - const FUN_SEL: FunSel = FunSel::Sel1; -} -impl PinMiso for Pin { - const SPI_ID: Bank = Bank::Spi0; - const FUN_SEL: FunSel = FunSel::Sel1; -} - -impl PinSck for Pin { - const SPI_ID: Bank = Bank::Spi0; - const FUN_SEL: FunSel = FunSel::Sel2; -} -impl PinMosi for Pin { - const SPI_ID: Bank = Bank::Spi0; - const FUN_SEL: FunSel = FunSel::Sel2; -} -impl PinMiso for Pin { - const SPI_ID: Bank = Bank::Spi0; - const FUN_SEL: FunSel = FunSel::Sel2; -} - -hw_cs_pins!( - Bank::Spi0, - (Pb0, FunSel::Sel2, HwChipSelectId::Id1), - (Pb1, FunSel::Sel2, HwChipSelectId::Id2), - (Pb2, FunSel::Sel2, HwChipSelectId::Id3), - (Pb3, FunSel::Sel2, HwChipSelectId::Id4), - (Pb4, FunSel::Sel2, HwChipSelectId::Id5), - (Pb5, FunSel::Sel2, HwChipSelectId::Id6), - (Pb6, FunSel::Sel2, HwChipSelectId::Id0), - (Pa24, FunSel::Sel1, HwChipSelectId::Id4), - (Pa25, FunSel::Sel1, HwChipSelectId::Id3), - (Pa26, FunSel::Sel1, HwChipSelectId::Id2), - (Pa27, FunSel::Sel1, HwChipSelectId::Id1), - (Pa28, FunSel::Sel1, HwChipSelectId::Id0), -); - -hw_cs_multi_pin!( - PinPb0SpiaHwCsId1, - Pb0, - Bank::Spi0, - FunSel::Sel2, - HwChipSelectId::Id1 -); -hw_cs_multi_pin!( - PinPb1SpiaHwCsId2, - Pb1, - Bank::Spi0, - FunSel::Sel2, - HwChipSelectId::Id2 -); -hw_cs_multi_pin!( - PinPb2SpiaHwCsId3, - Pb2, - Bank::Spi0, - FunSel::Sel2, - HwChipSelectId::Id3 -); - -hw_cs_multi_pin!( - PinPa21SpiaHwCsId7, - Pa21, - Bank::Spi0, - FunSel::Sel1, - HwChipSelectId::Id7 -); -hw_cs_multi_pin!( - PinPa22SpiaHwCsId6, - Pa22, - Bank::Spi0, - FunSel::Sel1, - HwChipSelectId::Id6 -); -hw_cs_multi_pin!( - PinPa23SpiaHwCsId5, - Pa23, - Bank::Spi0, - FunSel::Sel1, - HwChipSelectId::Id5 -); - -// SPIB - -impl PinSck for Pin { - const SPI_ID: Bank = Bank::Spi1; - const FUN_SEL: FunSel = FunSel::Sel2; -} -impl PinMosi for Pin { - const SPI_ID: Bank = Bank::Spi1; - const FUN_SEL: FunSel = FunSel::Sel2; -} -impl PinMiso for Pin { - const SPI_ID: Bank = Bank::Spi1; - const FUN_SEL: FunSel = FunSel::Sel2; -} - -pub type SpiBPortASck = Pin; -pub type SpiBPortAMosi = Pin; -pub type SpiBPortAMiso = Pin; - -impl PinSck for Pin { - const SPI_ID: Bank = Bank::Spi1; - const FUN_SEL: FunSel = FunSel::Sel1; -} -impl PinMosi for Pin { - const SPI_ID: Bank = Bank::Spi1; - const FUN_SEL: FunSel = FunSel::Sel1; -} -impl PinMiso for Pin { - const SPI_ID: Bank = Bank::Spi1; - const FUN_SEL: FunSel = FunSel::Sel1; -} - -impl PinSck for Pin { - const SPI_ID: Bank = Bank::Spi1; - const FUN_SEL: FunSel = FunSel::Sel1; -} -impl PinMosi for Pin { - const SPI_ID: Bank = Bank::Spi1; - const FUN_SEL: FunSel = FunSel::Sel1; -} -impl PinMiso for Pin { - const SPI_ID: Bank = Bank::Spi1; - const FUN_SEL: FunSel = FunSel::Sel1; -} - -// TODO: Need to deal with these duplications.. -hw_cs_pins!( - Bank::Spi1, - (Pb16, FunSel::Sel1, HwChipSelectId::Id0), - (Pb15, FunSel::Sel1, HwChipSelectId::Id1), - (Pb14, FunSel::Sel1, HwChipSelectId::Id2), - (Pb13, FunSel::Sel1, HwChipSelectId::Id3), - (Pa17, FunSel::Sel2, HwChipSelectId::Id0), - (Pa16, FunSel::Sel2, HwChipSelectId::Id1), - (Pa15, FunSel::Sel2, HwChipSelectId::Id2), - (Pa14, FunSel::Sel2, HwChipSelectId::Id3), - (Pa13, FunSel::Sel2, HwChipSelectId::Id4), - (Pa12, FunSel::Sel2, HwChipSelectId::Id5), - (Pa11, FunSel::Sel2, HwChipSelectId::Id6), - (Pa10, FunSel::Sel2, HwChipSelectId::Id7), - (Pa23, FunSel::Sel2, HwChipSelectId::Id5), - (Pa22, FunSel::Sel2, HwChipSelectId::Id6), - (Pa21, FunSel::Sel2, HwChipSelectId::Id7), -); - -hw_cs_multi_pin!( - PinPb0SpibHwCsId2, - Pb0, - Bank::Spi1, - FunSel::Sel1, - HwChipSelectId::Id2 -); -hw_cs_multi_pin!( - PinPb1SpibHwCsId1, - Pb1, - Bank::Spi1, - FunSel::Sel1, - HwChipSelectId::Id1 -); -hw_cs_multi_pin!( - PinPb2SpibHwCsId0, - Pb2, - Bank::Spi1, - FunSel::Sel1, - HwChipSelectId::Id0 -); - -hw_cs_multi_pin!( - PinPb10SpibHwCsId6, - Pb10, - Bank::Spi1, - FunSel::Sel1, - HwChipSelectId::Id6 -); -hw_cs_multi_pin!( - PinPb11SpibHwCsId5, - Pb11, - Bank::Spi1, - FunSel::Sel1, - HwChipSelectId::Id5 -); -hw_cs_multi_pin!( - PinPb12SpibHwCsId4, - Pb12, - Bank::Spi1, - FunSel::Sel1, - HwChipSelectId::Id4 -); - -hw_cs_multi_pin!( - PinPb10SpibHwCsId2, - Pb10, - Bank::Spi1, - FunSel::Sel2, - HwChipSelectId::Id2 -); -hw_cs_multi_pin!( - PinPb11SpibHwCsId1, - Pb11, - Bank::Spi1, - FunSel::Sel2, - HwChipSelectId::Id1 -); -hw_cs_multi_pin!( - PinPb12SpibHwCsId0, - Pb12, - Bank::Spi1, - FunSel::Sel2, - HwChipSelectId::Id0 -); - -hw_cs_multi_pin!( - PinPa21SpibHwCsId7, - Pa21, - Bank::Spi1, - FunSel::Sel2, - HwChipSelectId::Id7 -); -hw_cs_multi_pin!( - PinPa22SpibHwCsId6, - Pa22, - Bank::Spi1, - FunSel::Sel2, - HwChipSelectId::Id6 -); -hw_cs_multi_pin!( - PinPa23SpibHwCsId5, - Pa23, - Bank::Spi1, - FunSel::Sel2, - HwChipSelectId::Id5 -); - -// SPIC - -hw_cs_pins!( - Bank::Spi2, - (Pb9, FunSel::Sel3, HwChipSelectId::Id1), - (Pb8, FunSel::Sel3, HwChipSelectId::Id2), - (Pb7, FunSel::Sel3, HwChipSelectId::Id3), - (Pb23, FunSel::Sel3, HwChipSelectId::Id2), - (Pb22, FunSel::Sel3, HwChipSelectId::Id1), - (Pa20, FunSel::Sel1, HwChipSelectId::Id1), - (Pa19, FunSel::Sel1, HwChipSelectId::Id2), - (Pb18, FunSel::Sel1, HwChipSelectId::Id3), -); - -hw_cs_multi_pin!( - PinPa21SpicHwCsId3, - Pa21, - Bank::Spi2, - FunSel::Sel3, - HwChipSelectId::Id3 -); -hw_cs_multi_pin!( - PinPa22SpicHwCsId2, - Pa22, - Bank::Spi2, - FunSel::Sel3, - HwChipSelectId::Id2 -); -hw_cs_multi_pin!( - PinPa23SpicHwCsId1, - Pa23, - Bank::Spi2, - FunSel::Sel3, - HwChipSelectId::Id1 -); - -hw_cs_multi_pin!( - PinPa20SpicHwCsId1, - Pa20, - Bank::Spi2, - FunSel::Sel1, - HwChipSelectId::Id1 -); -hw_cs_multi_pin!( - PinPa20SpicHwCsId4, - Pa20, - Bank::Spi2, - FunSel::Sel3, - HwChipSelectId::Id4 -); diff --git a/vorago-shared-periphs/src/spi/regs.rs b/vorago-shared-periphs/src/spi/regs.rs deleted file mode 100644 index 1ca5fb0..0000000 --- a/vorago-shared-periphs/src/spi/regs.rs +++ /dev/null @@ -1,279 +0,0 @@ -use core::marker::PhantomData; - -pub use crate::shared::{FifoClear, TriggerLevel}; - -cfg_if::cfg_if! { - if #[cfg(feature = "vor1x")] { - /// SPI A base address - pub const BASE_ADDR_0: usize = 0x4005_0000; - /// SPI B base address - pub const BASE_ADDR_1: usize = 0x4005_1000; - /// SPI C base address - pub const BASE_ADDR_2: usize = 0x4005_2000; - } else if #[cfg(feature = "vor4x")] { - /// SPI 0 base address - pub const BASE_ADDR_0: usize = 0x4001_5000; - /// SPI 1 base address - pub const BASE_ADDR_1: usize = 0x4001_5400; - /// SPI 2 base address - pub const BASE_ADDR_2: usize = 0x4001_5800; - /// SPI 3 base address - pub const BASE_ADDR_3: usize = 0x4001_5C00; - } -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Bank { - Spi0, - Spi1, - Spi2, - #[cfg(feature = "vor4x")] - Spi3, -} - -impl Bank { - /// Unsafely steal the SPI peripheral block for the given port. - /// - /// # Safety - /// - /// Circumvents ownership and safety guarantees by the HAL. - pub unsafe fn steal_regs(&self) -> MmioSpi<'static> { - Spi::new_mmio(*self) - } -} - -#[bitbybit::bitenum(u4)] -#[derive(Debug, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum WordSize { - OneBit = 0x00, - FourBits = 0x03, - EightBits = 0x07, - SixteenBits = 0x0f, -} - -#[derive(Debug, PartialEq, Eq)] -#[bitbybit::bitenum(u3, exhaustive = true)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum HwChipSelectId { - Id0 = 0, - Id1 = 1, - Id2 = 2, - Id3 = 3, - Id4 = 4, - Id5 = 5, - Id6 = 6, - Id7 = 7, -} - -#[bitbybit::bitfield(u32, default = 0x0)] -#[derive(Debug)] -pub struct Control0 { - #[bits(8..=15, rw)] - scrdv: u8, - #[bit(7, rw)] - sph: bool, - #[bit(6, rw)] - spo: bool, - #[bits(0..=3, rw)] - word_size: Option, -} - -#[bitbybit::bitfield(u32, default = 0x0)] -#[derive(Debug)] -pub struct Control1 { - #[bit(11, rw)] - mtxpause: bool, - #[bit(10, rw)] - mdlycap: bool, - #[bit(9, rw)] - bm_stall: bool, - #[bit(8, rw)] - bm_start: bool, - #[bit(7, rw)] - blockmode: bool, - #[bits(4..=6, rw)] - ss: HwChipSelectId, - #[bit(3, rw)] - sod: bool, - #[bit(2, rw)] - slave_mode: bool, - #[bit(1, rw)] - enable: bool, - #[bit(0, rw)] - lbm: bool, -} - -#[bitbybit::bitfield(u32)] -#[derive(Debug)] -pub struct Data { - /// Only used for BLOCKMODE. For received data, this bit indicated that the data was the first - /// word after the chip select went active. For transmitted data, setting this bit to 1 - /// will end an SPI frame (deassert CS) after the specified data word. - #[bit(31, rw)] - bm_start_stop: bool, - /// Only used for BLOCKMODE. Setting this bit to 1 along with the BMSTOP bit will end an SPI - /// frame without any additional data to be transmitted. If BMSTOP is not set, this bit is - /// ignored. - #[bit(30, rw)] - bm_skipdata: bool, - #[bits(0..=15, rw)] - data: u16, -} - -#[bitbybit::bitfield(u32)] -#[derive(Debug)] -pub struct Status { - /// TX FIFO below the trigger level. - #[bit(7, r)] - tx_trigger: bool, - /// RX FIFO above or equals the trigger level. - #[bit(6, r)] - rx_trigger: bool, - #[bit(5, r)] - rx_data_first: bool, - #[bit(4, r)] - busy: bool, - #[bit(3, r)] - rx_full: bool, - #[bit(2, r)] - rx_not_empty: bool, - #[bit(1, r)] - tx_not_full: bool, - #[bit(0, r)] - tx_empty: bool, -} - -/// Clock divisor value. Bit 0 is ignored and always 0. This means that only the even values -/// are used as clock divisor values, and uneven values are truncated to the next even value. -/// A value of 0 acts as a 1 for the divisor value. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct ClkPrescaler(arbitrary_int::UInt); - -impl ClkPrescaler { - pub const fn new(value: u8) -> Self { - ClkPrescaler(arbitrary_int::UInt::::new(value as u32)) - } - pub const fn value(&self) -> u8 { - self.0.value() as u8 - } -} - -#[bitbybit::bitfield(u32)] -#[derive(Debug)] -pub struct InterruptControl { - /// TX FIFO count <= TX FIFO trigger level. - #[bit(3, rw)] - tx: bool, - /// RX FIFO count >= RX FIFO trigger level. - #[bit(2, rw)] - rx: bool, - /// Occurs when the RX FIFO has not been read within 32 clock ticks of the SPICLKx2 clock - /// within the RX FIFO not being empty. Clearing the RX interrupt or reading data from the - /// FIFO resets the timeout counter. - #[bit(1, rw)] - rx_timeout: bool, - #[bit(0, rw)] - rx_overrun: bool, -} - -#[bitbybit::bitfield(u32)] -#[derive(Debug)] -pub struct InterruptStatus { - /// TX FIFO count <= TX FIFO trigger level. - #[bit(3, r)] - tx: bool, - /// RX FIFO count >= RX FIFO trigger level. - #[bit(2, r)] - rx: bool, - /// Occurs when the RX FIFO has not been read within 32 clock ticks of the SPICLKx2 clock - /// within the RX FIFO not being empty. Clearing the RX interrupt or reading data from the - /// FIFO resets the timeout counter. - #[bit(1, r)] - rx_timeout: bool, - #[bit(0, r)] - rx_overrun: bool, -} - -#[bitbybit::bitfield(u32)] -#[derive(Debug)] -pub struct InterruptClear { - /// Clearing the RX interrupt or reading data from the FIFO resets the timeout counter. - #[bit(1, w)] - rx_timeout: bool, - #[bit(0, w)] - rx_overrun: bool, -} - -#[bitbybit::bitfield(u32)] -#[derive(Debug)] -pub struct State { - #[bits(0..=7, r)] - rx_state: u8, - #[bits(8..=15, r)] - rx_fifo: u8, - #[bits(24..=31, r)] - tx_fifo: u8, -} - -#[derive(derive_mmio::Mmio)] -#[mmio(no_ctors)] -#[repr(C)] -pub struct Spi { - ctrl0: Control0, - ctrl1: Control1, - data: Data, - #[mmio(PureRead)] - status: Status, - clkprescale: ClkPrescaler, - irq_enb: InterruptControl, - /// Raw interrupt status. - #[mmio(PureRead)] - irq_raw: InterruptStatus, - /// Enabled interrupt status. - #[mmio(PureRead)] - irq_status: InterruptStatus, - #[mmio(Write)] - irq_clear: InterruptClear, - rx_fifo_trigger: TriggerLevel, - tx_fifo_trigger: TriggerLevel, - #[mmio(Write)] - fifo_clear: FifoClear, - #[mmio(PureRead)] - state: u32, - #[cfg(feature = "vor1x")] - _reserved: [u32; 0x3F2], - #[cfg(feature = "vor4x")] - _reserved: [u32; 0xF2], - /// Vorago 1x: 0x0113_07E1. Vorago 4x: 0x0213_07E9. - #[mmio(PureRead)] - perid: u32, -} - -cfg_if::cfg_if! { - if #[cfg(feature = "vor1x")] { - static_assertions::const_assert_eq!(core::mem::size_of::(), 0x1000); - } else if #[cfg(feature = "vor4x")] { - static_assertions::const_assert_eq!(core::mem::size_of::(), 0x400); - } -} - -impl Spi { - fn new_mmio_at(base: usize) -> MmioSpi<'static> { - MmioSpi { - ptr: base as *mut _, - phantom: PhantomData, - } - } - - pub fn new_mmio(bank: Bank) -> MmioSpi<'static> { - match bank { - Bank::Spi0 => Self::new_mmio_at(BASE_ADDR_0), - Bank::Spi1 => Self::new_mmio_at(BASE_ADDR_1), - Bank::Spi2 => Self::new_mmio_at(BASE_ADDR_2), - #[cfg(feature = "vor4x")] - Bank::Spi3 => Self::new_mmio_at(BASE_ADDR_2), - } - } -} diff --git a/vorago-shared-periphs/src/sysconfig.rs b/vorago-shared-periphs/src/sysconfig.rs deleted file mode 100644 index 02054b9..0000000 --- a/vorago-shared-periphs/src/sysconfig.rs +++ /dev/null @@ -1,43 +0,0 @@ -#[cfg(feature = "vor1x")] -#[inline] -pub fn enable_peripheral_clock(clock: crate::PeripheralSelect) { - let syscfg = unsafe { va108xx::Sysconfig::steal() }; - syscfg - .peripheral_clk_enable() - .modify(|r, w| unsafe { w.bits(r.bits() | (1 << clock as u8)) }); -} - -#[cfg(feature = "vor1x")] -#[inline] -pub fn disable_peripheral_clock(clock: crate::PeripheralSelect) { - let syscfg = unsafe { va108xx::Sysconfig::steal() }; - syscfg - .peripheral_clk_enable() - .modify(|r, w| unsafe { w.bits(r.bits() & !(1 << clock as u8)) }); -} - -#[cfg(feature = "vor1x")] -#[inline] -pub fn assert_peripheral_reset(periph_sel: crate::PeripheralSelect) { - let syscfg = unsafe { va108xx::Sysconfig::steal() }; - syscfg - .peripheral_reset() - .modify(|r, w| unsafe { w.bits(r.bits() & !(1 << periph_sel as u8)) }); -} - -#[cfg(feature = "vor1x")] -#[inline] -pub fn deassert_peripheral_reset(periph_sel: crate::PeripheralSelect) { - let syscfg = unsafe { va108xx::Sysconfig::steal() }; - syscfg - .peripheral_reset() - .modify(|r, w| unsafe { w.bits(r.bits() | (1 << periph_sel as u8)) }); -} - -#[cfg(feature = "vor1x")] -#[inline] -pub fn reset_peripheral_for_cycles(periph_sel: crate::PeripheralSelect, cycles: u32) { - assert_peripheral_reset(periph_sel); - cortex_m::asm::delay(cycles); - deassert_peripheral_reset(periph_sel); -} diff --git a/vorago-shared-periphs/src/time.rs b/vorago-shared-periphs/src/time.rs deleted file mode 100644 index 9808028..0000000 --- a/vorago-shared-periphs/src/time.rs +++ /dev/null @@ -1,26 +0,0 @@ -//! 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/timer/mod.rs b/vorago-shared-periphs/src/timer/mod.rs deleted file mode 100644 index d826708..0000000 --- a/vorago-shared-periphs/src/timer/mod.rs +++ /dev/null @@ -1,466 +0,0 @@ -pub mod regs; - -use core::convert::Infallible; - -#[cfg(feature = "vor1x")] -pub use crate::InterruptConfig; -pub use regs::{CascadeSource, InvalidTimerIndex, TimId}; - -use crate::{ - PeripheralSelect, - gpio::{Pin, PinId, PinIdProvider}, - ioconfig::regs::FunSel, - pins::PinMarker, - sysconfig::enable_peripheral_clock, -}; -use crate::{ - enable_nvic_interrupt, - pins::{ - Pa0, Pa1, Pa2, Pa3, Pa4, Pa5, Pa6, Pa7, Pa8, Pa9, Pa10, Pa11, Pa12, Pa13, Pa14, Pa15, Pa24, - Pa25, Pa26, Pa27, Pa28, Pa29, Pa30, Pa31, Pb0, Pb1, Pb2, Pb3, Pb4, Pb5, Pb6, Pb10, Pb11, - Pb12, Pb13, Pb14, Pb15, Pb16, Pb17, Pb18, Pb19, Pb20, Pb21, Pb22, Pb23, - }, - sealed::Sealed, - time::Hertz, -}; -use fugit::RateExtU32; - -#[cfg(feature = "vor1x")] -use va108xx as pac; -#[cfg(feature = "vor4x")] -use va416xx as pac; - -//================================================================================================== -// Defintions -//================================================================================================== - -#[derive(Default, Debug, PartialEq, Eq, Copy, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct CascadeControl { - /// Enable Cascade 0 signal active as a requirement for counting - pub enable_src_0: bool, - /// Invert Cascade 0, making it active low - pub inv_src_0: regs::CascadeInvert, - /// Enable Cascade 1 signal active as a requirement for counting - pub enable_src_1: bool, - /// Invert Cascade 1, making it active low - pub inv_src_1: regs::CascadeInvert, - /// Specify required operation if both Cascade 0 and Cascade 1 are active. - /// 0 is a logical AND of both cascade signals, 1 is a logical OR - pub dual_operation: regs::DualCascadeOp, - /// Enable trigger mode for Cascade 0. In trigger mode, couting will start with the selected - /// cascade signal active, but once the counter is active, cascade control will be ignored - pub trigger_mode_0: bool, - /// Trigger mode, identical to [Self::trigger_mode_0] but for Cascade 1 - pub trigger_mode_1: bool, - /// Enable Cascade 2 signal active as a requirement to stop counting. This mode is similar - /// to the REQ_STOP control bit, but signalled by a Cascade source - pub enable_stop_src_2: bool, - /// Invert Cascade 2, making it active low - pub inv_src_2: regs::CascadeInvert, - /// The counter is automatically disabled if the corresponding Cascade 2 level-sensitive input - /// souce is active when the count reaches 0. If the counter is not 0, the cascade control is - /// ignored - pub trigger_mode_2: bool, -} - -#[derive(Debug, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum CascadeSelect { - Csd0 = 0, - Csd1 = 1, - Csd2 = 2, -} - -//================================================================================================== -// Valid TIM and PIN combinations -//================================================================================================== - -pub trait TimPin: PinMarker { - const PIN_ID: PinId; - const FUN_SEL: FunSel; - const TIM_ID: TimId; -} - -pub trait TimMarker: Sealed { - // TIM ID ranging from 0 to 23 for 24 TIM peripherals - const ID: TimId; -} - -macro_rules! tim_marker { - ($TIMX:path, $ID:expr) => { - impl TimMarker for $TIMX { - const ID: TimId = TimId::new_unchecked($ID); - } - - impl Sealed for $TIMX {} - }; -} - -tim_marker!(pac::Tim0, 0); -tim_marker!(pac::Tim1, 1); -tim_marker!(pac::Tim2, 2); -tim_marker!(pac::Tim3, 3); -tim_marker!(pac::Tim4, 4); -tim_marker!(pac::Tim5, 5); -tim_marker!(pac::Tim6, 6); -tim_marker!(pac::Tim7, 7); -tim_marker!(pac::Tim8, 8); -tim_marker!(pac::Tim9, 9); -tim_marker!(pac::Tim10, 10); -tim_marker!(pac::Tim11, 11); -tim_marker!(pac::Tim12, 12); -tim_marker!(pac::Tim13, 13); -tim_marker!(pac::Tim14, 14); -tim_marker!(pac::Tim15, 15); -tim_marker!(pac::Tim16, 16); -tim_marker!(pac::Tim17, 17); -tim_marker!(pac::Tim18, 18); -tim_marker!(pac::Tim19, 19); -tim_marker!(pac::Tim20, 20); -tim_marker!(pac::Tim21, 21); -tim_marker!(pac::Tim22, 22); -tim_marker!(pac::Tim23, 23); - -pub trait ValidTimAndPin: Sealed {} - -macro_rules! pin_and_tim { - ($Px:ident, $FunSel:path, $ID:expr) => { - impl TimPin for Pin<$Px> - where - $Px: PinIdProvider, - { - const PIN_ID: PinId = $Px::ID; - const FUN_SEL: FunSel = $FunSel; - const TIM_ID: TimId = TimId::new_unchecked($ID); - } - }; -} - -pin_and_tim!(Pa0, FunSel::Sel1, 0); -pin_and_tim!(Pa1, FunSel::Sel1, 1); -pin_and_tim!(Pa2, FunSel::Sel1, 2); -pin_and_tim!(Pa3, FunSel::Sel1, 3); -pin_and_tim!(Pa4, FunSel::Sel1, 4); -pin_and_tim!(Pa5, FunSel::Sel1, 5); -pin_and_tim!(Pa6, FunSel::Sel1, 6); -pin_and_tim!(Pa7, FunSel::Sel1, 7); -pin_and_tim!(Pa8, FunSel::Sel1, 8); -pin_and_tim!(Pa9, FunSel::Sel1, 9); -pin_and_tim!(Pa10, FunSel::Sel1, 10); -pin_and_tim!(Pa11, FunSel::Sel1, 11); -pin_and_tim!(Pa12, FunSel::Sel1, 12); -pin_and_tim!(Pa13, FunSel::Sel1, 13); -pin_and_tim!(Pa14, FunSel::Sel1, 14); -pin_and_tim!(Pa15, FunSel::Sel1, 15); - -pin_and_tim!(Pa24, FunSel::Sel2, 16); -pin_and_tim!(Pa25, FunSel::Sel2, 17); -pin_and_tim!(Pa26, FunSel::Sel2, 18); -pin_and_tim!(Pa27, FunSel::Sel2, 19); -pin_and_tim!(Pa28, FunSel::Sel2, 20); -pin_and_tim!(Pa29, FunSel::Sel2, 21); -pin_and_tim!(Pa30, FunSel::Sel2, 22); -pin_and_tim!(Pa31, FunSel::Sel2, 23); - -pin_and_tim!(Pb0, FunSel::Sel3, 0); -pin_and_tim!(Pb1, FunSel::Sel3, 1); -pin_and_tim!(Pb2, FunSel::Sel3, 2); -pin_and_tim!(Pb3, FunSel::Sel3, 3); -pin_and_tim!(Pb4, FunSel::Sel3, 4); -pin_and_tim!(Pb5, FunSel::Sel3, 5); -pin_and_tim!(Pb6, FunSel::Sel3, 6); - -pin_and_tim!(Pb10, FunSel::Sel3, 10); -pin_and_tim!(Pb11, FunSel::Sel3, 11); -pin_and_tim!(Pb12, FunSel::Sel3, 12); -pin_and_tim!(Pb13, FunSel::Sel3, 13); -pin_and_tim!(Pb14, FunSel::Sel3, 14); -pin_and_tim!(Pb15, FunSel::Sel3, 15); -pin_and_tim!(Pb16, FunSel::Sel3, 16); -pin_and_tim!(Pb17, FunSel::Sel3, 17); -pin_and_tim!(Pb18, FunSel::Sel3, 18); -pin_and_tim!(Pb19, FunSel::Sel3, 19); -pin_and_tim!(Pb20, FunSel::Sel3, 20); -pin_and_tim!(Pb21, FunSel::Sel3, 21); -pin_and_tim!(Pb22, FunSel::Sel3, 22); -pin_and_tim!(Pb23, FunSel::Sel3, 23); - -//================================================================================================== -// Timers -//================================================================================================== - -/// Hardware timers -pub struct CountdownTimer { - id: TimId, - regs: regs::MmioTimer<'static>, - curr_freq: Hertz, - sys_clk: Hertz, - rst_val: u32, - last_cnt: u32, -} - -impl CountdownTimer { - /// Create a countdown timer structure for a given TIM peripheral. - /// - /// This does not enable the timer. You can use the [Self::load], [Self::start], - /// [Self::enable_interrupt] and [Self::enable] API to set up and configure the countdown - /// timer. - pub fn new(sys_clk: Hertz, _tim: Tim) -> Self { - enable_tim_clk(Tim::ID); - assert_tim_reset_for_cycles(Tim::ID, 2); - CountdownTimer { - id: Tim::ID, - regs: regs::Timer::new_mmio(Tim::ID), - sys_clk, - rst_val: 0, - curr_freq: 0.Hz(), - last_cnt: 0, - } - } - - #[inline(always)] - pub fn enable(&mut self) { - self.regs - .write_enable_control(regs::EnableControl::new_enable()); - } - #[inline(always)] - pub fn disable(&mut self) { - self.regs - .write_enable_control(regs::EnableControl::new_disable()); - } - - pub fn enable_interrupt(&mut self, irq_cfg: InterruptConfig) { - if irq_cfg.route { - let irqsel = unsafe { pac::Irqsel::steal() }; - enable_peripheral_clock(PeripheralSelect::Irqsel); - irqsel - .tim0(self.id.value() as usize) - .write(|w| unsafe { w.bits(irq_cfg.id as u32) }); - } - if irq_cfg.enable_in_nvic { - unsafe { enable_nvic_interrupt(irq_cfg.id) }; - } - self.regs.modify_control(|mut value| { - value.set_irq_enable(true); - value - }); - } - - /// This function only clears the interrupt enable bit. - /// - /// It does not mask the interrupt in the NVIC or un-route the IRQ. - #[inline(always)] - pub fn disable_interrupt(&mut self) { - self.regs.modify_control(|mut value| { - value.set_irq_enable(false); - value - }); - } - - /// Calls [Self::load] to configure the specified frequency and then calls [Self::enable]. - pub fn start(&mut self, frequency: impl Into) { - self.load(frequency); - self.enable(); - } - - /// Return `Ok` if the timer has wrapped. Peripheral will automatically clear the - /// flag and restart the time if configured correctly - pub fn wait(&mut self) -> nb::Result<(), Infallible> { - let cnt = self.counter(); - if (cnt > self.last_cnt) || cnt == 0 { - self.last_cnt = self.rst_val; - Ok(()) - } else { - self.last_cnt = cnt; - Err(nb::Error::WouldBlock) - } - } - - /// Load the count down timer with a timeout but do not start it. - pub fn load(&mut self, timeout: impl Into) { - self.disable(); - self.curr_freq = timeout.into(); - self.rst_val = self.sys_clk.raw() / self.curr_freq.raw(); - self.set_reload(self.rst_val); - self.set_count(self.rst_val); - } - - #[inline(always)] - pub fn set_reload(&mut self, val: u32) { - self.regs.write_reset_value(val); - } - - #[inline(always)] - pub fn set_count(&mut self, val: u32) { - self.regs.write_count_value(val); - } - - #[inline(always)] - pub fn counter(&self) -> u32 { - self.regs.read_count_value() - } - - /// Disable the counter, setting both enable and active bit to 0 - #[inline] - pub fn auto_disable(&mut self, enable: bool) { - self.regs.modify_control(|mut value| { - value.set_auto_disable(enable); - value - }); - } - - /// This option only applies when the Auto-Disable functionality is 0. - /// - /// The active bit is changed to 0 when count reaches 0, but the counter stays - /// enabled. When Auto-Disable is 1, Auto-Deactivate is implied - #[inline] - pub fn auto_deactivate(&mut self, enable: bool) { - self.regs.modify_control(|mut value| { - value.set_auto_deactivate(enable); - value - }); - } - - /// Configure the cascade parameters - pub fn cascade_control(&mut self, ctrl: CascadeControl) { - self.regs.write_cascade_control( - regs::CascadeControl::builder() - .with_trigger2(ctrl.trigger_mode_2) - .with_inv2(ctrl.inv_src_2) - .with_en2(ctrl.enable_stop_src_2) - .with_trigger1(ctrl.trigger_mode_1) - .with_trigger0(ctrl.trigger_mode_0) - .with_dual_cascade_op(ctrl.dual_operation) - .with_inv1(ctrl.inv_src_1) - .with_en1(ctrl.enable_src_1) - .with_inv0(ctrl.inv_src_0) - .with_en0(ctrl.enable_src_0) - .build(), - ); - } - - pub fn cascade_source( - &mut self, - cascade_index: CascadeSelect, - src: regs::CascadeSource, - ) -> Result<(), regs::InvalidCascadeSourceId> { - // Safety: Index range safe by enum values. - unsafe { - self.regs - .write_cascade_unchecked(cascade_index as usize, regs::CascadeSourceReg::new(src)?); - } - Ok(()) - } - - pub fn curr_freq(&self) -> Hertz { - self.curr_freq - } - - /// Disables the TIM and the dedicated TIM clock. - pub fn stop_with_clock_disable(mut self) { - self.disable(); - unsafe { va108xx::Sysconfig::steal() } - .tim_clk_enable() - .modify(|r, w| unsafe { w.bits(r.bits() & !(1 << self.id.value())) }); - } -} - -//================================================================================================== -// Delay implementations -//================================================================================================== -// -impl embedded_hal::delay::DelayNs for CountdownTimer { - fn delay_ns(&mut self, ns: u32) { - let ticks = (u64::from(ns)) * (u64::from(self.sys_clk.raw())) / 1_000_000_000; - - let full_cycles = ticks >> 32; - let mut last_count; - let mut new_count; - if full_cycles > 0 { - self.set_reload(u32::MAX); - self.set_count(u32::MAX); - self.enable(); - - for _ in 0..full_cycles { - // Always ensure that both values are the same at the start. - new_count = self.counter(); - last_count = new_count; - loop { - new_count = self.counter(); - if new_count == 0 { - // Wait till timer has wrapped. - while self.counter() == 0 { - cortex_m::asm::nop() - } - break; - } - // Timer has definitely wrapped. - if new_count > last_count { - break; - } - last_count = new_count; - } - } - } - let ticks = (ticks & u32::MAX as u64) as u32; - self.disable(); - if ticks > 1 { - self.set_reload(ticks); - self.set_count(ticks); - self.enable(); - last_count = ticks; - - loop { - new_count = self.counter(); - if new_count == 0 || (new_count > last_count) { - break; - } - last_count = new_count; - } - } - - self.disable(); - } -} - -#[inline(always)] -pub fn enable_tim_clk(id: TimId) { - unsafe { pac::Sysconfig::steal() } - .tim_clk_enable() - .modify(|r, w| unsafe { w.bits(r.bits() | (1 << id.value())) }); -} - -#[inline(always)] -pub fn disable_tim_clk(id: TimId) { - unsafe { pac::Sysconfig::steal() } - .tim_clk_enable() - .modify(|r, w| unsafe { w.bits(r.bits() & !(1 << (id.value()))) }); -} - -/// Clear the reset bit of the TIM, holding it in reset -/// -/// # Safety -/// -/// Only the bit related to the corresponding TIM peripheral is modified -#[inline] -fn assert_tim_reset(id: TimId) { - unsafe { pac::Peripherals::steal() } - .sysconfig - .tim_reset() - .modify(|r, w| unsafe { w.bits(r.bits() & !(1 << id.value())) }); -} - -#[inline] -fn deassert_tim_reset(tim: TimId) { - unsafe { pac::Peripherals::steal() } - .sysconfig - .tim_reset() - .modify(|r, w| unsafe { w.bits(r.bits() | (1 << tim.value())) }); -} - -fn assert_tim_reset_for_cycles(tim: TimId, cycles: u32) { - assert_tim_reset(tim); - cortex_m::asm::delay(cycles); - deassert_tim_reset(tim); -} diff --git a/vorago-shared-periphs/src/timer/pins_vor1x.rs b/vorago-shared-periphs/src/timer/pins_vor1x.rs deleted file mode 100644 index e69de29..0000000 diff --git a/vorago-shared-periphs/src/timer/regs.rs b/vorago-shared-periphs/src/timer/regs.rs deleted file mode 100644 index c3077df..0000000 --- a/vorago-shared-periphs/src/timer/regs.rs +++ /dev/null @@ -1,309 +0,0 @@ -use core::marker::PhantomData; - -use arbitrary_int::{Number, u7}; - -#[cfg(feature = "vor1x")] -const BASE_ADDR: usize = 0x4002_0000; -#[cfg(feature = "vor4x")] -const BASE_ADDR: usize = 0x4001_8000; - -#[bitbybit::bitenum(u3)] -#[derive(Debug, PartialEq, Eq)] -pub enum StatusSelect { - /// Pulse when timer reaches 0. - OneCyclePulse = 0b000, - OutputActiveBit = 0b001, - /// Creates a divide by two output clock of the timer. - ToggleOnEachCycle = 0b010, - /// 1 when count value >= PWM A value, 0 otherwise - PwmaOutput = 0b011, - /// 1 when count value < PWM A value and >= PWM B, 0 when counter value >= PWM A value or < PWM - /// B value - PwmbOutput = 0b100, - EnabledBit = 0b101, - /// 1 when counter value <= PWM A value and 0 otherwise. - PwmaActiveBit = 0b110, -} - -#[bitbybit::bitfield(u32)] -pub struct Control { - /// The counter is requested to stop on the next normal count cycle. - #[bit(9, rw)] - request_stop: bool, - #[bit(8, rw)] - status_invert: bool, - #[bits(5..=7, rw)] - status_sel: Option, - #[bit(4, rw)] - irq_enable: bool, - /// Only applies if the Auto-Disable bit is 0. The ACTIVE bit goes to 0 when the count reaches - /// 0, but the timer remains enabled. - #[bit(3, rw)] - auto_deactivate: bool, - /// Counter is fully disabled when count reaches 0, which means that both the ENABLE - /// and ACTIVE bits go to 0. - #[bit(2, rw)] - auto_disable: bool, - #[bit(1, r)] - active: bool, - #[bit(0, rw)] - enable: bool, -} - -pub struct EnableControl(arbitrary_int::UInt); - -impl EnableControl { - pub fn new_disable() -> Self { - EnableControl(arbitrary_int::UInt::::from_u32(0)) - } - - pub fn new_enable() -> Self { - EnableControl(arbitrary_int::UInt::::from_u32(1)) - } - - pub fn enabled(&self) -> bool { - self.0.value() != 0 - } -} - -#[bitbybit::bitenum(u1, exhaustive = true)] -#[derive(Default, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum CascadeInvert { - #[default] - ActiveHigh = 0, - ActiveLow = 1, -} - -/// When two cascade sources are selected, configure the required operation. -#[bitbybit::bitenum(u1, exhaustive = true)] -#[derive(Default, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum DualCascadeOp { - #[default] - LogicalAnd = 0, - LogicalOr = 1, -} - -#[bitbybit::bitfield(u32, default = 0x0)] -pub struct CascadeControl { - /// The counter is automatically disabled if the corresponding Cascade 2 level-sensitive input - /// souce is active when the count reaches 0. If the counter is not 0, the cascade control is - /// ignored. - #[bit(10, rw)] - trigger2: bool, - #[bit(9, rw)] - inv2: CascadeInvert, - /// Enable Cascade 2 signal active as a requirement to stop counting. This mode is similar - /// to the REQ_STOP control bit, but signalled by a Cascade source. - #[bit(8, rw)] - en2: bool, - /// Same as the trigger field for Cascade 0. - #[bit(7, rw)] - trigger1: bool, - /// Enable trigger mode for Cascade 0. In trigger mode, couting will start with the selected - /// cascade signal active, but once the counter is active, cascade control will be ignored. - #[bit(6, rw)] - trigger0: bool, - /// Specify required operation if both Cascade 0 and Cascade 1 are active. - /// 0 is a logical AND of both cascade signals, 1 is a logical OR. - #[bit(4, rw)] - dual_cascade_op: DualCascadeOp, - /// Inversion bit for Cascade 1 - #[bit(3, rw)] - inv1: CascadeInvert, - /// Enable Cascade 1 signal active as a requirement for counting. - #[bit(2, rw)] - en1: bool, - /// Inversion bit for Cascade 0. - #[bit(1, rw)] - inv0: CascadeInvert, - /// Enable Cascade 0 signal active as a requirement for counting. - #[bit(0, rw)] - en0: bool, -} - -#[derive(Debug, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct InvalidCascadeSourceId; - -/// The numbers are the base numbers for bundles like PORTA, PORTB or TIM -#[cfg(feature = "vor1x")] -#[derive(Debug, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[repr(u8)] -pub enum CascadeSource { - PortA(u8), - PortB(u8), - Tim(u8), - RamSbe = 96, - RamMbe = 97, - RomSbe = 98, - RomMbe = 99, - Txev = 100, - ClockDivider(u8), -} - -impl CascadeSource { - #[cfg(feature = "vor1x")] - pub fn id(&self) -> Result { - let port_check = |base: u8, id: u8, len: u8| -> Result { - if id > len - 1 { - return Err(InvalidCascadeSourceId); - } - Ok(u7::new(base + id)) - }; - match self { - CascadeSource::PortA(id) => port_check(0, *id, 32), - CascadeSource::PortB(id) => port_check(32, *id, 32), - CascadeSource::Tim(id) => port_check(64, *id, 24), - CascadeSource::RamSbe => Ok(u7::new(96)), - CascadeSource::RamMbe => Ok(u7::new(97)), - CascadeSource::RomSbe => Ok(u7::new(98)), - CascadeSource::RomMbe => Ok(u7::new(99)), - CascadeSource::Txev => Ok(u7::new(100)), - CascadeSource::ClockDivider(id) => port_check(120, *id, 8), - } - } - - #[cfg(feature = "vor1x")] - pub fn from_raw(raw: u32) -> Result { - let id = u7::new((raw & 0x7F) as u8); - if id.value() > 127 { - return Err(InvalidCascadeSourceId); - } - let id = id.as_u8(); - if id < 32 { - return Ok(CascadeSource::PortA(id)); - } else if (32..56).contains(&id) { - return Ok(CascadeSource::PortB(id - 32)); - } else if (64..88).contains(&id) { - return Ok(CascadeSource::Tim(id - 64)); - } else if id > 120 { - return Ok(CascadeSource::ClockDivider(id - 120)); - } - match id { - 96 => Ok(CascadeSource::RamSbe), - 97 => Ok(CascadeSource::RamMbe), - 98 => Ok(CascadeSource::RomSbe), - 99 => Ok(CascadeSource::RomMbe), - 100 => Ok(CascadeSource::Txev), - _ => Err(InvalidCascadeSourceId), - } - } -} - -#[bitbybit::bitfield(u32)] -pub struct CascadeSourceReg { - #[bits(0..=6, rw)] - raw: u7, -} - -impl CascadeSourceReg { - pub fn new(source: CascadeSource) -> Result { - let id = source.id()?; - Ok(Self::new_with_raw_value(id.as_u32())) - } - - pub fn as_cascade_source(&self) -> Result { - CascadeSource::from_raw(self.raw().as_u32()) - } -} - -#[derive(derive_mmio::Mmio)] -#[mmio(no_ctors)] -#[repr(C)] -pub struct Timer { - control: Control, - reset_value: u32, - count_value: u32, - enable_control: EnableControl, - cascade_control: CascadeControl, - /// CASCADE0 and CASCADE1 are used to control the counting and activation of the counter. - /// CASCADE2 is used to request stopping of the timer. - cascade: [CascadeSourceReg; 3], - /// PWM A compare value. - pwma_value: u32, - /// PWM B compare value. - pwmb_value: u32, - #[cfg(feature = "vor1x")] - _reserved: [u32; 0x3f5], - #[cfg(feature = "vor4x")] - _reserved: [u32; 0xf5], - /// Vorago 1x: 0x0111_07E1. Vorago 4x: 0x0211_07E9 - perid: u32, -} - -cfg_if::cfg_if! { - if #[cfg(feature = "vor1x")] { - static_assertions::const_assert_eq!(core::mem::size_of::(), 0x1000); - } else if #[cfg(feature = "vor4x")] { - static_assertions::const_assert_eq!(core::mem::size_of::(), 0x400); - } -} - -#[derive(Debug, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct InvalidTimerIndex(pub usize); - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct TimId(u8); - -impl TimId { - pub const fn new(index: usize) -> Result { - if index > 23 { - return Err(InvalidTimerIndex(index)); - } - Ok(TimId(index as u8)) - } - - pub const fn new_unchecked(index: usize) -> Self { - if index > 23 { - panic!("invalid timer index"); - } - TimId(index as u8) - } - - /// Unsafely steal the TIM peripheral block for the TIM ID. - /// - /// # Safety - /// - /// Circumvents ownership and safety guarantees by the HAL. - pub const unsafe fn steal_regs(&self) -> MmioTimer<'static> { - Timer::new_mmio(*self) - } - - pub const fn value(&self) -> u8 { - self.0 - } -} - -impl Timer { - const fn new_mmio_at(base: usize) -> MmioTimer<'static> { - MmioTimer { - ptr: base as *mut _, - phantom: PhantomData, - } - } - - pub const fn new_mmio(id: TimId) -> MmioTimer<'static> { - if cfg!(feature = "vor1x") { - Timer::new_mmio_at(BASE_ADDR + 0x1000 * id.value() as usize) - } else { - Timer::new_mmio_at(BASE_ADDR + 0x400 * id.value() as usize) - } - } - pub fn new_mmio_with_raw_index( - timer_index: usize, - ) -> Result, InvalidTimerIndex> { - if timer_index > 23 { - return Err(InvalidTimerIndex(timer_index)); - } - if cfg!(feature = "vor1x") { - Ok(Timer::new_mmio_at(BASE_ADDR + 0x1000 * timer_index)) - } else { - Ok(Timer::new_mmio_at(BASE_ADDR + 0x400 * timer_index)) - } - } -} diff --git a/vorago-shared-periphs/src/uart/mod.rs b/vorago-shared-periphs/src/uart/mod.rs deleted file mode 100644 index ad66cb1..0000000 --- a/vorago-shared-periphs/src/uart/mod.rs +++ /dev/null @@ -1,1260 +0,0 @@ -//! # 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 rx_enabled = self.0.regs.read_enable().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[context.rx_idx] = byte; - context.rx_idx += 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 deleted file mode 100644 index c7c0a95..0000000 --- a/vorago-shared-periphs/src/uart/pins_vor1x.rs +++ /dev/null @@ -1,112 +0,0 @@ -// 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 deleted file mode 100644 index 878bd5f..0000000 --- a/vorago-shared-periphs/src/uart/regs.rs +++ /dev/null @@ -1,304 +0,0 @@ -use core::marker::PhantomData; - -use arbitrary_int::{u5, u6, u18}; - -cfg_if::cfg_if! { - if #[cfg(feature = "vor1x")] { - /// UART A base address - pub const BASE_ADDR_0: usize = 0x4004_0000; - /// UART B base address - pub const BASE_ADDR_1: usize = 0x4004_1000; - } else if #[cfg(feature = "vor4x")] { - /// UART 0 base address - pub const BASE_ADDR_0: usize = 0x4002_4000; - /// UART 1 base address - pub const BASE_ADDR_1: usize = 0x4002_5000; - /// UART 2 base address - pub const BASE_ADDR_2: usize = 0x4001_7000; - } -} - -#[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) - } -} - -#[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 deleted file mode 100644 index 19687ad..0000000 --- a/vorago-shared-periphs/src/uart/rx_asynch.rs +++ /dev/null @@ -1,437 +0,0 @@ -//! # 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 deleted file mode 100644 index 5234f76..0000000 --- a/vorago-shared-periphs/src/uart/tx_asynch.rs +++ /dev/null @@ -1,205 +0,0 @@ -//! # 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 - } -}