Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Support for FlexCAN(1/2) #171

Draft
wants to merge 52 commits into
base: v0.5
Choose a base branch
from
Draft

Conversation

sigil-03
Copy link

This PR adds support for the FlexCAN1 and FlexCAN2 interfaces to imxrt-hal 0.5.

FlexCAN1 interface has been tested in HW using a Teensy4.1 - I'll have a corresponding PR in teensy4-bsp with an RTIC example which was used to test.

More than happy to take any code comments / improvement suggestions!

Acknowledgements:
The vast majority of this work is from @dstric-aqueduct - who wrote the initial FlexCAN peripheral code in #122.

@SebKuzminsky rebased @dstric-aqueduct's work onto imxrt-hal 0.5 which gave me a great starting point to work from.

dstric-aqueduct and others added 28 commits July 30, 2024 14:56
Implementation of the FlexCAN1 and FlexCAN2 peripherals for the  NXP i.MX RT 1060 processor.

Includes APIs for initializing the peripherals, setting the clock source, setting mailbox receive filters, setting mailbox FIFO functionality, and receiving and transmitting frames.

The FlexCAN3 peripheral is not implemented.
- Remove StandardId and ExtendedId structs in favor of embedded-hal
- convert TryFrom to From for FlexCanMailboxCSCode
- added bounds checks on StandardID and ExtendedID for creation of IdReg

- improved Error descriptiveness

- added docs to Frame timestamp field

- Update Cargo.toml
- fixed clock configuration in builder method and removed set_ccm_ccg method

- removed unused `instance` method

- removed duplicated `set_fifo_filter` method in favor of single method that accepts a `FlexCanFilter` as arg

- removed dead code
Implementation of the FlexCAN1 and FlexCAN2 peripherals for the  NXP i.MX RT 1060 processor.

Includes APIs for initializing the peripherals, setting the clock source, setting mailbox receive filters, setting mailbox FIFO functionality, and receiving and transmitting frames.

The FlexCAN3 peripheral is not implemented.
We need properly-formmated code to pass CI.
It can't automatically fix all the issues, but the remaining fixes are
straightforward.
Use the fact that a bool is either a zero or a one to simplify the
filter value. Warned by clippy, and implemented by hand.
NOTE:
There was a bug with read_mailbox() where it would return all 8 bytes of
the frame, even if only a few of them were valid. If these bytes crossed
a word boundary (4 byte) - garbage data would be loaded from the mailbox
into the data frame.

For example, the can frame: 01#01 would cause the following output:
`[0x1, 0x0, 0x0, 0x0, ?, ?, ?, ?]`
Where `?` indicates an unknown value.
Copy link
Member

@mciantyre mciantyre left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More than happy to take any code comments / improvement suggestions!

Our CI workflows will provide you with the fastest feedback. I recommend enabling our GitHub actions on your fork. That way, you can run our automated checks yourself whenever you push to your fork.

One notable issue: builds for the 1011 and 1015 MCUs are failing. These MCUs do not have a CAN peripheral, so the RAL doesn't export any CAN registers. Since CAN isn't common to all MCUs, we won't be able to place the CAN driver under the common module tree. If we export the CAN driver through a chip configuration module (like imxrt1020, imxrt1060, and imxrt1170), then those build errors should go away.

If you have any questions as you work through CI issues, leave them here and I'll try to help. I'll provide a detailed review once CI is passing. Until then, here's some high-level questions based on the prior PR:

  • Does this driver solve the activating interrupt problem noted here?
  • How does this driver account for MCU errata?
  • Observers seem to want CAN FD support. Is there a path forward for adding that capability?

@sigil-03
Copy link
Author

sigil-03 commented Aug 19, 2024

Thanks for your comments - I'll start by addressing the failing pipelines, and then I'll have a look at answering your questions about the interrupts, MCU errata, and CANFD support.

@sigil-03
Copy link
Author

sigil-03 commented Aug 20, 2024

I've gone through and moved the CAN implementation out of common and created a Drivers sub-module inside of the Config module, which exposes available drivers (in this case just CAN).

I'm not sure this is the right approach, but it's working. Definitely open to feedback here!

I've also updated the docs + docs example code - so all of the pipeline tests should pass.

Currently I only expose the CAN interface on the 1060 since the only device I have for testing is a Teensy 4.1

@sigil-03 sigil-03 marked this pull request as draft August 22, 2024 03:20
Copy link
Member

@mciantyre mciantyre left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sigil-03#2 has additional thoughts.

Comment on lines +32 to +33
/// Disable flexcan clock
Disable = 3,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note for other / future reviewers: This variant is not documented in the 1020 reference manual. It's also not a constant known to the 1020 SVD. Nevertheless, I say we keep this as-is; we cover the full bit width of the field.


/// Decodes value of the ID Extended (IDE) and Remote Transmission Request (RTR) bits
///
/// TODO: Break this up into seperate RTR and IDE structs
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the plan to address these TODOs? I see the options as

  1. Now, during this review.
  2. After we merge, but before we release.
  3. After we release, in a breaking change.

Question and options applies to other TODOs listed in documentation. I'd prefer option 1, or option 2 if it can happen soon after we merge.

(In this instance, I haven't thought if separation is appropriate. I'll change my consideration if the answer is option 1.)

const RTR_MASK: u32 = 0b1_u32 << Self::RTR_SHIFT;

const DLC_SHIFT: u32 = 16;
const DLC_MASK: u32 = 0b111_u32 << Self::DLC_SHIFT;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The DLC mask should be four bits wide. I'm checking against both the 1020 and 1060 RMs. That high bit will be important when considering an FD driver implementation.

When fixing, please include a frame/tests.rs unit test that evaluates four bit DLCs.

Suggested change
const DLC_MASK: u32 = 0b111_u32 << Self::DLC_SHIFT;
const DLC_MASK: u32 = 0b1111_u32 << Self::DLC_SHIFT;

reg: ral::can::Instance<M>,
_pins: PhantomData<P>,
_module: PhantomData<ral::can::RegisterBlock>,
clock_frequency: u32,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're not using clock_frequency to do anything meaningful. We only make it available to the user with the implicit caveat "it might have changed." How about we remove it? Or, is there a reason to keep it I'm not considering?

Suggested change
clock_frequency: u32,

Comment on lines +470 to +478
/// Wrapper around [`Self::set_fifo(true)`](Self::set_fifo())
pub fn enable_fifo(&mut self) {
self.set_fifo(true);
}

/// Wrapper around [`Self::set_fifo(false)`](Self::set_fifo())
pub fn disable_fifo(&mut self) {
self.set_fifo(false);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

set_fifo seems to be a great way to control the FIFO. If you prefer to keep these helpers, these methods could be good candidates for inline.

Suggested change
/// Wrapper around [`Self::set_fifo(true)`](Self::set_fifo())
pub fn enable_fifo(&mut self) {
self.set_fifo(true);
}
/// Wrapper around [`Self::set_fifo(false)`](Self::set_fifo())
pub fn disable_fifo(&mut self) {
self.set_fifo(false);
}

Comment on lines +480 to +490
#[allow(rustdoc::private_intra_doc_links)]
/// Wrapper around [`Self::set_fifo_filter_mask()`] which sets the filter mask to [`filter::FlexCanFlten::RejectAll`]
pub fn set_fifo_reject_all(&mut self) {
self.set_fifo_filter_mask(filter::FlexCanFlten::RejectAll)
}

#[allow(rustdoc::private_intra_doc_links)]
/// Wrapper around [`Self::set_fifo_filter_mask()`] which sets the filter mask to [`filter::FlexCanFlten::AcceptAll`]
pub fn set_fifo_accept_all(&mut self) {
self.set_fifo_filter_mask(filter::FlexCanFlten::AcceptAll)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The #[allow(rustdoc::private_intra_doc_links)] means that we render a documentation link to a private method. When a user clicks that link in their browser, it goes nowhere. I don't think that's a great experience for the reader.

Let's document why the user might want to call these methods, instead of documenting how the method does what it does.

Suggested change
#[allow(rustdoc::private_intra_doc_links)]
/// Wrapper around [`Self::set_fifo_filter_mask()`] which sets the filter mask to [`filter::FlexCanFlten::RejectAll`]
pub fn set_fifo_reject_all(&mut self) {
self.set_fifo_filter_mask(filter::FlexCanFlten::RejectAll)
}
#[allow(rustdoc::private_intra_doc_links)]
/// Wrapper around [`Self::set_fifo_filter_mask()`] which sets the filter mask to [`filter::FlexCanFlten::AcceptAll`]
pub fn set_fifo_accept_all(&mut self) {
self.set_fifo_filter_mask(filter::FlexCanFlten::AcceptAll)
}
/// The FIFO will reject CAN frames that do not exactly match its filter.
///
/// This call is ignored if the FIFO is disabled. The call only takes effect when the FIFO is enabled.
pub fn set_fifo_reject_all(&mut self) {
self.set_fifo_filter_mask(filter::FlexCanFlten::RejectAll)
}
/// The FIFO will accept any CAN frame, no matter the filter.
///
/// This call is ignored if the FIFO is disabled. The call only takes effect when the FIFO is enabled.
pub fn set_fifo_accept_all(&mut self) {
self.set_fifo_filter_mask(filter::FlexCanFlten::AcceptAll)
}

Comment on lines +82 to +85
can::can::<1>(),
can::can::<2>(),
can::can_pe::<1>(),
can::can_pe::<2>(),
Copy link
Member

@mciantyre mciantyre Aug 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like only the CAN PE clock gates are downstream of the FlexCAN clock root. I'm gleaning this from table 14-4 of the 1060 RM (rev 3). The CAN clock gates are downstream of the IPG clock.

Suggested change
can::can::<1>(),
can::can::<2>(),
can::can_pe::<1>(),
can::can_pe::<2>(),
can::can_pe::<1>(),
can::can_pe::<2>(),

sigil-03 and others added 4 commits October 16, 2024 15:05
Fix a couple CAN frame decoding issues:
1. Extended vs. Standard ID (these were reversed)
2. Proper payload length by implementing `From<&[u8]> for Data`
Fix issue where DLC was decoded as 3 bits instead of 4

Add new functionality to parse a frame from a &[u8] slice
Add DLC fix and frame parsing from slice
@SebKuzminsky
Copy link

🥳

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Controller Area Network (CAN)
4 participants