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