From c7a9b9f3d4b9e71303c7b988d2bd916c2e4df9bc Mon Sep 17 00:00:00 2001 From: Ian McIntyre Date: Tue, 2 Aug 2022 06:21:12 -0400 Subject: First commit --- board/Cargo.toml | 47 ++++++++++++++++ board/README.md | 122 ++++++++++++++++++++++++++++++++++++++++++ board/build.rs | 53 ++++++++++++++++++ board/src/imxrt1010evk.rs | 36 +++++++++++++ board/src/imxrt1170evk_cm7.rs | 38 +++++++++++++ board/src/lib.rs | 107 ++++++++++++++++++++++++++++++++++++ board/src/shared/imxrt10xx.rs | 35 ++++++++++++ board/src/shared/imxrt11xx.rs | 52 ++++++++++++++++++ board/src/teensy4.rs | 37 +++++++++++++ 9 files changed, 527 insertions(+) create mode 100644 board/Cargo.toml create mode 100644 board/README.md create mode 100644 board/build.rs create mode 100644 board/src/imxrt1010evk.rs create mode 100644 board/src/imxrt1170evk_cm7.rs create mode 100644 board/src/lib.rs create mode 100644 board/src/shared/imxrt10xx.rs create mode 100644 board/src/shared/imxrt11xx.rs create mode 100644 board/src/teensy4.rs (limited to 'board') diff --git a/board/Cargo.toml b/board/Cargo.toml new file mode 100644 index 0000000..21defd4 --- /dev/null +++ b/board/Cargo.toml @@ -0,0 +1,47 @@ +[package] +name = "board" +version = "0.1.0" +edition = "2021" +publish = false + +[dependencies.cfg-if] +version = "1.0" + +[dependencies.imxrt-ral] +version = "0.5" + +[dependencies.imxrt-rt] +path = ".." + +[build-dependencies.imxrt-rt] +path = ".." + +[target.'cfg(all(target_arch = "arm", target_os = "none"))'.dependencies] +teensy4-fcb = { version = "0.3", optional = true } +teensy4-panic = { version = "0.2", optional = true } + +imxrt1010evk-fcb = { version = "0.1", optional = true } +imxrt1170evk-fcb = { version = "0.1", optional = true } +rtt-target = { version = "0.3", optional = true, features = ["cortex-m"] } +panic-rtt-target = { version = "0.1", optional = true, features = ["cortex-m"] } + +[features] +rtic = [] +# Begin board features. +teensy4 = [ + "imxrt-ral/imxrt1062", + "dep:teensy4-fcb", + "dep:teensy4-panic", +] +imxrt1010evk = [ + "imxrt-ral/imxrt1011", + "dep:imxrt1010evk-fcb", + "dep:rtt-target", + "dep:panic-rtt-target", +] +imxrt1170evk-cm7 = [ + "imxrt-ral/imxrt1176_cm7", + "dep:imxrt1170evk-fcb", + "dep:rtt-target", + "dep:panic-rtt-target", +] diff --git a/board/README.md b/board/README.md new file mode 100644 index 0000000..bd4a6e3 --- /dev/null +++ b/board/README.md @@ -0,0 +1,122 @@ +`board` provides a thin board support package for `imxrt-rt`. The +package provides cross-board compatibility for all `imxrt-rt` hardware +examples. It supports `imxrt-rt` development and testing, and is not +intended as a general BSP. + +`board` supports + +- Teensy 4.0 and Teensy 4.1 boards with the `teensy4` feature. +- the IMXRT1010EVK board with the `imxrt1010evk` feature. +- the Cortex M7 on the IMXRT1170EVK with the `imxrt1170evk-cm7` + feature. + +When using any NXP EVK, make sure that your boot device is FlexSPI. +Consult your board's hardware user guide for more information. + +## Board configurations + +The examples in this repository are very basic. They demonstrate a +working runtime by blinking an LED. They also configure timer interrupts +to show that the vector table is placed and registered correctly. They +only use `imxrt-ral` to access registers. + +Boards simply specify an LED. See the relevant module in `board/src/` +for more information. + +`build.rs` configures the runtime for each board. You can change this to +explore different runtime configurations. + +## Building hardware examples + +Hardware examples for `imxrt-rt` depend on `board` and a board +selection. This section describes how to build an example for your +board. It focuses on building examples for the Teensy 4, but the concept +generalizes for all supported boards. + +To build the `blink-blocking` example for a Teensy 4, run the command +from the repo's root: + + cargo build --example=blink-blocking --features=board/teensy4 --target=thumbv7em-none-eabihf + +Generally, you select the example with `--example`, and specify the +board with `--features=board/[your-board]`. To build the same example +for the IMXRT1010EVK, change `--features=board/teensy4` to +`--features=board/imxrt1010evk`. + +To build an RTIC-based example, enable the `rtic` feature of `board`. + +Artifacts are available under +`target/thumbv7em-none-eabihf/[debug|release]/examples`. Keep this in +mind when flashing your board. + +## Flashing hardware examples + +The tools required to flash an example depend on the board you're using. +This section recommends tooling to flash hardware examples on your +board. + +### NXP IMXRT EVKs + +If you're using an NXP IMXRT EVK, you can use any of the following to +flash your board. + +- [`pyOCD`] supports all i.MX RT 10xx and 11xx boards. +- [`probe-rs` tools] only support i.MX RT 10xx boards. These tools + include + - [`probe-run`] + - [`cargo-flash`] + - [`cargo-embed`] + +See each tool's documentation to understand its usage. To make some +tooling integration easier, see the Tips and Tricks section near the end +of this document. + + [`pyOCD`]: https://pyocd.io + [`probe-rs` tools]: https://probe.rs + [`probe-run`]: https://github.com/knurling-rs/probe-run + [`cargo-flash`]: https://github.com/probe-rs/cargo-flash + [`cargo-embed`]: https://github.com/probe-rs/cargo-embed + +### Teensy 4 + +If you're using a Teensy 4 board, you'll need all of the following: + +- An `objcopy` capable of transforming ELF files into Intel HEX. + Consider using `rust-objcopy` provided by [`cargo-binutils`]. The + rest of this documentation assumes you're using `cargo-binutils`. +- Either a build of [`teensy_loader_cli`], or the [Teensy Loader + Application]. The latter is available with the Teensyduino add-ons. + +After building your example, use `rust-objcopy` to convert the program +into HEX. For the `blink-blocking` example above, that command resembles + + rust-objcopy -O ihex target/thumbv7em-none-eabihf/debug/examples/blink-blocking blink-blocking.hex + +Finally, load the HEX file onto your board using your preferred loader. + + [`cargo-binutils`]: https://github.com/rust-embedded/cargo-binutils + [`teensy_loader_cli`]: https://github.com/PaulStoffregen/teensy_loader_cli + [Teensy Loader Application]: https://www.pjrc.com/teensy/loader.html + +## Tips and tricks + +If you're using `probe-run` or `pyOCD` to flash an EVK, use the tool as +a runner. See the [Cargo Configuration] documentation for more +information. Please do not check your runner setting into the +repository; consider using environment variables or hierarchical +configuration files to configure your runner and any other useful +command aliases. + + [Cargo Configuration]: https://doc.rust-lang.org/cargo/reference/config.html + +## Adding a new board + +Define a new module in `board/src/` that describes your board's LED. Use +the existing examples as your guide. + +Add a new feature to `board/Cargo.toml` for your board. Link any +additional dependencies for your board, like FCB crates and panic +handlers. If an FCB crate does not exist for your board, you can define +the FCB within your newly-added module. + +Update `board/build.rs` to configure a runtime for your chip. diff --git a/board/build.rs b/board/build.rs new file mode 100644 index 0000000..9ee64e8 --- /dev/null +++ b/board/build.rs @@ -0,0 +1,53 @@ +use std::{collections::HashSet, env}; + +fn extract_features() -> HashSet { + env::vars() + .map(|(k, _)| k) + .flat_map(|feat| feat.strip_prefix("CARGO_FEATURE_").map(str::to_lowercase)) + .collect() +} + +/// Configures the runtime for a variety of boards. +/// +/// Note that some automated tests may check these runtimes. Feel free to change +/// values and observe how they might affect the tests. +fn main() { + let features = extract_features(); + for feature in features { + match feature.as_str() { + "teensy4" => { + imxrt_rt::RuntimeBuilder::from_flexspi(imxrt_rt::Family::Imxrt1060, 1984 * 1024) + .flexram_banks(imxrt_rt::FlexRamBanks { + ocram: 0, + dtcm: 12, + itcm: 4, + }) + .heap_size(1024) + .text(imxrt_rt::Memory::Flash) + .rodata(imxrt_rt::Memory::Dtcm) + .data(imxrt_rt::Memory::Dtcm) + .bss(imxrt_rt::Memory::Dtcm) + .uninit(imxrt_rt::Memory::Dtcm) + .build() + .unwrap() + } + "imxrt1010evk" => imxrt_rt::RuntimeBuilder::from_flexspi( + imxrt_rt::Family::Imxrt1010, + 16 * 1024 * 1024, + ) + .heap_size(1024) + .rodata(imxrt_rt::Memory::Flash) + .build() + .unwrap(), + "imxrt1170evk_cm7" => imxrt_rt::RuntimeBuilder::from_flexspi( + imxrt_rt::Family::Imxrt1170, + 16 * 1024 * 1024, + ) + .rodata(imxrt_rt::Memory::Dtcm) + .build() + .unwrap(), + _ => continue, + } + break; + } +} diff --git a/board/src/imxrt1010evk.rs b/board/src/imxrt1010evk.rs new file mode 100644 index 0000000..9a596e9 --- /dev/null +++ b/board/src/imxrt1010evk.rs @@ -0,0 +1,36 @@ +//! iMXRT1010EVK support. + +use crate::ral; + +#[cfg(target_arch = "arm")] +use imxrt1010evk_fcb as _; +#[cfg(target_arch = "arm")] +use panic_rtt_target as _; + +const LED_OFFSET: u32 = 11; + +pub mod rtic_support { + pub use crate::ral::*; +} + +/// Prepare the board for the examples. +/// +/// Call this first. Panics if something went wrong. +pub fn prepare(timer_delay_microseconds: u32) -> Option { + #[cfg(target_arch = "arm")] + rtt_target::rtt_init_print!(); + + let iomuxc = unsafe { ral::iomuxc::IOMUXC::instance() }; + // Set the GPIO pad to a GPIO function (ALT 5) + ral::write_reg!(ral::iomuxc, iomuxc, SW_MUX_CTL_PAD_GPIO_11, 5); + // Increase drive strength, but leave other fields at their current value... + ral::modify_reg!(ral::iomuxc, iomuxc, SW_PAD_CTL_PAD_GPIO_11, DSE: DSE_7_R0_7); + + let pit = crate::prepare_pit(timer_delay_microseconds)?; + + let gpio1 = unsafe { ral::gpio::GPIO1::instance() }; + Some(crate::Resources { + led: crate::Led::new(LED_OFFSET, &gpio1), + pit, + }) +} diff --git a/board/src/imxrt1170evk_cm7.rs b/board/src/imxrt1170evk_cm7.rs new file mode 100644 index 0000000..7d966eb --- /dev/null +++ b/board/src/imxrt1170evk_cm7.rs @@ -0,0 +1,38 @@ +//! Support for booting the Cortex M7 on the i.MX RT 1170 EVK. + +use crate::ral; + +#[cfg(target_arch = "arm")] +use imxrt1170evk_fcb as _; +#[cfg(target_arch = "arm")] +use panic_rtt_target as _; + +const LED_OFFSET: u32 = 3; + +pub mod rtic_support { + pub use crate::ral::NVIC_PRIO_BITS; + #[allow(non_snake_case)] // For RTIC trickery... + pub mod Interrupt { + pub const PIT: crate::ral::Interrupt = crate::ral::Interrupt::PIT1; + } + pub use Interrupt as interrupt; +} + +pub fn prepare(timer_delay_microseconds: u32) -> Option { + #[cfg(target_arch = "arm")] + rtt_target::rtt_init_print!(); + + let iomuxc = unsafe { ral::iomuxc::IOMUXC::instance() }; + ral::modify_reg!(ral::iomuxc, iomuxc, SW_MUX_CTL_PAD_GPIO_AD_04, MUX_MODE: 5); + + let ccm = unsafe { ral::ccm::CCM::instance() }; + // Enable LPCG for GPIOs. + ral::write_reg!(ral::ccm, ccm, LPCG51_DIRECT, 1); + + let gpio = unsafe { ral::gpio::GPIO3::instance() }; + let pit = crate::prepare_pit(timer_delay_microseconds)?; + Some(crate::Resources { + pit, + led: crate::Led::new(LED_OFFSET, &gpio), + }) +} diff --git a/board/src/lib.rs b/board/src/lib.rs new file mode 100644 index 0000000..24d746a --- /dev/null +++ b/board/src/lib.rs @@ -0,0 +1,107 @@ +//! A very simple, multi-board BSP for imxrt-rt-support examples. +#![no_std] + +use imxrt_ral as ral; + +cfg_if::cfg_if! { + if #[cfg(feature = "teensy4")] { + mod shared { pub mod imxrt10xx; } + use shared::imxrt10xx::prepare_pit; + + mod teensy4; + pub use teensy4::*; + } else if #[cfg(feature = "imxrt1010evk")] { + mod shared { pub mod imxrt10xx; } + use shared::imxrt10xx::prepare_pit; + + mod imxrt1010evk; + pub use imxrt1010evk::*; + } else if #[cfg(feature = "imxrt1170evk-cm7")] { + mod shared { pub mod imxrt11xx; } + use shared::imxrt11xx::prepare_pit; + + mod imxrt1170evk_cm7; + pub use imxrt1170evk_cm7::*; + } else { + compile_error!("No board feature selected!"); + } +} + +pub struct Pit(&'static ral::pit::RegisterBlock); + +impl Pit { + fn new(pit: &ral::pit::RegisterBlock, timer_delay_microseconds: u32) -> Self { + // Disable the PIT, just in case it was used by the boot ROM + ral::write_reg!(ral::pit, pit, MCR, MDIS: 1); + let timer = &pit.TIMER[0]; + // Reset channel 0 control; we'll use channel 0 for our timer + ral::write_reg!(ral::pit::timer, timer, TCTRL, 0); + // Set the counter value + ral::write_reg!(ral::pit::timer, timer, LDVAL, timer_delay_microseconds); + // Enable the PIT timer + ral::modify_reg!(ral::pit, pit, MCR, MDIS: 0); + Self(unsafe { core::mem::transmute(pit) }) + } + pub fn blocking_delay(&mut self) { + let timer = &self.0.TIMER[0]; + // Start counting! + ral::write_reg!(ral::pit::timer, timer, TCTRL, TEN: 1); + // Are we done? + while ral::read_reg!(ral::pit::timer, timer, TFLG, TIF == 0) {} + // We're done; clear the flag + ral::write_reg!(ral::pit::timer, timer, TFLG, TIF: 1); + // Turn off the timer + ral::write_reg!(ral::pit::timer, timer, TCTRL, TEN: 0); + } + pub fn loop_with_interrupts(&mut self) { + let timer = &self.0.TIMER[0]; + // Enable interrupts and start counting + ral::write_reg!(ral::pit::timer, timer, TCTRL, TIE: 1); + ral::modify_reg!(ral::pit::timer, timer, TCTRL, TEN: 1); + } + pub fn clear_interrupts(&mut self) { + let timer = &self.0.TIMER[0]; + while ral::read_reg!(ral::pit::timer, timer, TFLG, TIF == 1) { + ral::write_reg!(ral::pit::timer, timer, TFLG, TIF: 1); + } + } +} + +unsafe impl Send for Pit {} + +pub struct Led { + offset: u32, + port: &'static ral::gpio::RegisterBlock, +} + +impl Led { + fn new(offset: u32, port: &ral::gpio::RegisterBlock) -> Self { + let led = Led { + offset, + port: unsafe { core::mem::transmute(port) }, + }; + ral::modify_reg!(ral::gpio, port, GDIR, |gdir| gdir | led.mask()); + led + } + fn mask(&self) -> u32 { + 1 << self.offset + } + pub fn set(&self) { + ral::write_reg!(ral::gpio, self.port, DR_SET, self.mask()); + } + + pub fn clear(&self) { + ral::write_reg!(ral::gpio, self.port, DR_CLEAR, self.mask()); + } + + pub fn toggle(&self) { + ral::write_reg!(ral::gpio, self.port, DR_TOGGLE, self.mask()); + } +} + +unsafe impl Send for Led {} + +pub struct Resources { + pub led: crate::Led, + pub pit: crate::Pit, +} diff --git a/board/src/shared/imxrt10xx.rs b/board/src/shared/imxrt10xx.rs new file mode 100644 index 0000000..5ba44eb --- /dev/null +++ b/board/src/shared/imxrt10xx.rs @@ -0,0 +1,35 @@ +//! Code shared across all i.MX RT 10xx chips. +use crate::ral; + +pub(crate) fn prepare_pit(timer_delay_microseconds: u32) -> Option { + #[cfg(feature = "rtic")] + { + extern "C" { + // Not actually mut in cortex-m. But, no one is reading it... + static __INTERRUPTS: [core::cell::UnsafeCell; 240]; + fn PIT(); + } + unsafe { + __INTERRUPTS[crate::ral::interrupt::PIT as usize] + .get() + .write_volatile(PIT); + } + } + let ccm = unsafe { ral::ccm::CCM::instance() }; + // Disable the PIT clock gate while we change the clock... + ral::modify_reg!(ral::ccm, ccm, CCGR1, CG6: 0b00); + // Set the periodic clock divider, selection. + // 24MHz crystal oscillator, divided by 24 == 1MHz PIT clock + ral::modify_reg!( + ral::ccm, + ccm, + CSCMR1, + PERCLK_PODF: DIVIDE_24, + PERCLK_CLK_SEL: PERCLK_CLK_SEL_1 // Oscillator clock + ); + // Re-enable PIT clock + ral::modify_reg!(ral::ccm, ccm, CCGR1, CG6: 0b11); + + let pit = unsafe { ral::pit::PIT::instance() }; + Some(crate::Pit::new(&pit, timer_delay_microseconds)) +} diff --git a/board/src/shared/imxrt11xx.rs b/board/src/shared/imxrt11xx.rs new file mode 100644 index 0000000..e0d1074 --- /dev/null +++ b/board/src/shared/imxrt11xx.rs @@ -0,0 +1,52 @@ +//! Code shared across all i.MX RT 11xx chips. + +use crate::ral; + +pub(crate) fn prepare_pit(timer_delay_microseconds: u32) -> Option { + #[cfg(feature = "rtic")] + { + extern "C" { + // Not actually mut in cortex-m. But, no one is reading it... + static __INTERRUPTS: [core::cell::UnsafeCell; 240]; + fn PIT(); + } + unsafe { + __INTERRUPTS[crate::ral::Interrupt::PIT1 as usize] + .get() + .write_volatile(PIT); + } + } + + let ccm = unsafe { ral::ccm::CCM::instance() }; + + // Change the bus clock to the 24 MHz XTAL. + // Wouldn't recommend doing this in a real system, + // since the bus clock is running rather slowly. + // But, it's good enough for a demo, and it lets us match + // the behaviors of the 10xx examples. + // + // If we decrease the bus speed too much, we seem to reach a condition + // where we can't re-flash the device. Haven't dug too deeply; only + // observed that keeping the bus clock faster lets us flash more reliably, + // at least with pyOCD. + let clock_root_2 = &ccm.CLOCK_ROOT[2]; + ral::modify_reg!(ral::ccm::clockroot, clock_root_2, CLOCK_ROOT_CONTROL, MUX: 0b001, DIV: 0); + while ral::read_reg!( + ral::ccm::clockroot, + clock_root_2, + CLOCK_ROOT_STATUS0, + CHANGING == 1 + ) {} + + // Enable the clock gate to PIT1. + ral::write_reg!(ral::ccm, ccm, LPCG61_DIRECT, 1); + + let pit = unsafe { ral::pit::PIT1::instance() }; + // 24x scaling accounts for the 24x faster PIT clock. + // Looks like it's blinking at 1Hz, but I'm not pulling out + // my scope or stopwatch or anything. + Some(crate::Pit::new( + &pit, + timer_delay_microseconds.saturating_mul(24), + )) +} diff --git a/board/src/teensy4.rs b/board/src/teensy4.rs new file mode 100644 index 0000000..28587cd --- /dev/null +++ b/board/src/teensy4.rs @@ -0,0 +1,37 @@ +//! Teensy4 support. + +use crate::ral; + +#[cfg(target_arch = "arm")] +use teensy4_fcb as _; +#[cfg(target_arch = "arm")] +use teensy4_panic as _; + +const LED_OFFSET: u32 = 3; + +pub mod rtic_support { + pub use crate::ral::*; +} + +/// Prepare the board for the examples. +/// +/// Call this first. Panics if something went wrong. +pub fn prepare(timer_delay_microseconds: u32) -> Option { + let iomuxc = unsafe { ral::iomuxc::IOMUXC::instance() }; + // Set the GPIO pad to a GPIO function (ALT 5) + ral::write_reg!(ral::iomuxc, iomuxc, SW_MUX_CTL_PAD_GPIO_B0_03, 5); + // Increase drive strength, but leave other fields at their current value... + ral::modify_reg!( + ral::iomuxc, + iomuxc, + SW_PAD_CTL_PAD_GPIO_B0_03, + DSE: DSE_7_R0_7 + ); + + let pit = crate::prepare_pit(timer_delay_microseconds)?; + let gpio2 = unsafe { ral::gpio::GPIO2::instance() }; + Some(crate::Resources { + led: crate::Led::new(LED_OFFSET, &gpio2), + pit, + }) +} -- cgit v1.2.3