hifive1/
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 SiFive HiFive1b RISC-V development platform.
6//!
7//! - <https://www.sifive.com/boards/hifive1-rev-b>
8//!
9//! This board file is only compatible with revision B of the HiFive1.
10
11#![no_std]
12#![no_main]
13
14use core::ptr::{addr_of, addr_of_mut};
15
16use capsules_core::virtualizers::virtual_alarm::{MuxAlarm, VirtualMuxAlarm};
17use e310_g002::interrupt_service::E310G002DefaultPeripherals;
18use kernel::capabilities;
19use kernel::component::Component;
20use kernel::hil;
21use kernel::hil::led::LedLow;
22use kernel::platform::chip::Chip;
23use kernel::platform::scheduler_timer::VirtualSchedulerTimer;
24use kernel::platform::{KernelResources, SyscallDriverLookup};
25use kernel::scheduler::cooperative::CooperativeSched;
26use kernel::utilities::registers::interfaces::ReadWriteable;
27use kernel::Kernel;
28use kernel::{create_capability, debug, static_init};
29use rv32i::csr;
30
31pub mod io;
32
33pub const NUM_PROCS: usize = 4;
34//
35// Actual memory for holding the active process structures. Need an empty list
36// at least.
37static mut PROCESSES: [Option<&'static dyn kernel::process::Process>; NUM_PROCS] =
38    [None; NUM_PROCS];
39
40// Reference to the chip for panic dumps.
41static mut CHIP: Option<&'static e310_g002::chip::E310x<E310G002DefaultPeripherals>> = None;
42// Reference to the process printer for panic dumps.
43static mut PROCESS_PRINTER: Option<&'static capsules_system::process_printer::ProcessPrinterText> =
44    None;
45
46// How should the kernel respond when a process faults.
47const FAULT_RESPONSE: capsules_system::process_policies::PanicFaultPolicy =
48    capsules_system::process_policies::PanicFaultPolicy {};
49
50/// Dummy buffer that causes the linker to reserve enough space for the stack.
51#[no_mangle]
52#[link_section = ".stack_buffer"]
53pub static mut STACK_MEMORY: [u8; 0x900] = [0; 0x900];
54
55/// A structure representing this platform that holds references to all
56/// capsules for this platform. We've included an alarm and console.
57struct HiFive1 {
58    led: &'static capsules_core::led::LedDriver<
59        'static,
60        LedLow<'static, sifive::gpio::GpioPin<'static>>,
61        3,
62    >,
63    console: &'static capsules_core::console::Console<'static>,
64    lldb: &'static capsules_core::low_level_debug::LowLevelDebug<
65        'static,
66        capsules_core::virtualizers::virtual_uart::UartDevice<'static>,
67    >,
68    alarm: &'static capsules_core::alarm::AlarmDriver<
69        'static,
70        VirtualMuxAlarm<'static, e310_g002::chip::E310xClint<'static>>,
71    >,
72    scheduler: &'static CooperativeSched<'static>,
73    scheduler_timer: &'static VirtualSchedulerTimer<
74        VirtualMuxAlarm<'static, e310_g002::chip::E310xClint<'static>>,
75    >,
76}
77
78/// Mapping of integer syscalls to objects that implement syscalls.
79impl SyscallDriverLookup for HiFive1 {
80    fn with_driver<F, R>(&self, driver_num: usize, f: F) -> R
81    where
82        F: FnOnce(Option<&dyn kernel::syscall::SyscallDriver>) -> R,
83    {
84        match driver_num {
85            capsules_core::led::DRIVER_NUM => f(Some(self.led)),
86            capsules_core::console::DRIVER_NUM => f(Some(self.console)),
87            capsules_core::alarm::DRIVER_NUM => f(Some(self.alarm)),
88            capsules_core::low_level_debug::DRIVER_NUM => f(Some(self.lldb)),
89            _ => f(None),
90        }
91    }
92}
93
94impl KernelResources<e310_g002::chip::E310x<'static, E310G002DefaultPeripherals<'static>>>
95    for HiFive1
96{
97    type SyscallDriverLookup = Self;
98    type SyscallFilter = ();
99    type ProcessFault = ();
100    type Scheduler = CooperativeSched<'static>;
101    type SchedulerTimer =
102        VirtualSchedulerTimer<VirtualMuxAlarm<'static, e310_g002::chip::E310xClint<'static>>>;
103    type WatchDog = ();
104    type ContextSwitchCallback = ();
105
106    fn syscall_driver_lookup(&self) -> &Self::SyscallDriverLookup {
107        self
108    }
109    fn syscall_filter(&self) -> &Self::SyscallFilter {
110        &()
111    }
112    fn process_fault(&self) -> &Self::ProcessFault {
113        &()
114    }
115    fn scheduler(&self) -> &Self::Scheduler {
116        self.scheduler
117    }
118    fn scheduler_timer(&self) -> &Self::SchedulerTimer {
119        self.scheduler_timer
120    }
121    fn watchdog(&self) -> &Self::WatchDog {
122        &()
123    }
124    fn context_switch_callback(&self) -> &Self::ContextSwitchCallback {
125        &()
126    }
127}
128
129/// For the HiFive1, if load_process is inlined, it leads to really large stack utilization in
130/// main. By wrapping it in a non-inlined function, this reduces the stack utilization once
131/// processes are running.
132#[inline(never)]
133fn load_processes_not_inlined<C: Chip>(board_kernel: &'static Kernel, chip: &'static C) {
134    // These symbols are defined in the linker script.
135    extern "C" {
136        /// Beginning of the ROM region containing app images.
137        static _sapps: u8;
138        /// End of the ROM region containing app images.
139        static _eapps: u8;
140        /// Beginning of the RAM region for app memory.
141        static mut _sappmem: u8;
142        /// End of the RAM region for app memory.
143        static _eappmem: u8;
144    }
145
146    let app_flash = unsafe {
147        core::slice::from_raw_parts(
148            core::ptr::addr_of!(_sapps),
149            core::ptr::addr_of!(_eapps) as usize - core::ptr::addr_of!(_sapps) as usize,
150        )
151    };
152
153    let app_memory = unsafe {
154        core::slice::from_raw_parts_mut(
155            core::ptr::addr_of_mut!(_sappmem),
156            core::ptr::addr_of!(_eappmem) as usize - core::ptr::addr_of!(_sappmem) as usize,
157        )
158    };
159
160    let process_mgmt_cap = create_capability!(capabilities::ProcessManagementCapability);
161    kernel::process::load_processes(
162        board_kernel,
163        chip,
164        app_flash,
165        app_memory,
166        unsafe { &mut *addr_of_mut!(PROCESSES) },
167        &FAULT_RESPONSE,
168        &process_mgmt_cap,
169    )
170    .unwrap_or_else(|err| {
171        debug!("Error loading processes!");
172        debug!("{:?}", err);
173    });
174}
175
176/// This is in a separate, inline(never) function so that its stack frame is
177/// removed when this function returns. Otherwise, the stack space used for
178/// these static_inits is wasted.
179#[inline(never)]
180unsafe fn start() -> (
181    &'static kernel::Kernel,
182    HiFive1,
183    &'static e310_g002::chip::E310x<'static, E310G002DefaultPeripherals<'static>>,
184) {
185    // only machine mode
186    rv32i::configure_trap_handler();
187
188    let peripherals = static_init!(
189        E310G002DefaultPeripherals,
190        E310G002DefaultPeripherals::new(344_000_000)
191    );
192
193    peripherals.init();
194
195    peripherals.e310x.watchdog.disable();
196    peripherals.e310x.rtc.disable();
197    peripherals.e310x.pwm0.disable();
198    peripherals.e310x.pwm1.disable();
199    peripherals.e310x.pwm2.disable();
200    peripherals.e310x.uart1.disable();
201
202    peripherals
203        .e310x
204        .prci
205        .set_clock_frequency(sifive::prci::ClockFrequency::Freq344Mhz);
206
207    let board_kernel = static_init!(kernel::Kernel, kernel::Kernel::new(&*addr_of!(PROCESSES)));
208
209    // Configure kernel debug gpios as early as possible
210    kernel::debug::assign_gpios(
211        Some(&peripherals.e310x.gpio_port[22]), // Red
212        None,
213        None,
214    );
215
216    // Create a shared UART channel for the console and for kernel debug.
217    let uart_mux = components::console::UartMuxComponent::new(&peripherals.e310x.uart0, 115200)
218        .finalize(components::uart_mux_component_static!());
219
220    // LEDs
221    let led = components::led::LedsComponent::new().finalize(components::led_component_static!(
222        LedLow<'static, sifive::gpio::GpioPin>,
223        LedLow::new(&peripherals.e310x.gpio_port[22]), // Red
224        LedLow::new(&peripherals.e310x.gpio_port[19]), // Green
225        LedLow::new(&peripherals.e310x.gpio_port[21]), // Blue
226    ));
227
228    peripherals.e310x.uart0.initialize_gpio_pins(
229        &peripherals.e310x.gpio_port[17],
230        &peripherals.e310x.gpio_port[16],
231    );
232
233    let hardware_timer = static_init!(
234        e310_g002::chip::E310xClint,
235        e310_g002::chip::E310xClint::new(&e310_g002::clint::CLINT_BASE)
236    );
237
238    // Create a shared virtualization mux layer on top of a single hardware
239    // alarm.
240    let mux_alarm = static_init!(
241        MuxAlarm<'static, e310_g002::chip::E310xClint>,
242        MuxAlarm::new(hardware_timer)
243    );
244    hil::time::Alarm::set_alarm_client(hardware_timer, mux_alarm);
245
246    // Alarm
247    let virtual_alarm_user = static_init!(
248        VirtualMuxAlarm<'static, e310_g002::chip::E310xClint>,
249        VirtualMuxAlarm::new(mux_alarm)
250    );
251    virtual_alarm_user.setup();
252
253    let systick_virtual_alarm = static_init!(
254        VirtualMuxAlarm<'static, e310_g002::chip::E310xClint>,
255        VirtualMuxAlarm::new(mux_alarm)
256    );
257    systick_virtual_alarm.setup();
258
259    let memory_allocation_cap = create_capability!(capabilities::MemoryAllocationCapability);
260    let alarm = static_init!(
261        capsules_core::alarm::AlarmDriver<
262            'static,
263            VirtualMuxAlarm<'static, e310_g002::chip::E310xClint>,
264        >,
265        capsules_core::alarm::AlarmDriver::new(
266            virtual_alarm_user,
267            board_kernel.create_grant(capsules_core::alarm::DRIVER_NUM, &memory_allocation_cap)
268        )
269    );
270    hil::time::Alarm::set_alarm_client(virtual_alarm_user, alarm);
271
272    let chip = static_init!(
273        e310_g002::chip::E310x<E310G002DefaultPeripherals>,
274        e310_g002::chip::E310x::new(peripherals, hardware_timer)
275    );
276    CHIP = Some(chip);
277
278    let process_printer = components::process_printer::ProcessPrinterTextComponent::new()
279        .finalize(components::process_printer_text_component_static!());
280    PROCESS_PRINTER = Some(process_printer);
281
282    let process_console = components::process_console::ProcessConsoleComponent::new(
283        board_kernel,
284        uart_mux,
285        mux_alarm,
286        process_printer,
287        None,
288    )
289    .finalize(components::process_console_component_static!(
290        e310_g002::chip::E310xClint
291    ));
292    let _ = process_console.start();
293
294    // Need to enable all interrupts for Tock Kernel
295    chip.enable_plic_interrupts();
296
297    // enable interrupts globally
298    csr::CSR
299        .mie
300        .modify(csr::mie::mie::mext::SET + csr::mie::mie::msoft::SET + csr::mie::mie::mtimer::SET);
301    csr::CSR.mstatus.modify(csr::mstatus::mstatus::mie::SET);
302
303    // Setup the console.
304    let console = components::console::ConsoleComponent::new(
305        board_kernel,
306        capsules_core::console::DRIVER_NUM,
307        uart_mux,
308    )
309    .finalize(components::console_component_static!());
310    // Create the debugger object that handles calls to `debug!()`.
311    const DEBUG_BUFFER_KB: usize = 1;
312    components::debug_writer::DebugWriterComponent::new(
313        uart_mux,
314        create_capability!(capabilities::SetDebugWriterCapability),
315    )
316    .finalize(components::debug_writer_component_static!(DEBUG_BUFFER_KB));
317
318    let lldb = components::lldb::LowLevelDebugComponent::new(
319        board_kernel,
320        capsules_core::low_level_debug::DRIVER_NUM,
321        uart_mux,
322    )
323    .finalize(components::low_level_debug_component_static!());
324
325    // Need two debug!() calls to actually test with QEMU. QEMU seems to have a
326    // much larger UART TX buffer (or it transmits faster). With a single call
327    // the entire message is printed to console even if the kernel loop does not run
328    debug!("HiFive1 initialization complete.");
329    debug!("Entering main loop.");
330
331    let scheduler =
332        components::sched::cooperative::CooperativeComponent::new(&*addr_of!(PROCESSES))
333            .finalize(components::cooperative_component_static!(NUM_PROCS));
334
335    let scheduler_timer = static_init!(
336        VirtualSchedulerTimer<VirtualMuxAlarm<'static, e310_g002::chip::E310xClint<'static>>>,
337        VirtualSchedulerTimer::new(systick_virtual_alarm)
338    );
339
340    let hifive1 = HiFive1 {
341        led,
342        console,
343        lldb,
344        alarm,
345        scheduler,
346        scheduler_timer,
347    };
348
349    load_processes_not_inlined(board_kernel, chip);
350
351    (board_kernel, hifive1, chip)
352}
353
354/// Main function called after RAM initialized.
355#[no_mangle]
356pub unsafe fn main() {
357    let main_loop_capability = create_capability!(capabilities::MainLoopCapability);
358
359    let (board_kernel, board, chip) = start();
360    board_kernel.kernel_loop(
361        &board,
362        chip,
363        None::<&kernel::ipc::IPC<0>>,
364        &main_loop_capability,
365    );
366}