teensy40/
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//! Teensy 4.0 Development Board
6//!
7//! System configuration
8//!
9//! - LED on pin 13
10//! - UART2 allocated for a debug console on pins 14 and 15
11//! - GPT1 is the alarm source
12
13#![no_std]
14#![no_main]
15
16mod fcb;
17mod io;
18
19use imxrt1060::gpio::PinId;
20use imxrt1060::iomuxc::{MuxMode, PadId, Sion};
21use imxrt10xx as imxrt1060;
22use kernel::capabilities;
23use kernel::component::Component;
24use kernel::debug::PanicResources;
25use kernel::hil::{gpio::Configure, led::LedHigh};
26use kernel::platform::chip::ClockInterface;
27use kernel::platform::{KernelResources, SyscallDriverLookup};
28use kernel::scheduler::round_robin::RoundRobinSched;
29use kernel::utilities::single_thread_value::SingleThreadValue;
30use kernel::{create_capability, static_init};
31
32/// Number of concurrent processes this platform supports
33const NUM_PROCS: usize = 4;
34
35type ChipHw = imxrt1060::chip::Imxrt10xx<imxrt1060::chip::Imxrt10xxDefaultPeripherals>;
36type ProcessPrinterInUse = capsules_system::process_printer::ProcessPrinterText;
37
38/// Resources for when a board panics used by io.rs.
39static PANIC_RESOURCES: SingleThreadValue<PanicResources<ChipHw, ProcessPrinterInUse>> =
40    SingleThreadValue::new(PanicResources::new());
41
42/// What should we do if a process faults?
43const FAULT_RESPONSE: capsules_system::process_policies::PanicFaultPolicy =
44    capsules_system::process_policies::PanicFaultPolicy {};
45
46/// Teensy 4 platform
47struct Teensy40 {
48    led: &'static capsules_core::led::LedDriver<
49        'static,
50        LedHigh<'static, imxrt1060::gpio::Pin<'static>>,
51        1,
52    >,
53    console: &'static capsules_core::console::Console<'static>,
54    ipc: kernel::ipc::IPC<{ NUM_PROCS as u8 }>,
55    alarm: &'static capsules_core::alarm::AlarmDriver<
56        'static,
57        capsules_core::virtualizers::virtual_alarm::VirtualMuxAlarm<
58            'static,
59            imxrt1060::gpt::Gpt1<'static>,
60        >,
61    >,
62
63    scheduler: &'static RoundRobinSched<'static>,
64    systick: cortexm7::systick::SysTick,
65}
66
67impl SyscallDriverLookup for Teensy40 {
68    fn with_driver<F, R>(&self, driver_num: usize, f: F) -> R
69    where
70        F: FnOnce(Option<&dyn kernel::syscall::SyscallDriver>) -> R,
71    {
72        match driver_num {
73            capsules_core::led::DRIVER_NUM => f(Some(self.led)),
74            capsules_core::console::DRIVER_NUM => f(Some(self.console)),
75            kernel::ipc::DRIVER_NUM => f(Some(&self.ipc)),
76            capsules_core::alarm::DRIVER_NUM => f(Some(self.alarm)),
77            _ => f(None),
78        }
79    }
80}
81
82impl KernelResources<imxrt1060::chip::Imxrt10xx<imxrt1060::chip::Imxrt10xxDefaultPeripherals>>
83    for Teensy40
84{
85    type SyscallDriverLookup = Self;
86    type SyscallFilter = ();
87    type ProcessFault = ();
88    type Scheduler = RoundRobinSched<'static>;
89    type SchedulerTimer = cortexm7::systick::SysTick;
90    type WatchDog = ();
91    type ContextSwitchCallback = ();
92
93    fn syscall_driver_lookup(&self) -> &Self::SyscallDriverLookup {
94        self
95    }
96    fn syscall_filter(&self) -> &Self::SyscallFilter {
97        &()
98    }
99    fn process_fault(&self) -> &Self::ProcessFault {
100        &()
101    }
102    fn scheduler(&self) -> &Self::Scheduler {
103        self.scheduler
104    }
105    fn scheduler_timer(&self) -> &Self::SchedulerTimer {
106        &self.systick
107    }
108    fn watchdog(&self) -> &Self::WatchDog {
109        &()
110    }
111    fn context_switch_callback(&self) -> &Self::ContextSwitchCallback {
112        &()
113    }
114}
115
116/// Static configurations for DMA channels.
117///
118/// All DMA channels must be unique.
119mod dma_config {
120    use super::imxrt1060::nvic;
121
122    /// DMA channel for LPUART2_RX (arbitrary).
123    pub const LPUART2_RX: usize = 7;
124    /// DMA channel for LPUART2_TX (arbitrary).
125    pub const LPUART2_TX: usize = 8;
126
127    /// Add your DMA interrupt vector numbers here.
128    const DMA_INTERRUPTS: &[u32] = &[nvic::DMA7_23, nvic::DMA8_24];
129
130    /// Enable DMA interrupts for the selected channels.
131    #[inline(always)]
132    pub fn enable_interrupts() {
133        DMA_INTERRUPTS
134            .iter()
135            .copied()
136            // Safety: creating NVIC vector in platform code. Vector is valid.
137            .map(|vector| unsafe { cortexm7::nvic::Nvic::new(vector) })
138            .for_each(|intr| intr.enable());
139    }
140}
141
142/// Set the ARM clock frequency to 600MHz
143///
144/// You should use this early in program initialization, before there's a chance
145/// for preemption.
146fn set_arm_clock(ccm: &imxrt1060::ccm::Ccm, ccm_analog: &imxrt1060::ccm_analog::CcmAnalog) {
147    use imxrt1060::ccm::{
148        PeripheralClock2Selection, PeripheralClockSelection, PrePeripheralClockSelection,
149    };
150
151    // Switch AHB clock root to 24MHz oscillator
152    ccm.set_peripheral_clock2_divider(1);
153    ccm.set_peripheral_clock2_selection(PeripheralClock2Selection::Oscillator);
154    ccm.set_peripheral_clock_selection(PeripheralClockSelection::PeripheralClock2Divided);
155
156    // Set PLL1 output frequency, which is
157    //
158    //      24MHz * DIV_SEL / 2
159    //
160    // 24MHz is from crystal oscillator.
161    // PLL1 output == 120MHz
162    ccm_analog.restart_pll1(100);
163
164    // ARM divider is right after the PLL1 output,
165    // bringing down the clock to 600MHz
166    ccm.set_arm_divider(2);
167
168    // Divider just before the AHB clock root
169    ccm.set_ahb_divider(1);
170
171    // Switch AHB clock (back) to PLL1
172    ccm.set_pre_peripheral_clock_selection(PrePeripheralClockSelection::Pll1);
173    ccm.set_peripheral_clock_selection(PeripheralClockSelection::PrePeripheralClock);
174}
175
176/// This is in a separate, inline(never) function so that its stack frame is
177/// removed when this function returns. Otherwise, the stack space used for
178/// these static_inits is wasted.
179#[inline(never)]
180unsafe fn start() -> (&'static kernel::Kernel, Teensy40, &'static ChipHw) {
181    imxrt1060::init();
182
183    // Initialize deferred calls very early.
184    kernel::deferred_call::initialize_deferred_call_state::<
185        <ChipHw as kernel::platform::chip::Chip>::ThreadIdProvider,
186    >();
187
188    // Bind global variables to this thread.
189    PANIC_RESOURCES.bind_to_thread::<<ChipHw as kernel::platform::chip::Chip>::ThreadIdProvider>();
190
191    let ccm = static_init!(imxrt1060::ccm::Ccm, imxrt1060::ccm::Ccm::new());
192    let peripherals = static_init!(
193        imxrt1060::chip::Imxrt10xxDefaultPeripherals,
194        imxrt1060::chip::Imxrt10xxDefaultPeripherals::new(ccm)
195    );
196
197    peripherals.ccm.set_low_power_mode();
198
199    peripherals.dcdc.clock().enable();
200    peripherals.dcdc.set_target_vdd_soc(1250);
201    set_arm_clock(peripherals.ccm, &peripherals.ccm_analog);
202    // IPG clock is 600MHz / 4 == 150MHz
203    peripherals.ccm.set_ipg_divider(4);
204
205    peripherals.lpuart1.disable_clock();
206    peripherals.lpuart2.disable_clock();
207    peripherals
208        .ccm
209        .set_uart_clock_sel(imxrt1060::ccm::UartClockSelection::PLL3);
210    peripherals.ccm.set_uart_clock_podf(1);
211
212    peripherals.ccm.enable_iomuxc_clock();
213    peripherals.ccm.enable_iomuxc_snvs_clock();
214
215    peripherals
216        .ccm
217        .set_perclk_sel(imxrt1060::ccm::PerclkClockSel::Oscillator);
218    peripherals.ccm.set_perclk_divider(8);
219
220    peripherals.ports.pin(PinId::B0_03).make_output();
221
222    // Pin 13 is an LED
223    peripherals
224        .iomuxc
225        .enable_sw_mux_ctl_pad_gpio(PadId::B0, MuxMode::ALT5, Sion::Disabled, 3);
226
227    // Pins 14 and 15 are UART TX and RX
228    peripherals
229        .iomuxc
230        .enable_sw_mux_ctl_pad_gpio(PadId::AdB1, MuxMode::ALT2, Sion::Disabled, 2);
231    peripherals
232        .iomuxc
233        .enable_sw_mux_ctl_pad_gpio(PadId::AdB1, MuxMode::ALT2, Sion::Disabled, 3);
234
235    peripherals.iomuxc.enable_lpuart2_tx_select_input();
236    peripherals.iomuxc.enable_lpuart2_rx_select_input();
237
238    peripherals.lpuart2.enable_clock();
239    peripherals.lpuart2.set_baud();
240
241    peripherals.gpt1.enable_clock();
242    peripherals.gpt1.start(
243        peripherals.ccm.perclk_sel(),
244        peripherals.ccm.perclk_divider(),
245    );
246
247    peripherals.dma.clock().enable();
248    peripherals.dma.reset_tcds();
249    peripherals
250        .lpuart2
251        .set_rx_dma_channel(&peripherals.dma.channels[dma_config::LPUART2_RX]);
252    peripherals
253        .lpuart2
254        .set_tx_dma_channel(&peripherals.dma.channels[dma_config::LPUART2_TX]);
255
256    cortexm7::nvic::Nvic::new(imxrt1060::nvic::GPT1).enable();
257    dma_config::enable_interrupts();
258
259    let chip = static_init!(ChipHw, ChipHw::new(peripherals));
260    PANIC_RESOURCES.get().map(|resources| {
261        resources.chip.put(chip);
262    });
263
264    // Start loading the kernel
265
266    // Create an array to hold process references.
267    let processes = components::process_array::ProcessArrayComponent::new()
268        .finalize(components::process_array_component_static!(NUM_PROCS));
269    PANIC_RESOURCES.get().map(|resources| {
270        resources.processes.put(processes.as_slice());
271    });
272
273    // Setup space to store the core kernel data structure.
274    let board_kernel = static_init!(kernel::Kernel, kernel::Kernel::new(processes.as_slice()));
275
276    // TODO how many of these should there be...?
277
278    let uart_mux = components::console::UartMuxComponent::new(&peripherals.lpuart2, 115_200)
279        .finalize(components::uart_mux_component_static!());
280    // Create the debugger object that handles calls to `debug!()`
281    components::debug_writer::DebugWriterComponent::new::<
282        <ChipHw as kernel::platform::chip::Chip>::ThreadIdProvider,
283    >(
284        uart_mux,
285        create_capability!(capabilities::SetDebugWriterCapability),
286    )
287    .finalize(components::debug_writer_component_static!());
288
289    // Setup the console
290    let console = components::console::ConsoleComponent::new(
291        board_kernel,
292        capsules_core::console::DRIVER_NUM,
293        uart_mux,
294    )
295    .finalize(components::console_component_static!());
296
297    // LED
298    let led = components::led::LedsComponent::new().finalize(components::led_component_static!(
299        LedHigh<imxrt1060::gpio::Pin>,
300        LedHigh::new(peripherals.ports.pin(PinId::B0_03))
301    ));
302
303    // Alarm
304    let mux_alarm = components::alarm::AlarmMuxComponent::new(&peripherals.gpt1).finalize(
305        components::alarm_mux_component_static!(imxrt1060::gpt::Gpt1),
306    );
307    let alarm = components::alarm::AlarmDriverComponent::new(
308        board_kernel,
309        capsules_core::alarm::DRIVER_NUM,
310        mux_alarm,
311    )
312    .finalize(components::alarm_component_static!(imxrt1060::gpt::Gpt1));
313
314    //
315    // Capabilities
316    //
317    let memory_allocation_capability = create_capability!(capabilities::MemoryAllocationCapability);
318    let process_management_capability =
319        create_capability!(capabilities::ProcessManagementCapability);
320
321    let ipc = kernel::ipc::IPC::new(
322        board_kernel,
323        kernel::ipc::DRIVER_NUM,
324        &memory_allocation_capability,
325    );
326
327    let process_printer = components::process_printer::ProcessPrinterTextComponent::new()
328        .finalize(components::process_printer_text_component_static!());
329    PANIC_RESOURCES.get().map(|resources| {
330        resources.printer.put(process_printer);
331    });
332
333    let scheduler = components::sched::round_robin::RoundRobinComponent::new(processes)
334        .finalize(components::round_robin_component_static!(NUM_PROCS));
335
336    //
337    // Platform
338    //
339    let teensy40 = Teensy40 {
340        led,
341        console,
342        ipc,
343        alarm,
344
345        scheduler,
346        systick: cortexm7::systick::SysTick::new_with_calibration(792_000_000),
347    };
348
349    //
350    // Kernel startup
351    //
352    extern "C" {
353        /// Beginning of the ROM region containing app images.
354        ///
355        /// This symbol is defined in the linker script.
356        static _sapps: u8;
357        /// End of the ROM region containing app images.
358        ///
359        /// This symbol is defined in the linker script.
360        static _eapps: u8;
361        /// Beginning of the RAM region for app memory.
362        static mut _sappmem: u8;
363        /// End of the RAM region for app memory.
364        static _eappmem: u8;
365    }
366
367    kernel::process::load_processes(
368        board_kernel,
369        chip,
370        core::slice::from_raw_parts(
371            core::ptr::addr_of!(_sapps),
372            core::ptr::addr_of!(_eapps) as usize - core::ptr::addr_of!(_sapps) as usize,
373        ),
374        core::slice::from_raw_parts_mut(
375            core::ptr::addr_of_mut!(_sappmem),
376            core::ptr::addr_of!(_eappmem) as usize - core::ptr::addr_of!(_sappmem) as usize,
377        ),
378        &FAULT_RESPONSE,
379        &process_management_capability,
380    )
381    .unwrap();
382
383    (board_kernel, teensy40, chip)
384}
385
386/// Main function called after RAM initialized.
387#[no_mangle]
388pub unsafe fn main() {
389    let main_loop_capability = create_capability!(capabilities::MainLoopCapability);
390
391    let (board_kernel, platform, chip) = start();
392    board_kernel.kernel_loop(&platform, chip, Some(&platform.ipc), &main_loop_capability);
393}
394
395kernel::stack_size! {0x2000}
396
397const FCB_SIZE: usize = core::mem::size_of::<fcb::FCB>();
398
399/// Buffer between FCB and IVT
400///
401/// The FCB is put at the start of flash. We then need to add a 4K buffer in between
402/// the start of flash to the IVT. This buffer provides that padding.
403///
404/// See justification for the `".stack_buffer"` section to understand why we need
405/// explicit padding for the FCB.
406///
407/// When compiling for a macOS host, the `link_section` attribute is elided as
408/// it yields the following error: `mach-o section specifier requires a segment
409/// and section separated by a comma`.
410#[cfg_attr(not(target_os = "macos"), link_section = ".fcb_buffer")]
411#[no_mangle]
412#[used]
413static mut FCB_BUFFER: [u8; 0x1000 - FCB_SIZE] = [0xFF; 0x1000 - FCB_SIZE];