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