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 Chip = imxrt1060::chip::Imxrt10xx<imxrt1060::chip::Imxrt10xxDefaultPeripherals>;
137static mut CHIP: Option<&'static Chip> = 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 Chip) {
180    imxrt1060::init();
181
182    let ccm = static_init!(imxrt1060::ccm::Ccm, imxrt1060::ccm::Ccm::new());
183    let peripherals = static_init!(
184        imxrt1060::chip::Imxrt10xxDefaultPeripherals,
185        imxrt1060::chip::Imxrt10xxDefaultPeripherals::new(ccm)
186    );
187
188    peripherals.ccm.set_low_power_mode();
189
190    peripherals.dcdc.clock().enable();
191    peripherals.dcdc.set_target_vdd_soc(1250);
192    set_arm_clock(peripherals.ccm, &peripherals.ccm_analog);
193    // IPG clock is 600MHz / 4 == 150MHz
194    peripherals.ccm.set_ipg_divider(4);
195
196    peripherals.lpuart1.disable_clock();
197    peripherals.lpuart2.disable_clock();
198    peripherals
199        .ccm
200        .set_uart_clock_sel(imxrt1060::ccm::UartClockSelection::PLL3);
201    peripherals.ccm.set_uart_clock_podf(1);
202
203    peripherals.ccm.enable_iomuxc_clock();
204    peripherals.ccm.enable_iomuxc_snvs_clock();
205
206    peripherals
207        .ccm
208        .set_perclk_sel(imxrt1060::ccm::PerclkClockSel::Oscillator);
209    peripherals.ccm.set_perclk_divider(8);
210
211    peripherals.ports.pin(PinId::B0_03).make_output();
212
213    // Pin 13 is an LED
214    peripherals
215        .iomuxc
216        .enable_sw_mux_ctl_pad_gpio(PadId::B0, MuxMode::ALT5, Sion::Disabled, 3);
217
218    // Pins 14 and 15 are UART TX and RX
219    peripherals
220        .iomuxc
221        .enable_sw_mux_ctl_pad_gpio(PadId::AdB1, MuxMode::ALT2, Sion::Disabled, 2);
222    peripherals
223        .iomuxc
224        .enable_sw_mux_ctl_pad_gpio(PadId::AdB1, MuxMode::ALT2, Sion::Disabled, 3);
225
226    peripherals.iomuxc.enable_lpuart2_tx_select_input();
227    peripherals.iomuxc.enable_lpuart2_rx_select_input();
228
229    peripherals.lpuart2.enable_clock();
230    peripherals.lpuart2.set_baud();
231
232    peripherals.gpt1.enable_clock();
233    peripherals.gpt1.start(
234        peripherals.ccm.perclk_sel(),
235        peripherals.ccm.perclk_divider(),
236    );
237
238    peripherals.dma.clock().enable();
239    peripherals.dma.reset_tcds();
240    peripherals
241        .lpuart2
242        .set_rx_dma_channel(&peripherals.dma.channels[dma_config::LPUART2_RX]);
243    peripherals
244        .lpuart2
245        .set_tx_dma_channel(&peripherals.dma.channels[dma_config::LPUART2_TX]);
246
247    cortexm7::nvic::Nvic::new(imxrt1060::nvic::GPT1).enable();
248    dma_config::enable_interrupts();
249
250    let chip = static_init!(Chip, Chip::new(peripherals));
251    CHIP = Some(chip);
252
253    // Start loading the kernel
254
255    // Create an array to hold process references.
256    let processes = components::process_array::ProcessArrayComponent::new()
257        .finalize(components::process_array_component_static!(NUM_PROCS));
258    PROCESSES = Some(processes);
259
260    // Setup space to store the core kernel data structure.
261    let board_kernel = static_init!(kernel::Kernel, kernel::Kernel::new(processes.as_slice()));
262
263    // TODO how many of these should there be...?
264
265    let uart_mux = components::console::UartMuxComponent::new(&peripherals.lpuart2, 115_200)
266        .finalize(components::uart_mux_component_static!());
267    // Create the debugger object that handles calls to `debug!()`
268    components::debug_writer::DebugWriterComponent::new(
269        uart_mux,
270        create_capability!(capabilities::SetDebugWriterCapability),
271    )
272    .finalize(components::debug_writer_component_static!());
273
274    // Setup the console
275    let console = components::console::ConsoleComponent::new(
276        board_kernel,
277        capsules_core::console::DRIVER_NUM,
278        uart_mux,
279    )
280    .finalize(components::console_component_static!());
281
282    // LED
283    let led = components::led::LedsComponent::new().finalize(components::led_component_static!(
284        LedHigh<imxrt1060::gpio::Pin>,
285        LedHigh::new(peripherals.ports.pin(PinId::B0_03))
286    ));
287
288    // Alarm
289    let mux_alarm = components::alarm::AlarmMuxComponent::new(&peripherals.gpt1).finalize(
290        components::alarm_mux_component_static!(imxrt1060::gpt::Gpt1),
291    );
292    let alarm = components::alarm::AlarmDriverComponent::new(
293        board_kernel,
294        capsules_core::alarm::DRIVER_NUM,
295        mux_alarm,
296    )
297    .finalize(components::alarm_component_static!(imxrt1060::gpt::Gpt1));
298
299    //
300    // Capabilities
301    //
302    let memory_allocation_capability = create_capability!(capabilities::MemoryAllocationCapability);
303    let process_management_capability =
304        create_capability!(capabilities::ProcessManagementCapability);
305
306    let ipc = kernel::ipc::IPC::new(
307        board_kernel,
308        kernel::ipc::DRIVER_NUM,
309        &memory_allocation_capability,
310    );
311
312    let process_printer = components::process_printer::ProcessPrinterTextComponent::new()
313        .finalize(components::process_printer_text_component_static!());
314    PROCESS_PRINTER = Some(process_printer);
315
316    let scheduler = components::sched::round_robin::RoundRobinComponent::new(processes)
317        .finalize(components::round_robin_component_static!(NUM_PROCS));
318
319    //
320    // Platform
321    //
322    let teensy40 = Teensy40 {
323        led,
324        console,
325        ipc,
326        alarm,
327
328        scheduler,
329        systick: cortexm7::systick::SysTick::new_with_calibration(792_000_000),
330    };
331
332    //
333    // Kernel startup
334    //
335    extern "C" {
336        /// Beginning of the ROM region containing app images.
337        ///
338        /// This symbol is defined in the linker script.
339        static _sapps: u8;
340        /// End of the ROM region containing app images.
341        ///
342        /// This symbol is defined in the linker script.
343        static _eapps: u8;
344        /// Beginning of the RAM region for app memory.
345        static mut _sappmem: u8;
346        /// End of the RAM region for app memory.
347        static _eappmem: u8;
348    }
349
350    kernel::process::load_processes(
351        board_kernel,
352        chip,
353        core::slice::from_raw_parts(
354            core::ptr::addr_of!(_sapps),
355            core::ptr::addr_of!(_eapps) as usize - core::ptr::addr_of!(_sapps) as usize,
356        ),
357        core::slice::from_raw_parts_mut(
358            core::ptr::addr_of_mut!(_sappmem),
359            core::ptr::addr_of!(_eappmem) as usize - core::ptr::addr_of!(_sappmem) as usize,
360        ),
361        &FAULT_RESPONSE,
362        &process_management_capability,
363    )
364    .unwrap();
365
366    (board_kernel, teensy40, chip)
367}
368
369/// Main function called after RAM initialized.
370#[no_mangle]
371pub unsafe fn main() {
372    let main_loop_capability = create_capability!(capabilities::MainLoopCapability);
373
374    let (board_kernel, platform, chip) = start();
375    board_kernel.kernel_loop(&platform, chip, Some(&platform.ipc), &main_loop_capability);
376}
377
378/// Space for the stack buffer
379///
380/// Justified in tock's `kernel_layout.ld`.
381#[no_mangle]
382#[link_section = ".stack_buffer"]
383#[used]
384static mut STACK_BUFFER: [u8; 0x2000] = [0; 0x2000];
385
386const FCB_SIZE: usize = core::mem::size_of::<fcb::FCB>();
387
388/// Buffer between FCB and IVT
389///
390/// The FCB is put at the start of flash. We then need to add a 4K buffer in between
391/// the start of flash to the IVT. This buffer provides that padding.
392///
393/// See justification for the `".stack_buffer"` section to understand why we need
394/// explicit padding for the FCB.
395#[no_mangle]
396#[link_section = ".fcb_buffer"]
397#[used]
398static mut FCB_BUFFER: [u8; 0x1000 - FCB_SIZE] = [0xFF; 0x1000 - FCB_SIZE];