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