Skip to main content

imxrt1050_evkb/
main.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Reference Manual for the Imxrt-1052 development board
6//!
7//! - <https://www.nxp.com/webapp/Download?colCode=IMXRT1050RM>
8
9#![no_std]
10#![no_main]
11#![deny(missing_docs)]
12
13use core::ptr::addr_of_mut;
14
15use capsules_core::virtualizers::virtual_alarm::VirtualMuxAlarm;
16use components::gpio::GpioComponent;
17use kernel::capabilities;
18use kernel::component::Component;
19use kernel::debug;
20use kernel::debug::PanicResources;
21use kernel::hil::gpio::Configure;
22use kernel::hil::led::LedLow;
23use kernel::platform::chip::Chip;
24use kernel::platform::{KernelResources, SyscallDriverLookup};
25use kernel::utilities::single_thread_value::SingleThreadValue;
26use kernel::{create_capability, static_init};
27
28use imxrt10xx::iomuxc::DriveStrength;
29use imxrt10xx::iomuxc::MuxMode;
30use imxrt10xx::iomuxc::OpenDrainEn;
31use imxrt10xx::iomuxc::PadId;
32use imxrt10xx::iomuxc::PullKeepEn;
33use imxrt10xx::iomuxc::PullUpDown;
34use imxrt10xx::iomuxc::Sion;
35use imxrt10xx::iomuxc::Speed;
36
37// Unit Tests for drivers.
38// #[allow(dead_code)]
39// mod virtual_uart_rx_test;
40
41/// Support routines for debugging I/O.
42pub mod io;
43
44/// Defines a vector which contains the boot section
45pub mod boot_header;
46
47// Number of concurrent processes this platform supports.
48const NUM_PROCS: usize = 4;
49
50type ChipHw = imxrt10xx::chip::Imxrt10xx<imxrt10xx::chip::Imxrt10xxDefaultPeripherals>;
51type ProcessPrinterInUse = capsules_system::process_printer::ProcessPrinterText;
52
53/// Resources for when a board panics used by io.rs.
54static PANIC_RESOURCES: SingleThreadValue<PanicResources<ChipHw, ProcessPrinterInUse>> =
55    SingleThreadValue::new();
56
57// How should the kernel respond when a process faults.
58const FAULT_RESPONSE: capsules_system::process_policies::PanicFaultPolicy =
59    capsules_system::process_policies::PanicFaultPolicy {};
60
61// Manually setting the boot header section that contains the FCB header
62//
63// When compiling for a macOS host, the `link_section` attribute is elided as it
64// yields the following error: `mach-o section specifier requires a segment and
65// section separated by a comma`.
66#[cfg_attr(not(target_os = "macos"), link_section = ".boot_hdr")]
67#[used]
68static BOOT_HDR: [u8; 8192] = boot_header::BOOT_HDR;
69
70kernel::stack_size! {0x2000}
71
72type SchedulerInUse = components::sched::round_robin::RoundRobinComponentType;
73
74// const NUM_LEDS: usize = 1;
75
76/// A structure representing this platform that holds references to all
77/// capsules for this platform.
78struct Imxrt1050EVKB {
79    alarm: &'static capsules_core::alarm::AlarmDriver<
80        'static,
81        VirtualMuxAlarm<'static, imxrt10xx::gpt::Gpt1<'static>>,
82    >,
83    button: &'static capsules_core::button::Button<'static, imxrt10xx::gpio::Pin<'static>>,
84    console: &'static capsules_core::console::Console<'static>,
85    gpio: &'static capsules_core::gpio::GPIO<'static, imxrt10xx::gpio::Pin<'static>>,
86    ipc: kernel::ipc::IPC<{ NUM_PROCS as u8 }>,
87    led: &'static capsules_core::led::LedDriver<
88        'static,
89        LedLow<'static, imxrt10xx::gpio::Pin<'static>>,
90        1,
91    >,
92    ninedof: &'static capsules_extra::ninedof::NineDof<'static>,
93
94    scheduler: &'static SchedulerInUse,
95    systick: cortexm7::systick::SysTick,
96}
97
98/// Mapping of integer syscalls to objects that implement syscalls.
99impl SyscallDriverLookup for Imxrt1050EVKB {
100    fn with_driver<F, R>(&self, driver_num: usize, f: F) -> R
101    where
102        F: FnOnce(Option<&dyn kernel::syscall::SyscallDriver>) -> R,
103    {
104        match driver_num {
105            capsules_core::alarm::DRIVER_NUM => f(Some(self.alarm)),
106            capsules_core::button::DRIVER_NUM => f(Some(self.button)),
107            capsules_core::console::DRIVER_NUM => f(Some(self.console)),
108            capsules_core::gpio::DRIVER_NUM => f(Some(self.gpio)),
109            kernel::ipc::DRIVER_NUM => f(Some(&self.ipc)),
110            capsules_core::led::DRIVER_NUM => f(Some(self.led)),
111            capsules_extra::ninedof::DRIVER_NUM => f(Some(self.ninedof)),
112            _ => f(None),
113        }
114    }
115}
116
117impl KernelResources<imxrt10xx::chip::Imxrt10xx<imxrt10xx::chip::Imxrt10xxDefaultPeripherals>>
118    for Imxrt1050EVKB
119{
120    type SyscallDriverLookup = Self;
121    type SyscallFilter = ();
122    type ProcessFault = ();
123    type Scheduler = SchedulerInUse;
124    type SchedulerTimer = cortexm7::systick::SysTick;
125    type WatchDog = ();
126    type ContextSwitchCallback = ();
127
128    fn syscall_driver_lookup(&self) -> &Self::SyscallDriverLookup {
129        self
130    }
131    fn syscall_filter(&self) -> &Self::SyscallFilter {
132        &()
133    }
134    fn process_fault(&self) -> &Self::ProcessFault {
135        &()
136    }
137    fn scheduler(&self) -> &Self::Scheduler {
138        self.scheduler
139    }
140    fn scheduler_timer(&self) -> &Self::SchedulerTimer {
141        &self.systick
142    }
143    fn watchdog(&self) -> &Self::WatchDog {
144        &()
145    }
146    fn context_switch_callback(&self) -> &Self::ContextSwitchCallback {
147        &()
148    }
149}
150
151/// Helper function called during bring-up that configures multiplexed I/O.
152unsafe fn set_pin_primary_functions(
153    peripherals: &'static imxrt10xx::chip::Imxrt10xxDefaultPeripherals,
154) {
155    use imxrt10xx::gpio::PinId;
156
157    peripherals.ccm.enable_iomuxc_clock();
158    peripherals.ccm.enable_iomuxc_snvs_clock();
159
160    peripherals.ports.gpio1.enable_clock();
161
162    // User_LED is connected to GPIO_AD_B0_09.
163    // Values set accordingly to the evkbimxrt1050_iled_blinky SDK example
164
165    // First we configure the pin in GPIO mode and disable the Software Input
166    // on Field, so that the Input Path is determined by functionality.
167    peripherals.iomuxc.enable_sw_mux_ctl_pad_gpio(
168        PadId::AdB0,
169        MuxMode::ALT5, // ALT5 for AdB0_09: GPIO1_IO09 of instance: gpio1
170        Sion::Disabled,
171        9,
172    );
173
174    // Configure the pin resistance value, pull up or pull down and other
175    // physical aspects.
176    peripherals.iomuxc.configure_sw_pad_ctl_pad_gpio(
177        PadId::AdB0,
178        9,
179        PullUpDown::Pus0_100kOhmPullDown,   // 100K Ohm Pull Down
180        PullKeepEn::Pke1PullKeeperEnabled,  // Pull-down resistor or keep the previous value
181        OpenDrainEn::Ode0OpenDrainDisabled, // Output is CMOS, either 0 logic or 1 logic
182        Speed::Medium2,                     // Operating frequency: 100MHz - 150MHz
183        DriveStrength::DSE6, // Dual/Single voltage: 43/43 Ohm @ 1.8V, 40/26 Ohm @ 3.3V
184    );
185
186    // Configuring the GPIO_AD_B0_09 as output
187    let pin = peripherals.ports.pin(PinId::AdB0_09);
188    pin.make_output();
189    let debug_gpios = static_init!([&'static dyn kernel::hil::gpio::Pin; 1], [pin]);
190    kernel::debug::initialize_debug_gpio::<
191        <ChipHw as kernel::platform::chip::Chip>::ThreadIdProvider,
192    >();
193    kernel::debug::assign_gpios(debug_gpios);
194
195    // User_Button is connected to IOMUXC_SNVS_WAKEUP.
196    peripherals.ports.gpio5.enable_clock();
197
198    // We configure the pin in GPIO mode and disable the Software Input
199    // on Field, so that the Input Path is determined by functionality.
200    peripherals.iomuxc_snvs.enable_sw_mux_ctl_pad_gpio(
201        MuxMode::ALT5, // ALT5 for AdB0_09: GPIO5_IO00 of instance: gpio5
202        Sion::Disabled,
203        0,
204    );
205
206    // Configuring the IOMUXC_SNVS_WAKEUP pin as input
207    peripherals.ports.pin(PinId::Wakeup).make_input();
208}
209
210/// Helper function for miscellaneous peripheral functions
211unsafe fn setup_peripherals(peripherals: &imxrt10xx::chip::Imxrt10xxDefaultPeripherals) {
212    // LPUART1 IRQn is 20
213    cortexm7::nvic::Nvic::new(imxrt10xx::nvic::LPUART1).enable();
214
215    // TIM2 IRQn is 28
216    peripherals.gpt1.enable_clock();
217    peripherals.gpt1.start(
218        peripherals.ccm.perclk_sel(),
219        peripherals.ccm.perclk_divider(),
220    );
221    cortexm7::nvic::Nvic::new(imxrt10xx::nvic::GPT1).enable();
222}
223
224/// This is in a separate, inline(never) function so that its stack frame is
225/// removed when this function returns. Otherwise, the stack space used for
226/// these static_inits is wasted.
227#[inline(never)]
228unsafe fn start() -> (
229    &'static kernel::Kernel,
230    Imxrt1050EVKB,
231    &'static imxrt10xx::chip::Imxrt10xx<imxrt10xx::chip::Imxrt10xxDefaultPeripherals>,
232) {
233    ChipHw::init();
234
235    // Initialize deferred calls very early.
236    kernel::deferred_call::initialize_deferred_call_state::<
237        <ChipHw as kernel::platform::chip::Chip>::ThreadIdProvider,
238    >();
239
240    // Bind global variables to this thread.
241    let _ = PANIC_RESOURCES
242        .bind_to_thread::<<ChipHw as kernel::platform::chip::Chip>::ThreadIdProvider>(
243            PanicResources::new(),
244        );
245
246    let ccm = static_init!(imxrt10xx::ccm::Ccm, imxrt10xx::ccm::Ccm::new());
247    let peripherals = static_init!(
248        imxrt10xx::chip::Imxrt10xxDefaultPeripherals,
249        imxrt10xx::chip::Imxrt10xxDefaultPeripherals::new(ccm)
250    );
251    peripherals.ccm.set_low_power_mode();
252    peripherals.lpuart1.disable_clock();
253    peripherals.lpuart2.disable_clock();
254    peripherals
255        .ccm
256        .set_uart_clock_sel(imxrt10xx::ccm::UartClockSelection::PLL3);
257    peripherals.ccm.set_uart_clock_podf(1);
258    peripherals.lpuart1.set_baud();
259
260    set_pin_primary_functions(peripherals);
261
262    setup_peripherals(peripherals);
263
264    // Create an array to hold process references.
265    let processes = components::process_array::ProcessArrayComponent::new()
266        .finalize(components::process_array_component_static!(NUM_PROCS));
267    PANIC_RESOURCES.get().map(|resources| {
268        resources.processes.put(processes.as_slice());
269    });
270
271    // Setup space to store the core kernel data structure.
272    let board_kernel = static_init!(kernel::Kernel, kernel::Kernel::new(processes.as_slice()));
273
274    let chip = static_init!(ChipHw, ChipHw::new(peripherals));
275    PANIC_RESOURCES.get().map(|resources| {
276        resources.chip.put(chip);
277    });
278
279    // LPUART1
280
281    // Enable tx and rx from iomuxc
282    // TX is on pad GPIO_AD_B0_12
283    // RX is on pad GPIO_AD_B0_13
284    // Values set accordingly to the evkbimxrt1050_hello_world SDK example
285
286    // First we configure the pin in LPUART mode and disable the Software Input
287    // on Field, so that the Input Path is determined by functionality.
288    peripherals.iomuxc.enable_sw_mux_ctl_pad_gpio(
289        PadId::AdB0,
290        MuxMode::ALT2, // ALT2: LPUART1_TXD of instance: lpuart1
291        Sion::Disabled,
292        13,
293    );
294    peripherals.iomuxc.enable_sw_mux_ctl_pad_gpio(
295        PadId::AdB0,
296        MuxMode::ALT2, // ALT2: LPUART1_RXD of instance: lpuart1
297        Sion::Disabled,
298        14,
299    );
300
301    // Configure the pin resistance value, pull up or pull down and other
302    // physical aspects.
303    peripherals.iomuxc.configure_sw_pad_ctl_pad_gpio(
304        PadId::AdB0,
305        13,
306        PullUpDown::Pus0_100kOhmPullDown,   // 100K Ohm Pull Down
307        PullKeepEn::Pke1PullKeeperEnabled,  // Pull-down resistor or keep the previous value
308        OpenDrainEn::Ode0OpenDrainDisabled, // Output is CMOS, either 0 logic or 1 logic
309        Speed::Medium2,                     // Operating frequency: 100MHz - 150MHz
310        DriveStrength::DSE6, // Dual/Single voltage: 43/43 Ohm @ 1.8V, 40/26 Ohm @ 3.3V
311    );
312    peripherals.iomuxc.configure_sw_pad_ctl_pad_gpio(
313        PadId::AdB0,
314        14,
315        PullUpDown::Pus0_100kOhmPullDown,   // 100K Ohm Pull Down
316        PullKeepEn::Pke1PullKeeperEnabled,  // Pull-down resistor or keep the previous value
317        OpenDrainEn::Ode0OpenDrainDisabled, // Output is CMOS, either 0 logic or 1 logic
318        Speed::Medium2,                     // Operating frequency: 100MHz - 150MHz
319        DriveStrength::DSE6, // Dual/Single voltage: 43/43 Ohm @ 1.8V, 40/26 Ohm @ 3.3V
320    );
321
322    // Enable clock
323    peripherals.lpuart1.enable_clock();
324
325    let lpuart_mux = components::console::UartMuxComponent::new(&peripherals.lpuart1, 115200)
326        .finalize(components::uart_mux_component_static!());
327    (*addr_of_mut!(io::WRITER)).set_initialized();
328
329    // Create capabilities that the board needs to call certain protected kernel
330    // functions.
331    let memory_allocation_capability = create_capability!(capabilities::MemoryAllocationCapability);
332    let process_management_capability =
333        create_capability!(capabilities::ProcessManagementCapability);
334
335    // Setup the console.
336    let console = components::console::ConsoleComponent::new(
337        board_kernel,
338        capsules_core::console::DRIVER_NUM,
339        lpuart_mux,
340    )
341    .finalize(components::console_component_static!());
342    // Create the debugger object that handles calls to `debug!()`.
343    components::debug_writer::DebugWriterComponent::new::<
344        <ChipHw as kernel::platform::chip::Chip>::ThreadIdProvider,
345    >(
346        lpuart_mux,
347        create_capability!(capabilities::SetDebugWriterCapability),
348    )
349    .finalize(components::debug_writer_component_static!());
350
351    // LEDs
352
353    // Clock to Port A is enabled in `set_pin_primary_functions()
354    let led = components::led::LedsComponent::new().finalize(components::led_component_static!(
355        LedLow<'static, imxrt10xx::gpio::Pin<'static>>,
356        LedLow::new(peripherals.ports.pin(imxrt10xx::gpio::PinId::AdB0_09)),
357    ));
358
359    // BUTTONs
360    let button = components::button::ButtonComponent::new(
361        board_kernel,
362        capsules_core::button::DRIVER_NUM,
363        components::button_component_helper!(
364            imxrt10xx::gpio::Pin,
365            (
366                peripherals.ports.pin(imxrt10xx::gpio::PinId::Wakeup),
367                kernel::hil::gpio::ActivationMode::ActiveHigh,
368                kernel::hil::gpio::FloatingState::PullDown
369            )
370        ),
371    )
372    .finalize(components::button_component_static!(imxrt10xx::gpio::Pin));
373
374    // ALARM
375    let gpt1 = &peripherals.gpt1;
376    let mux_alarm = components::alarm::AlarmMuxComponent::new(gpt1).finalize(
377        components::alarm_mux_component_static!(imxrt10xx::gpt::Gpt1),
378    );
379
380    let alarm = components::alarm::AlarmDriverComponent::new(
381        board_kernel,
382        capsules_core::alarm::DRIVER_NUM,
383        mux_alarm,
384    )
385    .finalize(components::alarm_component_static!(imxrt10xx::gpt::Gpt1));
386
387    // GPIO
388    // For now we expose only two pins
389    let gpio = GpioComponent::new(
390        board_kernel,
391        capsules_core::gpio::DRIVER_NUM,
392        components::gpio_component_helper!(
393            imxrt10xx::gpio::Pin<'static>,
394            // The User Led
395            0 => peripherals.ports.pin(imxrt10xx::gpio::PinId::AdB0_09)
396        ),
397    )
398    .finalize(components::gpio_component_static!(
399        imxrt10xx::gpio::Pin<'static>
400    ));
401
402    // LPI2C
403    // AD_B1_00 is LPI2C1_SCL
404    // AD_B1_01 is LPI2C1_SDA
405    // Values set accordingly to the evkbimxrt1050_bubble_peripheral SDK example
406
407    // First we configure the pin in LPUART mode and enable the Software Input
408    // on Field, so that we force input path of the pad.
409    peripherals.iomuxc.enable_sw_mux_ctl_pad_gpio(
410        PadId::AdB1,
411        MuxMode::ALT3, // ALT3:  LPI2C1_SCL of instance: lpi2c1
412        Sion::Enabled,
413        0,
414    );
415    // Selecting AD_B1_00 for LPI2C1_SCL in the Daisy Chain.
416    peripherals.iomuxc.enable_lpi2c_scl_select_input();
417
418    peripherals.iomuxc.enable_sw_mux_ctl_pad_gpio(
419        PadId::AdB1,
420        MuxMode::ALT3, // ALT3:  LPI2C1_SDA of instance: lpi2c1
421        Sion::Enabled,
422        1,
423    );
424    // Selecting AD_B1_01 for LPI2C1_SDA in the Daisy Chain.
425    peripherals.iomuxc.enable_lpi2c_sda_select_input();
426
427    // Configure the pin resistance value, pull up or pull down and other
428    // physical aspects.
429    peripherals.iomuxc.configure_sw_pad_ctl_pad_gpio(
430        PadId::AdB1,
431        0,
432        PullUpDown::Pus3_22kOhmPullUp,     // 22K Ohm Pull Up
433        PullKeepEn::Pke1PullKeeperEnabled, // Pull-down resistor or keep the previous value
434        OpenDrainEn::Ode1OpenDrainEnabled, // Open Drain Enabled (Output is Open Drain)
435        Speed::Medium2,                    // Operating frequency: 100MHz - 150MHz
436        DriveStrength::DSE6, // Dual/Single voltage: 43/43 Ohm @ 1.8V, 40/26 Ohm @ 3.3V
437    );
438
439    peripherals.iomuxc.configure_sw_pad_ctl_pad_gpio(
440        PadId::AdB1,
441        1,
442        PullUpDown::Pus3_22kOhmPullUp,     // 22K Ohm Pull Up
443        PullKeepEn::Pke1PullKeeperEnabled, // Pull-down resistor or keep the previous value
444        OpenDrainEn::Ode1OpenDrainEnabled, // Open Drain Enabled (Output is Open Drain)
445        Speed::Medium2,                    // Operating frequency: 100MHz - 150MHz
446        DriveStrength::DSE6, // Dual/Single voltage: 43/43 Ohm @ 1.8V, 40/26 Ohm @ 3.3V
447    );
448
449    // Enabling the lpi2c1 clock and setting the speed.
450    peripherals.lpi2c1.enable_clock();
451    peripherals
452        .lpi2c1
453        .set_speed(imxrt10xx::lpi2c::Lpi2cSpeed::Speed100k, 8);
454
455    use imxrt10xx::gpio::PinId;
456    let mux_i2c = components::i2c::I2CMuxComponent::new(&peripherals.lpi2c1, None).finalize(
457        components::i2c_mux_component_static!(imxrt10xx::lpi2c::Lpi2c),
458    );
459
460    // Fxos8700 sensor
461    let fxos8700 = components::fxos8700::Fxos8700Component::new(
462        mux_i2c,
463        0x1f,
464        peripherals.ports.pin(PinId::AdB1_00),
465    )
466    .finalize(components::fxos8700_component_static!(
467        imxrt10xx::lpi2c::Lpi2c
468    ));
469
470    // Ninedof
471    let ninedof = components::ninedof::NineDofComponent::new(
472        board_kernel,
473        capsules_extra::ninedof::DRIVER_NUM,
474    )
475    .finalize(components::ninedof_component_static!(fxos8700));
476
477    let scheduler = components::sched::round_robin::RoundRobinComponent::new(processes)
478        .finalize(components::round_robin_component_static!(NUM_PROCS));
479
480    let imxrt1050 = Imxrt1050EVKB {
481        console,
482        ipc: kernel::ipc::IPC::new(
483            board_kernel,
484            kernel::ipc::DRIVER_NUM,
485            &memory_allocation_capability,
486        ),
487        led,
488        button,
489        ninedof,
490        alarm,
491        gpio,
492
493        scheduler,
494        systick: cortexm7::systick::SysTick::new_with_calibration(792_000_000),
495    };
496
497    // Optional kernel tests
498    //
499    // See comment in `boards/imix/src/main.rs`
500    // virtual_uart_rx_test::run_virtual_uart_receive(mux_uart);
501
502    //--------------------------------------------------------------------------
503    // Process Console
504    //---------------------------------------------------------------------------
505    let process_printer = components::process_printer::ProcessPrinterTextComponent::new()
506        .finalize(components::process_printer_text_component_static!());
507    PANIC_RESOURCES.get().map(|resources| {
508        resources.printer.put(process_printer);
509    });
510
511    let process_console = components::process_console::ProcessConsoleComponent::new(
512        board_kernel,
513        lpuart_mux,
514        mux_alarm,
515        process_printer,
516        None,
517    )
518    .finalize(components::process_console_component_static!(
519        imxrt10xx::gpt::Gpt1
520    ));
521    let _ = process_console.start();
522
523    debug!("Tock OS initialization complete. Entering main loop");
524
525    extern "C" {
526        /// Beginning of the ROM region containing app images.
527        ///
528        /// This symbol is defined in the linker script.
529        static _sapps: u8;
530        /// End of the ROM region containing app images.
531        ///
532        /// This symbol is defined in the linker script.
533        static _eapps: u8;
534        /// Beginning of the RAM region for app memory.
535        static mut _sappmem: u8;
536        /// End of the RAM region for app memory.
537        static _eappmem: u8;
538    }
539
540    kernel::process::load_processes(
541        board_kernel,
542        chip,
543        core::slice::from_raw_parts(
544            core::ptr::addr_of!(_sapps),
545            core::ptr::addr_of!(_eapps) as usize - core::ptr::addr_of!(_sapps) as usize,
546        ),
547        core::slice::from_raw_parts_mut(
548            core::ptr::addr_of_mut!(_sappmem),
549            core::ptr::addr_of!(_eappmem) as usize - core::ptr::addr_of!(_sappmem) as usize,
550        ),
551        &FAULT_RESPONSE,
552        &process_management_capability,
553    )
554    .unwrap_or_else(|err| {
555        debug!("Error loading processes!");
556        debug!("{:?}", err);
557    });
558
559    (board_kernel, imxrt1050, chip)
560}
561
562/// Main function called after RAM initialized.
563#[no_mangle]
564pub unsafe fn main() {
565    let main_loop_capability = create_capability!(capabilities::MainLoopCapability);
566
567    let (board_kernel, board, chip) = start();
568    board_kernel.kernel_loop(&board, chip, Some(&board.ipc), &main_loop_capability);
569}