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(
264        uart_mux,
265        create_capability!(capabilities::SetDebugWriterCapability),
266    )
267    .finalize(components::debug_writer_component_static!());
268
269    // Setup the console
270    let console = components::console::ConsoleComponent::new(
271        board_kernel,
272        capsules_core::console::DRIVER_NUM,
273        uart_mux,
274    )
275    .finalize(components::console_component_static!());
276
277    // LED
278    let led = components::led::LedsComponent::new().finalize(components::led_component_static!(
279        LedHigh<imxrt1060::gpio::Pin>,
280        LedHigh::new(peripherals.ports.pin(PinId::B0_03))
281    ));
282
283    // Alarm
284    let mux_alarm = components::alarm::AlarmMuxComponent::new(&peripherals.gpt1).finalize(
285        components::alarm_mux_component_static!(imxrt1060::gpt::Gpt1),
286    );
287    let alarm = components::alarm::AlarmDriverComponent::new(
288        board_kernel,
289        capsules_core::alarm::DRIVER_NUM,
290        mux_alarm,
291    )
292    .finalize(components::alarm_component_static!(imxrt1060::gpt::Gpt1));
293
294    //
295    // Capabilities
296    //
297    let memory_allocation_capability = create_capability!(capabilities::MemoryAllocationCapability);
298    let process_management_capability =
299        create_capability!(capabilities::ProcessManagementCapability);
300
301    let ipc = kernel::ipc::IPC::new(
302        board_kernel,
303        kernel::ipc::DRIVER_NUM,
304        &memory_allocation_capability,
305    );
306
307    let process_printer = components::process_printer::ProcessPrinterTextComponent::new()
308        .finalize(components::process_printer_text_component_static!());
309    PROCESS_PRINTER = Some(process_printer);
310
311    let scheduler = components::sched::round_robin::RoundRobinComponent::new(&*addr_of!(PROCESSES))
312        .finalize(components::round_robin_component_static!(NUM_PROCS));
313
314    //
315    // Platform
316    //
317    let teensy40 = Teensy40 {
318        led,
319        console,
320        ipc,
321        alarm,
322
323        scheduler,
324        systick: cortexm7::systick::SysTick::new_with_calibration(792_000_000),
325    };
326
327    //
328    // Kernel startup
329    //
330    extern "C" {
331        /// Beginning of the ROM region containing app images.
332        ///
333        /// This symbol is defined in the linker script.
334        static _sapps: u8;
335        /// End of the ROM region containing app images.
336        ///
337        /// This symbol is defined in the linker script.
338        static _eapps: u8;
339        /// Beginning of the RAM region for app memory.
340        static mut _sappmem: u8;
341        /// End of the RAM region for app memory.
342        static _eappmem: u8;
343    }
344
345    kernel::process::load_processes(
346        board_kernel,
347        chip,
348        core::slice::from_raw_parts(
349            core::ptr::addr_of!(_sapps),
350            core::ptr::addr_of!(_eapps) as usize - core::ptr::addr_of!(_sapps) as usize,
351        ),
352        core::slice::from_raw_parts_mut(
353            core::ptr::addr_of_mut!(_sappmem),
354            core::ptr::addr_of!(_eappmem) as usize - core::ptr::addr_of!(_sappmem) as usize,
355        ),
356        &mut *addr_of_mut!(PROCESSES),
357        &FAULT_RESPONSE,
358        &process_management_capability,
359    )
360    .unwrap();
361
362    (board_kernel, teensy40, chip)
363}
364
365/// Main function called after RAM initialized.
366#[no_mangle]
367pub unsafe fn main() {
368    let main_loop_capability = create_capability!(capabilities::MainLoopCapability);
369
370    let (board_kernel, platform, chip) = start();
371    board_kernel.kernel_loop(&platform, chip, Some(&platform.ipc), &main_loop_capability);
372}
373
374/// Space for the stack buffer
375///
376/// Justified in tock's `kernel_layout.ld`.
377#[no_mangle]
378#[link_section = ".stack_buffer"]
379#[used]
380static mut STACK_BUFFER: [u8; 0x2000] = [0; 0x2000];
381
382const FCB_SIZE: usize = core::mem::size_of::<fcb::FCB>();
383
384/// Buffer between FCB and IVT
385///
386/// The FCB is put at the start of flash. We then need to add a 4K buffer in between
387/// the start of flash to the IVT. This buffer provides that padding.
388///
389/// See justification for the `".stack_buffer"` section to understand why we need
390/// explicit padding for the FCB.
391#[no_mangle]
392#[link_section = ".fcb_buffer"]
393#[used]
394static mut FCB_BUFFER: [u8; 0x1000 - FCB_SIZE] = [0xFF; 0x1000 - FCB_SIZE];