GithubHelp home page GithubHelp logo

imxrt-hal's Introduction

imxrt-hal

A Rust hardware abstraction layer (HAL) for NXP i.MX RT processors.

All Checks Crates.io

matrix-chat

Getting started

You'll need a compatible Rust toolchain in order to build and use imxrt-hal. For information on installing a toolchain, see the imxrt-rs book.

If you're new to imxrt-hal and want to try its examples on hardware, see this guide. Examples run on various i.MX RT development boards, like the 1010 EVK and the Teensy 4.

Many HAL drivers work across all of the i.MX RT chips supported by imxrt-ral. Select drivers become available for a specific chip build. If you want to use imxrt-hal in your project, see the package's API docs. For a high-level overview of how imxrt-ral and imxrt-hal work together, check out the ecosystem walkthrough

Contributing

If you're interested in extending or changing the HAL, or if you want to add testing support for a new board, see CONTRIBUTING.

License

Licensed under either of

at your option.

imxrt-hal's People

Contributors

djmdjm avatar dstric-aqueduct avatar finomnis avatar florob avatar mciantyre avatar serenalynas avatar spinfast avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

imxrt-hal's Issues

iMXRT1064 support

We should publish the imxrt-hal with support for the IMXRT1064 device if it is ready.

Continuing discussion from imxrt-rs imxrt-boot-gen#7.

I'm curious as to how to interface that on-board flash. I can't easily find any NXP docs that say "on-board flash starts at this address." (Since the 1064 RM notes that all FlexSPI2 pin muxing registers are "reserved," I'm guessing that on-board flash is available via FlexSPI2.)

The on-board iMXRT1064 flash is at 0x70000000 upto 0x703FFFFF. This document describes the required changes when moving from an iMXRT1060 series device.

The onboard flash does use the FlexSPI2. The onboard flash is actually an embedded Winbond W25Q32JV

Cannot build 0.2.0 crates from crates.io

I'm hitting build errors when referencing the 0.2.0 releases from crates.io. It affects the RAL, and maybe the HAL.

Minimal reproduction of the RAL:

cd /tmp
cargo new --lib test-ral
cd test-ral
echo 'imxrt-ral = { "version" = "=0.2.0", features = ["imxrt1062"] }' >> Cargo.toml
cargo build

...

error: couldn't read /Users/mciantyre/.cargo/registry/src/github.com-1ecc6299db9ec823/imxrt-ral-0.2.0/src: Is a directory (os error 21)

It looks like 742e687 explicitly sets the RAL library's path to src. Does that need to be the path to the library entrypoint, src/lib.rs? Is there a reason we're explicitly setting the library path?

Additionally, the HAL references a local directory containing a 0.2.0 release of the RAL. Should the path be there? I'm actually not sure if that will be troublesome for end users.

RAL doc string fixes and doc test enablement

Currently CI has doc testing disabled for RAL because the doc strings contain things like stm32ral::gpio type use statements, as well as a number of stm32ral specific types, modules, and such.

It'd be really nice to fix this so we can having tested rust code in our doc strings for RAL, though not a super high priority as most of the people working on the HAL I believe implicitly understand the nature of things at this point.

When fixed we can re-enable cargo test in CI for doc strings.

RegisterBlocks are not #[repr(C)]

Caveat: I've still not run anything on hardware ๐Ÿ˜›

I'm studying the RAL API, and I noticed that none of the peripheral RegisterBlocks are #[repr(C)]. Here's a snippet of the CCM peripheral:

// Expected #[repr(C)] here
pub struct RegisterBlock {
    /// CCM Control Register
    pub CCR: RWRegister<u32>,

    _reserved1: [u32; 1],

    /// CCM Status Register
    pub CSR: RORegister<u32>,

    /// CCM Clock Switcher Register
    pub CCSR: RWRegister<u32>,

    /// CCM Arm Clock Root Register
    pub CACRR: RWRegister<u32>,

    // Rest elided...
}

The block registers are written in order, according to the SVD and the processor reference manual. Its address is 0x400F_C000.

Given the RWRegister API, a hypothetical write to the CCSR register could resemble

ccm.CCSR.write(0xDEAD_BEEF)

The write will cast the the memory at 0x400F_C000 to a RegisterBlock, offset the pointer it by the number of registers between 0x400F_C000 and CCSR (expected to be the absolute address 0x400F_C00C), then store 0xDEAD_BEEF at that address.

Because the RegisterBlock is not #[repr(C)], we cannot guarantee it's layout, since Rust does not guarantee struct layout. Therefore, the store of 0xDEAD_BEEF might not necessarily happen on the actual CCSR register, address 0x400F_C00C; it could happen on another memory address between 0x400F_C000 and 0x400F_C000 + core::mem::size_of::<RegisterBlock>()

I think this is a bug, or could lead to a bug. It applies to all RegisterBlocks in imxrt-ral. It may also apply to the stm32ral crate, from which we've derived the RAL generation script. A study of the stm32ral crate also reveals no usage of #[repr(C)]. A fix could be to specify #[repr(C)] in the RAL generation script, then re-generate the RAL crate.

What do we think? Let me know if I'm missing something that makes this a non-issue!

Configure/Release

Configuring a peripheral should be undoable in some way to return it back to its default state.

The C SDK does this using _init/_deinit pairs for peripherals, we can probably do this using type states and similar calls, instead perhaps using configure/release as the call pair.

Support iMXRT1062 TRNG

The iMXRT1062 (and other devices?) has a True Random Number Generator, which would be useful to support. Unfortunately almost all of its documentation is contained in the Security Reference Manual, which requires a licensing agreement with NXP. Based on the site's assumption that I have a designated salesperson, I suspect I wouldn't be approved to read the SRM (and I don't really want to sign an NDA).

However, the MCUXpresso SDK contains example code for the TRNG. Reading that was enough to get it working: https://github.com/AlyoshaVasilieva/imxrt1062-trng/blob/master/src/lib.rs

This would probably fit better as part of the HAL rather than a separate driver library, but even if I wasn't currently sick I'm not sure I'd be able to design it properly, so hopefully someone here finds it useful. It depends on rand_core so I don't have to worry about messing up the block generation.

Speed: about 15 minutes to generate 1MB of random data, at least in my test. Probably best to use it to seed a (CS)PRNG.


Update: Revising the driver, should have something within a week or so.

Could you add some examples

I'm an engineer in NXP MCU system engineering team, I'm now learning Rust (in early stage) and I'd like to have a try and then later contribute.
I'm familiar with the i.MX RT devices and using Segger J-Link GDB server to debug it with GDB.
Could you just add some examples to let traditonal MCU users like me get started?
You can also contact me with [email protected]

Support SPI DMA transfers

From @dflemstr:

I love the recent development and buzz around SPI support. In my application, I want to transfer a lot of data, and it would hence be useful to support DMA transfers for SPI!

I have another side project where I've been trying to eek out the maximum amount of performance from SPI in a device independent manner. I defined the API to look like this: https://github.com/dflemstr/embedded-platform/blob/master/src/spi.rs

Of course, that introduces async as well, while this crate/embedded_hal is very focused on blocking APIs. However the main take-away from that API I think is:

You initiate a transfer which takes ownership of the slice of memory you want to send
You can poll that transfer to completion (and do that in a tight loop if you want a "blocking" API)

Manually migrating this from mciantyre/teensy4-rs#51

Default pin mux option wrong for evk

The teensy appears to do some work in its bootloader to setup a few things, and avoid the setup work normally needed on imxrt chips.

In order to get my led blinking (finally did!)

I needed to change the default alt of the pads to alt0 as that is in fact the default. Teensy must be setting these up to Alt5 by default. This needs to be reconciled.

I also needed to disable a few watchdogs otherwise the chip would reset very quickly leaving the debugger and myself very confused.

Lastly the runtime for the teensy makes some assumptions that I don't think are true for the evk boards and makes debugging difficult. The default setup for mcuxpresso is to load everything in ram and run it. This is what the imxrt-rt crate I've added to this repo on my branch does. It's effectively a fork of cortex-m-rt with some modifications I needed to make to have it work.

All in all I think we need to look at how the HAL can provide the tools, but not necessarily supply the default states assumed to be there currently to account for the variations between the teensy environment and the bare imxrt1062 environment found on the EVK boards.

See the imxrt1060evk_fixes branch for everything I needed to change to make this work.

Ignore generated ral code

I think it might make sense to ignore any generated code, to make it clearer that those files really shouldn't be edited by hand. It does mean any contributor would need to have python and some tooling installed (though already done semi-automatically via make venv) to generate the ral files.

I'd like some feedback on that before doing it.

RFC: Maintain i.MX RT-specific drivers in separate repos

In #56, we discuss a split HAL, which will have a single HAL crate per i.MX RT processor family. The HALs will probably depend on imxrt-iomuxc, our custom driver for pad multiplexing and configuration.

We maintain imxrt-iomuxc in this repository. However, this issue proposes that we maintain imxrt-iomuxc, along with other foundational, chip-specific drivers, in separate repositories. The approach will have us maintain only the split HALs, the common HAL, and the RAL in this repository.

I define a "foundational, chip-specific driver" as a library that

  • is specialized for i.MX RT processors
  • could be used across higher-level i.MX RT libraries within our organization, and outside our organization
  • fully replaces the equivalent API provided by imxrt-ral, so that the user does not require the RAL
  • requires a higher-level, safe interface before exposing to end-users

Drivers that meet this criteria include

  • IOMUXC, released as imxrt-iomuxc
    • API for pad configuration and multiplexing, specific for our processors
    • Used in HAL, async HAL; depended on by teensy4-pins
    • Does not depend on the RAL
  • CCM, in development as imxrt-ccm
    • API for clock configuration, and clock gating. Prototyped in ccm module of async HAL, but considered for HAL integration in #92
    • By default, usable without the RAL. Will optionally depend on RAL as a convenience for RAL-based APIs (#92).
  • DMA, in development as imxrt-dma
    • Unsafe API for allocating DMA channels, and scheduling DMA transfers. Re-exported by HAL and async HAL behind a safe interface.
    • Developed in HAL, then ported to async HAL with minimal changes. There's much code and documentation that could be shared.
    • Will not depend on the RAL

I do not consider HAL crates in the category of foundational, chip-specific drivers. HALs are

  • designed for safety and ergonomics. Foundational, chip-specific drivers may not provide a safe interface, or may require a deeper understanding of the peripheral.
  • intended to abstract processor complexities behind interfaces, like embedded-hal. Foundational, chip-specific drivers expect you to deal with that complexity.
  • dependent on foundational, chip-specific drivers, and may re-expose the interfaces of these drivers. Foundational, chip-specific drivers may be used by themselves, possibly with no required dependencies.

Pros

My thoughts for separating the development of these crates from the HAL:

  • We signal that these crates are for public use. Today's imxrt-iomuxc crate appears to be an implementation detail of the HAL. However, I'm happy to support it for others who want to use it outside of our HALs, or to develop more convenience APIs for their systems (like teensy4-pins). I also intend to support imxrt-ccm and imxrt-dma for similar users.
  • We share capabilities within our organization, while ensuring that one use-case doesn't dominate development. I'd like to keep using imxrt-iomuxc in the async HAL, just as we use it within the HAL. By separating the repositories, we can ensure that changes to imxrt-iomuxc are not biased towards support of the HAL at the expense of the async HAL. This extends towards external users, too.
  • We can more easily track changes, discuss issues, add documentation, and maintain releases independent of the HAL.
  • Consistency in our HAL and async HAL APIs. If #92 is accepted, our driver initialization HAL APIs will resemble the proposed async HAL APIs. This design consistency, based on a common set of crates, could be nice for users.

Cons

This comes with some downsides. Namely, it's more difficult to integrate work into the HAL(s). However, after prototyping the async HAL, which depends on imxrt-iomuxc as a git dependency, I don't consider these to be difficult challenges. I've listed my thoughts on these downsides below the problem.

  • Changes require two separate commits in two separate repositories.
    • No way around this. Workaround includes instructing contributors to use local path dependencies / git dependencies when prototyping, if they prefer.
    • (I consider this a feature in the vein of "easily tracking changes" and making sure a driver's development isn't dominated by one use case.)
  • Path dependencies are not portable across development environments.
    • Path dependencies are solved by git dependencies.
  • Continuous integration is less obvious.
    • When the HAL depends on imxrt-iomuxc as a git dependency, it will ensure that the APIs work as expected. CI doesn't change.
    • imxrt-iomuxc, and the other foundation drivers, should be tested in isolation, without us relying on "it works in the HAL." Relates back to "dominate development for a single use-case."
  • Requires more release coordination
    • No more difficult than releasing imxrt-iomuxc before the HALs, which we already do.
    • Still need to figure this out for imxrt-iomuxc and the async HAL support in today's model.
  • Inconsistent with imxrt-ral maintenance, which happens in this repository.
    • A similar approach might have us maintain the RAL in a separate repo. Out of scope until we have a stable design for #56.

Alternatives

An alternative approach would have us maintain the foundational, chip-specific drivers in this repository. However, these drivers still be shared with the async HAL in the manner described above, which may relegate the async HAL to a "less important" project in our organization. Once we're sharing the crates across both HALs, there's no reason why the foundational crates should be maintained here, versus in the async HAL repository. (A further step might including moving the async HAL into this repository; however, I feel the projects are different enough to warrant a repository boundary and separate issue tracker.)

Another approach would do nothing. We continue maintaining imxrt-ccm and imxrt-dma as separate modules within each of the HAL and async HAL projects. I'm not opposed to this approach; I did this with the HAL's dma module to support async HAL prototyping. But, this results in code duplication, which could have been mitigated. Still need to figure out how imxrt-iomuxc is consumed by async HAL.

Next Steps

If accepted, I'll

  • port imxrt-iomuxc to a separate repo
    • ensure that we have no pending changes to imxrt-iomuxc
    • move the imxrt-iomuxc crate into a separate repo under the imxrt-rs organization
    • integrate the crate into the HAL
    • integrate the crate into the async HAL
    • improve the imxrt-iomuxc documentation, making it more useful for users and contributors (tracked in imxrt-rs/imxrt-iomuxc#2)
    • transition any IOMUXC issues (#87) to the new repo
  • publish imxrt-ccm in an imxrt-rs org repo
    • integrate the crate back into async HAL
    • [skipping integration into HAL until #92]
  • publish imxrt-dma in an imxrt-rs org repo
    • integrate crate into HAL
    • integrate crate into async HAL

We will maintain imxrt-iomuxc releases from the new repo. We can release imxrt-ccm and -dma once we explore the APIs in the HAL and async HAL.

Support GPIO input interrupts

The 0.4 HAL does not have any way to support interrupts on GPIO inputs. This also applies to any previous version of imxrt-hal. Add and document an API that supports

  • enabling / disabling interrupts on a GPIO input
  • selecting the various edge / level configurations

Since this should be a brand-new feature, it should be possible to release in 0.4.5 without issues.

Using the HAL as a git dependency

cargo lets us specify git dependencies. But today, if we try to depend on the HAL (or the RAL) using a git dependency, it won't work:

[dependencies.imxrt-hal]
git = "https://github.com/imxrt-rs/imxrt-rs.git"
features = ["imxrt1062"]
cargo build
    Updating git repository `https://github.com/imxrt-rs/imxrt-rs.git`
    Updating crates.io index
error: no matching package named `imxrt-ral` found
location searched: https://github.com/imxrt-rs/imxrt-rs.git#adc91a14
perhaps you meant: imxrt-hal
required by package `imxrt-hal v0.3.0 (https://github.com/imxrt-rs/imxrt-rs.git#adc91a14)`
    ... which is depended on by `krate v0.1.0 (/private/tmp/krate)`

We see the error because

  1. the RAL is not tracked by git, and
  2. the HAL depends on the RAL by a path. Git dependencies will follow dependency paths in the crate's Cargo.toml file, similar to how local development will follow paths.

Supporting git dependencies is nifty. Users can track development in the HAL, and depend on features before they're released. I used git dependencies when developing the imxrt-uart-log crate, developing against the unreleased DMA feature. I checked-in the RAL's source in my fork, since I also needed unreleased RAL features for the crate development. A new collaborator, @mitchmindtree, is also sharing Cargo patches in the teensy4-rs project using git dependencies (mciantyre/teensy4-rs#64). I noticed that Mitch removed the RAL's path in order to make patching with a git dependency work. If we had support for git dependencies in this repository, we wouldn't each need one-offs to support sharing new features.

To support using the HAL as a git dependency, I propose that we roll-back #15, and we include the RAL's source code in version control. I can see a few benefits:

  • we can use both the HAL, and also the RAL, as git dependencies.
  • collaborators don't necessarily need the Python environment to develop the HAL, which is one of today's requirements. If the RAL is checked in, users only need to run the auto-generation when making fundamental changes to the RAL.

The drawback is that the imxrtral.py auto-generation script could fall out of sync with the checked-in RAL. That is, we need to defend against users who manually changes a RAL source file without making the change in imxrtral.py. I'm still working through ideas that are better than "just do a code review." My current thinking is that there's a CI job that generates the RAL, but doesn't overwrite the checked-in contents. Rather, it diffs the checked-in contents from what was just generated. If they vary, we flag the build. This should signal that RAL changes should be described in imxrtral.py instead of direct modifications to the source.

Any other approaches or ideas we should consider? Let me know! I'd like support for git dependencies so that we can develop RTIC support (mciantyre/teensy4-rs#62) without having to formally release small-ish HAL changes.

USB Support

I'd really like to support USB for audio/midi/mass storage/cdc purposes, there's on going work to build a usb device trait and implementations. We should join the party

CI Clippy Integration

Currently github actions uses doesn't support a working directory, and the actions-rs/clippy action would need a working directory to work correctly in this workspace repository. I'm not too keen on splitting the repo as its already a chore to modify Cargo.toml's to use local paths for the bsp and rt crates.

Perhaps we can at least run clippy using a github action run task in place of the nicer uses task.

svd2workspace needed

Currently the svd2rust code generator generates an absolutely enormous 300kloc lib.rs for the imxrt series chips.

The teensy4rs authors manually split the generated code up into crates to solves this. There is an rfc to allow svd2rust to generate workspaces

rust-embedded/wg#338

This would solve the problem of very long compile times for the PAC crates that get generated here.

Clock configuration for i.MX RT 1011, 1015, and 1021

Currently clock configuration is pretty specific to imxrt106x series chips (perhaps works with 105x)

For 101x there is no PLL_ARM, and the DCDC is set incorrectly, it should be setup to 1.5V

The mcuxpresso sdk has good examples of clock config that can be used for the logic flow to accomodate that.

Rethink how we accomplish pin muxing

If you want to configure peripherals that require processor pads, you need to set the pad into the correct 'alternative,' or mode. Pads have multiple alternatives, depending on their usage. (This is also called pin muxing.) The current approach puts the responsibility on users to know what alternative is necessary for a given peripheral. Although this could be identified from the method signatures, it's more overhead, and the user's code isn't as clean. We should be able to simplify this API.

Consider the initialization of a UART peripheral, using Teensy pins 14 and 15. The snippet below describes how it happens today.

let mut uart = uarts
        .uart2
        .init(
            peripherals.pins.p14.alt2(),
            peripherals.pins.p15.alt2(),
            BAUD,
        )
        .unwrap();

See those calls to alt2()? Those set the pins into the correct alternative for UART. They're required because the signature if init() looks like

pub fn init<TX, RX>(
        self,
        mut tx: TX,
        mut rx: RX,
        baud: u32,
    ) -> Result<UART<M>, ccm::uart::TimingsError>
    where
        TX: uart::Pin<Direction = uart::TX, Module = M>,
        RX: uart::Pin<Direction = uart::RX, Module = M>,

and the uart::Pin implementations resemble

impl uart::Pin for GPIO_AD_B1_02<Alt2> {
  type Direction = uart::TX;
  type Module = uart::module::_2;
}

impl uart::Pin for GPIO_AD_B1_03<Alt2> {
  type Direction = uart::RX;
  type Module = uart::module::_2;
}

If we follow that through, we see that GPIO_AD_B1_02 can only be the TX pin for UART 2 when it's in the second alternative. Same applies for GPIO_AD_B1_03 as an RX pin. The only way to get a pad in that state is to call alt2().

I want to remove the requirement for users to set the alternative. Remove the calls to alt2() so that the snippet simplifies to

let mut uart = uarts
        .uart2
        .init(
            peripherals.pins.p14, // no more alt2() call
            peripherals.pins.p15, // no more alt2() call
            BAUD,
        )
        .unwrap();

A solution should not sacrifice compile-time checking for a convenient API. You cannot provide the wrong pin to the UART module; if you tried to pass in p13, rather than p14, for the TX pin, it fails to compile. You also cannot put the pin into the wrong alternative; a call of alt3(), rather than alt2(), fails to compile. We should keep these kinds of guarantees in a new approach.

This will probably require changes throughout the HAL, including in the IOMUXC modules, and all peripheral modules that depend on processor pins.

(Manually transferred from mciantyre/teensy4-rs#46)

Expose each device.x linker script

We need to expose the device.x linker script for the feature flag selected device at compile time. This is then used by imxrt-rt to build a linker script.

FlexIO as SPI Master

I am currently working on a project that will use a teensy 4.1 to control about 100 SPI devices and plan to use the FlexIO periphial to minimize daisy-chaining by driving up to 16 devices in parallel (only one channel select though). I have already hacked together a version that basically writes the constants from the SPI Master example into the registers with a bit of extra configuration.

Does it make sense to include a FlexIOasSPI periphial here? I think I lack the experience to write a useful general FlexIO abstraction as this is my first embedded project.

Release 0.2.0

I'm very satisfied with where things are I think. Enough so I think its time to get the next minor pre 1.0 revision out there in the wild.

I've got most of the basic things working on an imxrt1060evk at this point.

I've tested some of the examples on the teensy and was happy enough with the results, I don't have a MPU9250 to test the i2c/spi examples with.

If there's anything left todo before then please note that here. Will wait til Apr 8 before tagging and bagging.

Port HAL to the RAL

The issue tracks how much of the HAL is migrated from the PAC-based implementation to the RAL-based implementation.

  • CCM
  • GPIO
  • I2C
  • IOMUXC
  • PIT
  • PWM
  • SPI
  • UART

My focus is on keeping parity with the existing features; for example, I don't intend on generalizing the HAL for other iMXRTs right now. Once I can build the HAL, I can show equivalence by using the examples in the teensy4-examples crate.

Release 0.4.2

We've accumulated non-breaking HAL changes, including three new peripherals:

I'm planning a 0.4.2 release of the HAL to publish these new peripherals. Once released, the new peripherals will be immediately available to users who typically target a 0.4 HAL crate.

I plan to release sometime next week. Should we hold off, or include any other work?

The ADC support will warrant a new release of the IOMUXC crate, which defines the ADC pins. I'll track that in the IOMUXC repo.

0.4 HAL releases may break RAL users

When we release new imxrt-hal peripherals in a backwards-compatible package, we risk breaking users who prefer to mix the HAL with imxrt-ral.

use imxrt_hal as hal;
use imxrt_ral as ral;

// The HAL doesn't support an ADC driver yet.
// We maintain our own, custom driver.
mod custom_adc;

let peripheral = hal::Peripherals::take().unwrap();
let adc = custom_adc::new(
  ral::adc::ADC1::take().unwrap() // <-- panics if the HAL takes the ADC driver for itself.
);

The 0.4.2 HAL release introduced an ADC peripheral. Suppose this user designed to the 0.4.1 HAL, and wanted to implement their own ADC peripheral. When this user upgrades from 0.4.1 to 0.4.2, they observe a panic!() at the indicated call site.

This just hit me while reviewing #115. Looks like the 0.4.2 release is the only example of this breakage in the 0.4 series.

Any thoughts on how to proceed?

ADC implementation

embedded-hal specifies ADC traits here

It seems that this crate does not implement those yet. This would be useful in order to e.g. read analog values from the pins of a Teensy 4 board.


Would love to try and help with this, if someone could point me to the right direction & provide a bit of mentoring ๐Ÿ™‚

[WIP] RFC: construct drivers from RAL instances

This more of a brain dump, not a well-formed RFC. I don't think it's ready for feedback. That being said, feel free to leave early thoughts if you see a thread of ideas. I'll revise this, then assign reviewers, when ideas are more coherent. My TODOs are emphasized throughout.

The RFC proposes that we change HAL driver initialization. Today, we have single Peripherals object that users take() or steal(). Instead of this API, we should let users supply HAL drivers with RAL instances, while still enforcing the driver states that we require.

Background

There's a few reasons we have the Peripherals object as the HAL driver entrypoint:

  • We previously used an svd2rust-generated peripheral access crate (PAC). This was the PAC API at the time, which we lifted into our API.
  • The approach lets us conveniently enforce driver states in the type system, without any extra thinking. For instance, we signal that all drivers are in an "unclocked" state, which requires that you "clock" them. We can insert this type state before users have any other ways to get the driver.
  • The approach let us include global system setup directly in the take() or steal() methods, without any extra thinking.
  • I thought I saw other HAL crates do this.
  • (Other reasons we did this that I don't remember...)

Downsides

Suppose a user wants to directly use the RAL API to control UART, but they want to use the rest of our HAL drivers. After calling Peripherals::take(), the user will need to unsafely steal the RAL LPUART2 instance. This is because Peripherals::take indiscriminately takes all of the RAL instances, leaving nothing for the user. Flip the sequence: the user safely takes the RAL's LPUART2 instance, then calls Peripherals::take(). The latter returns None, since the implementation sees that LPUART2 was already taken. The user needs to unsafely steal all of the peripherals. In either sequence, we see unsafe.

The approach requires that we mimic PAC APIs to support other embedded Rust frameworks. In #69, we implemented a steal() method on Peripherals to accommodate RTIC's requirements. Without this change, there would be no safe way for users to combine RTIC with the imxrt-hal. An alternative initialization API would let us use the HAL without duplicating API requirements in both our RAL and HAL.

Proposed Approach

HAL drivers accept RAL instances as inputs. We continue to enforce the driver clocking and other validity states through the type system.

(Insert self-contained, minimal example here. In lieu of that self-contained example, see the WIP imxrt-async-hal, which has prototyped the driver initialization flow I'm envisioning.)

Requirements

  1. Compatible with the imxrt-iomuxc crate. Specifically, we need to catch incorrect pairings of RAL peripheral instances with pads (i.e. you cannot combine a LPUART1 RAL instance with a LPUART2_TX pad). This should be caught at compile time.
  2. Enforce driver clocking, validity states through the type system.
  3. Amenable to a configure / release pattern (#20).

Discussion

This seems to be the favored approach in other Rust HALs. Recent study of STM32 HALs show a pattern of HAL drivers that accept PAC peripheral instances. We observe the same pattern in nrf-hal, which we're using as a model in #56.

The HAL would no longer need to explicitly support RTIC. The RAL is already compatible with RTIC. We teach users that, to use RTIC, you need to use the RAL, then construct HAL drivers from the RAL components.

Prototyping in imxrt-async-hal has shown ways to meet the three requirements, though they could be improved. Requirement 1 needs strongly-typed RAL instances, which we don't have. Strongly-typed RAL instances would signal their module number (the '2' in LPUART2) in the type system. To overcome this, the async HAL uses the instance interface. This incurs a runtime check, but it lets us meet the compile-time requirement, and it localizes an invalid state to a single call site. A new RAL API that signaled strongly-typed instances could help, but that comes with other trade-offs.

Requirement 2, specifically the clocking states, is enforced by combining the async HAL's ccm interface with peripheral initialization APIs. (Shown throughout the async HAL interfaces; see the code until I add more ideas here.)

Upload to EVK

Hi guys,

Great work! A quick question. Did you succeed in loading this to an EVK device? I am trying to upload the following program using mcuxpresso's GUI Flash tool to an imxrt1052 EVKB, it seems to upload it, but fails to light up the LED.

I would appreciate any feedback :)

//! Prints "Hello, world!" on the host console using semihosting

#![no_main]
#![no_std]

extern crate panic_halt;

use cortex_m_rt::entry;
use cortex_m_semihosting::{debug, hprintln};
use imxrt_ral::{read_reg, write_reg, modify_reg, reset_reg};
use imxrt_ral::{gpio};

fn set_pin_9(gpio: &gpio::RegisterBlock) {
    write_reg!(gpio, gpio, GDIR, 1<<9);
    write_reg!(gpio, gpio, DR, 1<<9);
}

#[entry]
fn main() -> ! {
    // exit QEMU
    // NOTE do not run this on hardware; it can corrupt OpenOCD state
    // debug::exit(debug::EXIT_SUCCESS);

    let gpio1 = gpio::GPIO1::take().unwrap();
    set_pin_9 (&gpio1);
    loop {}
}

memory.x

MEMORY
{
  /* Define each memory region */
  FLASH (rx) : ORIGIN = 0x60000000, LENGTH = 0x4000000 /* 64M bytes (alias Flash) */  
  RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x20000 /* 128K bytes (alias RAM) */  
}

Support additional UART configurations

We support 8N1, 8E1, and 8O1 UART configurations. But, the peripheral could support more settings, such as 7 bits or 9 bits of data, and double stop bits.

This is a tracking issue for supporting additional serial configurations. Additional configurations could include

  • 7O1
  • 7E1
  • 9N1
  • 9E1
  • 9O1
  • double stop bits

(Manually migrated from mciantyre/teensy4-rs#35)

Move imxrt-ral into its own repository

#94 will separate the imxrt-iomuxc crate into a separate repo. We'll also publish foundational, chip-specific crates into separate imxrt-rs repositories. See the issue to understand what a foundational, chip-specific crate means.

Once #94 is complete, this repository will contain a variety of HALs, and imxrt-ral, or the "RAL." #94 notes that maintaining the RAL in this repository is inconsistent with the rest of our organization. So, we may consider moving it into its own repository.

Move imxrt-ral into imxrt-rs/imxrt-ral so that its development, testing, and release may be managed separate of the HALs.

Add usage and contribution documentation

We've found that it's not easy for new contributors to get started (#51). We should add some getting started documentation, and some contribution documentation.

Things we should cover

  • how to use imxrt-hal in another project
    • note that we require a feature flag
  • how to contribute to the project
    • how to generate the RAL
    • helpful external resources (reference manuals, other projects to study)
  • how to test the HAL / RAL
    • we don't have runnable examples in this project; instead, we rely on other projects to test the HAL. Provide links to other works that substantially depend on the HAL, like the teensy4-bsp.

Add other ideas below!

Input pin HIGH state persists.

Hello!

I'm new to embedded Rust and I'm using this library with Teensy-rs and I'm facing some strange behaviour I'm not sure is intended.
During the setup, I prepared the LED (output, pin13) and a input (pin 0).

let mut led = {
   let pin = bsp::hal::gpio::GPIO::new(pins.p13);
   pin.set_fast(true);
   pin.output()
};
let input = bsp::hal::gpio::GPIO::new(pins.p0);

Input is connected to 3v by a button.
During the loop I check turn on or off the LED depending on Pin 0 state:

if input.is_set() {
   led.set();
} else {
   led.clear();
}

Problem is, once the button is pressed, the LED never turns off and the is_set() returns true even after rebooting the board. Only after removing the power source, the value is reset.
This does not happen if the code is written in C using Teensyduino.

I have found a workaround by turning the PIN into output and clearing it, but I'm not sure if this is what I'm supposed to do.

let mut output = input.output();
output.set_fast(true);
output.clear();
input = output.input();

Any help would be appreciated.

Runtime Feature

Since we are using our own runtime to deal with the many fun runtime configurations this chip can have, the rt feature should pull in imxrt-rt when used rather than cortex-m-rt (which it now no longer does, but did).

This requires changing the imxrtral.py to suite our needs further in this regard. It could always be changed back to cortex-m-rt if cortex-m-rt ever suited all our needs in terms of runtime.

uSDHC support

I'm not seeing code for the uSDHC/SDIO peripheral found in the RT1062.

Add this please?

Release: HAL 0.3.0, RAL 0.3.0

We've accumulated a decent set of features and fixes in the HAL (CHANGELOG). There's one more HAL feature pending in #58. And, once #57 is merged, we will have fixed the RAL's documentation. Given the updates, I'd like to cut a release of both crates.

  • imxrt_hal minor bump to 0.3.0
  • imxrt_ral patch feature bump to 0.2.2 0.3.0.

Release will gate on #57 and #58. I'm shooting for 2020/06/17 as the earliest release date.

Let me know if we'd like more time to prepare!

sion_enable/disable safety

I noticed when adding the pin configure functionality that sion_enable/disable take &self rather than &mut self, while internally mutating a register, this is possibly unsafe as two references can now mutate the state of the pin leading to confusing situations where perhaps multiple references of the pin ask for different sion enable states. It's unlikely, but it is still unsafe.

what is the proper way to configure and use the gpt2 peripheral?

I am using gpt1 to blink an LED. I want to use gpt2 to toggle the virtual shift modifier on my USB keyboard experiment. I have the following code (derived from https://github.com/imxrt-rs/imxrt-usbd/blob/master/examples/teensy4/src/support.rs)

pub fn rig_timer<'a>(
    duration: Duration,
    dcdc: &mut DCDC,
    gpt1: Unclocked,
    ccm_pll1: &mut imxrt_hal::ccm::PLL1,
    ccm_handle: &'a mut imxrt_hal::ccm::Handle,
    ccm_perclk: imxrt_hal::ccm::perclk::Multiplexer,
) -> (GPT, imxrt_hal::ccm::perclk::Configured<'a>) {
    let (_frequency, ipg_hz) = ccm_pll1.set_arm_clock(hal::ccm::PLL1::ARM_HZ, ccm_handle, dcdc);
    let mut cfg = ccm_perclk.configure(
        ccm_handle,
        hal::ccm::perclk::PODF::DIVIDE_3,
        hal::ccm::perclk::CLKSEL::IPG(ipg_hz),
    );
    let gpt1 = rig_timer_2(duration, gpt1, &mut cfg);

    (gpt1, cfg)
}

pub fn rig_timer_2(duration: Duration, gpt: Unclocked, cfg: &mut Configured<'_>) -> GPT {
    let mut gpt = gpt.clock(cfg);
    gpt.set_wait_mode_enable(true);
    gpt.set_mode(hal::gpt::Mode::Reset);

    let gpt_ocr: hal::gpt::OutputCompareRegister = hal::gpt::OutputCompareRegister::One;
    gpt.set_output_compare_duration(gpt_ocr, duration);
    gpt
}

and I use it later


        let (gpt1, mut clock_config) = support::rig_timer(
            duration,
            &mut dcdc,
            gpt1,
            &mut ccm.pll1,
            &mut ccm.handle,
            ccm.perclk,
        );

        let gpt2 = support::rig_timer_2(
            core::time::Duration::from_millis(200),
            gpt2,
            &mut clock_config,
        );

but it appears that only gpt1 is usable with the idiom (also from support.rs)

pub fn time_elapse(gpt: &mut hal::gpt::GPT, func: impl FnOnce()) {
    let mut status = gpt.output_compare_status(GPT_OCR);
    if status.is_set() {
        status.clear();
        func();
    }
}

If I use gpt2, I never observe status.is_set() being true.

I flipped things so that gpt2 controls the LED and gpt1 controls the virtual shift key, and then the shift key flips like I want, but the LED never toggles, so I am reasonably confident the problem is with gpt2 (probably my configuration of gpt2).

What am I overlooking? What should I change to make gpt2 work the way I think it should?

SAI/I2S Support

SAI support would be a great way to make good use of these chips as audio processors

RFC: A Split HAL

I've been mulling this over for awhile now, and really I'm starting to think...

We should have a HAL crate per chip, that depends on a common HAL (imxrt-hal) still.

My reasoning really comes back to the documentation part of the HAL, along with perhaps not wanting to rely too much on conditional compiles after all, at least at the end user supplied HAL crate.

So imagine the current HAL crate remains, but the Peripherals object that is built ends up in its own imxrt1062-hal crate which hides the need to set the feature flags up from end users.

Spurious CS high when using SPI enable_chip_select_0

I've been debugging an SPI problem for the last week, and in the end I ended up buying a logic analyzer to hunt the problem down.

The problem seems to stem from using enable_chip_select_0 instead of taking control over the CS myself.

I apologize in advance if the problem isn't in imxrt-hal, I've tried to follow the code, but I don't quite get how this specific feature works.

The code in question is this:

    let (_, _, _, spi4_builder) = p.spi.clock(
        &mut p.ccm.handle,
        ccm::spi::ClockSelect::Pll2,
        ccm::spi::PrescalarSelect::LPSPI_PODF_5,
    );

    let mut spi = spi4_builder.build(pins.p11, pins.p12, pins.p13);

    spi.set_clock_speed(bsp::hal::spi::ClockSpeed(1_000_000))
        .unwrap();

    spi.enable_chip_select_0(pins.p10);

    spi.set_mode(spi::MODE_0).unwrap();
    spi.clear_fifo();


// and later (using u16, since my chip uses that by default)

        let mut buf2 = [0x4100, 0];
        spi.transfer(&mut buf2)?;

Recording this with the logic analyzer I get this:

  • White is CS
  • Brown is clock
  • Red is MOSI
  • Orange is MISO

Screen Shot 2021-07-05 at 18 37 11 2

Notice how the white CS goes to high in between word 1 and word 2. I believe this not correct and the chip I'm working with (an IO-expander MCP23S17), does not like it at all.

The workaround is to take control over the CS myself, so quite easily solved once I saw the problem, but I wonder whether the behavior of method.enable_chip_select_0 can be fixed?

Release: RAL & HAL 0.4, IOMUXC 0.1

I'm preparing a 0.4 release of the RAL and HAL. Both crates have a feature-flag change: "rtfm" => "rtic". The HAL has also accumulated new, breaking changes, which we could receive broader feedback on. The 0.4 HAL requires us to publish 0.1 releases of the IOMUXC crates.

We also have a GPIO bug fix to publish. To support any 0.3 HAL users, I'm also preparing a 0.3.1 HAL release, which backports the GPIO patch onto the 0.3 release. Users who aren't interested in adopting the 0.4 breaking changes can still have their crates fixed.

Is there any other work we'd like to include? If not, I'll release by next week.

The section below is a snippet from the HAL's CHANGELOG describing the next release.


HAL Changelog

This release contains numerous breaking HAL changes. See the "Changed" section for more information.
The release includes 0.3.1 fixes.

Added

  • steal() the top-level Peripherals object. The steal() method lets users use the imxrt_hal
    as an RTIC device.
  • DMA Memcpy may support interrupt handling
  • A new interface for pad configuration, supported by the imxrt-iomuxc crate family. See the Changed
    section for migration information.

Changed

  • BREAKING The pad configuration interface has simplified. Users will use the
    interface exposed by the imxrt-iomuxc crate family. This section describes how you might
    update your 0.3 (and earlier) code for the new interface.

    • Naming: the IOMUXC instance exposes pads with a new naming convention. Previously, member
      accessed resembled

      let peripherals = imxrt_hal::Peripherals::take().unwrap();
      peripherals.iomuxc.gpio_ad_b1_02;

      Now, IOMUXC member access resembles

      peripherals.iomuxc.ad_b1.p02

      Generally, remove the "gpio_" prefix, and replace the second underscore with member access and
      a "p" symbol.

    • Pad Types: The interface simplifies the pad types, removing the alternate type state.
      Usages resembling

      use imxrt_hal as hal;
      
      type MyPin = hal::iomuxc::gpio::GPIO_AD_B0_12<hal::iomuxc::Alt5>;

      can be simply expressed as

      type MyPin = hal::iomuxc::ad_b0::AD_B0_12;

      Note the stuttering convention of pad_group::pad_group_offset to reference pad types.

    • No alternate transition: there are no altX() methods. Usage resembling

      let mut uart = uarts.uart2.init(
          peripherals.iomuxc.gpio_ad_b1_02.alt2(),
          peripherals.iomuxc.gpio_ad_b1_03.alt2(),
          BAUD,
      ).unwrap()

      should drop the altX() calls (after renaming the pads).

      let mut uart = uarts.uart2.init(
          peripherals.iomuxc.ad_b1.p02,
          peripherals.iomuxc.ad_b1.p03,
          BAUD,
      ).unwrap();
    • Type Tags: all custom type-level constants, like imxrt_hal::uart::module::_1 are now
      typenum constants. There are no peripheral-specific constants. Usage resembling

      use imxrt_hal::uart;
      
      type MyTX = uart::Tx<uart::module::_3>;

      should update to

      use imxrt_hal::uart;
      use imxrt_hal::iomuxc;
      
      type MyTX = uart::Tx<iomuxc::consts::U3>;
    • GPIO: the new IOMUXC driver results in a simpler GPIO interface. There is now a single GPIO
      type that wraps an IOMUXC pad. Any GPIO type, like

      use imxrt_hal as hal;
      
      type HardwareFlag = hal::gpio::GPIO1IO26<hal::gpio::GPIO1, hal::gpio::Output>;

      should change to

      type HardwareFlag = hal::gpio::GPIO<hal::iomuxc::ad_b1::AD_B1_10, hal::gpio::Output>;

      The new GPIO types expose a no-return toggle() method, which shadows an embedded_hal
      trait method. If you notice compilation issues surrounding toggle(), try removing
      unwrap() calls:

      led.toggle().unwrap() // Old
      led.toggle()          // New

      Or, qualify that the ToggleableOutputPin trait method should be called.

  • BREAKING The HAL's "rtfm" feature is changed to "rtic", reflecting the framework's
    new name. Users who are relying on the "rtfm" feature should now use the "rtic" feature.

  • BREAKING The dma::{Config, ConfigBuilder} types are gone. This affects the dma::Peripheral
    interface. To configure interrupt on completion / half settings, use dma::Channel::set_interrupt_on_completion()
    / dma::Channel::set_interrupt_on_half() to perform the same configurations before suppling the
    channel to dma::Peripheral.

RT600 arm core support

Hi,

This is more of a question; I would like to know if you would have an estimate for the amount of work needed to support the RT600 series? I'm not requesting it, just curious to know about your feeling for the effort needed. I think the main difference apart from peripherals is the M33 architecture (ARMv8), but I have just glanced at the data sheet.

I do understand that the additional DSP co-processor is completely different to the core CPU, and would probably better be implemented in it's own crate.

Thanks,
Max

https://www.nxp.com/products/processors-and-microcontrollers/arm-microcontrollers/i-mx-rt-crossover-mcus/i-mx-rt600-crossover-mcu-with-arm-cortex-m33-and-dsp-cores:i.MX-RT600

CAS loop or disable interrupts on unsafe modify of shared registers

A few places I'm finding we are doing unsafe modify_reg(); These need to be reviewed in the hal to ensure that when doing an unsafe modify that the register isn't possibly modified elsewhere in the hal.

This is so that after a read but before a write for a modify operation a pre-empted ISR which also modifies the same register may cause a data race once returned to the previously interrupted code.

In such cases (GPIO !) we need to likely either temporarily disable interrupts before and re-enable after these operations or implement a compare and swap loop such that when interrupts to occur an atomic cas will fail and a loop may retry the whole task.

This seems like its more likely an issue in the GPIO code where the GPIO port registers are shared among multiple individual GPIO pins, unless we change the way the GPIO is used perhaps to be on a port by port basis with a gpio port singleton.

Exposing the Ethernet Media Access Controller (MAC)

We recently ordered a Teensy 4.1 to test its Ethernet capabilities, so I thought I'd kick off a discussion on how to approach adding support and to track progress!

I asked in the rust-embedded matrix channel and someone mentioned we'd need to expose the MAC. They mentioned the stm32 implementation might make for a useful reference:

there's an example stm32 driver which supports smoltcp here which might be helpful in writing one for imxrt: https://github.com/stm32-rs/stm32-eth

The imxrt106X docs are pretty comprehensive, I think the ENET section is what we're looking for.

I'm happy to have a go at this, though it looks like our "Ethernet Kit" won't arrive for another week or two yet.

Implementing the imx rt1170

this is less of a feature request but a question on how this should be realised according to the organisation of the imxrt-rs project. For context I'm trying to write a HAL layer for the imx rt1170, which is a package containing a Cortex M4 and M7 that share the same memory, hardware acceleration modules and GPIO pins. See the product page provided by NXP which includes a nice overview diagram.

I've seen that others before tried to implement such a dual core MCU in Rust but they haven't used this HAL afaik, e.g. https://github.com/japaric/lpcxpresso54114

I first wanted to evaluate how this should be implemented, e.g. in it's own project referencing to the imxrt-hal, just using the embedded-hal project, merged into here, ...? But also how one would go further on and what I should look into to adapt this project to multi core processors

Automated Tasks for Releasing

I believe nrf-hal uses xtask and some automation to bump versions and perhaps do releases.

Might be worthwhile taking a peek before we release again to perhaps automate some of the release steps.

Not a high priority, something I noticed when updating our RELEASE.md and thinking on what I saw in nrf-hal

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.