esp32_c3_board/
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//! Board file for ESP32-C3 RISC-V development platform.
6//!
7
8#![no_std]
9#![no_main]
10#![feature(custom_test_frameworks)]
11#![test_runner(test_runner)]
12#![reexport_test_harness_main = "test_main"]
13
14use capsules_core::virtualizers::virtual_alarm::{MuxAlarm, VirtualMuxAlarm};
15use esp32_c3::chip::Esp32C3DefaultPeripherals;
16use kernel::capabilities;
17use kernel::component::Component;
18use kernel::debug::PanicResources;
19use kernel::platform::{KernelResources, SyscallDriverLookup};
20use kernel::scheduler::priority::PrioritySched;
21use kernel::utilities::registers::interfaces::ReadWriteable;
22use kernel::utilities::single_thread_value::SingleThreadValue;
23use kernel::{create_capability, debug, hil, static_init};
24use rv32i::csr;
25
26pub mod io;
27
28#[cfg(test)]
29mod tests;
30
31const NUM_PROCS: usize = 4;
32
33type ChipHw = esp32_c3::chip::Esp32C3<'static, Esp32C3DefaultPeripherals<'static>>;
34type AlarmHw = esp32_c3::timg::TimG<'static>;
35type SchedulerTimerHw =
36    components::virtual_scheduler_timer::VirtualSchedulerTimerNoMuxComponentType<AlarmHw>;
37type ProcessPrinterInUse = capsules_system::process_printer::ProcessPrinterText;
38
39/// Resources for when a board panics used by io.rs.
40static PANIC_RESOURCES: SingleThreadValue<PanicResources<ChipHw, ProcessPrinterInUse>> =
41    SingleThreadValue::new(PanicResources::new());
42
43// How should the kernel respond when a process faults.
44const FAULT_RESPONSE: capsules_system::process_policies::PanicFaultPolicy =
45    capsules_system::process_policies::PanicFaultPolicy {};
46
47// Test access to the peripherals
48#[cfg(test)]
49static mut PERIPHERALS: Option<&'static Esp32C3DefaultPeripherals> = None;
50// Test access to scheduler
51#[cfg(test)]
52static mut SCHEDULER: Option<&PrioritySched> = None;
53// Test access to board
54#[cfg(test)]
55static mut BOARD: Option<&'static kernel::Kernel> = None;
56// Test access to platform
57#[cfg(test)]
58static mut PLATFORM: Option<&'static Esp32C3Board> = None;
59// Test access to main loop capability
60#[cfg(test)]
61static mut MAIN_CAP: Option<&dyn kernel::capabilities::MainLoopCapability> = None;
62// Test access to alarm
63static mut ALARM: Option<&'static MuxAlarm<'static, esp32_c3::timg::TimG<'static>>> = None;
64
65kernel::stack_size! {0x900}
66
67type RngDriver = components::rng::RngComponentType<esp32_c3::rng::Rng<'static>>;
68type GpioHw = esp32::gpio::GpioPin<'static>;
69type LedHw = components::sk68xx::Sk68xxLedComponentType<GpioHw, 3>;
70type LedDriver = components::led::LedsComponentType<LedHw, 3>;
71type ButtonDriver = components::button::ButtonComponentType<GpioHw>;
72
73/// A structure representing this platform that holds references to all
74/// capsules for this platform. We've included an alarm and console.
75struct Esp32C3Board {
76    gpio: &'static capsules_core::gpio::GPIO<'static, esp32::gpio::GpioPin<'static>>,
77    console: &'static capsules_core::console::Console<'static>,
78    alarm: &'static capsules_core::alarm::AlarmDriver<
79        'static,
80        VirtualMuxAlarm<'static, esp32_c3::timg::TimG<'static>>,
81    >,
82    scheduler: &'static PrioritySched,
83    scheduler_timer: &'static SchedulerTimerHw,
84    rng: &'static RngDriver,
85    led: &'static LedDriver,
86    button: &'static ButtonDriver,
87}
88
89/// Mapping of integer syscalls to objects that implement syscalls.
90impl SyscallDriverLookup for Esp32C3Board {
91    fn with_driver<F, R>(&self, driver_num: usize, f: F) -> R
92    where
93        F: FnOnce(Option<&dyn kernel::syscall::SyscallDriver>) -> R,
94    {
95        match driver_num {
96            capsules_core::gpio::DRIVER_NUM => f(Some(self.gpio)),
97            capsules_core::console::DRIVER_NUM => f(Some(self.console)),
98            capsules_core::alarm::DRIVER_NUM => f(Some(self.alarm)),
99            capsules_core::rng::DRIVER_NUM => f(Some(self.rng)),
100            capsules_core::led::DRIVER_NUM => f(Some(self.led)),
101            capsules_core::button::DRIVER_NUM => f(Some(self.button)),
102            _ => f(None),
103        }
104    }
105}
106
107impl KernelResources<esp32_c3::chip::Esp32C3<'static, Esp32C3DefaultPeripherals<'static>>>
108    for Esp32C3Board
109{
110    type SyscallDriverLookup = Self;
111    type SyscallFilter = ();
112    type ProcessFault = ();
113    type ContextSwitchCallback = ();
114    type Scheduler = PrioritySched;
115    type SchedulerTimer = SchedulerTimerHw;
116    type WatchDog = ();
117
118    fn syscall_driver_lookup(&self) -> &Self::SyscallDriverLookup {
119        self
120    }
121    fn syscall_filter(&self) -> &Self::SyscallFilter {
122        &()
123    }
124    fn process_fault(&self) -> &Self::ProcessFault {
125        &()
126    }
127    fn scheduler(&self) -> &Self::Scheduler {
128        self.scheduler
129    }
130    fn scheduler_timer(&self) -> &Self::SchedulerTimer {
131        self.scheduler_timer
132    }
133    fn watchdog(&self) -> &Self::WatchDog {
134        &()
135    }
136    fn context_switch_callback(&self) -> &Self::ContextSwitchCallback {
137        &()
138    }
139}
140
141unsafe fn setup() -> (
142    &'static kernel::Kernel,
143    &'static Esp32C3Board,
144    &'static esp32_c3::chip::Esp32C3<'static, Esp32C3DefaultPeripherals<'static>>,
145    &'static Esp32C3DefaultPeripherals<'static>,
146) {
147    use esp32_c3::sysreg::{CpuFrequency, PllFrequency};
148
149    // only machine mode
150    esp32_c3::chip::configure_trap_handler();
151
152    // Initialize deferred calls very early.
153    kernel::deferred_call::initialize_deferred_call_state_unsafe::<
154        <ChipHw as kernel::platform::chip::Chip>::ThreadIdProvider,
155    >();
156
157    // Bind global variables to this thread.
158    PANIC_RESOURCES
159        .bind_to_thread_unsafe::<<ChipHw as kernel::platform::chip::Chip>::ThreadIdProvider>();
160
161    //
162    // PERIPHERALS
163    //
164
165    let peripherals = static_init!(Esp32C3DefaultPeripherals, Esp32C3DefaultPeripherals::new());
166
167    peripherals.timg0.disable_wdt();
168    peripherals.rtc_cntl.disable_wdt();
169    peripherals.rtc_cntl.disable_super_wdt();
170    peripherals.rtc_cntl.enable_fosc();
171    peripherals.sysreg.disable_timg0();
172    peripherals.sysreg.enable_timg0();
173
174    peripherals
175        .sysreg
176        .use_pll_clock_source(PllFrequency::MHz320, CpuFrequency::MHz160);
177
178    // initialise capabilities
179    let process_mgmt_cap = create_capability!(capabilities::ProcessManagementCapability);
180    let memory_allocation_cap = create_capability!(capabilities::MemoryAllocationCapability);
181
182    //
183    // BOARD SETUP AND PROCESSES
184    //
185
186    // Create an array to hold process references.
187    let processes = components::process_array::ProcessArrayComponent::new()
188        .finalize(components::process_array_component_static!(NUM_PROCS));
189    PANIC_RESOURCES.get().map(|resources| {
190        resources.processes.put(processes.as_slice());
191    });
192
193    // Setup space to store the core kernel data structure.
194    let board_kernel = static_init!(kernel::Kernel, kernel::Kernel::new(processes.as_slice()));
195
196    //
197    // UART
198    //
199
200    // Create a shared UART channel for the console and for kernel debug.
201    let uart_mux = components::console::UartMuxComponent::new(&peripherals.uart0, 115200)
202        .finalize(components::uart_mux_component_static!());
203
204    // Setup the console.
205    let console = components::console::ConsoleComponent::new(
206        board_kernel,
207        capsules_core::console::DRIVER_NUM,
208        uart_mux,
209    )
210    .finalize(components::console_component_static!());
211    // Create the debugger object that handles calls to `debug!()`.
212    components::debug_writer::DebugWriterComponent::new_unsafe(
213        uart_mux,
214        create_capability!(capabilities::SetDebugWriterCapability),
215        || unsafe {
216            kernel::debug::initialize_debug_writer_wrapper_unsafe::<
217                <ChipHw as kernel::platform::chip::Chip>::ThreadIdProvider,
218            >();
219        },
220    )
221    .finalize(components::debug_writer_component_static!());
222
223    // Create process printer for panic.
224    let process_printer = components::process_printer::ProcessPrinterTextComponent::new()
225        .finalize(components::process_printer_text_component_static!());
226    PANIC_RESOURCES.get().map(|resources| {
227        resources.printer.put(process_printer);
228    });
229
230    //
231    // GPIO
232    //
233
234    let gpio = components::gpio::GpioComponent::new(
235        board_kernel,
236        capsules_core::gpio::DRIVER_NUM,
237        components::gpio_component_helper!(
238            esp32::gpio::GpioPin,
239            0 => &peripherals.gpio[0],
240            1 => &peripherals.gpio[1],
241            2 => &peripherals.gpio[2],
242            3 => &peripherals.gpio[3],
243            4 => &peripherals.gpio[4],
244            5 => &peripherals.gpio[5],
245            6 => &peripherals.gpio[6],
246            7 => &peripherals.gpio[7],
247            8 => &peripherals.gpio[15]
248        ),
249    )
250    .finalize(components::gpio_component_static!(esp32::gpio::GpioPin));
251
252    //
253    // ALARM
254    //
255
256    // Create a shared virtualization mux layer on top of a single hardware
257    // alarm.
258    let mux_alarm = static_init!(
259        MuxAlarm<'static, esp32_c3::timg::TimG>,
260        MuxAlarm::new(&peripherals.timg0)
261    );
262    hil::time::Alarm::set_alarm_client(&peripherals.timg0, mux_alarm);
263
264    ALARM = Some(mux_alarm);
265
266    // Alarm
267    let virtual_alarm_user = static_init!(
268        VirtualMuxAlarm<'static, esp32_c3::timg::TimG>,
269        VirtualMuxAlarm::new(mux_alarm)
270    );
271    virtual_alarm_user.setup();
272
273    let alarm = static_init!(
274        capsules_core::alarm::AlarmDriver<'static, VirtualMuxAlarm<'static, esp32_c3::timg::TimG>>,
275        capsules_core::alarm::AlarmDriver::new(
276            virtual_alarm_user,
277            board_kernel.create_grant(capsules_core::alarm::DRIVER_NUM, &memory_allocation_cap)
278        )
279    );
280    hil::time::Alarm::set_alarm_client(virtual_alarm_user, alarm);
281
282    //
283    // LED
284    //
285
286    let led_gpio = &peripherals.gpio[8];
287    let sk68xx = components::sk68xx::Sk68xxComponent::new(led_gpio, rv32i::support::nop)
288        .finalize(components::sk68xx_component_static_esp32c3_160mhz!(GpioHw,));
289
290    let led = components::led::LedsComponent::new().finalize(components::led_component_static!(
291        LedHw,
292        capsules_extra::sk68xx::Sk68xxLed::new(sk68xx, 0), // red
293        capsules_extra::sk68xx::Sk68xxLed::new(sk68xx, 1), // green
294        capsules_extra::sk68xx::Sk68xxLed::new(sk68xx, 2), // blue
295    ));
296
297    //
298    // BUTTONS
299    //
300
301    let button_boot_gpio = &peripherals.gpio[9];
302    let button = components::button::ButtonComponent::new(
303        board_kernel,
304        capsules_core::button::DRIVER_NUM,
305        components::button_component_helper!(
306            GpioHw,
307            (
308                button_boot_gpio,
309                kernel::hil::gpio::ActivationMode::ActiveLow,
310                kernel::hil::gpio::FloatingState::PullUp
311            )
312        ),
313    )
314    .finalize(components::button_component_static!(GpioHw));
315
316    //
317    // SCHEDULER
318    //
319
320    let scheduler_timer_alarm = &peripherals.timg1;
321    let scheduler_timer =
322        components::virtual_scheduler_timer::VirtualSchedulerTimerNoMuxComponent::new(
323            scheduler_timer_alarm,
324        )
325        .finalize(components::virtual_scheduler_timer_no_mux_component_static!(AlarmHw));
326
327    let scheduler = components::sched::priority::PriorityComponent::new(board_kernel)
328        .finalize(components::priority_component_static!());
329
330    //
331    // PROCESS CONSOLE
332    //
333
334    let process_console = components::process_console::ProcessConsoleComponent::new(
335        board_kernel,
336        uart_mux,
337        mux_alarm,
338        process_printer,
339        None,
340    )
341    .finalize(components::process_console_component_static!(
342        esp32_c3::timg::TimG
343    ));
344    let _ = process_console.start();
345
346    //
347    // RNG
348    //
349
350    let rng = components::rng::RngComponent::new(
351        board_kernel,
352        capsules_core::rng::DRIVER_NUM,
353        &peripherals.rng,
354    )
355    .finalize(components::rng_component_static!(esp32_c3::rng::Rng));
356
357    //
358    // CHIP AND INTERRUPTS
359    //
360
361    let chip = static_init!(
362        esp32_c3::chip::Esp32C3<
363            Esp32C3DefaultPeripherals,
364        >,
365        esp32_c3::chip::Esp32C3::new(peripherals)
366    );
367    PANIC_RESOURCES.get().map(|resources| {
368        resources.chip.put(chip);
369    });
370
371    // Need to enable all interrupts for Tock Kernel
372    chip.map_pic_interrupts();
373    chip.enable_pic_interrupts();
374
375    // enable interrupts globally
376    csr::CSR.mstatus.modify(csr::mstatus::mstatus::mie::SET);
377
378    debug!("ESP32-C3 initialisation complete.");
379    debug!("Entering main loop.");
380
381    //
382    // LOAD PROCESSES
383    //
384
385    // These symbols are defined in the linker script.
386    extern "C" {
387        /// Beginning of the ROM region containing app images.
388        static _sapps: u8;
389        /// End of the ROM region containing app images.
390        static _eapps: u8;
391        /// Beginning of the RAM region for app memory.
392        static mut _sappmem: u8;
393        /// End of the RAM region for app memory.
394        static _eappmem: u8;
395    }
396
397    let esp32_c3_board = static_init!(
398        Esp32C3Board,
399        Esp32C3Board {
400            gpio,
401            console,
402            alarm,
403            scheduler,
404            scheduler_timer,
405            rng,
406            led,
407            button
408        }
409    );
410
411    kernel::process::load_processes(
412        board_kernel,
413        chip,
414        core::slice::from_raw_parts(
415            core::ptr::addr_of!(_sapps),
416            core::ptr::addr_of!(_eapps) as usize - core::ptr::addr_of!(_sapps) as usize,
417        ),
418        core::slice::from_raw_parts_mut(
419            core::ptr::addr_of_mut!(_sappmem),
420            core::ptr::addr_of!(_eappmem) as usize - core::ptr::addr_of!(_sappmem) as usize,
421        ),
422        &FAULT_RESPONSE,
423        &process_mgmt_cap,
424    )
425    .unwrap_or_else(|err| {
426        debug!("Error loading processes!");
427        debug!("{:?}", err);
428    });
429
430    peripherals.init();
431
432    (board_kernel, esp32_c3_board, chip, peripherals)
433}
434
435/// Main function.
436///
437/// This function is called from the arch crate after some very basic RISC-V
438/// setup and RAM initialization.
439#[no_mangle]
440pub unsafe fn main() {
441    #[cfg(test)]
442    test_main();
443
444    #[cfg(not(test))]
445    {
446        let (board_kernel, esp32_c3_board, chip, _peripherals) = setup();
447
448        let main_loop_cap = create_capability!(capabilities::MainLoopCapability);
449
450        board_kernel.kernel_loop(
451            esp32_c3_board,
452            chip,
453            None::<&kernel::ipc::IPC<0>>,
454            &main_loop_cap,
455        );
456    }
457}
458
459#[cfg(test)]
460use kernel::platform::watchdog::WatchDog;
461
462#[cfg(test)]
463fn test_runner(tests: &[&dyn Fn()]) {
464    unsafe {
465        let (board_kernel, esp32_c3_board, _chip, peripherals) = setup();
466
467        BOARD = Some(board_kernel);
468        PLATFORM = Some(&esp32_c3_board);
469        PERIPHERALS = Some(peripherals);
470        SCHEDULER = Some(
471            components::sched::priority::PriorityComponent::new(board_kernel)
472                .finalize(components::priority_component_static!()),
473        );
474        MAIN_CAP = Some(&create_capability!(capabilities::MainLoopCapability));
475
476        PLATFORM.map(|p| {
477            p.watchdog().setup();
478        });
479
480        for test in tests {
481            test();
482        }
483    }
484
485    loop {}
486}