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