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<Self::Output> {
-        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<Self, InvalidOffsetError> {
-        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<I: PinIdProvider>(_pin: Pin<I>) -> 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<Pull>) {
-        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<I: PinIdProvider> {
-    phantom: core::marker::PhantomData<I>,
-}
-
-impl<I: PinIdProvider> Pin<I> {
-    #[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<I: PinIdProvider>(_pin: Pin<I>, 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<bool, Self::Error> {
-        Ok(self.0.is_set_high())
-    }
-
-    fn is_set_low(&mut self) -> Result<bool, Self::Error> {
-        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<I: PinIdProvider>(_pin: Pin<I>) -> Self {
-        let mut ll = ll::LowLevelGpio::new(I::ID);
-        ll.configure_as_input_floating();
-        Input(ll)
-    }
-
-    pub fn new_with_pull<I: PinIdProvider>(_pin: Pin<I>, 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<bool, Self::Error> {
-        Ok(self.0.is_low())
-    }
-
-    fn is_high(&mut self) -> Result<bool, Self::Error> {
-        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<I: PinIdProvider>(_pin: Pin<I>) -> 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<bool, Self::Error> {
-        Ok(self.ll.is_low())
-    }
-
-    /// Reads the input state of the pin, regardless of configured mode.
-    fn is_high(&mut self) -> Result<bool, Self::Error> {
-        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<bool, Self::Error> {
-        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<bool, Self::Error> {
-        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<I: PinIdProvider>(
-        _pin: Pin<I>,
-        fun_sel: FunSel,
-        pull: Option<Pull>,
-    ) -> 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<Pull>) -> 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::<Gpio>(), 0x1000);
-    } else if #[cfg(feature = "vor4x")] {
-        static_assertions::const_assert_eq!(core::mem::size_of::<Gpio>(), 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<u8, ClockTooSlowForFastI2cError> {
-    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<TimingConfig>,
-    /// See [I2cMaster::set_clock_low_timeout] documentation.
-    pub timeout: Option<u20>,
-    // 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: &regs::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<Addr = SevenBitAddress> {
-    id: Bank,
-    regs: regs::MmioI2c<'static>,
-    addr: PhantomData<Addr>,
-}
-
-impl<Addr> I2cMaster<Addr> {
-    pub fn new<I2c: I2cMarker>(
-        sysclk: Hertz,
-        _i2c: I2c,
-        cfg: MasterConfig,
-        speed_mode: I2cSpeed,
-    ) -> Result<Self, ClockTooSlowForFastI2cError> {
-        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<SevenBitAddress> {
-    type Error = Error;
-}
-
-impl embedded_hal::i2c::I2c for I2cMaster<SevenBitAddress> {
-    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<TenBitAddress> {
-    type Error = Error;
-}
-
-impl embedded_hal::i2c::I2c<TenBitAddress> for I2cMaster<TenBitAddress> {
-    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<u32, 11>);
-
-impl Words {
-    pub const fn new(value: u11) -> Self {
-        Words(arbitrary_int::UInt::<u32, 11>::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<u32, 8>);
-
-impl Data {
-    pub const fn new(value: u8) -> Self {
-        Data(arbitrary_int::UInt::<u32, 8>::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<u32, 11>);
-
-#[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<u32, 20>);
-
-impl ClkTimeoutLimit {
-    pub fn new(value: u20) -> Self {
-        ClkTimeoutLimit(arbitrary_int::UInt::<u32, 20>::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::<I2c>(), 0x1000);
-    } else if #[cfg(feature = "vor4x")] {
-        static_assertions::const_assert_eq!(core::mem::size_of::<I2c>(), 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<FilterType>,
-}
-
-#[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::<IoConfig>(), 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<F: FnOnce(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<u32, 5>);
-
-    impl TriggerLevel {
-        pub const fn new(value: u5) -> Self {
-            TriggerLevel(arbitrary_int::UInt::<u32, 5>::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<I: PinIdProvider + Sealed> Sealed for Pin<I> {}
-
-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<Pa0>,
-    pub pa1: Pin<Pa1>,
-    pub pa2: Pin<Pa2>,
-    pub pa3: Pin<Pa3>,
-    pub pa4: Pin<Pa4>,
-    pub pa5: Pin<Pa5>,
-    pub pa6: Pin<Pa6>,
-    pub pa7: Pin<Pa7>,
-    pub pa8: Pin<Pa8>,
-    pub pa9: Pin<Pa9>,
-    pub pa10: Pin<Pa10>,
-    pub pa11: Pin<Pa11>,
-    pub pa12: Pin<Pa12>,
-    pub pa13: Pin<Pa13>,
-    pub pa14: Pin<Pa14>,
-    pub pa15: Pin<Pa15>,
-    pub pa16: Pin<Pa16>,
-    pub pa17: Pin<Pa17>,
-    pub pa18: Pin<Pa18>,
-    pub pa19: Pin<Pa19>,
-    pub pa20: Pin<Pa20>,
-    pub pa21: Pin<Pa21>,
-    pub pa22: Pin<Pa22>,
-    pub pa23: Pin<Pa23>,
-    pub pa24: Pin<Pa24>,
-    pub pa25: Pin<Pa25>,
-    pub pa26: Pin<Pa26>,
-    pub pa27: Pin<Pa27>,
-    pub pa28: Pin<Pa28>,
-    pub pa29: Pin<Pa29>,
-    pub pa30: Pin<Pa30>,
-    pub pa31: Pin<Pa31>,
-}
-
-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<Pb0>,
-    pub pb1: Pin<Pb1>,
-    pub pb2: Pin<Pb2>,
-    pub pb3: Pin<Pb3>,
-    pub pb4: Pin<Pb4>,
-    pub pb5: Pin<Pb5>,
-    pub pb6: Pin<Pb6>,
-    pub pb7: Pin<Pb7>,
-    pub pb8: Pin<Pb8>,
-    pub pb9: Pin<Pb9>,
-    pub pb10: Pin<Pb10>,
-    pub pb11: Pin<Pb11>,
-    pub pb12: Pin<Pb12>,
-    pub pb13: Pin<Pb13>,
-    pub pb14: Pin<Pb14>,
-    pub pb15: Pin<Pb15>,
-    pub pb16: Pin<Pb16>,
-    pub pb17: Pin<Pb17>,
-    pub pb18: Pin<Pb18>,
-    pub pb19: Pin<Pb19>,
-    pub pb20: Pin<Pb20>,
-    pub pb21: Pin<Pb21>,
-    pub pb22: Pin<Pb22>,
-    pub pb23: Pin<Pb23>,
-}
-
-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<Mode = PwmA> {
-    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<Mode>,
-}
-
-impl<Mode> PwmPin<Mode> {
-    /// Create a new strongly typed PWM pin
-    pub fn new<Pin: TimPin, Tim: TimMarker>(
-        sys_clk: Hertz,
-        _pin_and_tim: (Pin, Tim),
-        initial_frequency: Hertz,
-    ) -> Result<Self, TimMissmatchError> {
-        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<Hertz>) {
-        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<PwmPin<PwmA>> for PwmPin<PwmB> {
-    fn from(other: PwmPin<PwmA>) -> 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<PwmPin<PwmB>> for PwmPin<PwmA> {
-    fn from(other: PwmPin<PwmB>) -> 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<PwmB> {
-    #[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<P: PinMarker + HwCsProvider>(_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<SpiClkConfig>,
-    pub mode: Option<Mode>,
-    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<HwChipSelectId>,
-}
-
-impl TransferConfig {
-    pub fn new_with_hw_cs(
-        clk_cfg: Option<SpiClkConfig>,
-        mode: Option<Mode>,
-        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<u32> + TryFrom<u32> + '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<u32, Infallible>;
-
-    /// 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<Self, SpiClkConfigError> {
-        spi_clk_config_from_div(div)
-    }
-
-    pub fn from_clk(sys_clk: impl Into<Hertz>, spi_clk: impl Into<Hertz>) -> Option<Self> {
-        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<SpiClkConfig, SpiClkConfigError> {
-    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<Hertz>,
-    spi_clk: impl Into<Hertz>,
-) -> Option<u16> {
-    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<Word = u8> {
-    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<Word>,
-}
-
-impl<Word: WordProvider> Spi<Word>
-where
-    <Word as TryFrom<u32>>::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<SpiI: SpiMarker>(
-        sys_clk: Hertz,
-        spi: SpiI,
-        spi_cfg: SpiConfig,
-    ) -> Result<Self, SpiIdMissmatchError> {
-        #[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<SpiI: SpiMarker, Sck: PinSck, Miso: PinMiso, Mosi: PinMosi>(
-        sys_clk: Hertz,
-        spi: SpiI,
-        _pins: (Sck, Miso, Mosi),
-        spi_cfg: SpiConfig,
-    ) -> Result<Self, SpiIdMissmatchError> {
-        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<SpiI: SpiMarker>(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<Word: WordProvider> SpiLowLevel for Spi<Word>
-where
-    <Word as TryFrom<u32>>::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<u32, Infallible> {
-        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<Word: WordProvider> embedded_hal::spi::ErrorType for Spi<Word> {
-    type Error = Infallible;
-}
-
-impl<Word: WordProvider> embedded_hal::spi::SpiBus<Word> for Spi<Word>
-where
-    <Word as TryFrom<u32>>::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<Spi<u8>> for Spi<u16> {
-    fn from(mut old_spi: Spi<u8>) -> 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<Spi<u16>> for Spi<u8> {
-    fn from(mut old_spi: Spi<u16>) -> 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<Pa31> {
-    const SPI_ID: Bank = Bank::Spi0;
-    const FUN_SEL: FunSel = FunSel::Sel1;
-}
-impl PinMosi for Pin<Pa30> {
-    const SPI_ID: Bank = Bank::Spi0;
-    const FUN_SEL: FunSel = FunSel::Sel1;
-}
-impl PinMiso for Pin<Pa29> {
-    const SPI_ID: Bank = Bank::Spi0;
-    const FUN_SEL: FunSel = FunSel::Sel1;
-}
-
-impl PinSck for Pin<Pb9> {
-    const SPI_ID: Bank = Bank::Spi0;
-    const FUN_SEL: FunSel = FunSel::Sel2;
-}
-impl PinMosi for Pin<Pb8> {
-    const SPI_ID: Bank = Bank::Spi0;
-    const FUN_SEL: FunSel = FunSel::Sel2;
-}
-impl PinMiso for Pin<Pb7> {
-    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<Pa20> {
-    const SPI_ID: Bank = Bank::Spi1;
-    const FUN_SEL: FunSel = FunSel::Sel2;
-}
-impl PinMosi for Pin<Pa19> {
-    const SPI_ID: Bank = Bank::Spi1;
-    const FUN_SEL: FunSel = FunSel::Sel2;
-}
-impl PinMiso for Pin<Pa18> {
-    const SPI_ID: Bank = Bank::Spi1;
-    const FUN_SEL: FunSel = FunSel::Sel2;
-}
-
-pub type SpiBPortASck = Pin<Pa20>;
-pub type SpiBPortAMosi = Pin<Pa19>;
-pub type SpiBPortAMiso = Pin<Pa18>;
-
-impl PinSck for Pin<Pb19> {
-    const SPI_ID: Bank = Bank::Spi1;
-    const FUN_SEL: FunSel = FunSel::Sel1;
-}
-impl PinMosi for Pin<Pb18> {
-    const SPI_ID: Bank = Bank::Spi1;
-    const FUN_SEL: FunSel = FunSel::Sel1;
-}
-impl PinMiso for Pin<Pb17> {
-    const SPI_ID: Bank = Bank::Spi1;
-    const FUN_SEL: FunSel = FunSel::Sel1;
-}
-
-impl PinSck for Pin<Pb5> {
-    const SPI_ID: Bank = Bank::Spi1;
-    const FUN_SEL: FunSel = FunSel::Sel1;
-}
-impl PinMosi for Pin<Pb4> {
-    const SPI_ID: Bank = Bank::Spi1;
-    const FUN_SEL: FunSel = FunSel::Sel1;
-}
-impl PinMiso for Pin<Pb3> {
-    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<WordSize>,
-}
-
-#[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<u32, 8>);
-
-impl ClkPrescaler {
-    pub const fn new(value: u8) -> Self {
-        ClkPrescaler(arbitrary_int::UInt::<u32, 8>::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::<Spi>(), 0x1000);
-    } else if #[cfg(feature = "vor4x")] {
-        static_assertions::const_assert_eq!(core::mem::size_of::<Spi>(), 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<Pin: TimPin, Tim: TimMarker>: 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<Tim: TimMarker>(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<Hertz>) {
-        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<Hertz>) {
-        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<StatusSelect>,
-    #[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<u32, 1>);
-
-impl EnableControl {
-    pub fn new_disable() -> Self {
-        EnableControl(arbitrary_int::UInt::<u32, 1>::from_u32(0))
-    }
-
-    pub fn new_enable() -> Self {
-        EnableControl(arbitrary_int::UInt::<u32, 1>::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<u7, InvalidCascadeSourceId> {
-        let port_check = |base: u8, id: u8, len: u8| -> Result<u7, InvalidCascadeSourceId> {
-            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<Self, InvalidCascadeSourceId> {
-        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<Self, InvalidCascadeSourceId> {
-        let id = source.id()?;
-        Ok(Self::new_with_raw_value(id.as_u32()))
-    }
-
-    pub fn as_cascade_source(&self) -> Result<CascadeSource, InvalidCascadeSourceId> {
-        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::<Timer>(), 0x1000);
-    } else if #[cfg(feature = "vor4x")] {
-        static_assertions::const_assert_eq!(core::mem::size_of::<Timer>(), 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<Self, InvalidTimerIndex> {
-        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<MmioTimer<'static>, 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<Hertz> 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<UartErrors>,
-}
-
-/// 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<UartErrors>,
-    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<Target = uart_base::RegisterBlock> {
-    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<UartI: UartPeripheralMarker, Tx: TxPin, Rx: RxPin>(
-        sys_clk: Hertz,
-        uart: UartI,
-        pins: (Tx, Rx),
-        config: Config,
-        irq_cfg: InterruptConfig,
-    ) -> Result<Uart, UartIdMissmatchError> {
-        Self::new(sys_clk, uart, pins, config, Some(irq_cfg))
-    }
-
-    /// Calls [Self::new] with the interrupt configuration to [None].
-    pub fn new_without_interrupt<UartI: UartPeripheralMarker, Tx: TxPin, Rx: RxPin>(
-        sys_clk: Hertz,
-        uart: UartI,
-        pins: (Tx, Rx),
-        config: Config,
-    ) -> Result<Self, UartIdMissmatchError> {
-        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<UartI: UartPeripheralMarker, TxPinI: TxPin, RxPinI: RxPin>(
-        sys_clk: Hertz,
-        _uart: UartI,
-        _pins: (TxPinI, RxPinI),
-        config: Config,
-        opt_irq_cfg: Option<InterruptConfig>,
-    ) -> Result<Self, UartIdMissmatchError> {
-        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<UartErrors> {
-        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<u8> for Uart {
-    fn read(&mut self) -> nb::Result<u8, Self::Error> {
-        self.rx.read()
-    }
-}
-
-impl embedded_hal_nb::serial::Write<u8> 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<UartErrors> {
-        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<u32, Infallible> {
-        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<u8> for Rx {
-    fn read(&mut self) -> nb::Result<u8, Self::Error> {
-        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<usize, Self::Error> {
-        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 <Self as embedded_hal_nb::serial::Read<u8>>::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<u8> 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<usize, Self::Error> {
-        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 <Self as embedded_hal_nb::serial::Write<u8>>::write(self, *byte) {
-                Ok(_) => written += 1,
-                Err(nb::Error::WouldBlock) => return Ok(written),
-            }
-        }
-
-        Ok(written)
-    }
-
-    fn flush(&mut self) -> Result<(), Self::Error> {
-        nb::block!(<Self as embedded_hal_nb::serial::Write<u8>>::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<IrqResultMaxSizeOrTimeout, BufferTooShortError> {
-        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<UartErrors>) {
-        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<Pa9> {
-    const BANK: Bank = Bank::Uart0;
-    const FUN_SEL: FunSel = FunSel::Sel2;
-}
-impl RxPin for Pin<Pa8> {
-    const BANK: Bank = Bank::Uart0;
-    const FUN_SEL: FunSel = FunSel::Sel2;
-}
-
-impl TxPin for Pin<Pa17> {
-    const BANK: Bank = Bank::Uart0;
-    const FUN_SEL: FunSel = FunSel::Sel3;
-}
-impl RxPin for Pin<Pa16> {
-    const BANK: Bank = Bank::Uart0;
-    const FUN_SEL: FunSel = FunSel::Sel3;
-}
-
-impl TxPin for Pin<Pa31> {
-    const BANK: Bank = Bank::Uart0;
-    const FUN_SEL: FunSel = FunSel::Sel3;
-}
-impl RxPin for Pin<Pa30> {
-    const BANK: Bank = Bank::Uart0;
-    const FUN_SEL: FunSel = FunSel::Sel3;
-}
-
-impl TxPin for Pin<Pb9> {
-    const BANK: Bank = Bank::Uart0;
-    const FUN_SEL: FunSel = FunSel::Sel1;
-}
-impl RxPin for Pin<Pb8> {
-    const BANK: Bank = Bank::Uart0;
-    const FUN_SEL: FunSel = FunSel::Sel1;
-}
-
-impl TxPin for Pin<Pb23> {
-    const BANK: Bank = Bank::Uart0;
-    const FUN_SEL: FunSel = FunSel::Sel1;
-}
-impl RxPin for Pin<Pb22> {
-    const BANK: Bank = Bank::Uart0;
-    const FUN_SEL: FunSel = FunSel::Sel1;
-}
-
-// UART B pins
-
-impl TxPin for Pin<Pa3> {
-    const BANK: Bank = Bank::Uart1;
-    const FUN_SEL: FunSel = FunSel::Sel2;
-}
-impl RxPin for Pin<Pa2> {
-    const BANK: Bank = Bank::Uart1;
-    const FUN_SEL: FunSel = FunSel::Sel2;
-}
-
-impl TxPin for Pin<Pa19> {
-    const BANK: Bank = Bank::Uart1;
-    const FUN_SEL: FunSel = FunSel::Sel3;
-}
-impl RxPin for Pin<Pa18> {
-    const BANK: Bank = Bank::Uart1;
-    const FUN_SEL: FunSel = FunSel::Sel3;
-}
-
-impl TxPin for Pin<Pa27> {
-    const BANK: Bank = Bank::Uart1;
-    const FUN_SEL: FunSel = FunSel::Sel3;
-}
-impl RxPin for Pin<Pa26> {
-    const BANK: Bank = Bank::Uart1;
-    const FUN_SEL: FunSel = FunSel::Sel3;
-}
-
-impl TxPin for Pin<Pb7> {
-    const BANK: Bank = Bank::Uart1;
-    const FUN_SEL: FunSel = FunSel::Sel1;
-}
-impl RxPin for Pin<Pb6> {
-    const BANK: Bank = Bank::Uart1;
-    const FUN_SEL: FunSel = FunSel::Sel1;
-}
-
-impl TxPin for Pin<Pb19> {
-    const BANK: Bank = Bank::Uart1;
-    const FUN_SEL: FunSel = FunSel::Sel2;
-}
-impl RxPin for Pin<Pb18> {
-    const BANK: Bank = Bank::Uart1;
-    const FUN_SEL: FunSel = FunSel::Sel2;
-}
-
-impl TxPin for Pin<Pb21> {
-    const BANK: Bank = Bank::Uart1;
-    const FUN_SEL: FunSel = FunSel::Sel1;
-}
-impl RxPin for Pin<Pb20> {
-    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::<Uart>(), 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<Self::Output> {
-        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<UartErrors> {
-    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<UartErrors> {
-    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<const N: usize>(
-    bank: Bank,
-    prod: &mut heapless::spsc::Producer<u8, N>,
-    shared_consumer: &Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
-) -> Result<(), AsyncUartErrors> {
-    on_interrupt_rx_async_heapless_queue_overwriting(bank, prod, shared_consumer)
-}
-
-pub fn on_interrupt_rx_async_heapless_queue_overwriting<const N: usize>(
-    bank: Bank,
-    prod: &mut heapless::spsc::Producer<u8, N>,
-    shared_consumer: &Mutex<RefCell<Option<heapless::spsc::Consumer<'static, 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;
-                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<const N: usize>(
-    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<const N: usize>(
-    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<const N: usize> {
-    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<const N: usize>(Option<RxAsyncInner<N>>);
-
-impl<const N: usize> ErrorType for RxAsync<N> {
-    /// 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<const N: usize> RxAsync<N> {
-    /// 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<const N: usize> Drop for RxAsync<N> {
-    fn drop(&mut self) {
-        self.stop();
-    }
-}
-
-impl<const N: usize> embedded_io_async::Read for RxAsync<N> {
-    async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
-        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<const N: usize> {
-    rx: Rx,
-    pub shared_consumer: &'static Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
-}
-
-/// 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<const N: usize>(Option<RxAsyncOverwritingInner<N>>);
-
-impl<const N: usize> ErrorType for RxAsyncOverwriting<N> {
-    /// Error reporting is done using the result of the interrupt functions.
-    type Error = Infallible;
-}
-
-impl<const N: usize> RxAsyncOverwriting<N> {
-    /// 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<RefCell<Option<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(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<const N: usize> Drop for RxAsyncOverwriting<N> {
-    fn drop(&mut self) {
-        self.stop();
-    }
-}
-
-impl<const N: usize> embedded_io_async::Read for RxAsyncOverwriting<N> {
-    async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
-        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<N>| {
-            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<RefCell<TxContext>>; 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<usize, TxOverrunError>;
-
-    fn poll(
-        self: core::pin::Pin<&mut Self>,
-        cx: &mut core::task::Context<'_>,
-    ) -> core::task::Poll<Self::Output> {
-        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<usize, Self::Error> {
-        let fut = unsafe { TxFuture::new(&mut self.0, buf) };
-        fut.await
-    }
-}