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::debug::PanicResources;
17use kernel::hil;
18use kernel::platform::{KernelResources, SyscallDriverLookup};
19use kernel::scheduler::cooperative::CooperativeSched;
20use kernel::utilities::registers::interfaces::ReadWriteable;
21use kernel::utilities::single_thread_value::SingleThreadValue;
22use kernel::{create_capability, debug, static_init};
23use rv32i::csr;
24
25pub mod io;
26
27pub const NUM_PROCS: usize = 4;
28
29type ChipHw = e310_g003::chip::E310x<'static, E310G003DefaultPeripherals<'static>>;
30type AlarmHw = e310_g003::chip::E310xClint<'static>;
31type SchedulerTimerHw =
32    components::virtual_scheduler_timer::VirtualSchedulerTimerComponentType<AlarmHw>;
33type ProcessPrinterInUse = capsules_system::process_printer::ProcessPrinterText;
34
35/// Resources for when a board panics used by io.rs.
36static PANIC_RESOURCES: SingleThreadValue<PanicResources<ChipHw, ProcessPrinterInUse>> =
37    SingleThreadValue::new(PanicResources::new());
38
39// How should the kernel respond when a process faults.
40const FAULT_RESPONSE: capsules_system::process_policies::PanicFaultPolicy =
41    capsules_system::process_policies::PanicFaultPolicy {};
42
43kernel::stack_size! {0x1500}
44
45/// A structure representing this platform that holds references to all
46/// capsules for this platform. We've included an alarm and console.
47struct HiFiveInventor {
48    console: &'static capsules_core::console::Console<'static>,
49    lldb: &'static capsules_core::low_level_debug::LowLevelDebug<
50        'static,
51        capsules_core::virtualizers::virtual_uart::UartDevice<'static>,
52    >,
53    alarm: &'static capsules_core::alarm::AlarmDriver<
54        'static,
55        VirtualMuxAlarm<'static, e310_g003::chip::E310xClint<'static>>,
56    >,
57    scheduler: &'static CooperativeSched<'static>,
58    scheduler_timer: &'static SchedulerTimerHw,
59}
60
61/// Mapping of integer syscalls to objects that implement syscalls.
62impl SyscallDriverLookup for HiFiveInventor {
63    fn with_driver<F, R>(&self, driver_num: usize, f: F) -> R
64    where
65        F: FnOnce(Option<&dyn kernel::syscall::SyscallDriver>) -> R,
66    {
67        match driver_num {
68            capsules_core::console::DRIVER_NUM => f(Some(self.console)),
69            capsules_core::alarm::DRIVER_NUM => f(Some(self.alarm)),
70            capsules_core::low_level_debug::DRIVER_NUM => f(Some(self.lldb)),
71            _ => f(None),
72        }
73    }
74}
75
76impl KernelResources<e310_g003::chip::E310x<'static, E310G003DefaultPeripherals<'static>>>
77    for HiFiveInventor
78{
79    type SyscallDriverLookup = Self;
80    type SyscallFilter = ();
81    type ProcessFault = ();
82    type Scheduler = CooperativeSched<'static>;
83    type SchedulerTimer = SchedulerTimerHw;
84    type WatchDog = ();
85    type ContextSwitchCallback = ();
86
87    fn syscall_driver_lookup(&self) -> &Self::SyscallDriverLookup {
88        self
89    }
90    fn syscall_filter(&self) -> &Self::SyscallFilter {
91        &()
92    }
93    fn process_fault(&self) -> &Self::ProcessFault {
94        &()
95    }
96    fn scheduler(&self) -> &Self::Scheduler {
97        self.scheduler
98    }
99    fn scheduler_timer(&self) -> &Self::SchedulerTimer {
100        self.scheduler_timer
101    }
102    fn watchdog(&self) -> &Self::WatchDog {
103        &()
104    }
105    fn context_switch_callback(&self) -> &Self::ContextSwitchCallback {
106        &()
107    }
108}
109
110/// This is in a separate, inline(never) function so that its stack frame is
111/// removed when this function returns. Otherwise, the stack space used for
112/// these static_inits is wasted.
113#[inline(never)]
114unsafe fn start() -> (
115    &'static kernel::Kernel,
116    HiFiveInventor,
117    &'static e310_g003::chip::E310x<'static, E310G003DefaultPeripherals<'static>>,
118) {
119    // only machine mode
120    rv32i::configure_trap_handler();
121
122    // Initialize deferred calls very early.
123    kernel::deferred_call::initialize_deferred_call_state::<
124        <ChipHw as kernel::platform::chip::Chip>::ThreadIdProvider,
125    >();
126
127    // Bind global variables to this thread.
128    PANIC_RESOURCES.bind_to_thread::<<ChipHw as kernel::platform::chip::Chip>::ThreadIdProvider>();
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    // Create an array to hold process references.
164    let processes = components::process_array::ProcessArrayComponent::new()
165        .finalize(components::process_array_component_static!(NUM_PROCS));
166    PANIC_RESOURCES.get().map(|resources| {
167        resources.processes.put(processes.as_slice());
168    });
169
170    // Setup space to store the core kernel data structure.
171    let board_kernel = static_init!(kernel::Kernel, kernel::Kernel::new(processes.as_slice()));
172
173    // Create a shared UART channel for the console and for kernel debug.
174    let uart_mux = components::console::UartMuxComponent::new(&peripherals.e310x.uart0, 115200)
175        .finalize(components::uart_mux_component_static!());
176
177    let hardware_timer = static_init!(
178        e310_g003::chip::E310xClint,
179        e310_g003::chip::E310xClint::new(&e310_g003::clint::CLINT_BASE)
180    );
181
182    // Create a shared virtualization mux layer on top of a single hardware
183    // alarm.
184    let mux_alarm = static_init!(
185        MuxAlarm<'static, e310_g003::chip::E310xClint>,
186        MuxAlarm::new(hardware_timer)
187    );
188    hil::time::Alarm::set_alarm_client(hardware_timer, mux_alarm);
189
190    // Alarm
191    let virtual_alarm_user = static_init!(
192        VirtualMuxAlarm<'static, e310_g003::chip::E310xClint>,
193        VirtualMuxAlarm::new(mux_alarm)
194    );
195    virtual_alarm_user.setup();
196
197    let alarm = static_init!(
198        capsules_core::alarm::AlarmDriver<
199            'static,
200            VirtualMuxAlarm<'static, e310_g003::chip::E310xClint>,
201        >,
202        capsules_core::alarm::AlarmDriver::new(
203            virtual_alarm_user,
204            board_kernel.create_grant(capsules_core::alarm::DRIVER_NUM, &memory_allocation_cap)
205        )
206    );
207    hil::time::Alarm::set_alarm_client(virtual_alarm_user, alarm);
208
209    let chip = static_init!(
210        e310_g003::chip::E310x<E310G003DefaultPeripherals>,
211        e310_g003::chip::E310x::new(peripherals, hardware_timer)
212    );
213    PANIC_RESOURCES.get().map(|resources| {
214        resources.chip.put(chip);
215    });
216
217    let process_printer = components::process_printer::ProcessPrinterTextComponent::new()
218        .finalize(components::process_printer_text_component_static!());
219    PANIC_RESOURCES.get().map(|resources| {
220        resources.printer.put(process_printer);
221    });
222
223    let process_console = components::process_console::ProcessConsoleComponent::new(
224        board_kernel,
225        uart_mux,
226        mux_alarm,
227        process_printer,
228        None,
229    )
230    .finalize(components::process_console_component_static!(
231        e310_g003::chip::E310xClint
232    ));
233    let _ = process_console.start();
234
235    // Need to enable all interrupts for Tock Kernel
236    chip.enable_plic_interrupts();
237
238    // enable interrupts globally
239    csr::CSR
240        .mie
241        .modify(csr::mie::mie::mext::SET + csr::mie::mie::msoft::SET + csr::mie::mie::mtimer::SET);
242    csr::CSR.mstatus.modify(csr::mstatus::mstatus::mie::SET);
243
244    // Setup the console.
245    let console = components::console::ConsoleComponent::new(
246        board_kernel,
247        capsules_core::console::DRIVER_NUM,
248        uart_mux,
249    )
250    .finalize(components::console_component_static!());
251    // Create the debugger object that handles calls to `debug!()`.
252    components::debug_writer::DebugWriterComponent::new::<
253        <ChipHw as kernel::platform::chip::Chip>::ThreadIdProvider,
254    >(
255        uart_mux,
256        create_capability!(capabilities::SetDebugWriterCapability),
257    )
258    .finalize(components::debug_writer_component_static!());
259
260    let lldb = components::lldb::LowLevelDebugComponent::new(
261        board_kernel,
262        capsules_core::low_level_debug::DRIVER_NUM,
263        uart_mux,
264    )
265    .finalize(components::low_level_debug_component_static!());
266
267    debug!("HiFive1 initialization complete. Entering main loop.");
268
269    // These symbols are defined in the linker script.
270    extern "C" {
271        /// Beginning of the ROM region containing app images.
272        static _sapps: u8;
273        /// End of the ROM region containing app images.
274        static _eapps: u8;
275        /// Beginning of the RAM region for app memory.
276        static mut _sappmem: u8;
277        /// End of the RAM region for app memory.
278        static _eappmem: u8;
279    }
280
281    let scheduler = components::sched::cooperative::CooperativeComponent::new(processes)
282        .finalize(components::cooperative_component_static!(NUM_PROCS));
283
284    let scheduler_timer =
285        components::virtual_scheduler_timer::VirtualSchedulerTimerComponent::new(mux_alarm)
286            .finalize(components::virtual_scheduler_timer_component_static!(
287                AlarmHw
288            ));
289
290    let hifive1 = HiFiveInventor {
291        console,
292        lldb,
293        alarm,
294        scheduler,
295        scheduler_timer,
296    };
297    kernel::process::load_processes(
298        board_kernel,
299        chip,
300        core::slice::from_raw_parts(
301            core::ptr::addr_of!(_sapps),
302            core::ptr::addr_of!(_eapps) as usize - core::ptr::addr_of!(_sapps) as usize,
303        ),
304        core::slice::from_raw_parts_mut(
305            core::ptr::addr_of_mut!(_sappmem),
306            core::ptr::addr_of!(_eappmem) as usize - core::ptr::addr_of!(_sappmem) as usize,
307        ),
308        &FAULT_RESPONSE,
309        &process_mgmt_cap,
310    )
311    .unwrap_or_else(|err| {
312        debug!("Error loading processes!");
313        debug!("{:?}", err);
314    });
315
316    (board_kernel, hifive1, chip)
317}
318
319/// Main function called after RAM initialized.
320#[no_mangle]
321pub unsafe fn main() {
322    let main_loop_capability = create_capability!(capabilities::MainLoopCapability);
323
324    let (board_kernel, board, chip) = start();
325    board_kernel.kernel_loop(
326        &board,
327        chip,
328        None::<&kernel::ipc::IPC<0>>,
329        &main_loop_capability,
330    );
331}