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 SAMD20J18A support #555

Draft
wants to merge 10 commits into
base: master
Choose a base branch
from
Draft

Conversation

kaizensparc
Copy link

Summary

This Pull Request integrates the SAMD20J18A microcontroller:

  • It adds the SVD file and its XSL patches in the svd directory
  • It generates the corresponding pac
  • It updates the HAL to have support for it
  • It adds the SAMD20 Xplained Pro board in the BSP with a bliny_basic example

This is only a proof of concept, not all features have been tested in real life even though everything compiles fine :)

Checklist

  • CHANGELOG.md for the BSP or HAL updated
  • All new or modified code is well documented, especially public items
  • No new warnings or clippy suggestions have been introduced (see CI or check locally)

If Adding a new Board

  • Board CI added to crates.json
  • Board is properly following "Tier 2" conventions, unless otherwise decided to be "Tier 1"

boards/samd20_xplained_pro/.cargo/config Outdated Show resolved Hide resolved
boards/samd20_xplained_pro/Cargo.toml Outdated Show resolved Hide resolved
optional = true

[dependencies.atsamd-hal]
path = "../../hal"
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we'll want this to be a tier two board, so no path dependency

Copy link
Author

Choose a reason for hiding this comment

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

If I remove the path dependency, it will use the published crate which does not contains the current updates, how should I replace it?
Btw why does including this path makes it a Tier 1 dependency?

Copy link
Contributor

Choose a reason for hiding this comment

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

ah, good point... I think we'll need to leave it as a path dependency for now since it's new.

Tier one BSPs use path dependencies on the HAL, which means for any HAL change affecting BSPs, all Tier one BSPs must be upgraded in the same PR for CI to pass. Tier two BSPs only rely on released versions of the HAL/PACs, so they can be locked to an older version and updated if someone is interested in doing so. This lowers the burden on maintainers as well as folks looking to change the HAL. See the README for more details.

Copy link
Author

Choose a reason for hiding this comment

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

Ok, I understand now. Maybe we can remove the board implementation from this PR, and I open a new PR with it after this one is merged? (If yes, I would probably keep it until the end of the PR for practical reasons and remove it at the end :))

Copy link
Contributor

Choose a reason for hiding this comment

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

FWIW, to facilitate development, one option is to use a patch in Cargo.toml . The idea would be to PR the changes that you want to eventually be published, but don't include the patch section which just refers to your local copy of atsamd-rs instead of the one on crates.io .

That said, it probably still makes sense to publish the PAC and HAL changes, before a separate PR to add the new boards.

name: pb31,
aliases: { AlternateB: Sercom5Pad1SpiSS }
},
);
Copy link
Contributor

Choose a reason for hiding this comment

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

It would be nice to have some of the other BSP helper functions, like SPI/I2C/UART.

hal/Cargo.toml Outdated Show resolved Hide resolved
@@ -180,23 +180,35 @@ impl GenericClockController {
}

// Feed 32khz into the DFLL48
state.enable_clock_generator(DFLL48, GCLK1);
#[cfg(not(feature = "samd20"))]
state.enable_clock_generator(ClockId::DFLL48, GCLK1);
Copy link
Contributor

Choose a reason for hiding this comment

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

why DFLL48 here but DFLL48M in state.set_gclk_divider_and_source`?

Copy link
Author

Choose a reason for hiding this comment

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

The svd file defines it that way. Maybe I can try adding a replacement rule in the xslt, I'll check that

Copy link
Author

Choose a reason for hiding this comment

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

I just made a replacement in the xslt so the naming is now identical :)

hal/src/thumbv6m/clock.rs Outdated Show resolved Hide resolved
hal/src/thumbv6m/sercom/v1/i2c.rs Outdated Show resolved Hide resolved
#[cfg(feature = "samd20")]
while sercom.spi().status.read().syncbusy().bit_is_set()
|| sercom.spi().ctrla.read().swrst().bit_is_set()
{}
Copy link
Contributor

Choose a reason for hiding this comment

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

same syncbusy peripheral comment

Copy link
Contributor

Choose a reason for hiding this comment

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

I think you missed SPI in your reg sync cleanup

Copy link
Author

Choose a reason for hiding this comment

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

Yes, sorry I just pushed my work of tonight and I will continue tomorrow, there are still a few things I need to clean :)

pac/atsamd20j/Cargo.toml Outdated Show resolved Hide resolved
@Sympatron
Copy link
Contributor

Sympatron commented Dec 24, 2021

I'm not that familiar with SAMD20. But I think it's mostly SAMD21 without USB. So it's probably useful to have a common feature for them (samd2x?) to simplify the #[cfg]s.

@kaizensparc
Copy link
Author

I'm not that familiar with SAMD20. But I think it's mostly SAMD21 without USB. So it's probably useful to have a common feature for them (samd2x?) to simplify the #[cfg]s.

Yes, good idea! I'll try to make a samd2x feature which is activated from any of the samd20/samd21 features. I never wrote anything with features yet so that's going to be something new to learn :)

Copy link
Contributor

@TDHolmes TDHolmes left a comment

Choose a reason for hiding this comment

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

Looking better! the samd2x feature helps a lot I think


[target.thumbv6m-none-eabi]
runner = 'arm-none-eabi-gdb -q -x openocd.gdb'
#runner = 'probe-run --chip ATSAMD21G18A'
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: even this is commented out, it's the wrong chip name

optional = true

[dependencies.atsamd-hal]
path = "../../hal"
Copy link
Contributor

Choose a reason for hiding this comment

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

ah, good point... I think we'll need to leave it as a path dependency for now since it's new.

Tier one BSPs use path dependencies on the HAL, which means for any HAL change affecting BSPs, all Tier one BSPs must be upgraded in the same PR for CI to pass. Tier two BSPs only rely on released versions of the HAL/PACs, so they can be locked to an older version and updated if someone is interested in doing so. This lowers the burden on maintainers as well as folks looking to change the HAL. See the README for more details.

boards/samd20_xplained_pro/SAMD20.yaml Show resolved Hide resolved
(PA28, Pa28),
(PA30, Pa30),
(PA31, Pa31),
#[cfg(any(feature = "min-samd21j", feature = "min-samd51j"))]
#[cfg(any(feature = "min-samd2x", feature = "min-samd51g"))]
Copy link
Contributor

Choose a reason for hiding this comment

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

Are you sure about all of these samd51j -> samd51g changes?

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think these changes are correct. I was pretty confident about these attributes when I wrote the module.

@@ -40,6 +40,14 @@ pub mod uart;
#[cfg(feature = "dma")]
pub mod dma;

/// Register synchronization flags
pub enum reg_sync {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think enums are usually camel case RegSync

Copy link
Contributor

Choose a reason for hiding this comment

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

I would like to look at this and verify it's the best approach to integrating with the rest of the sercom code.

#[cfg(feature = "samd20")]
return !self.spi().status.read().syncbusy().bit_is_set();
#[cfg(not(feature = "samd20"))]
!self.spi().syncbusy.read().enable().bit_is_set()
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: I'd prefer either both using return or both not just for consistency

@@ -19,6 +19,12 @@ const BUS_STATE_BUSY: u8 = 3;
const MASTER_ACT_READ: u8 = 2;
const MASTER_ACT_STOP: u8 = 3;

enum i2c_reg_sync {
Copy link
Contributor

Choose a reason for hiding this comment

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

same camel case comment here


/// Wait for the registers synchronization
#[inline]
fn is_register_synced(&mut self, synced_reg: i2c_reg_sync) -> bool {
Copy link
Contributor

Choose a reason for hiding this comment

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

not sure &mut self is needed for just a read

Copy link
Contributor

Choose a reason for hiding this comment

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

Same suggestion as for the SPI and UART modules: consider using the approach that doesn't require maintaining a bespoke enum.

#[cfg(feature = "samd20")]
while sercom.spi().status.read().syncbusy().bit_is_set()
|| sercom.spi().ctrla.read().swrst().bit_is_set()
{}
Copy link
Contributor

Choose a reason for hiding this comment

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

I think you missed SPI in your reg sync cleanup


// 16x oversample fractional
#[cfg(not(feature = "samd20"))]
w.sampr().bits(0x00);
Copy link
Contributor

Choose a reason for hiding this comment

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

does SAMD20 not have oversampling setting? If it does and it's just different, make sure to still set it

pad_alias!(Sercom2, Sercom3);
#[cfg(any(feature = "min-samd21g", feature = "min-samd51g"))]
#[cfg(any(feature = "min-samd2x", feature = "min-samd51g"))]
Copy link
Contributor

Choose a reason for hiding this comment

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

What's the difference between min-samd2x and samd2x? That seems redundant. min-samd21g is intended to exclude samd21e.

Comment on lines +74 to +87
/// Wait for the registers synchronization
#[inline]
fn is_register_synced(&mut self, synced_reg: reg_sync) -> bool {
#[cfg(feature = "samd20")]
return !self.spi().status.read().syncbusy().bit_is_set();
#[cfg(not(feature = "samd20"))]
match synced_reg {
reg_sync::CtrlB => !self.spi().syncbusy.read().ctrlb().bit_is_set(),
reg_sync::Length => !self.spi().syncbusy.read().length().bit_is_set(),
reg_sync::Enable => !self.spi().syncbusy.read().enable().bit_is_set(),
reg_sync::SwRst => !self.spi().syncbusy.read().swrst().bit_is_set(),
}
}

Copy link
Contributor

Choose a reason for hiding this comment

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

I would like to look through the SAMD20 datasheet myself before we move forward with this.

Copy link
Contributor

Choose a reason for hiding this comment

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

I would like to propose an alternate approach. Instead of declaring a function that takes an enum, we could do something like:

#[cfg(not(feature = "samd20"))]
#[inline]
fn sync_ctrlb(&self) {
    while self.spi().syncbusy.read().ctrlb().bit_is_set() {}
}

#[cfg(feature = "samd20")]
#[inline]
fn sync_ctrlb(&self) {
    while self.spi().status.read().syncbusy().bit_is_set() {}
}

This has the advantage of not having any overhead of passing enums around, and reduces chances of making mistakes. At any rate, a shared reference to self is sufficient.

Also, SYNCBUSY.LENGTH seems to be exclusive to SAMx5x targets.

Comment on lines +92 to +96
#[cfg(feature = "samd20")]
w.txpo().bit(txpo != 0);
#[cfg(not(feature = "samd20"))]
w.txpo().bits(txpo);
w.rxpo().bits(rxpo)
Copy link
Contributor

Choose a reason for hiding this comment

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

We should review the differences in SERCOM carefully. @jbeaurivage, have you looked at this yet?

Copy link
Contributor

Choose a reason for hiding this comment

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

I haven't yet, but I'd definitely like to comb through this finely with the datasheet next to me. Annoyingly enough, it seems the SAMD20 isn't exactly 'just a SAMD21 minus the USB'.

Copy link
Contributor

Choose a reason for hiding this comment

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

@didjcodt, can you please explain what's the reasoning here? This kind of thing should probably be handled at the impl_rxpotxpo level rather than inside the functions.

#[cfg(any(feature = "samd11", feature = "samd21"))]
#[cfg(any(feature = "samd11", feature = "samd2x"))]
Copy link
Contributor

Choose a reason for hiding this comment

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

Please revert this change

Comment on lines -6 to +11
//! SAMD11/SAMD21, the CHID register must be written with the correct channel ID
//! before accessing the channel specific registers. There is a provided
//! `with_chid` method that takes a closure with the register read/write proxies
//! to ensure any read/write to these registers are done in an interrupt-safe
//! way. For SAMD51+, `with_chid` returns the register block which contains the
//! registers owned by a specific channel.
//! SAMD11/SAMD20/SAMD21, the CHID register must be written with the correct
//! channel ID before accessing the channel specific registers. There is a
//! provided `with_chid` method that takes a closure with the register
//! read/write proxies to ensure any read/write to these registers are done in
//! an interrupt-safe way. For SAMD51+, `with_chid` returns the register block
//! which contains the registers owned by a specific channel.
Copy link
Contributor

Choose a reason for hiding this comment

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

Please remove SAMD20 from this comment

#[cfg(any(feature = "samd11", feature = "samd21"))]
#[cfg(not(feature = "min-samd51g"))]
Copy link
Contributor

Choose a reason for hiding this comment

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

Please revert this change

#[cfg(any(feature = "samd11", feature = "samd21"))]
#[cfg(not(feature = "min-samd51g"))]
Copy link
Contributor

Choose a reason for hiding this comment

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

Please revert this change

not(any(feature = "samd11", feature = "samd21")),
feature = "min-samd51g",
Copy link
Contributor

Choose a reason for hiding this comment

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

I would remove all changes to the dmac module, because the SAMD20 chips do not have a DMAC peripheral on board. Could you please feature-gate the dmac module to exclude SAMD20?

#[cfg(feature = "samd21")]
use crate::pac::{TC3, TC4, TC5, TCC1, TCC2};
#[cfg(feature = "samd21j")]
#[cfg(feature = "samd2x")]
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this mean that SAMD20s don't have TC1 and TC2?


/// Wait for the registers synchronization
#[inline]
fn is_register_synced(&mut self, synced_reg: i2c_reg_sync) -> bool {
Copy link
Contributor

Choose a reason for hiding this comment

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

Same suggestion as for the SPI and UART modules: consider using the approach that doesn't require maintaining a bespoke enum.

Comment on lines +160 to +164
#[cfg(feature = "samd20")]
self.i2cm()
.addr
.write(|w| w.addr().bits(addr));
#[cfg(not(feature = "samd20"))]
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this different?

Comment on lines +187 to 195

#[cfg(feature = "samd20")]
if status.lowtout().bit_is_set() {
return Err(I2CError::Timeout);
}
#[cfg(not(feature = "samd20"))]
if status.lowtout().bit_is_set() || status.sexttout().bit_is_set()
|| status.mexttout().bit_is_set()
{
Copy link
Contributor

Choose a reason for hiding this comment

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

You might want to break this up into separate SAMD20/non-SAMD20 functions to avoid so many #[cfg]s, or at least into config blocks.

Comment on lines -382 to +427
#[cfg(feature = "min-samd21g")]
#[cfg(feature = "min-samd2x")]
Copy link
Contributor

Choose a reason for hiding this comment

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

min-samd21g -> min-samd2x

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.

7 participants