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