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