From 048638acf213eba92c1e6757a34dc38aeb5bf698 Mon Sep 17 00:00:00 2001 From: Zhouqi Jiang Date: Mon, 25 Nov 2024 10:20:35 +0800 Subject: [PATCH 1/5] uart: refactor uart module to build async/await serial structure The original serial structures were renamed to `BlockingSerial`, `BlockingTransmitHalf` and `BlockingReceiveHalf`. The new `Serial` structure dedicates to async/await function. Serial constructors now returns error instead of panicking in the HAL crate. Examples are modified to unwrap the return value of the constructor. Ref: https://docs.embassy.dev/embassy-stm32/git/stm32f746ve/usart/struct.Uart.html#method.new The serial `uart` module is rearranged into files for clearance of code. Signed-off-by: Zhouqi Jiang --- .github/workflows/Cargo.yml | 4 +- Cargo.toml | 1 + bouffalo-hal/Cargo.toml | 2 + bouffalo-hal/src/lib.rs | 5 +- bouffalo-hal/src/uart.rs | 1961 +---------------- bouffalo-hal/src/uart/asynch.rs | 209 ++ bouffalo-hal/src/uart/blocking.rs | 242 ++ bouffalo-hal/src/uart/config.rs | 183 ++ bouffalo-hal/src/uart/error.rs | 32 + bouffalo-hal/src/uart/mux.rs | 149 ++ bouffalo-hal/src/uart/pad.rs | 344 +++ bouffalo-hal/src/uart/register.rs | 1017 +++++++++ .../multicore/multicore-demo/dsp/src/main.rs | 3 +- examples/peripherals/i2c-demo/src/main.rs | 3 +- examples/peripherals/lz4d-demo/src/main.rs | 3 +- examples/peripherals/psram-demo/src/main.rs | 5 +- examples/peripherals/sdcard-demo/src/main.rs | 3 +- .../peripherals/sdcard-gpt-demo/src/main.rs | 3 +- .../peripherals/uart-async-demo/Cargo.toml | 18 + .../peripherals/uart-async-demo/README.md | 8 + examples/peripherals/uart-async-demo/build.rs | 3 + .../peripherals/uart-async-demo/src/main.rs | 55 + .../peripherals/uart-cli-demo/src/main.rs | 3 +- examples/peripherals/uart-demo/src/main.rs | 3 +- 24 files changed, 2324 insertions(+), 1935 deletions(-) create mode 100644 bouffalo-hal/src/uart/asynch.rs create mode 100644 bouffalo-hal/src/uart/blocking.rs create mode 100644 bouffalo-hal/src/uart/config.rs create mode 100644 bouffalo-hal/src/uart/error.rs create mode 100644 bouffalo-hal/src/uart/mux.rs create mode 100644 bouffalo-hal/src/uart/pad.rs create mode 100644 bouffalo-hal/src/uart/register.rs create mode 100644 examples/peripherals/uart-async-demo/Cargo.toml create mode 100644 examples/peripherals/uart-async-demo/README.md create mode 100644 examples/peripherals/uart-async-demo/build.rs create mode 100644 examples/peripherals/uart-async-demo/src/main.rs diff --git a/.github/workflows/Cargo.yml b/.github/workflows/Cargo.yml index c9dc727..655ae4c 100644 --- a/.github/workflows/Cargo.yml +++ b/.github/workflows/Cargo.yml @@ -43,7 +43,7 @@ jobs: TARGET: [riscv64imac-unknown-none-elf] TOOLCHAIN: [nightly] EXAMPLES: [gpio-demo, i2c-demo, jtag-demo, lz4d-demo, pwm-demo, - sdcard-demo, sdcard-gpt-demo, spi-demo, uart-demo] + sdcard-demo, sdcard-gpt-demo, spi-demo, uart-demo, uart-async-demo, uart-cli-demo] steps: - uses: actions/checkout@v4 - uses: actions-rust-lang/setup-rust-toolchain@v1 @@ -51,4 +51,4 @@ jobs: target: ${{ MATRIX.TARGET }} toolchain: ${{ MATRIX.TOOLCHAIN }} - name: Run build - run: cargo build --target ${{ MATRIX.TARGET }} --release -p ${{ MATRIX.EXAMPLES }} \ No newline at end of file + run: cargo build --target ${{ MATRIX.TARGET }} --release -p ${{ MATRIX.EXAMPLES }} diff --git a/Cargo.toml b/Cargo.toml index 77a78ca..f4fa9c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ members = [ "examples/peripherals/pwm-demo", "examples/peripherals/spi-demo", "examples/peripherals/uart-demo", + "examples/peripherals/uart-async-demo", "examples/peripherals/uart-cli-demo", "examples/peripherals/sdcard-demo", "examples/peripherals/sdcard-gpt-demo", diff --git a/bouffalo-hal/Cargo.toml b/bouffalo-hal/Cargo.toml index bdfb23f..b898804 100644 --- a/bouffalo-hal/Cargo.toml +++ b/bouffalo-hal/Cargo.toml @@ -20,6 +20,8 @@ as-slice = "0.2.1" nb = "1.1.0" # For backward compatibility only. embedded-hal-027 = { package = "embedded-hal", version = "0.2.7" } +embedded-io-async = "0.6.1" +atomic-waker = "1.1.2" [dev-dependencies] memoffset = "0.9.0" diff --git a/bouffalo-hal/src/lib.rs b/bouffalo-hal/src/lib.rs index d842f06..822d05f 100644 --- a/bouffalo-hal/src/lib.rs +++ b/bouffalo-hal/src/lib.rs @@ -40,7 +40,10 @@ pub mod prelude { }; pub use embedded_hal::i2c::I2c as _embedded_hal_i2c_I2c; pub use embedded_hal::pwm::SetDutyCycle as _embedded_hal_pwm_SetDutyCycle; - pub use embedded_io::{Read, Write}; + pub use embedded_io::{Read as _embedded_io_Read, Write as _embedded_io_Write}; + pub use embedded_io_async::{ + Read as _embedded_io_async_Read, Write as _embedded_io_async_Write, + }; } /// Wrapper type for manipulations of a field in a register. diff --git a/bouffalo-hal/src/uart.rs b/bouffalo-hal/src/uart.rs index 77afefc..681ce31 100644 --- a/bouffalo-hal/src/uart.rs +++ b/bouffalo-hal/src/uart.rs @@ -1,1414 +1,21 @@ //! Universal Asynchronous Receiver/Transmitter. use crate::clocks::Clocks; -use crate::glb::{self, v2::UartSignal}; -use crate::gpio::{MmUart, Pad, Uart}; -use core::marker::PhantomData; use core::ops::Deref; -use embedded_time::rate::{Baud, Extensions}; -use volatile_register::{RO, RW, WO}; -/// Universal Asynchronous Receiver/Transmitter registers. -#[repr(C)] -pub struct RegisterBlock { - /// Transmit configuration. - pub transmit_config: RW, - /// Receive configuration. - pub receive_config: RW, - /// Bit-period in clocks. - pub bit_period: RW, - /// Data format configuration. - pub data_config: RW, - _reserved1: [u8; 0x10], - /// Interrupt state register. - pub interrupt_state: RO, - /// Interrupt mask register. - pub interrupt_mask: RW, - /// Clear interrupt register. - pub interrupt_clear: WO, - /// Interrupt enable register. - pub interrupt_enable: RW, - /// Bus state. - pub bus_state: RO, - _reserved2: [u8; 0x4c], - /// First-in first-out queue configuration 0. - pub fifo_config_0: RW, - /// First-in first-out queue configuration 1. - pub fifo_config_1: RW, - /// Write data into first-in first-out queue. - pub fifo_write: WO, - _reserved3: [u8; 0x3], - /// Read data from first-in first-out queue. - pub fifo_read: RO, -} - -/// Transmit configuration register. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -#[repr(transparent)] -pub struct TransmitConfig(u32); - -// TODO: inherent associated types is unstable, put aliases here as WAR -/// Register fields aliases, defining the bit field shift and bit length -mod transmit_config { - use crate::BitField; - - pub(crate) type Enable = BitField<1, 0, u32>; - pub(crate) type ParityEnable = BitField<1, 4, u32>; - pub(crate) type ParityMode = BitField<1, 5, u32>; - pub(crate) type WordLength = BitField<3, 8, u32>; -} - -impl TransmitConfig { - const CTS: u32 = 1 << 1; - const FREERUN: u32 = 1 << 2; - const LIN_TRANSMIT: u32 = 1 << 3; - const IR_TRANSMIT: u32 = 1 << 6; - const IR_INVERSE: u32 = 1 << 7; - const STOP_BITS: u32 = 0b11 << 11; - const LIN_BREAK_BITS: u32 = 0b111 << 13; - const TRANSFER_LENGTH: u32 = 0xffff << 16; - - /// Enable transmit. - #[inline] - pub const fn enable_txd(self) -> Self { - Self(transmit_config::Enable::from(self.0).enable()) - } - /// Disable transmit. - #[inline] - pub const fn disable_txd(self) -> Self { - Self(transmit_config::Enable::from(self.0).disable()) - } - /// Check if transmit is enabled. - #[inline] - pub const fn is_txd_enabled(self) -> bool { - transmit_config::Enable::from(self.0).is_enabled() - } - /// Enable Clear-to-Send signal. - #[inline] - pub const fn enable_cts(self) -> Self { - Self(self.0 | Self::CTS) - } - /// Disable Clear-to-Send signal. - #[inline] - pub const fn disable_cts(self) -> Self { - Self(self.0 & !Self::CTS) - } - /// Check if Clear-to-Send signal is enabled. - #[inline] - pub const fn is_cts_enabled(self) -> bool { - self.0 & Self::CTS != 0 - } - /// Enable free-run mode. - #[inline] - pub const fn enable_freerun(self) -> Self { - Self(self.0 | Self::FREERUN) - } - /// Disable free-run mode. - #[inline] - pub const fn disable_freerun(self) -> Self { - Self(self.0 & !Self::FREERUN) - } - /// Check if free-run mode is enabled. - #[inline] - pub const fn is_freerun_enabled(self) -> bool { - self.0 & Self::FREERUN != 0 - } - /// Enable LIN protocol transmission. - #[inline] - pub const fn enable_lin_transmit(self) -> Self { - Self(self.0 | Self::LIN_TRANSMIT) - } - /// Disable LIN protocol transmission. - #[inline] - pub const fn disable_lin_transmit(self) -> Self { - Self(self.0 & !Self::LIN_TRANSMIT) - } - /// Check if LIN protocol transmission is enabled. - #[inline] - pub const fn is_lin_transmit_enabled(self) -> bool { - self.0 & Self::LIN_TRANSMIT != 0 - } - /// Set parity check mode. - #[inline] - pub const fn set_parity(self, parity: Parity) -> Self { - let field_en = transmit_config::ParityEnable::from(self.0); - - match parity { - Parity::Even => { - let field_odd = transmit_config::ParityMode::from(field_en.enable()); - Self(field_odd.disable()) - } - Parity::Odd => { - let field_odd = transmit_config::ParityMode::from(field_en.enable()); - Self(field_odd.enable()) - } - Parity::None => Self(field_en.disable()), - } - } - /// Get parity check mode. - #[inline] - pub const fn parity(self) -> Parity { - let field_en = transmit_config::ParityEnable::from(self.0); - let field_odd = transmit_config::ParityMode::from(self.0); - - if !field_en.is_enabled() { - Parity::None - } else if !field_odd.is_enabled() { - Parity::Even - } else { - Parity::Odd - } - } - /// Enable IR transmission. - #[inline] - pub const fn enable_ir_transmit(self) -> Self { - Self(self.0 | Self::IR_TRANSMIT) - } - /// Disable IR transmission. - #[inline] - pub const fn disable_ir_transmit(self) -> Self { - Self(self.0 & !Self::IR_TRANSMIT) - } - /// Check if IR transmission is enabled. - #[inline] - pub const fn is_ir_transmit_enabled(self) -> bool { - self.0 & Self::IR_TRANSMIT != 0 - } - /// Invert transmit signal output in IR mode. - #[inline] - pub const fn enable_ir_inverse(self) -> Self { - Self(self.0 | Self::IR_INVERSE) - } - /// Don't invert transmit signal output in IR mode. - #[inline] - pub const fn disable_ir_inverse(self) -> Self { - Self(self.0 & !Self::IR_INVERSE) - } - /// Check if transmit signal output in IR mode is inverted. - #[inline] - pub const fn is_ir_inverse_enabled(self) -> bool { - self.0 & Self::IR_INVERSE != 0 - } - /// Set word length. - #[inline] - pub const fn set_word_length(self, val: WordLength) -> Self { - let field = transmit_config::WordLength::from(self.0); - let val = match val { - WordLength::Five => 4, - WordLength::Six => 5, - WordLength::Seven => 6, - WordLength::Eight => 7, - }; - Self(field.set(val)) - } - /// Get word length. - #[inline] - pub const fn word_length(self) -> WordLength { - let field = transmit_config::WordLength::from(self.0); - match field.get() { - 4 => WordLength::Five, - 5 => WordLength::Six, - 6 => WordLength::Seven, - 7 => WordLength::Eight, - _ => unreachable!(), - } - } - /// Set stop-bit configuration. - #[inline] - pub const fn set_stop_bits(self, val: StopBits) -> Self { - let val = match val { - StopBits::ZeroPointFive => 0, - StopBits::One => 1, - StopBits::OnePointFive => 2, - StopBits::Two => 3, - }; - Self(self.0 & !Self::STOP_BITS | val << 11) - } - /// Get stop-bit configuration. - #[inline] - pub const fn stop_bits(self) -> StopBits { - let val = (self.0 & Self::STOP_BITS) >> 11; - match val { - 0 => StopBits::ZeroPointFive, - 1 => StopBits::One, - 2 => StopBits::OnePointFive, - 3 => StopBits::Two, - _ => unreachable!(), - } - } - /// Set synchronize interval under LIN mode. - /// - /// # Parameters - /// - /// * `bits` - Interval in bits, the value should be 0 ~ 7. - #[inline] - pub const fn set_lin_break_bits(self, bits: u8) -> Self { - Self(self.0 & !Self::LIN_BREAK_BITS | (bits as u32) << 13) - } - /// Get synchronize interval under LIN mode. - /// - /// Return value is 0 ~ 7, represent in bits. - #[inline] - pub const fn lin_break_bits(self) -> u8 { - ((self.0 & Self::LIN_BREAK_BITS) >> 13) as u8 - } - /// Trigger interrupt when specified length of data is sent. - /// - /// NOTE: This bit is not valid when it is running under free-run mode. - #[inline] - pub const fn set_transfer_length(self, length: u16) -> Self { - Self(self.0 & !Self::TRANSFER_LENGTH | (length as u32) << 16) - } - /// Get the length of data that triggers the interrupt. - #[inline] - pub const fn transfer_length(self) -> u16 { - ((self.0 & Self::TRANSFER_LENGTH) >> 16) as u16 - } -} - -impl Default for TransmitConfig { - #[inline] - fn default() -> Self { - Self(0x0000_8f00) - } -} - -/// Receive configuration register. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -#[repr(transparent)] -pub struct ReceiveConfig(u32); - -mod receive_config { - use crate::BitField; - - pub(crate) type Enable = BitField<1, 0, u32>; - pub(crate) type ParityEnable = BitField<1, 4, u32>; - pub(crate) type ParityMode = BitField<1, 5, u32>; - pub(crate) type WordLength = BitField<3, 8, u32>; -} - -impl ReceiveConfig { - const ABR: u32 = 1 << 1; - const LIN_RECEIVE: u32 = 1 << 3; - const IR_RECEIVE: u32 = 1 << 6; - const IR_INVERSE: u32 = 1 << 7; - const DEGLICH: u32 = 1 << 11; - const DEGLICH_CYCLE: u32 = 0xf << 12; - const TRANSFER_LENGTH: u32 = 0xffff << 16; - - /// Enable receive. - #[inline] - pub const fn enable_rxd(self) -> Self { - Self(receive_config::Enable::from(self.0).enable()) - } - /// Disable receive. - #[inline] - pub const fn disable_rxd(self) -> Self { - Self(receive_config::Enable::from(self.0).disable()) - } - /// Check if receive is enabled. - #[inline] - pub const fn is_rxd_enabled(self) -> bool { - receive_config::Enable::from(self.0).is_enabled() - } - /// Enable auto baud rate detection. - #[inline] - pub const fn enable_auto_baudrate(self) -> Self { - Self(self.0 | Self::ABR) - } - /// Disable auto baud rate detection. - #[inline] - pub const fn disable_auto_baudrate(self) -> Self { - Self(self.0 & !Self::ABR) - } - /// Check if auto baud rate detection is enabled. - #[inline] - pub const fn is_auto_baudrate_enabled(self) -> bool { - self.0 & Self::ABR != 0 - } - /// Enable LIN protocol receive. - #[inline] - pub const fn enable_lin_receive(self) -> Self { - Self(self.0 | Self::LIN_RECEIVE) - } - /// Disable LIN protocol receive. - #[inline] - pub const fn disable_lin_receive(self) -> Self { - Self(self.0 & !Self::LIN_RECEIVE) - } - /// Check if LIN protocol receive is enabled. - #[inline] - pub const fn is_lin_receive_enabled(self) -> bool { - self.0 & Self::LIN_RECEIVE != 0 - } - /// Set parity check mode. - #[inline] - pub const fn set_parity(self, parity: Parity) -> Self { - let field_en = receive_config::ParityEnable::from(self.0); - - match parity { - Parity::Even => { - let field_odd = receive_config::ParityMode::from(field_en.enable()); - Self(field_odd.disable()) - } - Parity::Odd => { - let field_odd = receive_config::ParityMode::from(field_en.enable()); - Self(field_odd.enable()) - } - Parity::None => Self(field_en.disable()), - } - } - /// Get parity check mode. - #[inline] - pub const fn parity(self) -> Parity { - let field_en = receive_config::ParityEnable::from(self.0); - let field_odd = receive_config::ParityMode::from(self.0); - - if !field_en.is_enabled() { - Parity::None - } else if !field_odd.is_enabled() { - Parity::Even - } else { - Parity::Odd - } - } - /// Enable IR receive. - #[inline] - pub const fn enable_ir_receive(self) -> Self { - Self(self.0 | Self::IR_RECEIVE) - } - /// Disable IR receive. - #[inline] - pub const fn disable_ir_receive(self) -> Self { - Self(self.0 & !Self::IR_RECEIVE) - } - /// Check if IR receive is enabled. - #[inline] - pub const fn is_ir_receive_enabled(self) -> bool { - self.0 & Self::IR_RECEIVE != 0 - } - /// Invert receive signal output in IR mode. - #[inline] - pub const fn enable_ir_inverse(self) -> Self { - Self(self.0 | Self::IR_INVERSE) - } - /// Don't invert receive signal output in IR mode. - #[inline] - pub const fn disable_ir_inverse(self) -> Self { - Self(self.0 & !Self::IR_INVERSE) - } - /// Check if receive signal output in IR mode is inverted. - #[inline] - pub const fn is_ir_inverse_enabled(self) -> bool { - self.0 & Self::IR_INVERSE != 0 - } - /// Set word length. - #[inline] - pub const fn set_word_length(self, val: WordLength) -> Self { - let field = receive_config::WordLength::from(self.0); - let val = match val { - WordLength::Five => 4, - WordLength::Six => 5, - WordLength::Seven => 6, - WordLength::Eight => 7, - }; - Self(field.set(val)) - } - /// Get word length. - #[inline] - pub const fn word_length(self) -> WordLength { - let field = receive_config::WordLength::from(self.0); - match field.get() { - 4 => WordLength::Five, - 5 => WordLength::Six, - 6 => WordLength::Seven, - 7 => WordLength::Eight, - _ => unreachable!(), - } - } - /// Enable de-glitch function. - #[inline] - pub const fn enable_deglitch(self) -> Self { - Self(self.0 | Self::DEGLICH) - } - /// Disable de-glitch function. - #[inline] - pub const fn disable_deglitch(self) -> Self { - Self(self.0 & !Self::DEGLICH) - } - /// Check if de-glitch function is enabled. - #[inline] - pub const fn is_deglitch_enabled(self) -> bool { - self.0 & Self::DEGLICH != 0 - } - /// Set de-glich function cycle count. - #[inline] - pub const fn set_deglitch_cycles(self, val: u8) -> Self { - Self(self.0 & !Self::DEGLICH_CYCLE | ((val as u32) << 12)) - } - /// Get de-glich function cycle count. - #[inline] - pub const fn deglitch_cycles(self) -> u8 { - ((self.0 & Self::DEGLICH_CYCLE) >> 12) as u8 - } - /// Set the length of data that triggers the interrupt. - #[inline] - pub const fn set_transfer_length(self, length: u16) -> Self { - Self(self.0 & !Self::TRANSFER_LENGTH | (length as u32) << 16) - } - /// Get the length of data that triggers the interrupt. - #[inline] - pub const fn transfer_length(self) -> u16 { - ((self.0 & Self::TRANSFER_LENGTH) >> 16) as u16 - } -} - -impl Default for ReceiveConfig { - #[inline] - fn default() -> Self { - Self(0x0000_0700) - } -} - -/// Bit period configuration register. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -#[repr(transparent)] -pub struct BitPeriod(u32); - -impl BitPeriod { - const TRANSMIT: u32 = 0xffff; - const RECEIVE: u32 = 0xffff << 16; - - /// Set transmit time interval. - #[inline] - pub const fn set_transmit_time_interval(self, val: u16) -> Self { - Self(self.0 & !Self::TRANSMIT | val as u32) - } - /// Get transmit time interval. - #[inline] - pub const fn transmit_time_interval(self) -> u16 { - (self.0 & Self::TRANSMIT) as u16 - } - /// Set receive time interval. - #[inline] - pub const fn set_receive_time_interval(self, val: u16) -> Self { - Self(self.0 & !Self::RECEIVE | ((val as u32) << 16)) - } - /// Get receive time interval. - #[inline] - pub const fn receive_time_interval(self) -> u16 { - ((self.0 & Self::RECEIVE) >> 16) as u16 - } -} - -impl Default for BitPeriod { - #[inline] - fn default() -> Self { - Self(0x00ff_00ff) - } -} - -/// Data configuration register. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -#[repr(transparent)] -pub struct DataConfig(u32); - -impl DataConfig { - const BIT_ORDER: u32 = 1 << 0; - - /// Set the bit order in each data word. - #[inline] - pub const fn set_bit_order(self, val: BitOrder) -> Self { - match val { - BitOrder::LsbFirst => Self(self.0 & !Self::BIT_ORDER), - BitOrder::MsbFirst => Self(self.0 | Self::BIT_ORDER), - } - } - /// Get the bit order in each data word. - #[inline] - pub const fn bit_order(self) -> BitOrder { - if self.0 & Self::BIT_ORDER == 0 { - BitOrder::LsbFirst - } else { - BitOrder::MsbFirst - } - } -} - -impl Default for DataConfig { - #[inline] - fn default() -> Self { - Self(0x0000_0000) - } -} - -/// Interrupt event. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -#[repr(u8)] -pub enum Interrupt { - TransmitEnd = 0, - ReceiveEnd = 1, - TransmitFifoReady = 2, - ReceiveFifoReady = 3, - ReceiveTimeout = 4, - ReceiveParityError = 5, - TransmitFifoError = 6, - ReceiveFifoError = 7, - ReceiveSyncError = 8, - ReceiveByteCountReached = 9, - ReceiveAutoBaudrateByStartBit = 10, - ReceiveAutoBaudrateByFiveFive = 11, -} - -/// Interrupt state register. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] -#[repr(transparent)] -pub struct InterruptState(u32); - -impl InterruptState { - /// Check if there is an interrupt flag. - #[inline] - pub const fn has_interrupt(self, val: Interrupt) -> bool { - (self.0 & (1 << (val as u32))) != 0 - } -} - -/// Interrupt mask register. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] -#[repr(transparent)] -pub struct InterruptMask(u32); - -impl InterruptMask { - /// Set interrupt mask. - #[inline] - pub const fn mask_interrupt(self, val: Interrupt) -> Self { - Self(self.0 | (1 << (val as u32))) - } - /// Clear interrupt mask. - #[inline] - pub const fn unmask_interrupt(self, val: Interrupt) -> Self { - Self(self.0 & !(1 << (val as u32))) - } - /// Check if interrupt is masked. - #[inline] - pub const fn is_interrupt_masked(self, val: Interrupt) -> bool { - (self.0 & (1 << (val as u32))) != 0 - } -} - -/// Interrupt clear register. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] -#[repr(transparent)] -pub struct InterruptClear(u32); - -impl InterruptClear { - /// Clear interrupt. - #[inline] - pub const fn clear_interrupt(self, val: Interrupt) -> Self { - Self(self.0 | (1 << (val as u32))) - } -} - -/// Interrupt enable register. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] -#[repr(transparent)] -pub struct InterruptEnable(u32); - -impl InterruptEnable { - /// Enable interrupt. - #[inline] - pub const fn enable_interrupt(self, val: Interrupt) -> Self { - Self(self.0 | (1 << (val as u32))) - } - /// Disable interrupt. - #[inline] - pub const fn disable_interrupt(self, val: Interrupt) -> Self { - Self(self.0 & !(1 << (val as u32))) - } - /// Check if interrupt is enabled. - #[inline] - pub const fn is_interrupt_enabled(self, val: Interrupt) -> bool { - (self.0 & (1 << (val as u32))) != 0 - } -} - -/// Bus state register. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] -#[repr(transparent)] -pub struct BusState(u32); - -impl BusState { - const TRANSMIT_BUSY: u32 = 1 << 0; - const RECEIVE_BUSY: u32 = 1 << 1; - - /// Get if UART transmit bus is busy. - #[inline] - pub const fn transmit_busy(self) -> bool { - self.0 & Self::TRANSMIT_BUSY != 0 - } - /// Get if UART receive bus is busy. - #[inline] - pub const fn receive_busy(self) -> bool { - self.0 & Self::RECEIVE_BUSY != 0 - } -} - -/// First-in first-out queue configuration 0. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] -#[repr(transparent)] -pub struct FifoConfig0(u32); - -impl FifoConfig0 { - const TRANSMIT_DMA_ENABLE: u32 = 1 << 0; - const RECEIVE_DMA_ENABLE: u32 = 1 << 1; - const TRANSMIT_FIFO_CLEAR: u32 = 1 << 2; - const RECEIVE_FIFO_CLEAR: u32 = 1 << 3; - const TRANSMIT_FIFO_OVERFLOW: u32 = 1 << 4; - const TRANSMIT_FIFO_UNDERFLOW: u32 = 1 << 5; - const RECEIVE_FIFO_OVERFLOW: u32 = 1 << 6; - const RECEIVE_FIFO_UNDERFLOW: u32 = 1 << 7; - - /// Enable transmit DMA. - #[inline] - pub const fn enable_transmit_dma(self) -> Self { - Self(self.0 | Self::TRANSMIT_DMA_ENABLE) - } - /// Disable transmit DMA. - #[inline] - pub const fn disable_transmit_dma(self) -> Self { - Self(self.0 & !Self::TRANSMIT_DMA_ENABLE) - } - /// Check if transmit DMA is enabled. - #[inline] - pub const fn is_transmit_dma_enabled(self) -> bool { - self.0 & Self::TRANSMIT_DMA_ENABLE != 0 - } - /// Enable receive DMA. - #[inline] - pub const fn enable_receive_dma(self) -> Self { - Self(self.0 | Self::RECEIVE_DMA_ENABLE) - } - /// Disable receive DMA. - #[inline] - pub const fn disable_receive_dma(self) -> Self { - Self(self.0 & !Self::RECEIVE_DMA_ENABLE) - } - /// Check if receive DMA is enabled. - #[inline] - pub const fn is_receive_dma_enabled(self) -> bool { - self.0 & Self::RECEIVE_DMA_ENABLE != 0 - } - /// Clear transmit FIFO. - #[inline] - pub const fn clear_transmit_fifo(self) -> Self { - Self(self.0 | Self::TRANSMIT_FIFO_CLEAR) - } - /// Clear receive FIFO. - #[inline] - pub const fn clear_receive_fifo(self) -> Self { - Self(self.0 | Self::RECEIVE_FIFO_CLEAR) - } - /// Check if transmit FIFO is overflow. - #[inline] - pub const fn transmit_fifo_overflow(self) -> bool { - self.0 & Self::TRANSMIT_FIFO_OVERFLOW != 0 - } - /// Check if transmit FIFO is underflow. - #[inline] - pub const fn transmit_fifo_underflow(self) -> bool { - self.0 & Self::TRANSMIT_FIFO_UNDERFLOW != 0 - } - /// Check if receive FIFO is overflow. - #[inline] - pub const fn receive_fifo_overflow(self) -> bool { - self.0 & Self::RECEIVE_FIFO_OVERFLOW != 0 - } - /// Check if receive FIFO is underflow. - #[inline] - pub const fn receive_fifo_underflow(self) -> bool { - self.0 & Self::RECEIVE_FIFO_UNDERFLOW != 0 - } -} - -/// First-in first-out queue configuration 1. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] -#[repr(transparent)] -pub struct FifoConfig1(u32); - -impl FifoConfig1 { - const TRANSMIT_COUNT: u32 = 0x3f; - const RECEIVE_COUNT: u32 = 0x3f << 8; - const TRANSMIT_THRESHOLD: u32 = 0x1f << 16; - const RECEIVE_THRESHOLD: u32 = 0x1f << 24; - - /// Get number of empty spaces remained in transmit FIFO queue. - #[inline] - pub const fn transmit_available_bytes(self) -> u8 { - (self.0 & Self::TRANSMIT_COUNT) as u8 - } - /// Get number of available bytes received in receive FIFO queue. - #[inline] - pub const fn receive_available_bytes(self) -> u8 { - ((self.0 & Self::RECEIVE_COUNT) >> 8) as u8 - } - /// Set transmit FIFO threshold. - #[inline] - pub const fn set_transmit_threshold(self, val: u8) -> Self { - Self(self.0 & !Self::TRANSMIT_THRESHOLD | ((val as u32) << 16)) - } - /// Get transmit FIFO threshold. - #[inline] - pub const fn transmit_threshold(self) -> u8 { - ((self.0 & Self::TRANSMIT_THRESHOLD) >> 16) as u8 - } - /// Set receive FIFO threshold. - #[inline] - pub const fn set_receive_threshold(self, val: u8) -> Self { - Self(self.0 & !Self::RECEIVE_THRESHOLD | ((val as u32) << 24)) - } - /// Get receive FIFO threshold. - #[inline] - pub const fn receive_threshold(self) -> u8 { - ((self.0 & Self::RECEIVE_THRESHOLD) >> 24) as u8 - } -} - -/// Multiplex to Request-to-Send (type state). -pub struct MuxRts; - -/// Multiplex to Clear-to-Send (type state). -pub struct MuxCts; - -/// Multiplex to Transmit (type state). -pub struct MuxTxd; - -/// Multiplex to Receive (type state). -pub struct MuxRxd; - -impl MuxRts { - #[inline] - fn signal() -> UartSignal { - match I { - 0 => UartSignal::Rts0, - 1 => UartSignal::Rts1, - 2 => UartSignal::Rts2, - _ => unreachable!(), - } - } -} - -impl MuxCts { - #[inline] - fn signal() -> UartSignal { - match I { - 0 => UartSignal::Cts0, - 1 => UartSignal::Cts1, - 2 => UartSignal::Cts2, - _ => unreachable!(), - } - } -} - -impl MuxTxd { - #[inline] - fn signal() -> UartSignal { - match I { - 0 => UartSignal::Txd0, - 1 => UartSignal::Txd1, - 2 => UartSignal::Txd2, - _ => unreachable!(), - } - } -} - -impl MuxRxd { - #[inline] - fn signal() -> UartSignal { - match I { - 0 => UartSignal::Rxd0, - 1 => UartSignal::Rxd1, - 2 => UartSignal::Rxd2, - _ => unreachable!(), - } - } -} - -/// Global peripheral UART signal multiplexer. -/// -/// This structure only owns the GLB signal multiplexer for signal number `N`. -pub struct UartMux { - base: GLB, - _mode: PhantomData, -} - -impl, const N: usize, M> UartMux { - /// Configure the internal UART signal to Request-to-Send (RTS). - #[inline] - pub fn into_request_to_send(self) -> UartMux> { - let config = self.base.uart_mux_group[N >> 3] - .read() - .set_signal(N & 0x7, MuxRts::::signal()); - unsafe { self.base.uart_mux_group[N >> 3].write(config) }; - UartMux { - base: self.base, - _mode: PhantomData, - } - } - /// Configure the internal UART signal to Transmit (TXD). - #[inline] - pub fn into_transmit(self) -> UartMux> { - let config = self.base.uart_mux_group[N >> 3] - .read() - .set_signal(N & 0x7, MuxTxd::::signal()); - unsafe { self.base.uart_mux_group[N >> 3].write(config) }; - UartMux { - base: self.base, - _mode: PhantomData, - } - } - /// Configure the internal UART signal to Receive (RXD). - #[inline] - pub fn into_receive(self) -> UartMux> { - let config = self.base.uart_mux_group[N >> 3] - .read() - .set_signal(N & 0x7, MuxRxd::::signal()); - unsafe { self.base.uart_mux_group[N >> 3].write(config) }; - UartMux { - base: self.base, - _mode: PhantomData, - } - } - /// Configure the internal UART signal to Clear-to-Send (CTS). - #[inline] - pub fn into_clear_to_send(self) -> UartMux> { - let config = self.base.uart_mux_group[N >> 3] - .read() - .set_signal(N & 0x7, MuxCts::::signal()); - unsafe { self.base.uart_mux_group[N >> 3].write(config) }; - UartMux { - base: self.base, - _mode: PhantomData, - } - } -} - -/// Available UART signal multiplexers. -pub struct UartMuxes { - /// Multiplexer of UART signal 0. - pub sig0: UartMux>, - /// Multiplexer of UART signal 1. - pub sig1: UartMux>, - /// Multiplexer of UART signal 2. - pub sig2: UartMux>, - /// Multiplexer of UART signal 3. - pub sig3: UartMux>, - /// Multiplexer of UART signal 4. - pub sig4: UartMux>, - /// Multiplexer of UART signal 5. - pub sig5: UartMux>, - /// Multiplexer of UART signal 6. - pub sig6: UartMux>, - /// Multiplexer of UART signal 7. - pub sig7: UartMux>, - /// Multiplexer of UART signal 8. - pub sig8: UartMux>, - /// Multiplexer of UART signal 9. - pub sig9: UartMux>, - /// Multiplexer of UART signal 10. - pub sig10: UartMux>, - /// Multiplexer of UART signal 11. - pub sig11: UartMux>, -} - -/// Check if target gpio `Pin` is internally connected to UART signal index `I`. -pub trait HasUartSignal {} - -impl HasUartSignal<0> for Pad {} -impl HasUartSignal<1> for Pad {} -impl HasUartSignal<2> for Pad {} -impl HasUartSignal<3> for Pad {} -impl HasUartSignal<4> for Pad {} -impl HasUartSignal<5> for Pad {} -impl HasUartSignal<6> for Pad {} -impl HasUartSignal<7> for Pad {} -impl HasUartSignal<8> for Pad {} -impl HasUartSignal<9> for Pad {} -impl HasUartSignal<10> for Pad {} -impl HasUartSignal<11> for Pad {} -impl HasUartSignal<0> for Pad {} -impl HasUartSignal<1> for Pad {} -impl HasUartSignal<2> for Pad {} -impl HasUartSignal<3> for Pad {} -impl HasUartSignal<4> for Pad {} -impl HasUartSignal<5> for Pad {} -impl HasUartSignal<6> for Pad {} -impl HasUartSignal<7> for Pad {} -impl HasUartSignal<8> for Pad {} -impl HasUartSignal<9> for Pad {} -impl HasUartSignal<10> for Pad {} -impl HasUartSignal<11> for Pad {} -impl HasUartSignal<0> for Pad {} -impl HasUartSignal<1> for Pad {} -impl HasUartSignal<2> for Pad {} -impl HasUartSignal<3> for Pad {} -impl HasUartSignal<4> for Pad {} -impl HasUartSignal<5> for Pad {} -impl HasUartSignal<6> for Pad {} -impl HasUartSignal<7> for Pad {} -impl HasUartSignal<8> for Pad {} -impl HasUartSignal<9> for Pad {} -impl HasUartSignal<10> for Pad {} -impl HasUartSignal<11> for Pad {} -impl HasUartSignal<0> for Pad {} -impl HasUartSignal<1> for Pad {} -impl HasUartSignal<2> for Pad {} -impl HasUartSignal<3> for Pad {} -impl HasUartSignal<4> for Pad {} -impl HasUartSignal<5> for Pad {} -impl HasUartSignal<6> for Pad {} -impl HasUartSignal<7> for Pad {} -impl HasUartSignal<8> for Pad {} -impl HasUartSignal<9> for Pad {} - -/// Check if an internal multi-media UART signal is connected to target gpio `Pin`. -pub trait HasMmUartSignal {} - -impl HasMmUartSignal for Pad {} - -/// Valid UART pads. -#[diagnostic::on_unimplemented( - message = "the I/O pad and signal multiplexer group {Self} is not connected to any UART peripherals on hardware" -)] -pub trait Pads { - /// Checks if this pin configuration includes Request-to-Send feature. - const RTS: bool; - /// Checks if this pin configuration includes Clear-to-Send feature. - const CTS: bool; - /// Checks if this pin configuration includes Transmit feature. - const TXD: bool; - /// Checks if this pin configuration includes Receive feature. - const RXD: bool; - /// Valid split configuration type for current pads and multiplexers. - type Split; - - fn split(self, uart: T) -> Self::Split; -} - -#[inline] -fn from_pads(uart: T, tx: TX, rx: RX) -> (TransmitHalf, ReceiveHalf) { - ( - TransmitHalf { - uart: unsafe { core::ptr::read_volatile(&uart) }, - _pads: tx, - }, - ReceiveHalf { uart, _pads: rx }, - ) -} - -impl Pads - for (Pad, UartMux>) -where - A1: Deref, - Pad: HasUartSignal, -{ - const RTS: bool = false; - const CTS: bool = false; - const TXD: bool = true; - const RXD: bool = false; - type Split = ( - TransmitHalf, UartMux>)>, - ReceiveHalf, - ); - #[inline] - fn split(self, uart: T) -> Self::Split { - from_pads(uart, self, ()) - } -} - -impl< - A1, - GLB2, - A3, - GLB4, - const I1: usize, - const I2: usize, - const U: usize, - const N1: usize, - const N2: usize, - > Pads - for ( - (Pad, UartMux>), - (Pad, UartMux>), - ) -where - A1: Deref, - A3: Deref, - Pad: HasUartSignal, - Pad: HasUartSignal, -{ - const RTS: bool = false; - const CTS: bool = false; - const TXD: bool = true; - const RXD: bool = true; - type Split = ( - TransmitHalf, UartMux>)>, - ReceiveHalf, UartMux>)>, - ); - #[inline] - fn split(self, uart: T) -> Self::Split { - from_pads(uart, self.0, self.1) - } -} - -impl< - A1, - GLB2, - A3, - GLB4, - const I1: usize, - const I2: usize, - const U: usize, - const N1: usize, - const N2: usize, - > Pads - for ( - (Pad, UartMux>), - (Pad, UartMux>), - ) -where - A1: Deref, - A3: Deref, - Pad: HasUartSignal, - Pad: HasUartSignal, -{ - const RTS: bool = false; - const CTS: bool = true; - const TXD: bool = true; - const RXD: bool = false; - type Split = TransmitHalf< - T, - ( - (Pad, UartMux>), - (Pad, UartMux>), - ), - >; - #[inline] - fn split(self, uart: T) -> Self::Split { - TransmitHalf { uart, _pads: self } - } -} - -impl< - A1, - GLB2, - A3, - GLB4, - A5, - GLB6, - A7, - GLB8, - const I1: usize, - const I2: usize, - const I3: usize, - const I4: usize, - const U: usize, - const N1: usize, - const N2: usize, - const N3: usize, - const N4: usize, - > Pads - for ( - (Pad, UartMux>), - (Pad, UartMux>), - (Pad, UartMux>), - (Pad, UartMux>), - ) -where - A1: Deref, - A3: Deref, - A5: Deref, - A7: Deref, - Pad: HasUartSignal, - Pad: HasUartSignal, - Pad: HasUartSignal, - Pad: HasUartSignal, -{ - const RTS: bool = false; - const CTS: bool = true; - const TXD: bool = true; - const RXD: bool = false; - type Split = ( - TransmitHalf< - T, - ( - (Pad, UartMux>), - (Pad, UartMux>), - ), - >, - ReceiveHalf< - T, - ( - (Pad, UartMux>), - (Pad, UartMux>), - ), - >, - ); - #[inline] - fn split(self, uart: T) -> Self::Split { - from_pads(uart, (self.0, self.3), (self.1, self.2)) - } -} - -// TODO: support split for MmUart pads. - -impl Pads for Pad -where - A1: Deref, - Pad: HasMmUartSignal, -{ - const RTS: bool = { N % 4 == 2 }; - const CTS: bool = { N % 4 == 3 }; - const TXD: bool = { N % 4 == 0 }; - const RXD: bool = { N % 4 == 1 }; - type Split = (); - #[inline] - fn split(self, uart: T) -> Self::Split { - let _ = uart; - () - } -} - -impl Pads - for (Pad, Pad) -where - A1: Deref, - A2: Deref, - Pad: HasMmUartSignal, - Pad: HasMmUartSignal, -{ - const RTS: bool = { N1 % 4 == 2 || N2 % 4 == 2 }; - const CTS: bool = { N1 % 4 == 3 || N2 % 4 == 3 }; - const TXD: bool = { N1 % 4 == 0 || N2 % 4 == 0 }; - const RXD: bool = { N1 % 4 == 1 || N2 % 4 == 1 }; - type Split = (); - #[inline] - fn split(self, uart: T) -> Self::Split { - let _ = uart; - () - } -} - -impl Pads - for ( - Pad, - Pad, - Pad, - ) -where - A1: Deref, - A2: Deref, - A3: Deref, - Pad: HasMmUartSignal, - Pad: HasMmUartSignal, - Pad: HasMmUartSignal, -{ - const RTS: bool = { N1 % 4 == 2 || N2 % 4 == 2 || N3 % 4 == 2 }; - const CTS: bool = { N1 % 4 == 3 || N2 % 4 == 3 || N3 % 4 == 3 }; - const TXD: bool = { N1 % 4 == 0 || N2 % 4 == 0 || N3 % 4 == 0 }; - const RXD: bool = { N1 % 4 == 1 || N2 % 4 == 1 || N3 % 4 == 1 }; - type Split = (); - #[inline] - fn split(self, uart: T) -> Self::Split { - let _ = uart; - () - } -} - -impl< - A1, - A2, - A3, - A4, - const U: usize, - const N1: usize, - const N2: usize, - const N3: usize, - const N4: usize, - > Pads - for ( - Pad, - Pad, - Pad, - Pad, - ) -where - A1: Deref, - A2: Deref, - A3: Deref, - A4: Deref, - Pad: HasMmUartSignal, - Pad: HasMmUartSignal, - Pad: HasMmUartSignal, - Pad: HasMmUartSignal, -{ - const RTS: bool = { N1 % 4 == 2 || N2 % 4 == 2 || N3 % 4 == 2 || N4 % 4 == 2 }; - const CTS: bool = { N1 % 4 == 3 || N2 % 4 == 3 || N3 % 4 == 3 || N4 % 4 == 3 }; - const TXD: bool = { N1 % 4 == 0 || N2 % 4 == 0 || N3 % 4 == 0 || N4 % 4 == 0 }; - const RXD: bool = { N1 % 4 == 1 || N2 % 4 == 1 || N3 % 4 == 1 || N4 % 4 == 1 }; - type Split = (); - #[inline] - fn split(self, uart: T) -> Self::Split { - let _ = uart; - () - } -} - -/// Managed serial peripheral. -pub struct Serial { - uart: UART, - pads: PADS, -} - -impl, PADS> Serial { - /// Creates a polling serial instance, without interrupt or DMA configurations. - #[inline] - pub fn freerun(uart: UART, config: Config, pads: PADS, clocks: &Clocks) -> Self - where - PADS: Pads, - { - // Calculate transmit interval. - let uart_clock = clocks.uart_clock::().expect("a valid UART clock source"); - let transmit_interval = uart_clock.0 / config.transmit_baudrate.0; - let receive_interval = uart_clock.0 / config.receive_baudrate.0; - if !(1..=65535).contains(&transmit_interval) { - panic!("Impossible transmit baudrate!"); - } - if !(1..=65535).contains(&receive_interval) { - panic!("Impossible receive baudrate!"); - } - let val = BitPeriod::default() - .set_transmit_time_interval(transmit_interval as u16) - .set_receive_time_interval(receive_interval as u16); - unsafe { uart.bit_period.write(val) }; - - let (data_config, transmit_config, receive_config) = config.into_registers(); - - // Write the bit-order. - unsafe { uart.data_config.write(data_config) }; - - // Configure freerun transmit feature. - let mut val = transmit_config; - val = val.enable_freerun(); - if PADS::TXD { - val = val.enable_txd(); - } - if PADS::CTS { - val = val.enable_cts(); - } - unsafe { uart.transmit_config.write(val) }; - - // Configure receive feature. - let mut val = receive_config; - if PADS::RXD { - val = val.enable_rxd(); - } - unsafe { uart.receive_config.write(val) }; - - Self { uart, pads } - } - - /// Release serial instance and return its peripheral and pads. - #[inline] - pub fn free(self) -> (UART, PADS) { - (self.uart, self.pads) - } - - /// Split serial instance into transmit and receive halves. - #[inline] - pub fn split(self) -> >::Split - where - PADS: Pads, - { - self.pads.split(self.uart) - } -} - -#[inline] -fn uart_write(uart: &RegisterBlock, buf: &[u8]) -> Result { - while uart.fifo_config_1.read().transmit_available_bytes() == 0 { - core::hint::spin_loop(); - } - let len = core::cmp::min( - uart.fifo_config_1.read().transmit_available_bytes() as usize, - buf.len(), - ); - buf.iter() - .take(len) - .for_each(|&word| unsafe { uart.fifo_write.write(word) }); - Ok(len) -} - -#[inline] -fn uart_write_nb(uart: &RegisterBlock, word: u8) -> nb::Result<(), Error> { - if uart.fifo_config_1.read().transmit_available_bytes() == 0 { - return Err(nb::Error::WouldBlock); - } - unsafe { uart.fifo_write.write(word) }; - Ok(()) -} - -#[inline] -fn uart_flush(uart: &RegisterBlock) -> Result<(), Error> { - // There are maximum 32 bytes in transmit FIFO queue, wait until all bytes are available, - // meaning that all data in queue has been sent into UART bus. - while uart.fifo_config_1.read().transmit_available_bytes() != 32 { - core::hint::spin_loop(); - } - Ok(()) -} - -#[inline] -fn uart_flush_nb(uart: &RegisterBlock) -> nb::Result<(), Error> { - if uart.fifo_config_1.read().transmit_available_bytes() != 32 { - return Err(nb::Error::WouldBlock); - } - Ok(()) -} - -#[inline] -fn uart_read(uart: &RegisterBlock, buf: &mut [u8]) -> Result { - while uart.fifo_config_1.read().receive_available_bytes() == 0 { - core::hint::spin_loop(); - } - let len = core::cmp::min( - uart.fifo_config_1.read().receive_available_bytes() as usize, - buf.len(), - ); - buf.iter_mut() - .take(len) - .for_each(|slot| *slot = uart.fifo_read.read()); - Ok(len) -} - -#[inline] -fn uart_read_nb(uart: &RegisterBlock) -> nb::Result { - if uart.fifo_config_1.read().receive_available_bytes() == 0 { - return Err(nb::Error::WouldBlock); - } - let ans = uart.fifo_read.read(); - Ok(ans) -} - -/// Transmit half from splitted serial structure. -pub struct TransmitHalf { - uart: UART, - _pads: PADS, -} - -/// Receive half from splitted serial structure. -pub struct ReceiveHalf { - uart: UART, - _pads: PADS, -} +mod register; +pub use register::*; +mod mux; +pub use mux::*; +mod pad; +pub use pad::*; +mod config; +pub use config::*; +mod error; +pub use error::*; +mod blocking; +pub use blocking::*; +mod asynch; +pub use asynch::*; /// Extend constructor to owned UART register blocks. pub trait UartExt: Sized { @@ -1418,7 +25,17 @@ pub trait UartExt: Sized { config: Config, pads: PADS, clocks: &Clocks, - ) -> Serial + ) -> Result, ConfigError> + where + PADS: Pads; + /// Creates an interrupt driven async/await serial instance without DMA configurations. + fn with_interrupt( + self, + config: Config, + pads: PADS, + clocks: &Clocks, + state: &'static SerialState, + ) -> Result, ConfigError> where PADS: Pads; } @@ -1430,525 +47,23 @@ impl, PADS> UartExt for UART { config: Config, pads: PADS, clocks: &Clocks, - ) -> Serial + ) -> Result, ConfigError> where PADS: Pads, { - Serial::freerun(self, config, pads, clocks) + BlockingSerial::freerun(self, config, pads, clocks) } -} - -impl embedded_io::Error for Error { - #[inline(always)] - fn kind(&self) -> embedded_io::ErrorKind { - embedded_io::ErrorKind::Other - } -} - -impl embedded_hal_nb::serial::Error for Error { - #[inline(always)] - fn kind(&self) -> embedded_hal_nb::serial::ErrorKind { - match self { - Error::Framing => embedded_hal_nb::serial::ErrorKind::FrameFormat, - Error::Noise => embedded_hal_nb::serial::ErrorKind::Noise, - Error::Overrun => embedded_hal_nb::serial::ErrorKind::Overrun, - Error::Parity => embedded_hal_nb::serial::ErrorKind::Parity, - } - } -} - -impl embedded_io::ErrorType for Serial { - type Error = Error; -} - -impl embedded_hal_nb::serial::ErrorType for Serial { - type Error = Error; -} - -impl embedded_io::ErrorType for TransmitHalf { - type Error = Error; -} - -impl embedded_hal_nb::serial::ErrorType for TransmitHalf { - type Error = Error; -} - -impl embedded_io::ErrorType for ReceiveHalf { - type Error = Error; -} - -impl embedded_hal_nb::serial::ErrorType for ReceiveHalf { - type Error = Error; -} - -impl, PADS> embedded_io::Write for Serial { - #[inline] - fn write(&mut self, buf: &[u8]) -> Result { - uart_write(&self.uart, buf) - } - #[inline] - fn flush(&mut self) -> Result<(), Self::Error> { - uart_flush(&self.uart) - } -} - -impl, PADS> embedded_hal_nb::serial::Write - for Serial -{ - #[inline] - fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { - uart_write_nb(&self.uart, word) - } - #[inline] - fn flush(&mut self) -> nb::Result<(), Self::Error> { - uart_flush_nb(&self.uart) - } -} - -impl, PADS> embedded_io::Read for Serial { - #[inline] - fn read(&mut self, buf: &mut [u8]) -> Result { - uart_read(&self.uart, buf) - } -} - -impl, PADS> embedded_hal_nb::serial::Read - for Serial -{ - #[inline] - fn read(&mut self) -> nb::Result { - uart_read_nb(&self.uart) - } -} - -impl, PADS> embedded_io::Write for TransmitHalf { - #[inline] - fn write(&mut self, buf: &[u8]) -> Result { - uart_write(&self.uart, buf) - } - #[inline] - fn flush(&mut self) -> Result<(), Self::Error> { - uart_flush(&self.uart) - } -} - -impl, PADS> embedded_hal_nb::serial::Write - for TransmitHalf -{ - #[inline] - fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { - uart_write_nb(&self.uart, word) - } - #[inline] - fn flush(&mut self) -> nb::Result<(), Self::Error> { - uart_flush_nb(&self.uart) - } -} - -impl, PADS> embedded_io::Read for ReceiveHalf { - #[inline] - fn read(&mut self, buf: &mut [u8]) -> Result { - uart_read(&self.uart, buf) - } -} - -impl, PADS> embedded_hal_nb::serial::Read - for ReceiveHalf -{ - #[inline] - fn read(&mut self) -> nb::Result { - uart_read_nb(&self.uart) - } -} - -/// Serial configuration. -#[derive(Copy, Clone, Debug, PartialEq)] -pub struct Config { - /// Baudrate on the transmit half. - pub transmit_baudrate: Baud, - /// Baudrate on the receive half. - pub receive_baudrate: Baud, - /// Data bit order. - pub bit_order: BitOrder, - /// Parity settings on the transmit half. - pub transmit_parity: Parity, - /// Parity settings on the receive half. - pub receive_parity: Parity, - /// Serial stop bits. - pub stop_bits: StopBits, - /// Data word length on the transmit half. - pub transmit_word_length: WordLength, - /// Data word length on the receive half. - pub receive_word_length: WordLength, -} - -impl Config { - /// Set baudrate for both the transmit and receive halves. - /// - /// This function sets the same baudrate for the transmit and receive halves. - #[inline] - pub const fn set_baudrate(self, baudrate: Baud) -> Self { - Self { - transmit_baudrate: baudrate, - receive_baudrate: baudrate, - ..self - } - } - /// Set parity for both the transmit and receive halves. - #[inline] - pub const fn set_parity(self, parity: Parity) -> Self { - Self { - transmit_parity: parity, - receive_parity: parity, - ..self - } - } - /// Set word length for both the transmit and receive halves. - #[inline] - pub const fn set_word_length(self, word_length: WordLength) -> Self { - Self { - transmit_word_length: word_length, - receive_word_length: word_length, - ..self - } - } - #[inline] - fn into_registers(self) -> (DataConfig, TransmitConfig, ReceiveConfig) { - let data_config = DataConfig::default().set_bit_order(self.bit_order); - let transmit_config = TransmitConfig::default() - .set_parity(self.transmit_parity) - .set_stop_bits(self.stop_bits) - .set_word_length(self.transmit_word_length); - let receive_config = ReceiveConfig::default() - .set_parity(self.receive_parity) - .set_word_length(self.receive_word_length); - (data_config, transmit_config, receive_config) - } -} - -impl Default for Config { - /// Serial configuration defaults to 8-bit word, no parity check, 1 stop bit, LSB first. #[inline] - fn default() -> Self { - Config { - transmit_baudrate: 115_200.Bd(), - receive_baudrate: 115_200.Bd(), - bit_order: BitOrder::LsbFirst, - transmit_parity: Parity::None, - receive_parity: Parity::None, - stop_bits: StopBits::One, - transmit_word_length: WordLength::Eight, - receive_word_length: WordLength::Eight, - } - } -} - -/// Order of the bits transmitted and received on the wire. -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub enum BitOrder { - /// Each byte is sent out LSB-first. - LsbFirst, - /// Each byte is sent out MSB-first. - MsbFirst, -} - -/// Parity check. -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum Parity { - /// No parity check. - None, - /// Even parity bit. - Even, - /// Odd parity bit. - Odd, -} - -/// Stop bits. -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum StopBits { - /// 0.5 stop bits. - ZeroPointFive, - /// 1 stop bit. - One, - /// 1.5 stop bits. - OnePointFive, - /// 2 stop bits. - Two, -} - -/// Word length. -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum WordLength { - /// Five bits per word. - Five, - /// Six bits per word. - Six, - /// Seven bits per word. - Seven, - /// Eight bits per word. - Eight, -} - -/// Serial error. -#[derive(Debug)] -#[non_exhaustive] -pub enum Error { - /// Framing error. - Framing, - /// Noise error. - Noise, - /// RX buffer overrun. - Overrun, - /// Parity check error. - Parity, -} - -#[cfg(test)] -mod tests { - use crate::uart::{StopBits, WordLength}; - - use super::{BitPeriod, Parity, ReceiveConfig, RegisterBlock, TransmitConfig}; - use memoffset::offset_of; - - #[test] - fn struct_register_block_offset() { - assert_eq!(offset_of!(RegisterBlock, transmit_config), 0x0); - assert_eq!(offset_of!(RegisterBlock, receive_config), 0x4); - assert_eq!(offset_of!(RegisterBlock, bit_period), 0x08); - assert_eq!(offset_of!(RegisterBlock, data_config), 0x0c); - assert_eq!(offset_of!(RegisterBlock, interrupt_state), 0x20); - assert_eq!(offset_of!(RegisterBlock, interrupt_mask), 0x24); - assert_eq!(offset_of!(RegisterBlock, interrupt_clear), 0x28); - assert_eq!(offset_of!(RegisterBlock, interrupt_enable), 0x2c); - assert_eq!(offset_of!(RegisterBlock, bus_state), 0x30); - assert_eq!(offset_of!(RegisterBlock, fifo_config_0), 0x80); - assert_eq!(offset_of!(RegisterBlock, fifo_config_1), 0x84); - assert_eq!(offset_of!(RegisterBlock, fifo_write), 0x88); - assert_eq!(offset_of!(RegisterBlock, fifo_read), 0x8c); - } - - #[test] - fn struct_transmit_config_functions() { - let mut val: TransmitConfig = TransmitConfig(0x0); - - val = val.enable_txd(); - assert_eq!(val.0, 0x00000001); - assert!(val.is_txd_enabled()); - val = val.disable_txd(); - assert_eq!(val.0, 0x00000000); - assert!(!val.is_txd_enabled()); - - val = val.enable_cts(); - assert_eq!(val.0, 0x00000002); - assert!(val.is_cts_enabled()); - val = val.disable_cts(); - assert_eq!(val.0, 0x00000000); - assert!(!val.is_cts_enabled()); - - val = val.enable_freerun(); - assert_eq!(val.0, 0x00000004); - assert!(val.is_freerun_enabled()); - val = val.disable_freerun(); - assert_eq!(val.0, 0x00000000); - assert!(!val.is_freerun_enabled()); - - val = val.enable_lin_transmit(); - assert_eq!(val.0, 0x00000008); - assert!(val.is_lin_transmit_enabled()); - val = val.disable_lin_transmit(); - assert_eq!(val.0, 0x00000000); - assert!(!val.is_lin_transmit_enabled()); - - val = val.set_parity(Parity::Even); - assert_eq!(val.0, 0x00000010); - assert_eq!(val.parity(), Parity::Even); - val = val.set_parity(Parity::Odd); - assert_eq!(val.0, 0x00000030); - assert_eq!(val.parity(), Parity::Odd); - val = val.set_parity(Parity::None); - assert_eq!(val.0 & 0x00000010, 0x00000000); - assert_eq!(val.parity(), Parity::None); - - val = TransmitConfig(0x0); - - val = val.enable_ir_transmit(); - assert_eq!(val.0, 0x00000040); - assert!(val.is_ir_transmit_enabled()); - val = val.disable_ir_transmit(); - assert_eq!(val.0, 0x00000000); - assert!(!val.is_ir_transmit_enabled()); - - val = val.enable_ir_inverse(); - assert_eq!(val.0, 0x00000080); - assert!(val.is_ir_inverse_enabled()); - val = val.disable_ir_inverse(); - assert_eq!(val.0, 0x00000000); - assert!(!val.is_ir_inverse_enabled()); - - val = val.set_word_length(WordLength::Five); - assert_eq!(val.0, 0x00000400); - assert_eq!(val.word_length(), WordLength::Five); - val = val.set_word_length(WordLength::Six); - assert_eq!(val.0, 0x00000500); - assert_eq!(val.word_length(), WordLength::Six); - val = val.set_word_length(WordLength::Seven); - assert_eq!(val.0, 0x00000600); - assert_eq!(val.word_length(), WordLength::Seven); - val = val.set_word_length(WordLength::Eight); - assert_eq!(val.0, 0x00000700); - assert_eq!(val.word_length(), WordLength::Eight); - - val = TransmitConfig(0x0); - - val = val.set_stop_bits(StopBits::Two); - assert_eq!(val.0, 0x00001800); - assert_eq!(val.stop_bits(), StopBits::Two); - val = val.set_stop_bits(StopBits::OnePointFive); - assert_eq!(val.0, 0x00001000); - assert_eq!(val.stop_bits(), StopBits::OnePointFive); - val = val.set_stop_bits(StopBits::One); - assert_eq!(val.0, 0x00000800); - assert_eq!(val.stop_bits(), StopBits::One); - val = val.set_stop_bits(StopBits::ZeroPointFive); - assert_eq!(val.0, 0x00000000); - assert_eq!(val.stop_bits(), StopBits::ZeroPointFive); - - for num in 0..=7 { - val = val.set_lin_break_bits(num); - assert_eq!(val.0, (num as u32) << 13); - assert_eq!(val.lin_break_bits(), num); - } - - val = TransmitConfig(0x0); - - for length in [0x0000, 0x1234, 0xabcd, 0xffff] { - val = val.set_transfer_length(length); - assert_eq!(val.0, (length as u32) << 16); - assert_eq!(val.transfer_length(), length); - } - - let default = TransmitConfig::default(); - assert_eq!(default.transfer_length(), 0); - assert_eq!(default.lin_break_bits(), 4); - assert_eq!(default.stop_bits(), StopBits::One); - assert_eq!(default.word_length(), WordLength::Eight); - assert!(!default.is_ir_inverse_enabled()); - assert!(!default.is_ir_transmit_enabled()); - assert_eq!(default.parity(), Parity::None); - assert!(!default.is_lin_transmit_enabled()); - assert!(!default.is_freerun_enabled()); - assert!(!default.is_cts_enabled()); - assert!(!default.is_txd_enabled()); - } - - #[test] - fn struct_bit_period_functions() { - let mut val: BitPeriod = BitPeriod(0x0); - - for trans in [0x0000, 0x1037, 0xabcd, 0xffff] { - val = val.set_transmit_time_interval(trans); - assert_eq!(val.0, trans as u32); - assert_eq!(val.transmit_time_interval(), trans); - } - - val = BitPeriod(0x0); - - for recv in [0x0000, 0x1037, 0xabcd, 0xffff] { - val = val.set_receive_time_interval(recv); - assert_eq!(val.0, (recv as u32) << 16); - assert_eq!(val.receive_time_interval(), recv); - } - - // TODO: use getter functions to check default value for BitPeriod - } - - #[test] - fn struct_receive_config_functions() { - let mut val: ReceiveConfig = ReceiveConfig(0x0); - - val = val.enable_rxd(); - assert_eq!(val.0, 0x00000001); - assert!(val.is_rxd_enabled()); - val = val.disable_rxd(); - assert_eq!(val.0, 0x00000000); - assert!(!val.is_rxd_enabled()); - - val = val.enable_auto_baudrate(); - assert_eq!(val.0, 0x00000002); - assert!(val.is_auto_baudrate_enabled()); - val = val.disable_auto_baudrate(); - assert_eq!(val.0, 0x00000000); - assert!(!val.is_auto_baudrate_enabled()); - - val = val.enable_lin_receive(); - assert_eq!(val.0, 0x00000008); - assert!(val.is_lin_receive_enabled()); - val = val.disable_lin_receive(); - assert_eq!(val.0, 0x00000000); - assert!(!val.is_lin_receive_enabled()); - - val = val.set_parity(Parity::Even); - assert_eq!(val.0, 0x00000010); - assert_eq!(val.parity(), Parity::Even); - val = val.set_parity(Parity::Odd); - assert_eq!(val.0, 0x00000030); - assert_eq!(val.parity(), Parity::Odd); - val = val.set_parity(Parity::None); - assert_eq!(val.0 & 0x00000010, 0x00000000); - assert_eq!(val.parity(), Parity::None); - - val = ReceiveConfig(0x0); - - val = val.enable_ir_receive(); - assert_eq!(val.0, 0x00000040); - assert!(val.is_ir_receive_enabled()); - val = val.disable_ir_receive(); - assert_eq!(val.0, 0x00000000); - assert!(!val.is_ir_receive_enabled()); - - val = val.enable_ir_inverse(); - assert_eq!(val.0, 0x00000080); - assert!(val.is_ir_inverse_enabled()); - val = val.disable_ir_inverse(); - assert_eq!(val.0, 0x00000000); - assert!(!val.is_ir_inverse_enabled()); - - val = val.set_word_length(WordLength::Five); - assert_eq!(val.0, 0x00000400); - assert_eq!(val.word_length(), WordLength::Five); - val = val.set_word_length(WordLength::Six); - assert_eq!(val.0, 0x00000500); - assert_eq!(val.word_length(), WordLength::Six); - val = val.set_word_length(WordLength::Seven); - assert_eq!(val.0, 0x00000600); - assert_eq!(val.word_length(), WordLength::Seven); - val = val.set_word_length(WordLength::Eight); - assert_eq!(val.0, 0x00000700); - assert_eq!(val.word_length(), WordLength::Eight); - - val = ReceiveConfig(0x0); - - val = val.enable_deglitch(); - assert_eq!(val.0, 0x00000800); - assert!(val.is_deglitch_enabled()); - val = val.disable_deglitch(); - assert_eq!(val.0, 0x00000000); - assert!(!val.is_deglitch_enabled()); - - for num in 0..=7 { - val = val.set_deglitch_cycles(num); - assert_eq!(val.0, (num as u32) << 12); - assert_eq!(val.deglitch_cycles(), num); - } - - val = ReceiveConfig(0x0); - - for length in [0x0000, 0x1234, 0xabcd, 0xffff] { - val = val.set_transfer_length(length); - assert_eq!(val.0, (length as u32) << 16); - assert_eq!(val.transfer_length(), length); - } + fn with_interrupt( + self, + config: Config, + pads: PADS, + clocks: &Clocks, + state: &'static SerialState, + ) -> Result, ConfigError> + where + PADS: Pads, + { + AsyncSerial::new(self, config, pads, clocks, state) } - - // TODO: use getter functions to check default value for ReceiveConfig } diff --git a/bouffalo-hal/src/uart/asynch.rs b/bouffalo-hal/src/uart/asynch.rs new file mode 100644 index 0000000..3320ec5 --- /dev/null +++ b/bouffalo-hal/src/uart/asynch.rs @@ -0,0 +1,209 @@ +use super::{ + uart_config, Config, ConfigError, Error, Interrupt, InterruptClear, Pads, RegisterBlock, +}; +use crate::clocks::Clocks; +use core::{ + future::Future, + ops::Deref, + pin::Pin, + sync::atomic::{AtomicUsize, Ordering}, + task::{Context, Poll}, +}; + +/// Managed async/await serial peripheral. +pub struct AsyncSerial { + uart: UART, + pads: PADS, + state: &'static SerialState, +} + +impl, PADS> AsyncSerial { + /// Creates the async/await serial peripheral from owned peripheral structure, configuration, pads + /// and a waker registry. + #[inline] + pub fn new( + uart: UART, + config: Config, + pads: PADS, + clocks: &Clocks, + state: &'static SerialState, + ) -> Result + where + PADS: Pads, + { + // Calculate transmit interval and register values from configuration. + let (bit_period, data_config, transmit_config, receive_config) = + uart_config::(config, &clocks)?; + + // Write bit period. + unsafe { uart.bit_period.write(bit_period) }; + // Write the bit-order. + unsafe { uart.data_config.write(data_config) }; + // Configure transmit feature without freerun. + unsafe { uart.transmit_config.write(transmit_config) }; + // Configure receive feature. + unsafe { uart.receive_config.write(receive_config) }; + + state + .ref_to_serial + .store(&*uart as *const _ as usize, Ordering::Release); + + Ok(AsyncSerial { uart, pads, state }) + } + + /// Release serial instance and return its peripheral and pads. + #[inline] + pub fn free(self) -> (UART, PADS) { + (self.uart, self.pads) + } +} + +/// Set of wakers as the state for an async/await serial peripheral. +pub struct SerialState { + transmit_ready: atomic_waker::AtomicWaker, + receive_ready: atomic_waker::AtomicWaker, + ref_to_serial: AtomicUsize, +} + +impl SerialState { + /// Creates the set of wakers for a serial peripheral. + #[inline] + pub const fn new() -> SerialState { + SerialState { + transmit_ready: atomic_waker::AtomicWaker::new(), + receive_ready: atomic_waker::AtomicWaker::new(), + ref_to_serial: AtomicUsize::new(0), + } + } + /// Use this waker set to handle interrupt. + #[inline] + pub fn on_interrupt(&self) { + let uart = + unsafe { &*(self.ref_to_serial.load(Ordering::Acquire) as *const RegisterBlock) }; + let state = uart.interrupt_state.read(); + for (interrupt, waker) in [ + (Interrupt::ReceiveFifoReady, &self.receive_ready), + (Interrupt::TransmitFifoReady, &self.transmit_ready), + ] { + if state.has_interrupt(interrupt) { + waker.wake(); + unsafe { + uart.interrupt_clear + .write(InterruptClear::default().clear_interrupt(interrupt)) + }; + } + } + } +} + +struct WaitForInterrupt<'r> { + uart: &'r RegisterBlock, + interrupt: Interrupt, + registry: &'r atomic_waker::AtomicWaker, +} + +impl<'r> WaitForInterrupt<'r> { + #[inline] + pub const fn new( + uart: &'r RegisterBlock, + interrupt: Interrupt, + registry: &'r atomic_waker::AtomicWaker, + ) -> Self { + Self { + uart, + interrupt, + registry, + } + } +} + +impl Future for WaitForInterrupt<'_> { + type Output = (); + + #[inline] + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + if self + .uart + .interrupt_state + .read() + .has_interrupt(self.interrupt) + { + Poll::Ready(()) + } else { + self.registry.register(cx.waker()); + Poll::Pending + } + } +} + +#[inline] +async fn uart_write_async( + uart: &RegisterBlock, + buf: &[u8], + registry: &atomic_waker::AtomicWaker, +) -> Result { + let buf = match buf.len() { + 0 => return Ok(0), + _ => buf, + }; + unsafe { + uart.interrupt_enable + .modify(|val| val.enable_interrupt(Interrupt::TransmitFifoReady)) + }; + WaitForInterrupt::new(uart, Interrupt::TransmitFifoReady, registry).await; + let len = core::cmp::min( + uart.fifo_config_1.read().transmit_available_bytes() as usize, + buf.len(), + ); + buf.iter() + .take(len) + .for_each(|&word| unsafe { uart.fifo_write.write(word) }); + Ok(len) +} + +#[inline] +async fn uart_read_async( + uart: &RegisterBlock, + buf: &mut [u8], + registry: &atomic_waker::AtomicWaker, +) -> Result { + let buf = match buf.len() { + 0 => return Ok(0), + _ => buf, + }; + unsafe { + uart.interrupt_enable + .modify(|val| val.enable_interrupt(Interrupt::ReceiveFifoReady)) + }; + WaitForInterrupt::new(uart, Interrupt::ReceiveFifoReady, registry).await; + let len = core::cmp::min( + uart.fifo_config_1.read().receive_available_bytes() as usize, + buf.len(), + ); + buf.iter_mut() + .take(len) + .for_each(|slot| *slot = uart.fifo_read.read()); + Ok(len) +} + +impl embedded_io_async::ErrorType for AsyncSerial { + type Error = Error; +} + +impl, PADS> embedded_io_async::Write + for AsyncSerial +{ + #[inline] + async fn write(&mut self, buf: &[u8]) -> Result { + uart_write_async(&self.uart, buf, &self.state.transmit_ready).await + } +} + +impl, PADS> embedded_io_async::Read + for AsyncSerial +{ + #[inline] + async fn read(&mut self, buf: &mut [u8]) -> Result { + uart_read_async(&self.uart, buf, &self.state.receive_ready).await + } +} diff --git a/bouffalo-hal/src/uart/blocking.rs b/bouffalo-hal/src/uart/blocking.rs new file mode 100644 index 0000000..7787331 --- /dev/null +++ b/bouffalo-hal/src/uart/blocking.rs @@ -0,0 +1,242 @@ +use super::{uart_config, Config, ConfigError, Error, Pads, RegisterBlock}; +use crate::clocks::Clocks; +use core::ops::Deref; + +/// Managed blocking serial peripheral. +pub struct BlockingSerial { + uart: UART, + pads: PADS, +} + +impl, PADS> BlockingSerial { + /// Creates a polling serial instance, without interrupt or DMA configurations. + #[inline] + pub fn freerun( + uart: UART, + config: Config, + pads: PADS, + clocks: &Clocks, + ) -> Result + where + PADS: Pads, + { + // Calculate transmit interval and register values from configuration. + let (bit_period, data_config, transmit_config, receive_config) = + uart_config::(config, &clocks)?; + + // Write bit period. + unsafe { uart.bit_period.write(bit_period) }; + // Write the bit-order. + unsafe { uart.data_config.write(data_config) }; + + // Configure freerun transmit feature. + let mut val = transmit_config; + val = val.enable_freerun(); + unsafe { uart.transmit_config.write(val) }; + // Configure receive feature. + unsafe { uart.receive_config.write(receive_config) }; + + Ok(Self { uart, pads }) + } + + /// Release serial instance and return its peripheral and pads. + #[inline] + pub fn free(self) -> (UART, PADS) { + (self.uart, self.pads) + } + + /// Split serial instance into transmit and receive halves. + #[inline] + pub fn split(self) -> >::Split + where + PADS: Pads, + { + self.pads.split(self.uart) + } +} + +/// Transmit half from splitted serial structure. +pub struct BlockingTransmitHalf { + pub(crate) uart: UART, + pub(crate) _pads: PADS, +} + +/// Receive half from splitted serial structure. +pub struct BlockingReceiveHalf { + pub(crate) uart: UART, + pub(crate) _pads: PADS, +} + +#[inline] +fn uart_write(uart: &RegisterBlock, buf: &[u8]) -> Result { + while uart.fifo_config_1.read().transmit_available_bytes() == 0 { + core::hint::spin_loop(); + } + let len = core::cmp::min( + uart.fifo_config_1.read().transmit_available_bytes() as usize, + buf.len(), + ); + buf.iter() + .take(len) + .for_each(|&word| unsafe { uart.fifo_write.write(word) }); + Ok(len) +} + +#[inline] +fn uart_write_nb(uart: &RegisterBlock, word: u8) -> nb::Result<(), Error> { + if uart.fifo_config_1.read().transmit_available_bytes() == 0 { + return Err(nb::Error::WouldBlock); + } + unsafe { uart.fifo_write.write(word) }; + Ok(()) +} + +#[inline] +fn uart_flush(uart: &RegisterBlock) -> Result<(), Error> { + // There are maximum 32 bytes in transmit FIFO queue, wait until all bytes are available, + // meaning that all data in queue has been sent into UART bus. + while uart.fifo_config_1.read().transmit_available_bytes() != 32 { + core::hint::spin_loop(); + } + Ok(()) +} + +#[inline] +fn uart_flush_nb(uart: &RegisterBlock) -> nb::Result<(), Error> { + if uart.fifo_config_1.read().transmit_available_bytes() != 32 { + return Err(nb::Error::WouldBlock); + } + Ok(()) +} + +#[inline] +fn uart_read(uart: &RegisterBlock, buf: &mut [u8]) -> Result { + while uart.fifo_config_1.read().receive_available_bytes() == 0 { + core::hint::spin_loop(); + } + let len = core::cmp::min( + uart.fifo_config_1.read().receive_available_bytes() as usize, + buf.len(), + ); + buf.iter_mut() + .take(len) + .for_each(|slot| *slot = uart.fifo_read.read()); + Ok(len) +} + +#[inline] +fn uart_read_nb(uart: &RegisterBlock) -> nb::Result { + if uart.fifo_config_1.read().receive_available_bytes() == 0 { + return Err(nb::Error::WouldBlock); + } + let ans = uart.fifo_read.read(); + Ok(ans) +} + +impl embedded_io::ErrorType for BlockingSerial { + type Error = Error; +} + +impl embedded_hal_nb::serial::ErrorType for BlockingSerial { + type Error = Error; +} + +impl embedded_io::ErrorType for BlockingTransmitHalf { + type Error = Error; +} + +impl embedded_hal_nb::serial::ErrorType for BlockingTransmitHalf { + type Error = Error; +} + +impl embedded_io::ErrorType for BlockingReceiveHalf { + type Error = Error; +} + +impl embedded_hal_nb::serial::ErrorType for BlockingReceiveHalf { + type Error = Error; +} + +impl, PADS> embedded_io::Write for BlockingSerial { + #[inline] + fn write(&mut self, buf: &[u8]) -> Result { + uart_write(&self.uart, buf) + } + #[inline] + fn flush(&mut self) -> Result<(), Self::Error> { + uart_flush(&self.uart) + } +} + +impl, PADS> embedded_hal_nb::serial::Write + for BlockingSerial +{ + #[inline] + fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { + uart_write_nb(&self.uart, word) + } + #[inline] + fn flush(&mut self) -> nb::Result<(), Self::Error> { + uart_flush_nb(&self.uart) + } +} + +impl, PADS> embedded_io::Read for BlockingSerial { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> Result { + uart_read(&self.uart, buf) + } +} + +impl, PADS> embedded_hal_nb::serial::Read + for BlockingSerial +{ + #[inline] + fn read(&mut self) -> nb::Result { + uart_read_nb(&self.uart) + } +} + +impl, PADS> embedded_io::Write + for BlockingTransmitHalf +{ + #[inline] + fn write(&mut self, buf: &[u8]) -> Result { + uart_write(&self.uart, buf) + } + #[inline] + fn flush(&mut self) -> Result<(), Self::Error> { + uart_flush(&self.uart) + } +} + +impl, PADS> embedded_hal_nb::serial::Write + for BlockingTransmitHalf +{ + #[inline] + fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { + uart_write_nb(&self.uart, word) + } + #[inline] + fn flush(&mut self) -> nb::Result<(), Self::Error> { + uart_flush_nb(&self.uart) + } +} + +impl, PADS> embedded_io::Read + for BlockingReceiveHalf +{ + #[inline] + fn read(&mut self, buf: &mut [u8]) -> Result { + uart_read(&self.uart, buf) + } +} + +impl, PADS> embedded_hal_nb::serial::Read + for BlockingReceiveHalf +{ + #[inline] + fn read(&mut self) -> nb::Result { + uart_read_nb(&self.uart) + } +} diff --git a/bouffalo-hal/src/uart/config.rs b/bouffalo-hal/src/uart/config.rs new file mode 100644 index 0000000..13d69f2 --- /dev/null +++ b/bouffalo-hal/src/uart/config.rs @@ -0,0 +1,183 @@ +use super::{BitPeriod, DataConfig, Pads, ReceiveConfig, TransmitConfig}; +use crate::clocks::Clocks; +use embedded_time::rate::{Baud, Extensions}; + +/// Serial configuration. +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct Config { + /// Baudrate on the transmit half. + pub transmit_baudrate: Baud, + /// Baudrate on the receive half. + pub receive_baudrate: Baud, + /// Data bit order. + pub bit_order: BitOrder, + /// Parity settings on the transmit half. + pub transmit_parity: Parity, + /// Parity settings on the receive half. + pub receive_parity: Parity, + /// Serial stop bits. + pub stop_bits: StopBits, + /// Data word length on the transmit half. + pub transmit_word_length: WordLength, + /// Data word length on the receive half. + pub receive_word_length: WordLength, +} + +impl Config { + /// Set baudrate for both the transmit and receive halves. + /// + /// This function sets the same baudrate for the transmit and receive halves. + #[inline] + pub const fn set_baudrate(self, baudrate: Baud) -> Self { + Self { + transmit_baudrate: baudrate, + receive_baudrate: baudrate, + ..self + } + } + /// Set parity for both the transmit and receive halves. + #[inline] + pub const fn set_parity(self, parity: Parity) -> Self { + Self { + transmit_parity: parity, + receive_parity: parity, + ..self + } + } + /// Set word length for both the transmit and receive halves. + #[inline] + pub const fn set_word_length(self, word_length: WordLength) -> Self { + Self { + transmit_word_length: word_length, + receive_word_length: word_length, + ..self + } + } + #[inline] + fn into_registers(self) -> (DataConfig, TransmitConfig, ReceiveConfig) { + let data_config = DataConfig::default().set_bit_order(self.bit_order); + let transmit_config = TransmitConfig::default() + .set_parity(self.transmit_parity) + .set_stop_bits(self.stop_bits) + .set_word_length(self.transmit_word_length); + let receive_config = ReceiveConfig::default() + .set_parity(self.receive_parity) + .set_word_length(self.receive_word_length); + (data_config, transmit_config, receive_config) + } +} + +impl Default for Config { + /// Serial configuration defaults to 8-bit word, no parity check, 1 stop bit, LSB first. + #[inline] + fn default() -> Self { + Config { + transmit_baudrate: 115_200.Bd(), + receive_baudrate: 115_200.Bd(), + bit_order: BitOrder::LsbFirst, + transmit_parity: Parity::None, + receive_parity: Parity::None, + stop_bits: StopBits::One, + transmit_word_length: WordLength::Eight, + receive_word_length: WordLength::Eight, + } + } +} + +#[inline] +pub(crate) fn uart_config>( + config: Config, + clocks: &Clocks, +) -> Result<(BitPeriod, DataConfig, TransmitConfig, ReceiveConfig), ConfigError> { + let uart_clock = match clocks.uart_clock::() { + Some(freq) => freq, + None => return Err(ConfigError::ClockSource), + }; + let transmit_interval = uart_clock.0 / config.transmit_baudrate.0; + let receive_interval = uart_clock.0 / config.receive_baudrate.0; + if transmit_interval > 65535 { + return Err(ConfigError::TransmitBaudrateTooLow); + } else if transmit_interval < 1 { + return Err(ConfigError::TransmitBaudrateTooHigh); + } + if receive_interval > 65535 { + return Err(ConfigError::ReceiveBaudrateTooLow); + } else if receive_interval < 1 { + return Err(ConfigError::ReceiveBaudrateTooHigh); + } + let bit_period = BitPeriod::default() + .set_transmit_time_interval(transmit_interval as u16) + .set_receive_time_interval(receive_interval as u16); + let (data_config, mut transmit_config, mut receive_config) = config.into_registers(); + if PADS::TXD { + transmit_config = transmit_config.enable_txd(); + } + if PADS::CTS { + transmit_config = transmit_config.enable_cts(); + } + if PADS::RXD { + receive_config = receive_config.enable_rxd(); + } + Ok((bit_period, data_config, transmit_config, receive_config)) +} + +/// Errors on serial configuration. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum ConfigError { + /// Impossibly high baudrate for current bus clock frequency. + TransmitBaudrateTooHigh, + /// Impossibly low baudrate for current bus clock frequency. + TransmitBaudrateTooLow, + /// Impossibly high baudrate for current bus clock frequency. + ReceiveBaudrateTooHigh, + /// Impossibly low baudrate for current bus clock frequency. + ReceiveBaudrateTooLow, + /// Clock source unavailable. + ClockSource, +} + +/// Order of the bits transmitted and received on the wire. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum BitOrder { + /// Each byte is sent out LSB-first. + LsbFirst, + /// Each byte is sent out MSB-first. + MsbFirst, +} + +/// Parity check. +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum Parity { + /// No parity check. + None, + /// Even parity bit. + Even, + /// Odd parity bit. + Odd, +} + +/// Stop bits. +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum StopBits { + /// 0.5 stop bits. + ZeroPointFive, + /// 1 stop bit. + One, + /// 1.5 stop bits. + OnePointFive, + /// 2 stop bits. + Two, +} + +/// Word length. +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum WordLength { + /// Five bits per word. + Five, + /// Six bits per word. + Six, + /// Seven bits per word. + Seven, + /// Eight bits per word. + Eight, +} diff --git a/bouffalo-hal/src/uart/error.rs b/bouffalo-hal/src/uart/error.rs new file mode 100644 index 0000000..92a126b --- /dev/null +++ b/bouffalo-hal/src/uart/error.rs @@ -0,0 +1,32 @@ +/// Serial error. +#[derive(Debug)] +#[non_exhaustive] +pub enum Error { + /// Framing error. + Framing, + /// Noise error. + Noise, + /// RX buffer overrun. + Overrun, + /// Parity check error. + Parity, +} + +impl embedded_io::Error for Error { + #[inline(always)] + fn kind(&self) -> embedded_io::ErrorKind { + embedded_io::ErrorKind::Other + } +} + +impl embedded_hal_nb::serial::Error for Error { + #[inline(always)] + fn kind(&self) -> embedded_hal_nb::serial::ErrorKind { + match self { + Error::Framing => embedded_hal_nb::serial::ErrorKind::FrameFormat, + Error::Noise => embedded_hal_nb::serial::ErrorKind::Noise, + Error::Overrun => embedded_hal_nb::serial::ErrorKind::Overrun, + Error::Parity => embedded_hal_nb::serial::ErrorKind::Parity, + } + } +} diff --git a/bouffalo-hal/src/uart/mux.rs b/bouffalo-hal/src/uart/mux.rs new file mode 100644 index 0000000..469b600 --- /dev/null +++ b/bouffalo-hal/src/uart/mux.rs @@ -0,0 +1,149 @@ +use crate::glb::{self, v2::UartSignal}; +use core::{marker::PhantomData, ops::Deref}; + +/// Multiplex to Request-to-Send (type state). +pub struct MuxRts; + +/// Multiplex to Clear-to-Send (type state). +pub struct MuxCts; + +/// Multiplex to Transmit (type state). +pub struct MuxTxd; + +/// Multiplex to Receive (type state). +pub struct MuxRxd; + +impl MuxRts { + #[inline] + fn signal() -> UartSignal { + match I { + 0 => UartSignal::Rts0, + 1 => UartSignal::Rts1, + 2 => UartSignal::Rts2, + _ => unreachable!(), + } + } +} + +impl MuxCts { + #[inline] + fn signal() -> UartSignal { + match I { + 0 => UartSignal::Cts0, + 1 => UartSignal::Cts1, + 2 => UartSignal::Cts2, + _ => unreachable!(), + } + } +} + +impl MuxTxd { + #[inline] + fn signal() -> UartSignal { + match I { + 0 => UartSignal::Txd0, + 1 => UartSignal::Txd1, + 2 => UartSignal::Txd2, + _ => unreachable!(), + } + } +} + +impl MuxRxd { + #[inline] + fn signal() -> UartSignal { + match I { + 0 => UartSignal::Rxd0, + 1 => UartSignal::Rxd1, + 2 => UartSignal::Rxd2, + _ => unreachable!(), + } + } +} + +/// Global peripheral UART signal multiplexer. +/// +/// This structure only owns the GLB signal multiplexer for signal number `N`. +pub struct UartMux { + base: GLB, + _mode: PhantomData, +} + +impl, const N: usize, M> UartMux { + /// Configure the internal UART signal to Request-to-Send (RTS). + #[inline] + pub fn into_request_to_send(self) -> UartMux> { + let config = self.base.uart_mux_group[N >> 3] + .read() + .set_signal(N & 0x7, MuxRts::::signal()); + unsafe { self.base.uart_mux_group[N >> 3].write(config) }; + UartMux { + base: self.base, + _mode: PhantomData, + } + } + /// Configure the internal UART signal to Transmit (TXD). + #[inline] + pub fn into_transmit(self) -> UartMux> { + let config = self.base.uart_mux_group[N >> 3] + .read() + .set_signal(N & 0x7, MuxTxd::::signal()); + unsafe { self.base.uart_mux_group[N >> 3].write(config) }; + UartMux { + base: self.base, + _mode: PhantomData, + } + } + /// Configure the internal UART signal to Receive (RXD). + #[inline] + pub fn into_receive(self) -> UartMux> { + let config = self.base.uart_mux_group[N >> 3] + .read() + .set_signal(N & 0x7, MuxRxd::::signal()); + unsafe { self.base.uart_mux_group[N >> 3].write(config) }; + UartMux { + base: self.base, + _mode: PhantomData, + } + } + /// Configure the internal UART signal to Clear-to-Send (CTS). + #[inline] + pub fn into_clear_to_send(self) -> UartMux> { + let config = self.base.uart_mux_group[N >> 3] + .read() + .set_signal(N & 0x7, MuxCts::::signal()); + unsafe { self.base.uart_mux_group[N >> 3].write(config) }; + UartMux { + base: self.base, + _mode: PhantomData, + } + } +} + +/// Available UART signal multiplexers. +pub struct UartMuxes { + /// Multiplexer of UART signal 0. + pub sig0: UartMux>, + /// Multiplexer of UART signal 1. + pub sig1: UartMux>, + /// Multiplexer of UART signal 2. + pub sig2: UartMux>, + /// Multiplexer of UART signal 3. + pub sig3: UartMux>, + /// Multiplexer of UART signal 4. + pub sig4: UartMux>, + /// Multiplexer of UART signal 5. + pub sig5: UartMux>, + /// Multiplexer of UART signal 6. + pub sig6: UartMux>, + /// Multiplexer of UART signal 7. + pub sig7: UartMux>, + /// Multiplexer of UART signal 8. + pub sig8: UartMux>, + /// Multiplexer of UART signal 9. + pub sig9: UartMux>, + /// Multiplexer of UART signal 10. + pub sig10: UartMux>, + /// Multiplexer of UART signal 11. + pub sig11: UartMux>, +} diff --git a/bouffalo-hal/src/uart/pad.rs b/bouffalo-hal/src/uart/pad.rs new file mode 100644 index 0000000..4a6cf72 --- /dev/null +++ b/bouffalo-hal/src/uart/pad.rs @@ -0,0 +1,344 @@ +use super::{BlockingReceiveHalf, BlockingTransmitHalf, MuxCts, MuxRts, MuxRxd, MuxTxd, UartMux}; +use crate::glb; +use crate::gpio::{MmUart, Pad, Uart}; +use core::ops::Deref; + +/// Check if target gpio `Pin` is internally connected to UART signal index `I`. +pub trait HasUartSignal {} + +impl HasUartSignal<0> for Pad {} +impl HasUartSignal<1> for Pad {} +impl HasUartSignal<2> for Pad {} +impl HasUartSignal<3> for Pad {} +impl HasUartSignal<4> for Pad {} +impl HasUartSignal<5> for Pad {} +impl HasUartSignal<6> for Pad {} +impl HasUartSignal<7> for Pad {} +impl HasUartSignal<8> for Pad {} +impl HasUartSignal<9> for Pad {} +impl HasUartSignal<10> for Pad {} +impl HasUartSignal<11> for Pad {} +impl HasUartSignal<0> for Pad {} +impl HasUartSignal<1> for Pad {} +impl HasUartSignal<2> for Pad {} +impl HasUartSignal<3> for Pad {} +impl HasUartSignal<4> for Pad {} +impl HasUartSignal<5> for Pad {} +impl HasUartSignal<6> for Pad {} +impl HasUartSignal<7> for Pad {} +impl HasUartSignal<8> for Pad {} +impl HasUartSignal<9> for Pad {} +impl HasUartSignal<10> for Pad {} +impl HasUartSignal<11> for Pad {} +impl HasUartSignal<0> for Pad {} +impl HasUartSignal<1> for Pad {} +impl HasUartSignal<2> for Pad {} +impl HasUartSignal<3> for Pad {} +impl HasUartSignal<4> for Pad {} +impl HasUartSignal<5> for Pad {} +impl HasUartSignal<6> for Pad {} +impl HasUartSignal<7> for Pad {} +impl HasUartSignal<8> for Pad {} +impl HasUartSignal<9> for Pad {} +impl HasUartSignal<10> for Pad {} +impl HasUartSignal<11> for Pad {} +impl HasUartSignal<0> for Pad {} +impl HasUartSignal<1> for Pad {} +impl HasUartSignal<2> for Pad {} +impl HasUartSignal<3> for Pad {} +impl HasUartSignal<4> for Pad {} +impl HasUartSignal<5> for Pad {} +impl HasUartSignal<6> for Pad {} +impl HasUartSignal<7> for Pad {} +impl HasUartSignal<8> for Pad {} +impl HasUartSignal<9> for Pad {} + +/// Check if an internal multi-media UART signal is connected to target gpio `Pin`. +pub trait HasMmUartSignal {} + +impl HasMmUartSignal for Pad {} + +/// Valid UART pads. +#[diagnostic::on_unimplemented( + message = "the I/O pad and signal multiplexer group {Self} is not connected to any UART peripherals on hardware" +)] +pub trait Pads { + /// Checks if this pin configuration includes Request-to-Send feature. + const RTS: bool; + /// Checks if this pin configuration includes Clear-to-Send feature. + const CTS: bool; + /// Checks if this pin configuration includes Transmit feature. + const TXD: bool; + /// Checks if this pin configuration includes Receive feature. + const RXD: bool; + /// Valid split configuration type for current pads and multiplexers. + type Split; + + fn split(self, uart: T) -> Self::Split; +} + +#[inline] +fn from_pads( + uart: T, + tx: TX, + rx: RX, +) -> (BlockingTransmitHalf, BlockingReceiveHalf) { + ( + BlockingTransmitHalf { + uart: unsafe { core::ptr::read_volatile(&uart) }, + _pads: tx, + }, + BlockingReceiveHalf { uart, _pads: rx }, + ) +} + +impl Pads + for (Pad, UartMux>) +where + A1: Deref, + Pad: HasUartSignal, +{ + const RTS: bool = false; + const CTS: bool = false; + const TXD: bool = true; + const RXD: bool = false; + type Split = ( + BlockingTransmitHalf, UartMux>)>, + BlockingReceiveHalf, + ); + #[inline] + fn split(self, uart: T) -> Self::Split { + from_pads(uart, self, ()) + } +} + +impl< + A1, + GLB2, + A3, + GLB4, + const I1: usize, + const I2: usize, + const U: usize, + const N1: usize, + const N2: usize, + > Pads + for ( + (Pad, UartMux>), + (Pad, UartMux>), + ) +where + A1: Deref, + A3: Deref, + Pad: HasUartSignal, + Pad: HasUartSignal, +{ + const RTS: bool = false; + const CTS: bool = false; + const TXD: bool = true; + const RXD: bool = true; + type Split = ( + BlockingTransmitHalf, UartMux>)>, + BlockingReceiveHalf, UartMux>)>, + ); + #[inline] + fn split(self, uart: T) -> Self::Split { + from_pads(uart, self.0, self.1) + } +} + +impl< + A1, + GLB2, + A3, + GLB4, + const I1: usize, + const I2: usize, + const U: usize, + const N1: usize, + const N2: usize, + > Pads + for ( + (Pad, UartMux>), + (Pad, UartMux>), + ) +where + A1: Deref, + A3: Deref, + Pad: HasUartSignal, + Pad: HasUartSignal, +{ + const RTS: bool = false; + const CTS: bool = true; + const TXD: bool = true; + const RXD: bool = false; + type Split = BlockingTransmitHalf< + T, + ( + (Pad, UartMux>), + (Pad, UartMux>), + ), + >; + #[inline] + fn split(self, uart: T) -> Self::Split { + BlockingTransmitHalf { uart, _pads: self } + } +} + +impl< + A1, + GLB2, + A3, + GLB4, + A5, + GLB6, + A7, + GLB8, + const I1: usize, + const I2: usize, + const I3: usize, + const I4: usize, + const U: usize, + const N1: usize, + const N2: usize, + const N3: usize, + const N4: usize, + > Pads + for ( + (Pad, UartMux>), + (Pad, UartMux>), + (Pad, UartMux>), + (Pad, UartMux>), + ) +where + A1: Deref, + A3: Deref, + A5: Deref, + A7: Deref, + Pad: HasUartSignal, + Pad: HasUartSignal, + Pad: HasUartSignal, + Pad: HasUartSignal, +{ + const RTS: bool = false; + const CTS: bool = true; + const TXD: bool = true; + const RXD: bool = false; + type Split = ( + BlockingTransmitHalf< + T, + ( + (Pad, UartMux>), + (Pad, UartMux>), + ), + >, + BlockingReceiveHalf< + T, + ( + (Pad, UartMux>), + (Pad, UartMux>), + ), + >, + ); + #[inline] + fn split(self, uart: T) -> Self::Split { + from_pads(uart, (self.0, self.3), (self.1, self.2)) + } +} + +// TODO: support split for MmUart pads. + +const MMUART_UART_ID: usize = 3; + +impl Pads for Pad +where + A1: Deref, + Pad: HasMmUartSignal, +{ + const RTS: bool = { N % 4 == 2 }; + const CTS: bool = { N % 4 == 3 }; + const TXD: bool = { N % 4 == 0 }; + const RXD: bool = { N % 4 == 1 }; + type Split = (); + #[inline] + fn split(self, uart: T) -> Self::Split { + let _ = uart; + () + } +} + +impl Pads + for (Pad, Pad) +where + A1: Deref, + A2: Deref, + Pad: HasMmUartSignal, + Pad: HasMmUartSignal, +{ + const RTS: bool = { N1 % 4 == 2 || N2 % 4 == 2 }; + const CTS: bool = { N1 % 4 == 3 || N2 % 4 == 3 }; + const TXD: bool = { N1 % 4 == 0 || N2 % 4 == 0 }; + const RXD: bool = { N1 % 4 == 1 || N2 % 4 == 1 }; + type Split = (); + #[inline] + fn split(self, uart: T) -> Self::Split { + let _ = uart; + () + } +} + +impl Pads + for ( + Pad, + Pad, + Pad, + ) +where + A1: Deref, + A2: Deref, + A3: Deref, + Pad: HasMmUartSignal, + Pad: HasMmUartSignal, + Pad: HasMmUartSignal, +{ + const RTS: bool = { N1 % 4 == 2 || N2 % 4 == 2 || N3 % 4 == 2 }; + const CTS: bool = { N1 % 4 == 3 || N2 % 4 == 3 || N3 % 4 == 3 }; + const TXD: bool = { N1 % 4 == 0 || N2 % 4 == 0 || N3 % 4 == 0 }; + const RXD: bool = { N1 % 4 == 1 || N2 % 4 == 1 || N3 % 4 == 1 }; + type Split = (); + #[inline] + fn split(self, uart: T) -> Self::Split { + let _ = uart; + () + } +} + +impl + Pads + for ( + Pad, + Pad, + Pad, + Pad, + ) +where + A1: Deref, + A2: Deref, + A3: Deref, + A4: Deref, + Pad: HasMmUartSignal, + Pad: HasMmUartSignal, + Pad: HasMmUartSignal, + Pad: HasMmUartSignal, +{ + const RTS: bool = { N1 % 4 == 2 || N2 % 4 == 2 || N3 % 4 == 2 || N4 % 4 == 2 }; + const CTS: bool = { N1 % 4 == 3 || N2 % 4 == 3 || N3 % 4 == 3 || N4 % 4 == 3 }; + const TXD: bool = { N1 % 4 == 0 || N2 % 4 == 0 || N3 % 4 == 0 || N4 % 4 == 0 }; + const RXD: bool = { N1 % 4 == 1 || N2 % 4 == 1 || N3 % 4 == 1 || N4 % 4 == 1 }; + type Split = (); + #[inline] + fn split(self, uart: T) -> Self::Split { + let _ = uart; + () + } +} diff --git a/bouffalo-hal/src/uart/register.rs b/bouffalo-hal/src/uart/register.rs new file mode 100644 index 0000000..4ce2226 --- /dev/null +++ b/bouffalo-hal/src/uart/register.rs @@ -0,0 +1,1017 @@ +use super::{BitOrder, Parity, StopBits, WordLength}; +use volatile_register::{RO, RW, WO}; + +/// Universal Asynchronous Receiver/Transmitter registers. +#[repr(C)] +pub struct RegisterBlock { + /// Transmit configuration. + pub transmit_config: RW, + /// Receive configuration. + pub receive_config: RW, + /// Bit-period in clocks. + pub bit_period: RW, + /// Data format configuration. + pub data_config: RW, + _reserved1: [u8; 0x10], + /// Interrupt state register. + pub interrupt_state: RO, + /// Interrupt mask register. + pub interrupt_mask: RW, + /// Clear interrupt register. + pub interrupt_clear: WO, + /// Interrupt enable register. + pub interrupt_enable: RW, + /// Bus state. + pub bus_state: RO, + _reserved2: [u8; 0x4c], + /// First-in first-out queue configuration 0. + pub fifo_config_0: RW, + /// First-in first-out queue configuration 1. + pub fifo_config_1: RW, + /// Write data into first-in first-out queue. + pub fifo_write: WO, + _reserved3: [u8; 0x3], + /// Read data from first-in first-out queue. + pub fifo_read: RO, +} + +/// Transmit configuration register. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[repr(transparent)] +pub struct TransmitConfig(u32); + +// TODO: inherent associated types is unstable, put aliases here as WAR +/// Register fields aliases, defining the bit field shift and bit length +mod transmit_config { + use crate::BitField; + + pub(crate) type Enable = BitField<1, 0, u32>; + pub(crate) type ParityEnable = BitField<1, 4, u32>; + pub(crate) type ParityMode = BitField<1, 5, u32>; + pub(crate) type WordLength = BitField<3, 8, u32>; +} + +impl TransmitConfig { + const CTS: u32 = 1 << 1; + const FREERUN: u32 = 1 << 2; + const LIN_TRANSMIT: u32 = 1 << 3; + const IR_TRANSMIT: u32 = 1 << 6; + const IR_INVERSE: u32 = 1 << 7; + const STOP_BITS: u32 = 0b11 << 11; + const LIN_BREAK_BITS: u32 = 0b111 << 13; + const TRANSFER_LENGTH: u32 = 0xffff << 16; + + /// Enable transmit. + #[inline] + pub const fn enable_txd(self) -> Self { + Self(transmit_config::Enable::from(self.0).enable()) + } + /// Disable transmit. + #[inline] + pub const fn disable_txd(self) -> Self { + Self(transmit_config::Enable::from(self.0).disable()) + } + /// Check if transmit is enabled. + #[inline] + pub const fn is_txd_enabled(self) -> bool { + transmit_config::Enable::from(self.0).is_enabled() + } + /// Enable Clear-to-Send signal. + #[inline] + pub const fn enable_cts(self) -> Self { + Self(self.0 | Self::CTS) + } + /// Disable Clear-to-Send signal. + #[inline] + pub const fn disable_cts(self) -> Self { + Self(self.0 & !Self::CTS) + } + /// Check if Clear-to-Send signal is enabled. + #[inline] + pub const fn is_cts_enabled(self) -> bool { + self.0 & Self::CTS != 0 + } + /// Enable free-run mode. + #[inline] + pub const fn enable_freerun(self) -> Self { + Self(self.0 | Self::FREERUN) + } + /// Disable free-run mode. + #[inline] + pub const fn disable_freerun(self) -> Self { + Self(self.0 & !Self::FREERUN) + } + /// Check if free-run mode is enabled. + #[inline] + pub const fn is_freerun_enabled(self) -> bool { + self.0 & Self::FREERUN != 0 + } + /// Enable LIN protocol transmission. + #[inline] + pub const fn enable_lin_transmit(self) -> Self { + Self(self.0 | Self::LIN_TRANSMIT) + } + /// Disable LIN protocol transmission. + #[inline] + pub const fn disable_lin_transmit(self) -> Self { + Self(self.0 & !Self::LIN_TRANSMIT) + } + /// Check if LIN protocol transmission is enabled. + #[inline] + pub const fn is_lin_transmit_enabled(self) -> bool { + self.0 & Self::LIN_TRANSMIT != 0 + } + /// Set parity check mode. + #[inline] + pub const fn set_parity(self, parity: Parity) -> Self { + let field_en = transmit_config::ParityEnable::from(self.0); + + match parity { + Parity::Even => { + let field_odd = transmit_config::ParityMode::from(field_en.enable()); + Self(field_odd.disable()) + } + Parity::Odd => { + let field_odd = transmit_config::ParityMode::from(field_en.enable()); + Self(field_odd.enable()) + } + Parity::None => Self(field_en.disable()), + } + } + /// Get parity check mode. + #[inline] + pub const fn parity(self) -> Parity { + let field_en = transmit_config::ParityEnable::from(self.0); + let field_odd = transmit_config::ParityMode::from(self.0); + + if !field_en.is_enabled() { + Parity::None + } else if !field_odd.is_enabled() { + Parity::Even + } else { + Parity::Odd + } + } + /// Enable IR transmission. + #[inline] + pub const fn enable_ir_transmit(self) -> Self { + Self(self.0 | Self::IR_TRANSMIT) + } + /// Disable IR transmission. + #[inline] + pub const fn disable_ir_transmit(self) -> Self { + Self(self.0 & !Self::IR_TRANSMIT) + } + /// Check if IR transmission is enabled. + #[inline] + pub const fn is_ir_transmit_enabled(self) -> bool { + self.0 & Self::IR_TRANSMIT != 0 + } + /// Invert transmit signal output in IR mode. + #[inline] + pub const fn enable_ir_inverse(self) -> Self { + Self(self.0 | Self::IR_INVERSE) + } + /// Don't invert transmit signal output in IR mode. + #[inline] + pub const fn disable_ir_inverse(self) -> Self { + Self(self.0 & !Self::IR_INVERSE) + } + /// Check if transmit signal output in IR mode is inverted. + #[inline] + pub const fn is_ir_inverse_enabled(self) -> bool { + self.0 & Self::IR_INVERSE != 0 + } + /// Set word length. + #[inline] + pub const fn set_word_length(self, val: WordLength) -> Self { + let field = transmit_config::WordLength::from(self.0); + let val = match val { + WordLength::Five => 4, + WordLength::Six => 5, + WordLength::Seven => 6, + WordLength::Eight => 7, + }; + Self(field.set(val)) + } + /// Get word length. + #[inline] + pub const fn word_length(self) -> WordLength { + let field = transmit_config::WordLength::from(self.0); + match field.get() { + 4 => WordLength::Five, + 5 => WordLength::Six, + 6 => WordLength::Seven, + 7 => WordLength::Eight, + _ => unreachable!(), + } + } + /// Set stop-bit configuration. + #[inline] + pub const fn set_stop_bits(self, val: StopBits) -> Self { + let val = match val { + StopBits::ZeroPointFive => 0, + StopBits::One => 1, + StopBits::OnePointFive => 2, + StopBits::Two => 3, + }; + Self(self.0 & !Self::STOP_BITS | val << 11) + } + /// Get stop-bit configuration. + #[inline] + pub const fn stop_bits(self) -> StopBits { + let val = (self.0 & Self::STOP_BITS) >> 11; + match val { + 0 => StopBits::ZeroPointFive, + 1 => StopBits::One, + 2 => StopBits::OnePointFive, + 3 => StopBits::Two, + _ => unreachable!(), + } + } + /// Set synchronize interval under LIN mode. + /// + /// # Parameters + /// + /// * `bits` - Interval in bits, the value should be 0 ~ 7. + #[inline] + pub const fn set_lin_break_bits(self, bits: u8) -> Self { + Self(self.0 & !Self::LIN_BREAK_BITS | (bits as u32) << 13) + } + /// Get synchronize interval under LIN mode. + /// + /// Return value is 0 ~ 7, represent in bits. + #[inline] + pub const fn lin_break_bits(self) -> u8 { + ((self.0 & Self::LIN_BREAK_BITS) >> 13) as u8 + } + /// Trigger interrupt when specified length of data is sent. + /// + /// NOTE: This bit is not valid when it is running under free-run mode. + #[inline] + pub const fn set_transfer_length(self, length: u16) -> Self { + Self(self.0 & !Self::TRANSFER_LENGTH | (length as u32) << 16) + } + /// Get the length of data that triggers the interrupt. + #[inline] + pub const fn transfer_length(self) -> u16 { + ((self.0 & Self::TRANSFER_LENGTH) >> 16) as u16 + } +} + +impl Default for TransmitConfig { + #[inline] + fn default() -> Self { + Self(0x0000_8f00) + } +} + +/// Receive configuration register. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[repr(transparent)] +pub struct ReceiveConfig(u32); + +mod receive_config { + use crate::BitField; + + pub(crate) type Enable = BitField<1, 0, u32>; + pub(crate) type ParityEnable = BitField<1, 4, u32>; + pub(crate) type ParityMode = BitField<1, 5, u32>; + pub(crate) type WordLength = BitField<3, 8, u32>; +} + +impl ReceiveConfig { + const ABR: u32 = 1 << 1; + const LIN_RECEIVE: u32 = 1 << 3; + const IR_RECEIVE: u32 = 1 << 6; + const IR_INVERSE: u32 = 1 << 7; + const DEGLICH: u32 = 1 << 11; + const DEGLICH_CYCLE: u32 = 0xf << 12; + const TRANSFER_LENGTH: u32 = 0xffff << 16; + + /// Enable receive. + #[inline] + pub const fn enable_rxd(self) -> Self { + Self(receive_config::Enable::from(self.0).enable()) + } + /// Disable receive. + #[inline] + pub const fn disable_rxd(self) -> Self { + Self(receive_config::Enable::from(self.0).disable()) + } + /// Check if receive is enabled. + #[inline] + pub const fn is_rxd_enabled(self) -> bool { + receive_config::Enable::from(self.0).is_enabled() + } + /// Enable auto baud rate detection. + #[inline] + pub const fn enable_auto_baudrate(self) -> Self { + Self(self.0 | Self::ABR) + } + /// Disable auto baud rate detection. + #[inline] + pub const fn disable_auto_baudrate(self) -> Self { + Self(self.0 & !Self::ABR) + } + /// Check if auto baud rate detection is enabled. + #[inline] + pub const fn is_auto_baudrate_enabled(self) -> bool { + self.0 & Self::ABR != 0 + } + /// Enable LIN protocol receive. + #[inline] + pub const fn enable_lin_receive(self) -> Self { + Self(self.0 | Self::LIN_RECEIVE) + } + /// Disable LIN protocol receive. + #[inline] + pub const fn disable_lin_receive(self) -> Self { + Self(self.0 & !Self::LIN_RECEIVE) + } + /// Check if LIN protocol receive is enabled. + #[inline] + pub const fn is_lin_receive_enabled(self) -> bool { + self.0 & Self::LIN_RECEIVE != 0 + } + /// Set parity check mode. + #[inline] + pub const fn set_parity(self, parity: Parity) -> Self { + let field_en = receive_config::ParityEnable::from(self.0); + + match parity { + Parity::Even => { + let field_odd = receive_config::ParityMode::from(field_en.enable()); + Self(field_odd.disable()) + } + Parity::Odd => { + let field_odd = receive_config::ParityMode::from(field_en.enable()); + Self(field_odd.enable()) + } + Parity::None => Self(field_en.disable()), + } + } + /// Get parity check mode. + #[inline] + pub const fn parity(self) -> Parity { + let field_en = receive_config::ParityEnable::from(self.0); + let field_odd = receive_config::ParityMode::from(self.0); + + if !field_en.is_enabled() { + Parity::None + } else if !field_odd.is_enabled() { + Parity::Even + } else { + Parity::Odd + } + } + /// Enable IR receive. + #[inline] + pub const fn enable_ir_receive(self) -> Self { + Self(self.0 | Self::IR_RECEIVE) + } + /// Disable IR receive. + #[inline] + pub const fn disable_ir_receive(self) -> Self { + Self(self.0 & !Self::IR_RECEIVE) + } + /// Check if IR receive is enabled. + #[inline] + pub const fn is_ir_receive_enabled(self) -> bool { + self.0 & Self::IR_RECEIVE != 0 + } + /// Invert receive signal output in IR mode. + #[inline] + pub const fn enable_ir_inverse(self) -> Self { + Self(self.0 | Self::IR_INVERSE) + } + /// Don't invert receive signal output in IR mode. + #[inline] + pub const fn disable_ir_inverse(self) -> Self { + Self(self.0 & !Self::IR_INVERSE) + } + /// Check if receive signal output in IR mode is inverted. + #[inline] + pub const fn is_ir_inverse_enabled(self) -> bool { + self.0 & Self::IR_INVERSE != 0 + } + /// Set word length. + #[inline] + pub const fn set_word_length(self, val: WordLength) -> Self { + let field = receive_config::WordLength::from(self.0); + let val = match val { + WordLength::Five => 4, + WordLength::Six => 5, + WordLength::Seven => 6, + WordLength::Eight => 7, + }; + Self(field.set(val)) + } + /// Get word length. + #[inline] + pub const fn word_length(self) -> WordLength { + let field = receive_config::WordLength::from(self.0); + match field.get() { + 4 => WordLength::Five, + 5 => WordLength::Six, + 6 => WordLength::Seven, + 7 => WordLength::Eight, + _ => unreachable!(), + } + } + /// Enable de-glitch function. + #[inline] + pub const fn enable_deglitch(self) -> Self { + Self(self.0 | Self::DEGLICH) + } + /// Disable de-glitch function. + #[inline] + pub const fn disable_deglitch(self) -> Self { + Self(self.0 & !Self::DEGLICH) + } + /// Check if de-glitch function is enabled. + #[inline] + pub const fn is_deglitch_enabled(self) -> bool { + self.0 & Self::DEGLICH != 0 + } + /// Set de-glich function cycle count. + #[inline] + pub const fn set_deglitch_cycles(self, val: u8) -> Self { + Self(self.0 & !Self::DEGLICH_CYCLE | ((val as u32) << 12)) + } + /// Get de-glich function cycle count. + #[inline] + pub const fn deglitch_cycles(self) -> u8 { + ((self.0 & Self::DEGLICH_CYCLE) >> 12) as u8 + } + /// Set the length of data that triggers the interrupt. + #[inline] + pub const fn set_transfer_length(self, length: u16) -> Self { + Self(self.0 & !Self::TRANSFER_LENGTH | (length as u32) << 16) + } + /// Get the length of data that triggers the interrupt. + #[inline] + pub const fn transfer_length(self) -> u16 { + ((self.0 & Self::TRANSFER_LENGTH) >> 16) as u16 + } +} + +impl Default for ReceiveConfig { + #[inline] + fn default() -> Self { + Self(0x0000_0700) + } +} + +/// Bit period configuration register. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[repr(transparent)] +pub struct BitPeriod(u32); + +impl BitPeriod { + const TRANSMIT: u32 = 0xffff; + const RECEIVE: u32 = 0xffff << 16; + + /// Set transmit time interval. + #[inline] + pub const fn set_transmit_time_interval(self, val: u16) -> Self { + Self(self.0 & !Self::TRANSMIT | val as u32) + } + /// Get transmit time interval. + #[inline] + pub const fn transmit_time_interval(self) -> u16 { + (self.0 & Self::TRANSMIT) as u16 + } + /// Set receive time interval. + #[inline] + pub const fn set_receive_time_interval(self, val: u16) -> Self { + Self(self.0 & !Self::RECEIVE | ((val as u32) << 16)) + } + /// Get receive time interval. + #[inline] + pub const fn receive_time_interval(self) -> u16 { + ((self.0 & Self::RECEIVE) >> 16) as u16 + } +} + +impl Default for BitPeriod { + #[inline] + fn default() -> Self { + Self(0x00ff_00ff) + } +} + +/// Data configuration register. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[repr(transparent)] +pub struct DataConfig(u32); + +impl DataConfig { + const BIT_ORDER: u32 = 1 << 0; + + /// Set the bit order in each data word. + #[inline] + pub const fn set_bit_order(self, val: BitOrder) -> Self { + match val { + BitOrder::LsbFirst => Self(self.0 & !Self::BIT_ORDER), + BitOrder::MsbFirst => Self(self.0 | Self::BIT_ORDER), + } + } + /// Get the bit order in each data word. + #[inline] + pub const fn bit_order(self) -> BitOrder { + if self.0 & Self::BIT_ORDER == 0 { + BitOrder::LsbFirst + } else { + BitOrder::MsbFirst + } + } +} + +impl Default for DataConfig { + #[inline] + fn default() -> Self { + Self(0x0000_0000) + } +} + +/// Interrupt event. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[repr(u8)] +pub enum Interrupt { + TransmitEnd = 0, + ReceiveEnd = 1, + TransmitFifoReady = 2, + ReceiveFifoReady = 3, + ReceiveTimeout = 4, + ReceiveParityError = 5, + TransmitFifoError = 6, + ReceiveFifoError = 7, + ReceiveSyncError = 8, + ReceiveByteCountReached = 9, + ReceiveAutoBaudrateByStartBit = 10, + ReceiveAutoBaudrateByFiveFive = 11, +} + +/// Interrupt state register. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] +#[repr(transparent)] +pub struct InterruptState(u32); + +impl InterruptState { + /// Check if there is an interrupt flag. + #[inline] + pub const fn has_interrupt(self, val: Interrupt) -> bool { + (self.0 & (1 << (val as u32))) != 0 + } +} + +/// Interrupt mask register. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] +#[repr(transparent)] +pub struct InterruptMask(u32); + +impl InterruptMask { + /// Set interrupt mask. + #[inline] + pub const fn mask_interrupt(self, val: Interrupt) -> Self { + Self(self.0 | (1 << (val as u32))) + } + /// Clear interrupt mask. + #[inline] + pub const fn unmask_interrupt(self, val: Interrupt) -> Self { + Self(self.0 & !(1 << (val as u32))) + } + /// Check if interrupt is masked. + #[inline] + pub const fn is_interrupt_masked(self, val: Interrupt) -> bool { + (self.0 & (1 << (val as u32))) != 0 + } +} + +/// Interrupt clear register. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] +#[repr(transparent)] +pub struct InterruptClear(u32); + +impl InterruptClear { + /// Clear interrupt. + #[inline] + pub const fn clear_interrupt(self, val: Interrupt) -> Self { + Self(self.0 | (1 << (val as u32))) + } +} + +/// Interrupt enable register. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] +#[repr(transparent)] +pub struct InterruptEnable(u32); + +impl InterruptEnable { + /// Enable interrupt. + #[inline] + pub const fn enable_interrupt(self, val: Interrupt) -> Self { + Self(self.0 | (1 << (val as u32))) + } + /// Disable interrupt. + #[inline] + pub const fn disable_interrupt(self, val: Interrupt) -> Self { + Self(self.0 & !(1 << (val as u32))) + } + /// Check if interrupt is enabled. + #[inline] + pub const fn is_interrupt_enabled(self, val: Interrupt) -> bool { + (self.0 & (1 << (val as u32))) != 0 + } +} + +/// Bus state register. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] +#[repr(transparent)] +pub struct BusState(u32); + +impl BusState { + const TRANSMIT_BUSY: u32 = 1 << 0; + const RECEIVE_BUSY: u32 = 1 << 1; + + /// Get if UART transmit bus is busy. + #[inline] + pub const fn transmit_busy(self) -> bool { + self.0 & Self::TRANSMIT_BUSY != 0 + } + /// Get if UART receive bus is busy. + #[inline] + pub const fn receive_busy(self) -> bool { + self.0 & Self::RECEIVE_BUSY != 0 + } +} + +/// First-in first-out queue configuration 0. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] +#[repr(transparent)] +pub struct FifoConfig0(u32); + +impl FifoConfig0 { + const TRANSMIT_DMA_ENABLE: u32 = 1 << 0; + const RECEIVE_DMA_ENABLE: u32 = 1 << 1; + const TRANSMIT_FIFO_CLEAR: u32 = 1 << 2; + const RECEIVE_FIFO_CLEAR: u32 = 1 << 3; + const TRANSMIT_FIFO_OVERFLOW: u32 = 1 << 4; + const TRANSMIT_FIFO_UNDERFLOW: u32 = 1 << 5; + const RECEIVE_FIFO_OVERFLOW: u32 = 1 << 6; + const RECEIVE_FIFO_UNDERFLOW: u32 = 1 << 7; + + /// Enable transmit DMA. + #[inline] + pub const fn enable_transmit_dma(self) -> Self { + Self(self.0 | Self::TRANSMIT_DMA_ENABLE) + } + /// Disable transmit DMA. + #[inline] + pub const fn disable_transmit_dma(self) -> Self { + Self(self.0 & !Self::TRANSMIT_DMA_ENABLE) + } + /// Check if transmit DMA is enabled. + #[inline] + pub const fn is_transmit_dma_enabled(self) -> bool { + self.0 & Self::TRANSMIT_DMA_ENABLE != 0 + } + /// Enable receive DMA. + #[inline] + pub const fn enable_receive_dma(self) -> Self { + Self(self.0 | Self::RECEIVE_DMA_ENABLE) + } + /// Disable receive DMA. + #[inline] + pub const fn disable_receive_dma(self) -> Self { + Self(self.0 & !Self::RECEIVE_DMA_ENABLE) + } + /// Check if receive DMA is enabled. + #[inline] + pub const fn is_receive_dma_enabled(self) -> bool { + self.0 & Self::RECEIVE_DMA_ENABLE != 0 + } + /// Clear transmit FIFO. + #[inline] + pub const fn clear_transmit_fifo(self) -> Self { + Self(self.0 | Self::TRANSMIT_FIFO_CLEAR) + } + /// Clear receive FIFO. + #[inline] + pub const fn clear_receive_fifo(self) -> Self { + Self(self.0 | Self::RECEIVE_FIFO_CLEAR) + } + /// Check if transmit FIFO is overflow. + #[inline] + pub const fn transmit_fifo_overflow(self) -> bool { + self.0 & Self::TRANSMIT_FIFO_OVERFLOW != 0 + } + /// Check if transmit FIFO is underflow. + #[inline] + pub const fn transmit_fifo_underflow(self) -> bool { + self.0 & Self::TRANSMIT_FIFO_UNDERFLOW != 0 + } + /// Check if receive FIFO is overflow. + #[inline] + pub const fn receive_fifo_overflow(self) -> bool { + self.0 & Self::RECEIVE_FIFO_OVERFLOW != 0 + } + /// Check if receive FIFO is underflow. + #[inline] + pub const fn receive_fifo_underflow(self) -> bool { + self.0 & Self::RECEIVE_FIFO_UNDERFLOW != 0 + } +} + +/// First-in first-out queue configuration 1. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] +#[repr(transparent)] +pub struct FifoConfig1(u32); + +impl FifoConfig1 { + const TRANSMIT_COUNT: u32 = 0x3f; + const RECEIVE_COUNT: u32 = 0x3f << 8; + const TRANSMIT_THRESHOLD: u32 = 0x1f << 16; + const RECEIVE_THRESHOLD: u32 = 0x1f << 24; + + /// Get number of empty spaces remained in transmit FIFO queue. + #[inline] + pub const fn transmit_available_bytes(self) -> u8 { + (self.0 & Self::TRANSMIT_COUNT) as u8 + } + /// Get number of available bytes received in receive FIFO queue. + #[inline] + pub const fn receive_available_bytes(self) -> u8 { + ((self.0 & Self::RECEIVE_COUNT) >> 8) as u8 + } + /// Set transmit FIFO threshold. + #[inline] + pub const fn set_transmit_threshold(self, val: u8) -> Self { + Self(self.0 & !Self::TRANSMIT_THRESHOLD | ((val as u32) << 16)) + } + /// Get transmit FIFO threshold. + #[inline] + pub const fn transmit_threshold(self) -> u8 { + ((self.0 & Self::TRANSMIT_THRESHOLD) >> 16) as u8 + } + /// Set receive FIFO threshold. + #[inline] + pub const fn set_receive_threshold(self, val: u8) -> Self { + Self(self.0 & !Self::RECEIVE_THRESHOLD | ((val as u32) << 24)) + } + /// Get receive FIFO threshold. + #[inline] + pub const fn receive_threshold(self) -> u8 { + ((self.0 & Self::RECEIVE_THRESHOLD) >> 24) as u8 + } +} + +#[cfg(test)] +mod tests { + use crate::uart::{StopBits, WordLength}; + + use super::{BitPeriod, Parity, ReceiveConfig, RegisterBlock, TransmitConfig}; + use memoffset::offset_of; + + #[test] + fn struct_register_block_offset() { + assert_eq!(offset_of!(RegisterBlock, transmit_config), 0x0); + assert_eq!(offset_of!(RegisterBlock, receive_config), 0x4); + assert_eq!(offset_of!(RegisterBlock, bit_period), 0x08); + assert_eq!(offset_of!(RegisterBlock, data_config), 0x0c); + assert_eq!(offset_of!(RegisterBlock, interrupt_state), 0x20); + assert_eq!(offset_of!(RegisterBlock, interrupt_mask), 0x24); + assert_eq!(offset_of!(RegisterBlock, interrupt_clear), 0x28); + assert_eq!(offset_of!(RegisterBlock, interrupt_enable), 0x2c); + assert_eq!(offset_of!(RegisterBlock, bus_state), 0x30); + assert_eq!(offset_of!(RegisterBlock, fifo_config_0), 0x80); + assert_eq!(offset_of!(RegisterBlock, fifo_config_1), 0x84); + assert_eq!(offset_of!(RegisterBlock, fifo_write), 0x88); + assert_eq!(offset_of!(RegisterBlock, fifo_read), 0x8c); + } + + #[test] + fn struct_transmit_config_functions() { + let mut val: TransmitConfig = TransmitConfig(0x0); + + val = val.enable_txd(); + assert_eq!(val.0, 0x00000001); + assert!(val.is_txd_enabled()); + val = val.disable_txd(); + assert_eq!(val.0, 0x00000000); + assert!(!val.is_txd_enabled()); + + val = val.enable_cts(); + assert_eq!(val.0, 0x00000002); + assert!(val.is_cts_enabled()); + val = val.disable_cts(); + assert_eq!(val.0, 0x00000000); + assert!(!val.is_cts_enabled()); + + val = val.enable_freerun(); + assert_eq!(val.0, 0x00000004); + assert!(val.is_freerun_enabled()); + val = val.disable_freerun(); + assert_eq!(val.0, 0x00000000); + assert!(!val.is_freerun_enabled()); + + val = val.enable_lin_transmit(); + assert_eq!(val.0, 0x00000008); + assert!(val.is_lin_transmit_enabled()); + val = val.disable_lin_transmit(); + assert_eq!(val.0, 0x00000000); + assert!(!val.is_lin_transmit_enabled()); + + val = val.set_parity(Parity::Even); + assert_eq!(val.0, 0x00000010); + assert_eq!(val.parity(), Parity::Even); + val = val.set_parity(Parity::Odd); + assert_eq!(val.0, 0x00000030); + assert_eq!(val.parity(), Parity::Odd); + val = val.set_parity(Parity::None); + assert_eq!(val.0 & 0x00000010, 0x00000000); + assert_eq!(val.parity(), Parity::None); + + val = TransmitConfig(0x0); + + val = val.enable_ir_transmit(); + assert_eq!(val.0, 0x00000040); + assert!(val.is_ir_transmit_enabled()); + val = val.disable_ir_transmit(); + assert_eq!(val.0, 0x00000000); + assert!(!val.is_ir_transmit_enabled()); + + val = val.enable_ir_inverse(); + assert_eq!(val.0, 0x00000080); + assert!(val.is_ir_inverse_enabled()); + val = val.disable_ir_inverse(); + assert_eq!(val.0, 0x00000000); + assert!(!val.is_ir_inverse_enabled()); + + val = val.set_word_length(WordLength::Five); + assert_eq!(val.0, 0x00000400); + assert_eq!(val.word_length(), WordLength::Five); + val = val.set_word_length(WordLength::Six); + assert_eq!(val.0, 0x00000500); + assert_eq!(val.word_length(), WordLength::Six); + val = val.set_word_length(WordLength::Seven); + assert_eq!(val.0, 0x00000600); + assert_eq!(val.word_length(), WordLength::Seven); + val = val.set_word_length(WordLength::Eight); + assert_eq!(val.0, 0x00000700); + assert_eq!(val.word_length(), WordLength::Eight); + + val = TransmitConfig(0x0); + + val = val.set_stop_bits(StopBits::Two); + assert_eq!(val.0, 0x00001800); + assert_eq!(val.stop_bits(), StopBits::Two); + val = val.set_stop_bits(StopBits::OnePointFive); + assert_eq!(val.0, 0x00001000); + assert_eq!(val.stop_bits(), StopBits::OnePointFive); + val = val.set_stop_bits(StopBits::One); + assert_eq!(val.0, 0x00000800); + assert_eq!(val.stop_bits(), StopBits::One); + val = val.set_stop_bits(StopBits::ZeroPointFive); + assert_eq!(val.0, 0x00000000); + assert_eq!(val.stop_bits(), StopBits::ZeroPointFive); + + for num in 0..=7 { + val = val.set_lin_break_bits(num); + assert_eq!(val.0, (num as u32) << 13); + assert_eq!(val.lin_break_bits(), num); + } + + val = TransmitConfig(0x0); + + for length in [0x0000, 0x1234, 0xabcd, 0xffff] { + val = val.set_transfer_length(length); + assert_eq!(val.0, (length as u32) << 16); + assert_eq!(val.transfer_length(), length); + } + + let default = TransmitConfig::default(); + assert_eq!(default.transfer_length(), 0); + assert_eq!(default.lin_break_bits(), 4); + assert_eq!(default.stop_bits(), StopBits::One); + assert_eq!(default.word_length(), WordLength::Eight); + assert!(!default.is_ir_inverse_enabled()); + assert!(!default.is_ir_transmit_enabled()); + assert_eq!(default.parity(), Parity::None); + assert!(!default.is_lin_transmit_enabled()); + assert!(!default.is_freerun_enabled()); + assert!(!default.is_cts_enabled()); + assert!(!default.is_txd_enabled()); + } + + #[test] + fn struct_bit_period_functions() { + let mut val: BitPeriod = BitPeriod(0x0); + + for trans in [0x0000, 0x1037, 0xabcd, 0xffff] { + val = val.set_transmit_time_interval(trans); + assert_eq!(val.0, trans as u32); + assert_eq!(val.transmit_time_interval(), trans); + } + + val = BitPeriod(0x0); + + for recv in [0x0000, 0x1037, 0xabcd, 0xffff] { + val = val.set_receive_time_interval(recv); + assert_eq!(val.0, (recv as u32) << 16); + assert_eq!(val.receive_time_interval(), recv); + } + + // TODO: use getter functions to check default value for BitPeriod + } + + #[test] + fn struct_receive_config_functions() { + let mut val: ReceiveConfig = ReceiveConfig(0x0); + + val = val.enable_rxd(); + assert_eq!(val.0, 0x00000001); + assert!(val.is_rxd_enabled()); + val = val.disable_rxd(); + assert_eq!(val.0, 0x00000000); + assert!(!val.is_rxd_enabled()); + + val = val.enable_auto_baudrate(); + assert_eq!(val.0, 0x00000002); + assert!(val.is_auto_baudrate_enabled()); + val = val.disable_auto_baudrate(); + assert_eq!(val.0, 0x00000000); + assert!(!val.is_auto_baudrate_enabled()); + + val = val.enable_lin_receive(); + assert_eq!(val.0, 0x00000008); + assert!(val.is_lin_receive_enabled()); + val = val.disable_lin_receive(); + assert_eq!(val.0, 0x00000000); + assert!(!val.is_lin_receive_enabled()); + + val = val.set_parity(Parity::Even); + assert_eq!(val.0, 0x00000010); + assert_eq!(val.parity(), Parity::Even); + val = val.set_parity(Parity::Odd); + assert_eq!(val.0, 0x00000030); + assert_eq!(val.parity(), Parity::Odd); + val = val.set_parity(Parity::None); + assert_eq!(val.0 & 0x00000010, 0x00000000); + assert_eq!(val.parity(), Parity::None); + + val = ReceiveConfig(0x0); + + val = val.enable_ir_receive(); + assert_eq!(val.0, 0x00000040); + assert!(val.is_ir_receive_enabled()); + val = val.disable_ir_receive(); + assert_eq!(val.0, 0x00000000); + assert!(!val.is_ir_receive_enabled()); + + val = val.enable_ir_inverse(); + assert_eq!(val.0, 0x00000080); + assert!(val.is_ir_inverse_enabled()); + val = val.disable_ir_inverse(); + assert_eq!(val.0, 0x00000000); + assert!(!val.is_ir_inverse_enabled()); + + val = val.set_word_length(WordLength::Five); + assert_eq!(val.0, 0x00000400); + assert_eq!(val.word_length(), WordLength::Five); + val = val.set_word_length(WordLength::Six); + assert_eq!(val.0, 0x00000500); + assert_eq!(val.word_length(), WordLength::Six); + val = val.set_word_length(WordLength::Seven); + assert_eq!(val.0, 0x00000600); + assert_eq!(val.word_length(), WordLength::Seven); + val = val.set_word_length(WordLength::Eight); + assert_eq!(val.0, 0x00000700); + assert_eq!(val.word_length(), WordLength::Eight); + + val = ReceiveConfig(0x0); + + val = val.enable_deglitch(); + assert_eq!(val.0, 0x00000800); + assert!(val.is_deglitch_enabled()); + val = val.disable_deglitch(); + assert_eq!(val.0, 0x00000000); + assert!(!val.is_deglitch_enabled()); + + for num in 0..=7 { + val = val.set_deglitch_cycles(num); + assert_eq!(val.0, (num as u32) << 12); + assert_eq!(val.deglitch_cycles(), num); + } + + val = ReceiveConfig(0x0); + + for length in [0x0000, 0x1234, 0xabcd, 0xffff] { + val = val.set_transfer_length(length); + assert_eq!(val.0, (length as u32) << 16); + assert_eq!(val.transfer_length(), length); + } + } + + // TODO: use getter functions to check default value for ReceiveConfig +} diff --git a/examples/multicore/multicore-demo/dsp/src/main.rs b/examples/multicore/multicore-demo/dsp/src/main.rs index 8d3d372..38d1705 100644 --- a/examples/multicore/multicore-demo/dsp/src/main.rs +++ b/examples/multicore/multicore-demo/dsp/src/main.rs @@ -12,9 +12,10 @@ fn main(p: Peripherals, c: Clocks) -> ! { let rx = p.gpio.io15.into_uart(); let sig2 = p.uart_muxes.sig2.into_transmit::<0>(); let sig3 = p.uart_muxes.sig3.into_receive::<0>(); + let pads = ((tx, sig2), (rx, sig3)); let config = Config::default().set_baudrate(2000000.Bd()); - let mut serial = p.uart0.freerun(config, ((tx, sig2), (rx, sig3)), &c); + let mut serial = p.uart0.freerun(config, pads, &c).unwrap(); loop { writeln!( diff --git a/examples/peripherals/i2c-demo/src/main.rs b/examples/peripherals/i2c-demo/src/main.rs index 9c1e8b1..62c92e5 100644 --- a/examples/peripherals/i2c-demo/src/main.rs +++ b/examples/peripherals/i2c-demo/src/main.rs @@ -15,9 +15,10 @@ fn main(p: Peripherals, c: Clocks) -> ! { let rx = p.gpio.io15.into_uart(); let sig2 = p.uart_muxes.sig2.into_transmit::<0>(); let sig3 = p.uart_muxes.sig3.into_receive::<0>(); + let pads = ((tx, sig2), (rx, sig3)); let config = Config::default().set_baudrate(2000000.Bd()); - let mut serial = p.uart0.freerun(config, ((tx, sig2), (rx, sig3)), &c); + let mut serial = p.uart0.freerun(config, pads, &c).unwrap(); let mut led = p.gpio.io8.into_floating_output(); let scl = p.gpio.io6.into_i2c::<2>(); diff --git a/examples/peripherals/lz4d-demo/src/main.rs b/examples/peripherals/lz4d-demo/src/main.rs index cb14dcd..6c97174 100644 --- a/examples/peripherals/lz4d-demo/src/main.rs +++ b/examples/peripherals/lz4d-demo/src/main.rs @@ -19,9 +19,8 @@ fn main(p: Peripherals, c: Clocks) -> ! { let pads = ((tx, sig2), (rx, sig3)); let config = Config::default().set_baudrate(2000000.Bd()); - let mut serial = p.uart0.freerun(config, pads, &c); + let mut serial = p.uart0.freerun(config, pads, &c).unwrap(); - writeln!(serial, "Hardware accelerated LZ4 decompression example.").ok(); unsafe { p.glb.clock_config_1.modify(|v| v.enable_lz4d()) }; let decompress = p.lz4d.decompress( diff --git a/examples/peripherals/psram-demo/src/main.rs b/examples/peripherals/psram-demo/src/main.rs index 460e969..04a389a 100644 --- a/examples/peripherals/psram-demo/src/main.rs +++ b/examples/peripherals/psram-demo/src/main.rs @@ -23,8 +23,11 @@ fn main(p: Peripherals, c: Clocks) -> ! { let rx = p.gpio.io15.into_uart(); let sig2 = p.uart_muxes.sig2.into_transmit::<0>(); let sig3 = p.uart_muxes.sig3.into_receive::<0>(); + let pads = ((tx, sig2), (rx, sig3)); + let config = Config::default().set_baudrate(2000000.Bd()); - let mut serial = p.uart0.freerun(config, ((tx, sig2), (rx, sig3)), &c); + let mut serial = p.uart0.freerun(config, pads, &c).unwrap(); + writeln!(serial, "Welcome to psram-demo🦀!").ok(); uhs_psram_init(); diff --git a/examples/peripherals/sdcard-demo/src/main.rs b/examples/peripherals/sdcard-demo/src/main.rs index e78797c..bfe842e 100644 --- a/examples/peripherals/sdcard-demo/src/main.rs +++ b/examples/peripherals/sdcard-demo/src/main.rs @@ -23,9 +23,10 @@ fn main(p: Peripherals, c: Clocks) -> ! { let rx = p.gpio.io15.into_uart(); let sig2 = p.uart_muxes.sig2.into_transmit::<0>(); let sig3 = p.uart_muxes.sig3.into_receive::<0>(); + let pads = ((tx, sig2), (rx, sig3)); let config = Config::default().set_baudrate(2000000.Bd()); - let mut serial = p.uart0.freerun(config, ((tx, sig2), (rx, sig3)), &c); + let mut serial = p.uart0.freerun(config, pads, &c).unwrap(); writeln!(serial, "Hello world!").ok(); let mut led = p.gpio.io8.into_floating_output(); diff --git a/examples/peripherals/sdcard-gpt-demo/src/main.rs b/examples/peripherals/sdcard-gpt-demo/src/main.rs index 5b26cb5..038662d 100644 --- a/examples/peripherals/sdcard-gpt-demo/src/main.rs +++ b/examples/peripherals/sdcard-gpt-demo/src/main.rs @@ -193,9 +193,10 @@ fn main(p: Peripherals, c: Clocks) -> ! { let rx = p.gpio.io15.into_uart(); let sig2 = p.uart_muxes.sig2.into_transmit::<0>(); let sig3 = p.uart_muxes.sig3.into_receive::<0>(); + let pads = ((tx, sig2), (rx, sig3)); let config = Config::default().set_baudrate(2000000.Bd()); - let mut serial = p.uart0.freerun(config, ((tx, sig2), (rx, sig3)), &c); + let mut serial = p.uart0.freerun(config, pads, &c).unwrap(); writeln!(serial, "Hello world!").ok(); let mut led = p.gpio.io8.into_floating_output(); diff --git a/examples/peripherals/uart-async-demo/Cargo.toml b/examples/peripherals/uart-async-demo/Cargo.toml new file mode 100644 index 0000000..36b4498 --- /dev/null +++ b/examples/peripherals/uart-async-demo/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "uart-async-demo" +version = "0.1.0" +edition = "2021" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +bouffalo-hal = { path = "../../../bouffalo-hal", features = ["bl808"] } +bouffalo-rt = { path = "../../../bouffalo-rt", features = ["bl808-dsp"] } +panic-halt = "0.2.0" +embedded-time = "0.12.1" +riscv = "0.11.1" + +[[bin]] +name = "uart-async-demo" +test = false diff --git a/examples/peripherals/uart-async-demo/README.md b/examples/peripherals/uart-async-demo/README.md new file mode 100644 index 0000000..0a61727 --- /dev/null +++ b/examples/peripherals/uart-async-demo/README.md @@ -0,0 +1,8 @@ +Async/await UART peripheral demo + +Build this example with: + +``` +rustup target install riscv64imac-unknown-none-elf +cargo build --target riscv64imac-unknown-none-elf --release -p uart-async-demo +``` diff --git a/examples/peripherals/uart-async-demo/build.rs b/examples/peripherals/uart-async-demo/build.rs new file mode 100644 index 0000000..569839a --- /dev/null +++ b/examples/peripherals/uart-async-demo/build.rs @@ -0,0 +1,3 @@ +fn main() { + println!("cargo:rustc-link-arg=-Tbouffalo-rt.ld"); +} diff --git a/examples/peripherals/uart-async-demo/src/main.rs b/examples/peripherals/uart-async-demo/src/main.rs new file mode 100644 index 0000000..622a898 --- /dev/null +++ b/examples/peripherals/uart-async-demo/src/main.rs @@ -0,0 +1,55 @@ +#![feature(noop_waker)] +#![no_std] +#![no_main] + +use bouffalo_hal::{ + prelude::*, + uart::{Config, SerialState}, +}; +use bouffalo_rt::{entry, interrupt, Clocks, Peripherals}; +use embedded_time::rate::*; +use panic_halt as _; + +async fn async_main(p: Peripherals, c: Clocks) { + let tx = p.gpio.io16.into_mm_uart(); + let rx = p.gpio.io17.into_mm_uart(); + + let config = Config::default().set_baudrate(2000000.Bd()); + let mut serial = p + .uart3 + .with_interrupt(config, (tx, rx), &c, &UART3_STATE) + .unwrap(); + + serial + .write_all(b"Hello world from async/await uart demo!") + .await; +} + +static UART3_STATE: SerialState = SerialState::new(); + +#[interrupt] +fn uart3() { + UART3_STATE.on_interrupt(); +} + +// ---- Async/await runtime environment ---- + +#[entry] +fn main(p: Peripherals, c: Clocks) -> ! { + use core::{ + future::Future, + task::{Context, Poll, Waker}, + }; + let mut fut = core::pin::pin!(async_main(p, c)); + let waker = Waker::noop(); + let mut ctx = Context::from_waker(waker); + loop { + match fut.as_mut().poll(&mut ctx) { + Poll::Ready(_) => break, + Poll::Pending => riscv::asm::wfi(), + } + } + loop { + riscv::asm::wfi(); + } +} diff --git a/examples/peripherals/uart-cli-demo/src/main.rs b/examples/peripherals/uart-cli-demo/src/main.rs index 61e9738..ee09bd7 100644 --- a/examples/peripherals/uart-cli-demo/src/main.rs +++ b/examples/peripherals/uart-cli-demo/src/main.rs @@ -34,9 +34,10 @@ fn main(p: Peripherals, c: Clocks) -> ! { let rx = p.gpio.io15.into_uart(); let sig2 = p.uart_muxes.sig2.into_transmit::<0>(); let sig3 = p.uart_muxes.sig3.into_receive::<0>(); + let pads = ((tx, sig2), (rx, sig3)); let config = Config::default().set_baudrate(2000000.Bd()); - let serial = p.uart0.freerun(config, ((tx, sig2), (rx, sig3)), &c); + let serial = p.uart0.freerun(config, pads, &c).unwrap(); let (mut tx, mut rx) = serial.split(); diff --git a/examples/peripherals/uart-demo/src/main.rs b/examples/peripherals/uart-demo/src/main.rs index 70f9ea0..2a7af33 100644 --- a/examples/peripherals/uart-demo/src/main.rs +++ b/examples/peripherals/uart-demo/src/main.rs @@ -12,9 +12,10 @@ fn main(p: Peripherals, c: Clocks) -> ! { let rx = p.gpio.io15.into_uart(); let sig2 = p.uart_muxes.sig2.into_transmit::<0>(); let sig3 = p.uart_muxes.sig3.into_receive::<0>(); + let pads = ((tx, sig2), (rx, sig3)); let config = Config::default().set_baudrate(2000000.Bd()); - let mut serial = p.uart0.freerun(config, ((tx, sig2), (rx, sig3)), &c); + let mut serial = p.uart0.freerun(config, pads, &c).unwrap(); let mut led = p.gpio.io8.into_floating_output(); let mut led_state = PinState::Low; From dc79c0d9653c9ea037d4f7bde7519767da23f1ad Mon Sep 17 00:00:00 2001 From: Zhouqi Jiang Date: Fri, 29 Nov 2024 16:23:48 +0800 Subject: [PATCH 2/5] rt: introduce DspInterrupt structure which implements plic::InterruptSource Implement interrupt enable, initialize RISC-V interrupt in uart-async-demo example Signed-off-by: Zhouqi Jiang --- bouffalo-rt/src/soc/bl808.rs | 27 ++++++++++++++++--- .../peripherals/uart-async-demo/src/main.rs | 15 +++++++++-- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/bouffalo-rt/src/soc/bl808.rs b/bouffalo-rt/src/soc/bl808.rs index e02763a..3528d6e 100644 --- a/bouffalo-rt/src/soc/bl808.rs +++ b/bouffalo-rt/src/soc/bl808.rs @@ -304,7 +304,7 @@ fn rust_bl808_dsp_machine_external(_tf: &mut TrapFrame) { if idx >= 16 && idx < 16 + 67 { unsafe { (D0_INTERRUPT_HANDLERS[idx - 16])() }; } - plic.complete(D0Machine, PlicSource(source)); + plic.complete(D0Machine, RawPlicSource(source)); } } @@ -461,16 +461,37 @@ impl plic::HartContext for D0Machine { } #[cfg(all(feature = "bl808-dsp", target_arch = "riscv64"))] -struct PlicSource(core::num::NonZeroU32); +struct RawPlicSource(core::num::NonZeroU32); #[cfg(all(feature = "bl808-dsp", target_arch = "riscv64"))] -impl plic::InterruptSource for PlicSource { +impl plic::InterruptSource for RawPlicSource { #[inline] fn id(self) -> core::num::NonZeroU32 { self.0 } } +/// DSP core PLIC interrupt source. +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] +pub enum DspInterrupt { + /// UART3 interrupt. + UART3 = 16 + 4, + // TODO other interrupts. + // /// I2C2 interrupt. + // I2C2 = 16 + 5, + // ... +} + +impl plic::InterruptSource for DspInterrupt { + #[inline] + fn id(self) -> core::num::NonZeroU32 { + core::num::NonZeroU32::new(self as u32).unwrap() + } +} + +// TODO: MCU and Low-Power core interrupt source. +// pub enum McuLpInterrupt { ... } + /// Trap stack frame. #[repr(C)] pub struct TrapFrame { diff --git a/examples/peripherals/uart-async-demo/src/main.rs b/examples/peripherals/uart-async-demo/src/main.rs index 622a898..1fbfe9e 100644 --- a/examples/peripherals/uart-async-demo/src/main.rs +++ b/examples/peripherals/uart-async-demo/src/main.rs @@ -6,7 +6,11 @@ use bouffalo_hal::{ prelude::*, uart::{Config, SerialState}, }; -use bouffalo_rt::{entry, interrupt, Clocks, Peripherals}; +use bouffalo_rt::{ + entry, interrupt, + soc::bl808::{D0Machine, DspInterrupt}, + Clocks, Peripherals, +}; use embedded_time::rate::*; use panic_halt as _; @@ -19,10 +23,12 @@ async fn async_main(p: Peripherals, c: Clocks) { .uart3 .with_interrupt(config, (tx, rx), &c, &UART3_STATE) .unwrap(); + p.plic.enable(DspInterrupt::UART3, D0Machine); serial .write_all(b"Hello world from async/await uart demo!") - .await; + .await + .ok(); } static UART3_STATE: SerialState = SerialState::new(); @@ -43,12 +49,17 @@ fn main(p: Peripherals, c: Clocks) -> ! { let mut fut = core::pin::pin!(async_main(p, c)); let waker = Waker::noop(); let mut ctx = Context::from_waker(waker); + unsafe { + riscv::register::mie::set_mext(); + riscv::register::mstatus::set_mie(); + } loop { match fut.as_mut().poll(&mut ctx) { Poll::Ready(_) => break, Poll::Pending => riscv::asm::wfi(), } } + unsafe { riscv::register::mstatus::clear_mie() }; loop { riscv::asm::wfi(); } From d52fc3f25a309586630b0e211289b20eb9de2409 Mon Sep 17 00:00:00 2001 From: Zhouqi Jiang Date: Sun, 15 Dec 2024 00:27:16 +0800 Subject: [PATCH 3/5] example: enable interrupt using PLIC in uart-async-demo TODO: check if T-Head C906 PLIC is different from standard PLIC. If so, we'd contribute the non-standard PLIC into `xuantie` crate. Signed-off-by: Zhouqi Jiang --- examples/peripherals/uart-async-demo/src/main.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/examples/peripherals/uart-async-demo/src/main.rs b/examples/peripherals/uart-async-demo/src/main.rs index 1fbfe9e..27dbf3d 100644 --- a/examples/peripherals/uart-async-demo/src/main.rs +++ b/examples/peripherals/uart-async-demo/src/main.rs @@ -15,6 +15,12 @@ use embedded_time::rate::*; use panic_halt as _; async fn async_main(p: Peripherals, c: Clocks) { + // enable jtag + p.gpio.io0.into_jtag_d0(); + p.gpio.io1.into_jtag_d0(); + p.gpio.io2.into_jtag_d0(); + p.gpio.io3.into_jtag_d0(); + let tx = p.gpio.io16.into_mm_uart(); let rx = p.gpio.io17.into_mm_uart(); @@ -23,6 +29,8 @@ async fn async_main(p: Peripherals, c: Clocks) { .uart3 .with_interrupt(config, (tx, rx), &c, &UART3_STATE) .unwrap(); + // TODO: is T-Head C906 PLIC different from standard PLIC? + p.plic.set_priority(DspInterrupt::UART3, 1); p.plic.enable(DspInterrupt::UART3, D0Machine); serial @@ -46,6 +54,7 @@ fn main(p: Peripherals, c: Clocks) -> ! { future::Future, task::{Context, Poll, Waker}, }; + p.plic.set_threshold(D0Machine, 0); let mut fut = core::pin::pin!(async_main(p, c)); let waker = Waker::noop(); let mut ctx = Context::from_waker(waker); From 7f95e4c3429b18800471d6b5e88e127eae67392c Mon Sep 17 00:00:00 2001 From: Zhouqi Jiang Date: Sun, 15 Dec 2024 16:36:42 +0800 Subject: [PATCH 4/5] hal: uart: use freerun mode on AsyncSerial structure Signed-off-by: Zhouqi Jiang --- bouffalo-hal/src/uart/asynch.rs | 4 ++-- bouffalo-rt/Cargo.toml | 1 + bouffalo-rt/src/soc/bl808.rs | 2 +- examples/peripherals/uart-async-demo/src/main.rs | 15 ++++++++++----- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/bouffalo-hal/src/uart/asynch.rs b/bouffalo-hal/src/uart/asynch.rs index 3320ec5..c7acc9c 100644 --- a/bouffalo-hal/src/uart/asynch.rs +++ b/bouffalo-hal/src/uart/asynch.rs @@ -39,8 +39,8 @@ impl, PADS> AsyncSerial { unsafe { uart.bit_period.write(bit_period) }; // Write the bit-order. unsafe { uart.data_config.write(data_config) }; - // Configure transmit feature without freerun. - unsafe { uart.transmit_config.write(transmit_config) }; + // Configure transmit feature with freerun. + unsafe { uart.transmit_config.write(transmit_config.enable_freerun()) }; // Configure receive feature. unsafe { uart.receive_config.write(receive_config) }; diff --git a/bouffalo-rt/Cargo.toml b/bouffalo-rt/Cargo.toml index 19fbe6a..239962f 100644 --- a/bouffalo-rt/Cargo.toml +++ b/bouffalo-rt/Cargo.toml @@ -12,6 +12,7 @@ crc = "3.2.1" cfg-if = "1.0.0" embedded-time = "0.12.1" plic = "0.0.2" +xuantie-riscv = { git = "https://github.com/rustsbi/xuantie" } [dev-dependencies] memoffset = "0.9.1" diff --git a/bouffalo-rt/src/soc/bl808.rs b/bouffalo-rt/src/soc/bl808.rs index 3526ad6..cb1c431 100644 --- a/bouffalo-rt/src/soc/bl808.rs +++ b/bouffalo-rt/src/soc/bl808.rs @@ -873,7 +873,7 @@ soc! { /// Pseudo Static Random Access Memory controller. pub struct PSRAM => 0x3000F000, bouffalo_hal::psram::RegisterBlock; /// Platform-local Interrupt Controller. - pub struct PLIC => 0xE0000000, plic::Plic; + pub struct PLIC => 0xE0000000, xuantie_riscv::peripheral::plic::Plic; } pub use bouffalo_hal::clocks::Clocks; diff --git a/examples/peripherals/uart-async-demo/src/main.rs b/examples/peripherals/uart-async-demo/src/main.rs index 27dbf3d..4adbc3e 100644 --- a/examples/peripherals/uart-async-demo/src/main.rs +++ b/examples/peripherals/uart-async-demo/src/main.rs @@ -29,14 +29,19 @@ async fn async_main(p: Peripherals, c: Clocks) { .uart3 .with_interrupt(config, (tx, rx), &c, &UART3_STATE) .unwrap(); - // TODO: is T-Head C906 PLIC different from standard PLIC? p.plic.set_priority(DspInterrupt::UART3, 1); p.plic.enable(DspInterrupt::UART3, D0Machine); - serial - .write_all(b"Hello world from async/await uart demo!") - .await - .ok(); + serial.write_all(b"Hello async/await world!\n").await.ok(); + + let paragraph = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam eu eleifend quam. Maecenas in maximus ex. In quis dolor sit amet risus condimentum egestas sed ut ex. Aenean placerat, mauris vel rutrum sodales, odio felis tempor lectus, et ullamcorper magna sem id mi. Donec laoreet justo vel finibus gravida. Nam eleifend accumsan orci vitae fermentum. Vestibulum dictum arcu sed rhoncus aliquet. +Aenean sollicitudin felis nec nisi scelerisque, quis sodales diam sagittis. Nullam consequat, ex eget porttitor laoreet, purus ipsum lacinia eros, et porttitor metus risus sit amet enim. Maecenas ligula diam, eleifend id massa ac, bibendum varius sapien. Aliquam eget erat vitae nunc consequat maximus. Curabitur erat lacus, laoreet nec purus id, fermentum imperdiet sem. In finibus enim urna, eget varius tortor efficitur a. In sit amet auctor dui. Quisque elementum felis vel lectus dapibus, et consectetur nisi semper. Nunc lacus ante, aliquet non est sed, rhoncus rhoncus mauris. Suspendisse dignissim nibh nec velit convallis, ac accumsan orci auctor. Aliquam ornare consequat hendrerit. Sed lectus nibh, lacinia elementum tristique et, scelerisque id mauris. Mauris ac mollis ipsum. +Ut eu sagittis mi. Cras quis mollis libero. Etiam sed lectus tincidunt, maximus eros et, accumsan mauris. Ut interdum nulla augue, nec sollicitudin diam venenatis sed. Praesent eget elit ut ipsum rutrum egestas nec non dui. Duis ac diam magna. Pellentesque nibh purus, sollicitudin sed vehicula a, pulvinar nec eros. Nullam vitae suscipit enim, eget accumsan diam. Duis imperdiet aliquam efficitur. Cras interdum malesuada elit, non ultricies justo hendrerit quis. Mauris lectus ante, consequat ac lectus sollicitudin, elementum faucibus tellus. Sed pretium placerat diam ultricies sagittis. +Aenean sagittis fringilla ex pharetra gravida. Aenean feugiat tincidunt nulla non elementum. Fusce ut lectus neque. Nam nec aliquam nisi. Vivamus suscipit quam vehicula, pulvinar elit eget, iaculis magna. Curabitur congue, elit vel faucibus ultricies, arcu nisi congue risus, sit amet efficitur mi turpis quis sapien. Sed eu elit eu sem mattis laoreet. Nam ullamcorper, arcu ut eleifend vestibulum, mi augue tempor eros, ac maximus metus lectus quis sapien. +Suspendisse potenti. Nam bibendum, velit quis ullamcorper blandit, nunc odio ultricies diam, vitae euismod arcu neque eu ex. Vivamus et quam massa. Curabitur eget semper nulla, quis convallis nibh. Praesent rutrum dolor in ultrices tincidunt. Suspendisse placerat blandit mi, eget blandit arcu consequat eu. Aliquam suscipit eget velit et mattis. Etiam pulvinar velit a odio consequat, quis consectetur metus consectetur. Maecenas convallis eleifend metus, et dapibus sem eleifend eget. +"; + + serial.write_all(paragraph).await.ok(); } static UART3_STATE: SerialState = SerialState::new(); From 7c722ecf083d2251dfb92d6795cae9a5891434c1 Mon Sep 17 00:00:00 2001 From: Zhouqi Jiang Date: Sun, 15 Dec 2024 17:01:12 +0800 Subject: [PATCH 5/5] rt: fix test error by using xuantie repository commit fe7ec712 Signed-off-by: Zhouqi Jiang --- bouffalo-rt/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bouffalo-rt/Cargo.toml b/bouffalo-rt/Cargo.toml index 239962f..1ddfa16 100644 --- a/bouffalo-rt/Cargo.toml +++ b/bouffalo-rt/Cargo.toml @@ -12,7 +12,7 @@ crc = "3.2.1" cfg-if = "1.0.0" embedded-time = "0.12.1" plic = "0.0.2" -xuantie-riscv = { git = "https://github.com/rustsbi/xuantie" } +xuantie-riscv = { git = "https://github.com/rustsbi/xuantie", commit = "fe7ec712" } [dev-dependencies] memoffset = "0.9.1"