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