From 997e0502ab9cd158880128c7cd9bb1fdc89e2465 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Mon, 28 Apr 2025 18:51:37 +0200 Subject: [PATCH] finished first impl --- va416xx-hal/Cargo.toml | 3 +- va416xx-hal/src/can/asynch.rs | 289 +++++++++++++++++++++++++++ va416xx-hal/src/can/frame.rs | 3 + va416xx-hal/src/can/ll.rs | 309 +++++++++++++++++++++++++++++ va416xx-hal/src/can/mod.rs | 360 ++++++++++++++++++---------------- va416xx-hal/src/can/regs.rs | 178 +++++++++++------ 6 files changed, 914 insertions(+), 228 deletions(-) create mode 100644 va416xx-hal/src/can/asynch.rs create mode 100644 va416xx-hal/src/can/ll.rs diff --git a/va416xx-hal/Cargo.toml b/va416xx-hal/Cargo.toml index d9687ab..42730a4 100644 --- a/va416xx-hal/Cargo.toml +++ b/va416xx-hal/Cargo.toml @@ -13,7 +13,7 @@ categories = ["embedded", "no-std", "hardware-support"] [dependencies] cortex-m = { version = "0.7", features = ["critical-section-single-core"] } va416xx = { version = "0.4", features = ["critical-section"], default-features = false } -derive-mmio = "0.4" +derive-mmio = { version = "0.4", git = "https://github.com/us-irs/derive-mmio.git", branch = "more-constness" } vorago-shared-periphs = { git = "https://egit.irs.uni-stuttgart.de/rust/vorago-shared-periphs.git", features = ["vor4x"] } libm = "0.2" @@ -25,6 +25,7 @@ bitbybit = "1.3" arbitrary-int = "1.3" fugit = "0.3" embedded-can = "0.4" +embassy-sync = "0.6" thiserror = { version = "2", default-features = false } defmt = { version = "0.3", optional = true } diff --git a/va416xx-hal/src/can/asynch.rs b/va416xx-hal/src/can/asynch.rs new file mode 100644 index 0000000..eca03cf --- /dev/null +++ b/va416xx-hal/src/can/asynch.rs @@ -0,0 +1,289 @@ +use core::{ + future::Future, + sync::atomic::{AtomicU8, Ordering}, + usize, +}; + +use crate::can::regs::BufferState; + +use super::{ + regs::{DiagnosticRegister, InterruptClear, MmioCan, StatusPending}, + CanChannelLowLevel, CanFrame, CanId, +}; + +#[derive(Debug)] +pub enum TxChannelState { + Unconfigured = 0, + Idle = 1, + TxDataFrame = 2, + TxRtrTransmission = 3, + TxRtrReception = 4, + Finished = 5, +} + +static TX_STATES: [AtomicU8; 15] = [const { AtomicU8::new(0) }; 15]; +static TX_WAKERS: [embassy_sync::waitqueue::AtomicWaker; 15] = + [const { embassy_sync::waitqueue::AtomicWaker::new() }; 15]; + +#[derive(Debug)] +pub enum TxEventId { + /// Buffer state went from [BufferState::TxOnce] to [BufferState::TxNotActive]. + TxDataFrame, + /// Buffer state went from [BufferState::TxOnce] to [BufferState::TxNotActive] for a remote + /// frame (RTR bit set). Channel might be in reception mode [BufferState::RxReady] now. + TxRemoteFrame, + /// A response to a remote frame was performed successfully, and the buffer state went from + /// [BufferState::TxOnceRtr] to [BufferState::TxRtr]. + RtrResponse, + /// A remote frame was received and the transmission of a response frame was scheduled. The + /// buffer state went from [BufferState::TxRtr] to [BufferState::TxOnceRtr]. + TransmitScheduling, +} + +#[derive(Debug)] +pub enum InterruptResult { + NoInterrupt, + ReceivedFrame { + channel_index: usize, + frame: CanFrame, + }, + TransmissionEvent { + channel_index: usize, + id: TxEventId, + }, +} + +pub enum InterruptError { + UnexpectedError, + InvalidInterruptId(StatusPending), + InvalidStatus(u8), + UnexpectedState(BufferState), + CanError(DiagnosticRegister), +} + +/// This interrupt handler allow asynchronous transmission and reception of CAN frames. +/// +/// This handler will re-configure a channel to [BufferState::RxReady] after successfull reception +/// of a frame without disabling the interrupts, assuming that the user wants to immediately +/// receive the next frame on the channel. +/// The user should re-configure the buffer state to [BufferState::RxNotActive] if the reception +/// should be disabled. +/// +/// The handler will re-configure a channel to [BufferState::TxNotActive] instead of +/// [BufferState::RxReady] if the completed frame transmission was a remote frame and after +/// successfully having received a response to that remote frame. The assumption is that this +/// channel is used to request more frames. If the argument `reconfigure_tx_rtr_to_tx` is set to +/// true, the channel will automatically be configured back to [BufferState::TxNotActive] with +/// interrupts for the respective channel disabled after transmission of a remote frame. +/// +/// The handler will not disable the interrupts realted to the TX RTR and TX RTR ONCE auto-response +/// functionality of the CAN peripheral. It will report the event type to the caller via the +/// [TxEventId] enumeration. +pub fn on_interrupt_can( + id: CanId, + reconfigure_tx_rtr_to_tx: bool, +) -> Result { + let mut regs = unsafe { id.steal_regs() }; + // Check if any interrupts are enabled. + let ie = regs.read_ien(); + if ie.raw_value() == 0 { + return Ok(InterruptResult::NoInterrupt); + } + let pending_id = regs.read_status_pending(); + if pending_id.interrupt_id().is_none() { + regs.write_iclr(InterruptClear::new_with_raw_value(0xFFFF_FFFF)); + return Err(InterruptError::InvalidInterruptId(pending_id)); + } + match pending_id.interrupt_id().unwrap() { + super::regs::CanInterruptId::None => { + return Ok(InterruptResult::NoInterrupt); + } + super::regs::CanInterruptId::Error => { + return Err(InterruptError::CanError(regs.read_diag())); + } + super::regs::CanInterruptId::Buffer(idx) => { + let mut channel = unsafe { CanChannelLowLevel::steal_unchecked(id, idx) }; + let status = channel.read_status(); + if status.is_err() { + let mut clr = InterruptClear::new_with_raw_value(0); + clr.set_buffer(idx as usize, true); + regs.write_iclr(clr); + regs.modify_ien(|mut val| { + val.set_buffer(idx as usize, false); + val + }); + return Err(InterruptError::InvalidStatus(status.unwrap_err())); + } + let buf_state = status.unwrap(); + if buf_state == BufferState::TxNotActive { + let tx_state = TX_STATES[idx as usize].load(Ordering::Relaxed); + clear_and_disable_interrupt(&mut regs, idx); + // Handle reading frames, updating states etc. + if tx_state == TxChannelState::TxDataFrame as u8 { + // Transmission complete. + TX_STATES[idx as usize] + .store(TxChannelState::Finished as u8, Ordering::Relaxed); + TX_WAKERS[idx as usize].wake(); + return Ok(InterruptResult::TransmissionEvent { + channel_index: idx, + id: TxEventId::TxDataFrame, + }); + } + } + if buf_state == BufferState::RxReady { + let tx_state = TX_STATES[idx as usize].load(Ordering::Relaxed); + if tx_state == TxChannelState::TxRtrTransmission as u8 { + if reconfigure_tx_rtr_to_tx { + channel.write_status(BufferState::TxNotActive); + clear_and_disable_interrupt(&mut regs, idx); + // Transmission complete. + TX_STATES[idx as usize] + .store(TxChannelState::Idle as u8, Ordering::Relaxed); + } else { + // Do not disable interrupt, channel is now used to receive the frame. + clear_interrupt(&mut regs, idx); + // Transmission complete. + TX_STATES[idx as usize] + .store(TxChannelState::TxRtrReception as u8, Ordering::Relaxed); + } + TX_WAKERS[idx as usize].wake(); + return Ok(InterruptResult::TransmissionEvent { + channel_index: idx, + id: TxEventId::TxRemoteFrame, + }); + } + } + if buf_state == BufferState::RxOverrun || buf_state == BufferState::RxFull { + let tx_state = TX_STATES[idx as usize].load(Ordering::Relaxed); + // Do not disable interrupt and assume continuous reception. + clear_interrupt(&mut regs, idx); + let frame = channel.read_frame_unchecked(); + if tx_state == TxChannelState::TxRtrReception as u8 { + // Reception of response complete. We can release the channel for TX (or RX) + // usage again. + TX_STATES[idx as usize].store(TxChannelState::Idle as u8, Ordering::Relaxed); + channel.write_status(BufferState::TxNotActive); + } else { + // Assume continous reception of frames. + channel.write_status(BufferState::RxReady); + } + return Ok(InterruptResult::ReceivedFrame { + channel_index: idx, + frame, + }); + } + if buf_state == BufferState::TxRtr { + // Do not disable interrupt and assume continuous transmission. + clear_interrupt(&mut regs, idx); + return Ok(InterruptResult::TransmissionEvent { + channel_index: idx, + id: TxEventId::RtrResponse, + }); + } + if buf_state == BufferState::TxOnceRtr { + // Do not disable interrupt and assume continuous transmission. + clear_interrupt(&mut regs, idx); + return Ok(InterruptResult::TransmissionEvent { + channel_index: idx, + id: TxEventId::TransmitScheduling, + }); + } + + Err(InterruptError::UnexpectedState(buf_state)) + } + } +} + +#[inline(always)] +fn clear_interrupt(regs: &mut MmioCan<'static>, idx: usize) { + let mut clr = InterruptClear::new_with_raw_value(0); + clr.set_buffer(idx, true); + regs.write_iclr(clr); +} + +#[inline(always)] +fn clear_and_disable_interrupt(regs: &mut MmioCan<'static>, idx: usize) { + clear_interrupt(regs, idx); + regs.modify_ien(|mut val| { + val.set_buffer(idx as usize, false); + val + }); +} + +#[derive(Debug, thiserror::Error)] +#[error("all channels are unconfigured, none available for TX")] +pub struct AllTxChannelsUnconfiguredError; + +pub struct CanTxFuture(usize); + +impl Future for CanTxFuture { + type Output = (); + + fn poll( + self: core::pin::Pin<&mut Self>, + cx: &mut core::task::Context<'_>, + ) -> core::task::Poll { + TX_WAKERS[self.0].register(cx.waker()); + if TX_STATES[self.0].load(Ordering::Relaxed) == TxChannelState::Finished as u8 { + TX_STATES[self.0].store(TxChannelState::Idle as u8, Ordering::Relaxed); + return core::task::Poll::Ready(()); + } + core::task::Poll::Pending + } +} + +impl CanTxFuture { + pub fn new(frame: CanFrame) -> nb::Result { + let mut channel_is_free = [false; 15]; + let mut all_channels_unused = true; + for (idx, state) in TX_STATES.iter().enumerate() { + let state = state.load(Ordering::Relaxed); + if state == TxChannelState::Idle as u8 { + channel_is_free[idx] = true; + } + if state != TxChannelState::Unconfigured as u8 { + all_channels_unused = false; + } + } + if channel_is_free.iter().all(|&x| !x) { + return Err(nb::Error::WouldBlock); + } + if all_channels_unused { + return Err(nb::Error::Other(AllTxChannelsUnconfiguredError)); + } + let free_channel_id = channel_is_free.iter().position(|&x| x).unwrap(); + let mut channel = + unsafe { CanChannelLowLevel::steal_unchecked(CanId::Can0, free_channel_id) }; + channel.enable_interrupt(true); + channel.enable_error_interrupt(true); + channel.transmit_frame_unchecked(frame); + + TX_STATES[free_channel_id].store(TxChannelState::TxDataFrame as u8, Ordering::Relaxed); + Ok(CanTxFuture(free_channel_id)) + } +} + +pub struct CanTxAsync(pub super::Can); + +impl CanTxAsync { + pub fn new(can: super::Can) -> Self { + CanTxAsync(can) + } + + /// Start a transmission and returns the future which can be polled to completion. + pub fn start_transmit( + &mut self, + frame: CanFrame, + ) -> nb::Result { + CanTxFuture::new(frame) + } + + /// Calls [Self::start_transmit] and awaits the returned future to completion immediately. + pub async fn transmit( + &mut self, + frame: CanFrame, + ) -> nb::Result<(), AllTxChannelsUnconfiguredError> { + self.start_transmit(frame)?.await; + Ok(()) + } +} diff --git a/va416xx-hal/src/can/frame.rs b/va416xx-hal/src/can/frame.rs index 033f9fc..3c3e8bc 100644 --- a/va416xx-hal/src/can/frame.rs +++ b/va416xx-hal/src/can/frame.rs @@ -2,6 +2,7 @@ #[error("invalid data size error {0}")] pub struct InvalidDataSizeError(usize); +#[derive(Debug)] pub struct CanFrameNormal { id: embedded_can::Id, size: usize, @@ -33,6 +34,7 @@ impl CanFrameNormal { } } +#[derive(Debug)] pub struct CanFrameRtr { id: embedded_can::Id, dlc: usize, @@ -52,6 +54,7 @@ impl CanFrameRtr { } } +#[derive(Debug)] pub enum CanFrame { Normal(CanFrameNormal), Rtr(CanFrameRtr), diff --git a/va416xx-hal/src/can/ll.rs b/va416xx-hal/src/can/ll.rs new file mode 100644 index 0000000..322a1d6 --- /dev/null +++ b/va416xx-hal/src/can/ll.rs @@ -0,0 +1,309 @@ +use arbitrary_int::{u11, u15, u3, u4, Number}; +use embedded_can::Frame; + +use super::{ + regs::{BaseId, BufStatusAndControl, BufferState, ExtendedId, MmioCanMsgBuf, TwoBytesData}, + CanFrame, CanFrameNormal, CanFrameRtr, CanId, InvalidBufferIndexError, +}; + +pub struct CanChannelLowLevel { + id: CanId, + /// Message buffer index. + idx: usize, + msg_buf: MmioCanMsgBuf<'static>, +} + +impl core::fmt::Debug for CanChannelLowLevel { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("CanChannel") + .field("can_id", &self.id) + .field("idx", &self.idx) + .finish() + } +} + +impl CanChannelLowLevel { + #[inline] + pub unsafe fn steal(can: CanId, idx: usize) -> Result { + if idx > 14 { + return Err(InvalidBufferIndexError(idx)); + } + let msg_buf = unsafe { can.steal_regs().steal_cmbs_unchecked(idx) }; + Ok(Self { + id: can, + idx, + msg_buf, + }) + } + + #[inline] + pub const unsafe fn steal_unchecked(can: CanId, idx: usize) -> Self { + if idx > 14 { + panic!("invalid buffer index for CAN low level channel"); + } + let msg_buf = unsafe { can.steal_regs().steal_cmbs_unchecked(idx) }; + Self { + id: can, + idx, + msg_buf, + } + } + + /// # Safety + /// + /// Allows to create an aribtrary amoutn of driver handles to the same message block, which + /// might lead to data races on invalid usage. + #[inline] + pub const unsafe fn clone(&self) -> Self { + Self { + id: self.id, + idx: self.idx, + msg_buf: unsafe { self.msg_buf.clone() }, + } + } + + #[inline] + pub fn read_status(&self) -> Result { + self.msg_buf.read_stat_ctrl().status() + } + + #[inline] + pub fn write_status(&mut self, buffer_state: BufferState) { + self.msg_buf.modify_stat_ctrl(|mut val| { + val.set_status(buffer_state); + val + }); + } + + pub fn configure_for_transmission( + &mut self, + tx_priority: Option, + ) -> Result<(), InvalidBufferIndexError> { + self.msg_buf.modify_stat_ctrl(|mut val| { + val.set_dlc(u4::new(0)); + if let Some(tx_priority) = tx_priority { + val.set_priority(tx_priority); + } + val.set_status(BufferState::TxNotActive); + val + }); + Ok(()) + } + + pub fn configure_for_reception_with_standard_id( + &mut self, + standard_id: embedded_can::StandardId, + set_rtr: bool, + ) -> Result<(), InvalidBufferIndexError> { + let mut id1_reg = standard_id.as_raw() << 5; + if set_rtr { + id1_reg |= 1 << 4; + } + self.msg_buf + .write_id1(BaseId::new_with_raw_value(id1_reg as u32)); + + self.msg_buf.write_stat_ctrl( + BufStatusAndControl::builder() + .with_dlc(u4::new(0)) + .with_priority(u4::new(0)) + .with_status(BufferState::RxReady) + .build(), + ); + Ok(()) + } + + pub fn configure_for_reception_with_extended_id( + &mut self, + extended_id: embedded_can::ExtendedId, + set_rtr: bool, + ) -> Result<(), InvalidBufferIndexError> { + let id_raw = extended_id.as_raw(); + let id1_reg = (((id_raw >> 18) & 0x7FF) << 4) as u16 | ((id_raw >> 15) & 0b111) as u16; + self.msg_buf + .write_id1(BaseId::new_with_raw_value(id1_reg as u32)); + let id0_reg = ((id_raw & 0x7FFF) << 1) as u16 | set_rtr as u16; + self.msg_buf + .write_id0(ExtendedId::new_with_raw_value(id0_reg as u32)); + self.msg_buf.write_stat_ctrl( + BufStatusAndControl::builder() + .with_dlc(u4::new(0)) + .with_priority(u4::new(0)) + .with_status(BufferState::RxReady) + .build(), + ); + Ok(()) + } + + pub fn transmit_frame_unchecked(&mut self, frame: CanFrame) { + let is_remote = frame.is_remote_frame(); + self.write_id(frame.id(), is_remote); + let dlc = frame.dlc(); + if !is_remote { + self.msg_buf + .write_data0(TwoBytesData::new_with_raw_value(0)); + self.msg_buf + .write_data1(TwoBytesData::new_with_raw_value(0)); + self.msg_buf + .write_data2(TwoBytesData::new_with_raw_value(0)); + self.msg_buf + .write_data3(TwoBytesData::new_with_raw_value(0)); + for idx in 0..dlc { + match idx { + 0 => self.msg_buf.modify_data3(|mut val| { + val.set_data_upper_byte(frame.data()[idx]); + val + }), + 1 => self.msg_buf.modify_data3(|mut val| { + val.set_data_lower_byte(frame.data()[idx]); + val + }), + 2 => self.msg_buf.modify_data2(|mut val| { + val.set_data_upper_byte(frame.data()[idx]); + val + }), + 3 => self.msg_buf.modify_data2(|mut val| { + val.set_data_lower_byte(frame.data()[idx]); + val + }), + 4 => self.msg_buf.modify_data1(|mut val| { + val.set_data_upper_byte(frame.data()[idx]); + val + }), + 5 => self.msg_buf.modify_data1(|mut val| { + val.set_data_lower_byte(frame.data()[idx]); + val + }), + 6 => self.msg_buf.modify_data0(|mut val| { + val.set_data_upper_byte(frame.data()[idx]); + val + }), + 7 => self.msg_buf.modify_data0(|mut val| { + val.set_data_lower_byte(frame.data()[idx]); + val + }), + _ => unreachable!(), + } + } + } + self.msg_buf.modify_stat_ctrl(|mut ctrl| { + ctrl.set_dlc(u4::new(dlc as u8)); + ctrl.set_status(BufferState::TxOnce); + ctrl + }); + } + + pub fn enable_error_interrupt(&mut self, enable_translation: bool) { + let mut regs = unsafe { self.id.steal_regs() }; + if enable_translation { + regs.modify_icen(|mut val| { + val.set_error(true); + val + }); + } + regs.modify_ien(|mut val| { + val.set_error(true); + val + }); + } + + pub fn enable_interrupt(&mut self, enable_translation: bool) { + let mut regs = unsafe { self.id.steal_regs() }; + if enable_translation { + regs.modify_icen(|mut val| { + val.set_buffer(self.idx, true); + val + }); + } + regs.modify_ien(|mut val| { + val.set_buffer(self.idx, true); + val + }); + } + + fn write_id(&mut self, id: embedded_can::Id, is_remote: bool) { + match id { + embedded_can::Id::Standard(standard_id) => { + self.msg_buf.write_id1( + BaseId::builder() + .with_mask_28_18(u11::new(standard_id.as_raw())) + .with_rtr_or_srr(is_remote) + .with_ide(false) + .with_mask_17_15(u3::new(0)) + .build(), + ); + self.msg_buf.write_id0(ExtendedId::new_with_raw_value(0)); + } + embedded_can::Id::Extended(extended_id) => { + let id_raw = extended_id.as_raw(); + self.msg_buf.write_id1( + BaseId::builder() + .with_mask_28_18(u11::new(((id_raw >> 18) & 0x7FF) as u16)) + .with_rtr_or_srr(true) + .with_ide(true) + .with_mask_17_15(u3::new(((id_raw >> 15) & 0b111) as u8)) + .build(), + ); + self.msg_buf.write_id0( + ExtendedId::builder() + .with_mask_14_0(u15::new((id_raw & 0x7FFF) as u16)) + .with_xrtr(is_remote) + .build(), + ); + } + } + } + + /// Reads a received CAN frame from the message buffer. + /// + /// This function does not check whether the pre-requisites for reading a CAN frame were + /// met and assumes this was already checked by the user. + pub fn read_frame_unchecked(&self) -> CanFrame { + let id0 = self.msg_buf.read_id0(); + let id1 = self.msg_buf.read_id1(); + let data0 = self.msg_buf.read_data0(); + let data1 = self.msg_buf.read_data1(); + let data2 = self.msg_buf.read_data2(); + let data3 = self.msg_buf.read_data3(); + let mut data: [u8; 8] = [0; 8]; + let mut read_data = |dlc: u4| { + (0..dlc.as_usize()).for_each(|i| match i { + 0 => data[i] = data3.data_upper_byte().as_u8(), + 1 => data[i] = data3.data_lower_byte().as_u8(), + 2 => data[i] = data2.data_upper_byte().as_u8(), + 3 => data[i] = data2.data_lower_byte().as_u8(), + 4 => data[i] = data1.data_upper_byte().as_u8(), + 5 => data[i] = data1.data_lower_byte().as_u8(), + 6 => data[i] = data0.data_upper_byte().as_u8(), + 7 => data[i] = data0.data_lower_byte().as_u8(), + _ => unreachable!(), + }); + }; + let (id, rtr) = if !id1.ide() { + let id = embedded_can::Id::Standard( + embedded_can::StandardId::new(id1.mask_28_18().as_u16()).unwrap(), + ); + if id1.rtr_or_srr() { + (id, true) + } else { + (id, false) + } + } else { + let id_raw = (id1.mask_28_18().as_u32() << 18) + | (id1.mask_17_15().as_u32() << 15) + | id0.mask_14_0().as_u32(); + let id = embedded_can::Id::Extended(embedded_can::ExtendedId::new(id_raw).unwrap()); + if id0.xrtr() { + (id, true) + } else { + (id, false) + } + }; + if rtr { + CanFrameRtr::new(id, self.msg_buf.read_stat_ctrl().dlc().as_usize()).into() + } else { + let dlc = self.msg_buf.read_stat_ctrl().dlc(); + read_data(dlc); + CanFrameNormal::new(id, &data[0..dlc.as_usize()]).into() + } + } +} diff --git a/va416xx-hal/src/can/mod.rs b/va416xx-hal/src/can/mod.rs index c321e0d..a0e9cd8 100644 --- a/va416xx-hal/src/can/mod.rs +++ b/va416xx-hal/src/can/mod.rs @@ -1,19 +1,23 @@ //! CAN driver. //! //! The VA416xx CAN module is based on the CP3UB26 module. +use core::sync::atomic::AtomicBool; + use arbitrary_int::{u11, u15, u2, u3, u4, u7, Number}; use embedded_can::Frame; -use regs::{ - BaseId, BufStatusAndControl, Control, DataDirection, ExtendedId, MmioCan, TimingConfig, -}; +use ll::CanChannelLowLevel; +use regs::{BaseId, BufferState, Control, ExtendedId, MmioCan, TimingConfig}; use crate::{clock::Clocks, enable_peripheral_clock, time::Hertz, PeripheralSelect}; use libm::roundf; pub mod frame; -pub mod regs; pub use frame::*; +pub mod asynch; +pub mod ll; +pub mod regs; + pub const PRESCALER_MIN: u8 = 2; pub const PRESCALER_MAX: u8 = 128; /// 1 is the minimum value, but not recommended by Vorago. @@ -26,6 +30,8 @@ pub const SJW_MAX: u8 = 4; pub const MIN_SAMPLE_POINT: f32 = 0.5; pub const MAX_BITRATE_DEVIATION: f32 = 0.005; +static CHANNELS_TAKEN: [AtomicBool; 2] = [AtomicBool::new(false), AtomicBool::new(false)]; + #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum CanId { Can0 = 0, @@ -38,7 +44,7 @@ impl CanId { /// # Safety /// /// See safety of the [regs::Can::new_mmio_fixed_0]. - pub unsafe fn steal_regs(&self) -> regs::MmioCan<'static> { + pub const unsafe fn steal_regs(&self) -> regs::MmioCan<'static> { match self { CanId::Can0 => unsafe { regs::Can::new_mmio_fixed_0() }, CanId::Can1 => unsafe { regs::Can::new_mmio_fixed_1() }, @@ -122,17 +128,17 @@ pub fn calculate_all_viable_clock_configs( Ok(configs) } -pub trait Instance { +pub trait CanMarker { const ID: CanId; const PERIPH_SEL: PeripheralSelect; } -impl Instance for va416xx::Can0 { +impl CanMarker for va416xx::Can0 { const ID: CanId = CanId::Can0; const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Can0; } -impl Instance for va416xx::Can1 { +impl CanMarker for va416xx::Can1 { const ID: CanId = CanId::Can1; const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Can1; } @@ -280,7 +286,7 @@ pub struct Can { } impl Can { - pub fn new(_can: CanI, clk_config: ClockConfig) -> Self { + pub fn new(_can: CanI, clk_config: ClockConfig) -> Self { enable_peripheral_clock(CanI::PERIPH_SEL); let id = CanI::ID; let mut regs = if id == CanId::Can0 { @@ -291,7 +297,7 @@ impl Can { // Disable the CAN bus before configuring it. regs.write_control(Control::new_with_raw_value(0)); for i in 0..15 { - regs.msg_buf_block_mut(i).reset(); + regs.cmbs(i).unwrap().reset(); } regs.write_timing( TimingConfig::builder() @@ -312,6 +318,13 @@ impl Can { self.regs.write_gmskb(BaseId::new_with_raw_value(0)); } + pub fn take_channels(&self) -> Option { + if CHANNELS_TAKEN[self.id() as usize].swap(true, core::sync::atomic::Ordering::SeqCst) { + return None; + } + Some(CanChannels::new(self.id)) + } + /// Similar to [Self::set_global_mask_for_exact_id_match] but masks the XRTR and RTR/SRR bits. /// /// This is useful for when transmitting remote frames with the RTR bit set. The hardware @@ -383,51 +396,123 @@ impl Can { } } -#[derive(Debug)] -pub enum ChannelState { +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum TxState { + Idle, + TransmissionIdle, + TransmittingDataFrame, + TransmittingRemoteFrame, + AwaitingRemoteFrameReply, +} + +#[derive(Debug, thiserror::Error)] +#[error("invalid tx state {0:?}")] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct InvalidTxStateError(pub TxState); + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum RxState { Idle, Receiving, - Transmitting, - AwaitingRtrReply, } -pub struct CanChannel { - can_id: CanId, - idx: usize, - regs: regs::MmioCanMsgBuf<'static>, - mode: ChannelState, +#[derive(Debug, thiserror::Error)] +#[error("invalid rx state {0:?}")] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct InvalidRxStateError(pub RxState); + +#[derive(Debug)] +pub struct CanTx { + ll: CanChannelLowLevel, + mode: TxState, } -impl core::fmt::Debug for CanChannel { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_struct("CanChannel") - .field("can_id", &self.can_id) - .field("idx", &self.idx) - .field("mode", &self.mode) - .finish() +impl CanTx { + pub fn new(ll: CanChannelLowLevel) -> Self { + Self { + ll, + mode: TxState::Idle, + } + } + + pub fn configure_for_transmission( + &mut self, + tx_priority: Option, + ) -> Result<(), InvalidBufferIndexError> { + self.ll.configure_for_transmission(tx_priority).unwrap(); + self.mode = TxState::TransmissionIdle; + Ok(()) + } + + pub fn transmit_frame(&mut self, frame: CanFrame) -> Result<(), InvalidTxStateError> { + if self.mode == TxState::AwaitingRemoteFrameReply { + self.configure_for_transmission(None).unwrap(); + self.mode = TxState::TransmissionIdle; + } + if self.mode != TxState::TransmissionIdle { + return Err(InvalidTxStateError(self.mode)); + } + if !frame.is_remote_frame() { + self.mode = TxState::TransmittingDataFrame; + } else { + self.mode = TxState::TransmittingRemoteFrame; + } + self.ll.transmit_frame_unchecked(frame); + Ok(()) + } + + pub fn data_frame_transfer_done(&mut self) -> nb::Result<(), InvalidTxStateError> { + if self.mode != TxState::TransmittingDataFrame { + return Err(nb::Error::Other(InvalidTxStateError(self.mode))); + } + let status = self.ll.read_status(); + if status.is_err() { + return Err(nb::Error::WouldBlock); + } + let status = status.unwrap(); + if status == BufferState::TxNotActive { + self.mode = TxState::TransmissionIdle; + return Ok(()); + } + Err(nb::Error::WouldBlock) + } + + pub fn remote_frame_transfer_done(&mut self) -> nb::Result { + if self.mode != TxState::TransmittingRemoteFrame { + return Err(nb::Error::Other(InvalidTxStateError(self.mode))); + } + let status = self.ll.read_status(); + if status.is_err() { + return Err(nb::Error::WouldBlock); + } + let status = status.unwrap(); + if status == BufferState::RxReady { + self.mode = TxState::AwaitingRemoteFrameReply; + return Ok(CanRx { + ll: unsafe { self.ll.clone() }, + mode: RxState::Receiving, + }); + } + Err(nb::Error::WouldBlock) } } -impl CanChannel { +pub struct CanRx { + ll: CanChannelLowLevel, + mode: RxState, +} + +impl CanRx { pub fn configure_for_reception_with_standard_id( &mut self, standard_id: embedded_can::StandardId, set_rtr: bool, ) -> Result<(), InvalidBufferIndexError> { - let mut id1_reg = standard_id.as_raw() << 5; - if set_rtr { - id1_reg |= 1 << 4; - } - self.regs - .write_id1(BaseId::new_with_raw_value(id1_reg as u32)); - - self.regs.write_stat_ctrl( - BufStatusAndControl::builder() - .with_dlc(u4::new(0)) - .with_priority(u4::new(0)) - .with_status(regs::BufferState::RxReady) - .build(), - ); + self.ll + .configure_for_reception_with_standard_id(standard_id, set_rtr)?; + self.mode = RxState::Receiving; Ok(()) } @@ -436,142 +521,81 @@ impl CanChannel { extended_id: embedded_can::ExtendedId, set_rtr: bool, ) -> Result<(), InvalidBufferIndexError> { - let mut regs = unsafe { self.can_id.steal_regs() }; - let mut cmb_block = regs.msg_buf_block_mut(self.idx); - let id_raw = extended_id.as_raw(); - let id1_reg = (((id_raw >> 18) & 0x7FF) << 4) as u16 | ((id_raw >> 15) & 0b111) as u16; - cmb_block.write_id1(BaseId::new_with_raw_value(id1_reg as u32)); - let id0_reg = ((id_raw & 0x7FFF) << 1) as u16 | set_rtr as u16; - cmb_block.write_id0(ExtendedId::new_with_raw_value(id0_reg as u32)); - cmb_block.write_stat_ctrl( - BufStatusAndControl::builder() - .with_dlc(u4::new(0)) - .with_priority(u4::new(0)) - .with_status(regs::BufferState::RxReady) - .build(), - ); - self.mode = ChannelState::Receiving; + self.ll + .configure_for_reception_with_extended_id(extended_id, set_rtr)?; + self.mode = RxState::Receiving; Ok(()) } - pub fn configure_for_transmission( + pub fn receive( &mut self, - tx_priority: u4, - ) -> Result<(), InvalidBufferIndexError> { - let mut regs = unsafe { self.can_id.steal_regs() }; - let mut cmb_block = regs.msg_buf_block_mut(self.idx); - cmb_block.write_stat_ctrl( - BufStatusAndControl::builder() - .with_dlc(u4::new(0)) - .with_priority(tx_priority) - .with_status(regs::BufferState::TxNotActive) - .build(), - ); - self.mode = ChannelState::Receiving; - Ok(()) - } - - /// Reads a received CAN frame from the message buffer. - /// - /// This function does not check whether the pre-requisites for reading a CAN frame were - /// met and assumes this was already checked by the user. - pub fn read_frame_unchecked(&self) -> CanFrame { - let id0 = self.regs.read_id0(); - let id1 = self.regs.read_id1(); - let data0 = self.regs.read_data0(); - let data1 = self.regs.read_data1(); - let data2 = self.regs.read_data2(); - let data3 = self.regs.read_data3(); - let mut data: [u8; 8] = [0; 8]; - let mut read_data = |dlc: u4| { - (0..dlc.as_usize()).for_each(|i| match i { - 0 => data[i] = data3.data_upper_byte().as_u8(), - 1 => data[i] = data3.data_lower_byte().as_u8(), - 2 => data[i] = data2.data_upper_byte().as_u8(), - 3 => data[i] = data2.data_lower_byte().as_u8(), - 4 => data[i] = data1.data_upper_byte().as_u8(), - 5 => data[i] = data1.data_lower_byte().as_u8(), - 6 => data[i] = data0.data_upper_byte().as_u8(), - 7 => data[i] = data0.data_lower_byte().as_u8(), - _ => unreachable!(), - }); - }; - let (id, rtr) = if !id1.ide() { - let id = embedded_can::Id::Standard( - embedded_can::StandardId::new(id1.mask_28_18().as_u16()).unwrap(), - ); - if id1.rtr_or_srr() { - (id, true) - } else { - (id, false) - } - } else { - let id_raw = (id1.mask_28_18().as_u32() << 18) - | (id1.mask_17_15().as_u32() << 15) - | id0.mask_14_0().as_u32(); - let id = embedded_can::Id::Extended(embedded_can::ExtendedId::new(id_raw).unwrap()); - if id0.xrtr() { - (id, true) - } else { - (id, false) - } - }; - if rtr { - CanFrameRtr::new(id, self.regs.read_stat_ctrl().dlc().as_usize()).into() - } else { - let dlc = self.regs.read_stat_ctrl().dlc(); - read_data(dlc); - CanFrameNormal::new(id, &data[0..dlc.as_usize()]).into() + reconfigure_for_reception: bool, + ) -> nb::Result { + if self.mode != RxState::Receiving { + return Err(nb::Error::Other(InvalidRxStateError(self.mode))); } - } - - pub fn transmit_frame_unchecked(&mut self, frame: CanFrame) { - let is_remote = frame.is_remote_frame(); - self.write_id(frame.id(), is_remote); - let dlc = frame.dlc(); - self.regs.modify_stat_ctrl(|mut ctrl| { - ctrl.set_status(regs::BufferState::TxOnce); - ctrl - }); - } - - fn write_id(&mut self, id: embedded_can::Id, is_remote: bool) { - match id { - embedded_can::Id::Standard(standard_id) => { - self.regs.write_id1( - BaseId::builder() - .with_mask_28_18(u11::new(standard_id.as_raw())) - .with_rtr_or_srr(is_remote) - .with_ide(false) - .with_mask_17_15(u3::new(0)) - .build(), - ); - self.regs.write_id0(ExtendedId::new_with_raw_value(0)); - } - embedded_can::Id::Extended(extended_id) => { - let id_raw = extended_id.as_raw(); - self.regs.write_id1( - BaseId::builder() - .with_mask_28_18(u11::new(((id_raw >> 18) & 0x7FF) as u16)) - .with_rtr_or_srr(true) - .with_ide(true) - .with_mask_17_15(u3::new(((id_raw >> 15) & 0b111) as u8)) - .build(), - ); - self.regs.write_id0( - ExtendedId::builder() - .with_mask_14_0(u15::new((id_raw & 0x7FFF) as u16)) - .with_xrtr(is_remote) - .build(), - ); - } + let status = self.ll.read_status(); + if status.is_err() { + return Err(nb::Error::WouldBlock); } + let status = status.unwrap(); + if status == BufferState::RxReady || status == BufferState::RxOverrun { + self.mode = RxState::Idle; + if reconfigure_for_reception { + self.ll.write_status(BufferState::RxReady); + } + return Ok(self.ll.read_frame_unchecked()); + } + Err(nb::Error::WouldBlock) } } -pub struct CanWorker { - can: Can, - channels: [CanChannel; 15], +pub struct CanChannels { + id: CanId, + channels: [Option; 15], +} + +impl CanChannels { + const fn new(id: CanId) -> Self { + Self { + id, + channels: [ + Some(CanChannelLowLevel::steal_unchecked(id, 0)), + Some(CanChannelLowLevel::steal_unchecked(id, 1)), + Some(CanChannelLowLevel::steal_unchecked(id, 2)), + Some(CanChannelLowLevel::steal_unchecked(id, 3)), + Some(CanChannelLowLevel::steal_unchecked(id, 4)), + Some(CanChannelLowLevel::steal_unchecked(id, 5)), + Some(CanChannelLowLevel::steal_unchecked(id, 6)), + Some(CanChannelLowLevel::steal_unchecked(id, 7)), + Some(CanChannelLowLevel::steal_unchecked(id, 8)), + Some(CanChannelLowLevel::steal_unchecked(id, 9)), + Some(CanChannelLowLevel::steal_unchecked(id, 10)), + Some(CanChannelLowLevel::steal_unchecked(id, 11)), + Some(CanChannelLowLevel::steal_unchecked(id, 12)), + Some(CanChannelLowLevel::steal_unchecked(id, 13)), + Some(CanChannelLowLevel::steal_unchecked(id, 14)), + ], + } + } + + pub const fn can_id(&self) -> CanId { + self.id + } + + pub fn take(&mut self, idx: usize) -> Option { + if idx > 14 { + return None; + } + self.channels[idx].take() + } + + pub fn give(&mut self, idx: usize, channel: CanChannelLowLevel) { + if idx > 14 { + panic!("invalid buffer index for CAN channel"); + } + self.channels[idx] = Some(channel); + } } #[cfg(test)] diff --git a/va416xx-hal/src/can/regs.rs b/va416xx-hal/src/can/regs.rs index 8aad508..7f331f0 100644 --- a/va416xx-hal/src/can/regs.rs +++ b/va416xx-hal/src/can/regs.rs @@ -1,12 +1,12 @@ //! Custom register definitions for the CAN register block to circumvent PAC API / SVD //! shortcomings. -use arbitrary_int::{u11, u15, u2, u3, u4, u7}; +use arbitrary_int::{u11, u15, u2, u3, u4, u6, u7, Number}; pub const CAN_0_BASE: usize = 0x4001_4000; pub const CAN_1_BASE: usize = 0x4001_4400; -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq)] #[bitbybit::bitenum(u4)] pub enum BufferState { /// Passive channel. @@ -197,6 +197,42 @@ pub struct InterruptPending { buffer: [bool; 15], } +#[derive(Debug)] +#[repr(usize)] +pub enum CanInterruptId { + None = 0b00000, + Error = 0b10000, + Buffer(usize), +} + +#[bitbybit::bitfield(u32)] +#[derive(Debug)] +pub struct StatusPending { + #[bits(5..=7, r)] + ns: u3, + #[bit(4, r)] + irq: bool, + #[bits(0..=3, r)] + ist: u4, +} + +impl StatusPending { + pub fn interrupt_id(&self) -> Option { + if !self.irq() && self.ist().value() == 0 { + return Some(CanInterruptId::None); + } + + //let raw_value = ((self.irq() as u8) << 4) | self.ist().as_u8(); + if self.irq() && self.ist().value() == 0 { + return Some(CanInterruptId::Error); + } + if !self.irq() { + return None; + } + Some(CanInterruptId::Buffer(self.ist().as_usize())) + } +} + #[bitbybit::bitfield(u32)] #[derive(Debug)] pub struct ErrorCounter { @@ -235,40 +271,89 @@ pub struct BaseId { mask_17_15: u3, } +#[derive(Debug, PartialEq, Eq)] +#[bitbybit::bitenum(u4, exhaustive = true)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum ErrorFieldId { + Error = 0b0000, + ErrorDel = 0b0001, + ErrorEcho = 0b0010, + BusIdle = 0b0011, + Ack = 0b0100, + Eof = 0b0101, + Intermission = 0b0110, + SuspendTransmission = 0b0111, + Sof = 0b1000, + Arbitration = 0b1001, + Ide = 0b1010, + ExtendedArbitration = 0b1011, + R1R0 = 0b1100, + Dlc = 0b1101, + Data = 0b1110, + Crc = 0b1111, +} + +#[bitbybit::bitfield(u32)] +pub struct DiagnosticRegister { + /// Shows the output value on the CAN TX pin at the time of the error. + #[bit(14, r)] + drive: bool, + /// Shows the bus value on the CAN RX pin as sampled by the CAN module at the time of the + /// error. + #[bit(13, r)] + mon: bool, + /// Indicated whether the CRC is invalid. This bit should only be checked if the EFID field + /// is [ErrorFieldId::Ack]. + #[bit(12, r)] + crc: bool, + /// Indicated whether the bit stuffing rule was violated at the time the error occured. + #[bit(11, r)] + stuff: bool, + /// Indicated whether the CAN module was an active transmitter at the time the error occured. + #[bit(10, r)] + txe: bool, + #[bits(4..=9, r)] + ebid: u6, + #[bits(0..=3, r)] + efid: ErrorFieldId, +} + +impl core::fmt::Debug for DiagnosticRegister { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("DiagnosticRegister") + .field("efid", &self.efid()) + .field("ebid", &self.ebid()) + .field("txe", &self.txe()) + .field("stuff", &self.stuff()) + .field("crc", &self.crc()) + .field("mon", &self.mon()) + .field("drive", &self.drive()) + .finish() + } +} + +#[cfg(feature = "defmt")] +impl defmt::Format for DiagnosticRegister { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!( + fmt, + "DiagnosticRegister {{ efid: {}, ebid: {}, txe: {}, stuff: {}, crc: {}, mon: {}, drive: {} }}", + self.efid(), + self.ebid(), + self.txe(), + self.stuff(), + self.crc(), + self.mon(), + self.drive() + ) + } +} + #[derive(derive_mmio::Mmio)] #[repr(C)] pub struct Can { #[mmio(inner)] - cmb0: CanMsgBuf, - #[mmio(inner)] - cmb1: CanMsgBuf, - #[mmio(inner)] - cmb2: CanMsgBuf, - #[mmio(inner)] - cmb3: CanMsgBuf, - #[mmio(inner)] - cmb4: CanMsgBuf, - #[mmio(inner)] - cmb5: CanMsgBuf, - #[mmio(inner)] - cmb6: CanMsgBuf, - #[mmio(inner)] - cmb7: CanMsgBuf, - #[mmio(inner)] - cmb8: CanMsgBuf, - #[mmio(inner)] - cmb9: CanMsgBuf, - #[mmio(inner)] - cmb10: CanMsgBuf, - #[mmio(inner)] - cmb11: CanMsgBuf, - #[mmio(inner)] - cmb12: CanMsgBuf, - #[mmio(inner)] - cmb13: CanMsgBuf, - // This CAN message buffer has different mask registers. - #[mmio(inner)] - cmb14: CanMsgBuf, + cmbs: [CanMsgBuf; 15], /// Hidden CAN message buffer. Only allowed to be used internally by the peripheral. #[mmio(inner)] _hcmb: CanMsgBuf, @@ -290,11 +375,11 @@ pub struct Can { /// Interrupt Code Enable Register. icen: InterruptEnable, #[mmio(PureRead)] - status_pending: u32, + status_pending: StatusPending, #[mmio(PureRead)] error_counter: ErrorCounter, #[mmio(PureRead)] - diag: u32, + diag: DiagnosticRegister, #[mmio(PureRead)] timer: u32, } @@ -322,28 +407,3 @@ impl Can { Self::new_mmio_at(CAN_1_BASE) } } - -impl MmioCan<'_> { - // TODO: It would be nice if derive-mmio could generate this for us.. - pub fn msg_buf_block_mut(&mut self, idx: usize) -> MmioCanMsgBuf<'static> { - assert!(idx < 15, "invalid index for CAN message buffer"); - match idx { - 0 => unsafe { self.steal_cmb0() }, - 1 => unsafe { self.steal_cmb1() }, - 2 => unsafe { self.steal_cmb2() }, - 3 => unsafe { self.steal_cmb3() }, - 4 => unsafe { self.steal_cmb4() }, - 5 => unsafe { self.steal_cmb5() }, - 6 => unsafe { self.steal_cmb6() }, - 7 => unsafe { self.steal_cmb7() }, - 8 => unsafe { self.steal_cmb8() }, - 9 => unsafe { self.steal_cmb9() }, - 10 => unsafe { self.steal_cmb10() }, - 11 => unsafe { self.steal_cmb11() }, - 12 => unsafe { self.steal_cmb12() }, - 13 => unsafe { self.steal_cmb13() }, - 14 => unsafe { self.steal_cmb14() }, - _ => unreachable!(), - } - } -}