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