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