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