litex_arty/
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 a LiteX-built VexRiscv-based SoC synthesized for a
6//! Digilent Arty-A7 FPGA board
7
8#![no_std]
9#![no_main]
10
11use capsules_core::virtualizers::virtual_alarm::{MuxAlarm, VirtualMuxAlarm};
12use kernel::capabilities;
13use kernel::component::Component;
14use kernel::hil::time::{Alarm, Timer};
15use kernel::platform::chip::InterruptService;
16use kernel::platform::{KernelResources, SyscallDriverLookup};
17use kernel::process::ProcessArray;
18use kernel::scheduler::mlfq::MLFQSched;
19use kernel::utilities::registers::interfaces::ReadWriteable;
20use kernel::utilities::StaticRef;
21use kernel::{create_capability, debug, static_init};
22use rv32i::csr;
23
24mod io;
25mod litex_generated_constants;
26
27// This module contains the LiteX SoC configuration options, register
28// positions, interrupt mappings and other implementation details of
29// the generated bitstream.
30//
31// Its values are used throughout the file, hence import it under a
32// short name.
33use litex_generated_constants as socc;
34
35/// Structure for dynamic interrupt mapping, depending on the SoC
36/// configuration
37///
38/// This struct is deliberately kept in the board crate. Because of
39/// the configurable nature of LiteX, it does not make sense to define
40/// a default interrupt mapping, as the interrupt numbers are
41/// generated sequentially for all softcores.
42struct LiteXArtyInterruptablePeripherals {
43    uart0: &'static litex_vexriscv::uart::LiteXUart<'static, socc::SoCRegisterFmt>,
44    timer0: &'static litex_vexriscv::timer::LiteXTimer<
45        'static,
46        socc::SoCRegisterFmt,
47        socc::ClockFrequency,
48    >,
49    ethmac0: &'static litex_vexriscv::liteeth::LiteEth<
50        'static,
51        { socc::ETHMAC_TX_SLOTS },
52        socc::SoCRegisterFmt,
53    >,
54}
55
56impl LiteXArtyInterruptablePeripherals {
57    // Resolve any recursive dependencies and set up deferred calls:
58    pub fn init(&'static self) {
59        kernel::deferred_call::DeferredCallClient::register(self.uart0);
60    }
61}
62
63impl InterruptService for LiteXArtyInterruptablePeripherals {
64    unsafe fn service_interrupt(&self, interrupt: u32) -> bool {
65        match interrupt as usize {
66            socc::UART_INTERRUPT => {
67                self.uart0.service_interrupt();
68                true
69            }
70            socc::TIMER0_INTERRUPT => {
71                self.timer0.service_interrupt();
72                true
73            }
74            socc::ETHMAC_INTERRUPT => {
75                self.ethmac0.service_interrupt();
76                true
77            }
78            _ => false,
79        }
80    }
81}
82
83const NUM_PROCS: usize = 4;
84
85type ChipHw = litex_vexriscv::chip::LiteXVexRiscv<LiteXArtyInterruptablePeripherals>;
86type AlarmHw =
87    litex_vexriscv::timer::LiteXAlarm<'static, 'static, socc::SoCRegisterFmt, socc::ClockFrequency>;
88type SchedulerTimerHw =
89    components::virtual_scheduler_timer::VirtualSchedulerTimerComponentType<AlarmHw>;
90
91/// Static variables used by io.rs.
92static mut PROCESSES: Option<&'static ProcessArray<NUM_PROCS>> = None;
93
94// Reference to the chip, led controller, UART hardware, and process printer for
95// panic dumps.
96struct LiteXArtyPanicReferences {
97    chip: Option<&'static litex_vexriscv::chip::LiteXVexRiscv<LiteXArtyInterruptablePeripherals>>,
98    uart: Option<&'static litex_vexriscv::uart::LiteXUart<'static, socc::SoCRegisterFmt>>,
99    led_controller:
100        Option<&'static litex_vexriscv::led_controller::LiteXLedController<socc::SoCRegisterFmt>>,
101    process_printer: Option<&'static capsules_system::process_printer::ProcessPrinterText>,
102}
103static mut PANIC_REFERENCES: LiteXArtyPanicReferences = LiteXArtyPanicReferences {
104    chip: None,
105    uart: None,
106    led_controller: None,
107    process_printer: None,
108};
109
110// How should the kernel respond when a process faults.
111const FAULT_RESPONSE: capsules_system::process_policies::PanicFaultPolicy =
112    capsules_system::process_policies::PanicFaultPolicy {};
113
114kernel::stack_size! {0x2000}
115
116/// A structure representing this platform that holds references to all
117/// capsules for this platform.
118struct LiteXArty {
119    led_driver: &'static capsules_core::led::LedDriver<
120        'static,
121        litex_vexriscv::led_controller::LiteXLed<'static, socc::SoCRegisterFmt>,
122        4,
123    >,
124    console: &'static capsules_core::console::Console<'static>,
125    pconsole: &'static capsules_core::process_console::ProcessConsole<
126        'static,
127        { capsules_core::process_console::DEFAULT_COMMAND_HISTORY_LEN },
128        VirtualMuxAlarm<'static, AlarmHw>,
129        components::process_console::Capability,
130    >,
131    lldb: &'static capsules_core::low_level_debug::LowLevelDebug<
132        'static,
133        capsules_core::virtualizers::virtual_uart::UartDevice<'static>,
134    >,
135    alarm: &'static capsules_core::alarm::AlarmDriver<'static, VirtualMuxAlarm<'static, AlarmHw>>,
136    ipc: kernel::ipc::IPC<{ NUM_PROCS as u8 }>,
137    scheduler: &'static MLFQSched<'static, VirtualMuxAlarm<'static, AlarmHw>>,
138    scheduler_timer: &'static SchedulerTimerHw,
139}
140
141/// Mapping of integer syscalls to objects that implement syscalls
142impl SyscallDriverLookup for LiteXArty {
143    fn with_driver<F, R>(&self, driver_num: usize, f: F) -> R
144    where
145        F: FnOnce(Option<&dyn kernel::syscall::SyscallDriver>) -> R,
146    {
147        match driver_num {
148            capsules_core::led::DRIVER_NUM => f(Some(self.led_driver)),
149            capsules_core::console::DRIVER_NUM => f(Some(self.console)),
150            capsules_core::alarm::DRIVER_NUM => f(Some(self.alarm)),
151            capsules_core::low_level_debug::DRIVER_NUM => f(Some(self.lldb)),
152            kernel::ipc::DRIVER_NUM => f(Some(&self.ipc)),
153            _ => f(None),
154        }
155    }
156}
157
158impl KernelResources<litex_vexriscv::chip::LiteXVexRiscv<LiteXArtyInterruptablePeripherals>>
159    for LiteXArty
160{
161    type SyscallDriverLookup = Self;
162    type SyscallFilter = ();
163    type ProcessFault = ();
164    type Scheduler = MLFQSched<'static, VirtualMuxAlarm<'static, AlarmHw>>;
165    type SchedulerTimer = SchedulerTimerHw;
166    type WatchDog = ();
167    type ContextSwitchCallback = ();
168
169    fn syscall_driver_lookup(&self) -> &Self::SyscallDriverLookup {
170        self
171    }
172    fn syscall_filter(&self) -> &Self::SyscallFilter {
173        &()
174    }
175    fn process_fault(&self) -> &Self::ProcessFault {
176        &()
177    }
178    fn scheduler(&self) -> &Self::Scheduler {
179        self.scheduler
180    }
181    fn scheduler_timer(&self) -> &Self::SchedulerTimer {
182        self.scheduler_timer
183    }
184    fn watchdog(&self) -> &Self::WatchDog {
185        &()
186    }
187    fn context_switch_callback(&self) -> &Self::ContextSwitchCallback {
188        &()
189    }
190}
191
192/// This is in a separate, inline(never) function so that its stack frame is
193/// removed when this function returns. Otherwise, the stack space used for
194/// these static_inits is wasted.
195#[inline(never)]
196unsafe fn start() -> (
197    &'static kernel::Kernel,
198    LiteXArty,
199    &'static litex_vexriscv::chip::LiteXVexRiscv<LiteXArtyInterruptablePeripherals>,
200) {
201    // These symbols are defined in the linker script.
202    extern "C" {
203        /// Beginning of the ROM region containing app images.
204        static _sapps: u8;
205        /// End of the ROM region containing app images.
206        static _eapps: u8;
207        /// Beginning of the RAM region for app memory.
208        static mut _sappmem: u8;
209        /// End of the RAM region for app memory.
210        static _eappmem: u8;
211        /// The start of the kernel text (Included only for kernel PMP)
212        static _stext: u8;
213        /// The end of the kernel text (Included only for kernel PMP)
214        static _etext: u8;
215        /// The start of the kernel / app / storage flash (Included only for kernel PMP)
216        static _sflash: u8;
217        /// The end of the kernel / app / storage flash (Included only for kernel PMP)
218        static _eflash: u8;
219        /// The start of the kernel / app RAM (Included only for kernel PMP)
220        static _ssram: u8;
221        /// The end of the kernel / app RAM (Included only for kernel PMP)
222        static _esram: u8;
223    }
224
225    // ---------- BASIC INITIALIZATION ----------
226
227    // Basic setup of the riscv platform.
228    rv32i::configure_trap_handler();
229
230    // Initialize deferred calls very early.
231    kernel::deferred_call::initialize_deferred_call_state_unsafe::<
232        <ChipHw as kernel::platform::chip::Chip>::ThreadIdProvider,
233    >();
234
235    // Set up memory protection immediately after setting the trap handler, to
236    // ensure that much of the board initialization routine runs with PMP kernel
237    // memory protection.
238    let pmp = rv32i::pmp::kernel_protection::KernelProtectionPMP::new(
239        rv32i::pmp::kernel_protection::FlashRegion(
240            rv32i::pmp::NAPOTRegionSpec::from_start_end(
241                core::ptr::addr_of!(_sflash),
242                core::ptr::addr_of!(_eflash),
243            )
244            .unwrap(),
245        ),
246        rv32i::pmp::kernel_protection::RAMRegion(
247            rv32i::pmp::NAPOTRegionSpec::from_start_end(
248                core::ptr::addr_of!(_ssram),
249                core::ptr::addr_of!(_esram),
250            )
251            .unwrap(),
252        ),
253        rv32i::pmp::kernel_protection::MMIORegion(
254            rv32i::pmp::NAPOTRegionSpec::from_start_size(
255                0xf0000000 as *const u8, // start
256                0x10000000,              // size
257            )
258            .unwrap(),
259        ),
260        rv32i::pmp::kernel_protection::KernelTextRegion(
261            rv32i::pmp::TORRegionSpec::from_start_end(
262                core::ptr::addr_of!(_stext),
263                core::ptr::addr_of!(_etext),
264            )
265            .unwrap(),
266        ),
267    )
268    .unwrap();
269
270    // initialize capabilities
271    let process_mgmt_cap = create_capability!(capabilities::ProcessManagementCapability);
272    let memory_allocation_cap = create_capability!(capabilities::MemoryAllocationCapability);
273
274    // Create an array to hold process references.
275    let processes = components::process_array::ProcessArrayComponent::new()
276        .finalize(components::process_array_component_static!(NUM_PROCS));
277    PROCESSES = Some(processes);
278
279    // Setup space to store the core kernel data structure.
280    let board_kernel = static_init!(kernel::Kernel, kernel::Kernel::new(processes.as_slice()));
281
282    // ---------- LED CONTROLLER HARDWARE ----------
283
284    // Initialize the LEDs, stopping any patterns from the bootloader
285    // / bios still running in HW and turn them all off
286    let led0 = static_init!(
287        litex_vexriscv::led_controller::LiteXLedController<socc::SoCRegisterFmt>,
288        litex_vexriscv::led_controller::LiteXLedController::new(
289            StaticRef::new(
290                socc::CSR_LEDS_BASE
291                    as *const litex_vexriscv::led_controller::LiteXLedRegisters<
292                        socc::SoCRegisterFmt,
293                    >
294            ),
295            4, // 4 LEDs on this board
296        )
297    );
298    led0.initialize();
299
300    PANIC_REFERENCES.led_controller = Some(led0);
301
302    // --------- TIMER & UPTIME CORE; ALARM INITIALIZATION ----------
303
304    // Initialize the hardware timer
305    let timer0 = static_init!(
306        litex_vexriscv::timer::LiteXTimer<'static, socc::SoCRegisterFmt, socc::ClockFrequency>,
307        litex_vexriscv::timer::LiteXTimer::new(StaticRef::new(
308            socc::CSR_TIMER0_BASE
309                as *const litex_vexriscv::timer::LiteXTimerRegisters<socc::SoCRegisterFmt>
310        ),)
311    );
312
313    // The SoC is expected to feature the 64-bit uptime extension to the timer hardware
314    let timer0_uptime = static_init!(
315        litex_vexriscv::timer::LiteXTimerUptime<
316            'static,
317            socc::SoCRegisterFmt,
318            socc::ClockFrequency,
319        >,
320        litex_vexriscv::timer::LiteXTimerUptime::new(timer0)
321    );
322
323    // Create the LiteXAlarm based on the hardware LiteXTimer core and
324    // the uptime peripheral
325    let litex_alarm = static_init!(
326        AlarmHw,
327        litex_vexriscv::timer::LiteXAlarm::new(timer0_uptime, timer0)
328    );
329    timer0.set_timer_client(litex_alarm);
330    litex_alarm.initialize();
331
332    // Create a shared virtualization mux layer on top of a single hardware
333    // alarm.
334    let mux_alarm = static_init!(MuxAlarm<'static, AlarmHw>, MuxAlarm::new(litex_alarm));
335    litex_alarm.set_alarm_client(mux_alarm);
336
337    // Userspace alarm driver
338    let virtual_alarm_user = static_init!(
339        VirtualMuxAlarm<'static, AlarmHw>,
340        VirtualMuxAlarm::new(mux_alarm)
341    );
342    virtual_alarm_user.setup();
343
344    let alarm = static_init!(
345        capsules_core::alarm::AlarmDriver<'static, VirtualMuxAlarm<'static, AlarmHw>>,
346        capsules_core::alarm::AlarmDriver::new(
347            virtual_alarm_user,
348            board_kernel.create_grant(capsules_core::alarm::DRIVER_NUM, &memory_allocation_cap)
349        )
350    );
351    virtual_alarm_user.set_alarm_client(alarm);
352
353    let scheduler_timer =
354        components::virtual_scheduler_timer::VirtualSchedulerTimerComponent::new(mux_alarm)
355            .finalize(components::virtual_scheduler_timer_component_static!(
356                AlarmHw
357            ));
358
359    // ---------- UART ----------
360
361    // Initialize the HW UART
362    let uart0 = static_init!(
363        litex_vexriscv::uart::LiteXUart<socc::SoCRegisterFmt>,
364        litex_vexriscv::uart::LiteXUart::new(
365            StaticRef::new(
366                socc::CSR_UART_BASE
367                    as *const litex_vexriscv::uart::LiteXUartRegisters<socc::SoCRegisterFmt>,
368            ),
369            // No UART PHY CSR present, thus baudrate fixed in
370            // hardware. Change with --uart-baudrate during SoC
371            // generation. Fixed to 1MBd.
372            None,
373        )
374    );
375    uart0.initialize();
376
377    PANIC_REFERENCES.uart = Some(uart0);
378
379    // Create a shared UART channel for the console and for kernel debug.
380    let uart_mux = components::console::UartMuxComponent::new(uart0, socc::UART_BAUDRATE)
381        .finalize(components::uart_mux_component_static!());
382
383    // ---------- ETHERNET ----------
384
385    // ETHMAC peripheral
386    let ethmac0 = static_init!(
387        litex_vexriscv::liteeth::LiteEth<{socc::ETHMAC_TX_SLOTS}, socc::SoCRegisterFmt>,
388        litex_vexriscv::liteeth::LiteEth::new(
389            StaticRef::new(
390                socc::CSR_ETHMAC_BASE
391                    as *const litex_vexriscv::liteeth::LiteEthMacRegisters<socc::SoCRegisterFmt>,
392            ),
393            socc::MEM_ETHMAC_BASE,
394            socc::MEM_ETHMAC_SIZE,
395            socc::ETHMAC_SLOT_SIZE,
396            socc::ETHMAC_RX_SLOTS,
397            socc::ETHMAC_TX_SLOTS,
398        )
399    );
400
401    // Initialize the ETHMAC controller
402    ethmac0.initialize();
403
404    // ---------- LED DRIVER ----------
405
406    // LEDs
407    let led_driver =
408        components::led::LedsComponent::new().finalize(components::led_component_static!(
409            litex_vexriscv::led_controller::LiteXLed<'static, socc::SoCRegisterFmt>,
410            led0.get_led(0).unwrap(),
411            led0.get_led(1).unwrap(),
412            led0.get_led(2).unwrap(),
413            led0.get_led(3).unwrap(),
414        ));
415
416    // ---------- INITIALIZE CHIP, ENABLE INTERRUPTS ----------
417
418    let interrupt_service = static_init!(
419        LiteXArtyInterruptablePeripherals,
420        LiteXArtyInterruptablePeripherals {
421            uart0,
422            timer0,
423            ethmac0,
424        }
425    );
426    interrupt_service.init();
427
428    let chip = static_init!(
429        litex_vexriscv::chip::LiteXVexRiscv<
430            LiteXArtyInterruptablePeripherals,
431        >,
432        litex_vexriscv::chip::LiteXVexRiscv::new(
433            "LiteX on Arty A7",
434            interrupt_service,
435            pmp,
436        )
437    );
438
439    PANIC_REFERENCES.chip = Some(chip);
440
441    let process_printer = components::process_printer::ProcessPrinterTextComponent::new()
442        .finalize(components::process_printer_text_component_static!());
443
444    PANIC_REFERENCES.process_printer = Some(process_printer);
445
446    // Enable RISC-V interrupts globally
447    csr::CSR
448        .mie
449        .modify(csr::mie::mie::mext::SET + csr::mie::mie::msoft::SET);
450    csr::CSR.mstatus.modify(csr::mstatus::mstatus::mie::SET);
451
452    // Unmask all interrupt sources in the interrupt controller
453    chip.unmask_interrupts();
454
455    // Setup the process console.
456    let pconsole = components::process_console::ProcessConsoleComponent::new(
457        board_kernel,
458        uart_mux,
459        mux_alarm,
460        process_printer,
461        None,
462    )
463    .finalize(components::process_console_component_static!(AlarmHw));
464
465    // Setup the console.
466    let console = components::console::ConsoleComponent::new(
467        board_kernel,
468        capsules_core::console::DRIVER_NUM,
469        uart_mux,
470    )
471    .finalize(components::console_component_static!());
472
473    // Create the debugger object that handles calls to `debug!()`.
474    components::debug_writer::DebugWriterComponent::new_unsafe(
475        uart_mux,
476        create_capability!(capabilities::SetDebugWriterCapability),
477        || unsafe {
478            kernel::debug::initialize_debug_writer_wrapper_unsafe::<
479                <ChipHw as kernel::platform::chip::Chip>::ThreadIdProvider,
480            >();
481        },
482    )
483    .finalize(components::debug_writer_component_static!());
484
485    let lldb = components::lldb::LowLevelDebugComponent::new(
486        board_kernel,
487        capsules_core::low_level_debug::DRIVER_NUM,
488        uart_mux,
489    )
490    .finalize(components::low_level_debug_component_static!());
491
492    let scheduler = components::sched::mlfq::MLFQComponent::new(mux_alarm, processes).finalize(
493        components::mlfq_component_static!(
494            litex_vexriscv::timer::LiteXAlarm<
495                'static,
496                'static,
497                socc::SoCRegisterFmt,
498                socc::ClockFrequency,
499            >,
500            NUM_PROCS
501        ),
502    );
503
504    let litex_arty = LiteXArty {
505        console,
506        pconsole,
507        alarm,
508        lldb,
509        led_driver,
510        scheduler,
511        scheduler_timer,
512        ipc: kernel::ipc::IPC::new(
513            board_kernel,
514            kernel::ipc::DRIVER_NUM,
515            &memory_allocation_cap,
516        ),
517    };
518
519    debug!("LiteX+VexRiscv on ArtyA7: initialization complete, entering main loop.");
520    let _ = litex_arty.pconsole.start();
521
522    kernel::process::load_processes(
523        board_kernel,
524        chip,
525        core::slice::from_raw_parts(
526            core::ptr::addr_of!(_sapps),
527            core::ptr::addr_of!(_eapps) as usize - core::ptr::addr_of!(_sapps) as usize,
528        ),
529        core::slice::from_raw_parts_mut(
530            core::ptr::addr_of_mut!(_sappmem),
531            core::ptr::addr_of!(_eappmem) as usize - core::ptr::addr_of!(_sappmem) as usize,
532        ),
533        &FAULT_RESPONSE,
534        &process_mgmt_cap,
535    )
536    .unwrap_or_else(|err| {
537        debug!("Error loading processes!");
538        debug!("{:?}", err);
539    });
540
541    (board_kernel, litex_arty, chip)
542}
543
544/// Main function called after RAM initialized.
545#[no_mangle]
546pub unsafe fn main() {
547    let main_loop_capability = create_capability!(capabilities::MainLoopCapability);
548
549    let (board_kernel, board, chip) = start();
550    board_kernel.kernel_loop(&board, chip, Some(&board.ipc), &main_loop_capability);
551}