aboutsummaryrefslogtreecommitdiff
path: root/board
diff options
context:
space:
mode:
authorIan McIntyre <ianpmcintyre@gmail.com>2022-08-02 06:21:12 -0400
committerIan McIntyre <ianpmcintyre@gmail.com>2022-12-01 20:21:05 -0500
commitc7a9b9f3d4b9e71303c7b988d2bd916c2e4df9bc (patch)
tree6d41ea7e433cac328fa165d45d1bc0cd71a1bf8f /board
First commit
Diffstat (limited to 'board')
-rw-r--r--board/Cargo.toml47
-rw-r--r--board/README.md122
-rw-r--r--board/build.rs53
-rw-r--r--board/src/imxrt1010evk.rs36
-rw-r--r--board/src/imxrt1170evk_cm7.rs38
-rw-r--r--board/src/lib.rs107
-rw-r--r--board/src/shared/imxrt10xx.rs35
-rw-r--r--board/src/shared/imxrt11xx.rs52
-rw-r--r--board/src/teensy4.rs37
9 files changed, 527 insertions, 0 deletions
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<String> {
+ 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<crate::Resources> {
+ #[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<crate::Resources> {
+ #[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<crate::Pit> {
+ #[cfg(feature = "rtic")]
+ {
+ extern "C" {
+ // Not actually mut in cortex-m. But, no one is reading it...
+ static __INTERRUPTS: [core::cell::UnsafeCell<unsafe extern "C" fn()>; 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<crate::Pit> {
+ #[cfg(feature = "rtic")]
+ {
+ extern "C" {
+ // Not actually mut in cortex-m. But, no one is reading it...
+ static __INTERRUPTS: [core::cell::UnsafeCell<unsafe extern "C" fn()>; 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<crate::Resources> {
+ 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,
+ })
+}