aboutsummaryrefslogtreecommitdiff
path: root/embassy-stm32
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2024-05-28 11:12:37 +0000
committerGitHub <[email protected]>2024-05-28 11:12:37 +0000
commit000b022ae2e52e9abaabbd10110b4c583fe4344c (patch)
tree9986e9e7ec1f2feba794de4361c7b52b77729ef4 /embassy-stm32
parent34bc439f174ed95cf2e733c7276e314361fb50da (diff)
parent807e573994d046d0cd00e631db111fafd2627559 (diff)
Merge pull request #2988 from de-vri-es/bxcan-tx-fifo-scheduling
embassy_stm32: implement optional FIFO scheduling for outgoing frames
Diffstat (limited to 'embassy-stm32')
-rw-r--r--embassy-stm32/src/can/bxcan/mod.rs45
-rw-r--r--embassy-stm32/src/can/bxcan/registers.rs75
2 files changed, 98 insertions, 22 deletions
diff --git a/embassy-stm32/src/can/bxcan/mod.rs b/embassy-stm32/src/can/bxcan/mod.rs
index 912634b84..0ac4cdab6 100644
--- a/embassy-stm32/src/can/bxcan/mod.rs
+++ b/embassy-stm32/src/can/bxcan/mod.rs
@@ -133,7 +133,7 @@ impl<T: Instance> CanConfig<'_, T> {
133 self 133 self
134 } 134 }
135 135
136 /// Enables or disables automatic retransmission of messages. 136 /// Enables or disables automatic retransmission of frames.
137 /// 137 ///
138 /// If this is enabled, the CAN peripheral will automatically try to retransmit each frame 138 /// If this is enabled, the CAN peripheral will automatically try to retransmit each frame
139 /// until it can be sent. Otherwise, it will try only once to send each frame. 139 /// until it can be sent. Otherwise, it will try only once to send each frame.
@@ -298,6 +298,23 @@ impl<'d, T: Instance> Can<'d, T> {
298 T::regs().ier().modify(|i| i.set_slkie(false)); 298 T::regs().ier().modify(|i| i.set_slkie(false));
299 } 299 }
300 300
301 /// Enable FIFO scheduling of outgoing frames.
302 ///
303 /// If this is enabled, frames will be transmitted in the order that they are passed to
304 /// [`write()`][Self::write] or [`try_write()`][Self::try_write()].
305 ///
306 /// If this is disabled, frames are transmitted in order of priority.
307 ///
308 /// FIFO scheduling is disabled by default.
309 pub fn set_tx_fifo_scheduling(&mut self, enabled: bool) {
310 Registers(T::regs()).set_tx_fifo_scheduling(enabled)
311 }
312
313 /// Checks if FIFO scheduling of outgoing frames is enabled.
314 pub fn tx_fifo_scheduling_enabled(&self) -> bool {
315 Registers(T::regs()).tx_fifo_scheduling_enabled()
316 }
317
301 /// Queues the message to be sent. 318 /// Queues the message to be sent.
302 /// 319 ///
303 /// If the TX queue is full, this will wait until there is space, therefore exerting backpressure. 320 /// If the TX queue is full, this will wait until there is space, therefore exerting backpressure.
@@ -307,7 +324,13 @@ impl<'d, T: Instance> Can<'d, T> {
307 324
308 /// Attempts to transmit a frame without blocking. 325 /// Attempts to transmit a frame without blocking.
309 /// 326 ///
310 /// Returns [Err(TryWriteError::Full)] if all transmit mailboxes are full. 327 /// Returns [Err(TryWriteError::Full)] if the frame can not be queued for transmission now.
328 ///
329 /// If FIFO scheduling is enabled, any empty mailbox will be used.
330 ///
331 /// Otherwise, the frame will only be accepted if there is no frame with the same priority already queued.
332 /// This is done to work around a hardware limitation that could lead to out-of-order delivery
333 /// of frames with the same priority.
311 pub fn try_write(&mut self, frame: &Frame) -> Result<TransmitStatus, TryWriteError> { 334 pub fn try_write(&mut self, frame: &Frame) -> Result<TransmitStatus, TryWriteError> {
312 self.split().0.try_write(frame) 335 self.split().0.try_write(frame)
313 } 336 }
@@ -318,6 +341,11 @@ impl<'d, T: Instance> Can<'d, T> {
318 } 341 }
319 342
320 /// Waits until any of the transmit mailboxes become empty 343 /// Waits until any of the transmit mailboxes become empty
344 ///
345 /// Note that [`Self::try_write()`] may fail with [`TryWriteError::Full`],
346 /// even after the future returned by this function completes.
347 /// This will happen if FIFO scheduling of outgoing frames is not enabled,
348 /// and a frame with equal priority is already queued for transmission.
321 pub async fn flush_any(&self) { 349 pub async fn flush_any(&self) {
322 CanTx::<T>::flush_any_inner().await 350 CanTx::<T>::flush_any_inner().await
323 } 351 }
@@ -465,7 +493,13 @@ impl<'d, T: Instance> CanTx<'d, T> {
465 493
466 /// Attempts to transmit a frame without blocking. 494 /// Attempts to transmit a frame without blocking.
467 /// 495 ///
468 /// Returns [Err(TryWriteError::Full)] if all transmit mailboxes are full. 496 /// Returns [Err(TryWriteError::Full)] if the frame can not be queued for transmission now.
497 ///
498 /// If FIFO scheduling is enabled, any empty mailbox will be used.
499 ///
500 /// Otherwise, the frame will only be accepted if there is no frame with the same priority already queued.
501 /// This is done to work around a hardware limitation that could lead to out-of-order delivery
502 /// of frames with the same priority.
469 pub fn try_write(&mut self, frame: &Frame) -> Result<TransmitStatus, TryWriteError> { 503 pub fn try_write(&mut self, frame: &Frame) -> Result<TransmitStatus, TryWriteError> {
470 Registers(T::regs()).transmit(frame).map_err(|_| TryWriteError::Full) 504 Registers(T::regs()).transmit(frame).map_err(|_| TryWriteError::Full)
471 } 505 }
@@ -505,6 +539,11 @@ impl<'d, T: Instance> CanTx<'d, T> {
505 } 539 }
506 540
507 /// Waits until any of the transmit mailboxes become empty 541 /// Waits until any of the transmit mailboxes become empty
542 ///
543 /// Note that [`Self::try_write()`] may fail with [`TryWriteError::Full`],
544 /// even after the future returned by this function completes.
545 /// This will happen if FIFO scheduling of outgoing frames is not enabled,
546 /// and a frame with equal priority is already queued for transmission.
508 pub async fn flush_any(&self) { 547 pub async fn flush_any(&self) {
509 Self::flush_any_inner().await 548 Self::flush_any_inner().await
510 } 549 }
diff --git a/embassy-stm32/src/can/bxcan/registers.rs b/embassy-stm32/src/can/bxcan/registers.rs
index 446f3ad6f..ad27e0744 100644
--- a/embassy-stm32/src/can/bxcan/registers.rs
+++ b/embassy-stm32/src/can/bxcan/registers.rs
@@ -181,46 +181,85 @@ impl Registers {
181 None 181 None
182 } 182 }
183 183
184 /// Enables or disables FIFO scheduling of outgoing mailboxes.
185 ///
186 /// If this is enabled, mailboxes are scheduled based on the time when the transmit request bit of the mailbox was set.
187 ///
188 /// If this is disabled, mailboxes are scheduled based on the priority of the frame in the mailbox.
189 pub fn set_tx_fifo_scheduling(&mut self, enabled: bool) {
190 self.0.mcr().modify(|w| w.set_txfp(enabled))
191 }
192
193 /// Checks if FIFO scheduling of outgoing mailboxes is enabled.
194 pub fn tx_fifo_scheduling_enabled(&self) -> bool {
195 self.0.mcr().read().txfp()
196 }
197
184 /// Puts a CAN frame in a transmit mailbox for transmission on the bus. 198 /// Puts a CAN frame in a transmit mailbox for transmission on the bus.
185 /// 199 ///
186 /// Frames are transmitted to the bus based on their priority (see [`FramePriority`]). 200 /// The behavior of this function depends on wheter or not FIFO scheduling is enabled.
187 /// Transmit order is preserved for frames with identical priority. 201 /// See [`Self::set_tx_fifo_scheduling()`] and [`Self::tx_fifo_scheduling_enabled()`].
202 ///
203 /// # Priority based scheduling
204 ///
205 /// If FIFO scheduling is disabled, frames are transmitted to the bus based on their
206 /// priority (see [`FramePriority`]). Transmit order is preserved for frames with identical
207 /// priority.
188 /// 208 ///
189 /// If all transmit mailboxes are full, and `frame` has a higher priority than the 209 /// If all transmit mailboxes are full, and `frame` has a higher priority than the
190 /// lowest-priority message in the transmit mailboxes, transmission of the enqueued frame is 210 /// lowest-priority message in the transmit mailboxes, transmission of the enqueued frame is
191 /// cancelled and `frame` is enqueued instead. The frame that was replaced is returned as 211 /// cancelled and `frame` is enqueued instead. The frame that was replaced is returned as
192 /// [`TransmitStatus::dequeued_frame`]. 212 /// [`TransmitStatus::dequeued_frame`].
213 ///
214 /// # FIFO scheduling
215 ///
216 /// If FIFO scheduling is enabled, frames are transmitted in the order that they are passed to this function.
217 ///
218 /// If all transmit mailboxes are full, this function returns [`nb::Error::WouldBlock`].
193 pub fn transmit(&mut self, frame: &Frame) -> nb::Result<TransmitStatus, Infallible> { 219 pub fn transmit(&mut self, frame: &Frame) -> nb::Result<TransmitStatus, Infallible> {
220 // Check if FIFO scheduling is enabled.
221 let fifo_scheduling = self.0.mcr().read().txfp();
222
194 // Get the index of the next free mailbox or the one with the lowest priority. 223 // Get the index of the next free mailbox or the one with the lowest priority.
195 let tsr = self.0.tsr().read(); 224 let tsr = self.0.tsr().read();
196 let idx = tsr.code() as usize; 225 let idx = tsr.code() as usize;
197 226
198 let frame_is_pending = !tsr.tme(0) || !tsr.tme(1) || !tsr.tme(2); 227 let frame_is_pending = !tsr.tme(0) || !tsr.tme(1) || !tsr.tme(2);
199 let pending_frame = if frame_is_pending { 228 let all_frames_are_pending = !tsr.tme(0) && !tsr.tme(1) && !tsr.tme(2);
200 // High priority frames are transmitted first by the mailbox system. 229
201 // Frames with identical identifier shall be transmitted in FIFO order. 230 let pending_frame;
202 // The controller schedules pending frames of same priority based on the 231 if fifo_scheduling && all_frames_are_pending {
203 // mailbox index instead. As a workaround check all pending mailboxes 232 // FIFO scheduling is enabled and all mailboxes are full.
204 // and only accept higher priority frames. 233 // We will not drop a lower priority frame, we just report WouldBlock.
234 return Err(nb::Error::WouldBlock);
235 } else if !fifo_scheduling && frame_is_pending {
236 // Priority scheduling is enabled and alteast one mailbox is full.
237 //
238 // In this mode, the peripheral transmits high priority frames first.
239 // Frames with identical priority should be transmitted in FIFO order,
240 // but the controller schedules pending frames of same priority based on the
241 // mailbox index. As a workaround check all pending mailboxes and only accept
242 // frames with a different priority.
205 self.check_priority(0, frame.id().into())?; 243 self.check_priority(0, frame.id().into())?;
206 self.check_priority(1, frame.id().into())?; 244 self.check_priority(1, frame.id().into())?;
207 self.check_priority(2, frame.id().into())?; 245 self.check_priority(2, frame.id().into())?;
208 246
209 let all_frames_are_pending = !tsr.tme(0) && !tsr.tme(1) && !tsr.tme(2);
210 if all_frames_are_pending { 247 if all_frames_are_pending {
211 // No free mailbox is available. This can only happen when three frames with 248 // No free mailbox is available. This can only happen when three frames with
212 // ascending priority (descending IDs) were requested for transmission and all 249 // ascending priority (descending IDs) were requested for transmission and all
213 // of them are blocked by bus traffic with even higher priority. 250 // of them are blocked by bus traffic with even higher priority.
214 // To prevent a priority inversion abort and replace the lowest priority frame. 251 // To prevent a priority inversion abort and replace the lowest priority frame.
215 self.read_pending_mailbox(idx) 252 pending_frame = self.read_pending_mailbox(idx);
216 } else { 253 } else {
217 // There was a free mailbox. 254 // There was a free mailbox.
218 None 255 pending_frame = None;
219 } 256 }
220 } else { 257 } else {
221 // All mailboxes are available: Send frame without performing any checks. 258 // Either we have FIFO scheduling and at-least one free mailbox,
222 None 259 // or we have priority scheduling and all mailboxes are free.
223 }; 260 // No further checks are needed.
261 pending_frame = None
262 }
224 263
225 self.write_mailbox(idx, frame); 264 self.write_mailbox(idx, frame);
226 265
@@ -237,18 +276,16 @@ impl Registers {
237 } 276 }
238 277
239 /// Returns `Ok` when the mailbox is free or if it contains pending frame with a 278 /// Returns `Ok` when the mailbox is free or if it contains pending frame with a
240 /// lower priority (higher ID) than the identifier `id`. 279 /// different priority from the identifier `id`.
241 fn check_priority(&self, idx: usize, id: IdReg) -> nb::Result<(), Infallible> { 280 fn check_priority(&self, idx: usize, id: IdReg) -> nb::Result<(), Infallible> {
242 // Read the pending frame's id to check its priority. 281 // Read the pending frame's id to check its priority.
243 assert!(idx < 3); 282 assert!(idx < 3);
244 let tir = &self.0.tx(idx).tir().read(); 283 let tir = &self.0.tx(idx).tir().read();
245 //let tir = &can.tx[idx].tir.read();
246 284
247 // Check the priority by comparing the identifiers. But first make sure the 285 // Check the priority by comparing the identifiers. But first make sure the
248 // frame has not finished the transmission (`TXRQ` == 0) in the meantime. 286 // frame has not finished the transmission (`TXRQ` == 0) in the meantime.
249 if tir.txrq() && id <= IdReg::from_register(tir.0) { 287 if tir.txrq() && id == IdReg::from_register(tir.0) {
250 // There's a mailbox whose priority is higher or equal 288 // There's a mailbox whose priority is equal to the priority of the new frame.
251 // the priority of the new frame.
252 return Err(nb::Error::WouldBlock); 289 return Err(nb::Error::WouldBlock);
253 } 290 }
254 291