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::platform::scheduler_timer::VirtualSchedulerTimer;
19use kernel::platform::{KernelResources, SyscallDriverLookup};
20use kernel::process::ProcessArray;
21use kernel::scheduler::priority::PrioritySched;
22use kernel::utilities::registers::interfaces::ReadWriteable;
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>>;
34
35/// Static variables used by io.rs.
36static mut PROCESSES: Option<&'static ProcessArray<NUM_PROCS>> = None;
37// Reference to the chip for panic dumps.
38static mut CHIP: Option<&'static esp32_c3::chip::Esp32C3<Esp32C3DefaultPeripherals>> = None;
39// Static reference to process printer for panic dumps.
40static mut PROCESS_PRINTER: Option<&'static capsules_system::process_printer::ProcessPrinterText> =
41    None;
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>>;
68
69/// A structure representing this platform that holds references to all
70/// capsules for this platform. We've included an alarm and console.
71struct Esp32C3Board {
72    gpio: &'static capsules_core::gpio::GPIO<'static, esp32::gpio::GpioPin<'static>>,
73    console: &'static capsules_core::console::Console<'static>,
74    alarm: &'static capsules_core::alarm::AlarmDriver<
75        'static,
76        VirtualMuxAlarm<'static, esp32_c3::timg::TimG<'static>>,
77    >,
78    scheduler: &'static PrioritySched,
79    scheduler_timer: &'static VirtualSchedulerTimer<esp32_c3::timg::TimG<'static>>,
80    rng: &'static RngDriver,
81}
82
83/// Mapping of integer syscalls to objects that implement syscalls.
84impl SyscallDriverLookup for Esp32C3Board {
85    fn with_driver<F, R>(&self, driver_num: usize, f: F) -> R
86    where
87        F: FnOnce(Option<&dyn kernel::syscall::SyscallDriver>) -> R,
88    {
89        match driver_num {
90            capsules_core::gpio::DRIVER_NUM => f(Some(self.gpio)),
91            capsules_core::console::DRIVER_NUM => f(Some(self.console)),
92            capsules_core::alarm::DRIVER_NUM => f(Some(self.alarm)),
93            capsules_core::rng::DRIVER_NUM => f(Some(self.rng)),
94            _ => f(None),
95        }
96    }
97}
98
99impl KernelResources<esp32_c3::chip::Esp32C3<'static, Esp32C3DefaultPeripherals<'static>>>
100    for Esp32C3Board
101{
102    type SyscallDriverLookup = Self;
103    type SyscallFilter = ();
104    type ProcessFault = ();
105    type ContextSwitchCallback = ();
106    type Scheduler = PrioritySched;
107    type SchedulerTimer = VirtualSchedulerTimer<esp32_c3::timg::TimG<'static>>;
108    type WatchDog = ();
109
110    fn syscall_driver_lookup(&self) -> &Self::SyscallDriverLookup {
111        self
112    }
113    fn syscall_filter(&self) -> &Self::SyscallFilter {
114        &()
115    }
116    fn process_fault(&self) -> &Self::ProcessFault {
117        &()
118    }
119    fn scheduler(&self) -> &Self::Scheduler {
120        self.scheduler
121    }
122    fn scheduler_timer(&self) -> &Self::SchedulerTimer {
123        self.scheduler_timer
124    }
125    fn watchdog(&self) -> &Self::WatchDog {
126        &()
127    }
128    fn context_switch_callback(&self) -> &Self::ContextSwitchCallback {
129        &()
130    }
131}
132
133unsafe fn setup() -> (
134    &'static kernel::Kernel,
135    &'static Esp32C3Board,
136    &'static esp32_c3::chip::Esp32C3<'static, Esp32C3DefaultPeripherals<'static>>,
137    &'static Esp32C3DefaultPeripherals<'static>,
138) {
139    use esp32_c3::sysreg::{CpuFrequency, PllFrequency};
140
141    // only machine mode
142    esp32_c3::chip::configure_trap_handler();
143
144    let peripherals = static_init!(Esp32C3DefaultPeripherals, Esp32C3DefaultPeripherals::new());
145
146    peripherals.timg0.disable_wdt();
147    peripherals.rtc_cntl.disable_wdt();
148    peripherals.rtc_cntl.disable_super_wdt();
149    peripherals.rtc_cntl.enable_fosc();
150    peripherals.sysreg.disable_timg0();
151    peripherals.sysreg.enable_timg0();
152
153    peripherals
154        .sysreg
155        .use_pll_clock_source(PllFrequency::MHz320, CpuFrequency::MHz160);
156
157    // initialise capabilities
158    let process_mgmt_cap = create_capability!(capabilities::ProcessManagementCapability);
159    let memory_allocation_cap = create_capability!(capabilities::MemoryAllocationCapability);
160
161    // Create an array to hold process references.
162    let processes = components::process_array::ProcessArrayComponent::new()
163        .finalize(components::process_array_component_static!(NUM_PROCS));
164    PROCESSES = Some(processes);
165
166    // Setup space to store the core kernel data structure.
167    let board_kernel = static_init!(kernel::Kernel, kernel::Kernel::new(processes.as_slice()));
168
169    // Configure kernel debug gpios as early as possible
170    kernel::debug::assign_gpios(None, None, None);
171
172    // Create a shared UART channel for the console and for kernel debug.
173    let uart_mux = components::console::UartMuxComponent::new(&peripherals.uart0, 115200)
174        .finalize(components::uart_mux_component_static!());
175
176    let gpio = components::gpio::GpioComponent::new(
177        board_kernel,
178        capsules_core::gpio::DRIVER_NUM,
179        components::gpio_component_helper!(
180            esp32::gpio::GpioPin,
181            0 => &peripherals.gpio[0],
182            1 => &peripherals.gpio[1],
183            2 => &peripherals.gpio[2],
184            3 => &peripherals.gpio[3],
185            4 => &peripherals.gpio[4],
186            5 => &peripherals.gpio[5],
187            6 => &peripherals.gpio[6],
188            7 => &peripherals.gpio[7],
189            8 => &peripherals.gpio[15]
190        ),
191    )
192    .finalize(components::gpio_component_static!(esp32::gpio::GpioPin));
193
194    // Create a shared virtualization mux layer on top of a single hardware
195    // alarm.
196    let mux_alarm = static_init!(
197        MuxAlarm<'static, esp32_c3::timg::TimG>,
198        MuxAlarm::new(&peripherals.timg0)
199    );
200    hil::time::Alarm::set_alarm_client(&peripherals.timg0, mux_alarm);
201
202    ALARM = Some(mux_alarm);
203
204    // Alarm
205    let virtual_alarm_user = static_init!(
206        VirtualMuxAlarm<'static, esp32_c3::timg::TimG>,
207        VirtualMuxAlarm::new(mux_alarm)
208    );
209    virtual_alarm_user.setup();
210
211    let alarm = static_init!(
212        capsules_core::alarm::AlarmDriver<'static, VirtualMuxAlarm<'static, esp32_c3::timg::TimG>>,
213        capsules_core::alarm::AlarmDriver::new(
214            virtual_alarm_user,
215            board_kernel.create_grant(capsules_core::alarm::DRIVER_NUM, &memory_allocation_cap)
216        )
217    );
218    hil::time::Alarm::set_alarm_client(virtual_alarm_user, alarm);
219
220    let scheduler_timer = static_init!(
221        VirtualSchedulerTimer<esp32_c3::timg::TimG<'static>>,
222        VirtualSchedulerTimer::new(&peripherals.timg1)
223    );
224
225    let chip = static_init!(
226        esp32_c3::chip::Esp32C3<
227            Esp32C3DefaultPeripherals,
228        >,
229        esp32_c3::chip::Esp32C3::new(peripherals)
230    );
231    CHIP = Some(chip);
232
233    // Need to enable all interrupts for Tock Kernel
234    chip.map_pic_interrupts();
235    chip.enable_pic_interrupts();
236
237    // enable interrupts globally
238    csr::CSR.mstatus.modify(csr::mstatus::mstatus::mie::SET);
239
240    // Setup the console.
241    let console = components::console::ConsoleComponent::new(
242        board_kernel,
243        capsules_core::console::DRIVER_NUM,
244        uart_mux,
245    )
246    .finalize(components::console_component_static!());
247    // Create the debugger object that handles calls to `debug!()`.
248    components::debug_writer::DebugWriterComponent::new_unsafe(
249        uart_mux,
250        create_capability!(capabilities::SetDebugWriterCapability),
251        || unsafe {
252            kernel::debug::initialize_debug_writer_wrapper_unsafe::<
253                <ChipHw as kernel::platform::chip::Chip>::ThreadIdProvider,
254            >();
255        },
256    )
257    .finalize(components::debug_writer_component_static!());
258
259    // Create process printer for panic.
260    let process_printer = components::process_printer::ProcessPrinterTextComponent::new()
261        .finalize(components::process_printer_text_component_static!());
262    PROCESS_PRINTER = Some(process_printer);
263
264    debug!("ESP32-C3 initialisation complete.");
265    debug!("Entering main loop.");
266
267    // These symbols are defined in the linker script.
268    extern "C" {
269        /// Beginning of the ROM region containing app images.
270        static _sapps: u8;
271        /// End of the ROM region containing app images.
272        static _eapps: u8;
273        /// Beginning of the RAM region for app memory.
274        static mut _sappmem: u8;
275        /// End of the RAM region for app memory.
276        static _eappmem: u8;
277    }
278
279    let scheduler = components::sched::priority::PriorityComponent::new(board_kernel)
280        .finalize(components::priority_component_static!());
281
282    // PROCESS CONSOLE
283    let process_console = components::process_console::ProcessConsoleComponent::new(
284        board_kernel,
285        uart_mux,
286        mux_alarm,
287        process_printer,
288        None,
289    )
290    .finalize(components::process_console_component_static!(
291        esp32_c3::timg::TimG
292    ));
293    let _ = process_console.start();
294
295    let rng = components::rng::RngComponent::new(
296        board_kernel,
297        capsules_core::rng::DRIVER_NUM,
298        &peripherals.rng,
299    )
300    .finalize(components::rng_component_static!(esp32_c3::rng::Rng));
301
302    let esp32_c3_board = static_init!(
303        Esp32C3Board,
304        Esp32C3Board {
305            gpio,
306            console,
307            alarm,
308            scheduler,
309            scheduler_timer,
310            rng,
311        }
312    );
313
314    kernel::process::load_processes(
315        board_kernel,
316        chip,
317        core::slice::from_raw_parts(
318            core::ptr::addr_of!(_sapps),
319            core::ptr::addr_of!(_eapps) as usize - core::ptr::addr_of!(_sapps) as usize,
320        ),
321        core::slice::from_raw_parts_mut(
322            core::ptr::addr_of_mut!(_sappmem),
323            core::ptr::addr_of!(_eappmem) as usize - core::ptr::addr_of!(_sappmem) as usize,
324        ),
325        &FAULT_RESPONSE,
326        &process_mgmt_cap,
327    )
328    .unwrap_or_else(|err| {
329        debug!("Error loading processes!");
330        debug!("{:?}", err);
331    });
332
333    peripherals.init();
334
335    (board_kernel, esp32_c3_board, chip, peripherals)
336}
337
338/// Main function.
339///
340/// This function is called from the arch crate after some very basic RISC-V
341/// setup and RAM initialization.
342#[no_mangle]
343pub unsafe fn main() {
344    #[cfg(test)]
345    test_main();
346
347    #[cfg(not(test))]
348    {
349        let (board_kernel, esp32_c3_board, chip, _peripherals) = setup();
350
351        let main_loop_cap = create_capability!(capabilities::MainLoopCapability);
352
353        board_kernel.kernel_loop(
354            esp32_c3_board,
355            chip,
356            None::<&kernel::ipc::IPC<0>>,
357            &main_loop_cap,
358        );
359    }
360}
361
362#[cfg(test)]
363use kernel::platform::watchdog::WatchDog;
364
365#[cfg(test)]
366fn test_runner(tests: &[&dyn Fn()]) {
367    unsafe {
368        let (board_kernel, esp32_c3_board, _chip, peripherals) = setup();
369
370        BOARD = Some(board_kernel);
371        PLATFORM = Some(&esp32_c3_board);
372        PERIPHERALS = Some(peripherals);
373        SCHEDULER = Some(
374            components::sched::priority::PriorityComponent::new(board_kernel)
375                .finalize(components::priority_component_static!()),
376        );
377        MAIN_CAP = Some(&create_capability!(capabilities::MainLoopCapability));
378
379        PLATFORM.map(|p| {
380            p.watchdog().setup();
381        });
382
383        for test in tests {
384            test();
385        }
386    }
387
388    loop {}
389}