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