1 Commits

Author SHA1 Message Date
Tobias Baumgartl 0a3962b4d2 possible bugfix
shared-hal-ci / Check build (push) Has been cancelled
shared-hal-ci / Check formatting (push) Has been cancelled
shared-hal-ci / Check Documentation Build (push) Has been cancelled
shared-hal-ci / Clippy (push) Has been cancelled
va108xx-ci / Check build (push) Has been cancelled
va108xx-ci / Run Tests (push) Has been cancelled
va108xx-ci / Check formatting (push) Has been cancelled
va108xx-ci / Check Documentation Build (push) Has been cancelled
va108xx-ci / Clippy (push) Has been cancelled
va416xx-ci / Check build (push) Has been cancelled
va416xx-ci / Run Tests (push) Has been cancelled
va416xx-ci / Check formatting (push) Has been cancelled
va416xx-ci / Check Documentation Build (push) Has been cancelled
va416xx-ci / Clippy (push) Has been cancelled
shared-hal-ci / Check build (pull_request) Has been cancelled
shared-hal-ci / Check formatting (pull_request) Has been cancelled
shared-hal-ci / Check Documentation Build (pull_request) Has been cancelled
shared-hal-ci / Clippy (pull_request) Has been cancelled
va108xx-ci / Check build (pull_request) Has been cancelled
va108xx-ci / Run Tests (pull_request) Has been cancelled
va108xx-ci / Check formatting (pull_request) Has been cancelled
va108xx-ci / Check Documentation Build (pull_request) Has been cancelled
va108xx-ci / Clippy (pull_request) Has been cancelled
va416xx-ci / Check build (pull_request) Has been cancelled
va416xx-ci / Run Tests (pull_request) Has been cancelled
va416xx-ci / Check formatting (pull_request) Has been cancelled
va416xx-ci / Check Documentation Build (pull_request) Has been cancelled
va416xx-ci / Clippy (pull_request) Has been cancelled
2026-04-27 12:20:29 +02:00
13 changed files with 245 additions and 333 deletions
+4 -5
View File
@@ -4,9 +4,8 @@ version = "0.1.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
cortex-m-rt = "0.7"
cfg-if = "1" cfg-if = "1"
cortex-m-rt = "0.7"
embedded-hal-async = "1" embedded-hal-async = "1"
embedded-io = "0.7" embedded-io = "0.7"
embedded-io-async = "0.7" embedded-io-async = "0.7"
@@ -19,10 +18,10 @@ panic-probe = { version = "1", features = ["print-defmt"] }
critical-section = "1" critical-section = "1"
embassy-sync = "0.8" embassy-sync = "0.7"
embassy-time = "0.5" embassy-time = "0.5"
embassy-executor = { version = "0.10", features = [ embassy-executor = { version = "0.9", features = [
"platform-cortex-m", "arch-cortex-m",
"executor-thread", "executor-thread",
"executor-interrupt" "executor-interrupt"
]} ]}
+17 -19
View File
@@ -62,9 +62,6 @@ async fn main(spawner: Spawner) {
// Safety: Only called once here. // Safety: Only called once here.
va108xx_embassy::init(dp.tim23, dp.tim22, SYSCLK_FREQ); va108xx_embassy::init(dp.tim23, dp.tim22, SYSCLK_FREQ);
unsafe {
cortex_m::interrupt::enable();
}
let porta = PinsA::new(dp.porta); let porta = PinsA::new(dp.porta);
let portb = PinsB::new(dp.portb); let portb = PinsB::new(dp.portb);
@@ -74,23 +71,26 @@ async fn main(spawner: Spawner) {
let out_pb22 = Output::new(portb.pb22, PinState::Low); let out_pb22 = Output::new(portb.pb22, PinState::Low);
let in_pb23 = Input::new_floating(portb.pb23); let in_pb23 = Input::new_floating(portb.pb23);
let mut in_pa1_async = InputPinAsync::new( let mut in_pa1_async = InputPinAsync::new(in_pa1, pac::Interrupt::OC10);
in_pa1, let mut in_pb23_async = InputPinAsync::new(in_pb23, PB22_TO_PB23_IRQ);
va108xx_hal::InterruptConfig::new(pac::Interrupt::OC10, true, true),
);
let mut in_pb23_async = InputPinAsync::new(
in_pb23,
va108xx_hal::InterruptConfig::new(PB22_TO_PB23_IRQ, true, true),
);
spawner.spawn(output_task("PA0 to PA1", out_pa0, CHANNEL_PA0_PA1.receiver()).unwrap()); spawner
spawner.spawn(output_task("PB22 to PB23", out_pb22, CHANNEL_PB22_TO_PB23.receiver()).unwrap()); .spawn(output_task(
"PA0 to PA1",
out_pa0,
CHANNEL_PA0_PA1.receiver(),
))
.unwrap();
spawner
.spawn(output_task(
"PB22 to PB23",
out_pb22,
CHANNEL_PB22_TO_PB23.receiver(),
))
.unwrap();
for i in 0..3 {
defmt::info!("Starting async GPIO operations check {}", i);
if CHECK_PA0_TO_PA1 { if CHECK_PA0_TO_PA1 {
check_pin_to_pin_async_ops("PA0 to PA1", CHANNEL_PA0_PA1.sender(), &mut in_pa1_async) check_pin_to_pin_async_ops("PA0 to PA1", CHANNEL_PA0_PA1.sender(), &mut in_pa1_async).await;
.await;
defmt::info!("Example PA0 to PA1 done"); defmt::info!("Example PA0 to PA1 done");
} }
if CHECK_PB22_TO_PB23 { if CHECK_PB22_TO_PB23 {
@@ -102,8 +102,6 @@ async fn main(spawner: Spawner) {
.await; .await;
defmt::info!("Example PB22 to PB23 done"); defmt::info!("Example PB22 to PB23 done");
} }
Timer::after(Duration::from_millis(500)).await;
}
defmt::info!("Example done, toggling LED0"); defmt::info!("Example done, toggling LED0");
loop { loop {
@@ -97,7 +97,9 @@ async fn main(spawner: Spawner) {
}); });
let mut async_rx_uart_a = RxAsync::new(rx_uart_a, cons_uart_a); let mut async_rx_uart_a = RxAsync::new(rx_uart_a, cons_uart_a);
let async_rx_uart_b = RxAsyncOverwriting::new(rx_uart_b, &CONSUMER_UART_B); let async_rx_uart_b = RxAsyncOverwriting::new(rx_uart_b, &CONSUMER_UART_B);
spawner.spawn(uart_b_task(async_rx_uart_b, tx_uart_b).unwrap()); spawner
.spawn(uart_b_task(async_rx_uart_b, tx_uart_b))
.unwrap();
let mut buf = [0u8; 256]; let mut buf = [0u8; 256];
loop { loop {
defmt::info!("Current time UART A: {}", Instant::now().as_secs()); defmt::info!("Current time UART A: {}", Instant::now().as_secs());
+2 -2
View File
@@ -36,7 +36,7 @@ impl Button {
irq_cfg: InterruptConfig, irq_cfg: InterruptConfig,
) { ) {
self.0.configure_edge_interrupt(edge_type); self.0.configure_edge_interrupt(edge_type);
self.0.enable_interrupt(irq_cfg, true); self.0.enable_interrupt(irq_cfg);
} }
/// Configures an IRQ on level. /// Configures an IRQ on level.
@@ -46,7 +46,7 @@ impl Button {
irq_cfg: InterruptConfig, irq_cfg: InterruptConfig,
) { ) {
self.0.configure_level_interrupt(level); self.0.configure_level_interrupt(level);
self.0.enable_interrupt(irq_cfg, true); self.0.enable_interrupt(irq_cfg);
} }
/// Configures a filter on the button. This can be useful for debouncing the switch. /// Configures a filter on the button. This can be useful for debouncing the switch.
+28 -2
View File
@@ -270,11 +270,37 @@ fn boot_app(app_sel: AppSel, cp: &cortex_m::Peripherals) -> ! {
cortex_m::asm::isb(); cortex_m::asm::isb();
unsafe { unsafe {
if app_sel == AppSel::A { if app_sel == AppSel::A {
cortex_m::asm::bootload(APP_A_START_ADDR as *const u32); cp.SCB.vtor.write(APP_A_START_ADDR);
} else { } else {
cortex_m::asm::bootload(APP_B_START_ADDR as *const u32); cp.SCB.vtor.write(APP_B_START_ADDR);
} }
} }
cortex_m::asm::dsb();
cortex_m::asm::isb();
vector_reset();
}
pub fn vector_reset() -> ! {
unsafe {
// Set R0 to VTOR address (0xE000ED08)
let vtor_address: u32 = 0xE000ED08;
// Load VTOR
let vtor: u32 = *(vtor_address as *const u32);
// Load initial MSP value
let initial_msp: u32 = *(vtor as *const u32);
// Set SP value (assume MSP is selected)
core::arch::asm!("mov sp, {0}", in(reg) initial_msp);
// Load reset vector
let reset_vector: u32 = *((vtor + 4) as *const u32);
// Branch to reset handler
core::arch::asm!("bx {0}", in(reg) reset_vector);
}
unreachable!();
} }
fn setup_edac(syscfg: &mut pac::Sysconfig) { fn setup_edac(syscfg: &mut pac::Sysconfig) {
-2
View File
@@ -30,8 +30,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Added missing `AnyPin` trait impl for Multi HW CS pins. - Added missing `AnyPin` trait impl for Multi HW CS pins.
- Expose inner `Input` pin for `InputPinAsync`. - Expose inner `Input` pin for `InputPinAsync`.
- Bugfix for UART clock calculation with 8x baud mode. - Bugfix for UART clock calculation with 8x baud mode.
- Possible bugfix for Asynch GPIO where the interrupt handler could become stuck in a loop.
- Robustness improvements for the Asynch GPIO driver code.
## [v0.2.0] 2025-09-03 ## [v0.2.0] 2025-09-03
+1 -1
View File
@@ -29,7 +29,7 @@ fugit = "0.3"
defmt = { version = "1", optional = true } defmt = { version = "1", optional = true }
va108xx = { version = "0.6", path = "../va108xx/va108xx", default-features = false, optional = true } va108xx = { version = "0.6", path = "../va108xx/va108xx", default-features = false, optional = true }
va416xx = { version = "0.5", path = "../va416xx/va416xx", default-features = false, optional = true } va416xx = { version = "0.5", path = "../va416xx/va416xx", default-features = false, optional = true }
embassy-sync = "0.8" embassy-sync = "0.7"
embassy-time-driver = "0.2" embassy-time-driver = "0.2"
embassy-time-queue-utils = "0.3" embassy-time-queue-utils = "0.3"
once_cell = { version = "1", default-features = false, features = [ once_cell = { version = "1", default-features = false, features = [
+32 -29
View File
@@ -21,6 +21,9 @@ use crate::{InterruptConfig, NUM_PORT_A, NUM_PORT_B};
#[cfg(feature = "vor4x")] #[cfg(feature = "vor4x")]
use super::ll::PortDoesNotSupportInterrupts; use super::ll::PortDoesNotSupportInterrupts;
#[cfg(feature = "vor1x")]
use va108xx as pac;
pub use super::ll::InterruptEdge; pub use super::ll::InterruptEdge;
use super::{ use super::{
Input, Port, Input, Port,
@@ -115,35 +118,34 @@ pub fn on_interrupt_for_async_gpio_for_port(
} }
fn on_interrupt_for_async_gpio_for_port_generic(port: Port) { fn on_interrupt_for_async_gpio_for_port_generic(port: Port) {
let mut gpio = unsafe { port.steal_regs() }; let gpio = unsafe { port.steal_gpio() };
let irq_enb = gpio.read_irq_enable(); let irq_enb = gpio.read_irq_enable();
let edge_status = gpio.read_edge_status(); let edge_status = gpio.read_edge_status();
let irq_status = gpio.read_irq_status();
// Depending on silicon/configuration, edge-triggered events can be reflected in EDGE_STATUS,
// IRQ_STATUS, or both. Accept either source for pending detection.
let pending = irq_enb & (edge_status | irq_status);
let (wakers, edge_detection) = pin_group_to_waker_and_edge_detection_group(port); let (wakers, edge_detection) = pin_group_to_waker_and_edge_detection_group(port);
on_interrupt_for_port(irq_enb, edge_status, wakers, edge_detection); on_interrupt_for_port(pending, wakers, edge_detection);
} }
#[inline] #[inline]
fn on_interrupt_for_port( fn on_interrupt_for_port(
mut irq_enb: u32, mut pending: u32,
edge_status: u32,
wakers: &'static [AtomicWaker], wakers: &'static [AtomicWaker],
edge_detection: &'static [AtomicBool], edge_detection: &'static [AtomicBool],
) { ) {
// Check all enabled interrupts. while pending != 0 {
while irq_enb != 0 { let bit_pos = pending.trailing_zeros() as usize;
// For all enabled interrupts, check whether the corresponding edge detection has
// triggered.
let bit_pos = irq_enb.trailing_zeros() as usize;
let bit_mask = 1 << bit_pos; let bit_mask = 1 << bit_pos;
if edge_status & bit_mask != 0 {
edge_detection[bit_pos].store(true, core::sync::atomic::Ordering::Relaxed);
wakers[bit_pos].wake(); wakers[bit_pos].wake();
} edge_detection[bit_pos].store(true, core::sync::atomic::Ordering::Relaxed);
// Clear the processed bit
irq_enb &= !bit_mask; // Clear the processed bit in our local bitmap.
pending &= !bit_mask;
} }
} }
@@ -161,12 +163,13 @@ pub struct InputPinFuture {
impl InputPinFuture { impl InputPinFuture {
/// Create a new input pin future from mutable reference to an [Input] pin. /// Create a new input pin future from mutable reference to an [Input] pin.
#[cfg(feature = "vor1x")] #[cfg(feature = "vor1x")]
pub fn new_with_input_pin(pin: &mut Input, edge: InterruptEdge) -> Self { pub fn new_with_input_pin(pin: &mut Input, irq: pac::Interrupt, edge: InterruptEdge) -> Self {
let (waker_group, edge_detection_group) = let (waker_group, edge_detection_group) =
pin_group_to_waker_and_edge_detection_group(pin.id().port()); pin_group_to_waker_and_edge_detection_group(pin.id().port());
edge_detection_group[pin.id().offset()].store(false, core::sync::atomic::Ordering::Relaxed); edge_detection_group[pin.id().offset()].store(false, core::sync::atomic::Ordering::Relaxed);
pin.configure_edge_interrupt(edge); pin.configure_edge_interrupt(edge);
pin.enable_interrupt_gpio_only(); #[cfg(feature = "vor1x")]
pin.enable_interrupt(InterruptConfig::new(irq, true, true));
Self { Self {
id: pin.id(), id: pin.id(),
waker_group, waker_group,
@@ -183,7 +186,7 @@ impl InputPinFuture {
let (waker_group, edge_detection_group) = let (waker_group, edge_detection_group) =
pin_group_to_waker_and_edge_detection_group(pin.id().port()); pin_group_to_waker_and_edge_detection_group(pin.id().port());
pin.configure_edge_interrupt(edge); pin.configure_edge_interrupt(edge);
pin.enable_interrupt_gpio_only(); pin.enable_interrupt(true)?;
Ok(Self { Ok(Self {
id: pin.id(), id: pin.id(),
waker_group, waker_group,
@@ -220,6 +223,8 @@ impl Future for InputPinFuture {
/// Input pin which has additional asynchronous support. /// Input pin which has additional asynchronous support.
pub struct InputPinAsync { pub struct InputPinAsync {
pin: Input, pin: Input,
#[cfg(feature = "vor1x")]
irq: va108xx::Interrupt,
} }
impl InputPinAsync { impl InputPinAsync {
@@ -230,10 +235,8 @@ impl InputPinAsync {
/// generic [on_interrupt_for_async_gpio_for_port] function must be called inside that function /// generic [on_interrupt_for_async_gpio_for_port] function must be called inside that function
/// for the asynchronous functionality to work. /// for the asynchronous functionality to work.
#[cfg(feature = "vor1x")] #[cfg(feature = "vor1x")]
pub fn new(mut pin: Input, irq_config: InterruptConfig) -> Self { pub fn new(pin: Input, irq: va108xx::Interrupt) -> Self {
// Do not enable GPIO interrupt bit yet. Self { pin, irq }
pin.enable_interrupt(irq_config, false);
Self { pin }
} }
/// Create a new asynchronous input pin from an [Input] pin. The interrupt ID to be used must be /// Create a new asynchronous input pin from an [Input] pin. The interrupt ID to be used must be
@@ -243,12 +246,10 @@ impl InputPinAsync {
/// generic [on_interrupt_for_async_gpio_for_port] function must be called inside that function /// generic [on_interrupt_for_async_gpio_for_port] function must be called inside that function
/// for the asynchronous functionality to work. /// for the asynchronous functionality to work.
#[cfg(feature = "vor4x")] #[cfg(feature = "vor4x")]
pub fn new(mut pin: Input) -> Result<Self, PortDoesNotSupportInterrupts> { pub fn new(pin: Input) -> Result<Self, PortDoesNotSupportInterrupts> {
if pin.id().port() == Port::G { if pin.id().port() == Port::G {
return Err(PortDoesNotSupportInterrupts); return Err(PortDoesNotSupportInterrupts);
} }
// Do not enable GPIO interrupt bit yet.
pin.enable_interrupt(true, false)?;
Ok(Self { pin }) Ok(Self { pin })
} }
@@ -258,7 +259,8 @@ impl InputPinAsync {
pub async fn wait_for_high(&mut self) { pub async fn wait_for_high(&mut self) {
// Unwrap okay, checked pin in constructor. // Unwrap okay, checked pin in constructor.
#[cfg(feature = "vor1x")] #[cfg(feature = "vor1x")]
let fut = InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::LowToHigh); let fut =
InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh);
#[cfg(feature = "vor4x")] #[cfg(feature = "vor4x")]
let fut = let fut =
InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::LowToHigh).unwrap(); InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::LowToHigh).unwrap();
@@ -298,7 +300,8 @@ impl InputPinAsync {
pub async fn wait_for_low(&mut self) { pub async fn wait_for_low(&mut self) {
// Unwrap okay, checked pin in constructor. // Unwrap okay, checked pin in constructor.
#[cfg(feature = "vor1x")] #[cfg(feature = "vor1x")]
let fut = InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::HighToLow); let fut =
InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow);
#[cfg(feature = "vor4x")] #[cfg(feature = "vor4x")]
let fut = let fut =
InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::HighToLow).unwrap(); InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::HighToLow).unwrap();
@@ -312,7 +315,7 @@ impl InputPinAsync {
pub async fn wait_for_falling_edge(&mut self) { pub async fn wait_for_falling_edge(&mut self) {
// Unwrap okay, checked pin in constructor. // Unwrap okay, checked pin in constructor.
#[cfg(feature = "vor1x")] #[cfg(feature = "vor1x")]
InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::HighToLow).await; InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow).await;
#[cfg(feature = "vor4x")] #[cfg(feature = "vor4x")]
InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::HighToLow) InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::HighToLow)
.unwrap() .unwrap()
@@ -323,14 +326,14 @@ impl InputPinAsync {
pub async fn wait_for_rising_edge(&mut self) { pub async fn wait_for_rising_edge(&mut self) {
// Unwrap okay, checked pin in constructor. // Unwrap okay, checked pin in constructor.
#[cfg(feature = "vor1x")] #[cfg(feature = "vor1x")]
InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::LowToHigh).await; InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh).await;
} }
/// Asynchronously wait until the pin sees any edge (either rising or falling). /// Asynchronously wait until the pin sees any edge (either rising or falling).
pub async fn wait_for_any_edge(&mut self) { pub async fn wait_for_any_edge(&mut self) {
// Unwrap okay, checked pin in constructor. // Unwrap okay, checked pin in constructor.
#[cfg(feature = "vor1x")] #[cfg(feature = "vor1x")]
InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::BothEdges).await; InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::BothEdges).await;
#[cfg(feature = "vor4x")] #[cfg(feature = "vor4x")]
InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::BothEdges) InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::BothEdges)
.unwrap() .unwrap()
+9 -30
View File
@@ -384,53 +384,32 @@ impl LowLevelGpio {
self.gpio.write_tog_out(self.mask_32()); self.gpio.write_tog_out(self.mask_32());
} }
/// Only enabled GPIO peripheral interrupt bit without enabling the interrupt in NVIC
/// or routing it in the IRQSEL peripheral for VA108xx devices.
#[inline]
pub fn enable_interrupt_gpio_only(&mut self) {
self.gpio.modify_irq_enable(|mut value| {
value |= 1 << self.id.offset;
value
});
}
/// Depending on the configuration parameters, does the following:
///
/// - Routes the interrupt in the IRQSEL peripheral
/// - Enables the interrupt in the NVIC,
/// - Enable the GPIO peripheral interrupt bit for this pin if configured.
#[cfg(feature = "vor1x")] #[cfg(feature = "vor1x")]
pub fn enable_interrupt(&mut self, irq_cfg: crate::InterruptConfig, gpio: bool) { pub fn enable_interrupt(&mut self, irq_cfg: crate::InterruptConfig) {
if irq_cfg.route { if irq_cfg.route {
self.configure_irqsel(irq_cfg.id); self.configure_irqsel(irq_cfg.id);
} }
if irq_cfg.enable_in_nvic { if irq_cfg.enable_in_nvic {
unsafe { crate::enable_nvic_interrupt(irq_cfg.id) }; unsafe { crate::enable_nvic_interrupt(irq_cfg.id) };
} }
if gpio { self.gpio.modify_irq_enable(|mut value| {
self.enable_interrupt_gpio_only(); value |= 1 << self.id.offset;
} value
});
} }
/// Depending on the configuration parameters, does the following:
///
/// - Enables the interrupt in the NVIC,
/// - Enable the GPIO peripheral interrupt bit for this pin if configured.
#[cfg(feature = "vor4x")] #[cfg(feature = "vor4x")]
pub fn enable_interrupt( pub fn enable_interrupt(
&mut self, &mut self,
enable_in_nvic: bool, enable_in_nvic: bool,
gpio: bool,
) -> Result<(), PortDoesNotSupportInterrupts> { ) -> Result<(), PortDoesNotSupportInterrupts> {
if self.id().port() == Port::G {
return Err(PortDoesNotSupportInterrupts);
}
if enable_in_nvic { if enable_in_nvic {
unsafe { crate::enable_nvic_interrupt(self.id().irq_unchecked()) }; unsafe { crate::enable_nvic_interrupt(self.id().irq_unchecked()) };
} }
if gpio { self.gpio.modify_irq_enable(|mut value| {
self.enable_interrupt_gpio_only(); value |= 1 << self.id.offset;
} value
});
Ok(()) Ok(())
} }
+3 -18
View File
@@ -132,34 +132,19 @@ impl Input {
self.0.id() self.0.id()
} }
#[inline]
pub fn enable_interrupt_gpio_only(&mut self) {
self.0.enable_interrupt_gpio_only();
}
/// Depending on the configuration parameters, does the following:
///
/// - Routes the interrupt in the IRQSEL peripheral
/// - Enables the interrupt in the NVIC,
/// - Enable the GPIO peripheral interrupt bit for this pin if configured.
#[cfg(feature = "vor1x")] #[cfg(feature = "vor1x")]
#[inline] #[inline]
pub fn enable_interrupt(&mut self, irq_cfg: crate::InterruptConfig, gpio: bool) { pub fn enable_interrupt(&mut self, irq_cfg: crate::InterruptConfig) {
self.0.enable_interrupt(irq_cfg, gpio); self.0.enable_interrupt(irq_cfg);
} }
/// Depending on the configuration parameters, does the following:
///
/// - Enables the interrupt in the NVIC,
/// - Enable the GPIO peripheral interrupt bit for this pin if configured.
#[cfg(feature = "vor4x")] #[cfg(feature = "vor4x")]
#[inline] #[inline]
pub fn enable_interrupt( pub fn enable_interrupt(
&mut self, &mut self,
enable_in_nvic: bool, enable_in_nvic: bool,
gpio: bool,
) -> Result<(), ll::PortDoesNotSupportInterrupts> { ) -> Result<(), ll::PortDoesNotSupportInterrupts> {
self.0.enable_interrupt(enable_in_nvic, gpio) self.0.enable_interrupt(enable_in_nvic)
} }
#[inline] #[inline]
+1 -2
View File
@@ -58,8 +58,7 @@ pub struct Gpio {
/// Read-only register which shows enabled and active interrupts. Called IRQ_end by Vorago. /// Read-only register which shows enabled and active interrupts. Called IRQ_end by Vorago.
#[mmio(PureRead)] #[mmio(PureRead)]
irq_status: u32, irq_status: u32,
// Reading this register clears it. #[mmio(PureRead)]
#[mmio(Read)]
edge_status: u32, edge_status: u32,
#[cfg(feature = "vor1x")] #[cfg(feature = "vor1x")]
+1 -1
View File
@@ -137,7 +137,7 @@ impl Port {
/// # Safety /// # Safety
/// ///
/// Circumvents ownership and safety guarantees by the HAL. /// Circumvents ownership and safety guarantees by the HAL.
pub unsafe fn steal_regs(&self) -> gpio::regs::MmioGpio<'static> { pub unsafe fn steal_gpio(&self) -> gpio::regs::MmioGpio<'static> {
gpio::regs::Gpio::new_mmio(*self) gpio::regs::Gpio::new_mmio(*self)
} }
} }
+127 -204
View File
@@ -1,4 +1,4 @@
use core::cell::RefCell; use core::{cell::RefCell, convert::Infallible};
use arbitrary_int::u5; use arbitrary_int::u5;
use critical_section::Mutex; use critical_section::Mutex;
@@ -8,10 +8,7 @@ use raw_slice::{RawBufSlice, RawBufSliceMut};
use crate::{ use crate::{
shared::{FifoClear, TriggerLevel}, shared::{FifoClear, TriggerLevel},
spi::{ spi::regs::{Data, InterruptClear, InterruptControl, InterruptStatus},
FIFO_DEPTH,
regs::{Data, InterruptClear, InterruptControl},
},
}; };
#[cfg(feature = "vor1x")] #[cfg(feature = "vor1x")]
@@ -26,17 +23,6 @@ static TRANSFER_CONTEXTS: [Mutex<RefCell<TransferContext>>; NUM_SPIS] =
// critical section. // critical section.
static DONE: [AtomicBool; NUM_SPIS] = [const { AtomicBool::new(false) }; NUM_SPIS]; static DONE: [AtomicBool; NUM_SPIS] = [const { AtomicBool::new(false) }; NUM_SPIS];
#[derive(Debug, Clone, Copy, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[error("SPI RX FIFO overrun")]
pub struct RxOverrunError;
impl embedded_hal_async::spi::Error for RxOverrunError {
fn kind(&self) -> embedded_hal::spi::ErrorKind {
embedded_hal::spi::ErrorKind::Overrun
}
}
/// This is a generic interrupt handler to handle asynchronous SPI operations for a given /// This is a generic interrupt handler to handle asynchronous SPI operations for a given
/// SPI peripheral. /// SPI peripheral.
/// ///
@@ -44,24 +30,18 @@ impl embedded_hal_async::spi::Error for RxOverrunError {
/// the given SPI bank. /// the given SPI bank.
pub fn on_interrupt(peripheral: super::Bank) { pub fn on_interrupt(peripheral: super::Bank) {
let mut spi = unsafe { peripheral.steal_regs() }; let mut spi = unsafe { peripheral.steal_regs() };
let index = peripheral as usize; let idx = peripheral as usize;
let enabled_irqs = spi.read_interrupt_control(); let interrupt_enabled = spi.read_interrupt_control();
let interrupt_status = spi.read_interrupt_status(); let isr = spi.read_interrupt_status();
spi.write_interrupt_clear(InterruptClear::ALL);
// Prevent spurious interrupts from messing with out logic here.
spi.write_interrupt_control(InterruptControl::DISABLE_ALL);
// IRQ is not related. // IRQ is not related.
if enabled_irqs.raw_value() == 0 { if interrupt_enabled.raw_value() == 0 {
reset_trigger_levels(&mut spi);
spi.write_fifo_clear(FifoClear::ALL);
return; return;
} }
if interrupt_status.rx_overrun() { // Prevent spurious interrupts from messing with out logic here.
// Not sure how to otherwise handle this cleanly.. spi.write_interrupt_control(InterruptControl::DISABLE_ALL);
return handle_rx_overrun(&mut spi, index); spi.write_interrupt_clear(InterruptClear::ALL);
}
let mut context = critical_section::with(|cs| { let mut context = critical_section::with(|cs| {
let context_ref = TRANSFER_CONTEXTS[index].borrow(cs); let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
*context_ref.borrow() *context_ref.borrow()
}); });
// No transfer active. // No transfer active.
@@ -70,47 +50,30 @@ pub fn on_interrupt(peripheral: super::Bank) {
} }
let transfer_type = context.transfer_type.unwrap(); let transfer_type = context.transfer_type.unwrap();
match transfer_type { match transfer_type {
TransferType::Read => on_interrupt_read(index, &mut context, &mut spi, enabled_irqs), TransferType::Read => on_interrupt_read(idx, &mut context, &mut spi, isr),
TransferType::Write => on_interrupt_write(index, &mut context, &mut spi, enabled_irqs), TransferType::Write => on_interrupt_write(idx, &mut context, &mut spi, isr),
TransferType::Transfer => { TransferType::Transfer => on_interrupt_transfer(idx, &mut context, &mut spi, isr),
on_interrupt_transfer(index, &mut context, &mut spi, enabled_irqs)
}
TransferType::TransferInPlace => { TransferType::TransferInPlace => {
on_interrupt_transfer_in_place(index, &mut context, &mut spi, enabled_irqs) on_interrupt_transfer_in_place(idx, &mut context, &mut spi, isr)
} }
}; };
} }
fn handle_rx_overrun(spi: &mut super::regs::MmioSpi<'static>, idx: usize) {
critical_section::with(|cs| {
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
context_ref.borrow_mut().rx_overrun = true;
});
// Clean up, restore clean state.
reset_trigger_levels(spi);
spi.write_fifo_clear(FifoClear::ALL);
// Interrupts were already disabled and cleared.
DONE[idx].store(true, core::sync::atomic::Ordering::Relaxed);
WAKERS[idx].wake();
}
fn on_interrupt_read( fn on_interrupt_read(
idx: usize, idx: usize,
context: &mut TransferContext, context: &mut TransferContext,
spi: &mut super::regs::MmioSpi<'static>, spi: &mut super::regs::MmioSpi<'static>,
enabled_irqs: InterruptControl, isr: InterruptStatus,
) { ) {
let read_slice = unsafe { context.rx_slice.get_mut().unwrap() }; let read_slice = unsafe { context.rx_slice.get_mut().unwrap() };
let transfer_len = read_slice.len(); let transfer_len = read_slice.len();
// Read data from RX FIFO first. // Read data from RX FIFO first.
while spi.read_status().rx_not_empty() { let read_len = calculate_read_len(spi, isr, transfer_len, context.rx_progress);
let data = spi.read_data(); (0..read_len).for_each(|_| {
if context.rx_progress < transfer_len { read_slice[context.rx_progress] = (spi.read_data().data() & 0xFF) as u8;
read_slice[context.rx_progress] = (data.data() & 0xFF) as u8;
context.rx_progress += 1; context.rx_progress += 1;
} });
}
// The FIFO still needs to be pumped. // The FIFO still needs to be pumped.
while context.tx_progress < read_slice.len() && spi.read_status().tx_not_full() { while context.tx_progress < read_slice.len() && spi.read_status().tx_not_full() {
@@ -118,25 +81,24 @@ fn on_interrupt_read(
context.tx_progress += 1; context.tx_progress += 1;
} }
isr_finish_handler(idx, spi, context, transfer_len, enabled_irqs) isr_finish_handler(idx, spi, context, transfer_len)
} }
fn on_interrupt_write( fn on_interrupt_write(
idx: usize, idx: usize,
context: &mut TransferContext, context: &mut TransferContext,
spi: &mut super::regs::MmioSpi<'static>, spi: &mut super::regs::MmioSpi<'static>,
enabled_irqs: InterruptControl, isr: InterruptStatus,
) { ) {
let write_slice = unsafe { context.tx_slice.get().unwrap() }; let write_slice = unsafe { context.tx_slice.get().unwrap() };
let transfer_len = write_slice.len(); let transfer_len = write_slice.len();
// Read data from RX FIFO first. // Read data from RX FIFO first.
while spi.read_status().rx_not_empty() { let read_len = calculate_read_len(spi, isr, transfer_len, context.rx_progress);
(0..read_len).for_each(|_| {
spi.read_data(); spi.read_data();
if context.rx_progress < transfer_len {
context.rx_progress += 1; context.rx_progress += 1;
} });
}
// Data still needs to be sent // Data still needs to be sent
while context.tx_progress < transfer_len && spi.read_status().tx_not_full() { while context.tx_progress < transfer_len && spi.read_status().tx_not_full() {
@@ -146,14 +108,14 @@ fn on_interrupt_write(
context.tx_progress += 1; context.tx_progress += 1;
} }
isr_finish_handler(idx, spi, context, transfer_len, enabled_irqs) isr_finish_handler(idx, spi, context, transfer_len)
} }
fn on_interrupt_transfer( fn on_interrupt_transfer(
idx: usize, idx: usize,
context: &mut TransferContext, context: &mut TransferContext,
spi: &mut super::regs::MmioSpi<'static>, spi: &mut super::regs::MmioSpi<'static>,
enabled_irqs: InterruptControl, isr: InterruptStatus,
) { ) {
let read_slice = unsafe { context.rx_slice.get_mut().unwrap() }; let read_slice = unsafe { context.rx_slice.get_mut().unwrap() };
let read_len = read_slice.len(); let read_len = read_slice.len();
@@ -163,31 +125,36 @@ fn on_interrupt_transfer(
// Send data first to avoid overwriting data that still needs to be sent. // Send data first to avoid overwriting data that still needs to be sent.
while context.tx_progress < transfer_len && spi.read_status().tx_not_full() { while context.tx_progress < transfer_len && spi.read_status().tx_not_full() {
if context.tx_progress < write_len {
spi.write_data(Data::new_with_raw_value( spi.write_data(Data::new_with_raw_value(
write_slice.get(context.tx_progress).copied().unwrap_or(0) as u32, write_slice[context.tx_progress] as u32,
)); ));
// Always increment this. } else {
// Dummy write.
spi.write_data(Data::new_with_raw_value(0));
}
context.tx_progress += 1; context.tx_progress += 1;
} }
// Read data from RX FIFO. // Read data from RX FIFO.
while spi.read_status().rx_not_empty() { let read_len = calculate_read_len(spi, isr, transfer_len, context.rx_progress);
let data = spi.read_data(); (0..read_len).for_each(|_| {
if context.rx_progress < read_len { if context.rx_progress < read_len {
read_slice[context.rx_progress] = (data.data() & 0xFF) as u8; read_slice[context.rx_progress] = (spi.read_data().data() & 0xFF) as u8;
} else {
spi.read_data();
} }
// Always increment this.
context.rx_progress += 1; context.rx_progress += 1;
} });
isr_finish_handler(idx, spi, context, transfer_len, enabled_irqs) isr_finish_handler(idx, spi, context, transfer_len)
} }
fn on_interrupt_transfer_in_place( fn on_interrupt_transfer_in_place(
idx: usize, idx: usize,
context: &mut TransferContext, context: &mut TransferContext,
spi: &mut super::regs::MmioSpi<'static>, spi: &mut super::regs::MmioSpi<'static>,
enabled_irqs: InterruptControl, isr: InterruptStatus,
) { ) {
let transfer_slice = unsafe { context.rx_slice.get_mut().unwrap() }; let transfer_slice = unsafe { context.rx_slice.get_mut().unwrap() };
let transfer_len = transfer_slice.len(); let transfer_len = transfer_slice.len();
@@ -199,15 +166,29 @@ fn on_interrupt_transfer_in_place(
context.tx_progress += 1; context.tx_progress += 1;
} }
// Read data from RX FIFO. // Read data from RX FIFO.
while spi.read_status().rx_not_empty() { let read_len = calculate_read_len(spi, isr, transfer_len, context.rx_progress);
let data = spi.read_data(); (0..read_len).for_each(|_| {
if context.rx_progress < transfer_len { transfer_slice[context.rx_progress] = (spi.read_data().data() & 0xFF) as u8;
transfer_slice[context.rx_progress] = (data.data() & 0xFF) as u8;
context.rx_progress += 1; context.rx_progress += 1;
} });
}
isr_finish_handler(idx, spi, context, transfer_len, enabled_irqs) isr_finish_handler(idx, spi, context, transfer_len)
}
fn calculate_read_len(
spi: &mut super::regs::MmioSpi<'static>,
isr: InterruptStatus,
total_read_len: usize,
rx_progress: usize,
) -> usize {
if isr.rx() {
core::cmp::min(super::FIFO_DEPTH, total_read_len - rx_progress)
} else if spi.read_status().rx_not_empty() {
let fifo_level = spi.read_state().rx_fifo();
core::cmp::min(total_read_len - rx_progress, fifo_level as usize)
} else {
0
}
} }
/// Generic handler after RX FIFO and TX FIFO were handled. Checks and handles finished /// Generic handler after RX FIFO and TX FIFO were handled. Checks and handles finished
@@ -217,73 +198,49 @@ fn isr_finish_handler(
spi: &mut super::regs::MmioSpi<'static>, spi: &mut super::regs::MmioSpi<'static>,
context: &mut TransferContext, context: &mut TransferContext,
transfer_len: usize, transfer_len: usize,
enabled: InterruptControl,
) { ) {
// Transfer finish condition. // Transfer finish condition.
if context.rx_progress == context.tx_progress && context.rx_progress == transfer_len { if context.rx_progress == context.tx_progress && context.rx_progress == transfer_len {
finish_transfer(spi, idx, context); finish_transfer(idx, context, spi);
return; return;
} }
unfinished_transfer(spi, transfer_len, context.rx_progress);
// If the transfer is done, the context structure was already written back. // If the transfer is done, the context structure was already written back.
// Write back updated context structure. // Write back updated context structure.
critical_section::with(|cs| { critical_section::with(|cs| {
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs); let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
*context_ref.borrow_mut() = *context; *context_ref.borrow_mut() = *context;
}); });
unfinished_transfer(spi, transfer_len, context, enabled);
} }
fn finish_transfer( fn finish_transfer(
spi: &mut super::regs::MmioSpi<'static>,
idx: usize, idx: usize,
context: &mut TransferContext, context: &mut TransferContext,
spi: &mut super::regs::MmioSpi<'static>,
) { ) {
// Write back updated context structure. // Write back updated context structure.
critical_section::with(|cs| { critical_section::with(|cs| {
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs); let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
*context_ref.borrow_mut() = *context; *context_ref.borrow_mut() = *context;
}); });
// Clean up, restore clean state. spi.write_rx_fifo_trigger(TriggerLevel::new(u5::new(0x08)));
reset_trigger_levels(spi); spi.write_tx_fifo_trigger(TriggerLevel::new(u5::new(0x00)));
spi.write_fifo_clear(FifoClear::ALL);
// Interrupts were already disabled and cleared. // Interrupts were already disabled and cleared.
DONE[idx].store(true, core::sync::atomic::Ordering::Relaxed); DONE[idx].store(true, core::sync::atomic::Ordering::Relaxed);
WAKERS[idx].wake(); WAKERS[idx].wake();
} }
#[inline]
fn unfinished_transfer( fn unfinished_transfer(
spi: &mut super::regs::MmioSpi<'static>, spi: &mut super::regs::MmioSpi<'static>,
transfer_len: usize, transfer_len: usize,
context: &TransferContext, rx_progress: usize,
enabled_irqs: InterruptControl,
) { ) {
// Take 8 as a conservative value to make sure that the FIFO does not overflow even if there let new_trig_level = core::cmp::min(super::FIFO_DEPTH, transfer_len - rx_progress);
// is a significant delay between the interrupt being triggered and the handler being executed.
let new_trig_level = core::cmp::min(8, transfer_len - context.rx_progress);
spi.write_rx_fifo_trigger(TriggerLevel::new(u5::new(new_trig_level as u8))); spi.write_rx_fifo_trigger(TriggerLevel::new(u5::new(new_trig_level as u8)));
// If TX was already enabled and the transfer is finished, stop enabling it. Otherwise, we can
// become stuck in an interrupt loop. In any other case, enable it. I am not fully sure
// why this is necessary and why we can not stop interrupts as soon as we have the full
// TX progress, but tests with ADCs have shown that not doing this causes timeouts.
let enable_tx = !(enabled_irqs.tx() && context.tx_progress == transfer_len);
// Re-enable interrupts with the new RX FIFO trigger level. // Re-enable interrupts with the new RX FIFO trigger level.
spi.write_interrupt_control( spi.write_interrupt_control(InterruptControl::ENABLE_ALL);
InterruptControl::builder()
.with_tx(enable_tx)
.with_rx(true)
.with_rx_timeout(true)
.with_rx_overrun(true)
.build(),
);
}
#[inline]
fn reset_trigger_levels(spi: &mut super::regs::MmioSpi<'static>) {
spi.write_rx_fifo_trigger(TriggerLevel::new(u5::new(0x08)));
spi.write_tx_fifo_trigger(TriggerLevel::new(u5::new(0x00)));
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
@@ -302,7 +259,6 @@ pub struct TransferContext {
rx_progress: usize, rx_progress: usize,
tx_slice: RawBufSlice, tx_slice: RawBufSlice,
rx_slice: RawBufSliceMut, rx_slice: RawBufSliceMut,
rx_overrun: bool,
} }
#[allow(clippy::new_without_default)] #[allow(clippy::new_without_default)]
@@ -314,7 +270,6 @@ impl TransferContext {
rx_progress: 0, rx_progress: 0,
tx_slice: RawBufSlice::new_nulled(), tx_slice: RawBufSlice::new_nulled(),
rx_slice: RawBufSliceMut::new_nulled(), rx_slice: RawBufSliceMut::new_nulled(),
rx_overrun: false,
} }
} }
} }
@@ -330,30 +285,33 @@ impl<'spi> SpiFuture<'spi> {
if words.is_empty() { if words.is_empty() {
panic!("words length unexpectedly 0"); panic!("words length unexpectedly 0");
} }
Self::generic_init_transfer(spi, bank); let idx = bank as usize;
DONE[idx].store(false, core::sync::atomic::Ordering::Relaxed);
let write_index = core::cmp::min(super::FIFO_DEPTH, words.len()); spi.regs
.write_interrupt_control(InterruptControl::DISABLE_ALL);
spi.regs.write_fifo_clear(FifoClear::ALL);
spi.regs.modify_ctrl1(|v| v.with_mtxpause(true));
let write_idx = core::cmp::min(super::FIFO_DEPTH, words.len());
// Send dummy bytes. // Send dummy bytes.
(0..write_index).for_each(|_| { (0..write_idx).for_each(|_| {
spi.regs.write_data(Data::new_with_raw_value(0)); spi.regs.write_data(Data::new_with_raw_value(0));
}); });
Self::set_triggers(spi, write_index, words.len()); Self::set_triggers(spi, write_idx, words.len());
critical_section::with(|cs| { critical_section::with(|cs| {
let context_ref = TRANSFER_CONTEXTS[bank as usize].borrow(cs); let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
let mut context = context_ref.borrow_mut(); let mut context = context_ref.borrow_mut();
context.transfer_type = Some(TransferType::Read); context.transfer_type = Some(TransferType::Read);
unsafe { unsafe {
context.rx_slice.set(words); context.rx_slice.set(words);
} }
context.tx_slice.set_null(); context.tx_slice.set_null();
context.tx_progress = write_index; context.tx_progress = write_idx;
context.rx_progress = 0; context.rx_progress = 0;
spi.regs.write_interrupt_clear(InterruptClear::ALL); spi.regs.write_interrupt_clear(InterruptClear::ALL);
spi.regs.write_interrupt_control( spi.regs
InterruptControl::ENABLE_ALL.with_tx(words.len() > FIFO_DEPTH), .write_interrupt_control(InterruptControl::ENABLE_ALL);
);
spi.regs.modify_ctrl1(|v| v.with_mtxpause(false)); spi.regs.modify_ctrl1(|v| v.with_mtxpause(false));
}); });
Self { Self {
@@ -367,22 +325,20 @@ impl<'spi> SpiFuture<'spi> {
if words.is_empty() { if words.is_empty() {
panic!("words length unexpectedly 0"); panic!("words length unexpectedly 0");
} }
let index = bank as usize; let (idx, write_idx) = Self::generic_init_transfer(spi, bank, words);
let write_index = Self::generic_init_transfer_write_transfer_in_place(spi, bank, words);
critical_section::with(|cs| { critical_section::with(|cs| {
let context_ref = TRANSFER_CONTEXTS[index].borrow(cs); let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
let mut context = context_ref.borrow_mut(); let mut context = context_ref.borrow_mut();
context.transfer_type = Some(TransferType::Write); context.transfer_type = Some(TransferType::Write);
unsafe { unsafe {
context.tx_slice.set(words); context.tx_slice.set(words);
} }
context.rx_slice.set_null(); context.rx_slice.set_null();
context.tx_progress = write_index; context.tx_progress = write_idx;
context.rx_progress = 0; context.rx_progress = 0;
spi.regs.write_interrupt_clear(InterruptClear::ALL); spi.regs.write_interrupt_clear(InterruptClear::ALL);
spi.regs.write_interrupt_control( spi.regs
InterruptControl::ENABLE_ALL.with_tx(words.len() > FIFO_DEPTH), .write_interrupt_control(InterruptControl::ENABLE_ALL);
);
spi.regs.modify_ctrl1(|v| v.with_mtxpause(false)); spi.regs.modify_ctrl1(|v| v.with_mtxpause(false));
}); });
Self { Self {
@@ -394,44 +350,31 @@ impl<'spi> SpiFuture<'spi> {
fn new_for_transfer( fn new_for_transfer(
spi: &'spi mut super::Spi<u8>, spi: &'spi mut super::Spi<u8>,
bank: super::Bank, spi_id: super::Bank,
read: &mut [u8], read: &mut [u8],
write: &[u8], write: &[u8],
) -> Self { ) -> Self {
if read.is_empty() || write.is_empty() { if read.is_empty() || write.is_empty() {
panic!("read or write buffer unexpectedly empty"); panic!("read or write buffer unexpectedly empty");
} }
let index = bank as usize; let (idx, write_idx) = Self::generic_init_transfer(spi, spi_id, write);
let full_write_len = core::cmp::max(read.len(), write.len());
let fifo_prefill = core::cmp::min(super::FIFO_DEPTH, full_write_len);
Self::generic_init_transfer(spi, bank);
for write_index in 0..fifo_prefill {
let value = write.get(write_index).copied().unwrap_or(0);
spi.regs.write_data(Data::new_with_raw_value(value as u32));
}
Self::set_triggers(spi, fifo_prefill, full_write_len);
critical_section::with(|cs| { critical_section::with(|cs| {
let context_ref = TRANSFER_CONTEXTS[index].borrow(cs); let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
let mut context = context_ref.borrow_mut(); let mut context = context_ref.borrow_mut();
context.transfer_type = Some(TransferType::Transfer); context.transfer_type = Some(TransferType::Transfer);
unsafe { unsafe {
context.tx_slice.set(write); context.tx_slice.set(write);
context.rx_slice.set(read); context.rx_slice.set(read);
} }
context.tx_progress = fifo_prefill; context.tx_progress = write_idx;
context.rx_progress = 0; context.rx_progress = 0;
spi.regs.write_interrupt_clear(InterruptClear::ALL); spi.regs.write_interrupt_clear(InterruptClear::ALL);
spi.regs.write_interrupt_control( spi.regs
InterruptControl::ENABLE_ALL.with_tx(fifo_prefill > FIFO_DEPTH), .write_interrupt_control(InterruptControl::ENABLE_ALL);
);
spi.regs.modify_ctrl1(|v| v.with_mtxpause(false)); spi.regs.modify_ctrl1(|v| v.with_mtxpause(false));
}); });
Self { Self {
bank, bank: spi_id,
spi, spi,
finished_regularly: core::cell::Cell::new(false), finished_regularly: core::cell::Cell::new(false),
} }
@@ -439,15 +382,15 @@ impl<'spi> SpiFuture<'spi> {
fn new_for_transfer_in_place( fn new_for_transfer_in_place(
spi: &'spi mut super::Spi<u8>, spi: &'spi mut super::Spi<u8>,
bank: super::Bank, spi_id: super::Bank,
words: &mut [u8], words: &mut [u8],
) -> Self { ) -> Self {
if words.is_empty() { if words.is_empty() {
panic!("read and write buffer unexpectedly empty"); panic!("read and write buffer unexpectedly empty");
} }
let write_idx = Self::generic_init_transfer_write_transfer_in_place(spi, bank, words); let (idx, write_idx) = Self::generic_init_transfer(spi, spi_id, words);
critical_section::with(|cs| { critical_section::with(|cs| {
let context_ref = TRANSFER_CONTEXTS[bank as usize].borrow(cs); let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
let mut context = context_ref.borrow_mut(); let mut context = context_ref.borrow_mut();
context.transfer_type = Some(TransferType::TransferInPlace); context.transfer_type = Some(TransferType::TransferInPlace);
unsafe { unsafe {
@@ -457,34 +400,28 @@ impl<'spi> SpiFuture<'spi> {
context.tx_progress = write_idx; context.tx_progress = write_idx;
context.rx_progress = 0; context.rx_progress = 0;
spi.regs.write_interrupt_clear(InterruptClear::ALL); spi.regs.write_interrupt_clear(InterruptClear::ALL);
spi.regs.write_interrupt_control( spi.regs
InterruptControl::ENABLE_ALL.with_tx(words.len() > FIFO_DEPTH), .write_interrupt_control(InterruptControl::ENABLE_ALL);
);
spi.regs.modify_ctrl1(|v| v.with_mtxpause(false)); spi.regs.modify_ctrl1(|v| v.with_mtxpause(false));
}); });
Self { Self {
bank, bank: spi_id,
spi, spi,
finished_regularly: core::cell::Cell::new(false), finished_regularly: core::cell::Cell::new(false),
} }
} }
fn generic_init_transfer(spi: &mut super::Spi<u8>, bank: super::Bank) { fn generic_init_transfer(
spi: &mut super::Spi<u8>,
bank: super::Bank,
write: &[u8],
) -> (usize, usize) {
let idx = bank as usize; let idx = bank as usize;
DONE[idx].store(false, core::sync::atomic::Ordering::Relaxed); DONE[idx].store(false, core::sync::atomic::Ordering::Relaxed);
spi.regs spi.regs
.write_interrupt_control(InterruptControl::DISABLE_ALL); .write_interrupt_control(InterruptControl::DISABLE_ALL);
spi.regs.write_fifo_clear(FifoClear::ALL); spi.regs.write_fifo_clear(FifoClear::ALL);
spi.regs.modify_ctrl1(|v| v.with_mtxpause(true)); spi.regs.modify_ctrl1(|v| v.with_mtxpause(true));
}
// Returns amount of bytes written to FIFO.
fn generic_init_transfer_write_transfer_in_place(
spi: &mut super::Spi<u8>,
bank: super::Bank,
write: &[u8],
) -> usize {
Self::generic_init_transfer(spi, bank);
let write_idx = core::cmp::min(super::FIFO_DEPTH, write.len()); let write_idx = core::cmp::min(super::FIFO_DEPTH, write.len());
(0..write_idx).for_each(|idx| { (0..write_idx).for_each(|idx| {
@@ -493,21 +430,20 @@ impl<'spi> SpiFuture<'spi> {
}); });
Self::set_triggers(spi, write_idx, write.len()); Self::set_triggers(spi, write_idx, write.len());
write_idx (idx, write_idx)
} }
fn set_triggers(spi: &mut super::Spi<u8>, fifo_prefill: usize, write_len: usize) { fn set_triggers(spi: &mut super::Spi<u8>, write_idx: usize, write_len: usize) {
// This should never fail because it is never larger than the FIFO depth.
spi.regs spi.regs
.write_rx_fifo_trigger(TriggerLevel::new(u5::new(core::cmp::min( .write_rx_fifo_trigger(TriggerLevel::new(u5::new(write_idx as u8)));
fifo_prefill,
FIFO_DEPTH / 2,
) as u8)));
// We want to re-fill the TX FIFO before it is completely empty if the full transfer size // We want to re-fill the TX FIFO before it is completely empty if the full transfer size
// is larger than the FIFO depth. Otherwise, set it to 0. Not exactly sure what that does, // is larger than the FIFO depth. I am not sure whether the default value of 1 ensures
// but we do not enable interrupts anyway. // this because the PG says that this interrupt is triggered when the FIFO has less than
// threshold entries.
if write_len > super::FIFO_DEPTH { if write_len > super::FIFO_DEPTH {
spi.regs spi.regs
.write_tx_fifo_trigger(TriggerLevel::new(u5::new(8))); .write_tx_fifo_trigger(TriggerLevel::new(u5::new(2)));
} else { } else {
spi.regs spi.regs
.write_tx_fifo_trigger(TriggerLevel::new(u5::new(0))); .write_tx_fifo_trigger(TriggerLevel::new(u5::new(0)));
@@ -516,7 +452,7 @@ impl<'spi> SpiFuture<'spi> {
} }
impl<'spi> Future for SpiFuture<'spi> { impl<'spi> Future for SpiFuture<'spi> {
type Output = Result<(), RxOverrunError>; type Output = ();
fn poll( fn poll(
self: core::pin::Pin<&mut Self>, self: core::pin::Pin<&mut Self>,
@@ -524,19 +460,14 @@ impl<'spi> Future for SpiFuture<'spi> {
) -> core::task::Poll<Self::Output> { ) -> core::task::Poll<Self::Output> {
WAKERS[self.bank as usize].register(cx.waker()); WAKERS[self.bank as usize].register(cx.waker());
if DONE[self.bank as usize].swap(false, core::sync::atomic::Ordering::Relaxed) { if DONE[self.bank as usize].swap(false, core::sync::atomic::Ordering::Relaxed) {
let rx_overrun = critical_section::with(|cs| { critical_section::with(|cs| {
let mut ctx = TRANSFER_CONTEXTS[self.bank as usize] let mut ctx = TRANSFER_CONTEXTS[self.bank as usize]
.borrow(cs) .borrow(cs)
.borrow_mut(); .borrow_mut();
let overrun = ctx.rx_overrun;
*ctx = TransferContext::default(); *ctx = TransferContext::default();
overrun
}); });
self.finished_regularly.set(true); self.finished_regularly.set(true);
if rx_overrun { return core::task::Poll::Ready(());
return core::task::Poll::Ready(Err(RxOverrunError));
}
return core::task::Poll::Ready(Ok(()));
} }
core::task::Poll::Pending core::task::Poll::Pending
} }
@@ -623,36 +554,28 @@ impl SpiAsync {
} }
impl embedded_hal_async::spi::ErrorType for SpiAsync { impl embedded_hal_async::spi::ErrorType for SpiAsync {
type Error = RxOverrunError; type Error = Infallible;
} }
impl embedded_hal_async::spi::SpiBus for SpiAsync { impl embedded_hal_async::spi::SpiBus for SpiAsync {
async fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { async fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
if words.is_empty() { self.read(words).unwrap().await;
return Ok(()); Ok(())
}
self.read(words).unwrap().await
} }
async fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { async fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
if words.is_empty() { self.write(words).unwrap().await;
return Ok(()); Ok(())
}
self.write(words).unwrap().await
} }
async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> { async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
if read.is_empty() && write.is_empty() { self.transfer(read, write).unwrap().await;
return Ok(()); Ok(())
}
self.transfer(read, write).unwrap().await
} }
async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
if words.is_empty() { self.transfer_in_place(words).unwrap().await;
return Ok(()); Ok(())
}
self.transfer_in_place(words).unwrap().await
} }
async fn flush(&mut self) -> Result<(), Self::Error> { async fn flush(&mut self) -> Result<(), Self::Error> {