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