diff --git a/embassy-stm32/src/can/fd/config/message_ram.rs b/embassy-stm32/src/can/fd/config/message_ram.rs new file mode 100644 index 000000000..af5846c0e --- /dev/null +++ b/embassy-stm32/src/can/fd/config/message_ram.rs @@ -0,0 +1,169 @@ +/// For RX: +/// Excess data is IGNORED, only the number of bytes which fit +/// into the element are stored. +/// +/// For TX: +/// If DLC is higher than the data field size, excess bytes are +/// transmitted as 0xCC (padding bytes). +#[derive(Clone, Copy, Debug)] +pub enum DataFieldSize { + /// Supports storing DLC up to 8 bytes. + B8 = 0b000, + /// Supports storing DLC up to 12 bytes. + B12 = 0b001, + /// Supports storing DLC up to 16 bytes. + B16 = 0b010, + /// Supports storing DLC up to 20 bytes. + B20 = 0b011, + /// Supports storing DLC up to 24 bytes. + B24 = 0b100, + /// Supports storing DLC up to 32 bytes. + B32 = 0b101, + /// Supports storing DLC up to 48 bytes. + B48 = 0b110, + /// Supports storing DLC up to 64 bytes. + B64 = 0b111, +} + +impl DataFieldSize { + pub(crate) fn reg_value(self) -> u8 { + self as u8 + } + + /// Returns the max byte size for this setting. + pub fn byte_size(self) -> usize { + match self { + DataFieldSize::B8 => 8, + DataFieldSize::B12 => 12, + DataFieldSize::B16 => 16, + DataFieldSize::B20 => 20, + DataFieldSize::B24 => 24, + DataFieldSize::B32 => 32, + DataFieldSize::B48 => 48, + DataFieldSize::B64 => 64, + } + } + + pub(crate) fn word_size(self) -> usize { + self.byte_size() / 4 + } +} + +/// Configuration for an Rx FIFO +#[derive(Clone, Copy, Debug)] +pub struct RxFifoConfig { + /// 0: Disabled + /// 1-64: Watermark interrupt level + /// >64: Disabled + pub watermark_interrupt_level: u8, + /// 0-64: Number of RX FIFO elements + pub fifo_size: u8, + + /// The data size for each Tx buffer. This will indicate the max + /// data length you will be able to send. + /// + /// If you are using Classic CAN only, there is no reason to set + /// this to any value above B8. + /// + /// If you receive a frame with data that doesn't fit within the + /// configured data field size, the data will be truncated. + pub data_field_size: DataFieldSize, +} + +impl RxFifoConfig { + /// Configuration which disables the FIFO. + pub const DISABLED: Self = RxFifoConfig { + watermark_interrupt_level: 0, + fifo_size: 0, + data_field_size: DataFieldSize::B8, + }; +} + +/// Configuration for an RX Buffer +#[derive(Clone, Copy, Debug)] +pub struct RxBufferConfig { + /// 0-64: Number of RX Buffer elements + pub size: u8, + + /// The data size for each Tx buffer. This will indicate the max + /// data length you will be able to send. + /// + /// If you are using Classic CAN only, there is no reason to set + /// this to any value above B8. + /// + /// If you receive a frame with data that doesn't fit within the + /// configured data field size, the data will be truncated. + pub data_field_size: DataFieldSize, +} + +impl RxBufferConfig { + /// Configuration which disables the buffer. + pub const DISABLED: Self = RxBufferConfig { + size: 0, + data_field_size: DataFieldSize::B8, + }; +} + +/// Configuration for TX buffers +#[derive(Clone, Copy, Debug)] +pub struct TxConfig { + /// Number of elements reserved for TX Queue. + /// NOTE: queue_size + dedicated_size may not be greater than 32. + /// + /// 0-32: Number of TX buffers used for TX FIFO/Priority queue + pub queue_size: u8, + /// Number of elements reserved for Dedicated TX buffers. + /// NOTE: queue_size + dedicated_size may not be greater than 32. + /// + /// 0-32: Number of TX buffers used for TX dedicated buffers + pub dedicated_size: u8, + /// The data size for each Tx buffer. This will indicate the max + /// data length you will be able to send. + /// + /// If you are using Classic CAN only, there is no reason to set + /// this to any value above B8. + pub data_field_size: DataFieldSize, +} + +/// Configuration for Message RAM layout +#[derive(Clone, Copy, Debug)] +pub struct MessageRamConfig { + /// 0-128: Number of standard Message ID filter elements + /// >128: Interpreted as 128 + pub standard_id_filter_size: u8, + /// 0-64: Number of extended Message ID filter elements + /// >64: Interpreted as 64 + pub extended_id_filter_size: u8, + + /// Configuration for Rx FIFO 0 + pub rx_fifo_0: RxFifoConfig, + /// Configuration for Rx FIFO 1 + pub rx_fifo_1: RxFifoConfig, + /// Configuration for Rx Buffers + pub rx_buffer: RxBufferConfig, + + /// Configuration for Tx FIFO/Queue and dedicated buffers + pub tx: TxConfig, +} + +impl Default for MessageRamConfig { + fn default() -> Self { + // TODO make better default config. + MessageRamConfig { + standard_id_filter_size: 28, + extended_id_filter_size: 8, + rx_fifo_0: RxFifoConfig { + watermark_interrupt_level: 3, + fifo_size: 3, + data_field_size: DataFieldSize::B64, + }, + rx_fifo_1: RxFifoConfig::DISABLED, + rx_buffer: RxBufferConfig::DISABLED, + tx: TxConfig { + queue_size: 3, + dedicated_size: 0, + data_field_size: DataFieldSize::B64, + }, + } + } +} diff --git a/embassy-stm32/src/can/fd/config.rs b/embassy-stm32/src/can/fd/config/mod.rs similarity index 90% rename from embassy-stm32/src/can/fd/config.rs rename to embassy-stm32/src/can/fd/config/mod.rs index 856f2fb3a..eaf0d5e5a 100644 --- a/embassy-stm32/src/can/fd/config.rs +++ b/embassy-stm32/src/can/fd/config/mod.rs @@ -3,6 +3,11 @@ use core::num::{NonZeroU16, NonZeroU8}; +#[cfg(can_fdcan_h7)] +mod message_ram; +#[cfg(can_fdcan_h7)] +pub use message_ram::*; + /// Configures the bit timings. /// /// You can use to calculate the `btr` parameter. Enter @@ -117,20 +122,6 @@ impl Default for DataBitTiming { } } -/// Configures which modes to use -/// Individual headers can contain a desire to be send via FdCan -/// or use Bit rate switching. But if this general setting does not allow -/// that, only classic CAN is used instead. -#[derive(Clone, Copy, Debug)] -pub enum FrameTransmissionConfig { - /// Only allow Classic CAN message Frames - ClassicCanOnly, - /// Allow (non-brs) FdCAN Message Frames - AllowFdCan, - /// Allow FdCAN Message Frames and allow Bit Rate Switching - AllowFdCanAndBRS, -} - /// #[derive(Clone, Copy, Debug)] pub enum ClockDivider { @@ -314,6 +305,25 @@ impl From for TxBufferMode { } } +/// Configures which modes to use +/// Individual headers can contain a desire to be send via FdCan +/// or use Bit rate switching. But if this general setting does not allow +/// that, only classic CAN is used instead. +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum CanFdMode { + /// Only allow Classic CAN message Frames. + /// Will behave as a Classic CAN node on the bus, will reject + /// CAN FD frames. + ClassicCanOnly, + /// Allow (non-BRS) FdCAN Message Frames. + /// This will allow the peripheral to send and receive frames + /// with data length up to 64 bytes, but will never perform bit + /// rate switching. + AllowFdCan, + /// Allow FdCAN Message Frames and allow Bit Rate Switching. + AllowFdCanAndBRS, +} + /// FdCan Config Struct #[derive(Clone, Copy, Debug)] pub struct FdCanConfig { @@ -334,12 +344,6 @@ pub struct FdCanConfig { /// "babbling idiot" scenarios where the application program erroneously requests too many /// transmissions. pub transmit_pause: bool, - /// Enabled or disables the pausing between transmissions - /// - /// This feature looses up burst transmissions coming from a single node and it protects against - /// "babbling idiot" scenarios where the application program erroneously requests too many - /// transmissions. - pub frame_transmit: FrameTransmissionConfig, /// Edge Filtering: Two consecutive dominant tq required to detect an edge for hard synchronization pub edge_filtering: bool, /// Enables protocol exception handling @@ -353,9 +357,17 @@ pub struct FdCanConfig { /// TX buffer mode (FIFO or priority queue) pub tx_buffer_mode: TxBufferMode, - /// If this is set, the FDCAN uses the CAN FD frame format as specified by the Bosch CAN - /// FD Specification V1.0. - pub can_fd_enabled: bool, + /// Configures whether the peripheral uses CAN FD as specified by the + /// Bosch CAN FD Specification V1.0 or if it is limited to regular classic + /// ISO CAN. + pub can_fd_mode: CanFdMode, + + /// This peripheral stores TX/RX buffers and more in a memory region called + /// Message RAM. + /// + /// The size of the different buffers are reconfigurable. + #[cfg(can_fdcan_h7)] + pub message_ram_config: MessageRamConfig, } impl FdCanConfig { @@ -396,11 +408,12 @@ impl FdCanConfig { self } - /// If this is set, the FDCAN uses the CAN FD frame format as specified by the Bosch CAN - /// FD Specification V1.0. + /// Configures whether the peripheral uses CAN FD as specified by the + /// Bosch CAN FD Specification V1.0 or if it is limited to regular classic + /// ISO CAN. #[inline] - pub const fn set_can_fd_enabled(mut self, enabled: bool) -> Self { - self.can_fd_enabled = enabled; + pub const fn set_can_fd_mode(mut self, mode: CanFdMode) -> Self { + self.can_fd_mode = mode; self } @@ -411,13 +424,6 @@ impl FdCanConfig { self } - /// Sets the allowed transmission types for messages. - #[inline] - pub const fn set_frame_transmit(mut self, fts: FrameTransmissionConfig) -> Self { - self.frame_transmit = fts; - self - } - /// Enables protocol exception handling #[inline] pub const fn set_protocol_exception_handling(mut self, peh: bool) -> Self { @@ -452,6 +458,12 @@ impl FdCanConfig { self.tx_buffer_mode = txbm; self } + + #[inline] + pub const fn set_message_ram_config(mut self, config: MessageRamConfig) -> Self { + self.message_ram_config = config; + self + } } impl Default for FdCanConfig { @@ -462,14 +474,14 @@ impl Default for FdCanConfig { dbtr: DataBitTiming::default(), automatic_retransmit: true, transmit_pause: false, - frame_transmit: FrameTransmissionConfig::ClassicCanOnly, - non_iso_mode: false, edge_filtering: false, protocol_exception_handling: true, clock_divider: ClockDivider::_1, timestamp_source: TimestampSource::None, global_filter: GlobalFilter::default(), tx_buffer_mode: TxBufferMode::Priority, + can_fd_mode: CanFdMode::ClassicCanOnly, + message_ram_config: MessageRamConfig::default(), } } } diff --git a/embassy-stm32/src/can/fd/configurator.rs b/embassy-stm32/src/can/fd/configurator.rs index 710f90572..eebe75ec4 100644 --- a/embassy-stm32/src/can/fd/configurator.rs +++ b/embassy-stm32/src/can/fd/configurator.rs @@ -7,7 +7,7 @@ use crate::interrupt::typelevel::Interrupt; use crate::{ can::{ common::CanMode, - config::{FrameTransmissionConfig, TimestampPrescaler, TimestampSource}, + config::{TimestampPrescaler, TimestampSource}, filter::{ExtendedFilter, StandardFilter}, util, Can, Instance, OperatingMode, RxPin, TxPin, }, @@ -15,6 +15,7 @@ use crate::{ interrupt, rcc, Peripheral, }; +use super::config::CanFdMode; use super::{calc_ns_per_timer_tick, IT0InterruptHandler, IT1InterruptHandler, Info, State}; /// FDCAN Configuration instance instance @@ -56,9 +57,7 @@ impl<'d> CanConfigurator<'d> { info.low.apply_config(&config); } - { - unsafe { T::mut_info() }.low.apply_dummy_msg_ram_config(); - } + //unsafe { T::mut_info() }.low.apply_message_ram_config(); rx.set_as_af(rx.af_num(), AfType::input(Pull::None)); tx.set_as_af(tx.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); @@ -119,22 +118,47 @@ impl<'d> CanConfigurator<'d> { seg1: bit_timing.seg1, seg2: bit_timing.seg2, }; - self.config.frame_transmit = FrameTransmissionConfig::AllowFdCanAndBRS; + self.config.can_fd_mode = CanFdMode::AllowFdCanAndBRS; self.config = self.config.set_data_bit_timing(nbtr); } /// Start in mode. pub fn start(self, mode: OperatingMode) -> Can<'d, M> { - let ns_per_timer_tick = calc_ns_per_timer_tick(self.info, self.periph_clock, self.config.frame_transmit); + use crate::can::common::DynCanMode; + + match M::dyn_can_mode() { + // Creating a FD parametrized CAN instance for Classic hardware config + // is supported. Can be used to be able to switch back and fourth without + // refactoring all your code or parametrizing it. + DynCanMode::Fd => (), + // Creating a Classic parametrized CAN instance for FD hardware config + // could lead to unexpected behaviour, and is not supported. + DynCanMode::Classic => assert!( + self.config.can_fd_mode == CanFdMode::ClassicCanOnly, + "Short frame types are not supported for FD hardware configuration" + ), + } + + let ns_per_timer_tick = calc_ns_per_timer_tick(self.info, self.periph_clock, self.config.can_fd_mode); + + // TODO: I really don't like this.. critical_section::with(|_| { let state = self.state as *const State; unsafe { let mut_state = state as *mut State; (*mut_state).ns_per_timer_tick = ns_per_timer_tick; } + + let info = self.info as *const Info; + unsafe { + let mut_info = info as *mut Info; + (*mut_info).low.apply_message_ram_config(self.config.message_ram_config); + } }); + self.info.low.apply_config(&self.config); self.info.low.into_mode(mode); + Can { _phantom: PhantomData, config: self.config, diff --git a/embassy-stm32/src/can/fd/interrupt.rs b/embassy-stm32/src/can/fd/interrupt.rs index 1be498d49..025413d8f 100644 --- a/embassy-stm32/src/can/fd/interrupt.rs +++ b/embassy-stm32/src/can/fd/interrupt.rs @@ -57,32 +57,35 @@ pub struct IT0InterruptHandler { impl interrupt::typelevel::Handler for IT0InterruptHandler { unsafe fn on_interrupt() { let regs = T::info().low.regs; - let ir = regs.ir().read(); + // TX transfer complete if ir.tc() { regs.ir().write(|w| w.set_tc(true)); } + // TX cancel request finished + if ir.tcf() { + regs.ir().write(|w| w.set_tc(true)); + } + + // TX event FIFO new element if ir.tefn() { regs.ir().write(|w| w.set_tefn(true)); } T::state().tx_waker.wake(); - //match &T::state().tx_mode { - // TxMode::NonBuffered(waker) => waker.wake(), - //} + // RX FIFO new element if ir.rfn(0) { T::info().low.regs.ir().write(|w| w.set_rfn(0, true)); T::state().rx_waker.wake(); - //T::state().rx_mode.on_interrupt::(0); } if ir.rfn(1) { T::info().low.regs.ir().write(|w| w.set_rfn(1, true)); T::state().rx_waker.wake(); - //T::state().rx_mode.on_interrupt::(1); } + // Bus_Off if ir.bo() { regs.ir().write(|w| w.set_bo(true)); if regs.psr().read().bo() { diff --git a/embassy-stm32/src/can/fd/low_level/configuration.rs b/embassy-stm32/src/can/fd/low_level/configuration.rs index d1b8d9fa2..f2ab9515b 100644 --- a/embassy-stm32/src/can/fd/low_level/configuration.rs +++ b/embassy-stm32/src/can/fd/low_level/configuration.rs @@ -1,8 +1,8 @@ use super::{ - message_ram::{self, MessageRam}, + message_ram::{MessageRam, MessageRamSegment}, CanLowLevel, LoopbackMode, TimestampSource, }; -use crate::can::config::{DataBitTiming, FdCanConfig, FrameTransmissionConfig, GlobalFilter, NominalBitTiming}; +use crate::can::config::{CanFdMode, DataBitTiming, FdCanConfig, GlobalFilter, MessageRamConfig, NominalBitTiming}; /// Configuration. /// Can only be called when in config mode (CCCR.INIT=1, CCCR.CCE=1). @@ -22,32 +22,13 @@ impl CanLowLevel { } } - pub fn apply_message_ram_config(&mut self, config: message_ram::MessageRamConfig) { - let message_ram = config.apply_config(&self.regs, &self.msgram); - self.message_ram = message_ram; - } - - pub fn apply_dummy_msg_ram_config(&mut self) { - self.apply_message_ram_config(message_ram::MessageRamConfig { + pub fn apply_message_ram_config(&mut self, config: MessageRamConfig) { + let segment = MessageRamSegment { base_offset: self.msg_ram_offset, available_space: Some(self.msg_ram_size), - standard_id_filter_size: 28, - extended_id_filter_size: 8, - rx_fifo_0: message_ram::RxFifoConfig { - operation_mode: message_ram::RxFifoOperationMode::Blocking, - watermark_interrupt_level: 3, - fifo_size: 3, - data_field_size: message_ram::DataFieldSize::B64, - }, - rx_fifo_1: message_ram::RxFifoConfig::DISABLED, - rx_buffer: message_ram::RxBufferConfig::DISABLED, - tx: message_ram::TxConfig { - queue_operation_mode: message_ram::TxQueueOperationMode::FIFO, - queue_size: 3, - dedicated_size: 0, - data_field_size: message_ram::DataFieldSize::B64, - }, - }) + }; + let message_ram = unsafe { config.apply_config(&segment, &self.regs, &self.msgram) }; + self.message_ram = message_ram; } /// Applies the settings of a new FdCanConfig See [`FdCanConfig`] @@ -75,8 +56,7 @@ impl CanLowLevel { self.set_nominal_bit_timing(config.nbtr); self.set_automatic_retransmit(config.automatic_retransmit); self.set_transmit_pause(config.transmit_pause); - self.set_frame_transmit(config.frame_transmit); - self.set_non_iso_mode(config.can_fd_enabled); + self.set_can_fd_mode(config.can_fd_mode); self.set_edge_filtering(config.edge_filtering); self.set_protocol_exception_handling(config.protocol_exception_handling); self.set_global_filter(config.global_filter); @@ -163,23 +143,18 @@ impl CanLowLevel { self.regs.cccr().modify(|w| w.set_efbi(enabled)); } - /// Configures non-iso mode. See [`FdCanConfig::set_non_iso_mode`] - #[inline] - pub fn set_non_iso_mode(&self, enabled: bool) { - self.regs.cccr().modify(|w| w.set_niso(enabled)); - } - /// Configures frame transmission mode. See /// [`FdCanConfig::set_frame_transmit`] #[inline] - pub fn set_frame_transmit(&self, fts: FrameTransmissionConfig) { - let (fdoe, brse) = match fts { - FrameTransmissionConfig::ClassicCanOnly => (false, false), - FrameTransmissionConfig::AllowFdCan => (true, false), - FrameTransmissionConfig::AllowFdCanAndBRS => (true, true), + pub fn set_can_fd_mode(&self, fts: CanFdMode) { + let (niso, fdoe, brse) = match fts { + CanFdMode::ClassicCanOnly => (false, false, false), + CanFdMode::AllowFdCan => (true, true, false), + CanFdMode::AllowFdCanAndBRS => (true, true, true), }; self.regs.cccr().modify(|w| { + w.set_niso(niso); w.set_fdoe(fdoe); #[cfg(can_fdcan_h7)] w.set_bse(brse); diff --git a/embassy-stm32/src/can/fd/low_level/message_ram/element/tx_buffer.rs b/embassy-stm32/src/can/fd/low_level/message_ram/element/tx_buffer.rs index 8656c9268..b2d71c5db 100644 --- a/embassy-stm32/src/can/fd/low_level/message_ram/element/tx_buffer.rs +++ b/embassy-stm32/src/can/fd/low_level/message_ram/element/tx_buffer.rs @@ -153,7 +153,7 @@ impl<'a> ID_W<'a> { #[doc = r"Writes raw bits to the field"] #[inline(always)] #[allow(dead_code)] - pub unsafe fn bits(self, value: u32) -> &'a mut W { + pub fn bits(self, value: u32) -> &'a mut W { self.w.bits[0] = (self.w.bits[0] & !(0x1FFFFFFF)) | ((value as u32) & 0x1FFFFFFF); self.w } @@ -304,13 +304,14 @@ pub(crate) struct MM_W<'a> { impl<'a> MM_W<'a> { #[doc = r"Writes raw bits to the field"] #[inline(always)] - pub unsafe fn bits(self, value: u8) -> &'a mut W { - self.w.bits[1] = (self.w.bits[1] & !(0x7F << 24)) | (((value as u32) & 0x7F) << 24); + pub fn bits(self, value: u8) -> &'a mut W { + self.w.bits[1] = (self.w.bits[1] & !(0xff << 24)) | (((value as u32) & 0xff) << 24); self.w } - fn set_message_marker(self, mm: Marker) -> &'a mut W { - unsafe { self.bits(mm.0) } + #[inline(always)] + pub fn set_message_marker(self, mm: Marker) -> &'a mut W { + self.bits(mm.0) } } diff --git a/embassy-stm32/src/can/fd/low_level/message_ram/mod.rs b/embassy-stm32/src/can/fd/low_level/message_ram/mod.rs index 212cde849..b25fc99b3 100644 --- a/embassy-stm32/src/can/fd/low_level/message_ram/mod.rs +++ b/embassy-stm32/src/can/fd/low_level/message_ram/mod.rs @@ -1,7 +1,6 @@ use core::marker::PhantomData; use element::tx_event::TxEventElement; -use stm32_metapac::can::vals::Tfqm; use volatile_register::RW; mod element; @@ -11,6 +10,8 @@ pub(crate) use element::{ tx_buffer::TxBufferElementHeader, }; +use crate::can::config::MessageRamConfig; + /// Configuration for MessageRam layout. pub struct MessageRam { // 32 bit words @@ -70,24 +71,6 @@ impl MessageRam { #[cfg(can_fdcan_h7)] trigger_memory: Elements::EMPTY, }; - - pub fn debug_print(&self, regs: &crate::pac::can::Fdcan) { - defmt::info!("msgram seg base: {}", self.base_ptr); - defmt::info!("msgram tx base: {}", self.tx_elements.base); - //defmt::info!( - // "msgram tx calc offet: {}", - // ((self.tx_elements.base as usize) - (self.base_ptr as usize)) >> 2 - //); - defmt::info!("msgram tx periph offset: {}", regs.txbc().read().tbsa()); - let ram_ptr = crate::pac::FDCANRAM.as_ptr(); - defmt::info!("msgram tx periph ptr: {}", unsafe { - ram_ptr.byte_add(regs.txbc().read().tbsa() as usize * 4) - }); - //defmt::info!("elem 0 start {}", self.tx_elements.get_mut(0) as *mut _); - //defmt::info!("elem 1 start {}", self.tx_elements.get_mut(1) as *mut _); - //defmt::info!("elem 0 data start {}", (&self.tx_elements.get_mut(0).data).as_ptr()); - //defmt::info!("elem 1 data start {}", (&self.tx_elements.get_mut(1).data).as_ptr()); - } } #[repr(C)] @@ -167,161 +150,6 @@ impl Elements> { } } -impl MessageRam {} - -#[derive(Debug, Clone, Copy)] -pub enum RxFifoOperationMode { - Blocking = 0, - Overwrite = 1, -} - -/// For RX: -/// Excess data is IGNORED, only the number of bytes which fit -/// into the element are stored. -/// -/// For TX: -/// If DLC is higher than the data field size, excess bytes are -/// transmitted as 0xCC (padding bytes). -#[derive(Debug, Clone, Copy)] -pub enum DataFieldSize { - B8 = 0b000, - B12 = 0b001, - B16 = 0b010, - B20 = 0b011, - B24 = 0b100, - B32 = 0b101, - B48 = 0b110, - B64 = 0b111, -} - -impl DataFieldSize { - fn reg_value(self) -> u8 { - self as u8 - } - - fn byte_size(self) -> usize { - match self { - DataFieldSize::B8 => 8, - DataFieldSize::B12 => 12, - DataFieldSize::B16 => 16, - DataFieldSize::B20 => 20, - DataFieldSize::B24 => 24, - DataFieldSize::B32 => 32, - DataFieldSize::B48 => 48, - DataFieldSize::B64 => 64, - } - } - - fn word_size(self) -> usize { - self.byte_size() / 4 - } -} - -#[derive(Debug, Clone, Copy)] -pub struct RxFifoConfig { - pub(crate) operation_mode: RxFifoOperationMode, - /// 0: Disabled - /// 1-64: Watermark interrupt level - /// >64: Disabled - pub(crate) watermark_interrupt_level: u8, - /// 0: RX FIFO disabled - /// 1-64: Number of RX FIFO elements - /// >64: Interpreted as 64 - pub(crate) fifo_size: u8, - pub(crate) data_field_size: DataFieldSize, -} - -impl RxFifoConfig { - pub const DISABLED: Self = RxFifoConfig { - operation_mode: RxFifoOperationMode::Blocking, - watermark_interrupt_level: 0, - fifo_size: 0, - data_field_size: DataFieldSize::B8, - }; -} - -pub struct RxBufferConfig { - /// 0-64 - pub(crate) size: u8, - pub(crate) data_field_size: DataFieldSize, -} - -impl RxBufferConfig { - pub const DISABLED: Self = RxBufferConfig { - size: 0, - data_field_size: DataFieldSize::B8, - }; -} - -#[derive(Debug, Clone, Copy)] -pub enum TxQueueOperationMode { - /// Operates as a strict FIFO. - /// First element entered into the FIFO is always the first element sent. - FIFO = 0, - /// Operates as a priority queue. - /// Element with highest priority (lowest ID) is always the first element sent. - Priority = 1, -} - -impl TxQueueOperationMode { - fn reg_value(self) -> Tfqm { - match self { - TxQueueOperationMode::FIFO => Tfqm::FIFO, - TxQueueOperationMode::Priority => Tfqm::QUEUE, - } - } -} - -#[cfg(can_fdcan_h7)] -pub struct TxConfig { - pub(crate) queue_operation_mode: TxQueueOperationMode, - /// Number of elements reserved for TX Queue. - /// NOTE: queue_size + dedicated_size may not be greater than 32. - /// - /// 0: No TX FIFO/Priority queue - /// 1-32: Number of TX buffers used for TX FIFO/Priority queue - /// >32: Interpreted as 32 - pub(crate) queue_size: u8, - /// Number of elements reserved for Dedicated TX buffers. - /// NOTE: queue_size + dedicated_size may not be greater than 32. - /// - /// 0: No TX dedicated buffers - /// 1-32: Number of TX buffers used for TX dedicated buffers - /// >32: Interpreted as 32 - pub(crate) dedicated_size: u8, - pub(crate) data_field_size: DataFieldSize, -} - -#[cfg(can_fdcan_h7)] -pub struct MessageRamConfig { - /// Base offset of the Message RAM region allocated to this - /// peripheral instance. - /// In bytes. - pub(crate) base_offset: usize, - /// Available space allocated for this peripheral instance in bytes. - /// If present, it will be validated that everything fits into the - /// allocated space. - /// In bytes. - pub(crate) available_space: Option, - - /// 0: No standard Message ID filter - /// 1-128: Number of standard Message ID filter elements - /// >128: Interpreted as 128 - pub(crate) standard_id_filter_size: u8, - /// 0: No extended Message ID filter - /// 1-64: Number of extended Message ID filter elements - /// >64: Interpreted as 64 - pub(crate) extended_id_filter_size: u8, - - pub(crate) rx_fifo_0: RxFifoConfig, - pub(crate) rx_fifo_1: RxFifoConfig, - pub(crate) rx_buffer: RxBufferConfig, - pub(crate) tx: TxConfig, -} - -#[cfg(can_fdcan_h7)] -const MSG_RAM_SIZE: usize = 0x2800; - struct ElementAllocator(usize); impl ElementAllocator { fn new(offset: usize) -> Self { @@ -363,6 +191,9 @@ impl ElementSizing { fn end_offset_words(&self) -> usize { self.offset + self.total_size_words() } + unsafe fn make(&self, base_ptr: *mut RW) -> Elements { + Elements::new(base_ptr.add(self.offset), self.num_elements, self.element_size) + } } struct ElementsSizing { @@ -381,7 +212,20 @@ struct ElementsSizing { trigger: ElementSizing, } +pub(crate) struct MessageRamSegment { + /// Base offset of the Message RAM region allocated to this + /// peripheral instance. + /// In bytes. + pub base_offset: usize, + /// Available space allocated for this peripheral instance in bytes. + /// If present, it will be validated that everything fits into the + /// allocated space. + /// In bytes. + pub available_space: Option, +} + impl ElementsSizing { + /// Calculates the sizing used for the simplified variant of the M_CAN peripheral. fn calculate_element_sizing_simplified() -> ElementsSizing { let mut a = ElementAllocator::new(0); @@ -401,7 +245,9 @@ impl ElementsSizing { } } - fn calculate_element_sizing(config: &MessageRamConfig) -> ElementsSizing { + /// Calculates the sizing used for the full variant of the M_CAN peripheral. + /// This mapping is customizable, a configuration is needed to instantiate it. + fn calculate_element_sizing(config: &MessageRamConfig, segment: &MessageRamSegment) -> ElementsSizing { assert!( config.standard_id_filter_size <= 128, "more than 128 standard id filters not supported" @@ -427,7 +273,7 @@ impl ElementsSizing { "total TX elements can not be larger than 32" ); - let base_offset = config.base_offset; + let base_offset = segment.base_offset; let base_offset_words = base_offset >> 2; let mut a = ElementAllocator::new(base_offset_words); @@ -447,148 +293,97 @@ impl ElementsSizing { 2 + config.rx_buffer.data_field_size.word_size(), config.rx_buffer.size as usize, ), - // TODO implement TX events - tx_event: a.next(2, 0), + // Hard code size 16 for TX Event FIFO. + // Closely coupled to the driver implementation, no reason to + // allow for customization for now. + tx_event: a.next(2, 16), tx_elements: a.next( 2 + config.tx.data_field_size.word_size(), (config.tx.dedicated_size + config.tx.queue_size) as usize, ), dedicated_len: config.tx.dedicated_size as usize, - // TODO implement triggers + // Driver does not support TTCAN for now, zero triggers. trigger: a.next(2, 0), end_offset: a.0, } } + + unsafe fn make(&self, ram_base: *mut RW) -> MessageRam { + MessageRam { + base_ptr: ram_base.add(self.start_offset), + standard_filter: self.standard_id.make(ram_base), + extended_filter: self.extended_id.make(ram_base), + rx_fifos: [self.rx_fifo_0.make(ram_base), self.rx_fifo_1.make(ram_base)], + #[cfg(can_fdcan_h7)] + rx_buffer: self.rx_buffer.make(ram_base), + tx_event_fifo: self.tx_event.make(ram_base), + tx_elements: self.tx_elements.make(ram_base), + #[cfg(can_fdcan_h7)] + tx_buffer_len: self.dedicated_len, + tx_queue_len: self.tx_elements.num_elements - self.dedicated_len, + #[cfg(can_fdcan_h7)] + trigger_memory: self.trigger.make(ram_base), + } + } } #[cfg(can_fdcan_h7)] impl MessageRamConfig { + // This constant is a H7 thing, not a limitation of M_CAN. + const H7_MSG_RAM_SIZE: usize = 0x2800; + /// Configures message ram for the peripheral according to the supplied /// config and returns a struct which can be used to interact with the /// message RAM. - #[cfg(can_fdcan_h7)] - pub fn apply_config(&self, regs: &crate::pac::can::Fdcan, ram: &crate::pac::fdcanram::Fdcanram) -> MessageRam { - let base_offset = self.base_offset; - let base_offset_words = base_offset >> 2; + pub(crate) unsafe fn apply_config( + &self, + segment: &MessageRamSegment, + regs: &crate::pac::can::Fdcan, + ram: &crate::pac::fdcanram::Fdcanram, + ) -> MessageRam { + let sizing = ElementsSizing::calculate_element_sizing(self, segment); - // Abbreviations: - // sa: start address - // es: element size - // en: element num - // ts: total size - - assert!( - self.standard_id_filter_size <= 128, - "more than 128 standard id filters not supported" - ); - let sid_sa = base_offset_words; - let sid_es = 1; - let sid_en = self.standard_id_filter_size as usize; - let sid_ts = sid_es * sid_en; - - assert!( - self.extended_id_filter_size <= 64, - "more than 64 extended id filters not supported" - ); - let xid_sa = sid_sa + sid_ts; - let xid_es = 2; - let xid_en = self.extended_id_filter_size as usize; - let xid_ts = xid_es * xid_en; - - assert!( - self.rx_fifo_0.fifo_size <= 64, - "more than 64 rx fifo 0 elements not supported" - ); - let rx0_sa = xid_sa + xid_ts; - let rx0_es = 2 + self.rx_fifo_0.data_field_size.word_size(); - let rx0_en = self.rx_fifo_0.fifo_size as usize; - let rx0_ts = rx0_es * rx0_en; - - assert!( - self.rx_fifo_1.fifo_size <= 64, - "more than 64 rx fifo 1 elements not supported" - ); - let rx1_sa = rx0_sa + rx0_ts; - let rx1_es = 2 + self.rx_fifo_1.data_field_size.word_size(); - let rx1_en = self.rx_fifo_1.fifo_size as usize; - let rx1_ts = rx1_es * rx1_en; - - assert!( - self.rx_buffer.size <= 64, - "more than 64 rx buffer elements not supported" - ); - let rxb_sa = rx1_sa + rx1_ts; - let rxb_es = 2 + self.rx_buffer.data_field_size.word_size(); - let rxb_en = self.rx_buffer.size as usize; - let rxb_ts = rxb_es * rxb_en; - - let txe_sa = rxb_sa + rxb_ts; - let txe_es = 2; - let txe_en = 0; // TODO implement TX events - let txe_ts = txe_es * txe_en; - - assert!( - self.tx.dedicated_size + self.tx.queue_size <= 32, - "total TX elements can not be larger than 32" - ); - let txx_es = 2 + self.tx.data_field_size.word_size(); - - let txq_sa = txe_sa + txe_ts; - let txq_en = self.tx.queue_size as usize; - let txq_ts = txx_es * txq_en; - - let txd_sa = txq_sa + txq_ts; - let txd_en = self.tx.dedicated_size as usize; - let txd_ts = txx_es * txd_en; - - let tmc_sa = txd_sa + txd_ts; - let tmc_es = 2; - let tmc_en = 0; // TODO implement trigger stuff - let tmc_ts = tmc_es * tmc_en; - - let end_offset_words = tmc_sa + tmc_ts; - let total_size_words = end_offset_words - base_offset_words; + let total_size_words = sizing.end_offset - sizing.start_offset; let total_size_bytes = total_size_words << 2; - //defmt::info!("total calculated word size: {:#x}", total_size_words); - if let Some(avail) = self.available_space { + if let Some(avail) = segment.available_space { assert!(total_size_bytes <= avail, "CAN RAM config exceeded available space!"); } // Standard ID filter config // Fully managed regs.sidfc().modify(|v| { - v.set_flssa(sid_sa as u16); - v.set_lss(sid_en as u8); + v.set_flssa(sizing.standard_id.offset as u16); + v.set_lss(sizing.standard_id.num_elements as u8); }); // Extended ID filter config // Fully managed regs.xidfc().modify(|v| { - v.set_flesa(xid_sa as u16); - v.set_lse(xid_en as u8); + v.set_flesa(sizing.extended_id.offset as u16); + v.set_lse(sizing.extended_id.num_elements as u8); }); // RX FIFO 0 config regs.rxfc(0).modify(|v| { // F0OM - RX FIFO Operating Mode // F0WM - RX FIFO Water Mark - v.set_fsa(rx0_sa as u16); - v.set_fs(rx0_en as u8); + v.set_fsa(sizing.rx_fifo_0.offset as u16); + v.set_fs(sizing.rx_fifo_0.num_elements as u8); }); // RX FIFO 1 config regs.rxfc(1).modify(|v| { // F1OM - RX FIFO Operating Mode // F1WM - RX FIFO Water Mark - v.set_fsa(rx1_sa as u16); - v.set_fs(rx1_en as u8); + v.set_fsa(sizing.rx_fifo_1.offset as u16); + v.set_fs(sizing.rx_fifo_1.num_elements as u8); }); // RX buffer config // Fully managed regs.rxbc().modify(|v| { - v.set_rbsa(rxb_sa as u16); + v.set_rbsa(sizing.rx_buffer.offset as u16); }); // Rx buffer / queue element size config @@ -602,8 +397,8 @@ impl MessageRamConfig { // TX event FIFO config regs.txefc().modify(|v| { // EFWM - Event FIFO Water Mark - v.set_efsa(txe_sa as u16); - v.set_efs(txe_en as u8); + v.set_efsa(sizing.tx_event.offset as u16); + v.set_efs(sizing.tx_event.num_elements as u8); }); // Tx buffer / queue element size config @@ -615,57 +410,25 @@ impl MessageRamConfig { // TX queue configuration // Fully managed regs.txbc().modify(|v| { - v.set_tfqm(self.tx.queue_operation_mode.reg_value()); - v.set_tbsa(txq_sa as u16); - v.set_ndtb(txd_en as u8); - v.set_tfqs(txq_en as u8); + //v.set_tfqm(self.tx.queue_operation_mode.reg_value()); + v.set_tbsa(sizing.tx_elements.offset as u16); + v.set_ndtb(sizing.dedicated_len as u8); + v.set_tfqs((sizing.tx_elements.num_elements - sizing.dedicated_len) as u8); }); // TT Trigger memory config // Fully managed regs.tttmc().modify(|v| { - v.set_tmsa(tmc_sa as u16); - v.set_tme(tmc_en as u8); + v.set_tmsa(sizing.trigger.offset as u16); + v.set_tme(sizing.trigger.num_elements as u8); }); let ram_ptr = ram.as_ptr() as *mut RW; - let base_ptr = unsafe { ram_ptr.add(base_offset_words) }; - - // let mut test_ptr = base_ptr as *const u32; - // while test_ptr < unsafe { base_ptr.byte_add(self.available_space.unwrap()) as *const u32 } { - // let val = unsafe { test_ptr.read() }; - // if val == 0xaa95fd71 || val == 0x71fd95aa { - // defmt::info!("FOUND BYTE MATCH: {}", test_ptr); - // } - // test_ptr = unsafe { test_ptr.byte_add(4) }; - // } - - // if let Some(avail) = self.available_space { - // unsafe { core::ptr::write_bytes(ram_ptr as *mut u8, 0, avail) }; - // } - - // assert!(unsafe { base_ptr as usize == (ram_ptr as *mut u8).add(self.base_offset) as usize }); assert!(unsafe { - (ram_ptr.add(end_offset_words) as usize) < ((ram_ptr as *mut u8).add(MSG_RAM_SIZE) as usize) + (ram_ptr.add(sizing.end_offset) as usize) < ((ram_ptr as *mut u8).add(Self::H7_MSG_RAM_SIZE) as usize) }); - unsafe { - MessageRam { - base_ptr, - standard_filter: Elements::new(ram_ptr.add(sid_sa), sid_en, sid_es), - extended_filter: Elements::new(ram_ptr.add(xid_sa), xid_en, xid_es), - rx_fifos: [ - Elements::new(ram_ptr.add(rx0_sa), rx0_en, rx0_es), - Elements::new(ram_ptr.add(rx1_sa), rx1_en, rx1_es), - ], - rx_buffer: Elements::new(ram_ptr.add(rxb_sa), rxb_en, rxb_es), - tx_event_fifo: Elements::new(ram_ptr.add(txe_sa), txe_en, txe_es), - tx_elements: Elements::new(ram_ptr.add(txq_sa), txd_en + txq_en, txx_es), - tx_buffer_len: txd_en, - tx_queue_len: txq_en, - trigger_memory: Elements::new(ram_ptr.add(tmc_sa), tmc_en, tmc_es), - } - } + unsafe { sizing.make(ram_ptr) } } } diff --git a/embassy-stm32/src/can/fd/low_level/mod.rs b/embassy-stm32/src/can/fd/low_level/mod.rs index b61670035..c6f24c1ad 100644 --- a/embassy-stm32/src/can/fd/low_level/mod.rs +++ b/embassy-stm32/src/can/fd/low_level/mod.rs @@ -3,6 +3,7 @@ use core::convert::Infallible; use cfg_if::cfg_if; use message_ram::{HeaderElement, RxFifoElementHeader}; use stm32_metapac::can::regs::{Ndat1, Ndat2, Txbcr}; +use util::{RxElementData, TxElementData}; use crate::can::{ enums::BusError, @@ -15,8 +16,6 @@ mod filter; pub(crate) mod message_ram; mod util; -use util::{extract_frame, put_tx_data, put_tx_header}; - /// Loopback Mode #[derive(Clone, Copy, Debug)] pub(crate) enum LoopbackMode { @@ -73,35 +72,21 @@ impl CanLowLevel { } } -/// Legacy and bad -impl CanLowLevel { - pub fn bad_write(&self, frame: &F) -> nb::Result { - let is_full = self.regs.txfqs().read().tfqf(); - - // TODO this does not dequeue lower priority frame! - // Temporary! - - if is_full { - // TODO only for FIFO! - return Err(nb::Error::WouldBlock); - } - - let result = self.tx_fifo_add(frame.header(), frame.data()); - - if let Some(_idx) = result { - Ok(true) - } else { - Ok(false) - } - } -} - /// Tx impl CanLowLevel { fn tx_element_set(&self, idx: u8, header: &Header, data: &[u8]) { let element = self.message_ram.tx_elements.get_mut(idx as usize); - put_tx_header(&mut element.header, &header); - put_tx_data(&mut element.data, data); + + let mut n_data = [0u8; 64]; + n_data.iter_mut().zip(data.iter()).for_each(|(to, from)| *to = *from); + + TxElementData { + header: *header, + data: n_data, + marker: 0, + tx_event: false, + } + .put(element); } fn tx_element_get(&self, idx: u8) -> (Header, [u8; 64]) { @@ -181,16 +166,7 @@ impl CanLowLevel { } impl CanLowLevel { - pub fn rx_element_get(&self, element: &HeaderElement) -> Option<(Header, u16, [u8; 64])> { - let mut buffer = [0u8; 64]; - // TODO allow read len to be lower than drl? - let maybe_header = extract_frame(&element.header, &element.data, &mut buffer); - - let (header, ts) = maybe_header.unwrap(); - Some((header, ts, buffer)) - } - - pub fn rx_buffer_read(&self, buffer_idx: u8) -> Option<(Header, u16, [u8; 64])> { + pub fn rx_buffer_read(&self, buffer_idx: u8) -> Option { let bit_idx = buffer_idx & 0b11111; let bit = 1 << bit_idx; @@ -206,7 +182,7 @@ impl CanLowLevel { } let element = self.message_ram.rx_buffer.get_mut(buffer_idx as usize); - let ret = self.rx_element_get(element); + let ret = RxElementData::extract(element); match buffer_idx { idx if idx < 32 => self.regs.ndat1().write_value(Ndat1(bit)), @@ -214,10 +190,10 @@ impl CanLowLevel { _ => panic!(), }; - ret + Some(ret) } - pub fn rx_fifo_read(&self, fifo_num: u8) -> Option<(Header, u16, [u8; 64])> { + pub fn rx_fifo_read(&self, fifo_num: u8) -> Option { let status = self.regs.rxfs(fifo_num as usize).read(); let fill_level = status.ffl(); @@ -227,11 +203,11 @@ impl CanLowLevel { let get_index = self.regs.rxfs(fifo_num as usize).read().fgi(); let element = self.message_ram.rx_fifos[fifo_num as usize].get_mut(get_index as usize); - let ret = self.rx_element_get(element); + let ret = RxElementData::extract(element); self.regs.rxfa(fifo_num as usize).write(|v| v.set_fai(get_index)); - ret + Some(ret) } } diff --git a/embassy-stm32/src/can/fd/low_level/util.rs b/embassy-stm32/src/can/fd/low_level/util.rs index 0b8eee71c..edb76d395 100644 --- a/embassy-stm32/src/can/fd/low_level/util.rs +++ b/embassy-stm32/src/can/fd/low_level/util.rs @@ -4,7 +4,9 @@ use volatile_register::RW; use crate::can::frame::Header; -use super::message_ram::{DataLength, Event, FrameFormat, IdType, RxFifoElementHeader, TxBufferElementHeader}; +use super::message_ram::{ + DataLength, Event, FrameFormat, HeaderElement, IdType, RxFifoElementHeader, TxBufferElementHeader, +}; fn make_id(id: u32, extended: bool) -> embedded_can::Id { if extended { @@ -15,93 +17,101 @@ fn make_id(id: u32, extended: bool) -> embedded_can::Id { } } -pub(crate) fn put_tx_header(reg: &mut TxBufferElementHeader, header: &Header) { - let (id, id_type) = match header.id() { - // A standard identifier has to be written to ID[28:18]. - embedded_can::Id::Standard(id) => ((id.as_raw() as u32) << 18, IdType::StandardId), - embedded_can::Id::Extended(id) => (id.as_raw() as u32, IdType::ExtendedId), - }; - - // Use FDCAN only for DLC > 8. FDCAN users can revise this if required. - let frame_format = if header.len() > 8 || header.fdcan() { - FrameFormat::Fdcan - } else { - FrameFormat::Classic - }; - let brs = (frame_format == FrameFormat::Fdcan) && header.bit_rate_switching(); - - reg.write(|w| { - unsafe { w.id().bits(id) } - .rtr() - .bit(header.len() == 0 && header.rtr()) - .xtd() - .set_id_type(id_type) - .set_len(DataLength::new(header.len(), frame_format)) - .set_event(Event::NoEvent) - .fdf() - .set_format(frame_format) - .brs() - .bit(brs) - //esi.set_error_indicator(//TODO//) - }); +pub(crate) struct TxElementData { + pub header: Header, + pub data: [u8; 64], + pub marker: u8, + pub tx_event: bool, } -pub(crate) fn put_tx_data(mailbox_data: &mut [RW], buffer: &[u8]) { - let mut lbuffer = [0_u32; 16]; - let len = buffer.len(); - let data = unsafe { slice::from_raw_parts_mut(lbuffer.as_mut_ptr() as *mut u8, len) }; - data[..len].copy_from_slice(&buffer[..len]); - let data_len = ((len) + 3) / 4; - for (register, byte) in mailbox_data.iter_mut().zip(lbuffer[..data_len].iter()) { - unsafe { register.write(*byte) }; +impl TxElementData { + //#[inline] + //pub(crate) fn extract(element: &HeaderElement) -> Self { + // todo!() + //} + + #[inline] + pub fn put(&self, element: &mut HeaderElement) { + let (id, id_type) = match self.header.id() { + // A standard identifier has to be written to ID[28:18]. + embedded_can::Id::Standard(id) => ((id.as_raw() as u32) << 18, IdType::StandardId), + embedded_can::Id::Extended(id) => (id.as_raw() as u32, IdType::ExtendedId), + }; + + let frame_format = if self.header.fdcan() { + FrameFormat::Fdcan + } else { + FrameFormat::Classic + }; + + element.header.write(|w| { + w.esi().bit(self.header.esi()); + w.xtd().set_id_type(id_type); + w.rtr().bit(self.header.rtr()); + w.id().bits(id); + w.mm().bits(self.marker); + w.efc().bit(self.tx_event); + w.fdf().bit(self.header.fdcan()); + w.brs().bit(self.header.bit_rate_switching()); + w.set_len(DataLength::new(self.header.len(), frame_format)); + w + }); + + // TODO validate endianness + let u32_data: &[u32; 16] = unsafe { core::mem::transmute(&self.data) }; + element.data.iter_mut().zip(u32_data.iter()).for_each(|(reg, data)| { + unsafe { reg.write(*data) }; + }); } } -pub(crate) fn data_from_fifo(buffer: &mut [u8], mailbox_data: &[RW], len: usize) { - for (i, register) in mailbox_data.iter().enumerate() { - let register_value = register.read(); - let register_bytes = unsafe { slice::from_raw_parts(®ister_value as *const u32 as *const u8, 4) }; - let num_bytes = (len) - i * 4; - if num_bytes <= 4 { - buffer[i * 4..i * 4 + num_bytes].copy_from_slice(®ister_bytes[..num_bytes]); - break; +pub(crate) struct RxElementData { + pub header: Header, + pub data: [u8; 64], + /// If `dlc` is above what can be represented in the configured element, + /// data can be clipped. + /// This indicates what the real data word size (4 byte words) was. + pub data_words: u8, + pub timestamp: u16, + /// If None, the frame was accepted without a filter as enabled by + /// `GFC.ANFS` or `GFC.ANFE`. + /// If Some, contains the index of the filter that was matched. + pub filter_index: Option, +} + +impl RxElementData { + #[inline] + pub fn extract(element: &HeaderElement) -> Self { + let reg = element.header.read(); + + let dlc = reg.to_data_length().len(); + + let id = make_id(reg.id().bits(), reg.xtd().bits()); + let timestamp = reg.txts().bits; + + let rtr = reg.rtr().bits; + let can_fd = reg.fdf().bits; + let brs = reg.brs().bits; + let esi = reg.esi().bits; + + let filter_index = if reg.anmf().bits { Some(reg.fidx().bits) } else { None }; + + let mut data_u32 = [0u32; 16]; + data_u32 + .iter_mut() + .zip(element.data.iter()) + .for_each(|(val, reg)| *val = reg.read()); + // TODO validate endianness + let data: [u8; 64] = unsafe { core::mem::transmute(data_u32) }; + + let data_words = element.data.len() as u8; + + Self { + header: Header::new(id, dlc, rtr).set_can_fd(can_fd, brs).set_esi(esi), + data_words, + data, + timestamp, + filter_index, } - buffer[i * 4..(i + 1) * 4].copy_from_slice(register_bytes); } } - -pub(crate) fn data_from_tx_buffer(buffer: &mut [u8], mailbox_data: &[RW], len: usize) { - for (i, register) in mailbox_data.iter().enumerate() { - let register_value = register.read(); - let register_bytes = unsafe { slice::from_raw_parts(®ister_value as *const u32 as *const u8, 4) }; - let num_bytes = (len) - i * 4; - if num_bytes <= 4 { - buffer[i * 4..i * 4 + num_bytes].copy_from_slice(®ister_bytes[..num_bytes]); - break; - } - buffer[i * 4..(i + 1) * 4].copy_from_slice(register_bytes); - } -} - -pub(crate) fn extract_frame( - mailbox_header: &RxFifoElementHeader, - mailbox_data: &[RW], - buffer: &mut [u8], -) -> Option<(Header, u16)> { - let header_reg = mailbox_header.read(); - - let id = make_id(header_reg.id().bits(), header_reg.xtd().bits()); - let dlc = header_reg.to_data_length().len(); - let len = dlc as usize; - let timestamp = header_reg.txts().bits; - if len > buffer.len() { - return None; - } - data_from_fifo(buffer, mailbox_data, len); - let header = if header_reg.fdf().bits { - Header::new_fd(id, dlc, header_reg.rtr().bits(), header_reg.brs().bits()) - } else { - Header::new(id, dlc, header_reg.rtr().bits()) - }; - Some((header, timestamp)) -} diff --git a/embassy-stm32/src/can/fd/mod.rs b/embassy-stm32/src/can/fd/mod.rs index e2b4c34bc..26872b7a6 100644 --- a/embassy-stm32/src/can/fd/mod.rs +++ b/embassy-stm32/src/can/fd/mod.rs @@ -1,6 +1,11 @@ //! Module containing that which is specific to fdcan hardware variant -use core::{future::poll_fn, marker::PhantomData, sync::atomic::AtomicU32, task::Poll}; +use core::{ + future::poll_fn, + marker::PhantomData, + sync::atomic::{AtomicU32, Ordering}, + task::Poll, +}; use configurator::Properties; use embassy_sync::waitqueue::AtomicWaker; @@ -36,6 +41,12 @@ pub struct Can<'d, M> { properties: Properties, } +#[derive(Debug, Clone, Copy)] +pub enum RxQueue { + Q0 = 0, + Q1 = 1, +} + impl<'d, M: CanMode> Can<'d, M> { /// Get driver properties pub fn properties(&self) -> &Properties { @@ -62,10 +73,12 @@ impl<'d, M: CanMode> Can<'d, M> { } fn internal_try_read(&self) -> Option, BusError>> { - if let Some((header, ts, data)) = self.info.low.rx_fifo_read(0) { - let ts = self.info.calc_timestamp(self.state.ns_per_timer_tick, ts); - let data = &data[0..header.len() as usize]; - let frame = match BaseFrame::from_header(header, data) { + if let Some(element) = self.info.low.rx_fifo_read(0) { + let ts = self + .info + .calc_timestamp(self.state.ns_per_timer_tick, element.timestamp); + let data = &element.data[0..element.header.len() as usize]; + let frame = match BaseFrame::from_header(element.header, data) { Ok(frame) => frame, Err(_) => panic!(), }; @@ -102,57 +115,110 @@ impl<'d, M: CanMode> Can<'d, M> { //self.state.rx_mode.try_read(self.info, self.state) self.internal_try_read() } +} - /// Queues the message to be sent but exerts backpressure. If a lower-priority - /// frame is dropped from the mailbox, it is returned. If no lower-priority frames - /// can be replaced, this call asynchronously waits for a frame to be successfully - /// transmitted, then tries again. - async fn internal_write(&self, frame: &BaseFrame) -> Option> { +impl<'d, M: CanMode> Can<'d, M> { + /// Queues the message to be sent but exerts backpressure. + /// If the given queue is full, this call will wait asynchronously + /// until it can be added to the queue. + /// Returns a `TxRef` which can be used to monitor the completion + /// of the transmission. + pub async fn tx_queue(&self, frame: &BaseFrame) -> TxRef { poll_fn(|cx| { self.state.tx_waker.register(cx.waker()); match self.info.low.tx_fifo_add(frame.header(), frame.data()) { - Some(_idx) => Poll::Ready(None), + // Frame was successfully added to slot. + Some(slot_idx) => { + let generation = self.state.tx_generations[slot_idx as usize].fetch_add(1, Ordering::Relaxed); + + Poll::Ready(TxRef { + slot_idx, + generation, + state: self.state, + completed: false, + }) + } + // No free slot, retry later. None => Poll::Pending, } + }) + .await + } +} - //if !info.low.bad_write(frame) { - // return Poll::Ready(*frame); - //} - //if let Ok(dropped) = info.low.bad_write(frame) { - // return Poll::Ready(dropped); - //} +// == TX +// Buffer add req -> TXBAR +// Buffer pending, TXBRP AS, TXBTO DE, TXBCF DE +// +// Buffer transmitted, TXBTO AS, TXBRP DE +// +// Buffer cancel req -> TXBCR +// Buffer failed, TXBCF AS, TXBRP DE - // Couldn't replace any lower priority frames. Need to wait for some mailboxes - // to clear. - //Poll::Pending +/// Reference to a frame which is in the process of being transmitted. +pub struct TxRef { + slot_idx: u8, + generation: u32, + state: &'static State, + completed: bool, +} + +impl TxRef { + /// Waits for transmission to finish. + pub async fn wait(&mut self) { + poll_fn(|cx| { + if self.poll_done() { + Poll::Ready(()) + } else { + self.state.tx_done_waker.register(cx.waker()); + Poll::Pending + } }) .await } - /// Queues the message to be sent but exerts backpressure. If a lower-priority - /// frame is dropped from the mailbox, it is returned. If no lower-priority frames - /// can be replaced, this call asynchronously waits for a frame to be successfully - /// transmitted, then tries again. - pub async fn write(&mut self, frame: &BaseFrame) -> Option> { - self.internal_write(frame).await + /// Polls the transmission, returns true if it is finished. + pub fn poll_done(&mut self) -> bool { + // If there is a result stored in self, we are finished. + if self.completed { + return true; + } + + // Check the generation for the current slot, if it is different + // the slot finished. + let current_generation = self.state.tx_generations[self.slot_idx as usize].load(Ordering::Relaxed); + if current_generation != self.generation { + // Store completion state to handle subsequent calls. + self.completed = true; + return true; + } + + return false; } - /// Queues the message to be sent. Handles rescheduling lower priority message - /// if one was removed from the mailbox. - pub async fn write_blocking(&mut self, frame: &BaseFrame) { - let mut frame_opt = self.internal_write(frame).await; - while let Some(frame) = frame_opt { - frame_opt = self.internal_write(&frame).await; - } + /// Begins cancellation of the transmission. + /// Cancellation may take some time, and may race with + /// successful transmission. + /// You can call `wait` to wait for transmission completion + /// or call `poll` to check the state at any point. + pub fn cancel(&mut self) -> &mut Self { + // Set the tx busy flag. + // This prevents any other transmission from taking our slot + self.state.info.low.tx_cancel(1u32 << self.slot_idx); + self } } pub(self) struct State { + pub info: &'static Info, + pub rx_waker: AtomicWaker, pub tx_waker: AtomicWaker, - pub refcount: AtomicU32, + pub tx_done_waker: AtomicWaker, + pub tx_busy: AtomicU32, + pub tx_generations: [AtomicU32; 32], pub err_waker: AtomicWaker, @@ -160,11 +226,50 @@ pub(self) struct State { } impl State { - const fn new() -> Self { + const fn new(info: &'static Info) -> Self { Self { + info, + rx_waker: AtomicWaker::new(), tx_waker: AtomicWaker::new(), - refcount: AtomicU32::new(0), + + tx_done_waker: AtomicWaker::new(), + tx_busy: AtomicU32::new(0), + tx_generations: [ + AtomicU32::new(0), + AtomicU32::new(0), + AtomicU32::new(0), + AtomicU32::new(0), + AtomicU32::new(0), + AtomicU32::new(0), + AtomicU32::new(0), + AtomicU32::new(0), + AtomicU32::new(0), + AtomicU32::new(0), + AtomicU32::new(0), + AtomicU32::new(0), + AtomicU32::new(0), + AtomicU32::new(0), + AtomicU32::new(0), + AtomicU32::new(0), + AtomicU32::new(0), + AtomicU32::new(0), + AtomicU32::new(0), + AtomicU32::new(0), + AtomicU32::new(0), + AtomicU32::new(0), + AtomicU32::new(0), + AtomicU32::new(0), + AtomicU32::new(0), + AtomicU32::new(0), + AtomicU32::new(0), + AtomicU32::new(0), + AtomicU32::new(0), + AtomicU32::new(0), + AtomicU32::new(0), + AtomicU32::new(0), + ], + err_waker: AtomicWaker::new(), ns_per_timer_tick: 0, } diff --git a/embassy-stm32/src/can/fd/peripheral.rs b/embassy-stm32/src/can/fd/peripheral.rs index 3840b4460..cce8e5814 100644 --- a/embassy-stm32/src/can/fd/peripheral.rs +++ b/embassy-stm32/src/can/fd/peripheral.rs @@ -64,52 +64,55 @@ macro_rules! impl_fdcan { ($inst:ident, //$irq0:ident, $irq1:ident, $msg_ram_inst:ident, $msg_ram_offset:literal, $msg_ram_size:literal) => { - impl SealedInstance for peripherals::$inst { - const MSG_RAM_OFFSET: usize = $msg_ram_offset; - - unsafe fn mut_info() -> &'static mut Info { - static mut INFO: Info = Info { - low: unsafe { CanLowLevel::new(crate::pac::$inst, crate::pac::$msg_ram_inst, $msg_ram_offset, $msg_ram_size) }, - interrupt0: crate::_generated::peripheral_interrupts::$inst::IT0::IRQ, - _interrupt1: crate::_generated::peripheral_interrupts::$inst::IT1::IRQ, - tx_waker: crate::_generated::peripheral_interrupts::$inst::IT0::pend, - }; - &mut *core::ptr::addr_of_mut!(INFO) - } - - fn info() -> &'static Info { - unsafe { peripherals::$inst::mut_info() } - } - - unsafe fn mut_state() -> &'static mut State { - static mut STATE: State = State::new(); - &mut *core::ptr::addr_of_mut!(STATE) - } - fn state() -> &'static State { - unsafe { peripherals::$inst::mut_state() } - } - - #[cfg(feature = "time")] - fn calc_timestamp(ns_per_timer_tick: u64, ts_val: u16) -> Timestamp { - let now_embassy = embassy_time::Instant::now(); - if ns_per_timer_tick == 0 { - return now_embassy; - } - let cantime = { Self::info().low.regs.tscv().read().tsc() }; - let delta = cantime.overflowing_sub(ts_val).0 as u64; - let ns = ns_per_timer_tick * delta as u64; - now_embassy - embassy_time::Duration::from_nanos(ns) - } - - #[cfg(not(feature = "time"))] - fn calc_timestamp(_ns_per_timer_tick: u64, ts_val: u16) -> Timestamp { - ts_val - } - - } #[allow(non_snake_case)] pub(crate) mod $inst { + use super::*; + + static mut INFO: Info = Info { + low: unsafe { CanLowLevel::new(crate::pac::$inst, crate::pac::$msg_ram_inst, $msg_ram_offset, $msg_ram_size) }, + interrupt0: crate::_generated::peripheral_interrupts::$inst::IT0::IRQ, + _interrupt1: crate::_generated::peripheral_interrupts::$inst::IT1::IRQ, + tx_waker: crate::_generated::peripheral_interrupts::$inst::IT0::pend, + }; + + impl SealedInstance for peripherals::$inst { + const MSG_RAM_OFFSET: usize = $msg_ram_offset; + + unsafe fn mut_info() -> &'static mut Info { + &mut *core::ptr::addr_of_mut!(INFO) + } + + fn info() -> &'static Info { + unsafe { peripherals::$inst::mut_info() } + } + + unsafe fn mut_state() -> &'static mut State { + static mut STATE: State = State::new(unsafe { &*core::ptr::addr_of!(INFO) }); + &mut *core::ptr::addr_of_mut!(STATE) + } + fn state() -> &'static State { + unsafe { peripherals::$inst::mut_state() } + } + + #[cfg(feature = "time")] + fn calc_timestamp(ns_per_timer_tick: u64, ts_val: u16) -> Timestamp { + let now_embassy = embassy_time::Instant::now(); + if ns_per_timer_tick == 0 { + return now_embassy; + } + let cantime = { Self::info().low.regs.tscv().read().tsc() }; + let delta = cantime.overflowing_sub(ts_val).0 as u64; + let ns = ns_per_timer_tick * delta as u64; + now_embassy - embassy_time::Duration::from_nanos(ns) + } + + #[cfg(not(feature = "time"))] + fn calc_timestamp(_ns_per_timer_tick: u64, ts_val: u16) -> Timestamp { + ts_val + } + + } foreach_interrupt!( ($inst,can,FDCAN,IT0,$irq:ident) => { @@ -119,10 +122,11 @@ macro_rules! impl_fdcan { pub type Interrupt1 = crate::interrupt::typelevel::$irq; }; ); - } - impl Instance for peripherals::$inst { - type IT0Interrupt = $inst::Interrupt0; - type IT1Interrupt = $inst::Interrupt1; + + impl Instance for peripherals::$inst { + type IT0Interrupt = $inst::Interrupt0; + type IT1Interrupt = $inst::Interrupt1; + } } }; } diff --git a/embassy-stm32/src/can/fd/util.rs b/embassy-stm32/src/can/fd/util.rs index a740cc84c..f68dc6607 100644 --- a/embassy-stm32/src/can/fd/util.rs +++ b/embassy-stm32/src/can/fd/util.rs @@ -3,11 +3,11 @@ use super::Info; pub fn calc_ns_per_timer_tick( info: &'static Info, freq: crate::time::Hertz, - mode: crate::can::fd::config::FrameTransmissionConfig, + mode: crate::can::fd::config::CanFdMode, ) -> u64 { match mode { // Use timestamp from Rx FIFO to adjust timestamp reported to user - crate::can::fd::config::FrameTransmissionConfig::ClassicCanOnly => { + crate::can::fd::config::CanFdMode::ClassicCanOnly => { let prescale: u64 = ({ info.low.regs.nbtp().read().nbrp() } + 1) as u64 * ({ info.low.regs.tscc().read().tcp() } + 1) as u64; 1_000_000_000 as u64 / (freq.0 as u64 * prescale) diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs index c51788138..455e0eb6a 100644 --- a/embassy-stm32/src/can/fdcan.rs +++ b/embassy-stm32/src/can/fdcan.rs @@ -12,6 +12,7 @@ use super::frame::*; pub use self::fd::{config, filter}; pub use super::common::{BufferedCanReceiver, BufferedCanSender}; pub use fd::configurator::CanConfigurator; +pub use fd::interrupt::{IT0InterruptHandler, IT1InterruptHandler}; pub use fd::peripheral::*; pub use fd::Can; diff --git a/embassy-stm32/src/can/frame.rs b/embassy-stm32/src/can/frame.rs index e8171d110..4cd9a27e7 100644 --- a/embassy-stm32/src/can/frame.rs +++ b/embassy-stm32/src/can/frame.rs @@ -38,9 +38,17 @@ impl defmt::Format for Header { } impl Header { - const FLAG_RTR: usize = 0; // Remote - const FLAG_FDCAN: usize = 1; // FDCan vs Classic CAN - const FLAG_BRS: usize = 2; // Bit-rate switching, ignored for Classic CAN + /// RTR (remote transmit request) flag in CAN frame + const FLAG_RTR: usize = 0; + /// FDCan vs Classic CAN + const FLAG_FDCAN: usize = 1; + /// Bit-rate switching, ignored for Classic CAN + const FLAG_BRS: usize = 2; + /// ESI recessive in CAN FD message. + /// ORed with error passive flag before transmission. + /// By spec, an error active mode may transmit ESI resessive, + /// but an error passive node will always transmit ESI resessive. + const FLAG_ESI: usize = 3; /// Create new CAN Header pub fn new(id: embedded_can::Id, len: u8, rtr: bool) -> Header { @@ -49,13 +57,15 @@ impl Header { Header { id, len, flags } } - /// Create new CAN FD Header - pub fn new_fd(id: embedded_can::Id, len: u8, rtr: bool, brs: bool) -> Header { - let mut flags = 0u8; - flags.set_bit(Self::FLAG_RTR, rtr); - flags.set_bit(Self::FLAG_FDCAN, true); - flags.set_bit(Self::FLAG_BRS, brs); - Header { id, len, flags } + pub fn set_can_fd(mut self, flag: bool, brs: bool) -> Header { + self.flags.set_bit(Self::FLAG_FDCAN, flag); + self.flags.set_bit(Self::FLAG_BRS, brs); + self + } + + pub fn set_esi(mut self, flag: bool) -> Header { + self.flags.set_bit(Self::FLAG_ESI, flag); + self } /// Return ID @@ -73,6 +83,10 @@ impl Header { self.flags.get_bit(Self::FLAG_RTR) } + pub fn esi(&self) -> bool { + self.flags.get_bit(Self::FLAG_ESI) + } + /// Request/is FDCAN frame pub fn fdcan(&self) -> bool { self.flags.get_bit(Self::FLAG_FDCAN)